Mint passport for individuals

Let users claim (or mint) their passport directly from your DApp. The onboarding process performs KYC, AML risk scoring, and wallet screening on passport holders. If the verification is successful, the user is prompted to claim their passport.
The fee to mint a passport for specific attributes is set by the Passport issuers. It can be paid by either the end-user or subsidized by the project.

Install Quadrata NPM Package

Our UI library can be easily installed and imported to your project.
@quadrata/kyc-react is version-specific. The latest version is 1.0.0
  1. 1.
    Install Quadrata npm package:
    • yarn add @quadrata/kyc-react
    • or npm install @quadrata/kyc-react
  2. 2.
    Contact Quadrata via Discord or email to request API keys and API URLs.
    • Optional - Request to reserve a Token ID slot for a custom NFT Passport artwork.
Protocol integrating with Quadrata Passport can request to have their personalized Quadrata NFT Passport by reserving a Token ID. Contact Quadrata to get your assigned tokenId and your custom NFT animation.

<QuadrataKyc /> Component

import { QuadrataKyc } from '@quadrata/kyc-react';
const MyComponent = () => {
return <QuadrataKyc
onSign={handleSign}
account={account}
chainId={chainId}
issuers={issuers}
signature={signature}
onMintClick={handleMintClick}
mintComplete={mintComplete}
transactionHash={transactionHash}
<>Loading...</>
</QuadrataKyc>
}
NOTE: If chainId and/or account prop values areundefined The loading screen will be displayed. This is because usually those props are asynchronously fetched during the Dapp's initial load and wallet connection flow.

Quadrata Issuers Object

Quadrata supports single or multiple issuers, this can be useful to ensure users will have specific attributes right after onboarding.
Example usage of the issuers prop:
import { QuadrataIssuer } from '@quadrata/kyc-react';
// Single issuer example
const issuers = {
// Contact SpringLabs.com to request a testnet API key and a backend URL.
[QuadrataIssuer.SPRINGLABS]: {
API_URL: process.env.SPRINGLABS_API_URL!,
API_KEY: process.env.SPRINGLABS_API_KEY!,
}
}
// Multiple issuers example
const issuers = {
// Contact SpringLabs.com to request a testnet API key and a backend URL.
[QuadrataIssuer.SPRINGLABS]: {
API_URL: process.env.SPRINGLABS_API_URL!,
API_KEY: process.env.SPRINGLABS_API_KEY!,
},
// Contact Cred Protocol Inc. to request credentials.
[QuadrataIssuer.CRED]: {
API_URL: process.env.CRED_API_URL!,
API_KEY: process.env.CRED_API_KEY!,
},
};
// In your JSX
<QuadrataKyc issuers={issuers} />
NOTE: Contact Quadrata via Discord or email to request API keys and API URLs.

<QuadrataKyc /> Props

Prop
Description
Type
issuers
The requested issuer/issuers credentials, this will affect the UI flow based on the required information by each issuer. See example above.
QuadrataIssuers (required)
onSign
A callback function that is triggered when a user clicks Sign Wallet. It passes a "Welcome to Quadrata! By signing, you agree to the Terms of Service." message for the user to sign.
(message: string) => void (required)
account
The user's wallet address.
string | undefined (required)
chainId
The decimal chain ID number of the currently connected network. In case of unsupported chain IDs the user will be navigated to the "Auth Error" screen. Supported chain IDs can be found in the QuadSupportedChainId enum which can be imported from the @quadrata/core-react NPM package. we recommend checking against this enum to prevent displaying the Auth Error page, and handle this case in your DApp instead.
number (required)
tokenId
The token ID to mint (assuming you want to mint a customized Quadrata Passport for your protocol) Default: 1
number (optional)
signature
The user's signature of the "Welcome to Quadrata! By signing, you agree to the Terms of Service." message passed by the onSign prop.
string | undefined (required)
onMintClick
A callback function, triggered when a user clicks Claim Passport. the event object passed to this callback function will contain all the data required to mint a passport. event.params event.signature and event.signatureIssuer should be passed to the QuadPassport.setAttributes() function in the correct order, and include the event.params.fee as the value passed to the overrides of the setAttributes function. see the example below.
tip: you can import the QuadPassportOnMintClickEventHandlerfrom the @quadrata/core-react library
QuadPassportOnMintClickEventHandler(required)
mintComplete
Upon a successful mint, pass a true value to this prop to display the final page.
boolean (required)
transactionHash
The mint transaction hash. If the value is not undefined, it will trigger navigation to the "Mint in progress" page and will generate the Etherscan transaction link URL.
string | undefined (required)
error
Generic error indicator, can be of type string or undefinedwhen defined, an error message will appear at the bottom.
string | undefined (optional)
onPageChange
A callback function that is triggrerd when a page is changed. Passes a Page or a PageError type string.
tip: you can import the Page and PageErrortypes from the @quadrata/kyc-react library
(page: Page | PageError) => void (optional)
children
Overrides the default loader component inside of the form markup. This component will be shown in all loading states during the flow. if not defined, a "Loading..." text will be shown instead.
React.ReactNode (optional)
showSocialButtons
Show or hide the social media share buttons on the "mint complete" page, default to true
boolean (optional)
className
The class name to be appended to the root DOM element of the form. className can be used for styling. We recommend using styled-components library to add custom styles.
string (optional)
_debug
Development debug flag. when true, useful debug information will be printed in the console. Never use this prop in production.
boolean | undefined (optional)
_unsafeForcePage
A dev-only helper prop to force a specific page to be displayed. Useful when styling and testing different pages. to avoid most render errors, make sure to set the _debug prop to true when using this prop as well. Never use this prop in production.
Page | PageError (optional)

Example Reactjs Dapp Implementing <QuadrataKyc />

In this example we use ethers library to manage Web3 connectivity, you might use any other library.
All of our UI libraries support both Javascript and Typescript environments.
Types, interfaces, helper functions, and object maps can be found in and imported from the @quadrata/core-react and @quadrata/kyc-react NPM packages.
React.js
import React, { useEffect, useState } from 'react';
import { ethers } from 'ethers';
import { JsonRpcSigner, TransactionResponse, Web3Provider } from '@ethersproject/providers';
import { Page, PageError, QuadPassportOnMintClickEventHandler, QuadrataIssuer } from '@quadrata/kyc-react';
// Quadrata Passport
import { QuadPassport } from '@quadrata/contracts/types/QuadPassport';
import QUAD_PASSPORT_ABI from '@quadrata/contracts/abis/QuadPassport.json';
const QUAD_PASSPORT_ADDRESS = '0xF4d4F629eDD73680767eb7b509C7C2D1fE551522';
// Issuers credentials
const quadrataIssuers = {
// Contact SpringLabs.com to request a testnet API key and a backend URL.
[QuadrataIssuer.SPRINGLABS]: {
API_URL: process.env.NEXT_PUBLIC_SPRINGLABS_API_URL!,
API_KEY: process.env.NEXT_PUBLIC_SPRINGLABS_API_KEY!,
},
// Contact Cred Protocol Inc. to request credentials.
[QuadrataIssuer.CRED]: {
API_URL: process.env.NEXT_PUBLIC_CRED_API_URL!,
API_KEY: process.env.NEXT_PUBLIC_CRED_API_KEY!,
},
};
enum ErrorType {
INSUFFICIENT_FUNDS = 'INSUFFICIENT_FUNDS',
OTHER = 'OTHER',
}
const generateErrorMessage = (error?: ErrorType) => {
if (error) return error === ErrorType.INSUFFICIENT_FUNDS ? 'Insufficient funds' : 'Something went wrong';
};
export const KycFlow: React.FC = () => {
// State
const [errorType, setErrorType] = useState<ErrorType>();
const [signer, setSigner] = useState<JsonRpcSigner>();
const [account, setAccount] = useState<string>();
const [chainId, setChainId] = useState<number>();
const [provider, setProvider] = useState<Web3Provider>();
const [signature, setSignature] = useState<string>();
const [mintComplete, setMintComplete] = useState<boolean>(false);
const [transactionHash, setTransactionHash] = useState<string>();
// Listening to provider changes
useEffect(() => {
if (window?.ethereum?.on) {
window.ethereum.on('accountsChanged', function (accounts: string[]) {
setAccount(accounts[0]);
console.log('changed account to: ', accounts[0]);
});
window.ethereum.on('chainChanged', function (chainIdHex: string) {
// converting hexadecimal chainId to decimal
const chainId = parseInt(chainIdHex, 16);
setChainId(chainId);
console.log('chain changed to: ', chainId);
});
}
}, []);
// Event Handlers
const handleConnectWalletClick = async () => {
// Connecting ethereum injected provider (you may use any other provider)
const provider = new ethers.providers.Web3Provider(window.ethereum as any, 'any');
setProvider(provider);
// Getting current chainId
const chainId = await provider.getNetwork().then((res) => res.chainId);
setChainId(chainId);
// Getting account address & signer.
provider.send('eth_requestAccounts', []).then(() => {
const signer = provider.getSigner();
setSigner(signer);
signer.getAddress().then((address) => {
setAccount(address);
});
});
};
const handleSign = async (message: string) => {
// User clicked the initial sign button
// Signing the message and updating state.
// kyc form will automatically navigate to the next step upon signature update
if (signer && account) {
const signature = await signer.signMessage(message);
setSignature(signature);
}
};
const handlePageChange = (page: Page | PageError) => {
if (page === Page.INTRO && signature) {
// Intro page navigation will get triggered when a different wallet is detected,
// Resetting previous signature if present
setSignature(undefined);
}
};
const handleMintClick: QuadPassportOnMintClickEventHandler = async ({
fee,
params,
signatures,
signaturesIssuers,
}) => {
// User clicked Approve & Mint Passport button
// the parameters that are being passed to this function is all you need to mint the passport on chain
// Resetting errors in case it's a retry
setErrorType(undefined);
// QuadPassport contract interface (you may use any provider or library to interact with the blockchain)
const quadPassportContract = new ethers.Contract(
QUAD_PASSPORT_ADDRESS,
QUAD_PASSPORT_ABI,
provider,
) as QuadPassport;
if (signer) {
try {
// Minting passport
const transaction: TransactionResponse = await quadPassportContract
.connect(signer)
.setAttributesBulk(params, signaturesIssuers, signatures, { value: fee });
// Setting the transaction hash prop (required)
// When defined, the from will automatically navigate to the "minting in progress" page
// the tx hash will be added to the "View in Etherscan" link
setTransactionHash(transaction.hash);
transaction
.wait()
.then((receipt) => {
// Setting the mintComplete prop to true (required)
// The form will automatically navigate to the last page.
console.log('Passport minted successfully...', receipt);
setErrorType(undefined);
setMintComplete(true);
})
.catch((error) => {
// Setting the mintComplete prop to false
// You may handle errors here
console.error('Passport minting failed, ', error);
setErrorType(ErrorType.OTHER);
setMintComplete(false);
});
} catch (e: any) {
// Catching insufficient funds error
if (e && e.code === ErrorType.INSUFFICIENT_FUNDS) {
setErrorType(ErrorType.INSUFFICIENT_FUNDS);
} else {
// Caching any other errors here
setErrorType(ErrorType.OTHER);
}
}
} else {
setErrorType(ErrorType.OTHER);
console.error('Missing required params to mint: ', { signer });
}
};
const error = generateErrorMessage(errorType);
return (
<>
{account && chainId ? (
<QuadrataKyc
error={error}
apiKey={API_KEY}
onSign={handleSign}
chainId={chainId}
account={account}
signature={signature}
backendUrl={BACKEND_URL}
onMintClick={handleMintClick}
mintComplete={mintComplete}
onPageChange={handlePageChange}
transactionHash={transactionHash}
>
<div>Loading component here....</div>
</QuadrataKyc>
) : (
<div>
<h1>Please connect your wallet</h1>
<button onClick={handleConnectWalletClick}>Connect wallet</button>
</div>
)}
</>
);
};

CSS Styling

The @quadrata/kyc-react library does NOT include any CSS styling.
To match with the overall branding and style of your DApp, It is the developer's responsibility to style the <QuadrataKyc /> component as they see fit.
Tip: There are CSS class names baked in most of the HTML elements that could be used to add custom CSS freely and flexibly.
There's also a className prop that can be used to encapsulate the CSS selectors and prevent CSS override issues.
Recommendation: Use styled-components library for CSS styling.