Unzip the QuickDapp zip file you received when you purchased a license. If you are pro user then you can visit the repository at https://github.com/QuickDapp/QuickDapp and clone any of the version tags or the master branch (if you're feeling brave!).
+
Unzip the QuickDapp zip file you received when you purchased a license. If you are pro user then you can visit the repository at https://github.com/QuickDapp/QuickDapp and download a ZIP of any of the git version tags or you can just fork the master branch (if you're feeling brave!).
diff --git a/resources/js/config.js b/resources/js/config.js
index b5c1c1e..56bc6d2 100644
--- a/resources/js/config.js
+++ b/resources/js/config.js
@@ -1 +1 @@
-var __DOCS_CONFIG__ = {"id":"1VbvUm9EirRF055hfBZ218XxztqfykvPHmh","key":"HAdMSGHSujnJeu8VXfkdNOCBW6QZL6HnFxN/kpcL04E.i/g9/R1O9fetNfUoJM6MRnltbN5i0jTW3QHuLrLEkqD0zHcZzglc8Z2JoKS+qajlavcrM2aecB6g5IwKAlQvIQ.8285","base":"/","host":"docs.quickdapp.xyz","version":"1.0.0","useRelativePaths":true,"documentName":"index.html","appendDocumentName":false,"trailingSlash":true,"preloadSearch":false,"cacheBustingToken":"3.5.0.761198031966","cacheBustingStrategy":"query","sidebarFilterPlaceholder":"Filter","toolbarFilterPlaceholder":"Filter","showSidebarFilter":true,"filterNotFoundMsg":"No member names found containing the query \"{query}\"","maxHistoryItems":15,"homeIcon":"","access":[{"value":"public","label":"Public"},{"value":"protected","label":"Protected"}],"toolbarLinks":[{"id":"fields","label":"Fields"},{"id":"properties","label":"Properties"},{"id":"methods","label":"Methods"},{"id":"events","label":"Events"}],"sidebar":[{"n":"/","l":"Introduction"},{"n":"architecture-layout","l":"Architecture"},{"n":"getting-started","l":"Getting started"},{"n":"environment-variables","l":"Environment variables"},{"n":"command-line","l":"Command-​line","o":true,"i":[{"n":"dev"},{"n":"contracts"},{"n":"build"},{"n":"prod"},{"n":"docker"},{"n":"do-cloud","l":"do-​cloud"},{"n":"other","l":"Other commands"}],"s":""},{"n":"backend","l":"Backend architecture","o":true,"i":[{"n":"bootstrap","l":"Bootstrap object"},{"n":"logging","l":"Logging"},{"n":"database","l":"Database"},{"n":"sending-emails","l":"Sending emails"},{"n":"push-notifications","l":"Push notifications"}],"s":""},{"n":"frontend","l":"Frontend architecture","o":true,"i":[{"n":"web3","l":"Web​3"},{"n":"graphql","l":"Graph​QL queries"},{"n":"components","l":"React components"},{"n":"forms","l":"Forms"},{"n":"global","l":"Global context"},{"n":"cookies","l":"Cookie consent"},{"n":"analytics","l":"Analytics"}],"s":""},{"n":"worker","l":"Background worker","o":true,"i":[{"n":"adding-a-job","l":"Adding a job"},{"n":"built-in-jobs","l":"Built-​in jobs"}],"s":""},{"n":"users","l":"Users","o":true,"i":[{"n":"authentication","l":"Authentication"},{"n":"notifications","l":"Notifications"}],"s":""},{"n":"smart-contracts","l":"Smart contracts","o":true,"i":[{"n":"abis-and-addresses","l":"AB​Is and addresses"}],"s":""},{"n":"deployment","l":"Deployment","o":true,"i":[{"n":"docker","l":"Docker images"},{"n":"vercel","l":"Deploying to Vercel"},{"n":"digital-ocean","l":"Deploying to Digital​Ocean"}],"s":""}],"search":{"mode":0,"minChars":2,"maxResults":20,"placeholder":"Search","hotkeys":["k"],"noResultsFoundMsg":"Sorry, no results found.","recognizeLanguages":true,"languages":[0],"preload":false},"resources":{"History_Title_Label":"History","History_ClearLink_Label":"Clear","History_NoHistory_Label":"No history items","API_AccessFilter_Label":"Access","API_ParameterSection_Label":"PARAMETERS","API_SignatureSection_Label":"SIGNATURE","API_CopyHint_Label":"Copy","API_CopyNameHint_Label":"Copy name","API_CopyLinkHint_Label":"Copy link","API_CopiedAckHint_Label":"Copied!","API_MoreOverloads_Label":"more","API_MoreDropdownItems_Label":"More","API_OptionalParameter_Label":"optional","API_DefaultParameterValue_Label":"Default value","API_InheritedFilter_Label":"Inherited","Search_Input_Placeholder":"Search","Toc_Contents_Label":"Contents","Toc_RelatedClasses_Label":"Related Classes","History_JustNowTime_Label":"just now","History_AgoTime_Label":"ago","History_YearTime_Label":"y","History_MonthTime_Label":"mo","History_DayTime_Label":"d","History_HourTime_Label":"h","History_MinuteTime_Label":"m","History_SecondTime_Label":"s"}};
+var __DOCS_CONFIG__ = {"id":"Q218vCzXd8LPlSgWGPCjoPSx2ezl8AuP1o+","key":"yfuohcff/Gt6iotNVtwT++FZyIQHu3K/JLZd5rijYIw.Y/HNzoy2V7CNRGnIkrU0jElhGUpQ5+kLFIjt+0DDQTjNvFsscPZT9va1YfLVY9xyUUwii0e5QVt3v80nPYBb2Q.8289","base":"/","host":"docs.quickdapp.xyz","version":"1.0.0","useRelativePaths":true,"documentName":"index.html","appendDocumentName":false,"trailingSlash":true,"preloadSearch":false,"cacheBustingToken":"3.5.0.761198160170","cacheBustingStrategy":"query","sidebarFilterPlaceholder":"Filter","toolbarFilterPlaceholder":"Filter","showSidebarFilter":true,"filterNotFoundMsg":"No member names found containing the query \"{query}\"","maxHistoryItems":15,"homeIcon":"","access":[{"value":"public","label":"Public"},{"value":"protected","label":"Protected"}],"toolbarLinks":[{"id":"fields","label":"Fields"},{"id":"properties","label":"Properties"},{"id":"methods","label":"Methods"},{"id":"events","label":"Events"}],"sidebar":[{"n":"/","l":"Introduction"},{"n":"architecture-layout","l":"Architecture"},{"n":"getting-started","l":"Getting started"},{"n":"environment-variables","l":"Environment variables"},{"n":"command-line","l":"Command-​line","o":true,"i":[{"n":"dev"},{"n":"contracts"},{"n":"build"},{"n":"prod"},{"n":"docker"},{"n":"do-cloud","l":"do-​cloud"},{"n":"other","l":"Other commands"}],"s":""},{"n":"backend","l":"Backend architecture","o":true,"i":[{"n":"bootstrap","l":"Bootstrap object"},{"n":"logging","l":"Logging"},{"n":"database","l":"Database"},{"n":"sending-emails","l":"Sending emails"},{"n":"push-notifications","l":"Push notifications"}],"s":""},{"n":"frontend","l":"Frontend architecture","o":true,"i":[{"n":"web3","l":"Web​3"},{"n":"graphql","l":"Graph​QL queries"},{"n":"components","l":"React components"},{"n":"forms","l":"Forms"},{"n":"global","l":"Global context"},{"n":"cookies","l":"Cookie consent"},{"n":"analytics","l":"Analytics"}],"s":""},{"n":"worker","l":"Background worker","o":true,"i":[{"n":"adding-a-job","l":"Adding a job"},{"n":"built-in-jobs","l":"Built-​in jobs"}],"s":""},{"n":"users","l":"Users","o":true,"i":[{"n":"authentication","l":"Authentication"},{"n":"notifications","l":"Notifications"}],"s":""},{"n":"smart-contracts","l":"Smart contracts","o":true,"i":[{"n":"abis-and-addresses","l":"AB​Is and addresses"}],"s":""},{"n":"deployment","l":"Deployment","o":true,"i":[{"n":"docker","l":"Docker images"},{"n":"vercel","l":"Deploying to Vercel"},{"n":"digital-ocean","l":"Deploying to Digital​Ocean"}],"s":""}],"search":{"mode":0,"minChars":2,"maxResults":20,"placeholder":"Search","hotkeys":["k"],"noResultsFoundMsg":"Sorry, no results found.","recognizeLanguages":true,"languages":[0],"preload":false},"resources":{"History_Title_Label":"History","History_ClearLink_Label":"Clear","History_NoHistory_Label":"No history items","API_AccessFilter_Label":"Access","API_ParameterSection_Label":"PARAMETERS","API_SignatureSection_Label":"SIGNATURE","API_CopyHint_Label":"Copy","API_CopyNameHint_Label":"Copy name","API_CopyLinkHint_Label":"Copy link","API_CopiedAckHint_Label":"Copied!","API_MoreOverloads_Label":"more","API_MoreDropdownItems_Label":"More","API_OptionalParameter_Label":"optional","API_DefaultParameterValue_Label":"Default value","API_InheritedFilter_Label":"Inherited","Search_Input_Placeholder":"Search","Toc_Contents_Label":"Contents","Toc_RelatedClasses_Label":"Related Classes","History_JustNowTime_Label":"just now","History_AgoTime_Label":"ago","History_YearTime_Label":"y","History_MonthTime_Label":"mo","History_DayTime_Label":"d","History_HourTime_Label":"h","History_MinuteTime_Label":"m","History_SecondTime_Label":"s"}};
diff --git a/resources/js/search.json b/resources/js/search.json
index 04b5c86..b7dff45 100644
--- a/resources/js/search.json
+++ b/resources/js/search.json
@@ -1 +1 @@
-[[{"l":"Introduction","p":["Ably integration for real-time push notifications to clients.","Also, the base QuickDapp distribution is itself a ready-made dapp (see live demo) which lets you deploy and interact with ERC-20 contracts on Sepolia so that you can see all the elements of a working dapp from the get-go.","As you can see above, QuickDapp does a lot for you out of the box.","Background job scheduling \"worker\" with support for cron jobs, repeat-on-failure logic, etc.","Commander+ Enquirer for powerful CLI scripts.","Datadog integration for cloud logging and browser session capture.","Diamond standard-based upgradeable smart contracts, see @QuickDapp/contracts repository.","DigitalOcean for production deployments.","Docker build scripts for deploying as containers.","It is designed to save you a massive amount of time and effort, freeing you up to focus on the parts of your dapp that actually matter.","NextAuth+ Sign-in-with-Ethereum for wallet authentication.","Prisma+ PostgreSQL for database storage.","QuickDapp also comes with a built-in , etc. You can deploy the worker process as its own Docker image whilst deploying the Next app to a serverless cloud (e.g Vercel). Or you can combine the two into a single Docker image. All using the readily available build scripts.","QuickDapp is a highly opinionated framework that helps you quickly build and deploy Web3 dapps, batteries included.","RainbowKit+ Wagmi+ Viem for web3 interaction.","React-query+ GraphQL for AJAX calls.","Retype for beautiful, locally-runnable docs.","Roughly speaking, it integrates the following:","Sendgrid integration for email sending.","TailwindCSS+ PostCSS+ shadcn/ui for components and styling.","This documentation is built using Retype and can be viewed in the browser using pnpm showdocs in the project folder.","Typescript+ Next.js+ React as the foundation."]},{"i":"why-does-this-exist","l":"Why does this exist?","p":["If you've ever multiple dapps you'll have found yourself reusing code and integrations from one dapp to the next to save time. Still, you'll need to spend time change the base layer to suite the new dapp.","Now, imagine the reusable part of a dapp was re-built to be generically usable for any dapp with some sensible defaults included. And imagine it had ready integrations for cloud deployment with baked-in support for useful third-party services. This would save you a tonne of time.","This is exactly what QuickDapp is."]},{"i":"what-if-i-dont-like-something","l":"What if I don't like something?","p":["QuickDapp has been carefully designed to give you the flexibility to build whatever you want without having to change the core structure. However, you may wish to replace some of the peripheral components (e.g the cloud logging provider) with your own choices.","In these cases it's easy to do so since QuickDapp is distributed as source code which you then modify/enhance to build your dapp. Meaning that any and every aspect of QuickDapp can be modified as you see fit. There are no limits or restrictions.","Note that parts of the documentation will touch on how you can customize and/or replace certain components to your liking whilst still taking advantage of QuickDapp's other useful features."]},{"i":"where-do-i-start","l":"Where do I start?","p":["The Getting Started section will get you up and running quickly.","The remainder of this documentation gives you a thorough understanding of all the different parts of QuickDapp and how to get the most out of the framework as a whole."]}],[{"l":"Architecture","p":["QuickDapp is built on Next.js. Thus, if you can build Next.js apps then you can build a QuickDapp. In addition to this, it also provides a background worker Node process as well as various command-line tools for running and deploying the dapp and worker servers."]},{"l":"File-system layout","p":["Note: This is not intended to be an exhaustive list of all the files in repository, but should give you a good idea of where things are."]}],[{"l":"Getting started"},{"i":"step-0---pre-requisites","l":"Step 0 - Pre-requisites","p":["Ensure you have the following pre-requisites installed and ready:","Node.js v20+.","We recommend using NVM to manage multiple Node.js versions simultaneously.","PNPM.","PostgreSQL 11+ running locally on port 5432, with a default admin user called postgres."]},{"i":"step-1---source-code","l":"Step 1 - Source code","p":["Unzip the QuickDapp zip file you received when you purchased a license. If you are pro user then you can visit the repository at https://github.com/QuickDapp/QuickDapp and clone any of the version tags or the master branch (if you're feeling brave!)."]},{"i":"step-2---dependencies","l":"Step 2 - Dependencies","p":["In the project folder, let's install the dependencies:","Now, let's bootstrap the project and generate the initial scaffolding:","This installs a Git hook which ensures your future commit messages adhere to the conventional commits. It also generates the Prisma client."]},{"i":"step-3---postgresql-database","l":"Step 3 - PostgreSQL database","p":["By default, QuickDapp assumes the existence of a PostgreSQL database. The default connection parameters (defined in the .env file) are:","host: localhost","port: 5432","user: postgres","db: quickdapp","schema: public","If you haven't already, create the quickdapp database, ensuring the postgres user has full system-level privileged access to it:","Let's get the dev database setup:"]},{"i":"step-4---demo-contracts-deployed-locally","l":"Step 4 - Demo contracts deployed locally","p":["The QuickDapp source code is actually a fully working Dapp which lets you deploy and interact with custom ERC-20 token contracts. The smart contract used is from the public @QuickDapp/contracts repository.","This @QuickDapp/contracts repository contains commands to setup a local node (using Anvil) and deploy the proxy contract to it.","First, clone this repository into the QuickDapp project folder:","There should now be a ./contracts folder. Now let's set it up:","Now let's run the local Anvil node and deploy the Diamond Proxy to it, and watch for changes:","When you run the above command you will see output which looks like this:","Follow the instructions and place this into either one of those files. Create the file if it doesn't exist.","At this point we have locally running Anvil node with our upgradeable proxy contract deployed to it."]},{"i":"step-5---setup-metamask","l":"Step 5 - Setup metamask","p":["The local node pre-funds a number of test wallets with money. The mnemonic used to generate these wallets is usually:","Note: Double-check that this mnemonic is correct by comparing with with the anvil node output above","Enter this mnemonic into your browser Metamask wallet to ensure you can use these accounts to interact with the contract that is now deployed on your local node."]},{"i":"step-6---run-dev-servers","l":"Step 6 - Run dev servers","p":["Now we're ready to run the dapp and test it locally. In the QuickDapp project folder:","This does the following:","Starts the Next.js dev server mode.","Starts the worker process dev server."]},{"i":"step-7---interact-with-the-dapp","l":"Step 7 - Interact with the dapp","p":["Goto http://localhost:3000 in your Metamask-enabled browser to interact with the dapp!"]},{"i":"step-8---deploying-to-production","l":"Step 8 - Deploying to production","p":["The following steps all deal with deploying our dapp to production.","We will do the following:","Deploy smart contracts to Sepolia test network.","Deploy the dapp (both Next.js + background worker process) as a Docker image to DigitalOcean's App platform.","Use a hosted PostgreSQL database on DigitalOcean as the production database.","If you wish, the Next.js dapp can be deployed by itself to Vercel and other serverless hosts very easily using their normal deployment processes. The docker image can just contain the background worker process. The choice is yours."]},{"i":"step-9---setup-production-database","l":"Step 9 - Setup production database","p":["We will setup a PostgreSQL database on DigitalOcean as our production database.","Pre-requisites:","Sign-up to DigitalOcean.","Obtain a Personal Access Token.","Create a PostgreSQL database cluster.","In the QuickDapp project folder, create a .env.production file, entering the access token as follows:","Now let's setup a database in the cluster named quickdapp, with a user named quickdappuser to access it:","This command will output the database connection string, which will look similar to:","Enter this connection string as the DATABASE_URL environment variable in the .env.production file, e.g:","Now setup the production database schema:"]},{"i":"step-9---deploy-contracts-to-sepolia","l":"Step 9 - Deploy contracts to Sepolia","p":["We will deploy the contracts to the Sepolia test network. This means the production dapp will require the user's wallet be connected to Sepolia in order for interactions to work.","Pre-requisites:","Setup a new Ethereum wallet using a mnemonic (you can do this in Metamask).","Fund this new allet using the Sepolia ETH faucet.","The following two environment variables need to be set in the shell environment for the deployment to work:","MNEMONIC- Mnemonic to the new wallet setup earlier.","SEPOLIA_RPC_URL- RPC endpoint for accessing Sepolia. You can get one from Alchemy.","Set these environment variables in the shell environment:","Now go into the contracts folder and run:","Note down the deployed proxy contract address from the log output. The address can also be found inside the gemforge.deployments.json file.","Go back into the QuickDapp project folder and edit the .env.production file, adding the proxy address follows:","You are now ready to build the production dapp."]},{"i":"step-10---test-run-production-build-locally","l":"Step 10 - Test-run production build locally","p":["Note: This step is optional, and is useful it you want to debug some production issues locally","In the project folder, build the production apps:","Now, run the production apps:","Now goto http://localhost:3000 in your Metamask-enabled browser to interact with the dapp. You will need to connect to the Sepolia test network in your wallet."]},{"i":"step-11---build-docker-image","l":"Step 11 - Build docker image","p":["We are going to build a Docker image to run both the Next.js app and background worker processes in tandem.","Pre-requisites:","Install Docker and ensure that the Docker daemon is running in the background.","In the project folder run:","This will build a Docker image named quickdapp-all.","You can test-run this image locally using:","Now goto http://localhost:3000 in your Metamask-enabled browser to interact with the dapp. You will need to connect to the Sepolia test network in your wallet."]},{"i":"step-12---deploy-to-digitalocean","l":"Step 12 - Deploy to DigitalOcean","p":["To push the quickdapp-all image to DigitalOcean:","This will create a container registry named quickdapp if it doesn't already exist and push the local image to it.","At this point we can use the App platform to create an app from this image. Follow the official docs for instructions on how to do this.","If the App build succeeds (and it should!) your dapp will be up and running at the URL presented by DigitalOcean.","Now, every time you push an updated image to the container registry the App will auto-redeploy."]},{"i":"step-13---hurrah","l":"Step 13 - Hurrah!","p":["Congratulations! your dapp is now available on the web in production mode.","If you wish to gain more insight into what your dapp is doing a good starting point is to enable cloud logging."]}],[{"l":"Environment variables","p":[".env",".env contains APP_NAME=root",".env.development contains APP_NAME=dev",".env.development or .env.production, depending on the value of the NODE_ENV environment variable.",".env.local",".env.local contains APP_NAME=local",".env.production contains APP_NAME=prod","Environment variables are loaded in the following order, with later places taking precendence over earlier ones:","Environment variables are the primary means through which to pass configuration to your app at both build-time and runtime.","Example","If we restore the .env.local file but this time run the production server then the runtime value will be local. However, if we delete the .env.local file again and restart the production server then the runtime value will be prod.","Let's assume that:","QuickDapp follows the Next.js conventions when it comes to specifying environment variables for your app.","Some key points to note:","The .env file is required. This file should contain values suitable for local development as well as any values which are unlikely to change across environments. It gets bundled into production and Docker builds and can be checked into source control.","The .env.development and .env.production files are already git-ignored and should never be checked into source control.","The .env.development file gets loaded when the app is run in dev server mode.","The .env.local file is for further customization in all environments, e.g if you are temporarily testing a value different to the default. However, note that it does not get bundled into Docker images.","The .env.production file gets loaded when building production version of the app, running the production version and also gets bundled into Docker images.","The dotenv loads environement varibles from various .env* files in the project root folder whilst still allowing for overrides specified directly via the shell/terminal environment itself.","The files use the INI file format:","What this means is that if the same environment variable is declared in multiple places then its final value during runtime is determined by the above order.","When the dev server is run, at runtime the process.env.APP_NAME value will equal local. If we delete the .env.local file and restart the server then the runtime value will be dev."]},{"l":"Client-side vs Server-side","p":["There are two types of environment variables: client-side and server-side.","Client-side environment variables are made available client-side in the browser and are prefixed with NEXT_PUBLIC_ to indicate this. Any environment variable that needs to be accessible client-side must be given this prefix. Since these variables are accessible browser-side they must not hold sensitive passwords, API keys or any other information that browser clients are not meant to see.","Server-side environment variables are only available server-side and are never sent to the browser. Thus these variables can hold sensitive passwords, API keys and other information that browser clients are not meant to see.","Note: All client-side environment variables are automatically available to server-side code."]},{"l":"Overriding at runtime","p":["Server-side environment variables can be overridden at runtime by setting them in the shell/environment prior to starting the app. For example:","However, client-side environment variables get bundled (i.e. hardcoded) into the frontend application as part of the build process and thus cannot be overridden at runtime."]},{"l":"Programmatic access","p":["The src/config/** modules are responsible for loading, parsing and ensuring the right syntax for each of the various environment variables. It is recommended that your code access environment variables through this rather than accessing process.env directly."]},{"l":"Client-side","p":["Although client-side variables are defined with the NEXT_PUBLIC_ prefix, at runtime the prefix gets removed to make your code more readable."]},{"l":"Server-side"},{"l":"Adding your own variables","p":["To add your own custom environment variable:","Add your variable to either src/config/client.ts or src/config/server.ts depending on whether it is a client-side or server-side variable. Follow the conventions used for existing environment variables, including removing the NEXT_PUBLIC_ prefix at runtime for client-side variables.","Add a default value for your variable to the .env file, again following the conventions shown in in the file for other variables. If your variable does not have a default value then set its value inside .env to the empty string ().","Optionally add a value override for your variable to either or both of .env.development and .env.production."]},{"l":"Example","p":["Let's add a client-side variable called SHOW_COOKIE_BANNER which will specify whether a cookie consent banner should be shown to the user. Our requirements for this variable:","It can only be a boolean value - true or false.","It should be false in development environments.","It should be true in production environments.","First, we will edit src/config.client.ts to add the variable:","In .env we set it to false:","In .env.production we set it to true:"]}],[{"i":"command-line-cli","l":"Command-line (CLI)","p":["The QuickDapp CLI is a collection of Node.js scripts which live within the ./scripts folder.","The available commands are:","dev- run development code, interact with development database.","contracts- interact with ./contracts folder, build and deploy contracts to a local node.","build- build production code.","prod- run production code, interact with production database.","docker- build and run Docker images.","do-cloud- interact with DigitalOcean cloud hosting.","To get help on any command simply affix --help. However, note that some commands have sub-commands, e.g:","build- build both web + worker production servers","To get usage help: build (default) --help","build web- build web production server only","To get usage help: build web --help"]}],[{"l":"dev","p":["The dev command is responsible for:","Running the Next.js dev server.","Running the background worker dev server.","Running development database management commands.","All of these commands operate using the development environment configuration as defined via the environment variables.","The servers are spawned as child processes of the CLI command script, with their stdio mapping to the console."]},{"i":"nextjs","l":"Next.js","p":["This internally runs:","Next.js dev server which watches for changes to your web app and reloads automatically.","GraphQL code generator which auto-generates Typescript bindings when your GraphQL schema changes.","The website will be accessible at http://localhost:3000."]},{"l":"Background worker","p":["This internally runs:","Node.js process for the background worker which auto-restarts when changes are made to the worker code.","Code generator which auto-generates Typescript exports when a background job is added or modified.","## Running servers simultaneously","To run both the the Next.js and background worker servers simultaneously:"]},{"l":"Database management","p":["To migrate the database to the latest schema:","To reset the database to the latest schema and clear all of its data:","To re-generate the Prisma client and associated Typescript bindings:"]}],[{"l":"contracts","p":["The contracts command is responsible for:","Initializing the contracts repository in the contracts/ folder.","Running a local node and deploying the contracts to it","Re-building and redeploying when contract code changes.","This command assumes that the concracts are in the contracts/ sub-folder and that they are based on the official contracts repository."]},{"l":"Initialize contracts","p":["This command must be run after the contracts folder has been setup. It will install any dependencies required to build and deploy contracts."]},{"l":"Local development deployment","p":["This does the following:","Run a local Anvil node","Build and deploy the Diamond Proxy and associated facets to the local node","Re-build and upgrade the on-chain contract whenever the contracts source code gets changed.","Rebuilding the contracts will cause the web and worker servers to auto-reload the ABI from the contracts folder. Thus, you can now develop your app code and contracts in tandem."]}],[{"l":"build","p":["The build command is responsible for:","Building the Next.js production code.","Building the background worker production code."]},{"i":"nextjs","l":"Next.js","p":["The .next/standalone/ folder will contain the output production code."]},{"l":"Background worker","p":["The build/ folder will contain the output JS code as a single file."]},{"l":"Building both together","p":["To build both the the Next.js and background worker production code together:"]}],[{"l":"prod","p":["The prod command is responsible for:","Running the Next.js production server.","Running the background worker production server.","Running production database management commands.","All of these commands operate using the production environment configuration as defined via the environment variables.","The servers are spawned as child processes of the CLI command script, with their stdio mapping to the console."]},{"i":"nextjs","l":"Next.js","p":["The website will be accessible at http://localhost:3000."]},{"l":"Background worker","p":["## Running servers simultaneously","To run both the the Next.js and background worker servers simultaneously:"]},{"l":"Database management","p":["To migrate the database to the latest schema:"]}],[{"l":"docker","p":["The docker command is responsible for:","Building and running Next.js production Docker images.","Building and running background worker production Docker images.","All of these commands operate using the production environment configuration as defined via the environment variables."]},{"i":"nextjs","l":"Next.js","p":["To run this image:","The built image will be named quickdapp-web- this can be customized using the --prefix option. Example:"]},{"l":"Background worker","p":["To run this image:","The built image will be named quickdapp-worker- this can be customized using the --prefix option. Example:"]},{"i":"combined-nextjs--worker","l":"Combined Next.js + worker","p":["To run this image:","The built image will be named quickdapp-all- this can be customized using the --prefix option. Example:"]},{"l":"Terminal mode","p":["To run any of the Docker images in terminal mode so that you can browse within the container using a shell, append the --term option, e.g:"]}],[{"l":"do-cloud","p":["The do-cloud command is responsible for interacting with the DigitalOcean API. Specifically it enables the following:","Provision a managed PostgreSQL database and associated user.","Push Docker images to a managed container registry.","All of these commands require the following environment variable to be set in either .env.local(recommended) or .env.production:","DIGITALOCEAN_ACCESS_TOKEN- set this to a DigitalOcean personal access token."]},{"l":"Database provisining","p":["This will setup a managed PostgreSQL database, creating a basic db cluster if it doesn't already exist.","If existing clusters are found then it will ask you to select the cluster within which to create the database.","It will then create a PostgreSQL user that is able to access and modify the newly created database.","The default database name and username are both quickdapp. To customize:","The final output of the command will be the database connection string to set as the DATABASE_URL environment variable, e.g:"]},{"l":"Docker images","p":["This will first create a Docker container registry if it doesn't already exist.","If a registry needs to be created then the most basic type of registry will be created. At the time of writing (Dec 2024) this can only hold a single Docker image. If you wish to push multiple images to it then you will need to upgrade it manually via the DigitalOcean dashboard.","By default the created registry is called quickdapp. To customize the name:","If the myregistry registry already exists then it will be used, otherwise it will be created.","Once the registry has been created the specified Docker image will be pushed to it. Note that the Docker tag is always set to latest.","If you had used a custom docker image name prefix when building the images then you will need to supply that prefix here too, e.g:"]}],[{"l":"Other commands"},{"l":"bootstrap","p":["The bootstrap command initializes QuickDapp, and only needs to be run once, after NPM dependencies have been installed.","It installs a git hook which ensures your future commit messages adhere to the conventional commits.","Example usage:"]},{"l":"showdocs","p":["The showdocs command is responsible for building and rendering this documentation in the browser using Retype.","Example usage:","The documentation will now be accessible at http://localhost:5000."]}],[{"l":"Backend architecture","p":["The web backend is built on Next.js, a serverless Node.js framework for serving up React.js apps with server-side rendering and static page generation enabled.","Prisma is integrated as an ORM layer for accessing a persistent database(PostgreSQL by default).","A GraphQL API layer allows for the frontend (and indeed, any third-party client) to read from and write to the backend.","Configuration parameters are supplied via server-side-only environment variables.","A hierarchical logging system allows for categorised logging output with differing log level thresholds."]}],[{"l":"Bootstrap object","p":["The src/backend/boostrap/index.ts file contains code which is executed on every backend API and/or page invocation. This code bootstraps basic backend services and returns a corresponding BootstrappedApp object which looks something like:","This object operates as a sort of backend application-level context and gets passed around the various components of the backend code.","For any application-level objects or services used throughout your backend, it is recommended that you define them in the BootstrappedApp interface and then initialize them within the bootstrap code."]}],[{"l":"Logging","p":["The backend logging system is built on bunyan and is designed for categorised, hierarchical logging with log thresholds.","Each log treshold has a corresponding method with the same name available on a logger instance. The possible log thresholds are:","trace- for very low-level messages that you normally don't need to see.","debug- for messages that can help with debugging issues.","info- for general messages that you want to see in the logs in production.","warn- for minor issues that don't cause problems but may need investigating.","error- for problems and errors that need to be investigated and/or fixed.","For example, to create a logger which logs to console:","Now a child logger can be created from the root logger:","There is always an instantiated log property in the bootstrapped object."]},{"l":"Datadog cloud","p":["Log messages will be sent to Datadog if the following environment variables are set:","DATADOG_API_KEY","DATADOG_APPLICATION_KEY","See https://github.com/DataDog/datadog-api-client-typescript for information on these parameters."]}],[{"l":"Database","p":["QuickDapp supports PostgreSQL databases by default, with Prisma as the ORM layer.","Technically speaking, any database type supported by Prisma could be used, although note that some of the DigitalOcean database-related commands (e.g do-cloud) will then require modification."]},{"l":"Connection parameters","p":["The database connection parameters are supplied as the DATABASE_URL environment variable, e.g:"]},{"l":"Schema","p":["A number of built-in tables are provided for your convenince:","Setting- for any persisted app-level settings as key-value pairs.","User- for authenticated users - includes a field for storing the user's wallet address and any settings field which can hold any JSON data.","Notification- for notifying users with a message or other data.","WorkerJob- for the background worker task queue."]},{"l":"Accessing","p":["All database tables are accessed through purpose-built model code found in src/backend/db. This provides for an abstraction layer over the database which hids the raw Prisma commands from other backend code. Each model function excpets a db parameter which can be obtained from the bootstrap object."]},{"l":"Local development","p":["To migrate the database to the latest schema:","To reset the database to the latest schema and clear all of its data:","To re-generate the Prisma client and associated Typescript bindings:"]},{"l":"Production deployment","p":["For the production database only a migration/upgrade ability is provided.","To migrate the database to the latest schema:"]}],[{"l":"Sending emails","p":["Sending emails via Mailgun is supported out of the box.","The following environment variables must be set for email sending to be enabled:","MAILGUN_API_KEY- the Mailgun domain sending API key.","MAILGUN_API_ENDPOINT- the Mailgun API endpoint (e.g https://api.eu.mailgun.net/).","MAILGUN_FROM_ADDRESS- email address for the from field in an email - must end in @DOMAIN where DOMAIN is the domain for which Mailgun sending is configured.","If the mailer is enabled then the mailer property in the bootstrapped object will be set."]}],[{"l":"Push notifications","p":["Real-time push notifications can be sent to browser clients using Ably.","All of the following environment variables must be set for this to be enabled:","ABLY_API_KEY- the Ably API key.","If enabled then the ably property in the bootstrapped object will be set."]}],[{"l":"Frontend architecture","p":["The front-end is written in React.js, using the Next.js architecture.","There are predefined React contexts provided to make co-ordination between different parts of your app easier, e.g global and cookie consent."]}],[{"l":"Web3","p":["Web3 wallet discovery, authentication and interaction is facilitated via RainbowKit, Wagmi, Viem and Sign-in-with-Ethereum."]},{"l":"Chain configuration","p":["QuickDapp is built for EVM chains. The target EVM chain is specified via the following environment variables:","NEXT_PUBLIC_CHAIN","NEXT_PUBLIC_CHAIN_RPC_ENDPOINT git","Note that both variables are needed for the chain configuration to work correctly. The backend server and worker processes also rely on these environment variables to know which chain to monitor."]},{"l":"Connection and authentication","p":["The ConnectWallet component triggers the RainbowKit connection dialog. By default, once a wallet is connected the user is prompted to sign a message with their private key to prove that they actually own this wallet.","The signed message is sent to the backend for verification. Once verified, a JWT (JSON Web Token) is used to store the user's sessions for any subsequent queries made to the backend. The global context object will also be updated to contains the user's wallet information.","The user remains authenticated across browser sessions until and unless the user changes their active wallet and/or manually disconnects from the dapp.","Note: If the user's crypto wallet is connected to a different chain then they will be prompted to switch to the right chain."]},{"l":"Contracts","p":["Please refer to the the section on Smart Contracts for a general introduction."]},{"i":"readwrite-hooks","l":"Read/write hooks","p":["The following high-level contract interaction hooks are provided:","useGetContractValue- call a read-only method on a single contract.","useGetMultipleContractValues- call multiple read-only methods on multiple contracts. (Note: The background worker will deploy Multicall3 to the chain if not already present).","useGetContractPaginatedValues- read paginated values from a single contract.","useSetContractValue- call a read-write method on a contract with a transaction.","You can of course use the Wagmi React hooks to access contract methods if you wish. However, the above hooks incorporate the ABI and address fetching methods above to provide for a more seamless interaction with contracts.","The default QuickDapp dapp (the built-in ERC-20 demo) utilises all of these hooks, providing you with working code examples."]}],[{"l":"GraphQL queries","p":["All API requests made to the backend are done as GraphQL requests.","React query is used to actually execute queries and handle pagination, optimistic updates, etc. The src/frontend/hooks/api.ts file contains wrapper hooks for queries/mutations that need to be made in the dapp. It is recommended that you stick to this convention when adding new queries.","The src/shared/graphql/schema.ts file contains all the queries and mutations available. Adding a new query or mutation involves the following:","Update schema.ts.","Update one or more of fragments.ts, queries.ts, mutations.ts.","Update the backend resolvers.ts to ensure the query gets handled in the backend.","In dev mode a code generator watches for changes to schema.ts, generating updated Typescript bindings on-the-fly. These bindings are used by the backend resolvers to accurately resolve query and parameter names."]},{"l":"Authenticated queries","p":["It's useful to be able to have certain queries and mutations only be callable by authenticated users. This is easily accomplished through the use of the @auth directive in the schema definition:","Example:","If the user isn't authenticated (i.e. logged in) then the above getMyNotifications query will fail.","Authenticated queries/mutations automatically have a variable set within their corresponding backend resolver contexts, e.g:"]},{"l":"Sandbox GUI","p":["When the dev server is run, accessing the /api/graphql path in the browser will bring up the Apollo Sandbox. Through this GUI you can enumerate and test all the GraphQL schema definitions, including queries and mutations."]}],[{"l":"React components","p":["Button- Buttons. Supports a number of sizes and variants, and you can easily add your own.","ClientOnly- Only renders its children client-side - useful for wrapping any components that should not be rendered server-side. If you have React hydration issues you might need to use this component to wrap parts of your UI.","ConnectWallet- Wallet connection button.","ContractValue- Displaying values returned from a smart contract. Handles loading/error states and allows for values sanitization prior to rendering.","Dialog- Modal dialog popups.","ErrorButton- Error buttons (uses Button under the hood).","ErrorMessageBox- Messages boxes which show large error traces.","Footer- The site footer.","Form- Forms and their fields, to be used in conjunction with the form hooks.","Header- The site header.","Icons- SVG icons from Lucide React.","IfWalletConnected- Only renders its children if the user is authenticated; otherwise it renders the wallet connection button ( ConnectWallet).","Loading- Loading indicator animation.","Notifications- User notifications dialog and indicator icon.","OnceVisibleInViewport- Triggers a passed-in callback when its UI (a div /) comes into view. This is useful for performing visibility-based actions.","PingAnimation- pinging animation (used for notifications icon).","Popover- In-place popups (used by Tooltip component).","Svg- SVG rendering.","The bundled React components are mostly generic, although some are specific to the built-in dapp, e.g CreateTokenDialog. All dapp-specific components are in the src/frontend/components/dapp folder.","The re-usable generic components are:","Tooltip- Tooltip popups."]},{"i":"shadcnui","l":"@shadcn/ui","p":["Key reusable UI components - Dialog, Popover, etc - are actually @shadcn/ui components. These are a great set of starter components to build on top of as they are well architected, so we recommend you first check these component libraries before rolling your own.","The bundled components.json file is the configuration for @shadcn/ui.","To install a new component:","At this point the src/frontend/components folder will contain a component name.tsx file containing the code to use. Feel free to then modify this code as you see fit."]}],[{"l":"Forms","p":["QuickDapp comes with a powerful form handling framework, comprising of hooks and components which work in tandem to make form processing easy. Its features:","Features:","Built-in fields: dropdown, text, textarea, number.","Ability to support any custom input type.","Field-level sanitization, validation and error handling.","Form-level validation and error reporting.","Asynchronous validation with debounce delay.","The built-in example dapp illustrates all aspects of this framework."]},{"l":"Hooks","p":["There are two hooks, both found in src/frontend/hooks/forms.ts:","useForm()- defines the fields of a form as well as form-level validation methods.","useField()- defines a single field - its name, initial value, sanitization, validation and whether it is a required field.","Example usage:","The validator methods are expected to return an error string if there is a validation error. This error string is what gets displayed to the user.","For example:","For efficiency purposes, it's best to define the validation functions outside of the component or wrapped within useCallback hooks so that the useField hook doesn't return a friend field each time."]},{"l":"Components","p":["Here is how the UI component source might look, using the properties defined earlier:","The built-in components automatically display any field-level validation errors. For form level validation errors you have to manually display them as, as shown above.","The onSubmit handler would look similar to:"]}],[{"l":"Global context","p":["The GlobalContext object contains information on the currently authenticated user and their wallet, the currently connected blockchain as well as an interface for accessing push notifications.","The context is accessed through the useGlobalContext() hook as such:"]}],[{"l":"Cookie consent","p":["Cookie law compliance is made easy through the use of a ready-made cookie consent architecture.","A user may choose to allow or disallow cookies. Their response is cached in window.localStorage so that they do not get asked again. Note that this also means that once a user has allowed or disallowed cookies they cannot change their response later on unless you modify the cookie consent logic or clear the window.localStorage data."]},{"l":"Consent banner","p":["There is a built-in consent banner which gets shown to user's when they first visit the site. This appears as an overlay at the footer of the page and is triggered automatically if the user has not yet set their response."]},{"l":"CookieConsentContext","p":["The CookieConsentContext object is responsible for keeping track of the user's cookie consent response. It loads/saves a user's response from/to window.localStorage. It also provides methods for triggering the built-in consent popup."]},{"i":"getusercookieconsent","l":"getUserCookieConsent()","p":["The getUserCookieConsent() method provides a way for you to run any custom code after a user's cookie consent response has been recorded, e.g:","This method can be called from anywhere within the frontend app, as many times as you like.","If the user has already recorded their cookie response (e.g in a previous browser session) then the callbacks will get executed immediately. Otherwise the callbacks will only get executed once the user records a response in the cookie consent banner.","The analytics feature requires cookies and so it relies on this method to activate itself."]}],[{"l":"Analytics","p":["Front-end analytics and browser session recording is done through Datadog. This allows you to see what users's see on the page as well as how they move around the page, visually.","To enable these, you'll need to set the required environment variables:","NEXT_PUBLIC_DATADOG_APPLICATION_ID","NEXT_PUBLIC_DATADOG_CLIENT_TOKEN","NEXT_PUBLIC_DATADOG_SITE","NEXT_PUBLIC_DATADOG_SERVICE","(See https://docs.datadoghq.com/real_user_monitoring/browser/ for information on these parameters)","Note that all the above environment variables must be set for analytics to be enabled. Furthermore, the user must give consent for cookies to be stored."]}],[{"l":"Background worker","p":["QuickDapp bundles a background worker process that runs in parallel to the Next.js server process, so that you can schedule long-running persistent background jobs at any time."]},{"i":"why-its-useful","l":"Why it's useful","p":["Next.js apps tend to be deployed to serverless environments for scalability and efficiency reasons. As such, there is no persistent server, meaning there is no way to run a backend process which persists beyond the lifetime of an incoming request.","Although one could use a cron service to periodically call a Next.js route, many serverless clouds (e.g Vercel) cap the duration for which any given route can run, limiting the work that can be done. In any case, a serverless architecture is not the ideal approach for long-running background jobs."]},{"l":"How it works","p":["Jobs are scheduled via the WorkerJob database table. All job results (both successes and failures) are logged. If a job fails it can be set to automatically retry after a predefined delay. Jobs can also be run regularly according to a cron schedule.","The worker process has full access to all of the same backend resources as the Next.js server. However, it does not deal with any frontend resources and does not listen for incoming connections. Thus, to know what's going on with a worker process you will need to monitor the database as well as log messages.","Although it's possible to run more than 1 worker process to scale up, note that there is currently no mutex mechanism to prevent two or more workers may end up executing the same task at the same time. Though this is unlikely to happen often anyway, it should be noted that it could happen."]},{"l":"Configuration","p":["It loads its configuration from the same environment variables as every other part of your dapp.","The only worker-specific environment variables are:","WORKER_LOG_LEVEL- Minimum logging threshold level. Allows you to log worker process messages at a different minimum threshold to that of the Next.js backend.","## Database table","Any data returned by a job's run() method will be stored as JSON in the worker job table row corresponding to that particular invocation of the job."]}],[{"l":"Adding a job","p":["To create a new type of job a new file must be added to src/worker/jobs which adheres to the Job interface:","In dev mode, when any changes are made to the src/worker/jobs folder contents a code generator automatically re-generates the Typescript bindings (see src/worker/generated) - these are used by worker-related database methods."]},{"l":"Scheduling a job","p":["The two job scheduling methods are:","A job is specified as follows:","The scheduleCronJob() method additionally requires a crontab specification."]},{"l":"Pending jobs","p":["When a new job is scheduled any matching pending jobs are first cancelled.","A matching pending job is one which has all the following properties:","It has the same type and userId as the new job.","It has not yet started OR it started over an hour ago but is yet to complete.","Thus, you should ensure any given job doesn't execute for longer than an hour."]},{"l":"Database table","p":["Regarding the worker database table schema, the following two fields should be noted:b","result- The data returned by the job's run() method if successful.","rescheduledFromJob- If the job has been rescheduled from an earlier job (whether due to a cron schedule or as an auto-retry-on-failure) this field will hold a reference to that earlier job."]}],[{"l":"Built-in jobs","p":["There are two built-in worker jobs which are set to run on a cron schedule:","removeOldWorkerJobs- Removes successfully completed jobs. Runs every minute.","watchChain- Watches the blockchain for changes according to user-defined filters. Runs every 5 seconds."]},{"l":"Watching the blockchain","p":["Watching and reacting to blockchain events is done by defining user-defined filters in the src/worker/chainFilters folder. The built-in watchChain job uses these filters to watch the blockchain for changes.","The chain to watch is defined by the chain-related environment variables.","Each filter module must adhere to the following interface:","The default bundled dapp has two filters - createToken, sendToken- which serve as examples of how to use this architecture. Both filters listen for events emitted from the dapp contract."]}],[{"l":"Users","p":["QuickDapp has a built-in user authentication and management system that can easily be extended to suit your requirements.","All users are stored in the users database table. By default each user record stores the user's unique Ethereum wallet address as well as an settings JSON object."]}],[{"l":"Authentication","p":["User authentication is built on top of NextAuth, with Sign-in-with-Ethereum (SIWE) as an authentication mechanism.","The flow:","A user must connect their web3 wallet to the Dapp.","They are asked to sign a login message using their private key to prove ownership of the wallet.","The signature is sent to the NextAuth backend for verification.","Once verified, a corresponding JWT (JSON web token) is stored client-side, to be passed in with all subsequent queries to the backend. The token persists across browser sessions, until the user chooses to disconnect their wallet.","If a database record for the user doesn't yet exist then it is created during the backed verification step."]}],[{"l":"Notifications","p":["The built-in user notifications system lets the backend notify a user of something new. This could be a simple message and/or a JSON data blob.","The notifications database table is responsible for storing all notifications and recording whether they have seen by the user or not.","Note that notifications can only be created from within the backend (including background workers)."]},{"l":"Creating a notification","p":["The backend bootstrap object provides a convenient API for creating notifications:","This does two things:","Creates the corresponding entry in the notifications table.","Sends a push notification to the user informing them of new notifications. (The frontend automatically updates the notifications indicator in the browser UI).","The data parameter can contain any structure, though at present the following structure is handled in the UI by default:"]}],[{"l":"Smart contracts","p":["QuickDapp is setup to work with forks of the @QuickDapp/contracts repository.","You can of course use whatever smart contract architecture you want but it's worth forking the default contracts repository as a starting point since it is setup to make development easy.","The default repository has the following features:","Upgradeable proxy contract based on the EIP-2535 Diamond Standard.","Deployments and upgrades (both dev and production) managed using Gemforge.","Generates an ABI for the proxy contract to be used by your dapp.","Works with the contracts command out of the box."]}],[{"l":"ABIs and addresses","p":["All required ABIs and contract addresses should be specified in src/shared/contracts.ts, following the format there-in.","The following methods are provided:","getContractInfo- returns ABI of a contract","getDeployedContractInfo- returns ABU and address of a contract on a given chain.","getMulticall3Info- returns address of Multicall3 contract on the current chain. (Note: the address should be the same on every chain)."]}],[{"l":"Deployment","p":["QuickDapp deployment consists of deploying both:","Next.js app","Background worker process","You will most likely want to deploy the dapp to Vercel or an equivalent serverless environment for maximum scalability.","However, QuickDapp has built-in support for building Docker images of the dapp in order to deploy it to hosted container environments. The background worker process must be built as a docker image."]}],[{"l":"Docker images","p":["The docker command is used to build Docker images. By default, 3 types of images are supported:","web- Next.js app server. ( Exposes the app on port 3000).","worker- Background worker server.","all- Next.js app server + background worker server combined. ( Exposes the Next.js app on port 3000).","The images internally bundle the QuickDapp CLI so that the actual entrypoint for each image is the prod command. This means the servers are spawned as child processes of the main process. If any server crashes then the main process (and thus the Docker container) will also crash and exit.","Images are based on Alpine Linux and are built to hold only the minimal required dependencies to run the servers. Thus, even the all image comes to <500 MB in size.","To make this possible, Webpack is used to build the background worker process to a single file. For the Next.js app the standalone option is enabled in the config."]},{"l":"Deployment","p":["One built the images can be run locally and/or deployed to any Docker container hosting environment of your choice.","At present, QuickDapp provides built-in support for deploying to DigitalOcean."]},{"l":"Bundling NPM packages","p":["Most Node packages will get automatically bundled into the built output by webpack. However, some packages will still require their node_modules sub-folders to exist at runtime in order to work properly (e.g Prisma).","These can be seen inside the Dockerfile, e.g:","If you are using a package also has the same requirement then simply add a corresponding line for it in all of the sections in the Dockerfile. For example, if you you are using the mailgen package then you would add a single line for each of the three build image types:","Some NPM packages need to bundled"]}],[{"l":"Deploying to Vercel","p":["You can deploy the QuickDapp Next.js app to Vercel by simply deploying from your Github repo within the Vercel dashboard.","Note that since the .env.production file does not get checked into version control - this is intentional. Instead, the production environment variables will need to be set in Vercel's dashboard, which is their recommended method.","Vercel will only build the Next.js web app (thus excluding the background worker). This behaviour can be changed by editing the vercel.json config file."]}],[{"l":"Deploying to DigitalOcean","p":["The do-cloud command makes it easy to deploy built Docker images to your DigitalOcean docker container registry. If a registry doesn't yet exist it will be created.","To actually deploy and run your application you will need to create an App in the DigitalOcean dashboard, pointing it your container registry and Docker image.","This way, every time a new version of the Docker image is pushed to the registry, the live app will be automatically updated to use this image. At this point you will have something akin to \"Continuous Deployment\" setup."]},{"l":"Database setup","p":["You can also use the do-cloud command to setup a PostgreSQL database in DigitalOcean for your live app to use. For maximum communication efficiency ensure that both the App and database are located in the same regional datacenter."]}]]
\ No newline at end of file
+[[{"l":"Introduction","p":["Ably integration for real-time push notifications to clients.","Also, the base QuickDapp distribution is itself a ready-made dapp (see live demo) which lets you deploy and interact with ERC-20 contracts on Sepolia so that you can see all the elements of a working dapp from the get-go.","As you can see above, QuickDapp does a lot for you out of the box.","Background job scheduling \"worker\" with support for cron jobs, repeat-on-failure logic, etc.","Commander+ Enquirer for powerful CLI scripts.","Datadog integration for cloud logging and browser session capture.","Diamond standard-based upgradeable smart contracts, see @QuickDapp/contracts repository.","DigitalOcean for production deployments.","Docker build scripts for deploying as containers.","It is designed to save you a massive amount of time and effort, freeing you up to focus on the parts of your dapp that actually matter.","NextAuth+ Sign-in-with-Ethereum for wallet authentication.","Prisma+ PostgreSQL for database storage.","QuickDapp also comes with a built-in , etc. You can deploy the worker process as its own Docker image whilst deploying the Next app to a serverless cloud (e.g Vercel). Or you can combine the two into a single Docker image. All using the readily available build scripts.","QuickDapp is a highly opinionated framework that helps you quickly build and deploy Web3 dapps, batteries included.","RainbowKit+ Wagmi+ Viem for web3 interaction.","React-query+ GraphQL for AJAX calls.","Retype for beautiful, locally-runnable docs.","Roughly speaking, it integrates the following:","Sendgrid integration for email sending.","TailwindCSS+ PostCSS+ shadcn/ui for components and styling.","This documentation is built using Retype and can be viewed in the browser using pnpm showdocs in the project folder.","Typescript+ Next.js+ React as the foundation."]},{"i":"why-does-this-exist","l":"Why does this exist?","p":["If you've ever multiple dapps you'll have found yourself reusing code and integrations from one dapp to the next to save time. Still, you'll need to spend time change the base layer to suite the new dapp.","Now, imagine the reusable part of a dapp was re-built to be generically usable for any dapp with some sensible defaults included. And imagine it had ready integrations for cloud deployment with baked-in support for useful third-party services. This would save you a tonne of time.","This is exactly what QuickDapp is."]},{"i":"what-if-i-dont-like-something","l":"What if I don't like something?","p":["QuickDapp has been carefully designed to give you the flexibility to build whatever you want without having to change the core structure. However, you may wish to replace some of the peripheral components (e.g the cloud logging provider) with your own choices.","In these cases it's easy to do so since QuickDapp is distributed as source code which you then modify/enhance to build your dapp. Meaning that any and every aspect of QuickDapp can be modified as you see fit. There are no limits or restrictions.","Note that parts of the documentation will touch on how you can customize and/or replace certain components to your liking whilst still taking advantage of QuickDapp's other useful features."]},{"i":"where-do-i-start","l":"Where do I start?","p":["The Getting Started section will get you up and running quickly.","The remainder of this documentation gives you a thorough understanding of all the different parts of QuickDapp and how to get the most out of the framework as a whole."]}],[{"l":"Architecture","p":["QuickDapp is built on Next.js. Thus, if you can build Next.js apps then you can build a QuickDapp. In addition to this, it also provides a background worker Node process as well as various command-line tools for running and deploying the dapp and worker servers."]},{"l":"File-system layout","p":["Note: This is not intended to be an exhaustive list of all the files in repository, but should give you a good idea of where things are."]}],[{"l":"Getting started"},{"i":"step-0---pre-requisites","l":"Step 0 - Pre-requisites","p":["Ensure you have the following pre-requisites installed and ready:","Node.js v20+.","We recommend using NVM to manage multiple Node.js versions simultaneously.","PNPM.","PostgreSQL 11+ running locally on port 5432, with a default admin user called postgres."]},{"i":"step-1---source-code","l":"Step 1 - Source code","p":["Unzip the QuickDapp zip file you received when you purchased a license. If you are pro user then you can visit the repository at https://github.com/QuickDapp/QuickDapp and download a ZIP of any of the git version tags or you can just fork the master branch (if you're feeling brave!)."]},{"i":"step-2---dependencies","l":"Step 2 - Dependencies","p":["In the project folder, let's install the dependencies:","Now, let's bootstrap the project and generate the initial scaffolding:","This installs a Git hook which ensures your future commit messages adhere to the conventional commits. It also generates the Prisma client."]},{"i":"step-3---postgresql-database","l":"Step 3 - PostgreSQL database","p":["By default, QuickDapp assumes the existence of a PostgreSQL database. The default connection parameters (defined in the .env file) are:","host: localhost","port: 5432","user: postgres","db: quickdapp","schema: public","If you haven't already, create the quickdapp database, ensuring the postgres user has full system-level privileged access to it:","Let's get the dev database setup:"]},{"i":"step-4---demo-contracts-deployed-locally","l":"Step 4 - Demo contracts deployed locally","p":["The QuickDapp source code is actually a fully working Dapp which lets you deploy and interact with custom ERC-20 token contracts. The smart contract used is from the public @QuickDapp/contracts repository.","This @QuickDapp/contracts repository contains commands to setup a local node (using Anvil) and deploy the proxy contract to it.","First, clone this repository into the QuickDapp project folder:","There should now be a ./contracts folder. Now let's set it up:","Now let's run the local Anvil node and deploy the Diamond Proxy to it, and watch for changes:","When you run the above command you will see output which looks like this:","Follow the instructions and place this into either one of those files. Create the file if it doesn't exist.","At this point we have locally running Anvil node with our upgradeable proxy contract deployed to it."]},{"i":"step-5---setup-metamask","l":"Step 5 - Setup metamask","p":["The local node pre-funds a number of test wallets with money. The mnemonic used to generate these wallets is usually:","Note: Double-check that this mnemonic is correct by comparing with with the anvil node output above","Enter this mnemonic into your browser Metamask wallet to ensure you can use these accounts to interact with the contract that is now deployed on your local node."]},{"i":"step-6---run-dev-servers","l":"Step 6 - Run dev servers","p":["Now we're ready to run the dapp and test it locally. In the QuickDapp project folder:","This does the following:","Starts the Next.js dev server mode.","Starts the worker process dev server."]},{"i":"step-7---interact-with-the-dapp","l":"Step 7 - Interact with the dapp","p":["Goto http://localhost:3000 in your Metamask-enabled browser to interact with the dapp!"]},{"i":"step-8---deploying-to-production","l":"Step 8 - Deploying to production","p":["The following steps all deal with deploying our dapp to production.","We will do the following:","Deploy smart contracts to Sepolia test network.","Deploy the dapp (both Next.js + background worker process) as a Docker image to DigitalOcean's App platform.","Use a hosted PostgreSQL database on DigitalOcean as the production database.","If you wish, the Next.js dapp can be deployed by itself to Vercel and other serverless hosts very easily using their normal deployment processes. The docker image can just contain the background worker process. The choice is yours."]},{"i":"step-9---setup-production-database","l":"Step 9 - Setup production database","p":["We will setup a PostgreSQL database on DigitalOcean as our production database.","Pre-requisites:","Sign-up to DigitalOcean.","Obtain a Personal Access Token.","Create a PostgreSQL database cluster.","In the QuickDapp project folder, create a .env.production file, entering the access token as follows:","Now let's setup a database in the cluster named quickdapp, with a user named quickdappuser to access it:","This command will output the database connection string, which will look similar to:","Enter this connection string as the DATABASE_URL environment variable in the .env.production file, e.g:","Now setup the production database schema:"]},{"i":"step-9---deploy-contracts-to-sepolia","l":"Step 9 - Deploy contracts to Sepolia","p":["We will deploy the contracts to the Sepolia test network. This means the production dapp will require the user's wallet be connected to Sepolia in order for interactions to work.","Pre-requisites:","Setup a new Ethereum wallet using a mnemonic (you can do this in Metamask).","Fund this new allet using the Sepolia ETH faucet.","The following two environment variables need to be set in the shell environment for the deployment to work:","MNEMONIC- Mnemonic to the new wallet setup earlier.","SEPOLIA_RPC_URL- RPC endpoint for accessing Sepolia. You can get one from Alchemy.","Set these environment variables in the shell environment:","Now go into the contracts folder and run:","Note down the deployed proxy contract address from the log output. The address can also be found inside the gemforge.deployments.json file.","Go back into the QuickDapp project folder and edit the .env.production file, adding the proxy address follows:","You are now ready to build the production dapp."]},{"i":"step-10---test-run-production-build-locally","l":"Step 10 - Test-run production build locally","p":["Note: This step is optional, and is useful it you want to debug some production issues locally","In the project folder, build the production apps:","Now, run the production apps:","Now goto http://localhost:3000 in your Metamask-enabled browser to interact with the dapp. You will need to connect to the Sepolia test network in your wallet."]},{"i":"step-11---build-docker-image","l":"Step 11 - Build docker image","p":["We are going to build a Docker image to run both the Next.js app and background worker processes in tandem.","Pre-requisites:","Install Docker and ensure that the Docker daemon is running in the background.","In the project folder run:","This will build a Docker image named quickdapp-all.","You can test-run this image locally using:","Now goto http://localhost:3000 in your Metamask-enabled browser to interact with the dapp. You will need to connect to the Sepolia test network in your wallet."]},{"i":"step-12---deploy-to-digitalocean","l":"Step 12 - Deploy to DigitalOcean","p":["To push the quickdapp-all image to DigitalOcean:","This will create a container registry named quickdapp if it doesn't already exist and push the local image to it.","At this point we can use the App platform to create an app from this image. Follow the official docs for instructions on how to do this.","If the App build succeeds (and it should!) your dapp will be up and running at the URL presented by DigitalOcean.","Now, every time you push an updated image to the container registry the App will auto-redeploy."]},{"i":"step-13---hurrah","l":"Step 13 - Hurrah!","p":["Congratulations! your dapp is now available on the web in production mode.","If you wish to gain more insight into what your dapp is doing a good starting point is to enable cloud logging."]}],[{"l":"Environment variables","p":[".env",".env contains APP_NAME=root",".env.development contains APP_NAME=dev",".env.development or .env.production, depending on the value of the NODE_ENV environment variable.",".env.local",".env.local contains APP_NAME=local",".env.production contains APP_NAME=prod","Environment variables are loaded in the following order, with later places taking precendence over earlier ones:","Environment variables are the primary means through which to pass configuration to your app at both build-time and runtime.","Example","If we restore the .env.local file but this time run the production server then the runtime value will be local. However, if we delete the .env.local file again and restart the production server then the runtime value will be prod.","Let's assume that:","QuickDapp follows the Next.js conventions when it comes to specifying environment variables for your app.","Some key points to note:","The .env file is required. This file should contain values suitable for local development as well as any values which are unlikely to change across environments. It gets bundled into production and Docker builds and can be checked into source control.","The .env.development and .env.production files are already git-ignored and should never be checked into source control.","The .env.development file gets loaded when the app is run in dev server mode.","The .env.local file is for further customization in all environments, e.g if you are temporarily testing a value different to the default. However, note that it does not get bundled into Docker images.","The .env.production file gets loaded when building production version of the app, running the production version and also gets bundled into Docker images.","The dotenv loads environement varibles from various .env* files in the project root folder whilst still allowing for overrides specified directly via the shell/terminal environment itself.","The files use the INI file format:","What this means is that if the same environment variable is declared in multiple places then its final value during runtime is determined by the above order.","When the dev server is run, at runtime the process.env.APP_NAME value will equal local. If we delete the .env.local file and restart the server then the runtime value will be dev."]},{"l":"Client-side vs Server-side","p":["There are two types of environment variables: client-side and server-side.","Client-side environment variables are made available client-side in the browser and are prefixed with NEXT_PUBLIC_ to indicate this. Any environment variable that needs to be accessible client-side must be given this prefix. Since these variables are accessible browser-side they must not hold sensitive passwords, API keys or any other information that browser clients are not meant to see.","Server-side environment variables are only available server-side and are never sent to the browser. Thus these variables can hold sensitive passwords, API keys and other information that browser clients are not meant to see.","Note: All client-side environment variables are automatically available to server-side code."]},{"l":"Overriding at runtime","p":["Server-side environment variables can be overridden at runtime by setting them in the shell/environment prior to starting the app. For example:","However, client-side environment variables get bundled (i.e. hardcoded) into the frontend application as part of the build process and thus cannot be overridden at runtime."]},{"l":"Programmatic access","p":["The src/config/** modules are responsible for loading, parsing and ensuring the right syntax for each of the various environment variables. It is recommended that your code access environment variables through this rather than accessing process.env directly."]},{"l":"Client-side","p":["Although client-side variables are defined with the NEXT_PUBLIC_ prefix, at runtime the prefix gets removed to make your code more readable."]},{"l":"Server-side"},{"l":"Adding your own variables","p":["To add your own custom environment variable:","Add your variable to either src/config/client.ts or src/config/server.ts depending on whether it is a client-side or server-side variable. Follow the conventions used for existing environment variables, including removing the NEXT_PUBLIC_ prefix at runtime for client-side variables.","Add a default value for your variable to the .env file, again following the conventions shown in in the file for other variables. If your variable does not have a default value then set its value inside .env to the empty string ().","Optionally add a value override for your variable to either or both of .env.development and .env.production."]},{"l":"Example","p":["Let's add a client-side variable called SHOW_COOKIE_BANNER which will specify whether a cookie consent banner should be shown to the user. Our requirements for this variable:","It can only be a boolean value - true or false.","It should be false in development environments.","It should be true in production environments.","First, we will edit src/config.client.ts to add the variable:","In .env we set it to false:","In .env.production we set it to true:"]}],[{"i":"command-line-cli","l":"Command-line (CLI)","p":["The QuickDapp CLI is a collection of Node.js scripts which live within the ./scripts folder.","The available commands are:","dev- run development code, interact with development database.","contracts- interact with ./contracts folder, build and deploy contracts to a local node.","build- build production code.","prod- run production code, interact with production database.","docker- build and run Docker images.","do-cloud- interact with DigitalOcean cloud hosting.","To get help on any command simply affix --help. However, note that some commands have sub-commands, e.g:","build- build both web + worker production servers","To get usage help: build (default) --help","build web- build web production server only","To get usage help: build web --help"]}],[{"l":"dev","p":["The dev command is responsible for:","Running the Next.js dev server.","Running the background worker dev server.","Running development database management commands.","All of these commands operate using the development environment configuration as defined via the environment variables.","The servers are spawned as child processes of the CLI command script, with their stdio mapping to the console."]},{"i":"nextjs","l":"Next.js","p":["This internally runs:","Next.js dev server which watches for changes to your web app and reloads automatically.","GraphQL code generator which auto-generates Typescript bindings when your GraphQL schema changes.","The website will be accessible at http://localhost:3000."]},{"l":"Background worker","p":["This internally runs:","Node.js process for the background worker which auto-restarts when changes are made to the worker code.","Code generator which auto-generates Typescript exports when a background job is added or modified.","## Running servers simultaneously","To run both the the Next.js and background worker servers simultaneously:"]},{"l":"Database management","p":["To migrate the database to the latest schema:","To reset the database to the latest schema and clear all of its data:","To re-generate the Prisma client and associated Typescript bindings:"]}],[{"l":"contracts","p":["The contracts command is responsible for:","Initializing the contracts repository in the contracts/ folder.","Running a local node and deploying the contracts to it","Re-building and redeploying when contract code changes.","This command assumes that the concracts are in the contracts/ sub-folder and that they are based on the official contracts repository."]},{"l":"Initialize contracts","p":["This command must be run after the contracts folder has been setup. It will install any dependencies required to build and deploy contracts."]},{"l":"Local development deployment","p":["This does the following:","Run a local Anvil node","Build and deploy the Diamond Proxy and associated facets to the local node","Re-build and upgrade the on-chain contract whenever the contracts source code gets changed.","Rebuilding the contracts will cause the web and worker servers to auto-reload the ABI from the contracts folder. Thus, you can now develop your app code and contracts in tandem."]}],[{"l":"build","p":["The build command is responsible for:","Building the Next.js production code.","Building the background worker production code."]},{"i":"nextjs","l":"Next.js","p":["The .next/standalone/ folder will contain the output production code."]},{"l":"Background worker","p":["The build/ folder will contain the output JS code as a single file."]},{"l":"Building both together","p":["To build both the the Next.js and background worker production code together:"]}],[{"l":"prod","p":["The prod command is responsible for:","Running the Next.js production server.","Running the background worker production server.","Running production database management commands.","All of these commands operate using the production environment configuration as defined via the environment variables.","The servers are spawned as child processes of the CLI command script, with their stdio mapping to the console."]},{"i":"nextjs","l":"Next.js","p":["The website will be accessible at http://localhost:3000."]},{"l":"Background worker","p":["## Running servers simultaneously","To run both the the Next.js and background worker servers simultaneously:"]},{"l":"Database management","p":["To migrate the database to the latest schema:"]}],[{"l":"docker","p":["The docker command is responsible for:","Building and running Next.js production Docker images.","Building and running background worker production Docker images.","All of these commands operate using the production environment configuration as defined via the environment variables."]},{"i":"nextjs","l":"Next.js","p":["To run this image:","The built image will be named quickdapp-web- this can be customized using the --prefix option. Example:"]},{"l":"Background worker","p":["To run this image:","The built image will be named quickdapp-worker- this can be customized using the --prefix option. Example:"]},{"i":"combined-nextjs--worker","l":"Combined Next.js + worker","p":["To run this image:","The built image will be named quickdapp-all- this can be customized using the --prefix option. Example:"]},{"l":"Terminal mode","p":["To run any of the Docker images in terminal mode so that you can browse within the container using a shell, append the --term option, e.g:"]}],[{"l":"do-cloud","p":["The do-cloud command is responsible for interacting with the DigitalOcean API. Specifically it enables the following:","Provision a managed PostgreSQL database and associated user.","Push Docker images to a managed container registry.","All of these commands require the following environment variable to be set in either .env.local(recommended) or .env.production:","DIGITALOCEAN_ACCESS_TOKEN- set this to a DigitalOcean personal access token."]},{"l":"Database provisining","p":["This will setup a managed PostgreSQL database, creating a basic db cluster if it doesn't already exist.","If existing clusters are found then it will ask you to select the cluster within which to create the database.","It will then create a PostgreSQL user that is able to access and modify the newly created database.","The default database name and username are both quickdapp. To customize:","The final output of the command will be the database connection string to set as the DATABASE_URL environment variable, e.g:"]},{"l":"Docker images","p":["This will first create a Docker container registry if it doesn't already exist.","If a registry needs to be created then the most basic type of registry will be created. At the time of writing (Dec 2024) this can only hold a single Docker image. If you wish to push multiple images to it then you will need to upgrade it manually via the DigitalOcean dashboard.","By default the created registry is called quickdapp. To customize the name:","If the myregistry registry already exists then it will be used, otherwise it will be created.","Once the registry has been created the specified Docker image will be pushed to it. Note that the Docker tag is always set to latest.","If you had used a custom docker image name prefix when building the images then you will need to supply that prefix here too, e.g:"]}],[{"l":"Other commands"},{"l":"bootstrap","p":["The bootstrap command initializes QuickDapp, and only needs to be run once, after NPM dependencies have been installed.","It installs a git hook which ensures your future commit messages adhere to the conventional commits.","Example usage:"]},{"l":"showdocs","p":["The showdocs command is responsible for building and rendering this documentation in the browser using Retype.","Example usage:","The documentation will now be accessible at http://localhost:5000."]}],[{"l":"Backend architecture","p":["The web backend is built on Next.js, a serverless Node.js framework for serving up React.js apps with server-side rendering and static page generation enabled.","Prisma is integrated as an ORM layer for accessing a persistent database(PostgreSQL by default).","A GraphQL API layer allows for the frontend (and indeed, any third-party client) to read from and write to the backend.","Configuration parameters are supplied via server-side-only environment variables.","A hierarchical logging system allows for categorised logging output with differing log level thresholds."]}],[{"l":"Bootstrap object","p":["The src/backend/boostrap/index.ts file contains code which is executed on every backend API and/or page invocation. This code bootstraps basic backend services and returns a corresponding BootstrappedApp object which looks something like:","This object operates as a sort of backend application-level context and gets passed around the various components of the backend code.","For any application-level objects or services used throughout your backend, it is recommended that you define them in the BootstrappedApp interface and then initialize them within the bootstrap code."]}],[{"l":"Logging","p":["The backend logging system is built on bunyan and is designed for categorised, hierarchical logging with log thresholds.","Each log treshold has a corresponding method with the same name available on a logger instance. The possible log thresholds are:","trace- for very low-level messages that you normally don't need to see.","debug- for messages that can help with debugging issues.","info- for general messages that you want to see in the logs in production.","warn- for minor issues that don't cause problems but may need investigating.","error- for problems and errors that need to be investigated and/or fixed.","For example, to create a logger which logs to console:","Now a child logger can be created from the root logger:","There is always an instantiated log property in the bootstrapped object."]},{"l":"Datadog cloud","p":["Log messages will be sent to Datadog if the following environment variables are set:","DATADOG_API_KEY","DATADOG_APPLICATION_KEY","See https://github.com/DataDog/datadog-api-client-typescript for information on these parameters."]}],[{"l":"Database","p":["QuickDapp supports PostgreSQL databases by default, with Prisma as the ORM layer.","Technically speaking, any database type supported by Prisma could be used, although note that some of the DigitalOcean database-related commands (e.g do-cloud) will then require modification."]},{"l":"Connection parameters","p":["The database connection parameters are supplied as the DATABASE_URL environment variable, e.g:"]},{"l":"Schema","p":["A number of built-in tables are provided for your convenince:","Setting- for any persisted app-level settings as key-value pairs.","User- for authenticated users - includes a field for storing the user's wallet address and any settings field which can hold any JSON data.","Notification- for notifying users with a message or other data.","WorkerJob- for the background worker task queue."]},{"l":"Accessing","p":["All database tables are accessed through purpose-built model code found in src/backend/db. This provides for an abstraction layer over the database which hids the raw Prisma commands from other backend code. Each model function excpets a db parameter which can be obtained from the bootstrap object."]},{"l":"Local development","p":["To migrate the database to the latest schema:","To reset the database to the latest schema and clear all of its data:","To re-generate the Prisma client and associated Typescript bindings:"]},{"l":"Production deployment","p":["For the production database only a migration/upgrade ability is provided.","To migrate the database to the latest schema:"]}],[{"l":"Sending emails","p":["Sending emails via Mailgun is supported out of the box.","The following environment variables must be set for email sending to be enabled:","MAILGUN_API_KEY- the Mailgun domain sending API key.","MAILGUN_API_ENDPOINT- the Mailgun API endpoint (e.g https://api.eu.mailgun.net/).","MAILGUN_FROM_ADDRESS- email address for the from field in an email - must end in @DOMAIN where DOMAIN is the domain for which Mailgun sending is configured.","If the mailer is enabled then the mailer property in the bootstrapped object will be set."]}],[{"l":"Push notifications","p":["Real-time push notifications can be sent to browser clients using Ably.","All of the following environment variables must be set for this to be enabled:","ABLY_API_KEY- the Ably API key.","If enabled then the ably property in the bootstrapped object will be set."]}],[{"l":"Frontend architecture","p":["The front-end is written in React.js, using the Next.js architecture.","There are predefined React contexts provided to make co-ordination between different parts of your app easier, e.g global and cookie consent."]}],[{"l":"Web3","p":["Web3 wallet discovery, authentication and interaction is facilitated via RainbowKit, Wagmi, Viem and Sign-in-with-Ethereum."]},{"l":"Chain configuration","p":["QuickDapp is built for EVM chains. The target EVM chain is specified via the following environment variables:","NEXT_PUBLIC_CHAIN","NEXT_PUBLIC_CHAIN_RPC_ENDPOINT git","Note that both variables are needed for the chain configuration to work correctly. The backend server and worker processes also rely on these environment variables to know which chain to monitor."]},{"l":"Connection and authentication","p":["The ConnectWallet component triggers the RainbowKit connection dialog. By default, once a wallet is connected the user is prompted to sign a message with their private key to prove that they actually own this wallet.","The signed message is sent to the backend for verification. Once verified, a JWT (JSON Web Token) is used to store the user's sessions for any subsequent queries made to the backend. The global context object will also be updated to contains the user's wallet information.","The user remains authenticated across browser sessions until and unless the user changes their active wallet and/or manually disconnects from the dapp.","Note: If the user's crypto wallet is connected to a different chain then they will be prompted to switch to the right chain."]},{"l":"Contracts","p":["Please refer to the the section on Smart Contracts for a general introduction."]},{"i":"readwrite-hooks","l":"Read/write hooks","p":["The following high-level contract interaction hooks are provided:","useGetContractValue- call a read-only method on a single contract.","useGetMultipleContractValues- call multiple read-only methods on multiple contracts. (Note: The background worker will deploy Multicall3 to the chain if not already present).","useGetContractPaginatedValues- read paginated values from a single contract.","useSetContractValue- call a read-write method on a contract with a transaction.","You can of course use the Wagmi React hooks to access contract methods if you wish. However, the above hooks incorporate the ABI and address fetching methods above to provide for a more seamless interaction with contracts.","The default QuickDapp dapp (the built-in ERC-20 demo) utilises all of these hooks, providing you with working code examples."]}],[{"l":"GraphQL queries","p":["All API requests made to the backend are done as GraphQL requests.","React query is used to actually execute queries and handle pagination, optimistic updates, etc. The src/frontend/hooks/api.ts file contains wrapper hooks for queries/mutations that need to be made in the dapp. It is recommended that you stick to this convention when adding new queries.","The src/shared/graphql/schema.ts file contains all the queries and mutations available. Adding a new query or mutation involves the following:","Update schema.ts.","Update one or more of fragments.ts, queries.ts, mutations.ts.","Update the backend resolvers.ts to ensure the query gets handled in the backend.","In dev mode a code generator watches for changes to schema.ts, generating updated Typescript bindings on-the-fly. These bindings are used by the backend resolvers to accurately resolve query and parameter names."]},{"l":"Authenticated queries","p":["It's useful to be able to have certain queries and mutations only be callable by authenticated users. This is easily accomplished through the use of the @auth directive in the schema definition:","Example:","If the user isn't authenticated (i.e. logged in) then the above getMyNotifications query will fail.","Authenticated queries/mutations automatically have a variable set within their corresponding backend resolver contexts, e.g:"]},{"l":"Sandbox GUI","p":["When the dev server is run, accessing the /api/graphql path in the browser will bring up the Apollo Sandbox. Through this GUI you can enumerate and test all the GraphQL schema definitions, including queries and mutations."]}],[{"l":"React components","p":["Button- Buttons. Supports a number of sizes and variants, and you can easily add your own.","ClientOnly- Only renders its children client-side - useful for wrapping any components that should not be rendered server-side. If you have React hydration issues you might need to use this component to wrap parts of your UI.","ConnectWallet- Wallet connection button.","ContractValue- Displaying values returned from a smart contract. Handles loading/error states and allows for values sanitization prior to rendering.","Dialog- Modal dialog popups.","ErrorButton- Error buttons (uses Button under the hood).","ErrorMessageBox- Messages boxes which show large error traces.","Footer- The site footer.","Form- Forms and their fields, to be used in conjunction with the form hooks.","Header- The site header.","Icons- SVG icons from Lucide React.","IfWalletConnected- Only renders its children if the user is authenticated; otherwise it renders the wallet connection button ( ConnectWallet).","Loading- Loading indicator animation.","Notifications- User notifications dialog and indicator icon.","OnceVisibleInViewport- Triggers a passed-in callback when its UI (a div /) comes into view. This is useful for performing visibility-based actions.","PingAnimation- pinging animation (used for notifications icon).","Popover- In-place popups (used by Tooltip component).","Svg- SVG rendering.","The bundled React components are mostly generic, although some are specific to the built-in dapp, e.g CreateTokenDialog. All dapp-specific components are in the src/frontend/components/dapp folder.","The re-usable generic components are:","Tooltip- Tooltip popups."]},{"i":"shadcnui","l":"@shadcn/ui","p":["Key reusable UI components - Dialog, Popover, etc - are actually @shadcn/ui components. These are a great set of starter components to build on top of as they are well architected, so we recommend you first check these component libraries before rolling your own.","The bundled components.json file is the configuration for @shadcn/ui.","To install a new component:","At this point the src/frontend/components folder will contain a component name.tsx file containing the code to use. Feel free to then modify this code as you see fit."]}],[{"l":"Forms","p":["QuickDapp comes with a powerful form handling framework, comprising of hooks and components which work in tandem to make form processing easy. Its features:","Features:","Built-in fields: dropdown, text, textarea, number.","Ability to support any custom input type.","Field-level sanitization, validation and error handling.","Form-level validation and error reporting.","Asynchronous validation with debounce delay.","The built-in example dapp illustrates all aspects of this framework."]},{"l":"Hooks","p":["There are two hooks, both found in src/frontend/hooks/forms.ts:","useForm()- defines the fields of a form as well as form-level validation methods.","useField()- defines a single field - its name, initial value, sanitization, validation and whether it is a required field.","Example usage:","The validator methods are expected to return an error string if there is a validation error. This error string is what gets displayed to the user.","For example:","For efficiency purposes, it's best to define the validation functions outside of the component or wrapped within useCallback hooks so that the useField hook doesn't return a friend field each time."]},{"l":"Components","p":["Here is how the UI component source might look, using the properties defined earlier:","The built-in components automatically display any field-level validation errors. For form level validation errors you have to manually display them as, as shown above.","The onSubmit handler would look similar to:"]}],[{"l":"Global context","p":["The GlobalContext object contains information on the currently authenticated user and their wallet, the currently connected blockchain as well as an interface for accessing push notifications.","The context is accessed through the useGlobalContext() hook as such:"]}],[{"l":"Cookie consent","p":["Cookie law compliance is made easy through the use of a ready-made cookie consent architecture.","A user may choose to allow or disallow cookies. Their response is cached in window.localStorage so that they do not get asked again. Note that this also means that once a user has allowed or disallowed cookies they cannot change their response later on unless you modify the cookie consent logic or clear the window.localStorage data."]},{"l":"Consent banner","p":["There is a built-in consent banner which gets shown to user's when they first visit the site. This appears as an overlay at the footer of the page and is triggered automatically if the user has not yet set their response."]},{"l":"CookieConsentContext","p":["The CookieConsentContext object is responsible for keeping track of the user's cookie consent response. It loads/saves a user's response from/to window.localStorage. It also provides methods for triggering the built-in consent popup."]},{"i":"getusercookieconsent","l":"getUserCookieConsent()","p":["The getUserCookieConsent() method provides a way for you to run any custom code after a user's cookie consent response has been recorded, e.g:","This method can be called from anywhere within the frontend app, as many times as you like.","If the user has already recorded their cookie response (e.g in a previous browser session) then the callbacks will get executed immediately. Otherwise the callbacks will only get executed once the user records a response in the cookie consent banner.","The analytics feature requires cookies and so it relies on this method to activate itself."]}],[{"l":"Analytics","p":["Front-end analytics and browser session recording is done through Datadog. This allows you to see what users's see on the page as well as how they move around the page, visually.","To enable these, you'll need to set the required environment variables:","NEXT_PUBLIC_DATADOG_APPLICATION_ID","NEXT_PUBLIC_DATADOG_CLIENT_TOKEN","NEXT_PUBLIC_DATADOG_SITE","NEXT_PUBLIC_DATADOG_SERVICE","(See https://docs.datadoghq.com/real_user_monitoring/browser/ for information on these parameters)","Note that all the above environment variables must be set for analytics to be enabled. Furthermore, the user must give consent for cookies to be stored."]}],[{"l":"Background worker","p":["QuickDapp bundles a background worker process that runs in parallel to the Next.js server process, so that you can schedule long-running persistent background jobs at any time."]},{"i":"why-its-useful","l":"Why it's useful","p":["Next.js apps tend to be deployed to serverless environments for scalability and efficiency reasons. As such, there is no persistent server, meaning there is no way to run a backend process which persists beyond the lifetime of an incoming request.","Although one could use a cron service to periodically call a Next.js route, many serverless clouds (e.g Vercel) cap the duration for which any given route can run, limiting the work that can be done. In any case, a serverless architecture is not the ideal approach for long-running background jobs."]},{"l":"How it works","p":["Jobs are scheduled via the WorkerJob database table. All job results (both successes and failures) are logged. If a job fails it can be set to automatically retry after a predefined delay. Jobs can also be run regularly according to a cron schedule.","The worker process has full access to all of the same backend resources as the Next.js server. However, it does not deal with any frontend resources and does not listen for incoming connections. Thus, to know what's going on with a worker process you will need to monitor the database as well as log messages.","Although it's possible to run more than 1 worker process to scale up, note that there is currently no mutex mechanism to prevent two or more workers may end up executing the same task at the same time. Though this is unlikely to happen often anyway, it should be noted that it could happen."]},{"l":"Configuration","p":["It loads its configuration from the same environment variables as every other part of your dapp.","The only worker-specific environment variables are:","WORKER_LOG_LEVEL- Minimum logging threshold level. Allows you to log worker process messages at a different minimum threshold to that of the Next.js backend.","## Database table","Any data returned by a job's run() method will be stored as JSON in the worker job table row corresponding to that particular invocation of the job."]}],[{"l":"Adding a job","p":["To create a new type of job a new file must be added to src/worker/jobs which adheres to the Job interface:","In dev mode, when any changes are made to the src/worker/jobs folder contents a code generator automatically re-generates the Typescript bindings (see src/worker/generated) - these are used by worker-related database methods."]},{"l":"Scheduling a job","p":["The two job scheduling methods are:","A job is specified as follows:","The scheduleCronJob() method additionally requires a crontab specification."]},{"l":"Pending jobs","p":["When a new job is scheduled any matching pending jobs are first cancelled.","A matching pending job is one which has all the following properties:","It has the same type and userId as the new job.","It has not yet started OR it started over an hour ago but is yet to complete.","Thus, you should ensure any given job doesn't execute for longer than an hour."]},{"l":"Database table","p":["Regarding the worker database table schema, the following two fields should be noted:b","result- The data returned by the job's run() method if successful.","rescheduledFromJob- If the job has been rescheduled from an earlier job (whether due to a cron schedule or as an auto-retry-on-failure) this field will hold a reference to that earlier job."]}],[{"l":"Built-in jobs","p":["There are two built-in worker jobs which are set to run on a cron schedule:","removeOldWorkerJobs- Removes successfully completed jobs. Runs every minute.","watchChain- Watches the blockchain for changes according to user-defined filters. Runs every 5 seconds."]},{"l":"Watching the blockchain","p":["Watching and reacting to blockchain events is done by defining user-defined filters in the src/worker/chainFilters folder. The built-in watchChain job uses these filters to watch the blockchain for changes.","The chain to watch is defined by the chain-related environment variables.","Each filter module must adhere to the following interface:","The default bundled dapp has two filters - createToken, sendToken- which serve as examples of how to use this architecture. Both filters listen for events emitted from the dapp contract."]}],[{"l":"Users","p":["QuickDapp has a built-in user authentication and management system that can easily be extended to suit your requirements.","All users are stored in the users database table. By default each user record stores the user's unique Ethereum wallet address as well as an settings JSON object."]}],[{"l":"Authentication","p":["User authentication is built on top of NextAuth, with Sign-in-with-Ethereum (SIWE) as an authentication mechanism.","The flow:","A user must connect their web3 wallet to the Dapp.","They are asked to sign a login message using their private key to prove ownership of the wallet.","The signature is sent to the NextAuth backend for verification.","Once verified, a corresponding JWT (JSON web token) is stored client-side, to be passed in with all subsequent queries to the backend. The token persists across browser sessions, until the user chooses to disconnect their wallet.","If a database record for the user doesn't yet exist then it is created during the backed verification step."]}],[{"l":"Notifications","p":["The built-in user notifications system lets the backend notify a user of something new. This could be a simple message and/or a JSON data blob.","The notifications database table is responsible for storing all notifications and recording whether they have seen by the user or not.","Note that notifications can only be created from within the backend (including background workers)."]},{"l":"Creating a notification","p":["The backend bootstrap object provides a convenient API for creating notifications:","This does two things:","Creates the corresponding entry in the notifications table.","Sends a push notification to the user informing them of new notifications. (The frontend automatically updates the notifications indicator in the browser UI).","The data parameter can contain any structure, though at present the following structure is handled in the UI by default:"]}],[{"l":"Smart contracts","p":["QuickDapp is setup to work with forks of the @QuickDapp/contracts repository.","You can of course use whatever smart contract architecture you want but it's worth forking the default contracts repository as a starting point since it is setup to make development easy.","The default repository has the following features:","Upgradeable proxy contract based on the EIP-2535 Diamond Standard.","Deployments and upgrades (both dev and production) managed using Gemforge.","Generates an ABI for the proxy contract to be used by your dapp.","Works with the contracts command out of the box."]}],[{"l":"ABIs and addresses","p":["All required ABIs and contract addresses should be specified in src/shared/contracts.ts, following the format there-in.","The following methods are provided:","getContractInfo- returns ABI of a contract","getDeployedContractInfo- returns ABU and address of a contract on a given chain.","getMulticall3Info- returns address of Multicall3 contract on the current chain. (Note: the address should be the same on every chain)."]}],[{"l":"Deployment","p":["QuickDapp deployment consists of deploying both:","Next.js app","Background worker process","You will most likely want to deploy the dapp to Vercel or an equivalent serverless environment for maximum scalability.","However, QuickDapp has built-in support for building Docker images of the dapp in order to deploy it to hosted container environments. The background worker process must be built as a docker image."]}],[{"l":"Docker images","p":["The docker command is used to build Docker images. By default, 3 types of images are supported:","web- Next.js app server. ( Exposes the app on port 3000).","worker- Background worker server.","all- Next.js app server + background worker server combined. ( Exposes the Next.js app on port 3000).","The images internally bundle the QuickDapp CLI so that the actual entrypoint for each image is the prod command. This means the servers are spawned as child processes of the main process. If any server crashes then the main process (and thus the Docker container) will also crash and exit.","Images are based on Alpine Linux and are built to hold only the minimal required dependencies to run the servers. Thus, even the all image comes to <500 MB in size.","To make this possible, Webpack is used to build the background worker process to a single file. For the Next.js app the standalone option is enabled in the config."]},{"l":"Deployment","p":["One built the images can be run locally and/or deployed to any Docker container hosting environment of your choice.","At present, QuickDapp provides built-in support for deploying to DigitalOcean."]},{"l":"Bundling NPM packages","p":["Most Node packages will get automatically bundled into the built output by webpack. However, some packages will still require their node_modules sub-folders to exist at runtime in order to work properly (e.g Prisma).","These can be seen inside the Dockerfile, e.g:","If you are using a package also has the same requirement then simply add a corresponding line for it in all of the sections in the Dockerfile. For example, if you you are using the mailgen package then you would add a single line for each of the three build image types:","Some NPM packages need to bundled"]}],[{"l":"Deploying to Vercel","p":["You can deploy the QuickDapp Next.js app to Vercel by simply deploying from your Github repo within the Vercel dashboard.","Note that since the .env.production file does not get checked into version control - this is intentional. Instead, the production environment variables will need to be set in Vercel's dashboard, which is their recommended method.","Vercel will only build the Next.js web app (thus excluding the background worker). This behaviour can be changed by editing the vercel.json config file."]}],[{"l":"Deploying to DigitalOcean","p":["The do-cloud command makes it easy to deploy built Docker images to your DigitalOcean docker container registry. If a registry doesn't yet exist it will be created.","To actually deploy and run your application you will need to create an App in the DigitalOcean dashboard, pointing it your container registry and Docker image.","This way, every time a new version of the Docker image is pushed to the registry, the live app will be automatically updated to use this image. At this point you will have something akin to \"Continuous Deployment\" setup."]},{"l":"Database setup","p":["You can also use the do-cloud command to setup a PostgreSQL database in DigitalOcean for your live app to use. For maximum communication efficiency ensure that both the App and database are located in the same regional datacenter."]}]]
\ No newline at end of file
diff --git a/sitemap.xml.gz b/sitemap.xml.gz
index 5ba29c4..1a5029b 100644
Binary files a/sitemap.xml.gz and b/sitemap.xml.gz differ
diff --git a/smart-contracts/abis-and-addresses/index.html b/smart-contracts/abis-and-addresses/index.html
index af84b21..73ba6b5 100644
--- a/smart-contracts/abis-and-addresses/index.html
+++ b/smart-contracts/abis-and-addresses/index.html
@@ -3,7 +3,7 @@
-
+
@@ -31,11 +31,11 @@
-
+
-
+
-
+