Note: Originally published on August 3, 2023 on Mirror.
![](https://storage.googleapis.com/papyrus_images/b05c80b8722ef0ecdcac949f5d9380fb.png)
1. Overview
The aim of this tutorial is to demonstrate how to use the Sismo Connect Server Library to create a mini private "Only Fans" application. The main feature you're aiming for is to validate if a user owns a certain NFT (Pudgy Penguins, in this case) and offer them a discount based on their holdings. This project preserves user privacy and security using Zero-Knowledge Proofs (ZK Proofs) and offchain verification.
Note: For a comprehensive understanding, we recommend users watch the YouTube video linked in the tutorial. If you're seeking the full code, it's available on my GitHub repository.
2. Prerequisites
Node.js
Npm
MetaMask browser extension
3. Installation and Database Setup
Clone the Tutorial Repository
This step involves retrieving the application source code from GitHub:
Clone the Repository: git clone https://github.com/ChaskinOnChain/sismo_offchain_only_penguins.git
Navigate into the Directory: cd sismo_offchain_only_penguins
Checkout the Specific Commit: git checkout fee4c374be8839fe305a5a5b1c8d53d9c276d06a
This ensures you're working on the same codebase version as described in the tutorial.
Install Dependencies
Your application relies on various libraries and packages, primarily from the Next.js framework:
Install Next.js Dependencies: npm i
This command fetches and installs all necessary dependencies as mentioned in the package.json file of the cloned repository.
Database Setup on PlanetScale
The heart of most applications is the database. Here's how to set it up on PlanetScale:
Access the Platform:
Go to PlanetScale's website.
Account Setup:
If you're new to PlanetScale, sign up. Otherwise, log in.
Creating a Database:
Click on "See how PlanetScale works."
This tutorial essentially acts as a guide for beginners.
Keep progressing through the tutorial steps by pressing the presented buttons.
When prompted to create a database, name it appropriately (for example, onlypenguins).
Choose the 'Hobby' tier, which is free.
Database Connection Setup:
Once your database is ready, you'll see an option saying "Ready to connect to your database?".
Click on it and then click "create Password."
For the connection method, ensure "Connect with Prisma" is selected. Click on .env and subsequently click on the clipboard icon.
This action copies the connection string, which looks like DATABASE_URL=”mysql://…”.
Configure Connection in Your Local Environment: Switch back to your code editor. In the root directory of your cloned repository, create a .env file. Paste the copied DATABASE_URL connection string into this file.
This .env file stores sensitive environment variables, in this case, the connection string for your database.
Update Database with Prisma:
Next, you want your local setup to recognize and structure the database accordingly.
To produce the Prisma Client from your Prisma schema for type-safe database access:
npx prisma generate
To update the database schema to match your Prisma schema without using migrations:
npx prisma db push
4. Launching the Local Application
After completing the installation and database setup, you are set to launch the application locally and interact with its integrated features.
Run the Application:
npm run dev
This command initiates the Next.js application. The app has preset server routes located in app/api/verify/route.ts and app/api/paid/route.ts. Once the command is executed, the application should be accessible on your local machine.
Access the Application:
Using any web browser, visit http://localhost:3000. Here, you'll experience the tutorial application in action, already integrated with Sismo Connect.
Experiment with the Local App:
The application comes with Sismo Connect integration. To personalize the experience:
Navigate to the Sismo.tsx file located in app/components/.
In this file, find line 28, where you'll see an address parameter. Change this 'to' address to your own Ethereum address. This modification ensures that any transactions (payments) made within the app will be directed to your personal address.
Reminder: Always make sure you are on a testnet when experimenting with such changes. This way, you avoid unnecessary expenses of real Ether (ETH).
Enjoy Your Subscription:
After making the necessary modifications and ensuring you're on a testnet, go ahead and try subscribing to "Only Penguins".
![](https://storage.googleapis.com/papyrus_images/b7c95a1377e9ee683fd4e6c8c002e334.png)
Dive Deeper:
With the basic setup and operations out of the way, you can now delve deeper into how Sismo Connect is integrated within the application. The tutorial likely offers insights into the workings behind the scenes and ways to further refine and expand the integration.
![](https://storage.googleapis.com/papyrus_images/721071ccbec71342b51618b2962a199b.png)
5. Sismo Connect Configuration
Configuring Sismo Connect in Your Application:
Access the Configuration File:
Navigate to
app/components/Sismo.tsx
in your application.
Setting Up Application in the Sismo Factory:
If you wish to use Sismo Connect in your application, it's essential to first set up an application within the Sismo Factory.
Obtain the unique
appId
for your application from the Sismo Factory.For those unfamiliar with this process, there's a tutorial available here. However, for the sake of this tutorial, we're utilizing an already defined
appId
which is0xbffb8652509c7e27e0b0485beade19c2
.
Integration using the Library:
The library,
@sismo-core/sismo-connect-react
, considerably simplifies the integration of Sismo Connect. It includes a convenient React button that facilitates the request for proofs of user data.This button is designed to uphold user privacy, ensuring only necessary data is accessed without compromising sensitive information.
6. Sismo Connect React Button Configuration
Configuring the Button for User Data Proof Requests
This button serves as an essential bridge allowing your application to interact with the Sismo Connect and request proofs.
![](https://storage.googleapis.com/papyrus_images/c91903bcd587a6cf77413b9744d63e2b.png)
Button Configuration:
In your application, locate the Sismo Connect configuration that employs the
appId
obtained from the Sismo Factory. If you're unfamiliar with this configuration, refer to this configuration guide.
Proof Request Mechanism:
Auth Mechanism: The auths attribute facilitates the generation of proofs validating a user's specific vault id. Delve deeper into the concept of vault ids here.
Handling Responses: The
onResponse
attribute outlines how the application should react upon acquiring the proof. For instance, in this tutorial, once a proof is received, the server situated at/api/verify
is invoked with the proof being sent as the body. The server then ascertains the presence of the user's vault and acts accordingly: retrieving an existing user's vault id and payment status or creating a new user record.
![](https://storage.googleapis.com/papyrus_images/9180fcc8f284c7ea0f6b786031415a78.png)
Optional Discounts Based on Pudgy Penguin Ownership:
The objective here is to present users with a discount depending on their Pudgy Penguins ownership. Specifically, a discount rate of 0.001ETH is applied for each Pudgy Penguin token they possess in the account with the most Pudgy Penguin tokens. This discount considers all accounts in the user's vault, not necessarily the one executing the payment.
To implement this, begin by sourcing a proof from the user's account possessing the highest number of Pudgy Penguin tokens. Acquire the proof by identifying the
groupId
of the "Pudgy Penguins Nft Holders" group on the Sismo Factory here and subsequently initiating a claim request. Within your application'sSismoConnectButton
component, introduce a new claims prop. This prop should encapsulate details of the Pudgy Penguin ownership claim: This prop should define the specifics of the Pudgy Penguin ownership claim. In the object for this prop, you'll be configuring several properties:groupId
: Set this to the variablepudgyPenguinsGroupId
which represents the identifier of the Pudgy Penguin group on the Sismo Factory.claimType
: Configure this toClaimType.GTE
, which stands for "greater than or equal to". This type ensures the proof validates that the user has at least a specified number of tokens.isOptional
: Set this to true, implying that the claim is not mandatory for the user to provide.value
: Configure this to 1, indicating that the proof should validate the user has at least one Pudgy Penguin token.isSelectableByUser
: This is set to true to grant users discretion in revealing the quantity of their tokens. For instance, some users may be major Pudgy Penguin holders, and disclosing the entirety of their tokens might inadvertently reveal their identity. By making this selectable, you allow users to choose the specific amount they wish to reveal, but they must disclose at least one token.
![](https://storage.googleapis.com/papyrus_images/4f679aa7224b6d8f7d24b884d1f78b80.png)
This configuration ensures a flexible and user-centric approach to revealing Pudgy Penguin token holdings, balancing between utility and user privacy.
Impersonating Vaults for Demo Purposes:
For demonstration objectives, this tutorial suggests populating the data vault with specific addresses that you might not own. Sismo Connect simplifies the impersonation of such accounts. Within the
SismoConnectButton
's configuration (config
object), introduce avault
key paired with another object namedimpersonate
. This object should house an array of addresses for demonstration purposes. Examples include Ethereum addresses in hex format, ENS names, and social media handles (like Twitter or GitHub) with a suitable prefix.As an illustration, let's integrate
nansen.eth
andjebus.eth
into our vault - renowned Ethereum accounts with substantial Pudgy Penguin ownership.
![](https://storage.googleapis.com/papyrus_images/6e6f5835687b9f327b6c53ed46d698b6.png)
![](https://storage.googleapis.com/papyrus_images/5eebfb784cdbc14f92b321b96f72e003.png)
⚠ Caution: Prior to deploying in a production setting, ensure all impersonated accounts are duly removed. Post impersonation, it becomes necessary for our database to monitor the number of tokens the user opts to reveal. The server extracts this value from the proof and relays this information to the frontend, which we'll further detail in upcoming backend amendments.
7. Backend Database Updates
Reflecting Token Ownership in the Database
The application's backend needs to account for the token ownership of each user. This requires updating the database schema.
Steps:
Schema Update:
Navigate to
prisma/schema.prisma
. Update the User model to include a new entry for token numbers.This will keep track of how many tokens a user has chosen to reveal.
Update Database with Prisma:
Run the commands
npx prisma generate
andnpx prisma db push
to reflect changes in the database.
![](https://storage.googleapis.com/papyrus_images/5eebfb784cdbc14f92b321b96f72e003.png)
Located at app/prisma/schema.prisma
8. Server-side Verification: Processing and Verifying the ZK Proofs
The server is responsible for verifying the Zero-Knowledge (ZK) proof submitted by the front-end. Here's a step-by-step guide to achieve this:
Steps:
Setup Sismo Connect for Server:
Navigate to
app/api/verify/route.ts
. Utilize thesismo-connect-server
package, which facilitates server-side proof verification. The process for setting up on the server is akin to what's done on the front-end:Initialize the
sismoConnect
variable by assigning it toSismoConnect
from the package.Feed it an object that contains the configuration (which includes your
appId
and the vault impersonation details, if any) that mirrors the front-end setup.
ZK Proof Verification:
Using the
sismoConnect
variable, invoke theverify
function. Supply it with the ZK proof (or response) obtained from the front-end. Also provide an object containing additional setup details (like auths and claims) that were used when creating the front-end.It's imperative that this server-side setup aligns with the front-end to ensure accurate proof verification. Any discrepancy can result in the proof getting rejected.
![](https://storage.googleapis.com/papyrus_images/1ea50ed4c6c34eca43122e847e946cc7.png)
Data Extraction:
From the results of the verification, you can retrieve the
vaultId
. To extract the number of tokens the user opts to reveal, useresult.claims[0].value
.Given that the token claim is optional, a user might choose not to submit one. They might refrain from doing so either because they don't possess tokens or they might not want the discount. Hence, set the
tokens
variable dynamically: assign it to zero if no claim is presented, or assign it the revealed token value if there's a claim.
User Database Update & Response:
Augment the
create
function to incorporate thetokens
value, enabling it to be saved in your database.Subsequently, return the
tokens
value to the front-end for further processing or display.
![](https://storage.googleapis.com/papyrus_images/5738d23a7769794c85328df6c1f48c12.png)
Note: If you deploy the Impersonation Mode, the
vaultId
will be randomized. This means a distinct user will be stored in the database each time a subscription is made. Always remember to handle this mode judiciously to prevent unwanted discrepancies.
9. Final Frontend Integration: Adjusting Payment Logic for Discounts
To ensure a seamless user experience, you need to adjust the frontend to dynamically incorporate any potential discounts for NFT holders. Here's how to achieve this:
Steps:
Update Payment Logic:
Navigate to
Sismo.tsx
located inapp/components/Sismo.tsx
.Given that the number of tokens is stored as a state variable, you can dynamically adjust the price. Specifically, for each token the user owns, you'll provide a saving of 0.001ETH.
In the
subscribe
function, right aftersetLoading(true)
, introduce a constantprice
variable. It should be defined as:const price = Math.max(0.1 - (tokens || 0) * 0.001, 0)
This equation adjusts the price based on the discount, but will cap the price at a minimum of 0, especially for users who own 100 tokens or more.
Now, adapt the payment function to use this dynamic price. Convert the price to the appropriate format using:
parseEther(price.toString())
Display Discounts:
If a user is authenticated, integrate an
h4
HTML element just above the button. This element will display the quantity of tokens the user has unveiled, the savings they've achieved, and their adjusted total payment.
![](https://storage.googleapis.com/papyrus_images/d910eda92908e728647bb6528dca318a.png)
![](https://storage.googleapis.com/papyrus_images/0d932361a82c38103729b090ef50e150.png)
10. Recap and Conclusion
The integration process is complete!
In summary, an offchain Sismo Connect integration involves:
Creating an App in the Sismo Factory: The primary goal here is to obtain the crucial
appId
.Configuring Sismo Connect: This configuration must be done both on the frontend and the server. You'll use the obtained
appId
and, if required, enable the "impersonation mode" with selected accounts. This makes testing more straightforward.React Button Configuration: Set up the React button. Determine which proofs you need to request from your users.
API Creation for Proof Verification: You'll need to set up an API that can verify user proofs offchain.
And with that, we've reached the finish line. Congratulations! 🎉
To recap, you've transformed a straightforward request for vaultId
into a more sophisticated multi-request system that includes vaultId
+ group membership. This transformation lets you craft a mini "Only Fans" style platform that offers optional discounts derived from privately-aggregated data. Most importantly, every request made respects user privacy. At no point is the user's Ethereum address ever shared or exposed, all thanks to the data aggregation capabilities of Sismo's Data Vault.
![](https://storage.googleapis.com/papyrus_images/b876b51233e2fce4f8e9643c69d6ea58.png)
![highlight](/api/highlight?text=Sismo%20Connect%20Offchain%20Tutorial%20-%20Code%20a%20Private%20Mini%20Only%20Fans%20Application&author=Jason%20Chaskin&url=%40chaskin&avatarUrl=https%3A%2F%2Fstorage.googleapis.com%2Fpapyrus_images%2F063f04aa083d8110532b5569d44b4dca.jpg&featuredImageUrl=&size=512)
![Jason Chaskin logo](/_next/image?url=https%3A%2F%2Fstorage.googleapis.com%2Fpapyrus_images%2F063f04aa083d8110532b5569d44b4dca.jpg&w=384&q=75)