diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 1e903bf3..8a1039c6 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -514,6 +514,9 @@ importers:
'@verax-attestation-registry/verax-sdk':
specifier: 1.1.0
version: 1.1.0(@babel/core@7.23.3)(@envelop/core@4.0.3)(@graphql-mesh/types@0.93.2)(@graphql-tools/delegate@10.0.3)(@graphql-tools/merge@9.0.0)(@graphql-tools/utils@9.2.1)(@graphql-tools/wrap@10.0.1)(@types/node@20.10.3)(graphql-tag@2.12.6)(react-dom@18.2.0)(react-native@0.72.6)(react@18.2.0)(tslib@2.6.2)(typescript@5.2.2)(zod@3.22.4)
+ '@wagmi/core':
+ specifier: ^1.4.7
+ version: 1.4.7(@types/react@18.2.37)(react@18.2.0)(typescript@5.2.2)(viem@1.18.9)(zod@3.22.4)
'@web3modal/wagmi':
specifier: ^3.5.0
version: 3.5.4(@types/react@18.2.37)(@wagmi/core@1.4.7)(typescript@5.2.2)(viem@1.18.9)
@@ -526,6 +529,9 @@ importers:
react-dom:
specifier: ^18.2.0
version: 18.2.0(react@18.2.0)
+ react-json-view:
+ specifier: ^1.21.3
+ version: 1.21.3(@types/react@18.2.37)(react-dom@18.2.0)(react@18.2.0)
react-router-dom:
specifier: ^6.19.0
version: 6.19.0(react-dom@18.2.0)(react@18.2.0)
diff --git a/website/package.json b/website/package.json
index 3b38b277..e3c3c8bf 100644
--- a/website/package.json
+++ b/website/package.json
@@ -28,10 +28,12 @@
"@fortawesome/react-fontawesome": "^0.2.0",
"@lens-protocol/widgets-react": "^2.1.0",
"@verax-attestation-registry/verax-sdk": "1.1.0",
+ "@wagmi/core": "^1.4.7",
"@web3modal/wagmi": "^3.5.0",
"axios": "^1.6.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
+ "react-json-view": "^1.21.3",
"react-router-dom": "^6.19.0",
"viem": "^1.18.9",
"wagmi": "^1.4.6"
diff --git a/website/src/App.css b/website/src/App.css
index c7637078..166e77b1 100644
--- a/website/src/App.css
+++ b/website/src/App.css
@@ -4,6 +4,16 @@
text-align: center;
}
+.global-container {
+ display: flex;
+ flex-direction: column;
+ min-height: 100vh;
+}
+
+.global-body {
+ flex: 1;
+}
+
.logo {
height: 6em;
padding: 1.5em;
diff --git a/website/src/App.tsx b/website/src/App.tsx
index 805416e7..4fe8471f 100644
--- a/website/src/App.tsx
+++ b/website/src/App.tsx
@@ -5,22 +5,28 @@ import Poh from "./pages/Poh.tsx";
import SDKDemo from "./pages/SDKDemo.tsx";
import Navbar from "./components/Navbar.tsx";
import Footer from "./components/Footer.tsx";
+import Tutorials from "./pages/Tutorials.tsx";
function App() {
return (
-
-
-
- } />
- } />
- } />
-
-
-
+
+
+
+
+
+ } />
+ } />
+ } />
+ } />
+
+
+
+
+
);
}
diff --git a/website/src/components/AttestationPreview.css b/website/src/components/AttestationPreview.css
new file mode 100644
index 00000000..d3452f21
--- /dev/null
+++ b/website/src/components/AttestationPreview.css
@@ -0,0 +1,5 @@
+.attestation-preview {
+ width: 70%;
+ text-align: start;
+ margin: auto;
+}
diff --git a/website/src/components/AttestationPreview.tsx b/website/src/components/AttestationPreview.tsx
new file mode 100644
index 00000000..e3af5aa2
--- /dev/null
+++ b/website/src/components/AttestationPreview.tsx
@@ -0,0 +1,46 @@
+import { type FunctionComponent, useEffect, useState } from "react";
+import "./AttestationPreview.css";
+import { Attestation, VeraxSdk } from "@verax-attestation-registry/verax-sdk";
+import ReactJson from "react-json-view";
+
+export type SDKDemoProps = {
+ veraxSdk: VeraxSdk;
+ attestationId: string;
+};
+
+const AttestationPreview: FunctionComponent = ({ veraxSdk, attestationId }) => {
+ const [attestation, setAttestation] = useState();
+
+ useEffect(() => {
+ const fetchAttestation = async () => {
+ const attestation = (await veraxSdk.attestation.getAttestation(attestationId)) as 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..dff8d4cf
--- /dev/null
+++ b/website/src/components/CreatePortal.tsx
@@ -0,0 +1,46 @@
+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;
+};
+
+const CreatePortal: FunctionComponent = ({ veraxSdk, getTxHash }) => {
+ 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);
+ } 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..d8c4e276
--- /dev/null
+++ b/website/src/components/CreateSchema.tsx
@@ -0,0 +1,65 @@
+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;
+ getSchemaId: (schemaId: `0x${string}`) => void;
+};
+
+const SCHEMA = "(bool hasCompletedTutorial)";
+
+const CreateSchema: FunctionComponent = ({ veraxSdk, getTxHash, 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);
+ };
+
+ fetchSchema();
+ }, [getSchemaId, 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);
+ } 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/Footer.css b/website/src/components/Footer.css
index 92ad9a67..63976802 100644
--- a/website/src/components/Footer.css
+++ b/website/src/components/Footer.css
@@ -1,10 +1,4 @@
footer {
- position: fixed;
- left: 0;
- bottom: 0;
- width: 100%;
- height: 10vh;
- min-height: 60px;
margin: 20px 0;
display: flex;
justify-content: center;
diff --git a/website/src/components/IssueAttestation.tsx b/website/src/components/IssueAttestation.tsx
new file mode 100644
index 00000000..176b4dd3
--- /dev/null
+++ b/website/src/components/IssueAttestation.tsx
@@ -0,0 +1,50 @@
+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;
+ schemaId: `0x${string}`;
+ portalId: `0x${string}`;
+};
+
+const IssueAttestation: FunctionComponent = ({ veraxSdk, getTxHash, 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);
+ } 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/Home.css b/website/src/pages/Home.css
deleted file mode 100644
index e69de29b..00000000
diff --git a/website/src/pages/Home.tsx b/website/src/pages/Home.tsx
index 49d8acc6..795dc54e 100644
--- a/website/src/pages/Home.tsx
+++ b/website/src/pages/Home.tsx
@@ -1,5 +1,4 @@
import veraxLogo from "../assets/verax-logo-circle.svg";
-import "./Home.css";
import { type FunctionComponent, useEffect } from "react";
export type HomeProps = {
diff --git a/website/src/pages/SDKDemo.css b/website/src/pages/SDKDemo.css
deleted file mode 100644
index e69de29b..00000000
diff --git a/website/src/pages/SDKDemo.tsx b/website/src/pages/SDKDemo.tsx
index 8643ab8a..3757c4b7 100644
--- a/website/src/pages/SDKDemo.tsx
+++ b/website/src/pages/SDKDemo.tsx
@@ -1,5 +1,4 @@
import { type FunctionComponent, useEffect, useState } from "react";
-import "./SDKDemo.css";
import { Attestation, VeraxSdk } from "@verax-attestation-registry/verax-sdk";
import { useAccount, useNetwork } from "wagmi";
import ConnectWallet from "../components/ConnectWallet.tsx";
diff --git a/website/src/pages/Tutorials.tsx b/website/src/pages/Tutorials.tsx
new file mode 100644
index 00000000..bfb757ab
--- /dev/null
+++ b/website/src/pages/Tutorials.tsx
@@ -0,0 +1,120 @@
+import { type FunctionComponent, useEffect, useState } from "react";
+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 { address, isConnected } = useAccount();
+ const { chain } = useNetwork();
+
+ useEffect(() => {
+ document.title = title;
+ }, [title]);
+
+ 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: 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]);
+ };
+
+ return (
+ <>
+ 1. Connect your wallet
+
+
+
+
+ {veraxSdk && isConnected && (
+ <>
+ 2. Create a Schema
+
+
+
+ >
+ )}
+
+ {veraxSdk && schemaId && (
+ <>
+ 3. Create a Portal
+
+
+
+ >
+ )}
+
+ {veraxSdk && schemaId && portalId && (
+ <>
+ 4. Issue an attestation
+
+
+
+ >
+ )}
+
+ {veraxSdk && attestationId && (
+ <>
+ 5. Check the issued attestation
+
+ {attestationId ? (
+
+ ) : (
+
Send the transaction and wait for it to be confirmed
+ )}
+
+ >
+ )}
+ >
+ );
+};
+
+export default Tutorials;