This is the third part of a four-part tutorial series for building a tokengated storefront.
In this tutorial, you'll create a function that evaluates gates on products with your customer's connected wallet. The evaluation function determines if the customer should receive the associated reaction defined in the [gate configuration you created in the first tutorial](/docs/apps/build/blockchain/tokengating/build-a-tokengated-storefront/start-building#step-3-create-a-gate-and-attach-it-to-a-product).
## What you'll learn
In this tutorial, you'll learn how to do the following:
- Read gates bound to a product.
- Use wallet data stored server-side to evaluate gate eligibility.
- Update your `ProductForm` to include the `Tokengate` component, and display the customer's eligibility to receive the reaction defined in the `gateConfiguration`.
## Requirements
- You've followed the [getting started](/docs/apps/build/blockchain/tokengating/build-a-tokengated-storefront/start-building) and [saving connected wallet to session storage](/docs/apps/build/blockchain/tokengating/build-a-tokengated-storefront/save-wallet-to-session) steps of the tutorial series.
- You've created an [Alchemy](https://www.alchemy.com) account and [retrieved your Alchemy API key](https://docs.alchemy.com/docs/alchemy-quickstart-guide).
## Step 1: Update your .env file
Add a new environment variable named `ALCHEMY_API_KEY` to your ENV file. Your ENV file should look similar to the one below:
## Step 2: Retrieve product gates
To evaluate gates, you first need to retrieve the gates bound to your products. In this section, you'll make changes to your product query and your loader function to retrieve and parse the gates bound to the product.
### Update the product query
1. Open your `($locale).products.$productHandle` JSX or TSX file.
1. Near the end of the file, find the `PRODUCT_QUERY` constant.
1. In this query, paste the fields from the code sample below.
You can paste them anywhere within the `product` query field. In the example repository the fields are between `description` and `options`.
Make sure that you replace the placeholder `headless_gating_example` namespace with your own metafield namespace.
### Generate the unstable API schema (TypeScript only)
If you're using TypeScript, then you need to generate the types for the `unstable` Storefront API schema. Generating the `unstable` schema types allows you to assign the correct types to the `gateConfiguration` and `product` resources.
1. In a terminal, run the following command to install the packages needed to generate the schema types:
1. In your project's root folder, create a new file called `codegen.js`.
1. Add the following code:
```javascript?filename: 'codegen.js'
import 'dotenv/config';
const SCHEMA_URL = new URL(
`https://${process.env.PUBLIC_STORE_DOMAIN}/api/${process.env.PUBLIC_STOREFRONT_API_VERSION}/graphql.json`,
).toString();
const config = {
schema: [
{
[SCHEMA_URL]: {
headers: {
'X-Shopify-Storefront-Access-Token':
process.env.PUBLIC_STOREFRONT_API_TOKEN,
},
},
},
],
generates: {
'./app/generated/storefront-api-types.ts': {
plugins: ['typescript'],
},
},
overwrite: true,
};
export default config;
```
1. Generate the `unstable` API schema.
Run the following script by using your preferred package manager:
After running the following command, you'll notice a new file added to your project located in `/app/generated/storefront-api-types.ts`.
### Update the loader (TypeScript only)
on the $productHandle route to retrieve the stored wallet value.
If you're using TypeScript, then you need to update the `loader` function in your `$productHandle` route to reference the correct types from the Storefront API schema.
1. Open your `/app/routes/($locale).products.$productHandle` JSX or TSX file.
1. Add the following import statements:
```typescript
import type {
Metafield,
Product as UnstableProductType,
} from '~/generated/storefront-api-types';
```
1. Update the types for the GraphQL payload in the `loader` function by replacing the query with the following code:
```typescript
const {shop, product} = await context.storefront.query<{
product: UnstableProductType & {selectedVariant?: ProductVariant};
shop: Shop;
}>(PRODUCT_QUERY, {
variables: {
handle: productHandle,
selectedOptions,
country: context.storefront.i18n.country,
language: context.storefront.i18n.language,
},
});
```
## Step 3: Evaluate the gate
In this step, you'll create a server-side function that requests data related to a user's wallet address from [Alchemy](https://alchemy.com). The data that you receive from Alchemy is used to evaluate whether a customer should receive the benefits defined in the gate's `reaction`.
### Add Alchemy response types (TypeScript only)
If you're using TypeScript, then copy the following type definitions into the bottom of `/app/lib/type.ts`:
> Note:
> The following types are intended to be used for this tutorial and may not reference all data returned by the Alchemy endpoint. If you need additional types for the data you receive from the endpoint, you may have to add your own types.
### Add an evaluate gate function
In this step, you'll create functions to do the following:
- Parse the provided `gateConfiguration` for a `requirements` definition.
- Fetch a collection of owned NFTs for a wallet address.
1. In your project's `/app/lib` folder, create a new file called `evaluateGate.server.js`, or `evaluateGate.server.ts` if you're using TypeScript.
> Note:
> The `.server.js` extension tells the Remix compiler to bundle this file for the server and not include it in browser bundle.
1. Add the following code:
### Evaluate gates in the product loader
Using the product's gates from the `PRODUCT_QUERY` and the wallet stored in session from the [previous step](/docs/apps/build/blockchain/tokengating/build-a-tokengated-storefront/read-and-evaluate-gates#update-the-product-query), you can now perform gate evaluation.
1. Open the `/app/routes/($locale).products.$productHandle` JSX or TSX file.
1. Add the following import statement:
```javascript
import {evaluateGate} from '~/lib/evaluateGate.server';
```
1. In the same file, update the `loader` by adding the following code:
1. Update the `return` statement of the loader by adding `gateConfiguration`, `unlockingTokens`, and `wallet` to the return statement.
## Step 4: Update the product form
Now that the loader is returning `gateConfiguration`, `unlockingTokens`, and `wallet`, update your `ProductForm` to display your customer's eligibility, using the [`Tokengate`](/docs/api/blockchain/components/tokengate) component from `@shopify/tokengate`.
1. Open your `($locale).products.$productHandle` JSX or TSX file.
1. Import the following components:
The [`ConnectButton`](/docs/api/blockchain/components/connect-wallet#connectbutton) component from the `@shopify/connect-wallet` package.
The [`Tokengate`](/docs/api/blockchain/components/tokengate) component from `@shopify/tokengate` package.
1. In the same file, locate the `ProductForm` component, and update the destructured data from the `useLoaderData` to match the following code:
1. Locate the constant named `isOutOfStock` within the `ProductForm` component.
The `Tokengate` component uses a prop named `isSoldOut`, which won't allow ybcibbected wallets to connect when the product is sold out.
Paste the following code directly below the `isOutOfStock` constant:
1. Locate the return statement for the `ProductForm` component.
Directly above the return statement, paste the following code:
1. Add the `Tokengate` component to the `ProductForm` by copying the following code and pasting it into the first `div` element of the `ProductForm` return statement.
1. Directly below the code you pasted into the return statement from the last step, wrap the `ProductOptions` and remaining code with the following:
To see a representation of what your code might look like, [view the sample repository](https://github.com/Shopify/gated-hydrogen-example/blob/main/app/routes/(%24locale).products.%24productHandle.tsx).
## Next steps
- [Write gate context to cart attributes](/docs/apps/build/blockchain/tokengating/build-a-tokengated-storefront/write-gate-context-to-cart-attributes)