Hey,
I’ve been experimenting with Moralis and created a cloud function to serve gated content by NFT ownership. I just wanted to share it, since I saw a lot of conversation around this topic but not a full code example (maybe I am missing it). I am new to Moralis and React/Javascript. So I am not sure if this is the correct way or if I am following the best practices. So please let me know if you see any issues with the implementation or the whole logic around it.
I’ve used two Testnet OpenSea NFTs for this that are in the below collection. Make sure that NFTs are not lazy-minted.
“https://testnets.opensea.io/collection/testingthisstuff”
The gated content in this example is just a string; “Gated content for NFT 1”
Each NFT in our example have a seperate gated content.
NFT to Gated content mapping is stored in the nftToPageMapping
variable within the Cloud function itself. I thought this would be the best place to store the gated content in terms of security. (you are trusting Moralis to store your gated content, but we need start trusting from somewhere right :D) This can also be modified to store it in the database.
<token_address>_<token_id> maps to <gated content>
<token_address>_<token_id>
points to the NFT that we want to have gated content with.
Here is the Cloud function with explanations:
Please change the nftToPageMapping
variable in the code for your NFTs.
Moralis.Cloud.define("nftGatedContent", async (request) => {
// To ensure user has logged-in.
// The user object is passed by Moralis for logged in users.
if (request.user) {
// Mapping for NFT to gated content
// This is where we store the gated content that NFT relates to.
// You can expand this list for other NFTs
// <token_address>_<token_id> -> <gated content>
let nftToPageMapping = {
/* Tesst */"0x0ea82ca03aa355941271efbe1b0ee66ef3a74ea3_112310352153202421499958867797769186319214905582065839985351320021994009788417":
"Gated content 1",
/* Test2 */"0x0ea82ca03aa355941271efbe1b0ee66ef3a74ea3_112310352153202421499958867797769186319214905582065839985351320023093521416193":
"Gated content 2"
};
// Get logged in user's ETH address.
const userEthAddress = request.user.get("ethAddress");
// EthNFTTransfers is a table that stores user's NFT transactions. This table will automatically be updated by Moralis when a new user joins or an existing user makes an NFT transfer.
const query = new Moralis.Query("EthNFTTransfers");
// Filter the NFT transfters from EthNFTTransfers table to get the NFT transfers only for the current logged-in user.
query.equalTo("to_address", userEthAddress);
// Check if transaction is confirmed
query.equalTo("confirmed", true);
// Run the query
const nftTransters = await query.find();
// This is used to return the gated content for each NFT.
// This is what we will return as output.
let resultToReturn = {}
// Iterate over NFTs of the logged-in user to get the token address and token id of NFT's
for (let nftTransfer of nftTransters) {
// Key for nftToPageMapping, <token_address>_<token_id> -> col#
let key = nftTransfer.get("token_address") + "_" + nftTransfer.get("token_id");
// Get the gated content this NFT points to.
let currGatedContent = nftToPageMapping[key];
resultToReturn[key] = currGatedContent;
}
return JSON.stringify(resultToReturn)
}
else {
return "You are not authorized"
}
});
If the logged-in user owns the NFTs it will return the gated NFT content.
Example: {"Gated content 2":"0x0ea82ca0......","Gated content 1":"0x0ea82c....."}
If user doesn’t have any nfts with gated content it will return empty JSON; {}
Then in client-side. You can call this cloud function like below. You need
import { useEffect } from "react";
import { useMoralis, useMoralisCloudFunction } from "react-moralis";
function Cloudcall() {
const { isAuthenticated, logout, authenticate } = useMoralis();
// Here the User object passed to the request.
const { data, error, isLoading } = useMoralisCloudFunction("nftGatedContent");
useEffect(() => {
//if (!isAuthenticated) router.replace("/");
}, [isAuthenticated]);
if (isAuthenticated) {
return (
<div className="h-screen w-screen flex flex-col items-center justify-center">
<p>Here is the gated content: {data}</p>
<button
onClick={logout}
className="px-7 py-4 text-xl rounded-xl bg-yellow-300"
>
Logout
</button>
</div>
);
}
return (
<div className="h-screen w-screen flex flex-col items-center justify-center">
<p>You are not logged in.</p>
<button
onClick={() => authenticate({ signingMessage: "Authorize linking of your wallet to NFT Project" })
}
className="px-7 py-4 text-xl rounded-xl bg-yellow-300"
>
{!isAuthenticated ? "Login using Metamask" : "Logout"}
</button>
</div>
);
}
export default Cloudcall;
And in app.js you need something like below;
import { MoralisProvider } from "react-moralis";
import "../styles/globals.css";
function MyApp({ Component, pageProps }) {
return (
<MoralisProvider
appId={process.env.NEXT_PUBLIC_APP_ID}
serverUrl={process.env.NEXT_PUBLIC_SERVER_URL}
>
<Component {...pageProps} />
</MoralisProvider>
);
}
export default MyApp;
Hope it helps others as well.
Thanks,