This is a developer guide and example to get started with the Quadrata SDK quickly.
The examples on this guide use some libraries that are optional, such as RainbowKit, which you may choose to use or replace with another implementation.
Wagmi is used to support wallet connect abstraction through the Quadrata SDK, to make integrating easier.
Install Dependencies
Before getting started with the Quadrata SDK, you need to install the required dependencies.
npm i --save react react-dom
npm i --save @tanstack/react-query wagmi viem@2.x
RainbowKit Is Optional
If you choose to use RainbowKit, which is optional, you need to install it as a dependency and add their CSS library to your application. If you do not want to use RainbowKit, you need to implement your own Wagmi Connector or use another supported library.
npm i --save @rainbow-me/rainbowkit
RainbowKit has a CSS file that needs to be imported and supports theming options.
You can import the CSS library from within your code as follows:
Quadrata also has a library for dealing with contracts, which contains ABIs and contract addresses and the SDK takes advantage of these: @quadrata/contracts.
The SDK itself is an abstraction layer around all the client components and provides API services to communicate with the Quadrata REST API: @quadrata/sdk.
npm i --save @quadrata/sdk @quadrata/contracts
npm i --save @quadrata/core-react @quadrata/client-react @quadrata/kyb-react
@quadrata/core-react has a CSS file that needs to be imported.
You can import the CSS library from within your code as follows:
The SDK supports lazy loading and code splitting. You can read more in the advanced section of this documentation.
Wagmi + Connector
Before setting up the onboarding components, you need to set up wagmi and RainbowKit (or another supported Wagmi Connector) so that users can connect a wallet and sign transactions.
The set up is minimal and requires a structure of react providers that your onboarding component must be nested within.
To demonstrate this, the following code defines a component called WagmiWrapper which is intended to be placed in a file called WagmiWrapper.tsx.
If you are using NextJS, remember to place the 'use client'; directive at the top of the file.
// WagmiWrapper.tsx
import type { PropsWithChildren } from 'react';
import { WagmiProvider, http } from 'wagmi';
import * as chains from 'wagmi/chains';
import { getDefaultConfig, RainbowKitProvider } from '@rainbow-me/rainbowkit';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
// NOTE: you should only map the chains and transports you actually want to support.
/* NOTE: Every dApp that relies on WalletConnect now needs to obtain a projectId
from WalletConnect Cloud. This is absolutely free and only takes a few
minutes. */
const config = getDefaultConfig({
appName: 'Quadrata Demo App',
projectId: {{ WALLET_CONNECT_CLOUD_PROJECT_ID }},
ssr: true,
chains: [
chains.arbitrum,
chains.avalanche,
chains.avalancheFuji,
chains.base,
chains.evmos,
chains.evmosTestnet,
chains.mainnet,
chains.optimism,
chains.polygon,
chains.polygonMumbai,
chains.sepolia,
chains.zkSync,
chains.zkSyncSepoliaTestnet,
],
transports: {
[chains.arbitrum.id]: http(),
[chains.avalanche.id]: http(),
[chains.avalancheFuji.id]: http(),
[chains.base.id]: http(),
[chains.evmos.id]: http(),
[chains.evmosTestnet.id]: http(),
[chains.mainnet.id]: http(),
[chains.optimism.id]: http(),
[chains.polygon.id]: http(),
[chains.polygonMumbai.id]: http(),
[chains.sepolia.id]: http(),
[chains.zkSync.id]: http(),
[chains.zkSyncSepoliaTestnet.id]: http(),
},
});
const queryClient = new QueryClient();
export default function WagmiWrapper(props: PropsWithChildren) {
return (
<QueryClientProvider client={queryClient}>
<WagmiProvider config={config}>
<RainbowKitProvider>
{props.children}
</RainbowKitProvider>
</WagmiProvider>
</QueryClientProvider>
);
}
To demonstrate this, the following code defines a component called OnboardingComponent which is intended to be placed in a file called OnboardingComponent.tsx.
If you are using NextJS, remember to place the 'use client'; directive at the top of the file.
// OnboardingComponent.tsx
import * as QuadrataTypes from '@quadrata/sdk/types';
import { withWagmiConnect, Quadrata } from '@quadrata/sdk/client';
export default function OnboardingComponent(props: { accessToken: string }) {
const quadrataProps = withWagmiConnect({
accessToken: props.accessToken,
sdkConfig: {
// NOTE: use QuadrataEnvironment.PRODUCTION for production
environment: QuadrataTypes.QuadrataEnvironment.SANDBOX
},
clientConfig: {
_debug: true,
protocolName: {{ YOUR_COMPANY_NAME }}
},
sharedProps: {
onApplicationEnd: (data: Record<string, any>) => {
console.log('Application ended', data.status, data.error);
},
},
kycProps: {
// see available props at
// https://docs.quadrata.com/integration/how-to-integrate/onboard-users/individual-passport-onboarding/4.-quadclient-package#less-than-quadclient-greater-than-props
attributes: [
QuadrataTypes.QuadrataAttribute.DID,
QuadrataTypes.QuadrataAttribute.COUNTRY,
QuadrataTypes.QuadrataAttribute.AML
],
privacyScopes: [
QuadrataTypes.QuadrataPrivacyConsent.EMAIL,
QuadrataTypes.QuadrataPrivacyConsent.DATE_OF_BIRTH,
QuadrataTypes.QuadrataPrivacyConsent.FIRST_NAME,
QuadrataTypes.QuadrataPrivacyConsent.LAST_NAME,
QuadrataTypes.QuadrataPrivacyConsent.ADDRESS
],
},
kybProps: {
// this is optional - see available props at
// https://docs.quadrata.com/integration/how-to-integrate/onboard-users/business-passport-onboarding/3.-quadratakyb-package
}
});
return (
<Quadrata {...quadrataProps}>
{((helper: QuadrataTypes.Client.Components.Helper) => {
if (helper.isApplicationComplete) {
return <p>Application is Complete</p>;
}
if (helper.isPassportInReview) {
return <p>Your application is in review.</p>;
}
if (helper.isLoading) {
return <p>Quadrata is loading...</p>;
}
return (
<button
disabled={!helper.isApplicationReady}
onClick={helper.launchApplication}
>
Onboarding Application
</button>
);
})}
</Quadrata>
);
}
Putting It Together
In order for this all to work, the OnboardingComponent needs to be a child of WagmiWrapper. You are free to place the WagmiWrapper anywhere in your layout so long as the OnboardingComponent is somewhere in the child tree.
RainbowKit also has a Connect Button that you need to add which initiates Wallet Connect for the user. It is responsible for rendering the connect/disconnect button, as well as chain-swapping UI. The ConnectButton must also be nested as a child of WagmiWrapper.
To demonstrate this, the following code defines a component called MyComponent. This component serves as an example of how to nest the OnboardingComponent and ConnectButton inside the WagmiWrapper. For the purpose of this example, the following code should go into a file called MyComponent.tsx.
For styling purposes, it's common to place your WagmiConnect component somewhere near the top of the main layout body.
This way you can add your connect button on the top of the screen or off to the side, while the OnboardingComponent can be centered or conditionally rendered somewhere inside your page body.
import { ConnectButton } from '@rainbow-me/rainbowkit';
import WagmiWrapper from './WagmiWrapper';
import OnboardingComponent from './OnboardingComponent';
export default function MyComponent(props: { accessToken: string }) {
return (
<WagmiWrapper>
<ConnectButton/>
<OnboardingComponent
accessToken={props.accessToken}
/>
</WagmiWrapper>
);
}
The Access Token
In order to authenticate with the onboarding components and Quadrata REST API, you need to fetch an access token.
You can use the createAccessToken API service in the SDK to generate an access token to pass into your component.
You should do this on the server so that your API key is not exposed to anyone using your dApp.
If you cannot do this on the server, you are permitted to make this call from the client, but you should consider rotating your API keys frequently.
If you are using NextJS, you can use this example to fetch the access token on the server and pass it to your client component
'use server';
// app/onboarding/page.tsx
import { createAccessToken, QuadrataEnvironment } from '@quadrata/sdk/api';
// change this to import MyComponet from the example above
import MyComponent from '~/components/MyComponent';
export default async function OnboardingPage() {
const { data: { accessToken } } = await createAccessToken(
{
// api service params
apiKey: {{ QUADRATA_API_KEY }},
options: {
// fetch options
cache: 'no-cache'
}
},
{
// sdk config
// NOTE: use QuadrataEnvironment.PRODUCTION for production
environment: QuadrataEnvironment.SANDBOX
}
);
return (
<div>
<h1>Onboarding With NextJS</h1>
<MyComponent accessToken={accessToken} />
</div>
);
}
NOTE: This approach will expose your API key to anyone using your dApp!
import { useEffect, useState } from 'react';
import { createAccessToken, QuadrataEnvironment } from '@quadrata/sdk/api';
// change this to import MyComponet from the example above
import MyComponent from './MyComponent';
export default function App() {
const [accessToken, setAccessToken] = useState<string|undefined>(undefined);
useEffect(() => {
createAccessToken(
{
// api service params
apiKey: {{ QUADRATA_API_KEY }},
options: {
// this is necessary for client components
allowUnsafeClientApiCall: true,
// fetch options
cache: 'no-cache'
}
},
{
// sdk config
// NOTE: use QuadrataEnvironment.PRODUCTION for production
environment: QuadrataEnvironment.SANDBOX
}
).then(response => {
setAccessToken(response.data.accessToken);
});
}, []);
if (!accessToken) {
return <p>Loading...</p>;
}
return (
<div>
<h1>Client Component Onboarding</h1>
<MyComponent accessToken={accessToken} />
</div>
);
}