diff --git a/packages/nextjs/app/dashboard/attestation/[uid]/page.tsx b/packages/nextjs/app/dashboard/attestation/[uid]/page.tsx new file mode 100644 index 0000000..28bd520 --- /dev/null +++ b/packages/nextjs/app/dashboard/attestation/[uid]/page.tsx @@ -0,0 +1,138 @@ +"use client"; +import React, { useState, useEffect } from "react"; +import { useRouter, useSearchParams } from "next/navigation"; +import { fetchAttestation, Attestation } from "~~/utils/utils"; + +export default function AttestationPage({ + params, +}: { + params: { uid: string }; +}) { + const uid = params.uid; + const [attestation, setAttestation] = useState(null); + + useEffect(() => { + console.log("UUID:", uid); + if (uid) { + fetchAttestation(uid) + .then((data) => { + console.log("Fetched data:", data); + setAttestation(data); + }) + .catch((error) => { + console.error("Error fetching attestation:", error); + }); + } + }, [uid]); + + if (!attestation) { + return
Loading...
; + } + + return ( +
+
+ UID: + + {attestation.uid} + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Schema + + {attestation.schema} +
+ Is Endorsement + + {attestation.isEndorsement ? "True" : "False"} +
+ Name + + {attestation.name} +
+ Domain + + {attestation.domain} +
+ Context + + {attestation.context} +
+ IPFS Hash + + + {attestation.ipfsHash} + +
+ From + + {attestation.from} +
+ To + + {attestation.to} +
+ Created + + {attestation.created} +
+ Expiration + + {attestation.expiration} +
+ Revoked + + {attestation.revoked} +
+
+
+ ); +} diff --git a/packages/nextjs/app/dashboard/attestations/page.tsx b/packages/nextjs/app/dashboard/attestations/page.tsx new file mode 100644 index 0000000..8535082 --- /dev/null +++ b/packages/nextjs/app/dashboard/attestations/page.tsx @@ -0,0 +1,145 @@ +"use client"; +import React, { useState, useEffect } from "react"; +import Link from "next/link"; +import { fetchAllAttestations, shortAddress } from "~~/utils/utils"; +import Modal from "~~/components/Modal"; +import CreateAttestationForm from "~~/components/forms/CreateAttestationForm"; +const Dashboard = () => { + const [attestations, setAttestations] = useState([]); + const [stats, setStats] = useState<{ + totalAtestations: number; + totalSchemas: number; + totalAttestors: number; + }>({ + totalAtestations: 0, + totalSchemas: 0, + totalAttestors: 0, + }); + + const getAttestations = async () => { + try { + const attestationData = await fetchAllAttestations(); + } catch (error) { + console.error("Error fetching attestations:", error); + } + }; + + const AttestationLink = ({ uid }: { uid: string }) => ( + + {shortAddress(uid)} + + ); + + const DashboardStats = ({ + totalAtestations, + totalSchemas, + totalAttestors, + }: { + totalAtestations: number; + totalSchemas: number; + totalAttestors: number; + }) => ( +
+
+
+ {totalAtestations} +
+
Total Attestations
+
+
+
{totalSchemas}
+
Total Schemas
+
+
+
+ {totalAttestors} +
+
Total Attestors
+
+
+ ); + + useEffect(() => { + getAttestations(); + }, []); + return ( +
+
+
+
+
+

+ Attestation Scan +

+

+ Showing the most recent Solas Attestations. +

+
+ ( + + )} + > + + +
+ +
+ + + + + + + + + + + + + {attestations.map((attestation) => ( + + + + + + + + + ))} + +
UIDSchemaFromToTypeAge
+ + + {attestation.schema} + + {attestation.from} + + {attestation.to} + + {attestation.type} + + {attestation.age} +
+
+ +
+
+
+ ); +}; + +export default Dashboard; diff --git a/packages/nextjs/app/dashboard/page.tsx b/packages/nextjs/app/dashboard/page.tsx new file mode 100644 index 0000000..baa15a0 --- /dev/null +++ b/packages/nextjs/app/dashboard/page.tsx @@ -0,0 +1,18 @@ +"use client"; +import Link from "next/link"; +import React, { useState, useEffect } from "react"; +const Dashboard = () => { + return ( +
+
+

Welcome to Dashboard!

+ Attestations +

+

+ Schemas +
+
+ ); +}; + +export default Dashboard; diff --git a/packages/nextjs/app/dashboard/schema/[uid]/page.tsx b/packages/nextjs/app/dashboard/schema/[uid]/page.tsx new file mode 100644 index 0000000..6c54fe1 --- /dev/null +++ b/packages/nextjs/app/dashboard/schema/[uid]/page.tsx @@ -0,0 +1,73 @@ +"use client"; +import React, { useState, useEffect } from "react"; +import { useRouter, useSearchParams } from "next/navigation"; +import { fetchSchema, Schema } from "~~/utils/utils"; + +export default function AttestationPage({ + params, +}: { + params: { uid: string }; +}) { + const uid = params.uid; + const [schema, setSchema] = useState(null); + + useEffect(() => { + console.log("UUID:", uid); + if (uid) { + fetchSchema(uid) + .then((data) => { + console.log("Fetched data:", data); + setSchema(data); + }) + .catch((error) => { + console.error("Error fetching schema:", error); + }); + } + }, [uid]); + + if (!schema) { + return
Loading...
; + } + + return ( +
+
+ UID: + + {schema.uid} + +
+
+ + + + + + + + + + + + + + + + +
+ UID + + {schema.uid ? "True" : "False"} +
+ Schema + + {schema.schema} +
+ Attestations + + {schema.attestations ? "True" : "False"} +
+
+
+ ); +} diff --git a/packages/nextjs/app/dashboard/schemas/page.tsx b/packages/nextjs/app/dashboard/schemas/page.tsx new file mode 100644 index 0000000..ab245e2 --- /dev/null +++ b/packages/nextjs/app/dashboard/schemas/page.tsx @@ -0,0 +1,140 @@ +"use client"; +import React, { useState, useEffect } from "react"; +import Link from "next/link"; +import { useScaffoldEventHistory } from "~~/hooks/scaffold-stark/useScaffoldEventHistory"; +import Modal from "~~/components/Modal"; +import RegisterSchemaForm from "~~/components/forms/RegisterSchemaForm"; + +const Schemas = () => { + const [totalSchemas, setTotalSchemas] = useState(0); + const [schemas, setSchemas] = useState([]); + + const { + data: eventData, + isLoading, + error: err, + } = useScaffoldEventHistory({ + contractName: "SchemaRegistry", + eventName: "contracts::SchemaRegistry::SchemaRegistry::Registered", + fromBlock: BigInt(0), + blockData: true, + transactionData: false, + receiptData: false, + watch: true, + enabled: true, + }); + + useEffect(() => { + if (eventData) { + setTotalSchemas(eventData.length); + setSchemas( + eventData.map((event) => ({ + uid: event.args.uid, + schema: event.args.schema_record, + caller: event.args.caller, + })), + ); + } + }, [eventData]); + + const SchemaLink = ({ uid }: { uid: string }) => ( + + {uid.toString()} + + ); + + const DashboardStats = ({ totalSchemas }: { totalSchemas: number }) => ( +
+
+
{totalSchemas}
+
Total Schemas
+
+
+ ); + + return ( +
+
+
+
+
+

+ Solas Schemas +

+

+ Showing the most recent Solas Schemas. +

+
+ + ( + + )} + > + + +
+ + {isLoading ? ( +
+ +
+ ) : ( +
+ + + + + + + + + + {schemas.map((schema, index) => ( + + + + + + ))} + +
UIDCreatorSchema Record
+ + + {schema.caller} + + {schema.schema} +
+
+ )} + +
+
+
+ ); +}; + +export default Schemas; diff --git a/packages/nextjs/app/page.tsx b/packages/nextjs/app/page.tsx index 2b400ec..06d7397 100644 --- a/packages/nextjs/app/page.tsx +++ b/packages/nextjs/app/page.tsx @@ -54,9 +54,9 @@ const Home: NextPage = () => {

- Explore your local transactions with the{" "} - - Block Explorer + Explore attestation and schemas via the{" "} + + Dashboard {" "} tab.

diff --git a/packages/nextjs/components/Modal.tsx b/packages/nextjs/components/Modal.tsx new file mode 100644 index 0000000..e9a259e --- /dev/null +++ b/packages/nextjs/components/Modal.tsx @@ -0,0 +1,36 @@ +import React, { ReactNode, ReactElement, useState } from "react"; + +interface ModalProps { + children: ReactNode; + trigger: ({ openModal }: { openModal: () => void }) => ReactElement; +} + +const Modal: React.FC = ({ children, trigger }) => { + const [isOpen, setIsOpen] = useState(false); + + const openModal = () => setIsOpen(true); + const closeModal = () => setIsOpen(false); + + return ( + <> + {trigger({ openModal })} + {isOpen && ( +
+
+ {children} +
+ +
+
+
+ )} + + ); +}; + +export default Modal; diff --git a/packages/nextjs/components/forms/CreateAttestationForm.tsx b/packages/nextjs/components/forms/CreateAttestationForm.tsx new file mode 100644 index 0000000..84a0bb8 --- /dev/null +++ b/packages/nextjs/components/forms/CreateAttestationForm.tsx @@ -0,0 +1,132 @@ +"use client"; +import { useEffect } from "react"; +import { useRouter } from "next/navigation"; +import { useScaffoldReadContract } from "~~/hooks/scaffold-stark/useScaffoldReadContract"; +import { useAccount, useContractRead } from "@starknet-react/core"; +import { displayTxResult } from "~~/app/debug/_components/contract/utilsDisplay"; +import ERC20_ABI from "~~/utils/solas-abis/ERC20.json"; + +const CreateAttestationForm = () => { + const testAddress = + "0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7"; + const toAddress = + "0x1176a1bd84444c89232ec27754698e5d2e7e1a7f1539f12027f28b23ec9f3d8"; + + const { data, isError, isLoading, error } = useContractRead({ + functionName: "balanceOf", + args: [toAddress], + abi: ERC20_ABI, + address: testAddress, + watch: true, + }); + + const router = useRouter(); + + const isPending = false; + const isConfirming = false; + const isConfirmed = false; + const isSuccess = false; + + const LoadingSpinner = ( + + ); + + useEffect(() => { + if (isConfirmed) { + // redirect to dashboard + setTimeout(() => { + router.push("/dashboard"); + }, 5000); + } + }, [isPending, isConfirming, isConfirmed, router]); + + const handleSubmit = (formData: FormData) => {}; + + return ( +
+

Make Attestation

+
+ + + {!isPending && !isConfirming && ( + + )} + {(isPending || isConfirming) && ( +
+ {LoadingSpinner} +
+ )} +
+ {isPending && !isSuccess && ( +
+ Please sign the transaction with your wallet. +
+ )} + {isSuccess && isConfirming && ( +
+ Waiting for confirmation... +
+ )} + {isConfirmed && ( +
+ Transaction confirmed. You will be redirected to your dashboard. +

+ View on Etherscan: + + Transaction Link + +

+

+ Go to your{" "} + + dashboard + + . +

+
+ )} + {isLoading &&
Loading ...
} + {isError || (!data &&
{error?.message}
)} + {data && ( +
{displayTxResult(data, false)}
+ )} +
+ ); +}; +export default CreateAttestationForm; diff --git a/packages/nextjs/components/forms/RegisterSchemaForm.tsx b/packages/nextjs/components/forms/RegisterSchemaForm.tsx new file mode 100644 index 0000000..b790792 --- /dev/null +++ b/packages/nextjs/components/forms/RegisterSchemaForm.tsx @@ -0,0 +1,97 @@ +"use client"; +import { useState } from "react"; +import { useRouter } from "next/navigation"; +import { useScaffoldWriteContract } from "~~/hooks/scaffold-stark/useScaffoldWriteContract"; + +const RegisterSchemaForm = () => { + const [schema, setSchema] = useState(""); + const [revocable, setRevocable] = useState(false); + const router = useRouter(); + + const { writeAsync, isSuccess, isPending, isError } = + useScaffoldWriteContract({ + contractName: "SchemaRegistry", + functionName: "register", + args: [schema, false], + }); + + const handleSubmit = async (formData: FormData) => { + const schema = formData.get("schema") as string; + const revocable = formData.get("revocable") as string; + + setSchema(schema); + setRevocable(revocable === "true"); + try { + await writeAsync({ + args: [schema, true], + }); + } catch (err) { + console.error("Error submitting transaction:", err); + } + }; + + const LoadingSpinner = ( + + ); + + return ( +
+

Register Schema

+
+ + Revocable + + +
+ {isPending && ( +
+ {LoadingSpinner} +
+ )} + {isSuccess && ( +
+ Transaction confirmed. You will be redirected to your dashboard. +
+ )} + {isError && ( +
+ Error submitting transaction. +
+ )} +
+ ); +}; + +export default RegisterSchemaForm; diff --git a/packages/nextjs/utils/Provider.ts b/packages/nextjs/utils/Provider.ts new file mode 100644 index 0000000..ce54cc4 --- /dev/null +++ b/packages/nextjs/utils/Provider.ts @@ -0,0 +1,5 @@ +import { RpcProvider } from "starknet"; + +export const provider = new RpcProvider({ + nodeUrl: "https://free-rpc.nethermind.io/mainnet-juno/", +}); diff --git a/packages/nextjs/utils/placeholder-data.js b/packages/nextjs/utils/placeholder-data.js new file mode 100644 index 0000000..ce9db9f --- /dev/null +++ b/packages/nextjs/utils/placeholder-data.js @@ -0,0 +1,51 @@ +export const articles = [ + { + id: 1, + date: "2021-09-01", + author: "Author 1", + title: + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean nulla", + content: + "Lorem Ipsum is simply dummy text of the printing and typesetting industry. It has been the industry's standard dummy text ever since the 1500s. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages.", + image: "https://picsum.photos/300/300?random=1", + }, + { + id: 2, + date: "2021-09-02", + author: "Author 2", + title: + "Donec rutrum nibh velit, eget viverra purus dapibus id. In in imperdiet dolor", + content: + "Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium. Totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.", + image: "https://picsum.photos/300/300?random=2", + }, + { + id: 3, + date: "2021-09-03", + author: "Author 3", + title: + "Aenean lobortis fringilla lectus ac suscipit. Praesent iaculis neque nec rutrum dapibus", + content: + "At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident.", + image: "https://picsum.photos/300/300?random=3", + }, + { + id: 4, + date: "2021-09-04", + author: "Author 4", + title: + "Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae", + content: + "Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure.", + image: "https://picsum.photos/300/300?random=4", + }, + { + id: 5, + date: "2021-09-05", + author: "Author 5", + title: "Curabitur non nulla sit amet nisl tempus convallis quis ac lectus", + content: + "On the other hand, we denounce with righteous indignation and dislike men who are so beguiled and demoralized by the charms of pleasure of the moment, so blinded by desire, that they cannot foresee the pain and trouble.", + image: "https://picsum.photos/300/300?random=5", + }, +]; diff --git a/packages/nextjs/utils/solas-abis/ERC20.json b/packages/nextjs/utils/solas-abis/ERC20.json new file mode 100644 index 0000000..11241ea --- /dev/null +++ b/packages/nextjs/utils/solas-abis/ERC20.json @@ -0,0 +1,1029 @@ +[ + { + "name": "MintableToken", + "type": "impl", + "interface_name": "src::mintable_token_interface::IMintableToken" + }, + { + "name": "core::integer::u256", + "type": "struct", + "members": [ + { + "name": "low", + "type": "core::integer::u128" + }, + { + "name": "high", + "type": "core::integer::u128" + } + ] + }, + { + "name": "src::mintable_token_interface::IMintableToken", + "type": "interface", + "items": [ + { + "name": "permissioned_mint", + "type": "function", + "inputs": [ + { + "name": "account", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "name": "amount", + "type": "core::integer::u256" + } + ], + "outputs": [], + "state_mutability": "external" + }, + { + "name": "permissioned_burn", + "type": "function", + "inputs": [ + { + "name": "account", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "name": "amount", + "type": "core::integer::u256" + } + ], + "outputs": [], + "state_mutability": "external" + } + ] + }, + { + "name": "MintableTokenCamelImpl", + "type": "impl", + "interface_name": "src::mintable_token_interface::IMintableTokenCamel" + }, + { + "name": "src::mintable_token_interface::IMintableTokenCamel", + "type": "interface", + "items": [ + { + "name": "permissionedMint", + "type": "function", + "inputs": [ + { + "name": "account", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "name": "amount", + "type": "core::integer::u256" + } + ], + "outputs": [], + "state_mutability": "external" + }, + { + "name": "permissionedBurn", + "type": "function", + "inputs": [ + { + "name": "account", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "name": "amount", + "type": "core::integer::u256" + } + ], + "outputs": [], + "state_mutability": "external" + } + ] + }, + { + "name": "Replaceable", + "type": "impl", + "interface_name": "src::replaceability_interface::IReplaceable" + }, + { + "name": "core::array::Span::", + "type": "struct", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::" + } + ] + }, + { + "name": "src::replaceability_interface::EICData", + "type": "struct", + "members": [ + { + "name": "eic_hash", + "type": "core::starknet::class_hash::ClassHash" + }, + { + "name": "eic_init_data", + "type": "core::array::Span::" + } + ] + }, + { + "name": "core::option::Option::", + "type": "enum", + "variants": [ + { + "name": "Some", + "type": "src::replaceability_interface::EICData" + }, + { + "name": "None", + "type": "()" + } + ] + }, + { + "name": "core::bool", + "type": "enum", + "variants": [ + { + "name": "False", + "type": "()" + }, + { + "name": "True", + "type": "()" + } + ] + }, + { + "name": "src::replaceability_interface::ImplementationData", + "type": "struct", + "members": [ + { + "name": "impl_hash", + "type": "core::starknet::class_hash::ClassHash" + }, + { + "name": "eic_data", + "type": "core::option::Option::" + }, + { + "name": "final", + "type": "core::bool" + } + ] + }, + { + "name": "src::replaceability_interface::IReplaceable", + "type": "interface", + "items": [ + { + "name": "get_upgrade_delay", + "type": "function", + "inputs": [], + "outputs": [ + { + "type": "core::integer::u64" + } + ], + "state_mutability": "view" + }, + { + "name": "get_impl_activation_time", + "type": "function", + "inputs": [ + { + "name": "implementation_data", + "type": "src::replaceability_interface::ImplementationData" + } + ], + "outputs": [ + { + "type": "core::integer::u64" + } + ], + "state_mutability": "view" + }, + { + "name": "add_new_implementation", + "type": "function", + "inputs": [ + { + "name": "implementation_data", + "type": "src::replaceability_interface::ImplementationData" + } + ], + "outputs": [], + "state_mutability": "external" + }, + { + "name": "remove_implementation", + "type": "function", + "inputs": [ + { + "name": "implementation_data", + "type": "src::replaceability_interface::ImplementationData" + } + ], + "outputs": [], + "state_mutability": "external" + }, + { + "name": "replace_to", + "type": "function", + "inputs": [ + { + "name": "implementation_data", + "type": "src::replaceability_interface::ImplementationData" + } + ], + "outputs": [], + "state_mutability": "external" + } + ] + }, + { + "name": "AccessControlImplExternal", + "type": "impl", + "interface_name": "src::access_control_interface::IAccessControl" + }, + { + "name": "src::access_control_interface::IAccessControl", + "type": "interface", + "items": [ + { + "name": "has_role", + "type": "function", + "inputs": [ + { + "name": "role", + "type": "core::felt252" + }, + { + "name": "account", + "type": "core::starknet::contract_address::ContractAddress" + } + ], + "outputs": [ + { + "type": "core::bool" + } + ], + "state_mutability": "view" + }, + { + "name": "get_role_admin", + "type": "function", + "inputs": [ + { + "name": "role", + "type": "core::felt252" + } + ], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + } + ] + }, + { + "name": "RolesImpl", + "type": "impl", + "interface_name": "src::roles_interface::IMinimalRoles" + }, + { + "name": "src::roles_interface::IMinimalRoles", + "type": "interface", + "items": [ + { + "name": "is_governance_admin", + "type": "function", + "inputs": [ + { + "name": "account", + "type": "core::starknet::contract_address::ContractAddress" + } + ], + "outputs": [ + { + "type": "core::bool" + } + ], + "state_mutability": "view" + }, + { + "name": "is_upgrade_governor", + "type": "function", + "inputs": [ + { + "name": "account", + "type": "core::starknet::contract_address::ContractAddress" + } + ], + "outputs": [ + { + "type": "core::bool" + } + ], + "state_mutability": "view" + }, + { + "name": "register_governance_admin", + "type": "function", + "inputs": [ + { + "name": "account", + "type": "core::starknet::contract_address::ContractAddress" + } + ], + "outputs": [], + "state_mutability": "external" + }, + { + "name": "remove_governance_admin", + "type": "function", + "inputs": [ + { + "name": "account", + "type": "core::starknet::contract_address::ContractAddress" + } + ], + "outputs": [], + "state_mutability": "external" + }, + { + "name": "register_upgrade_governor", + "type": "function", + "inputs": [ + { + "name": "account", + "type": "core::starknet::contract_address::ContractAddress" + } + ], + "outputs": [], + "state_mutability": "external" + }, + { + "name": "remove_upgrade_governor", + "type": "function", + "inputs": [ + { + "name": "account", + "type": "core::starknet::contract_address::ContractAddress" + } + ], + "outputs": [], + "state_mutability": "external" + }, + { + "name": "renounce", + "type": "function", + "inputs": [ + { + "name": "role", + "type": "core::felt252" + } + ], + "outputs": [], + "state_mutability": "external" + } + ] + }, + { + "name": "ERC20Impl", + "type": "impl", + "interface_name": "openzeppelin::token::erc20::interface::IERC20" + }, + { + "name": "openzeppelin::token::erc20::interface::IERC20", + "type": "interface", + "items": [ + { + "name": "name", + "type": "function", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "name": "symbol", + "type": "function", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "name": "decimals", + "type": "function", + "inputs": [], + "outputs": [ + { + "type": "core::integer::u8" + } + ], + "state_mutability": "view" + }, + { + "name": "total_supply", + "type": "function", + "inputs": [], + "outputs": [ + { + "type": "core::integer::u256" + } + ], + "state_mutability": "view" + }, + { + "name": "balance_of", + "type": "function", + "inputs": [ + { + "name": "account", + "type": "core::starknet::contract_address::ContractAddress" + } + ], + "outputs": [ + { + "type": "core::integer::u256" + } + ], + "state_mutability": "view" + }, + { + "name": "allowance", + "type": "function", + "inputs": [ + { + "name": "owner", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "name": "spender", + "type": "core::starknet::contract_address::ContractAddress" + } + ], + "outputs": [ + { + "type": "core::integer::u256" + } + ], + "state_mutability": "view" + }, + { + "name": "transfer", + "type": "function", + "inputs": [ + { + "name": "recipient", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "name": "amount", + "type": "core::integer::u256" + } + ], + "outputs": [ + { + "type": "core::bool" + } + ], + "state_mutability": "external" + }, + { + "name": "transfer_from", + "type": "function", + "inputs": [ + { + "name": "sender", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "name": "recipient", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "name": "amount", + "type": "core::integer::u256" + } + ], + "outputs": [ + { + "type": "core::bool" + } + ], + "state_mutability": "external" + }, + { + "name": "approve", + "type": "function", + "inputs": [ + { + "name": "spender", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "name": "amount", + "type": "core::integer::u256" + } + ], + "outputs": [ + { + "type": "core::bool" + } + ], + "state_mutability": "external" + } + ] + }, + { + "name": "ERC20CamelOnlyImpl", + "type": "impl", + "interface_name": "openzeppelin::token::erc20::interface::IERC20CamelOnly" + }, + { + "name": "openzeppelin::token::erc20::interface::IERC20CamelOnly", + "type": "interface", + "items": [ + { + "name": "totalSupply", + "type": "function", + "inputs": [], + "outputs": [ + { + "type": "core::integer::u256" + } + ], + "state_mutability": "view" + }, + { + "name": "balanceOf", + "type": "function", + "inputs": [ + { + "name": "account", + "type": "core::starknet::contract_address::ContractAddress" + } + ], + "outputs": [ + { + "type": "core::integer::u256" + } + ], + "state_mutability": "view" + }, + { + "name": "transferFrom", + "type": "function", + "inputs": [ + { + "name": "sender", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "name": "recipient", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "name": "amount", + "type": "core::integer::u256" + } + ], + "outputs": [ + { + "type": "core::bool" + } + ], + "state_mutability": "external" + } + ] + }, + { + "name": "constructor", + "type": "constructor", + "inputs": [ + { + "name": "name", + "type": "core::felt252" + }, + { + "name": "symbol", + "type": "core::felt252" + }, + { + "name": "decimals", + "type": "core::integer::u8" + }, + { + "name": "initial_supply", + "type": "core::integer::u256" + }, + { + "name": "recipient", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "name": "permitted_minter", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "name": "provisional_governance_admin", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "name": "upgrade_delay", + "type": "core::integer::u64" + } + ] + }, + { + "name": "increase_allowance", + "type": "function", + "inputs": [ + { + "name": "spender", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "name": "added_value", + "type": "core::integer::u256" + } + ], + "outputs": [ + { + "type": "core::bool" + } + ], + "state_mutability": "external" + }, + { + "name": "decrease_allowance", + "type": "function", + "inputs": [ + { + "name": "spender", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "name": "subtracted_value", + "type": "core::integer::u256" + } + ], + "outputs": [ + { + "type": "core::bool" + } + ], + "state_mutability": "external" + }, + { + "name": "increaseAllowance", + "type": "function", + "inputs": [ + { + "name": "spender", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "name": "addedValue", + "type": "core::integer::u256" + } + ], + "outputs": [ + { + "type": "core::bool" + } + ], + "state_mutability": "external" + }, + { + "name": "decreaseAllowance", + "type": "function", + "inputs": [ + { + "name": "spender", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "name": "subtractedValue", + "type": "core::integer::u256" + } + ], + "outputs": [ + { + "type": "core::bool" + } + ], + "state_mutability": "external" + }, + { + "kind": "struct", + "name": "openzeppelin::token::erc20_v070::erc20::ERC20::Transfer", + "type": "event", + "members": [ + { + "kind": "data", + "name": "from", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "kind": "data", + "name": "to", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "kind": "data", + "name": "value", + "type": "core::integer::u256" + } + ] + }, + { + "kind": "struct", + "name": "openzeppelin::token::erc20_v070::erc20::ERC20::Approval", + "type": "event", + "members": [ + { + "kind": "data", + "name": "owner", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "kind": "data", + "name": "spender", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "kind": "data", + "name": "value", + "type": "core::integer::u256" + } + ] + }, + { + "kind": "struct", + "name": "src::replaceability_interface::ImplementationAdded", + "type": "event", + "members": [ + { + "kind": "data", + "name": "implementation_data", + "type": "src::replaceability_interface::ImplementationData" + } + ] + }, + { + "kind": "struct", + "name": "src::replaceability_interface::ImplementationRemoved", + "type": "event", + "members": [ + { + "kind": "data", + "name": "implementation_data", + "type": "src::replaceability_interface::ImplementationData" + } + ] + }, + { + "kind": "struct", + "name": "src::replaceability_interface::ImplementationReplaced", + "type": "event", + "members": [ + { + "kind": "data", + "name": "implementation_data", + "type": "src::replaceability_interface::ImplementationData" + } + ] + }, + { + "kind": "struct", + "name": "src::replaceability_interface::ImplementationFinalized", + "type": "event", + "members": [ + { + "kind": "data", + "name": "impl_hash", + "type": "core::starknet::class_hash::ClassHash" + } + ] + }, + { + "kind": "struct", + "name": "src::access_control_interface::RoleGranted", + "type": "event", + "members": [ + { + "kind": "data", + "name": "role", + "type": "core::felt252" + }, + { + "kind": "data", + "name": "account", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "kind": "data", + "name": "sender", + "type": "core::starknet::contract_address::ContractAddress" + } + ] + }, + { + "kind": "struct", + "name": "src::access_control_interface::RoleRevoked", + "type": "event", + "members": [ + { + "kind": "data", + "name": "role", + "type": "core::felt252" + }, + { + "kind": "data", + "name": "account", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "kind": "data", + "name": "sender", + "type": "core::starknet::contract_address::ContractAddress" + } + ] + }, + { + "kind": "struct", + "name": "src::access_control_interface::RoleAdminChanged", + "type": "event", + "members": [ + { + "kind": "data", + "name": "role", + "type": "core::felt252" + }, + { + "kind": "data", + "name": "previous_admin_role", + "type": "core::felt252" + }, + { + "kind": "data", + "name": "new_admin_role", + "type": "core::felt252" + } + ] + }, + { + "kind": "struct", + "name": "src::roles_interface::GovernanceAdminAdded", + "type": "event", + "members": [ + { + "kind": "data", + "name": "added_account", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "kind": "data", + "name": "added_by", + "type": "core::starknet::contract_address::ContractAddress" + } + ] + }, + { + "kind": "struct", + "name": "src::roles_interface::GovernanceAdminRemoved", + "type": "event", + "members": [ + { + "kind": "data", + "name": "removed_account", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "kind": "data", + "name": "removed_by", + "type": "core::starknet::contract_address::ContractAddress" + } + ] + }, + { + "kind": "struct", + "name": "src::roles_interface::UpgradeGovernorAdded", + "type": "event", + "members": [ + { + "kind": "data", + "name": "added_account", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "kind": "data", + "name": "added_by", + "type": "core::starknet::contract_address::ContractAddress" + } + ] + }, + { + "kind": "struct", + "name": "src::roles_interface::UpgradeGovernorRemoved", + "type": "event", + "members": [ + { + "kind": "data", + "name": "removed_account", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "kind": "data", + "name": "removed_by", + "type": "core::starknet::contract_address::ContractAddress" + } + ] + }, + { + "kind": "enum", + "name": "openzeppelin::token::erc20_v070::erc20::ERC20::Event", + "type": "event", + "variants": [ + { + "kind": "nested", + "name": "Transfer", + "type": "openzeppelin::token::erc20_v070::erc20::ERC20::Transfer" + }, + { + "kind": "nested", + "name": "Approval", + "type": "openzeppelin::token::erc20_v070::erc20::ERC20::Approval" + }, + { + "kind": "nested", + "name": "ImplementationAdded", + "type": "src::replaceability_interface::ImplementationAdded" + }, + { + "kind": "nested", + "name": "ImplementationRemoved", + "type": "src::replaceability_interface::ImplementationRemoved" + }, + { + "kind": "nested", + "name": "ImplementationReplaced", + "type": "src::replaceability_interface::ImplementationReplaced" + }, + { + "kind": "nested", + "name": "ImplementationFinalized", + "type": "src::replaceability_interface::ImplementationFinalized" + }, + { + "kind": "nested", + "name": "RoleGranted", + "type": "src::access_control_interface::RoleGranted" + }, + { + "kind": "nested", + "name": "RoleRevoked", + "type": "src::access_control_interface::RoleRevoked" + }, + { + "kind": "nested", + "name": "RoleAdminChanged", + "type": "src::access_control_interface::RoleAdminChanged" + }, + { + "kind": "nested", + "name": "GovernanceAdminAdded", + "type": "src::roles_interface::GovernanceAdminAdded" + }, + { + "kind": "nested", + "name": "GovernanceAdminRemoved", + "type": "src::roles_interface::GovernanceAdminRemoved" + }, + { + "kind": "nested", + "name": "UpgradeGovernorAdded", + "type": "src::roles_interface::UpgradeGovernorAdded" + }, + { + "kind": "nested", + "name": "UpgradeGovernorRemoved", + "type": "src::roles_interface::UpgradeGovernorRemoved" + } + ] + } +] diff --git a/packages/nextjs/utils/solas-abis/SchemaRegistry.json b/packages/nextjs/utils/solas-abis/SchemaRegistry.json new file mode 100644 index 0000000..20377de --- /dev/null +++ b/packages/nextjs/utils/solas-abis/SchemaRegistry.json @@ -0,0 +1,115 @@ +[ + { + "name": "SchemaRegistryImpl", + "type": "impl", + "interface_name": "contracts::SchemaRegistry::ISchemaRegistry" + }, + { + "name": "core::byte_array::ByteArray", + "type": "struct", + "members": [ + { + "name": "data", + "type": "core::array::Array::" + }, + { + "name": "pending_word", + "type": "core::felt252" + }, + { + "name": "pending_word_len", + "type": "core::integer::u32" + } + ] + }, + { + "name": "core::bool", + "type": "enum", + "variants": [ + { + "name": "False", + "type": "()" + }, + { + "name": "True", + "type": "()" + } + ] + }, + { + "name": "contracts::SchemaRegistry::ISchemaRegistry", + "type": "interface", + "items": [ + { + "name": "register", + "type": "function", + "inputs": [ + { + "name": "schema", + "type": "core::byte_array::ByteArray" + }, + { + "name": "revocable", + "type": "core::bool" + } + ], + "outputs": [ + { + "type": "core::integer::u128" + } + ], + "state_mutability": "external" + }, + { + "name": "get_schema", + "type": "function", + "inputs": [ + { + "name": "uid", + "type": "core::integer::u128" + } + ], + "outputs": [ + { + "type": "(core::integer::u128, core::bool, core::byte_array::ByteArray)" + } + ], + "state_mutability": "view" + } + ] + }, + { + "kind": "struct", + "name": "contracts::SchemaRegistry::SchemaRegistry::Registered", + "type": "event", + "members": [ + { + "kind": "key", + "name": "uid", + "type": "core::integer::u128" + }, + { + "kind": "data", + "name": "caller", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "kind": "data", + "name": "schema_record", + "type": "core::byte_array::ByteArray" + } + ] + }, + { + "kind": "enum", + "name": "contracts::SchemaRegistry::SchemaRegistry::Event", + "type": "event", + "variants": [ + { + "kind": "nested", + "name": "Registered", + "type": "contracts::SchemaRegistry::SchemaRegistry::Registered" + } + ] + } +] diff --git a/packages/nextjs/utils/utils.ts b/packages/nextjs/utils/utils.ts new file mode 100644 index 0000000..b130e75 --- /dev/null +++ b/packages/nextjs/utils/utils.ts @@ -0,0 +1,171 @@ +import { provider } from "~~/utils/Provider"; +import { Contract } from "starknet"; + +import ERC20abi from "~~/utils/solas-abis/ERC20.json"; +const contractAddress = + "0x041e7455a1009c150268b1bfec337246e4539f07885315b69495dac1abf5ff4c"; + +const contract = new Contract(ERC20abi, contractAddress, provider); + +export type Attestation = { + uid: string; + schema: string; + isEndorsement: boolean; + name: string; + domain: string; + context: string; + ipfsHash: string; + from: string; + to: string; + created: string; + expiration: string; + revoked: string; +}; +export type Schema = { + uid: string; + schema: string; + attestations: number; +}; + +export const fetchAllSchemas = async () => { + // Replace this with your actual fetch call to get attestation data + return [ + { + uid: "0x041e7455a1009c150268b1bfec337246e4539f07885315b69495dac1abf5ff4c", + schema: "address author uint stakeAmaount uint royaltyAmount", + attestations: 100, + }, + { + uid: "0x041e7455a1009c150268b1bfec337246e4539f07885315b69495dac1abf5ff4c", + schema: "address author uint stakeAmaount uint royaltyAmount", + attestations: 45, + }, + { + uid: "0x041e7455a1009c150268b1bfec337246e4539f07885315b69495dac1abf5ff4c", + schema: "address author uint stakeAmaount uint royaltyAmount", + attestations: 33, + }, + ]; +}; + +// Dummy fetch function to simulate fetching attestation data +export const fetchSchema = async (uuid: string): Promise => { + // Replace this with your actual fetch call to get attestation data + return { + uid: uuid, + schema: "address author uint stakeAmaount uint royaltyAmount", + attestations: 100, + }; +}; + +export const fetchAllAttestations = async () => { + // Replace this with your actual fetch call to get attestation data + return [ + { + uid: "0x041e7455a1009c150268b1bfec337246e4539f07885315b69495dac1abf5ff4c", + schema: "Endorsements", + from: "attestations.icebreakerlabs.eth", + to: "0xD7F75E6bB7717d4C6Df...", + type: "OFFCHAIN", + age: timeAgo(1719088781), + }, + { + uid: "0x041e7455a1009c150268b1bfec337246e4539f07885315b69495dac1abf5ff4c", + schema: "Endorsements", + from: "attestations.icebreakerlabs.eth", + to: "0xD7F75E6bB7717d4C6Df...", + type: "OFFCHAIN", + age: timeAgo(1719058781), + }, + { + uid: "0x041e7455a1009c150268b1bfec337246e4539f07885315b69495dac1abf5ff4c", + schema: "Endorsements", + from: "attestations.icebreakerlabs.eth", + to: "0xD7F75E6bB7717d4C6Df...", + type: "OFFCHAIN", + age: timeAgo(1719088681), + }, + // Add more attestations as needed + ]; +}; + +// Dummy fetch function to simulate fetching attestation data +export const fetchAttestation = async (uuid: string): Promise => { + // Replace this with your actual fetch call to get attestation data + return { + uid: uuid, + schema: "#159", + isEndorsement: true, + name: "Worked with", + domain: "via 0x794ce...", + context: "via 0x794ce...", + ipfsHash: "ipfs://QmZaTm...Jx3ziBQnPS", + from: "0x07a059F968efF2D70973Ed90abfb5B93DD6050198888792b5EAE5798BdE677E", + to: "0xd34d059F968efF2D70973Ed90abfb5B93DD6050198888792b5EAE5798BdE677E", + created: "06/22/2024 3:13:21 am", + expiration: "Never", + revoked: "No", + }; +}; + +export const fetchTokenName = async (): Promise => { + try { + const name = await contract.call("name"); + console.log("Token Name:", name); + return name; + } catch (error) { + console.error("Error fetching token name:", error); + } +}; + +export const getSpecVersion = async (): Promise => { + try { + const specVersion = await provider.getSpecVersion(); + console.log("Spec version is: ", specVersion); + return specVersion; + } catch (error) { + console.error("Error fetching spec version:", error); + throw error; // Rethrow the error to handle it in the calling function + } +}; + +export const shortAddress = (addr: string): string => { + if (!addr) { + return ""; + } + return ( + addr.toString().substring(0, 6) + + "..." + + addr + .toString() + .substring(addr.toString().length - 6, addr.toString().length + 1) + ); +}; + +export const timeAgo = (timestamp: number): string => { + const now = Date.now(); + const past = timestamp * 1000; // Convert seconds to milliseconds + const diff = now - past; + + const seconds = Math.floor(diff / 1000); + const minutes = Math.floor(seconds / 60); + const hours = Math.floor(minutes / 60); + const days = Math.floor(hours / 24); + const weeks = Math.floor(days / 7); + const months = Math.floor(days / 30); + const years = Math.floor(days / 365); + + if (years > 0) return `${years} year${years > 1 ? "s" : ""} ago`; + if (months > 0) return `${months} month${months > 1 ? "s" : ""} ago`; + if (weeks > 0) return `${weeks} week${weeks > 1 ? "s" : ""} ago`; + if (days > 0) return `${days} day${days > 1 ? "s" : ""} ago`; + if (hours > 0) return `${hours} hour${hours > 1 ? "s" : ""} ago`; + if (minutes > 0) return `${minutes} minute${minutes > 1 ? "s" : ""} ago`; + return `${seconds} second${seconds > 1 ? "s" : ""} ago`; +}; + +export const stringifyBigInt = (obj: any) => { + return JSON.stringify(obj, (_, value) => + typeof value === "bigint" ? value.toString() : value, + ); +};