 {"id":986,"date":"2023-01-04T05:53:35","date_gmt":"2023-01-03T19:53:35","guid":{"rendered":"https:\/\/test.secrypt.tech\/?page_id=986"},"modified":"2023-01-05T17:57:53","modified_gmt":"2023-01-05T07:57:53","slug":"genesis-contracts","status":"publish","type":"page","link":"https:\/\/test.secrypt.tech\/index.php\/genesis-contracts\/","title":{"rendered":"Genesis Contracts"},"content":{"rendered":"<style>\/*! elementor - v3.9.2 - 21-12-2022 *\/\n.elementor-widget-image{text-align:center}.elementor-widget-image a{display:inline-block}.elementor-widget-image a img[src$=\".svg\"]{width:48px}.elementor-widget-image img{vertical-align:middle;display:inline-block}<\/style>\n<p>\t\t\t\t\t\t\t\t\t\t\t\t\t<a href=\"https:\/\/test.secrypt.tech\/\"><br \/>\n\t\t\t\t\t\t\t<img decoding=\"async\" src=\"https:\/\/test.secrypt.tech\/wp-content\/uploads\/elementor\/thumbs\/image-removebg-preview-pusb43b4fkgjv12hawycptkh3fmhfjngdaesxsjilk.png\" title=\"image-removebg-preview\" alt=\"image-removebg-preview\" \/>\t\t\t\t\t\t\t\t<\/a><\/p>\n<style>\/*! elementor - v3.9.2 - 21-12-2022 *\/\n.elementor-column .elementor-spacer-inner{height:var(--spacer-size)}.e-con{--container-widget-width:100%}.e-con-inner>.elementor-widget-spacer,.e-con>.elementor-widget-spacer{width:var(--container-widget-width,var(--spacer-size));--align-self:var(--container-widget-align-self,initial);--flex-shrink:0}.e-con-inner>.elementor-widget-spacer>.elementor-widget-container,.e-con-inner>.elementor-widget-spacer>.elementor-widget-container>.elementor-spacer,.e-con>.elementor-widget-spacer>.elementor-widget-container,.e-con>.elementor-widget-spacer>.elementor-widget-container>.elementor-spacer{height:100%}.e-con-inner>.elementor-widget-spacer>.elementor-widget-container>.elementor-spacer>.elementor-spacer-inner,.e-con>.elementor-widget-spacer>.elementor-widget-container>.elementor-spacer>.elementor-spacer-inner{height:var(--container-widget-height,var(--spacer-size))}<\/style>\n<style>\/*! elementor - v3.9.2 - 21-12-2022 *\/\n.elementor-heading-title{padding:0;margin:0;line-height:1}.elementor-widget-heading .elementor-heading-title[class*=elementor-size-]>a{color:inherit;font-size:inherit;line-height:inherit}.elementor-widget-heading .elementor-heading-title.elementor-size-small{font-size:15px}.elementor-widget-heading .elementor-heading-title.elementor-size-medium{font-size:19px}.elementor-widget-heading .elementor-heading-title.elementor-size-large{font-size:29px}.elementor-widget-heading .elementor-heading-title.elementor-size-xl{font-size:39px}.elementor-widget-heading .elementor-heading-title.elementor-size-xxl{font-size:59px}<\/style>\n<h2>Genesis Contracts<\/h2>\n<p>Here you will find a list of contracts deployed on SECRYPT together with their initial address, that is, their location on the blockchain.<\/p>\n<ul>\n<li>SECRYPT -Mainnet<\/li>\n<\/ul>\n<ul>\n<li>SECRYPT-Testnet<\/li>\n<\/ul>\n<p>Parent Chain: Ethereum Mainnet<\/p>\n<table>\n<thead>\n<tr>\n<td>\n<p><strong>Contracts<\/strong><\/p>\n<\/td>\n<td>\n<p><strong><u>Address<\/u><\/strong><\/p>\n<\/td>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>\n<p>BytesLib<\/p>\n<\/td>\n<td>\n<p><u>0x1d21fACFC8CaD068eF0cbc87FdaCdFb20D7e2417<\/u><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>Common<\/p>\n<\/td>\n<td>\n<p><u>0x31851aAf1FA4cC6632f45570c2086aDcF8B7BD75<\/u><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>ECVerify<\/p>\n<\/td>\n<td>\n<p><u>0x71d91a8988D81617be53427126ee62471321b7DF<\/u><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>Merkle<\/p>\n<\/td>\n<td>\n<p><u>0x8b90C7633F1f751E19E76433990B1663c625B258<\/u><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>MerklePatriciaProof<\/p>\n<\/td>\n<td>\n<p><u>0x8E51a119E892D3fb324C0410F11f39F61dec9DC8<\/u><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>PriorityQueue<\/p>\n<\/td>\n<td>\n<p><u>0x61AdDcD534Bdc1721c91740Cf711dBEcE936053e<\/u><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>RLPEncode<\/p>\n<\/td>\n<td>\n<p><u>0x021c2Bf4d2941cE3D593e07317EC355937bae495<\/u><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>RLPReader<\/p>\n<\/td>\n<td>\n<p><u>0xD75f1d6A8A7Dc558A65c2f30eBF876DdbeE035a2<\/u><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>SafeMath<\/p>\n<\/td>\n<td>\n<p><u>0x96D358795782a73d90F2ed2d505aB235D197ca05<\/u><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>Governance<\/p>\n<\/td>\n<td>\n<p><u>0x98165b71cdDea047C0A49413350C40571195fd07<\/u><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>GovernanceProxy<\/p>\n<\/td>\n<td>\n<p><u>0x6e7a5820baD6cebA8Ef5ea69c0C92EbbDAc9CE48<\/u><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>Registry<\/p>\n<\/td>\n<td>\n<p><u>0x33a02E6cC863D393d6Bf231B697b82F6e499cA71<\/u><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>RootChain<\/p>\n<\/td>\n<td>\n<p><u>0x536c55cFe4892E581806e10b38dFE8083551bd03<\/u><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>RootChainProxy<\/p>\n<\/td>\n<td>\n<p><u>0x86E4Dc95c7FBdBf52e33D563BbDB00823894C287<\/u><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>ValidatorShareFactory<\/p>\n<\/td>\n<td>\n<p><u>0xc4FA447A0e77Eff9717b09C057B40570813bb642<\/u><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>StakingInfo<\/p>\n<\/td>\n<td>\n<p><u>0xa59C847Bd5aC0172Ff4FE912C5d29E5A71A7512B<\/u><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>StakingNFT<\/p>\n<\/td>\n<td>\n<p><u>0x47Cbe25BbDB40a774cC37E1dA92d10C2C7Ec897F<\/u><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>StakeManager<\/p>\n<\/td>\n<td>\n<p><u>0xd6f5c46d4e1a02f9d145cee41d2f8af30d8d2d76<\/u><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>StakeManagerProxy<\/p>\n<\/td>\n<td>\n<p><u>0x5e3Ef299fDDf15eAa0432E6e66473ace8c13D908<\/u><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>SlashingManager<\/p>\n<\/td>\n<td>\n<p><u>0x01F645DcD6C796F6BC6C982159B32fAaaebdC96A<\/u><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>ValidatorShare<\/p>\n<\/td>\n<td>\n<p><u>0x01d5dc56ad4206bb0c132d834644d57f51fed5ec<\/u><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>StateSender<\/p>\n<\/td>\n<td>\n<p><u>0x28e4F3a7f651294B9564800b2D01f35189A5bFbE<\/u><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>DepositManager<\/p>\n<\/td>\n<td>\n<p><u>0xd505C3822C787D51d5C2B1ae9aDB943B2304eB23<\/u><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>DepositManagerProxy<\/p>\n<\/td>\n<td>\n<p><u>0x401F6c983eA34274ec46f84D70b31C151321188b<\/u><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>WithdrawManager<\/p>\n<\/td>\n<td>\n<p><u>0x017C89Ca4Bda3D66cC65E3d20DD95432258201Ca<\/u><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>ExitNFT<\/p>\n<\/td>\n<td>\n<p><u>0xDF74156420Bd57ab387B195ed81EcA36F9fABAca<\/u><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>WithdrawManagerProxy<\/p>\n<\/td>\n<td>\n<p><u>0x2A88696e0fFA76bAA1338F2C74497cC013495922<\/u><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>ERC20Predicate<\/p>\n<\/td>\n<td>\n<p><u>0x158d5fa3ef8e4dda8a5367decf76b94e7effce95<\/u><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>ERC721Predicate<\/p>\n<\/td>\n<td>\n<p><u>0x54150f44c785d412ec262fe895cc3b689c72f49b<\/u><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Child Chain: SECRYPT Mainnet<\/p>\n<table>\n<thead>\n<tr>\n<td>\n<p><strong>Contracts<\/strong><\/p>\n<\/td>\n<td>\n<p><strong>Address<\/strong><\/p>\n<\/td>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>\n<p>ChildChain<\/p>\n<\/td>\n<td>\n<p><u>0xD9c7C4ED4B66858301D0cb28Cc88bf655Fe34861<\/u><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><u>Edit this page<\/u><\/p>\n<p>\u00a0<\/p>\n<h4><strong>Data Tutorial for Decentralized Applications<\/strong><\/h4>\n<p>dApps, or decentralized applications, are apps built on the blockchain structure. There are different types of blockchains that applications can be built off of &#8211; some you might\u2019ve heard of are Ethereum and Solana.<\/p>\n<p>These primary networks are sometimes supplemented by\u00a0sidechains, or secondary blockchains, that run parallel to the primary blockchain. Sidechains, such as SECRYPT, allow for tokens and other digital assets to be used between multiple blockchains, greatly expanding the capabilities of primary blockchains and allowing use cases such as reduction of transaction fees, making primary blockchains more scalable, and having more optimized transaction traffic and capacity since sidechains only periodically update their root chain, while primary blockchains update every new block.<\/p>\n<p>The use cases for dApps are expanding by the day &#8211; due to its growing popularity, more and more apps are imagining how they can fit their application into the decentralized world. Just as with centralized apps, some of these dApps may still require the usage of private data for scenarios like authenticating identities or purchasing something that\u2019s being shipped to your physical address.<\/p>\n<p>Having private application data transmitted and stored on a public blockchain, while necessary for dApps, can end up causing huge privacy breaches from bad actors. So how can we protect private data from making its way into the public and immutable nature of the blockchain?<\/p>\n<h4><strong>Safeguarding your dApp data<\/strong><u><\/u><\/h4>\n<p>In this tutorial, we\u2019ll be building a simple allowlisting app that utilizes\u00a0React\u00a0for the UI and functionality, the\u00a0SECRYPT sidechain for transactions, and\u00a0Fauna\u00a0for storing private data from transactions that we may not necessarily want to surface publicly in the blockchain.\u00a0Allowlisting, also known as\u00a0whitelisting, is a concept that is very common in the decentralized world &#8211; when signing up for a allowlist, you typically gain access to special privileges, such as being able to be the first to purchase digital assets.<\/p>\n<p>GIF of app workflow &#8211; user fills out form, Metamask window pops up, and then once the transaction is submitted and confirmed, it surfaces a success message.<\/p>\n<p>The application will take in a first name, last name, and a wallet address. Typically, a lot of allowlists take in a wallet address only, but we\u2019ll be using the first and last name as a way to pass in some additional private information within the transaction. The flow of data will be as illustrated in the diagram:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/test.secrypt.tech\/wp-content\/uploads\/2023\/01\/e.png\" alt=\"\" width=\"1018\" height=\"210\" \/><\/p>\n<p>User submits their first name, last name, and wallet address successfully through a form. The new database entry is submitted to Fauna that has a randomly generated UUID, first name, last name, and wallet address. Net transaction is submitted to SECRYPT that passes in first name, last name, and wallet address as part of the transaction details, but each property equals the UUID generated for the Fauna database.<\/p>\n<h4><strong>What is Fauna?<\/strong><u><\/u><\/h4>\n<p><u>Fauna<\/u>\u00a0is a data API that offers a serverless database experience. With in-depth\u00a0<u>documentation<\/u>\u00a0and a web-native GraphQL interface, it allows developers to quickly get started with storing their application data without needing to sacrifice things such as flexibility, scale, and performance.<\/p>\n<p>In this tutorial, we\u2019ll be using Fauna as a method of storing private transaction data we may not want to surface on the blockchain explicitly. We\u2019ll use UUIDs populated in the private transaction data to map information from a blockchain transaction to a specific entry with the actual private transaction details in the Fauna database.<\/p>\n<h4><strong>Getting started<\/strong><u><\/u><\/h4>\n<p>In order to get started with building, follow these steps:<\/p>\n<ul>\n<li>Create a Fauna Account \u2014 you can sign up\u00a0<u>here<\/u>.<\/li>\n<li>Create a Metamask Wallet with SECRYPT\u2019s Test Network (SECRYPT-Testnet) configured.\n<ul>\n<li>First, get set up with Metamask\u00a0<u>here<\/u>. Make sure you save your Secret Recovery Phrase.<\/li>\n<li>Then, configure the SECRYPT-Testnet on your Metamask by following the instructions\u00a0<u>here<\/u>.<\/li>\n<\/ul>\n<\/li>\n<li>Once you have Metamask set up and configured for the SECRYPT-Testnet, you\u2019ll need some SXC to add to your wallet\n<ul>\n<li>SXC is the native cryptocurrency of the SECRYPT network and it\u2019s used to pay network fees, for staking, and also for governance to the SECRYPT blockchain (SXC holders can vote on SECRYPT changes). In the context of this project, you will need SXC to pay gas fees that are charged for each transaction. You can learn more about SXC\u00a0<u>here<\/u>.<\/li>\n<li>To get started, use the\u00a0<u>SXC faucet<\/u>\u00a0to get free SXC sent to your wallet (note: this SXC will only be available on the SECRYPT-Testnet and is only for development purposes). Once on the webpage, make sure the following options are selected:<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/test.secrypt.tech\/wp-content\/uploads\/2023\/01\/f-1024x979.png\" alt=\"\" width=\"1024\" height=\"979\" \/><\/p>\n<p>Network: SECRYPT, Select Token: SXC Token, Wallet Address: Put your wallet address here<\/p>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li>To find out what your wallet address is, you can pull it from Metamask:<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/test.secrypt.tech\/wp-content\/uploads\/2023\/01\/g.png\" alt=\"\" width=\"708\" height=\"720\" \/><\/p>\n<p>Go into Metamask from your browser. At the top under your Account Name, you\u2019ll see a long string. That\u2019s your account ID. Copy that ID by clicking on it.<\/p>\n<p><b>INFO<\/b><\/p>\n<p>This can take a while and you may have to try a few times before the first transaction comes through your wallet. Each successful transaction from the SXC faucet will give you 1 SXC.<\/p>\n<p>Since testing the application requires spending SXC as gas fees (a fluctuating fee that you must pay to perform transactions), we recommend doing this transaction ~12 times so you have about 12 SXC in your wallet, which should be enough for testing the app in development.<\/p>\n<p>To track your wallet\u2019s transactions, you can visit\u00a0<u>https:\/\/explorer-testnet.secrypt.tech\/address\/<\/u>[wallet address].<\/p>\n<ul>\n<li>Now, install\u00a0<u>Truffle<\/u>\u00a0\u2014 this is a suite of tools designed for developing Ethereum applications.\n<ul>\n<li>To see if you already have Truffle installed, you can run\u00a0truffle version\u00a0&#8211; if you get back a response that looks like this, you already have it installed:<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>$ truffle version<br \/>Truffle v5.4.29 (core: 5.4.29)<br \/>Solidity v0.5.16 (solc-js)<br \/>Node v13.8.0<br \/>Web3.js v1.5.3<\/p>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li>To install Truffle, run\u00a0npm -g truffle.<\/li>\n<\/ul>\n<\/li>\n<li>Also, if you don\u2019t already have it, install the tool to spin up a sample React app with\u00a0npm i create-react-app. Learn more about React\u00a0<u>here<\/u>.<\/li>\n<\/ul>\n<h4><strong>Start building<\/strong><u><\/u><\/h4>\n<p>If you\u2019re someone that prefers walking through code independently, the GitHub repo is available\u00a0<u>here<\/u>. To get started with the GitHub repo, download or clone it and reference the\u00a0<u>README<\/u>\u00a0to get set up.<\/p>\n<h4><strong>Setup<\/strong><u><\/u><\/h4>\n<ol>\n<li>Create a new directory for your project in your location of choice &#8211; we called ours\u00a0SECRYPT -fauna-app.<\/li>\n<li>Once the directory is created, in your command line,\u00a0cd\u00a0into the folder (ex:\u00a0cd SECRYPT -fauna-app).<\/li>\n<li>Once inside the directory, it\u2019s time to set up the app. Truffle makes it easier for us to build a decentralized application with a React front-end. To set up your initial project, run\u00a0truffle unbox react.\n<ol>\n<li>Once the command has run, if you were to run\u00a0ls\u00a0in your current directory, you\u2019d notice a variety of different subdirectories that weren\u2019t there before. The most important ones we\u2019ll be focusing on are:\n<ol>\n<li>client\u00a0&#8211; this is where your application, its primary functionality, and its front-end will live. Within client, the structure looks exactly like a regular React project.<\/li>\n<li>contracts\u00a0&#8211; this directory is where your smart contracts will live. Smart contracts are programs that will execute specified functionality within an account on the blockchain when the functionality is called. You might notice that the files in this directory end in\u00a0.sol\u00a0&#8211; this is because they are built with\u00a0<u>Solidity<\/u>, which is an object-oriented, high-level language for implementing smart contracts.<\/li>\n<li>migrations\u00a0&#8211; within this directory, there are preconfigured scripts that will allow you to deploy the smart contracts in\u00a0contracts\u00a0on the specified network, which will end up being the SECRYPT-Testnet network.<\/li>\n<\/ol>\n<\/li>\n<\/ol>\n<\/li>\n<\/ol>\n<p><b>NOTE<\/b><\/p>\n<p>If you use an IDE to code, now would be the perfect time to open your project in the IDE! We\u2019re going to start editing some files pretty soon.<\/p>\n<h4><strong>Smart contract configuration and functionality<\/strong><u><\/u><\/h4>\n<ol>\n<li>Currently, the project is pointed towards deploying on the Ethereum blockchain directory. We\u2019re going to need to update this to point to the SECRYPT-Testnet that we want to work off of.\n<ul>\n<li>To do this, first we\u2019ll need to run these commands in your command line to install some necessary packages:<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n<p>npm install @truffle\/hdwallet-provider<\/p>\n<p>and<\/p>\n<p>npm install dotenv<\/p>\n<ol>\n<li style=\"list-style-type: none;\">\n<ul>\n<li>Then, create a file called\u00a0.env\u00a0in the directory you\u2019re currently in. Within\u00a0.env, do the following:\n<ul>\n<li>Create an environment variable called\u00a0MNEMONIC\u00a0and make it equal to the Secret Recovery Phrase for your Metamask wallet &#8211; if you didn\u2019t write it down, you can follow\u00a0<u>this guide<\/u>\u00a0to reveal it once again.<\/li>\n<li>Create an environment variable called\u00a0RPC_APP_ID\u00a0and<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n<p>Your\u00a0.env\u00a0should look something like this:<\/p>\n<p>\u00a0 MNEMONIC=your secret phrase should go here<\/p>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li>Finally, replace the truffle-config.js file that was generated through Truffle with the following (note: it should be in the same directory as the client, contracts, and migrations folders):<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>const HDWalletProvider = require(&#8220;@truffle\/hdwallet-provider&#8221;);<br \/>const path = require(&#8220;path&#8221;);<br \/>require(&#8220;dotenv&#8221;).config(); <em>\/\/ Load .env file<\/em><\/p>\n<p>module.exports = {<br \/>\u00a0 <em>\/\/ See &lt;http:\/\/truffleframework.com\/docs\/advanced\/configuration&gt;<\/em><br \/>\u00a0 <em>\/\/ to customize your Truffle configuration!<\/em><br \/>\u00a0 contracts_build_directory: path.join(__dirname, &#8220;client\/src\/contracts&#8221;),<br \/>\u00a0 networks: {<br \/>\u00a0\u00a0\u00a0 develop: {<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 port: 8545,<br \/>\u00a0\u00a0\u00a0 },<br \/>\u00a0\u00a0\u00a0 SXC: {<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 provider: () =&gt;<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 new HDWalletProvider(<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 process.env.MNEMONIC,<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 `https:\/\/sxc-secrypt.chainstacklabs.com`<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ),<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 network_id: 80001,<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 confirmations: 2,<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 timeoutBlocks: 200,<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 skipDryRun: true,<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 gas: 6000000,<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 gasPrice: 10000000000,<br \/>\u00a0\u00a0\u00a0 },<br \/>\u00a0 },<br \/>};<\/p>\n<ol>\n<li>Once you\u2019ve set up your configuration, test it and make sure it points to your account.<\/li>\n<\/ol>\n<ul>\n<li>To test this, migrate your current smart contracts (Truffle has auto-populated some for you) by running\u00a0truffle migrate &#8211;network SXC. If it\u2019s not your first time running this\u00a0deploy\u00a0command for this project, you\u2019ll want to run\u00a0truffle migrate &#8211;network SXC &#8211;reset\u00a0so it runs a fresh copy of migrations and pulls the most recent code updates.<\/li>\n<\/ul>\n<p>FACING ERRORS?<\/p>\n<p>If you run into any issues, try the following things:<\/p>\n<ul>\n<li>Make sure your\u00a0MNEMONIC\u00a0environment variable is defined in a\u00a0.env\u00a0file in your root directory.<\/li>\n<li>If you are running into continuous errors and you\u2019ve checked the above, try other RPC URLs in place of the\u00a0<u>SXC-SECRYPT.chainstacklabs.com<\/u>\u00a0URL in\u00a0truffle-config.js. A list of additional URLs for the SECRYPT-Testnet can be found on\u00a0<u>this page<\/u>\u00a0under the\u00a0ECRYPT-Testnet\u00a0section.<\/li>\n<\/ul>\n<p>:::<\/p>\n<ul>\n<li>Then, test being able to connect to your new application by running the front-end. Truffle added some pre-configured logic that will allow you to test this connection. You can test it by running the following from your main directory:<\/li>\n<\/ul>\n<p>`cd client`<br \/>`npm run start`<\/p>\n<p><strong>NOTE<\/strong><\/p>\n<p>You will only be able to start your application inside the\u00a0client\u00a0folder.<\/p>\n<p>Your browser will launch a development instance on\u00a0<u>http:\/\/localhost:3000<\/u>\u00a0that will then prompt Metamask. It may ask you to re-auth your password. Finally, Metamask<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/test.secrypt.tech\/wp-content\/uploads\/2023\/01\/h-812x1024.png\" alt=\"\" width=\"812\" height=\"1024\" \/><\/p>\n<ul>\n<li>To go through with the transaction, click \u201cConfirm\u201d. The Metamask window will disappear and you should get a small \u201cTransaction Confirmed\u201d notification in your browser. To verify that your wallet has been connected successfully, you should see this page:<\/li>\n<\/ul>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/test.secrypt.tech\/wp-content\/uploads\/2023\/01\/i.png\" alt=\"\" width=\"960\" height=\"482\" \/><\/p>\n<p>Note that the stored value at the bottom of the page in this screenshot is the value 5. The original value is set to 0 when the page is initially loaded prior to authenticating your wallet. It will update to 5 once it is successfully connected.<\/p>\n<ol>\n<li>Now, we can get to work on updating your smart contract functionality.<\/li>\n<\/ol>\n<ul>\n<li>To do this, we\u2019re going to clean up the\u00a0contracts\u00a0directory in the root project directory. You can access it in your command line by running\u00a0cd ..\/contracts\u00a0from the\u00a0client\u00a0directory.<\/li>\n<li>We\u2019ll first delete the default\u00a0SimpleStorage.sol\u00a0file in the\u00a0contracts\u00a0directory.<\/li>\n<li>Then, we\u2019ll add a new smart contract for the specific allowlisting functionality. Within\u00a0contracts, create a new file called\u00a0Allowlist.sol\u00a0and paste in the following:<\/li>\n<\/ul>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/test.secrypt.tech\/wp-content\/uploads\/2023\/01\/j-1024x504.png\" alt=\"\" width=\"1024\" height=\"504\" \/><\/p>\n<p>\u00a0 pragma solidity ^0.5.0;<\/p>\n<p>\u00a0 contract Allowlist {<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 <em>\/\/ all are going to be set to uuid value in smart contract, so declare as same type<\/em><br \/>\u00a0\u00a0\u00a0 struct allowlister {<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 string f_name;<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 string l_name;<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 string wallet_address;<br \/>\u00a0\u00a0\u00a0 }<\/p>\n<p>\u00a0\u00a0\u00a0 allowlister[] allowlisters; <em>\/\/ array of all allowlisters<\/em><\/p>\n<p>\u00a0\u00a0\u00a0 function _createAllowlister (string memory _uuid) public {<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 allowlisters.push(allowlister({<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 f_name: _uuid,<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 l_name: _uuid,<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 wallet_address: _uuid<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 })) -1;<br \/>\u00a0\u00a0\u00a0 }<br \/>\u00a0 }<\/p>\n<p>Let\u2019s break this code down a bit:<\/p>\n<ul>\n<li>The smart contract,\u00a0Allowlist, contained an outline of what an allowlister (someone who submits an entry to the Allowlist) will look like. We know that we\u2019ll be taking in a first name, last name, and wallet address in the form, so we created a\u00a0struct\u00a0representing an allowlister with those properties. When we store the items in the contract, they\u2019ll each map to that submission\u2019s UUID, meaning that each variable type should align with whatever variable type that UUID will be.\n<ul>\n<li>Since the UUID will be a string that exceeds the length of other kinds of variable types in Solidity, we\u2019ll be assigning each property the type\u00a0string, which accepts longer lengths.<\/li>\n<\/ul>\n<\/li>\n<li>We\u2019ve also initialized an array of\u00a0allowlisters\u00a0that we can add to as users submit information in the form.<\/li>\n<li>Finally, we\u2019ve created a function,\u00a0_createAllowlister, that will be what we call when users submit the form. The function takes in the generated\u00a0_uuid\u00a0string for that entry (the underscore in the variable name is to differentiate the variable as a function parameter). Within the function, we will push a new\u00a0allowlister\u00a0instance to the\u00a0allowlisters\u00a0array &#8211; each property in the instance will equal the\u00a0_uuid\u00a0that is passed in.<\/li>\n<li>Then, go into the migrations folder in the root directory. We\u2019re going to hop into the 2_deploy_contracts.js file and update the functionality so it deploys the new contract<\/li>\n<\/ul>\n<p>var Allowlist = artifacts.require(&#8220;.\/Allowlist.sol&#8221;);<\/p>\n<p>module.exports = function(deployer) {<br \/>\u00a0 deployer.deploy(Allowlist);<br \/>};<\/p>\n<ul>\n<li>Finally, you will migrate your new contract by running\u00a0truffle migrate &#8211;network SXC &#8211;reset.<\/li>\n<\/ul>\n<h4><strong>App and functionality development<\/strong><u><\/u><\/h4>\n<p>Once you\u2019ve deployed your new smart contract, let\u2019s get started on building out the application.<\/p>\n<ol>\n<li>We\u2019ll be using the\u00a0react-hook-form\u00a0library to build a simple form experience. You\u2019ll need to install it by running\u00a0npm install react-hook-form.<\/li>\n<li>Next, create a new directory in\u00a0client\/src\u00a0titled\u00a0components.<\/li>\n<li>Within\u00a0components, create a file called\u00a0AllowlistForm.js\u00a0and paste in the following:<\/li>\n<\/ol>\n<p>import React, { useEffect } from &#8220;react&#8221;;<br \/>import { useForm } from &#8220;react-hook-form&#8221;;<\/p>\n<p>export default function AllowlistForm(props) {<br \/>\u00a0 const {<br \/>\u00a0\u00a0\u00a0 register,<br \/>\u00a0\u00a0\u00a0 handleSubmit,<br \/>\u00a0\u00a0\u00a0 setValue,<br \/>\u00a0\u00a0\u00a0 formState: { errors },<br \/>\u00a0 } = useForm();<\/p>\n<p>\u00a0 useEffect(() =&gt; {<br \/>\u00a0\u00a0\u00a0 register(&#8220;firstName&#8221;, { required: true });<br \/>\u00a0\u00a0\u00a0 register(&#8220;lastName&#8221;, { required: true });<br \/>\u00a0 \u00a0\u00a0register(&#8220;walletAddress&#8221;, { required: true });<br \/>\u00a0 }, [register]);<\/p>\n<p>\u00a0 async function submitForm(data) {<br \/>\u00a0\u00a0\u00a0 console.log(data);<br \/>\u00a0 }<\/p>\n<p>\u00a0 return (<br \/>\u00a0\u00a0\u00a0 &lt;div className=&#8221;wrapper&#8221;&gt;<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;form onSubmit={handleSubmit((data) =&gt; submitForm(data))}&gt;<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;div className=&#8221;header&#8221;&gt;<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;h1&gt;Allowlist Form&lt;\/h1&gt;<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;p&gt;<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Please fill out this form to get allowlisted for this exclusive<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 project.<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;\/p&gt;<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;\/div&gt;<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;label htmlFor=&#8221;firstName&#8221;&gt;First Name&lt;\/label&gt;<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;input<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 id=&#8221;firstName&#8221;<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 onChange={(e) =&gt; setValue(&#8220;firstName&#8221;, e.target.value)}<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/&gt;<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 {errors.firstName &amp;&amp; (<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;span role=&#8221;alert&#8221; className=&#8221;errorField&#8221;&gt;<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 First name is required.<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;\/span&gt;<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 )}<br \/>\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&lt;label htmlFor=&#8221;lastName&#8221;&gt;Last Name&lt;\/label&gt;<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;input<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 id=&#8221;lastName&#8221;<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 onChange={(e) =&gt; setValue(&#8220;lastName&#8221;, e.target.value)}<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/&gt;<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 {errors.lastName &amp;&amp; (<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;span role=&#8221;alert&#8221; className=&#8221;errorField&#8221;&gt;<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0Last name is required.<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;\/span&gt;<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 )}<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;label htmlFor=&#8221;walletAddress&#8221;&gt;Wallet Address&lt;\/label&gt;<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;input<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 id=&#8221;walletAddress&#8221;<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 onChange={(e) =&gt; setValue(&#8220;walletAddress&#8221;, e.target.value)}<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/&gt;<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 {errors.walletAddress &amp;&amp; (<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;span role=&#8221;alert&#8221; className=&#8221;errorField&#8221;&gt;<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Wallet address is required.<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;\/span&gt;<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 )}<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;input type=&#8221;submit&#8221; className=&#8221;submitButton&#8221; \/&gt;<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;\/form&gt;<br \/>\u00a0\u00a0\u00a0 &lt;\/div&gt;<br \/>\u00a0 );<br \/>}<\/p>\n<ul>\n<li>\n<p>The above code creates a component called\u00a0AllowlistForm\u00a0using the\u00a0react-hook-form\u00a0library installed earlier. The form allows a\u00a0firstName,\u00a0lastName, and\u00a0walletAddress\u00a0field, and also explicitly specifies what fields are required or not for error validation. The component also contains some error-handling logic for leaving required inputs blank and attempting to submit.<\/p>\n<ol>\n<li>Next, to make the form look better, create a file called\u00a0AllowlistForm.css\u00a0and add in the following CSS code:<\/li>\n<\/ol>\n<\/li>\n<\/ul>\n<p>h1 {<br \/>\u00a0 border-bottom: 1px solid white;<br \/>\u00a0 color: #3d3d3d;<br \/>\u00a0 font-family: sans-serif;<br \/>\u00a0 font-size: 20px;<br \/>\u00a0 font-weight: 600;<br \/>\u00a0 line-height: 24px;<br \/>\u00a0 text-align: center;<br \/>}<\/p>\n<p>.header {<br \/>\u00a0 margin-bottom: 10px;<br \/>}<\/p>\n<p>form {<br \/>\u00a0 background: white;<br \/>\u00a0 border: 1px solid #dedede;<br \/>\u00a0 display: flex;<br \/>\u00a0 flex-direction: column;<br \/>\u00a0 justify-content: space-around;<br \/>\u00a0 margin: 0 auto;<br \/>\u00a0 margin-top: 10px;<br \/>\u00a0 max-width: 500px;<br \/>\u00a0 padding: 30px 50px 0px;<br \/>}<\/p>\n<p>input {<br \/>\u00a0 border: 1px solid #d9d9d9;<br \/>\u00a0 border-radius: 4px;<br \/>\u00a0 box-sizing: border-box;<br \/>\u00a0 padding: 10px;<br \/>\u00a0 width: 100%;<br \/>\u00a0 margin-bottom: 10px;<br \/>}<\/p>\n<p>label {<br \/>\u00a0 color: #3d3d3d;<br \/>\u00a0 display: block;<br \/>\u00a0 font-family: sans-serif;<br \/>\u00a0 font-size: 14px;<br \/>\u00a0 font-weight: bold;<br \/>\u00a0 margin-bottom: 5px;<br \/>\u00a0 text-align: left;<br \/>}<\/p>\n<p>.errorField {<br \/>\u00a0 color: red;<br \/>\u00a0 font-family: sans-serif;<br \/>\u00a0 font-size: 12px;<br \/>\u00a0 margin-bottom: 10px;<br \/>\u00a0 text-align: left;<br \/>}<\/p>\n<p>.submitButton {<br \/>\u00a0 background-color: #6976d9;<br \/>\u00a0 color: white;<br \/>\u00a0 font-family: sans-serif;<br \/>\u00a0 font-size: 14px;<br \/>\u00a0 margin: 20px 0px;<br \/>}<\/p>\n<p><strong>TIP<\/strong><\/p>\n<p>To implement the form field, button, and input styling and color scheme, as well as to help outline form functionality, you can use\u00a0<u>Retool tutorial<\/u>\u00a0on\u00a0react-hook-form\u00a0as a reference.<\/p>\n<p><b>NOTE<\/b><\/p>\n<p>If you receive a warning about old stylesheets, this is probably due to a legacy version of\u00a0create-react-app\u00a0that Truffle is using to set up the React project. To mitigate this, you can update your\u00a0package.json&#8217;s\u00a0browserlist\u00a0property so it looks like this:<\/p>\n<p>&#8220;browserslist&#8221;: {<br \/>\u00a0\u00a0\u00a0 &#8220;production&#8221;: [<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 &#8220;&gt;0.3%&#8221;,<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 &#8220;not ie 11&#8221;,<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 &#8220;not dead&#8221;,<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 &#8220;not op_mini all&#8221;<br \/>\u00a0\u00a0\u00a0 ],<br \/>\u00a0\u00a0\u00a0 &#8220;development&#8221;: [<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 &#8220;last 1 chrome version&#8221;,<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 &#8220;last 1 firefox version&#8221;,<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 &#8220;last 1 safari version&#8221;,<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 &#8220;&gt;0.3%&#8221;,<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 &#8220;not ie 11&#8221;,<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 &#8220;not dead&#8221;,<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 &#8220;not op_mini all&#8221;<br \/>\u00a0\u00a0\u00a0 ]<br \/>\u00a0 }<\/p>\n<p>To wire up the styling with the component, add the following import statement at the top of your\u00a0AllowlistForm.js\u00a0component:<\/p>\n<p>import &#8220;.\/AllowlistForm.css&#8221;;<\/p>\n<p>Finally, we need to actually surface the form in our demo app. We\u2019ll do this by updating the App.js file in the client directory. Replace the current contents of App.js with the following:<\/p>\n<p>import React, { Component } from &#8220;react&#8221;;<br \/>import AllowlistContract from &#8220;.\/contracts\/Allowlist.json&#8221;;<br \/>import getWeb3 from &#8220;.\/getWeb3&#8221;;<br \/>import AllowlistForm from &#8220;.\/components\/AllowlistForm&#8221;;<\/p>\n<p>import &#8220;.\/App.css&#8221;;<\/p>\n<p>class App extends Component {<br \/>\u00a0 state = { storageValue: 0, web3: null, accounts: null, contract: null };<\/p>\n<p>\u00a0 componentDidMount = async () =&gt; {<br \/>\u00a0\u00a0\u00a0 try {<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 <em>\/\/ Get network provider and web3 instance.<\/em><br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 const web3 = await getWeb3();<\/p>\n<p>\u00a0\u00a0\u00a0\u00a0\u00a0 <em>\/\/ Use web3 to get the user&#8217;s accounts.<\/em><br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 const accounts = await web3.eth.getAccounts();<\/p>\n<p>\u00a0\u00a0\u00a0\u00a0\u00a0 <em>\/\/ Get the contract instance.<\/em><br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 const networkId = await web3.eth.net.getId();<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 const deployedNetwork = AllowlistContract.networks[networkId];<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 const instance = new web3.eth.Contract(<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 AllowlistContract.abi,<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 deployedNetwork &amp;&amp; deployedNetwork.address<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 );<\/p>\n<p>\u00a0\u00a0\u00a0\u00a0\u00a0 <em>\/\/ Set web3, accounts, and contract to the state, and then proceed with an<\/em><br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 <em>\/\/ example of interacting with the contract&#8217;s methods.<\/em><br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 this.setState({ web3, accounts, contract: instance });<br \/>\u00a0\u00a0\u00a0 } catch (error) {<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 <em>\/\/ Catch any errors for any of the above operations.<\/em><br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 alert(<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 `Failed to load web3, accounts, or contract. Check console for details.`<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 );<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 console.error(error);<br \/>\u00a0\u00a0\u00a0 }<br \/>\u00a0 };<\/p>\n<p>\u00a0 render() {<br \/>\u00a0\u00a0\u00a0 if (!this.state.web3) {<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 return &lt;div&gt;Loading Web3, accounts, and contract&#8230;&lt;\/div&gt;;<br \/>\u00a0\u00a0\u00a0 }<br \/>\u00a0\u00a0\u00a0 return (<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;div className=&#8221;App&#8221;&gt;<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;AllowlistForm<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 contract={this.state.contract}<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 accounts={this.state.accounts}<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/&gt;<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;\/div&gt;<br \/>\u00a0\u00a0\u00a0 );<br \/>\u00a0 }<br \/>}<\/p>\n<p>export default App;<\/p>\n<p>You might notice that some of this\u00a0App.js\u00a0logic is similar to what was originally set up by Truffle! Our new-and-improved\u00a0App.js\u00a0will do the following:<\/p>\n<ul>\n<li>On page load, initialize a web3 instance, any related accounts, and an instance of our\u00a0Allowlist\u00a0smart contract (pulled from\u00a0Allowlist.json, an instance of our contract that is generated from the previously run\u00a0truffle migrate\u00a0command). Within these initializations, we set corresponding state variables for the contract, account list, and web3 instances.<\/li>\n<li>When the page renders, if we\u2019re still fetching all of the web3 information, it\u2019ll show a\u00a0Loading message\u00a0rather than the form &#8211; this is determined by checking whether the web3 state variable has been set. Once that web3 state variable has been set, we return the\u00a0AllowlistForm\u00a0component and pass in our\u00a0contract\u00a0and\u00a0accounts\u00a0state variables &#8211; we\u2019ll need them within our component to write to the blockchain when we submit the form.<\/li>\n<li>To get a beautiful full-page lavender background like in the demo GIF, add the following at the bottom of your\u00a0App.css\u00a0file:<\/li>\n<\/ul>\n<p>html,<br \/>body {<br \/>\u00a0 width: 100%;<br \/>\u00a0 height: 100%;<br \/>\u00a0 background: lavender;<br \/>}<\/p>\n<p>When navigating to http:\/\/localhost:3000 now, your application will look like this:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/test.secrypt.tech\/wp-content\/uploads\/2023\/01\/j-1024x504.png\" alt=\"\" width=\"1024\" height=\"504\" \/><\/p>\n<p>At this point, you can test the functionality of your form &#8211; try to submit empty responses to see if the error messages pop up. You can also fill out each field with random strings to test successfully submitting &#8211; to determine if it was successful, check your console, and you should see your submitted form responses as they come through.<\/p>\n<h4><strong>Fauna setup and wiring<\/strong><u><\/u><\/h4>\n<p>Now that the application is set up, hop into Fauna and connect it to our application.<\/p>\n<ol>\n<li>Log into your Fauna account. You\u2019ll be taken to the main dashboard, where you\u2019ll create a database by clicking the \u201cCreate Database\u201d button. Create a database with a name of your choice and make sure to select the \u201cClassic\u201d Region Group:<\/li>\n<\/ol>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/test.secrypt.tech\/wp-content\/uploads\/2023\/01\/k.png\" alt=\"\" width=\"844\" height=\"842\" \/><\/p>\n<p>Once you create your database, you will be taken to the overview page.<\/p>\n<ol>\n<li>Within your newly created database, you\u2019re going to create a\u00a0<u>Collection<\/u>. Collections in Fauna are equivalent to the concept of a \u201ctable\u201d in a traditional database &#8211; a dedicated place to store entries that all have the same information and fields as one another. You\u2019ll click the \u201cNew Collection\u201d button and create a Collection with a name of your choosing:<\/li>\n<\/ol>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/test.secrypt.tech\/wp-content\/uploads\/2023\/01\/l-1024x571.png\" alt=\"\" width=\"1024\" height=\"571\" \/><\/p>\n<p>TIP<\/p>\n<p>Something super interesting about Fauna is that you don\u2019t actually create\u00a0columns, which, in a traditional database, are a way to structure entries that you insert in the table. The data you insert into Fauna determines the collection structure rather than the other way around.<\/p>\n<ol>\n<li>Once a database and a collection within it has been made, we need to generate a secret key to be able to access it in-app. To do so, go to the\u00a0Security\u00a0option in your database and click\u00a0New Key. Create a new key with the following settings and give it a name of your choosing:<\/li>\n<\/ol>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/test.secrypt.tech\/wp-content\/uploads\/2023\/01\/m-1024x458.png\" alt=\"\" width=\"1024\" height=\"458\" \/><\/p>\n<p>Click\u00a0Save\u00a0and copy the Secret that appears above the list of keys. Then, create a\u00a0.env\u00a0file in your project within client &#8211; this is a React-specific environment file that the frontend will read from. Add a\u00a0REACT_APP_FAUNADB_SECRET\u00a0environment variable and set it equal to the Secret that you copied when generating the API key. Your\u00a0.env\u00a0should look like this:<\/p>\n<p>REACT_APP_FAUNADB_SECRET=your secret from fauna goes here<\/p>\n<p><b>NOTE<\/b><\/p>\n<p>For React\u00a0.env\u00a0files, all custom environment variables MUST start with\u00a0REACT_APP_\u00a0in order for React to recognize them.<\/p>\n<ol>\n<li>Now that we have a collection ready in Fauna and an API key generated from the database it lives in, we can start writing to Fauna.<\/li>\n<\/ol>\n<ul>\n<li>In your project directory, run\u00a0npm install &#8211;save faunadb\u00a0to install\u00a0faunadb\u00a0locally.<\/li>\n<li>Next, in\u00a0client\/src, create an\u00a0api\u00a0folder.<\/li>\n<li>Within the\u00a0api\u00a0folder, create a file called\u00a0fauna.js, which is going to be how we talk within our application to the Fauna API. Your\u00a0fauna.js\u00a0file should look like this:<\/li>\n<\/ul>\n<p>require(&#8220;dotenv&#8221;).config(); <em>\/\/ Load .env file<\/em><br \/>export async function addDocument(uuid, firstName, lastName, walletAddress) {<br \/>\u00a0 const faunadb = require(&#8220;faunadb&#8221;);<br \/>\u00a0 const q = faunadb.query;<br \/>\u00a0 const client = new faunadb.Client({<br \/>\u00a0\u00a0\u00a0 secret: process.env.REACT_APP_FAUNADB_SECRET,<br \/>\u00a0\u00a0\u00a0 domain: &#8220;db.fauna.com&#8221;,<br \/>\u00a0\u00a0\u00a0 scheme: &#8220;https&#8221;,<br \/>\u00a0 });<br \/>\u00a0 var response = client.query(<br \/>\u00a0\u00a0\u00a0 q.Create(q.Collection(&#8220;allowlist_members&#8221;), {<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 data: {<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 uuid: uuid,<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 f_name: firstName,<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 l_name: lastName,<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 wallet_address: walletAddress,<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 },<br \/>\u00a0\u00a0\u00a0 })<br \/>\u00a0 );<\/p>\n<p>\u00a0 return response;<br \/>}<\/p>\n<p>The\u00a0fauna.js\u00a0starts by pulling the environment variable set in the\u00a0client\/.env. Then, it declares an\u00a0addDocument\u00a0function that takes in a\u00a0uuid\u00a0(which we will generate programmatically in a section below), and the form-inputted\u00a0firstName,\u00a0lastName, and\u00a0walletAddress. Within the function, it initializes an instance of a Fauna API client utilizing the secret stores in the\u00a0.env, which specifically will point Fauna to the database you created.<\/p>\n<p>Finally, the function actually queries the API client to add a new item, called a Document, to the collection you created (in this code example it is pulling from a collection called\u00a0allowlist_members- you\u2019ll need to change this to the name you chose for your own collection).<\/p>\n<p>Finally, the API response is returned once the query is executed.<\/p>\n<p>TIP<\/p>\n<p>The\u00a0faunadb\u00a0<u>package website<\/u>\u00a0is a great resource for insight into configuring and connecting Fauna to the app!<\/p>\n<p><strong>Submit form responses to Fauna<\/strong><u><\/u><\/p>\n<p>With the Fauna API client added in and a way to add new items to our collection, let\u2019s wire it up to our form!<\/p>\n<p>If you recall from the user flow, we want the following things to happen when someone submits the form:<\/p>\n<ol>\n<li>All the information is sent to the blockchain, but the private values are replaced with generated UUIDs.<\/li>\n<li>Those private values (first name, last name, and wallet address) are instead sent to Fauna and stored next to that UUID.<\/li>\n<\/ol>\n<p>Now that we have the functionality in place to send information to Fauna, let\u2019s use it to accomplish goal #2.<\/p>\n<ol>\n<li>Add the following import at the top of your\u00a0AllowlistForm.js\u00a0file:<\/li>\n<\/ol>\n<p>`import { *addDocument* } from &#8220;..\/api\/fauna&#8221;;`<\/p>\n<ol>\n<li>Then, inside the\u00a0submitForm\u00a0function in\u00a0AllowlistForm.js, replace the\u00a0console.log\u00a0statement with the following:<\/li>\n<\/ol>\n<p><em>\/\/ add data to Fauna<\/em><br \/>const uuid = crypto.randomUUID();<br \/>await addDocument(uuid, data.firstName, data.lastName, data.walletAddress)<br \/>\u00a0 .then((res) =&gt; {<br \/>\u00a0\u00a0\u00a0 console.log(res);<br \/>\u00a0 }<br \/>)<\/p>\n<p>In this snippet, we are programmatically generating a UUID using the built-in\u00a0crypto.randomUUID()\u00a0<u>function<\/u>, which will generate a 36-character-long v4 UUID. Once the UUID has been generated, we call the\u00a0addDocument\u00a0function we just added. Within\u00a0addDocument, we\u2019re passing information from the form submission (captured in\u00a0data, which is being initially passed into\u00a0submitForm\u00a0&#8211;\u00a0data.firstName,\u00a0data.lastName, and\u00a0data.walletAddress) as well as the generated UUID. Finally, the API response is logged to the console, which will help us determine if the API call successfully went through.<\/p>\n<ol>\n<li>At this point, your form is now configured to send information to Fauna when submitting a fully filled out form. You can test this by filling out each field in the form and submitting it. It should log an object in the console with a\u00a0data\u00a0property inside it that shows all the information submitted to Fauna. To confirm that it successfully submitted, you can pull up your collection in Fauna and see if the entry is there. It should look something like this:<\/li>\n<\/ol>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/test.secrypt.tech\/wp-content\/uploads\/2023\/01\/n-1024x647.png\" alt=\"\" width=\"1024\" height=\"647\" \/><\/p>\n<h4><strong>Handling UUID collisions<\/strong><u><\/u><\/h4>\n<p>Our application is now sending responses to Fauna.\u00a0However, since we\u2019re using a randomly generated UUID, there is a chance that we could have duplicated UUID entries in our database. We want to make sure that before we store any information in Fauna that there are no other UUIDs that match the one we\u2019ve generated, and if there are, we\u2019ll need to generate a new one.<\/p>\n<ol>\n<li>In order to search the data we\u2019ve been populating into our Fauna collection, we can use an\u00a0<u>Index<\/u>. Indexes in Fauna allow you to browse and easily search the data stored in your collections. To do this, you can go to your collection in Fauna and click the \u201cNew Index\u201d button.<\/li>\n<li>You will be taken to a page to create a new index &#8211; make sure the Source Collection field has correctly populated the collection you\u2019re trying to add to. Indexes are used for querying, so try to name the index something that specifically captures what it\u2019s going to do &#8211; since the index will be used to query entries by UUID, we chose\u00a0allowlist_members_by_uuid. To specify what properties you\u2019ll be querying by, add\u00a0data.uuid\u00a0into the Terms field &#8211; this lets Fauna know to pull items based on the UUID. Lastly, since you do want the UUID to be unique, check the \u201cUnique\u201d option and also make sure the \u201cSerialized\u201d option also stays checked:<\/li>\n<\/ol>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/test.secrypt.tech\/wp-content\/uploads\/2023\/01\/o-1024x976.png\" alt=\"\" width=\"1024\" height=\"976\" \/><\/p>\n<ol>\n<li>Once the index has been created, we can now query it from Fauna. To do this, we\u2019ll need to hop into our client\/src\/api\/fauna.js file and add a new function, findUUID, to search by index when given a UUID passed in as a parameter:<\/li>\n<\/ol>\n<p>export async function findUUID(uuid) {<br \/>\u00a0 const faunadb = require(&#8220;faunadb&#8221;);<br \/>\u00a0 const q = faunadb.query;<br \/>\u00a0 const client = new faunadb.Client({<br \/>\u00a0\u00a0\u00a0 secret: process.env.REACT_APP_FAUNADB_SECRET,<br \/>\u00a0\u00a0\u00a0 domain: &#8220;db.fauna.com&#8221;,<br \/>\u00a0\u00a0\u00a0 scheme: &#8220;https&#8221;,<br \/>\u00a0 });<br \/>\u00a0 client<br \/>\u00a0\u00a0\u00a0 .query(q.Paginate(q.Match(q.Index(&#8220;allowlist_members_by_uuid&#8221;), uuid)))<br \/>\u00a0\u00a0\u00a0 .then((res) =&gt; {<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 if (res.data.length === 0) {<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return false;<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 } else {<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return true;<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 }<br \/>\u00a0\u00a0\u00a0 });<br \/>}<\/p>\n<p>Very similarly to the\u00a0addDocuments\u00a0function, we need to do some initial setup and configurations with the Fauna client in this function. Then, we\u2019re calling a query that looks into a specific index (in this case\u00a0allowlist_members_by_uuid\u00a0) and sees if there are any matching items that have the\u00a0uuid\u00a0passed in as a function argument. In the event that there are no matches, an empty array is returned, so the function then checks for an empty array and returns\u00a0false\u00a0(indicating the UUID has not been found and is therefore not taken) &#8211; otherwise, it\u2019ll return\u00a0true\u00a0(indicating the UUID has a match).<\/p>\n<ol>\n<li>Now that we have a way to check for existing UUIDs, we can add this in to our\u00a0submitForm\u00a0function in our\u00a0AllowlistForm.js\u00a0component.\n<ul>\n<li>To do this, we\u2019ll add the\u00a0findUUID\u00a0function in to the\u00a0..\/api\/fauna\u00a0import statement at the top of the file like so:<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n<p>import { addDocument, findUUID } from &#8220;..\/api\/fauna&#8221;;<\/p>\n<ol>\n<li style=\"list-style-type: none;\">\n<ul>\n<li>Replace the const uuid = crypto.randomUUID(); line with the following logic:<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n<p><em>\/\/ generate uuid<\/em><br \/>let uuid = &#8220;&#8221;;<br \/><em>\/\/ check for duplicate uuids in db<\/em><br \/>while (true) {<br \/>\u00a0 const generatedUUID = crypto.randomUUID();<br \/>\u00a0 const didFindUUID = await findUUID(generatedUUID);<br \/>\u00a0 if (!didFindUUID) {<br \/>\u00a0\u00a0\u00a0 uuid = generatedUUID;<br \/>\u00a0\u00a0\u00a0 break;<br \/>\u00a0 }<br \/>}<\/p>\n<p>With the above code, we\u2019re initializing a UUID variable. Then, we\u2019re diving into an infinite loop that will run until it is manually broken out of &#8211; within the loop, a UUID is generated using&nbsp;crypto.randomUUID(). The generated UUID is then passed into a call to the&nbsp;findUUID&nbsp;function, which will return either true or false to the variable&nbsp;didFindUUID&nbsp;once the function call is complete. If&nbsp;didFindUUID&nbsp;returns false, meaning there are no matches in the database for this generated UUID, then the UUID variable is set to the generated UUID and the loop is broken out of. Otherwise, the entire process is repeated until a non-duplicate UUID is identified.<\/p>\n<h4><strong>Submit form responses to the blockchain<\/strong><u><\/u><\/h4>\n<p>The final step in our application is to write to the blockchain using our smart contract. As we mentioned above, we want the generated UUID that we store in Fauna to replace all of the actual private data (in this case, the first name, last name, and wallet address submitted). This adds a layer of security to the private data &#8211; it\u2019s not publicly available on the blockchain, only the UUID is, and you\u2019d need access to the Fauna database to be able to see the information behind that UUID.<\/p>\n<ol>\n<li>Since we want the form to submit to the blockchain only if the Fauna API call is successful (and only once it happens), we can nest the API call to add a new allowlister within the Fauna API call where we add a new document. We\u2019ll do this in the&nbsp;submitForm&nbsp;function by replacing the&nbsp;console.log(res)&nbsp;line so that it calls our&nbsp;_createAllowlister&nbsp;method from our smart contract:<\/li>\n<\/ol>\n<p>async function submitForm(data) {<br \/>\u00a0 <em>\/\/ generate uuid<\/em><br \/>\u00a0\u00a0\u00a0 let uuid = &#8220;&#8221;;<br \/>\u00a0 <em>\/\/ check for duplicate uuids in db<\/em><br \/>\u00a0 while (true) {<br \/>\u00a0\u00a0\u00a0 const generatedUUID = crypto.randomUUID();<br \/>\u00a0\u00a0\u00a0 const didFindUUID = await findUUID(generatedUUID);<br \/>\u00a0\u00a0\u00a0 if (!didFindUUID) {<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 uuid = generatedUUID;<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 break;<br \/>\u00a0\u00a0\u00a0 }<br \/>\u00a0 }<br \/>\u00a0 <em>\/\/ add data to Fauna<\/em><br \/>\u00a0 await addDocument(uuid, data.firstName, data.lastName, data.walletAddress)<br \/>\u00a0\u00a0\u00a0 .then((res) =&gt; {<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 <em>\/\/ add data to contract<\/em><br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 props.contract.methods<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ._createAllowlister(uuid)<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 .send({ from: props.accounts[0] })<br \/>\u00a0\u00a0\u00a0 }<br \/>\u00a0 )<br \/>}<\/p>\n<p>Since we passed the&nbsp;Allowlist&nbsp;smart contract and all available accounts into our&nbsp;AllowlistForm&nbsp;component, we\u2019re able to access them to pull information from our smart contract. Since they\u2019re passed in as props, we\u2019re referencing them as&nbsp;props.contract&nbsp;and&nbsp;props.accounts&nbsp;rather than&nbsp;contract&nbsp;and&nbsp;accounts.<\/p>\n<ol>\n<li>Once that has been added in, you can see if your application is successfully submitting to Fauna and the blockchain by filling out the form in your application. A Metamask window should pop up, and if you click \u201cAccept\u201d, you\u2019ll receive a notification after a few seconds that the transaction was successfully submitted. You can also check for the newly added document in your Fauna collection to make sure it\u2019s writing to both Fauna and the blockchain.<\/li>\n<\/ol>\n<h4><strong>Read transaction data from the blockchain and lookup in Fauna<\/strong><u><\/u><\/h4>\n<p>Now that we\u2019re able to write transactions to Fauna and then the blockchain, how can we actually find the data we need from a transaction we\u2019ve submitted?<\/p>\n<p>Luckily, the public nature of the blockchain makes it easy to track down transactions and also find the metadata associated with a given transaction.<\/p>\n<ol>\n<li>We\u2019ll be using the SECRYPT explorer tool to find all the transactions associated with the wallet we\u2019ve used to develop the application. This tool allows you to look up any wallet on the SECRYPT-Testnet by its address and see a history of all of its transactions as well as the amount of SXC inside the wallet.\n<ul>\n<li>To find your wallet, you can go to this url:&nbsp;<u>https:\/\/explorer-testnet.secrypt.tech\/[<\/u>put your wallet address here]<\/li>\n<\/ul>\n<\/li>\n<li>Once you navigate to this page, you\u2019ll see a list of all of the transactions associated with your wallet. The transactions display from most to least recent.<\/li>\n<\/ol>\n<p>Click on a transaction to see more information by clicking on the transaction hash &#8211; this hash is uniquely generated and is what identifies your transaction.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/test.secrypt.tech\/wp-content\/uploads\/2023\/01\/p-1024x338.png\" alt=\"\" width=\"1024\" height=\"338\"><\/p>\n<p>When you click on the transaction hash, you\u2019ll go to a page that lists the transaction\u2019s specific details. To grab the information you\u2019ll need, click the \u201cClick to see more\u201d option near the bottom of the displayed transaction information.<\/p>\n<p>Within the \u201cClick to see more\u201d option, you\u2019ll see a lot more information. We\u2019re going to scroll to the \u201cInput Data\u201d section, where there will be a long hash.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/test.secrypt.tech\/wp-content\/uploads\/2023\/01\/q-1024x340.png\" alt=\"\" width=\"1024\" height=\"340\"><\/p>\n<p>We can write a script to parse the transaction hash for the Input Data, so that we can turn that hash into actual data that we can pull our&nbsp;uuid&nbsp;from to be able to query in Fauna.<\/p>\n<ol>\n<li>To decode this, we can use the&nbsp;<u>abi-decoder library<\/u>. In the&nbsp;client&nbsp;directory of your project, run the following to install the library:<\/li>\n<\/ol>\n<p>npm install abi-decoder<\/p>\n<ol>\n<li>Then, create a folder named&nbsp;scripts&nbsp;in the&nbsp;client&nbsp;directory.<\/li>\n<li>Within the&nbsp;scripts&nbsp;folder, create a file called&nbsp;decode-transaction.js&nbsp;and paste the following information in:<\/li>\n<\/ol>\n<p>const abiDecoder = require(&#8216;abi-decoder&#8217;);<\/p>\n<p><em>\/\/ pulled from Allowlist.json &#8220;abi&#8221; field<\/em><br \/>const testABI = [<br \/>\u00a0\u00a0\u00a0 {<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 &#8220;constant&#8221;: false,<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 &#8220;inputs&#8221;: [<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 {<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &#8220;internalType&#8221;: &#8220;string&#8221;,<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &#8220;name&#8221;: &#8220;_uuid&#8221;,<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &#8220;type&#8221;: &#8220;string&#8221;<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 ],<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 &#8220;name&#8221;: &#8220;_createAllowlister&#8221;,<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 &#8220;outputs&#8221;: [],<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 &#8220;payable&#8221;: false,<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 &#8220;stateMutability&#8221;: &#8220;nonpayable&#8221;,<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 &#8220;type&#8221;: &#8220;function&#8221;<br \/>\u00a0\u00a0\u00a0 }<br \/>];<br \/>abiDecoder.addABI(testABI);<\/p>\n<p><em>\/\/ add in your input data hash as a string here<\/em><br \/>const testData = &#8220;&#8221;;<br \/>const decodedData = abiDecoder.decodeMethod(testData);<br \/>console.log(decodedData);<\/p>\n<p>The above code will create an instance of the abi-decoder library so it can be used, storing it in the variable\u00a0abiDecoder. In order to read the Input Data hash, the library requires an ABI, or Application Binary Interface, to tell the decoder what information, such as functions and arguments, is in the smart contract being used for the transaction. The ABI will help inform the decoder what information can be parsed from the hash.<\/p>\n<p>If you used the same smart contract from this tutorial in your application, then the ABI has already been populated for you in the\u00a0testABI\u00a0variable. This ABI was taken from the smart contract metadata for\u00a0Allowlist.sol\u00a0that was generated upon migration. The metadata can be found in\u00a0client\/contracts\/Allowlist.json. Within this file, there is an\u00a0abi\u00a0property, representing the\u00a0Allowlist\u00a0contract ABI, which is where the\u00a0testABI\u00a0value was pulled from.<\/p>\n<p>The final touch for this program to run will be to add in the input data hash for the transaction you want to decode. Copy the Input Data you found from the SECRYPT Explorer website and set it as a string equal to\u00a0testData.<\/p>\n<ol>\n<li>Once all information has been added in to the script, we can run it in the command line to see the output. Run the script from the\u00a0client\u00a0folder using this command:<\/li>\n<\/ol>\n<p>\u00a0\u00a0\u00a0 node scripts\/decode-transaction.js<\/p>\n<p>You\u2019ll receive a response back that looks like this:<\/p>\n<p>{<br \/>\u00a0 name: &#8216;_createAllowlister&#8217;,<br \/>\u00a0 params: [<br \/>\u00a0\u00a0\u00a0 {<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 name: &#8216;_uuid&#8217;,<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 value: &#8216;2bb542ef-39b7-4991-bfa8-aac848fdce39&#8217;,<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 type: &#8216;string&#8217;<br \/>\u00a0\u00a0\u00a0 }<br \/>\u00a0 ]<br \/>}<\/p>\n<p>This response indicates that the transaction was initialized by the\u00a0_createAllowlister\u00a0function from the smart contract and that the\u00a0_uuid\u00a0value was passed in as a parameter (as well as what that value was).<\/p>\n<p>Our\u00a0uuid\u00a0has been successfully identified from the transaction &#8211; in this particular response, it\u2019s\u00a02bb542ef-39b7-4991-bfa8-aac848fdce39.<\/p>\n<ol>\n<li>Now that we have the\u00a0uuid, we can search in Fauna using it to find the data behind the transaction. To do so, navigate to your\u00a0<u>Fauna Dashboard<\/u>, click on the Database you used for your application, and then click on \u201cIndexes\u201d in the sidebar for that database. Make sure that the index you used for the app is displaying at the top of the page &#8211; if not, then select it from the list of indexes below to ensure you\u2019re querying from the right one.<\/li>\n<\/ol>\n<p>Once you have the right index pulled up, paste your UUID into the field under \u201cdata.uuid\u201d and make sure the dropdown above the field says \u201cString\u201d.<\/p>\n<p>When you click the search button, an entry should appear below the search field:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/test.secrypt.tech\/wp-content\/uploads\/2023\/01\/r-1024x685.png\" alt=\"\" width=\"1024\" height=\"685\" \/><\/p>\n<p>Once you expand the entry, you\u2019ll see all of the original data passed in to the form for that specific transaction, such as the first name, last name, and wallet address.<\/p>\n<h4><strong>Error and success messaging<\/strong><u><\/u><\/h4>\n<p>We\u2019ve written a React form application that stores its data directly in Fauna and then submits it as a transaction to the blockchain where its private details are stored as a UUID, which can be used to reference the record in Fauna. We also showed how to take a transaction from the blockchain, parse its input data, and use it to search up the records in Fauna.<\/p>\n<p>However, while working through form submission and testing everything in the application, you might\u2019ve noticed it was sometimes difficult to tell when a record was successfully submitted or not. To mitigate this, we can add in some error and success messaging that will surface to indicate an errored form submission (either from a Fauna or SECRYPT issue) or a successful form submission. This part of the tutorial will focus on changing some API call logic in\u00a0AllowlistForm.js.<\/p>\n<p>Error Messaging<\/p>\n<p>Ideally, we\u2019d want to catch when the Fauna API throws an error, and we\u2019d also want to catch when interacting with the blockchain throws an error &#8211; either of these scenarios indicates that the transaction won\u2019t go all the way through. We also want to make sure that if Fauna fails, it doesn\u2019t still try to interact with the blockchain &#8211; this could create inconsistency in the entries.<\/p>\n<ol>\n<li>To do this, let\u2019s add a state variable to track whether or not the form submission has failed at any point. To initialize new state variables, we\u2019ll need to start by importing the\u00a0useState\u00a0hook, which can be added in where we\u2019re importing\u00a0React\u00a0and\u00a0useEffect:<\/li>\n<\/ol>\n<p>import React, { useEffect, useState } from &#8220;react&#8221;;<\/p>\n<ol>\n<li>Once imported, we can initialize our variable. We\u2019ll call it\u00a0formField\u00a0&#8211; this will be added in after the\u00a0useForm()\u00a0usage at the top of the component:<\/li>\n<\/ol>\n<p>const {<br \/>\u00a0\u00a0\u00a0 register,<br \/>\u00a0\u00a0\u00a0 handleSubmit,<br \/>\u00a0\u00a0\u00a0 setValue,<br \/>\u00a0\u00a0\u00a0 formState: { errors },<br \/>} = useForm(); <em>\/\/ this is already in the code, add the line below<\/em><br \/>const [formFail, setFormFail] = useState(false);<\/p>\n<p>To initialize a new state variable, we need to declare a name (formFail) and also a function to set it (setFormFail). We also need to set it to a default value (since\u00a0formFail\u00a0will be a boolean indicating whether or not the form has failed, we can set the default value to\u00a0false).<\/p>\n<ol>\n<li>Now, we need to make sure that we add in error messaging that uses this state variable. If\u00a0formFail\u00a0becomes\u00a0true, we\u2019d want to surface the error messaging. We can add in this snippet of\u00a0jsx\u00a0within the\u00a0wrapper\u00a0div class in the\u00a0return()\u00a0function at the bottom of the file:<\/li>\n<\/ol>\n<p>{formFail &amp;&amp; (<br \/>\u00a0 &lt;div className=&#8221;errorMessage&#8221;&gt;<br \/>\u00a0\u00a0\u00a0 &lt;p&gt;Failed to submit allowlist entry. Please try again.&lt;\/p&gt;<br \/>\u00a0 &lt;\/div&gt;<br \/>)}<\/p>\n<ol>\n<li>In addition to adding the error message block, we need to add some styling for the error message in\u00a0AllowlistForm.css:<\/li>\n<\/ol>\n<p>\u00a0 .errorMessage {<br \/>\u00a0\u00a0\u00a0 background-color: #fe6f5e;<br \/>\u00a0\u00a0\u00a0 border: solid 1px;<br \/>\u00a0\u00a0\u00a0 border-color: #000000;<br \/>\u00a0\u00a0\u00a0 margin-bottom: 10px;<br \/>\u00a0\u00a0\u00a0 margin: 10px;<br \/>\u00a0 }<\/p>\n<ol>\n<li>Finally, we need to actually set\u00a0formFail\u00a0to\u00a0true\u00a0in areas where the form fails. To do this, we\u2019ll hop back into\u00a0AllowlistForm.js\u00a0and update our\u00a0submitForm\u00a0function\u2019s API calls to Fauna and the blockchain so that they have\u00a0.catch()\u00a0statements:<\/li>\n<\/ol>\n<p>async function submitForm(data) {<br \/>\u00a0\u00a0\u00a0 &#8230; <em>\/\/ some logic exists here<\/em><br \/>\u00a0\u00a0\u00a0 <em>\/\/ update this snippet that is already within submitForm()<\/em><br \/>\u00a0\u00a0\u00a0 await addDocument(uuid, data.firstName, data.lastName, data.walletAddress)<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 .then((res) =&gt; {<br \/>\u00a0\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0if (!res.ok &amp;&amp; res.status &gt;= 400) {<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 setFormFail(true);<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return;<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 } else {<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 <em>\/\/ add data to contract<\/em><br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 props.contract.methods<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ._createAllowlister(uuid)<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 .send({ from: props.accounts[0] })<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 .catch(() =&gt; {<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 setFormFail(true);<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return;<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 });<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 })<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 .catch(() =&gt; {<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 setFormFail(true);<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return;<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 });<br \/>\u00a0\u00a0\u00a0 }<br \/>}<\/p>\n<p>In addition to adding\u00a0.catch\u00a0statements to each API call (addDocument\u00a0and\u00a0_createAllowlister), the code above also adds a block that checks for a scenario where Fauna may not throw an error, but does return an error response (meaning that the API does not return a\u00a0res.ok\u00a0and that the\u00a0res.status\u00a0is greater than or equal to a\u00a0400\u00a0response code, which indicates an error).<\/p>\n<p>Your error messaging will look like this:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/test.secrypt.tech\/wp-content\/uploads\/2023\/01\/s-1024x219.png\" alt=\"\" width=\"1024\" height=\"219\" \/><\/p>\n<p>Success Messaging<\/p>\n<ul>\n<li>Let\u2019s say we want a success message that surfaces what we submitted in the form within the message &#8211; to do so, we\u2019ll need to store what we submitted in the form (first name, last name, and wallet address, which are all strings) as state variables, as well as one to track a successful form submission. To do so, add the following state variable initializations into your code:<\/li>\n<\/ul>\n<p>const [firstName, setFirstName] = useState(&#8220;&#8221;);<br \/>const [lastName, setLastName] = useState(&#8220;&#8221;);<br \/>const [walletAddress, setWalletAddress] = useState(&#8220;&#8221;);<br \/>const [formSuccess, setFormSuccess] = useState(false);<\/p>\n<ul>\n<li>Within the\u00a0wrapper\u00a0div in the\u00a0return()\u00a0in\u00a0AllowlistForm.js, just as you added a\u00a0jsx\u00a0block for the error message, add one for the success message above or below it. The message will surface the form input-related state variables:<\/li>\n<\/ul>\n<p>{formSuccess &amp;&amp; (<br \/>\u00a0 &lt;div className=&#8221;successMessage&#8221;&gt;<br \/>\u00a0\u00a0\u00a0 &lt;p&gt;<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 Successfully submitted allowlist entry for{&#8221; &#8220;}<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 {firstName + &#8221; &#8221; + lastName} with wallet address {walletAddress}!<br \/>\u00a0\u00a0\u00a0 &lt;\/p&gt;<br \/>\u00a0 &lt;\/div&gt;<br \/>)}<\/p>\n<ul>\n<li>Add some styling for the success message in\u00a0AllowlistForm.css:<\/li>\n<\/ul>\n<p>.successMessage {<br \/>\u00a0 background-color: #e2fee2;<br \/>\u00a0 border: solid 1px;<br \/>\u00a0 border-color: #000000;<br \/>\u00a0 margin-bottom: 10px;<br \/>\u00a0 margin: 10px;<br \/>}<\/p>\n<ul>\n<li>Finally, update all of your newly added state variables in a place where, after the API calls are executed, the form is considered fully submitted:<\/li>\n<\/ul>\n<p>async function submitForm(data) {<br \/>\u00a0\u00a0\u00a0 &#8230; <em>\/\/ some logic exists here<\/em><br \/>\u00a0 await addDocument(uuid, data.firstName, data.lastName, data.walletAddress)<br \/>\u00a0\u00a0\u00a0 .then((res) =&gt; {<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 if (!res.ok &amp;&amp; res.status &gt;= 400) {<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 setFormFail(true);<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return;<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 } else {<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 <em>\/\/ add data to contract<\/em><br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0\u00a0props.contract.methods<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ._createAllowlister(uuid)<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 .send({ from: props.accounts[0] })<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 .then(() =&gt; {<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 <em>\/\/ update this snippet that is already within submitForm()<\/em><br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 setFirstName(data.firstName);<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 setLastName(data.lastName);<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 setWalletAddress(data.walletAddress);<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 setFormSuccess(true);<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 })<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 .catch(() =&gt; {<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 setFormFail(true);<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return;<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 });<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 }<br \/>\u00a0\u00a0\u00a0 })<br \/>\u00a0\u00a0\u00a0 .catch(() =&gt; {<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 setFormFail(true);<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0 return;<br \/>\u00a0\u00a0\u00a0 });<br \/>}<\/p>\n<p>The form submission would be considered successful once both the Fauna and SECRYPT API calls went through. Because of this, we\u2019ve added the logic to set the\u00a0firstName,\u00a0lastName,\u00a0walletAddress,<\/p>\n<p>and\u00a0formSuccess\u00a0variables as a\u00a0.then()\u00a0block attached to\u00a0_createAllowlister\u00a0&#8211; this is the last API call that happens before the\u00a0submitForm\u00a0function ends, and adding in a\u00a0.then()\u00a0ensures that the states are only updated once the last API call has been completed.<\/p>\n<p>Your success messaging will look like this:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/test.secrypt.tech\/wp-content\/uploads\/2023\/01\/t-1024x240.png\" alt=\"\" width=\"1024\" height=\"240\" \/><\/p>\n<h4><strong>Conclusion<\/strong><u><\/u><\/h4>\n<p>We\u2019ve built an allowlisting application in React that takes in private information, securely stores it in a Fauna database, and then submits a public UUID for each property corresponding to the transaction\u2019s database record to the SECRYPT blockchain. The full GitHub repository for this tutorial can be found\u00a0<u>here<\/u>.<\/p>\n<p>If you\u2019re looking to build off of this tutorial or expand the app from here, some great next steps would be to:<\/p>\n<ul>\n<li>Write some tests for the Solidity smart contracts<\/li>\n<li>Write some tests for the React functionality and components<\/li>\n<li>Try adding some additional features, such as:\n<ul>\n<li>Surfacing every submitted allowlister and the corresponding UUID info as an easily referenceable guide to find them in Fauna<\/li>\n<li>Adding in logic so that if the SECRYPT API call fails for any reason, then it\u2019ll remove the entry from the Fauna database<\/li>\n<li>Adding in logic so that if someone gets a failed submission, and then resubmits and gets a successful submission, the error message goes away and only shows the success message and vice versa<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Genesis Contracts Here you will find a list of contracts deployed on SECRYPT together with their initial address, that is, their location on the blockchain. SECRYPT -Mainnet SECRYPT-Testnet Parent Chain: Ethereum Mainnet Contracts Address BytesLib 0x1d21fACFC8CaD068eF0cbc87FdaCdFb20D7e2417 Common 0x31851aAf1FA4cC6632f45570c2086aDcF8B7BD75 ECVerify 0x71d91a8988D81617be53427126ee62471321b7DF Merkle 0x8b90C7633F1f751E19E76433990B1663c625B258 MerklePatriciaProof 0x8E51a119E892D3fb324C0410F11f39F61dec9DC8 PriorityQueue 0x61AdDcD534Bdc1721c91740Cf711dBEcE936053e RLPEncode 0x021c2Bf4d2941cE3D593e07317EC355937bae495 RLPReader 0xD75f1d6A8A7Dc558A65c2f30eBF876DdbeE035a2 SafeMath 0x96D358795782a73d90F2ed2d505aB235D197ca05 Governance 0x98165b71cdDea047C0A49413350C40571195fd07 GovernanceProxy [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_header_footer","meta":{"site-sidebar-layout":"no-sidebar","site-content-layout":"page-builder","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"disabled","ast-breadcrumbs-content":"","ast-featured-img":"disabled","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"class_list":["post-986","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/test.secrypt.tech\/index.php\/wp-json\/wp\/v2\/pages\/986","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/test.secrypt.tech\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/test.secrypt.tech\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/test.secrypt.tech\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/test.secrypt.tech\/index.php\/wp-json\/wp\/v2\/comments?post=986"}],"version-history":[{"count":14,"href":"https:\/\/test.secrypt.tech\/index.php\/wp-json\/wp\/v2\/pages\/986\/revisions"}],"predecessor-version":[{"id":1423,"href":"https:\/\/test.secrypt.tech\/index.php\/wp-json\/wp\/v2\/pages\/986\/revisions\/1423"}],"wp:attachment":[{"href":"https:\/\/test.secrypt.tech\/index.php\/wp-json\/wp\/v2\/media?parent=986"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}