All you need to start your web3 service, ๐ included.
This project has been deployed using Vercel, Hasura Cloud and Railway.app. You can check it out here. For more information about the deployment process, check the deployment section ๐.
-
First you need to install pnpm in your machine
npm install -g pnpm
-
Install all the dependencies
pnpm install
-
Create a .env
Refer to this section to set your .env with the needed API keys
-
Then you can run the project
pnpm start
-
Make sure to apply the migrations to the prisma db and client once the container is running
pnpm prisma:migrate
View in NX Graph
The console is used as a backoffice to handle the graphQL server and to innerlink all the microservices.
This is the main web app client used to access the whole array of services.
- Prisma Studio:
pnpm prisma studio
The prisma studio is used to offer an admin interface to the database used by the Nestjs Server app.
This is the mail-catcher where all the mail are going in dev environment.
This is the toolkit server stack to access all the DID functionalities from walt.id: SSI, Wallet and NFT.
You can check the doc here for more information regarding the API.
Keycloak Identity and Access Management server is used as a provider for Next Auth. The IDP Kit from walt.id is used through the OpenID Connect protocol.
The login to access the administration console is admin@web3-monorepo.com / password
apps/web
: a Next.js appapps/web-e2e
: Cypress e2e test for the web appapps/storybook-main
: Main Storybook for the whole project (ui/web)apps/nestjs-server
: a Nest.js server app used to extend Hasura functionalitieshasura
: contain the config / metadata / migrations / seeds for the Hasura serviceprisma
: Prisma database schema and migrationstools
: Set of tools to to be used for DX (Developer Experience) and testing purposes.waltid-idpkit
: Contain the config files, the encryption keys for the DID server and the registered OIDC client.keycloak
: Contain all the realm settings loaded by the keycloak containerlibs/logger
: Isomorphic logger (a small wrapper around console.log)libs/utils
: Common utils functions and types for the whole projectlibs/workspace
: Contain all the generators and tooling dedicated to maintaining the NX workspace.libs/dlt/types
: contain the typescript types/interfaces related to the DLT (Blockchain) used in the projectlibs/client/gql/[user, admin]
: a library containing all the GraphQL queries and mutations and the generated schemas to be used on the web app. It is divided on 3 folders:user
,admin
andanonymous
depending of the role of the user.libs/client/gql/thegraph
: a library that use The Graph protocol in order to query data directly from smart contract on the blockchain.libs/client/hasura/adapter
: Next-auth adapter for the Hasura service.libs/client/hasura/fetcher
: Fetcher functions to query the Hasura service.libs/client/hasura/utils
: Utilities to interact with Hasura.libs/client/did/provider
: Next-auth OpenID Client provider for the walt.id idpkitlibs/client/siwe/provider
: Next-auth OpenID Client provider for the rainwbow kit.libs/client/next-auth/options
: Contain all the configs for Next-Authlibs/client/next-auth/common
: Common functions used in the context of Next-Authlibs/client/ui/components
: React reusable components librarylibs/client/ui/shared
: Functions and assets shared in the context of the UI librarylibs/client/ui/theme
: Contains all the specification for the global style and components stylelibs/test-utils/db
: All the utilities relating the handling of db operation on the context of testing, for ex: seeding/deleting.libs/test-utils/functions
: All the utilities functions common to every test runner.libs/test-utils/gql
: Offer an sdk and different test user clients to be used on test in order to interact with the hasura service.libs/server/api
: API service to query the external services (ex: CoinGecko api)libs/server/alchemy
: Low level service to interact with the Alchemy RPC providerlibs/server/arbitrum
: Service to interact with the Arbitrum RPC providerlibs/server/ethereum
: Service to interact with the Ethereum RPC providerlibs/server/polygon
: Service to interact with the Polygon RPC providerlibs/server/ethers
: High level service to interact with EVM compatible blockchainslibs/server/common
: Common functions used in the context of the serverlibs/server/cryptocurrencies
: High level service to get data related to cryptocurrencies: price, market cap, etc.libs/server/prisma
: Low level service to interact with the prisma databaselibs/server/task
: High level service for task scheduling, cron jobs, etc.
Each package/app is 100% TypeScript.
This repo is configured to be built with Docker, and Docker compose.
To build all apps in this repo:
pnpm docker:build
To shutdown all running containers:
pnpm docker:stop
To launch all the services containers:
pnpm docker:services
The command to run all the services in this repo is
pnpm docker:services
The command to run all the containers for unit and integration test is
pnpm docker:test
The web app is using next.js meta-framework v13 in order to leverage serverless api functions and react hybrid SSR/SSG/PWA capabilities. The web app is using the next-auth library to handle the authentication and the authorization and the ChakraUI components library.
The Hasura service is used as a GraphQL API gateway to the Postgres database and for introspection into the prisma database. It is also used to handle the authentication and authorization of the users through a Next-Auth adapter.
It act as the single endpoint for the web app to query the database and the external services like the Nestjs Server app in a federated way.
The Prisma ORM is used in the context of the Nest.js Server app to interact with the prisma Postgres database. It is used to generate the database schema and the migrations and offer an admin with Prisma Studio.
The Nest.js framework is used to extend the Hasura functionalities. It serves as a complementary layer to Hasura to handle the business logic of the application and to interact with the external services like the blockchain and the crypto APIs. Currently it serves as a service to handle the retrieval of the user's wallet balance of ERC20 tokens on the Ethereum/Polygon/Arbitrum blockchains. More info in the Nest.js Server app README.
You can find the Storybook for this project here
Stories are defined on the libs/ui/components
. We use interaction testing with the storybook version of jest and testing library in order to provide dynamic demonstration of the usage of individual components along with testing.
Aditionnaly, the service chromatic is launched on the CI in order to spot and approve/decline UI changes.
To create a new component, you can use the custom nx generator provided on this project @workspace - component
provided from the libs/workspace
. It will create the boilerplate code for the react component, the stories file and the jest spec file.
This repo has some additional tools already setup for you:
- TypeScript for static type checking
- ESLint for code linting
- Prettier for code formatting
- Jest test runner for all things JavaScript
- Husky Git hook library used to execute ESLint and Prettier on staged files before a commit.
- Cypress test runner for E2E and components test
- Graphql Code Generator a generator for the graphql schemas and a client builders with provided queries.
- The Graph: Graph Client a library to easily query the data from smart contract on supported blockchain such as Ethereum.
This project use Next-Auth to offer different way of authentication.
The user can sign in with a web3 wallet (Metamask, WalletConnect, etc) and the SIWE adapter will handle the request with Hasura.
You can offer login with OAuth2 providers from Google and Github by providing the corresponding env variables for users that want to connect with a web2 provider. As a fallback, it's also possible to connect with an email and password.
Additionally, this project offer a way to authenticate through a DID thanks to the Walt.id IDP Kit.
You can find the different providers used by next-auth in libs/client/next-auth/options
Hasura is used as an adapter to next-auth in order to persist in a database the user's provided information such as their id
. The adapter is located in libs/client/hasura/adapter
.
This project use Rainbowkit to offer a set of components and hooks to easily build a web3 application. Following the specs of SIWE (Sign In With Ethereum), it offers a way to authenticate in next-auth through the signature of a message with a given wallet.
This project use the IDP kit in order to offer web3 sign in with your DID.
In order to test it, you will need to follow this steps:
- Access the sign in page and click on the
Walt.id IDPKit
button - Login a new account on the walt.id web wallet by entering any credentials or by connecting your wallet
- You will be presented with a page to ask you to get the needed crendentials to be able to connect to the service. Click on the 'Fetch credential from issuer' button.
- Put any credentials to go through the fake login page of the 'Demo Issuer Portal'
- Confirm the claim of the Verifiable ID document.
- Accept the 'Received Credentials'
- Accept the 'Connection request' of 'Verifiable ID document'
- The user is logged in effectively and created on the DB through Hasura (Next Auth do it automatically for a new account)
The idpkit is configured to use the Demo Issuer Portal in order to get the credentials. This is a fake portal that will give you the credentials you need to go through the process.
This implementation of the IDP kit is by no mean production ready and is only here to show you how to use it. You will need to implement your own issuer portal and configure the IDP kit to use it.
An other use case of the IDP kit is to use it with the NFT Kit in order to sign in with an NFT. You can find the implementation of this use case in the tutorial Login with NFTs | Next.js
We use Keycloak as the main provider to authenticate with credentials or federated sign in with google. The IDP kit server is linked through with OpenID Connect protocol. Keycloak is then used as a provider by Next Auth to handle all the authentication process on the web application.
For any operation you do regarding the settings of keycloak, don't forget to export, rename the file to master-realm.json
and replace the existing file in the keycloak
folder. Otherwise your settings will not be persisted if you reset your containers or on the CI env.
Also, for each exports, your secret will be erased from the config file
"secret": "**********"
, "clientSecret": "**********"
.
You will have to set the secrets with the one you saved but make sure to not do that on production environment.
For convenience purpose everything is set correctly on master-realm.json
in order to use Keycloak directly with your app.
You will need to follow this steps to provide Keycloak with your own environment.
- Create a new Client with confidential Access Type
- Go to the Clients section and add a new client.
- Choose a client id, for instance
myApp
. - Select the Client Authentication option on the next page
- On the settings page, set the Home URL, Valid redirect URIs, Web origins according to your app URL. In our case that would be
http://localhost:3000/*
- Click on the Credentials tab to reveal and copy the client secret.
You now have the id and secret of the client to populate in KEYCLOAK_ID
and KEYCLOAK_SECRET
- Add IDPKit to Keycloak as a OpenID Connect provider
Check this tutorial for more information of how to register your own instance of IDP kit on the realm. To register your client as described in the tutorial you can use the following command:
curl --location --request GET 'http://localhost:3333/api/balances/eth/0xb21090C8f6bAC1ba614A3F529aAe728eA92B6487'
Be sure to copy the id and secret from the output in order to register it on keycloak.
As you are running both keycloak and idpkit from docker, the resulting address in Discovery endpoint
should be http://idpkit:9080/api/oidc/.well-known/openid-configuration
The command pnpm graphql-codegen
will launch the graphql-codegen
script. All the codegen definitions are written in the file codegen.yml
. You should run this command each time you modify a graphql query or update something on the hasura console to have the updated generated sdk and utilities functions.
The generator is divided in two parts, corresponding to the role of user
and admin
, targeting the graphql hasura server for those respective roles.
Each one have a grapqhl schema and an ast schema generated and specfic sdk.
User
The graphql queries definition are defined in libs/client/gql/user/queries
. We use the React-Query module in order to facilitate the querying the data for the user role in the fontend client. The hasura service will read the auth cookie in order to validate the request. We also generate a generic sdk in order to facilitate testing of user query with jest on libs/test-utils/gql/src/generated/test-user.ts
where we provide a Bearer JWT instead of a cookie because jest is not capable to provide one.
Admin
The graphql queries definition are defined in libs/client/gql/admin/queries
. We use a generic sdk with a simple fetch query in order to facilitate the querying the data for the admin role. Those queries are made on the server side of the frontend. Hasura will allow the request through the providing of the X-Hasura-Admin-Secret
.
The Graph Client library is used in order to interact easily with any smart contract on blockchain supported by The Graph protocol.
The library located in libs/client/gql/thegraph
integrate the client and the toolset from The Graph in order to generate the graphql code to be used by the web app to fetch live data from desired smart contracts.
The query are defined in libs/client/gql/thegraph/queries
and the smart contract sources are defined in libs/client/gql/thegraph/src/.graphclientrc.yml
. When updating queries or smart contract sources, be sure to launch the command pnpm thegraph-build
in order to generate the new version of the generated files located in libs/client/gql/thegraph/.graphclient
.
You can find an example of live query of smart contract on the Blockchain page.
Jest is the test-runner used for unit and integration tests.
To run all the jest test on affected code, you can use the command:
curl --location --request GET 'http://localhost:3333/api/balances/arb/0x9a8eC29B75Bc10d68D97Dce73fD3Bbec43752870'
curl --location --request GET 'http://localhost:3333/api/balances/poly/0x3c89fC868803A2478C2E875A97299F240b0290C4'
You should receive an Error 400
if the address is not valid or if you enter an invalid chain name.
curl --location --request GET 'http://localhost:3333/api/balances/poly/0xWasaWasaWasaWassup'
To proceed, simply copy the value of the cookie next-auth.session-token
once you login and paste the value for each users
The corresponding logins are:
- alpha_admin@test.io / Qwerty12345#
- beta_admin@test.io / Qwerty12345#
- sebpalluel@gmail.com (change it with your own google account globally in the workspace)
You can check the tests on auth.cy.ts for example usages of thoses utilities.
This project was generated using Nx.
๐ Smart, Fast and Extensible Build System
In a workspace, libraries are typically divided into four different types:
Libraries that implement โsmartโ UI (e.g. is effectful, is connected to data sources, handles routing, etc.) for specific business use cases.
Libraries that contain only presentational components. That is, compo- nents that render purely from their props, and calls function handlers when interaction occurs.
Libraries that contain the means for interacting with external data services; external services are typically backend services.
Libraries that contain common utilities that are shared by many projects.
Nx supports many plugins which add capabilities for developing different types of applications and different tools.
These capabilities include generating applications, libraries, etc as well as the devtools to test, and build projects as well.
Below are our core plugins:
- React
npm install --save-dev @nrwl/react
- Web (no framework frontends)
npm install --save-dev @nrwl/web
- Node
npm install --save-dev @nrwl/node
There are also many community plugins you could add.
Run nx g @nrwl/react:app my-app
to generate an application.
You can use any of the plugins above to generate applications as well.
When using Nx, you can create multiple applications and libraries in the same workspace.
Run nx g @nrwl/react:lib my-lib
to generate a library.
You can also use any of the plugins above to generate libraries as well.
Libraries are shareable across libraries and applications. They can be imported from @workspace/mylib
.
Run nx serve web
for a dev server. Navigate to http://localhost:3000/. The app will automatically reload if you change any of the source files.
Run nx g @nrwl/react:component my-component --project=my-app
to generate a new component.
Run nx build my-app
to build the project. The build artifacts will be stored in the dist/
directory. Use the --prod
flag for a production build.
Run nx test my-app
to execute the unit tests via Jest.
Run nx affected:test
to execute the unit tests affected by a change.
Run nx e2e my-app
to execute the end-to-end tests via Cypress.
Run nx affected:e2e
to execute the end-to-end tests affected by a change.
Run nx graph
to see a diagram of the dependencies of your projects.
Visit the Nx Documentation to learn more.
Nx Cloud pairs with Nx in order to enable you to build and test code more rapidly, by up to 10 times. Even teams that are new to Nx can connect to Nx Cloud and start saving time instantly.
Teams using Nx gain the advantage of building full-stack applications with their preferred framework alongside Nxโs advanced code generation and project dependency graph, plus a unified experience for both frontend and backend developers.
Visit Nx Cloud to learn more.
In order to run the project, you need to configure the following environment variables in you .env
:
The Nestjs and Nextjs Apps uses Alchemy as an RPC provider for the Ethereum, Polygon and Arbitrum blockchains. You need to create an account and get an API key for those on the alchemy dashboard:
ALCHEMY_ETHEREUM_MAINNET_TOKEN=
ALCHEMY_POLYGON_MAINNET_TOKEN=
ALCHEMY_ARBITRUM_MAINNET_TOKEN=
# Warning ! Those api keys are going to get leaked in the client side code so it's advised to set ALLOWLIST DOMAIN in the alchemy dashboard to your apex domain (in our case www.web3-monorepo.app) in order to avoid someone hijacking your api keys. By default a public rpc network is used for the client side code so you don't need to set those api keys if you don't want to.
# NEXT_APP_ALCHEMY_ETHEREUM_MAINNET_TOKEN=
# NEXT_APP_ALCHEMY_POLYGON_MAINNET_TOKEN=
# NEXT_APP_ALCHEMY_ARBITRUM_MAINNET_TOKEN=
The CI pipeline on github action will fail if you don't provide the Alchemy api keys as it's needed for the Nestjs Server app.
In order to do that, you need to:
- Go to the github repository
Settings
- Go to the
Environments
section - Create a new environment called
staging
(or any other name corresponding to the environment you want to deploy) - Create a new
Environment secrets
with the keyENV_FILE
and the value of the content of your.env
file in the base64 format (you can use this commandbase64 -i .env
to get the base64 value of your.env
file)
The following variables are optional, they are already set for you in the .env.local
but you need to set them in your private .env
if you want to use any of the related feature with you own project:
Follow this tutorial from google to setup your own OAuth provider
Once you have retrieved your client id and client secret assign them in GOOGLE_CLIENT_ID
and GOOGLE_CLIENT_SECRET
located in the file .env.local
Follow this tutorial from github to setup your own OAuth provider.
Once you have retrieved your client id and client secret assign them in GITHUB_CLIENT_ID
and GITHUB_CLIENT_SECRET
located in the file .env.local
In order to secure your JWT authentication provided by Next Auth you are going to need to generate your own RSA-256 keys.
Important ! For testing purpose, public and private keys are provided on this folder waltid-idpkit/data/OIDC/keystore/keys/c047f4e42cf54b66ad154d8ce51e03ef
. You are going to need to generate your own. For that, please refer to the section Configure Hasura and Next Auth with same RSA key. The keys provided to the idpkit container will need to be the same in order for the authentication process to work.
- Register a client with the IDP Kits CLI or the API exposed:
make idpkit-register-client -n "MyApp" --all-redirect-uris
- Update the
IDPKIT_CLIENT_ID
andIDPKIT_CLIENT_SECRET
environment variables based on the response received from the client registration
For more informations to register your own client, [please check this documentation](Client registration - Docs).
As refered in the section about access token in the nx doc, you have different strategies to setup your access to Nx Cloud. In order to beneficiate from local and remote cacheables operations, you can populate use this command to generate an access token allowing read-write access:
pnpx nx g @nrwl/nx-cloud:init
After that, you are going to need to setup your workspace on the nx cloud after registering an account.
You need to configure hasura and next auth to have the same asymmetric key. One is provided by default but you can generate your own RSA 256 key using those commands:
# Don't add passphrase
ssh-keygen -t rsa -P "" -b 4096 -m PEM -f jwtRS256.key
ssh-keygen -e -m PEM -f jwtRS256.key > jwtRS256.key.pub
https://hasura.io/blog/next-js-jwt-authentication-with-next-auth-and-integration-with-hasura/
- Copy the public key in a single line format:
awk -v ORS='\\n' '1' jwtRS256.key.pub | pbcopy
- Now paste this value in your clipboard to
HASURA_GRAPHQL_JWT_SECRET
env in the format
{ "type": "RS256", "key": "<insert-your-public-key-here>"}
- Transform private key into a single line to copy to your clipboard to
NEXTAUTH_SECRET
env
awk -v ORS='\\n' '1' jwtRS256.key | pbcopy
Don't forget to add double quotes "" arround so that \n
are interpreted correctly
In case you need your own image instead of sebpalluel/hasura_cli_with_socat_and_curl
you can do the following command to publish it in docker hub.
Be sure to have activated the buildx module first by following this article
cd hasura && docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t <username>/<image>:latest --push .
This project is based on the nextstarter-chakra template