This is the fourth and final part of a tutorial series for building a tokengated storefront. Read the [overview](/docs/apps/build/blockchain/tokengating) page before starting this tutorial.
In this tutorial, you'll create a function that generates a hash-based message authentication code (HMAC). The HMAC you create will be an encrypted version of the `gateConfiguration.id` that your customer has unlocking tokens for. With this, you can leverage Shopify Functions at checkout to apply automatic discounts
## What you'll learn
In this tutorial, you'll learn how to do the following:
- Generate an HMAC using [SubtleCrypto](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto) from the [Web Crypto API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API).
- Write the generated HMAC to cart attributes in a stringified array to allow your customers to unlock many gates.
## Requirements
- A Shopify Function that applies automatic discounts to the cart. Refer to the [creating a Shopify Function to apply the gated discount tutorial](/docs/apps/build/blockchain/tokengating/build-a-tokengating-app/create-gated-discount-function).
- You have followed the previous tutorials in this series:
- [Getting started](/docs/apps/build/blockchain/tokengating/build-a-tokengated-storefront/start-building)
- [Save a connected wallet to session storage](/docs/apps/build/blockchain/tokengating/build-a-tokengated-storefront/save-wallet-to-session)
- [Read and evaluate gates](/docs/apps/build/blockchain/tokengating/build-a-tokengated-storefront/read-and-evaluate-gates)
> Note:
> While you can still complete this tutorial without a Shopify Function, you won't have the full end-to-end functionality that applies discounts at checkout based on cart attributes.
## Step 1: Generate an HMAC
In the [last tutorial](/docs/apps/build/blockchain/tokengating/build-a-tokengated-storefront/read-and-evaluate-gates#step-3-evaluate-the-gate), you created an `evaluateGate.server` JS or TS file to perform gate evaluation.
In that same file, you'll add additional functionality to the `evaluateGate` function to create an HMAC when `unlockingTokens` are present for a given `gateConfiguration`.
1. Add a new environment variable named `SHOPIFY_FUNCTION_SECRET` to your ENV file. The value should match the secret defined in the Shopify Function you built for applying automatic discounts.
At this point, your `.env` file should look similar to the one shown below:
1. Open your `evaluateGate.server` JS or TS file.
1. Add the following code to the bottom of the file:
1. Update the `evaluateGate` function by pasting the following code just below the `unlockingTokens` variable:
1. Add a `gateContext` object to the `evaluateGate` function's return statement.
1. Add the `GateContext` type (TypeScript only).
Copy the following type definition into your `/app/lib/type.ts` file:
1. Update the `EvaluateGateResponse` type to include the `GateContext` type (TypeScript only).
In your `evaluateGate.server` JS or TS file, update your `EvaluateGateResponse` to include the `GateContext` type you just created by replacing your `EvaluateGateResponse` with the following code:
To see a representation of what your code might look like after following these steps, [view the sample repository](https://github.com/Shopify/gated-hydrogen-example/blob/main/app/lib/evaluateGate.server.ts).
## Step 2: Write the generated HMAC to cart attributes
Now that you've written the functionality to generate the HMAC, you need to write this HMAC to the cart attributes.
To do this, you'll add logic to the `loader` function defined in the `$productHandle` route.
This function checks if a cart is present before writing the attributes. If a cart is present, then it mutates the attributes for the cart. If a cart isn't present, the function creates a new cart and updates the cart cookie.
### Add cart attributes functions
The code in this file contains the business logic associated with creating and updating cart attributes.
1. In your project's `/app/lib` folder, create a new file called `attributes.server.js`, or `attributes.server.ts` if you're using TypeScript.
1. Add the following code:
### Run cart attribute mutations
With the added functions from your `attributes.server` JS or TS file, you'll update your loader to write `gateContext` values returned from `evaluateGate` to the cart attributes.
1. Open your `/app/routes/($locale).products.$productHandle` JSX or TSX file.
1. Add the following code to the top of the `loader` function:
```javascript
const headers = new Headers();
```
This will be the headers for the returned response from the loader. Using these headers, you'll set the cart ID in the response to ensure that there is only one cart for the session.
1. In the same file, add the following code below the `unlockingTokens` assignment:
## Step 3: Handle disconnection events
When a customer disconnects their wallet, clear the `gateContext` cart attributes. Doing this ensures that any gates which were previously unlocked are no longer unlocked.
1. Open your `/app/routes/($locale)._index.tsx` file.
1. Add the following import statement:
```javascript?filename: '($locale)._index.tsx'
import {clearContextAttributes} from '~/lib/attributes.server';
```
1. Locate the `clearFunction` and add the following code just below the `clear()` invocation:
```javascript
await clearContextAttributes({context, request});
```
## Step 4: Test your storefront
Your custom storefront now reads gates bound to products, parses the gate configurations for reactions and requirements, stores your buyer's wallet in cookies, evaluates their gate eligibility, and writes gate context to their cart.
Test that your storefront is enforcing gates and applying automatic discounts using your Shopify Function by following these steps:
1. Start your development server using the `dev` command:
1. Open a product that has gates.
You should see the `Tokengate` component with a list of requirements needed to unlock the discount.
1. Connect a wallet that meets the unlocking criteria.
Your `Tokengate` component should now show that the discount is unlocked.
1. Add the product to your cart.
Your cart should reflect the price of the item with the discount applied.
1. Navigate to your checkout.
Your discount should be applied to the product in your cart.
1. Go back to your storefront and disconnect your wallet.
The price for the item should update to reflect the non-discounted price.
## Resources