EligibilityWidget Integration in React¶
Here is how to integrate the Eligibility SDK widget into a typical React app.
Prerequisites¶
- react (v18.x or higher)
- react-dom (v18.x or higher)
- viem (v2.x or higher)
- wagmi (v2.x or higher)
- @tanstack/react-query (v5.x or higher)
More Information¶
Overview¶
- API Key Requirement for Security: The SDK is Issuer Claim credential and onboarding flow restricted by an API key. To obtain an API key, please contact Averer Customer Support.
Basic Flow¶
- Set up a state to track modal and proof status.
- Use the
EligibilityWidgetand passonSuccess+ backend config. - Display verified claims once proof is received.
Example¶
App.tsx
import { WagmiAdapter } from "@reown/appkit-adapter-wagmi";
import type { AppKitNetwork } from "@reown/appkit/networks";
import { defineChain } from "@reown/appkit/networks";
import { EligibilitySDKProvider } from "@redbellynetwork/eligibility-sdk";
import Home from "/Home";
// 🔹 Get your `projectId` from https://cloud.reown.com
export const projectId = import.meta.env.VITE_PROJECT_ID || "your-project-id";
if (!projectId) {
throw new Error("Project ID is not defined");
}
// 🔹 Define Redbelly Network Configuration
const staging = defineChain({
id: 153,
caipNetworkId: `eip155:153`,
chainNamespace: "eip155",
name: "Redbelly Network",
nativeCurrency: {
decimals: 18,
name: "Redbelly Token",
symbol: "RBNT",
},
rpcUrls: {
default: {
http: ["https://governors.testnet.redbelly.network/"],
},
},
});
// 🔹 Define Available Networks
export const networks = [staging] as [AppKitNetwork, ...AppKitNetwork[]];
// 🔹 Initialize Wagmi Adapter for Wallet Integration
export const wagmiAdapter = new WagmiAdapter({
projectId,
networks,
});
const queryClient = new QueryClient();
// 🔹 Initialize AppKit
createAppKit({
adapters: [wagmiAdapter],
projectId,
networks,
metadata: {
name: "Eligibility SDK Example",
description: "Eligibility SDK with Redbelly Network",
url: "https://reown.com",
icons: ["https://avatars.githubusercontent.com/u/179229932"],
},
themeMode: "light",
themeVariables: {
"--w3m-accent": "#000000",
},
});
// EligibilitySDKProvider: Wrap other element inside this, it will initilize the EligibilitySdk and hooks
export function App() {
return (
<WagmiProvider config={wagmiAdapter.wagmiConfig}>
<QueryClientProvider client={queryClient}>
{/* wrap the application code inside EligibilitySDKProvider it will enable the hooks realated to network and EligibilitySdk widget */}
<EligibilitySDKProvider
config={{
network: "staging", // Options: "mainnet" | "testnet" | "staging"
apiKey: "enter-your-api-key", // Connect with Averer Customer Support to obtain your API key
}}
>
<appkit-button />
<Home />
</EligibilitySDKProvider>
</QueryClientProvider>
</WagmiProvider>
);
}
export default App;
Home.tsx
import { useAppKitAccount } from "@reown/appkit/react";
import {
EligibilityWidget,
protocol,
useHasChainPermission,
} from "@redbellynetwork/eligibility-sdk";
export const Home = () => {
const { address, isConnected } = useAppKitAccount();
const { data: hasPermission } = useHasChainPermission(address!);
const [openModal, setOpenModal] = useState(false);
const [profile, setProfile] = useState<{
userDID?: string;
sessionId?: string;
proof?: unknown;
status?: string;
} | null>(null);
const onSuccessHandler: EligibilitySdk["onSuccess"] = (data) => {
setProfile({
userDID: data.userDID,
sessionId: data.sessionId,
proof: data.proof,
status: data.status,
});
};
// Custom query handler to call the backend and return session data
const queryHandler: EligibilitySdk["config"]["queryHandler"] = async () => {
// ⚠️ Note: Keep the query array minimal.
// QR codes can only encode ~3 KB of data reliably.
// Large or multiple queries may exceed the limit and cause QR rendering to fail.
const query: Array<protocol.ZeroKnowledgeProofRequest> = [
// {
// id: 1,
// circuitId: "credentialAtomicQuerySigV2",
// query: {
// allowedIssuers: ["*"],
// type: "KYCAgeCredential",
// context:
// "https://raw.githubusercontent.com/iden3/claim-schema-vocab/main/schemas/json-ld/kyc-v3.json-ld",
// skipClaimRevocationCheck: true,
// credentialSubject: { birthday: { $lt: 20000101 } },
// },
// },
{
id: 1,
circuitId: "credentialAtomicQuerySigV2",
query: {
allowedIssuers: ["*"],
type: "AMLCTFCredential",
context:
"https://raw.githubusercontent.com/redbellynetwork/receptor-schema/refs/heads/main/schemas/json-ld/AMLCTFCredential.jsonld",
skipClaimRevocationCheck: true,
credentialSubject: {
amlCheckStatus: { $eq: "passed" },
},
},
},
];
const res = await fetch("https://example.com/auth-request", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
flowName: "Investor Eligibility Check",
scope: query,
}),
});
const data = await res.json();
return {
request: data.request,
sessionId: data.sessionId,
};
};
// Custom status handler to check the session status
const authStatusHandler: EligibilitySdk["config"]["authStatusHandler"] =
async (sessionId) => {
const res = await fetch(`https://example.com/status/${sessionId}`, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
const data = await res.json();
return {
status: data.status,
proof: data?.proof,
error: data?.error,
};
};
return (
<>
<h2>Home page</h2>
{openModal ? (
<button onClick={() => setOpenModal(true)}>Close Eligibility</button>
) : (
<button onClick={() => setOpenModal(false)}>Open Eligibility</button>
)}
<div>
<p>proof status: {profile?.status}</p>
<p>proof reveiled value</p>
{profile.proof?.body.scope.map((item, index) =>
item.vp && item.vp.verifiableCredential?.credentialSubject
? Object.entries(item.vp.verifiableCredential.credentialSubject)
.filter(([key]) => key !== "@type")
.map(([key, value], subIndex) => (
<div key={`${index}-${subIndex}`}>
<strong>{key}</strong>
<span>{value.toString()}</span>
</div>
))
: null
)}
<p>chain permission {hasPermission ? "true" : "false"}</p>
</div>
{openModal && (
<EligibilityWidget
onSuccess={onSuccessHandler}
config={{
queryHandler: queryHandler,
authStatusHandler: authStatusHandler,
}}
/>
)}
</>
);
};
⚠️ IMPORTANT: Replace "*" in allowedIssuers array with specific, authorized issuer DIDs. Refer to the "Authorized Issuers' DIDs for Redbelly Chains" section in "API Reference" for the correct DIDs for Mainnet, Testnet, or Staging.
📘 You can define selective disclosure queries by leaving the
credentialSubjectobject empty eg:credentialSubject: { birthday: { } }.🔗 The SDK opens a QR code which the Privado wallet scans, submits proof, and then the frontend polls the backend using
authStatusHandler(sessionID)internally.⚠️ QR Code Size Limitations Authorization requests embedded in QR codes must stay small enough to fit within QR standards.
• Keep your query array minimal (1–2 queries when possible).
• Avoid adding large or multiple credential subjects in a single request.
• Oversized requests may cause the QR encoder to fail with errors such as “Data too long”.