();
+
+ useEffect(() => {
+ const fetchAttestation = async () => {
+ const attestation = (await veraxSdk.attestation.getAttestation(attestationId)) as Attestation;
+ console.log("attestation: ", attestation);
+ setAttestation(attestation);
+ };
+
+ fetchAttestation();
+ }, [attestationId, veraxSdk.attestation]);
+
+ return (
+
+ (typeof value === "bigint" ? value.toString() : value)),
+ )
+ : {}
+ }
+ name={false}
+ displayDataTypes={false}
+ collapsed={false}
+ enableClipboard={false}
+ quotesOnKeys={false}
+ sortKeys={false}
+ theme={"hopscotch"}
+ style={{ backgroundColor: "#12172C", padding: "1rem", borderRadius: "0.5rem" }}
+ />
+
+ );
+};
+
+export default AttestationPreview;
diff --git a/website/src/components/CreatePortal.tsx b/website/src/components/CreatePortal.tsx
new file mode 100644
index 00000000..5be09a2d
--- /dev/null
+++ b/website/src/components/CreatePortal.tsx
@@ -0,0 +1,48 @@
+import { type FunctionComponent, useState } from "react";
+import { VeraxSdk } from "@verax-attestation-registry/verax-sdk";
+import { useAccount } from "wagmi";
+
+export type SDKDemoProps = {
+ veraxSdk: VeraxSdk;
+ getTxHash: (hash: `0x${string}`) => void;
+ validateStep: () => void;
+};
+
+const CreatePortal: FunctionComponent = ({ veraxSdk, getTxHash, validateStep }) => {
+ const [txHash, setTxHash] = useState("");
+ const [error, setError] = useState("");
+
+ const { isConnected } = useAccount();
+
+ const createPortal = async () => {
+ try {
+ const hash = await veraxSdk.portal.deployDefaultPortal(
+ [],
+ "Tutorial Portal",
+ "This Portal is used for the tutorial",
+ true,
+ "Verax Tutorial",
+ );
+ setTxHash(hash);
+ getTxHash(hash);
+ validateStep();
+ } catch (e) {
+ console.log(e);
+ if (e instanceof Error) {
+ setError(`Oops, something went wrong: ${e.message}`);
+ }
+ }
+ };
+
+ return (
+ <>
+
+ {txHash !== "" && {`Transaction with hash ${txHash} sent!`}
}
+ {error !== "" && {error}
}
+ >
+ );
+};
+
+export default CreatePortal;
diff --git a/website/src/components/CreateSchema.tsx b/website/src/components/CreateSchema.tsx
new file mode 100644
index 00000000..1815d220
--- /dev/null
+++ b/website/src/components/CreateSchema.tsx
@@ -0,0 +1,68 @@
+import { type FunctionComponent, useEffect, useState } from "react";
+import { VeraxSdk } from "@verax-attestation-registry/verax-sdk";
+import { useAccount } from "wagmi";
+
+export type SDKDemoProps = {
+ veraxSdk: VeraxSdk;
+ getTxHash: (hash: `0x${string}`) => void;
+ validateStep: () => void;
+ getSchemaId: (schemaId: `0x${string}`) => void;
+};
+
+const SCHEMA = "(bool hasCompletedTutorial)";
+
+const CreateSchema: FunctionComponent = ({ veraxSdk, getTxHash, validateStep, getSchemaId }) => {
+ const [txHash, setTxHash] = useState("");
+ const [error, setError] = useState("");
+ const [schemaId, setSchemaId] = useState("");
+ const [schemaExists, setSchemaExists] = useState(false);
+
+ const { isConnected } = useAccount();
+
+ useEffect(() => {
+ const fetchSchema = async () => {
+ const schemaId = (await veraxSdk.schema.getIdFromSchemaString(SCHEMA)) as `0x${string}`;
+ const alreadyExists = (await veraxSdk.schema.getSchema(schemaId)) as boolean;
+ setSchemaId(schemaId);
+ setSchemaExists(alreadyExists);
+ getSchemaId(schemaId);
+ validateStep();
+ };
+
+ fetchSchema();
+ }, [getSchemaId, validateStep, veraxSdk.schema]);
+
+ useEffect(() => {}, [veraxSdk.schema]);
+
+ const createSchema = async () => {
+ try {
+ const hash = await veraxSdk.schema.create(
+ "Tutorial Schema",
+ "This Schema is used for the tutorial",
+ "https://ver.ax/#/tutorials",
+ SCHEMA,
+ );
+ setTxHash(hash);
+ getTxHash(hash);
+ validateStep();
+ } catch (e) {
+ console.log(e);
+ if (e instanceof Error) {
+ setError(`Oops, something went wrong: ${e.message}`);
+ }
+ }
+ };
+
+ return (
+ <>
+
+ {schemaExists && {`Schema already exists, with ID ${schemaId} !`}
}
+ {txHash !== "" && {`Transaction with hash ${txHash} sent!`}
}
+ {error !== "" && {error}
}
+ >
+ );
+};
+
+export default CreateSchema;
diff --git a/website/src/components/IssueAttestation.tsx b/website/src/components/IssueAttestation.tsx
new file mode 100644
index 00000000..4283adb9
--- /dev/null
+++ b/website/src/components/IssueAttestation.tsx
@@ -0,0 +1,58 @@
+import { type FunctionComponent, useState } from "react";
+import { VeraxSdk } from "@verax-attestation-registry/verax-sdk";
+import { useAccount } from "wagmi";
+
+export type SDKDemoProps = {
+ veraxSdk: VeraxSdk;
+ getTxHash: (hash: `0x${string}`) => void;
+ validateStep: () => void;
+ schemaId: `0x${string}`;
+ portalId: `0x${string}`;
+};
+
+const IssueAttestation: FunctionComponent = ({
+ veraxSdk,
+ getTxHash,
+ validateStep,
+ schemaId,
+ portalId,
+}) => {
+ const [txHash, setTxHash] = useState("");
+ const [error, setError] = useState("");
+
+ const { address, isConnected } = useAccount();
+
+ const issueAttestation = async () => {
+ if (address) {
+ try {
+ const attestationPayload = {
+ schemaId: schemaId,
+ expirationDate: Math.floor(Date.now() / 1000) + 2592000,
+ subject: address,
+ attestationData: [{ hasCompletedTutorial: true }],
+ };
+ const hash = await veraxSdk.portal.attest(portalId, attestationPayload, []);
+ setTxHash(hash);
+ getTxHash(hash);
+ validateStep();
+ } catch (e) {
+ console.log(e);
+ if (e instanceof Error) {
+ setError(`Oops, something went wrong: ${e.message}`);
+ }
+ }
+ }
+ };
+
+ return (
+ <>
+
+ {txHash !== "" && {`Transaction with hash ${txHash} sent!`}
}
+ {error !== "" && {error}
}
+ >
+ );
+};
+
+export default IssueAttestation;
diff --git a/website/src/components/Navbar.tsx b/website/src/components/Navbar.tsx
index a8f31759..553f6375 100644
--- a/website/src/components/Navbar.tsx
+++ b/website/src/components/Navbar.tsx
@@ -22,6 +22,11 @@ const Navbar: React.FC = () => {
SDK Demo
+
+
+ Tutorials
+
+
diff --git a/website/src/main.tsx b/website/src/main.tsx
index 953bd88f..6b78abf1 100644
--- a/website/src/main.tsx
+++ b/website/src/main.tsx
@@ -17,14 +17,14 @@ const metadata = {
url: "https://ver.ax",
};
-const chains = [linea, lineaTestnet, arbitrum, arbitrumGoerli, mainnet];
+const chains = [lineaTestnet, linea, arbitrum, arbitrumGoerli, mainnet];
const wagmiConfig = defaultWagmiConfig({ chains, projectId, metadata });
createWeb3Modal({
wagmiConfig,
projectId,
chains,
- defaultChain: linea,
+ defaultChain: lineaTestnet,
chainImages: {
59144: LineaMainnetIcon,
59140: LineaTestnetIcon,
diff --git a/website/src/pages/Tutorials.css b/website/src/pages/Tutorials.css
new file mode 100644
index 00000000..e69de29b
diff --git a/website/src/pages/Tutorials.tsx b/website/src/pages/Tutorials.tsx
new file mode 100644
index 00000000..33603d42
--- /dev/null
+++ b/website/src/pages/Tutorials.tsx
@@ -0,0 +1,139 @@
+import { type FunctionComponent, useEffect, useState } from "react";
+import "./Tutorials.css";
+import { VeraxSdk } from "@verax-attestation-registry/verax-sdk";
+import { useAccount, useNetwork } from "wagmi";
+import ConnectWallet from "../components/ConnectWallet.tsx";
+import IssueAttestation from "../components/IssueAttestation.tsx";
+import AttestationPreview from "../components/AttestationPreview.tsx";
+import { waitForTransactionReceipt } from "viem/actions";
+import { getPublicClient } from "@wagmi/core";
+import CreateSchema from "../components/CreateSchema.tsx";
+import CreatePortal from "../components/CreatePortal.tsx";
+import { decodeEventLog, parseAbi } from "viem";
+
+export type SDKDemoProps = {
+ title: string;
+};
+
+const Tutorials: FunctionComponent = ({ title }) => {
+ const [veraxSdk, setVeraxSdk] = useState();
+ const [schemaId, setSchemaId] = useState<`0x${string}`>();
+ const [portalId, setPortalId] = useState<`0x${string}`>();
+ const [attestationId, setAttestationId] = useState();
+ const [currentStep, setCurrentStep] = useState(1);
+
+ const { address, isConnected } = useAccount();
+ const { chain } = useNetwork();
+
+ useEffect(() => {
+ document.title = title;
+ }, [title]);
+
+ useEffect(() => {
+ if (isConnected && veraxSdk) {
+ setCurrentStep(2);
+ }
+ }, [isConnected, veraxSdk]);
+
+ useEffect(() => {
+ if (chain && address) {
+ const sdkConf =
+ chain.id === 59144 ? VeraxSdk.DEFAULT_LINEA_MAINNET_FRONTEND : VeraxSdk.DEFAULT_LINEA_TESTNET_FRONTEND;
+ const sdk = new VeraxSdk(sdkConf, address);
+ setVeraxSdk(sdk);
+ }
+ }, [chain, address]);
+
+ const handleSchemaTx = async (hash: `0x${string}`) => {
+ const receipt = await waitForTransactionReceipt(getPublicClient(), {
+ hash,
+ });
+ setSchemaId(receipt.logs[0].topics[1]);
+ };
+
+ const handlePortalTx = async (hash: `0x${string}`) => {
+ const receipt = await waitForTransactionReceipt(getPublicClient(), {
+ hash,
+ });
+ const decodedLogs = decodeEventLog({
+ abi: parseAbi(["event PortalRegistered(string name, string description, address portalAddress)"]),
+ // `data` should be 64 bytes, but is only 32 bytes.
+ data: receipt.logs[0].data,
+ topics: receipt.logs[0].topics,
+ });
+ setPortalId(decodedLogs.args.portalAddress);
+ };
+
+ const handleAttestationTx = async (hash: `0x${string}`) => {
+ const receipt = await waitForTransactionReceipt(getPublicClient(), {
+ hash,
+ });
+ setAttestationId(receipt.logs[0].topics[1]);
+ };
+
+ const handleValidateStep = (step: number) => () => {
+ setCurrentStep(step + 1);
+ };
+
+ return (
+ <>
+ 1. Connect your wallet
+
+
+
+
+ {veraxSdk && currentStep >= 2 && (
+ <>
+ 2. Create a Schema
+
+
+
+ >
+ )}
+
+ {veraxSdk && currentStep >= 3 && (
+ <>
+ 3. Create a Portal
+
+
+
+ >
+ )}
+
+ {veraxSdk && currentStep >= 4 && schemaId && portalId && (
+ <>
+ 4. Issue an attestation
+
+
+
+ >
+ )}
+
+ {veraxSdk && currentStep >= 5 && (
+ <>
+ 5. Check the issued attestation
+
+ {attestationId ? (
+
+ ) : (
+
Send the transaction and wait for it to be confirmed
+ )}
+
+ >
+ )}
+ >
+ );
+};
+
+export default Tutorials;