diff --git a/README.md b/README.md
index 922f5c4..c068ea8 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# Pin Save - decentralized Pinterest
+# PinSave - decentralized decentalized image sharing platform
@@ -18,7 +18,7 @@
-Pin Save is a decentralized image, video sharing and content aggregation platform where community controls the platform.
+PinSave is a decentralized image and content aggregation platform where community controls the platform.
1. The decentralized feed reinforces the discovery of content and feedback.
2. Upgradeable, resilient decentralized storage.
@@ -32,15 +32,15 @@ Pin Save is a decentralized image, video sharing and content aggregation platfor
- Image posting:
-
+
- Profile Page with ENS:
-
+
-### Optimism Smart contracts
+### Metis PinSave Smart contracts
-[Metis Smart contract Etherscan](https://explorer.metis.io/token/0x6F67850013b5775E36E35071a5CdD16ea43e1061)
+[Metis PinSave Smart contract Etherscan](https://explorer.metis.io/token/0x6F67850013b5775E36E35071a5CdD16ea43e1061)
## Setup
@@ -54,9 +54,8 @@ yarn dev
## Further Resources
- [PinSave Figma Resources](https://www.figma.com/community/file/1102944149244783025)
-- [Zk Ok Pin Save](https://zkok.io/mina/pin-save/)
-- [EthBucharest 2024: Zero Knowledge proofs on Mina, zkPassport and SoulBound NFTs](https://docs.google.com/presentation/d/1OmJJgzk4iFbKexqBw87oU7oh4H9lXlFFh3eas0EF9y8/edit?usp=sharing)
+- [EthBucharest 2024 PinSave: Zero Knowledge proofs on Mina, zkPassport and SoulBound NFTs](https://docs.google.com/presentation/d/1OmJJgzk4iFbKexqBw87oU7oh4H9lXlFFh3eas0EF9y8/edit?usp=sharing)
- [PinSave.app DR](https://ahrefs.com/website-authority-checker/?input=pinsave.app)
-- [Npm Pin Save mina package](https://www.npmjs.com/package/pin-mina)
-- [Pin Save on Dspyt](https://dspyt.com/PinSave)
-- [Pin Save retroPGF3](https://round3.optimism.io/projects/0xc613e2a991ce0dbcf8fae1d6128e67543da9710e14831112fba654cc8fe8c389)
+- [Npm PinSave mina package](https://www.npmjs.com/package/pin-mina)
+- [PinSave on Dspyt](https://dspyt.com/PinSave)
+- [PinSave retroPGF3](https://round3.optimism.io/projects/0xc613e2a991ce0dbcf8fae1d6128e67543da9710e14831112fba654cc8fe8c389)
diff --git a/packages/frontend/LICENSE b/packages/frontend/LICENSE
index 8b4dfcb..5824134 100644
--- a/packages/frontend/LICENSE
+++ b/packages/frontend/LICENSE
@@ -1,6 +1,6 @@
# MIT License
-Copyright (c) Pavel Fedotov 2024
+Copyright (c) Pavel Fedotov 2025
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/packages/frontend/components/Post/DisplayMedia.tsx b/packages/frontend/components/Post/DisplayMedia.tsx
index ec025c7..07f4b36 100644
--- a/packages/frontend/components/Post/DisplayMedia.tsx
+++ b/packages/frontend/components/Post/DisplayMedia.tsx
@@ -47,6 +47,7 @@ const DisplayMedia: React.FC = ({ post }) => {
width={width}
src={post.image}
alt={post.name}
+ loading="lazy"
style={{
height: "95%",
borderRadius: "10px",
diff --git a/packages/frontend/components/Posts/PostCard.tsx b/packages/frontend/components/Posts/PostCard.tsx
index 88edb38..59a47b5 100644
--- a/packages/frontend/components/Posts/PostCard.tsx
+++ b/packages/frontend/components/Posts/PostCard.tsx
@@ -2,10 +2,14 @@ import { Paper, Text } from "@mantine/core";
import Image from "next/image";
import Link from "next/link";
-import type { Post } from "@/services/upload";
+type PostReduced = {
+ image: string;
+ name: string;
+ tokenId: number;
+};
interface IMyProps {
- post: Post;
+ post: PostReduced;
}
const PostCard: React.FC = ({ post }) => {
diff --git a/packages/frontend/components/SEO/index.tsx b/packages/frontend/components/SEO/index.tsx
index bec93c3..2edf54c 100644
--- a/packages/frontend/components/SEO/index.tsx
+++ b/packages/frontend/components/SEO/index.tsx
@@ -23,6 +23,7 @@ const CommonSEO = ({
content={`${siteMetadata.siteUrl}${router.asPath}`}
/>
+
diff --git a/packages/frontend/components/SEO/siteMetadata.ts b/packages/frontend/components/SEO/siteMetadata.ts
index b1d0765..acb0e15 100644
--- a/packages/frontend/components/SEO/siteMetadata.ts
+++ b/packages/frontend/components/SEO/siteMetadata.ts
@@ -1,9 +1,9 @@
export const siteMetadata = {
- title: "Pin Save - decentralized Pinterest",
+ title: "PinSave - Decentralized Image Sharing & Content Aggregation Platform",
author: "Pavel Fedotov",
- headerTitle: "Pin Save - decentralized Pinterest",
+ headerTitle: "PinSave - Decentralized Image Sharing",
description:
- "Pin Save is a platform for decentralized content aggregation and image sharing where users have content ownership.",
+ "PinSave is a platform for decentralized content aggregation and image sharing where users have content ownership.",
ogType: "website",
ogImageUrl: "https://metis.pinsave.app/TwitterIconWords.png",
siteUrl: "https://metis.pinsave.app",
diff --git a/packages/frontend/components/UploadForm/index.tsx b/packages/frontend/components/UploadForm/index.tsx
index da59339..77a1e09 100644
--- a/packages/frontend/components/UploadForm/index.tsx
+++ b/packages/frontend/components/UploadForm/index.tsx
@@ -3,7 +3,6 @@ import {
Paper,
Title,
TextInput,
- Textarea,
Group,
Button,
Image,
@@ -13,7 +12,6 @@ import {
} from "@mantine/core";
import { Dropzone, MIME_TYPES } from "@mantine/dropzone";
import { useState, useEffect } from "react";
-import ReactPlayer from "react-player";
import { Upload, Replace } from "tabler-icons-react";
import {
useAccount,
@@ -29,24 +27,21 @@ import { getContractInfo } from "@/utils/contracts";
export const dropzoneChildren = (image: File | undefined) => {
if (image) {
- let link = URL.createObjectURL(image);
+ let link: string = URL.createObjectURL(image);
return (
- {image.type[0] === "i" ? (
-
- ) : (
-
- )}
+
+
{
const { address: senderAddress } = useAccount();
const { address: contractAddress, abi } = getContractInfo();
- const { data: hash, writeContract: writeMintPost } = useWriteContract();
+ const {
+ data: hash,
+ writeContract: writeMintPost,
+ status,
+ } = useWriteContract();
const [name, setName] = useState("");
const [description, setDescription] = useState("");
@@ -98,22 +97,19 @@ const UploadForm = () => {
const [postReceiver, setPostReceiver] = useState<`0x${string}` | undefined>(
senderAddress
);
-
- const [isPostUpdated, setIsPostUpdated] = useState(false);
- const [isPostLoading, setIsPostLoading] = useState(false);
-
- const [cid, setCid] = useState("");
-
+ const [ensName, setEnsName] = useState("");
const [provider, setProvider] = useState<"Pinata">("Pinata");
- const [lastHash, setLastHash] = useState("");
+ const [isPostReady, setIsPostReady] = useState(false);
+ const [isPostLoading, setIsPostLoading] = useState(false);
- const [ensName, setEnsName] = useState("");
+ const [cid, setCID] = useState("");
+ const [lastHash, setLastHash] = useState();
const config = createConfig({
chains: [mainnet],
transports: {
- [mainnet.id]: http(process.env.NEXT_PUBLIC_ALCHEMY),
+ [mainnet.id]: http(process.env.NEXT_PUBLIC_ALCHEMY_MAINNET),
},
});
@@ -129,6 +125,7 @@ const UploadForm = () => {
image?: File
) {
if (description !== "" && name !== "" && image && postReceiver) {
+ setIsPostLoading(true);
const cid: string | undefined = await UploadData({
data: { name: name, description: description, image: image },
provider: provider,
@@ -138,13 +135,12 @@ const UploadForm = () => {
throw new Error("no cid");
}
- setCid(cid);
+ setCID(cid);
+ setIsPostReady(true);
setImage(undefined);
setName("");
setDescription("");
-
- setIsPostLoading(true);
}
}
@@ -160,14 +156,9 @@ const UploadForm = () => {
setPostReceiver(receiverAddress);
}
- if (isPostLoading) {
- setIsPostUpdated(true);
- console.log("Updated response:" + cid);
- }
-
- if (hash && hash !== lastHash && isPostUpdated) {
+ if (hash && hash !== lastHash && isPostReady) {
setLastHash(hash);
- setIsPostUpdated(false);
+ setIsPostReady(false);
setIsPostLoading(false);
}
}, [
@@ -176,10 +167,11 @@ const UploadForm = () => {
receiverAddress,
lastHash,
cid,
- isPostUpdated,
+ isPostReady,
senderAddress,
ensName,
hash,
+ fetchedAccount,
]);
return (
@@ -214,7 +206,7 @@ const UploadForm = () => {
)}
- {!isPostUpdated ? (
+ {!isPostLoading ? (
{
value={name}
onChange={(e) => setName(e.target.value)}
/>
-
) : (
-
-
-
+ <>
+ {isPostReady ? (
+ <>
+
+
+
+
+ CID: {cid}
+
+ >
+ ) : (
+
+ Uploading to IPFS
+
+ )}
+ >
)}
diff --git a/packages/frontend/environment.d.ts b/packages/frontend/environment.d.ts
index 696894f..3b42694 100644
--- a/packages/frontend/environment.d.ts
+++ b/packages/frontend/environment.d.ts
@@ -6,7 +6,9 @@ declare global {
NEXT_PUBLIC_PINATA_JWT: string;
NEXT_PUBLIC_GATEWAY_URL: string;
NEXT_PUBLIC_WALLETCONNECT_ID: string;
- NEXT_PUBLIC_ALCHEMY: string;
+ NEXT_PUBLIC_ALCHEMY_MAINNET: string;
+ NEXT_PUBLIC_ALCHEMY_METIS_FIRST: string;
+ NEXT_PUBLIC_ALCHEMY_METIS_SECOND: string;
ENV: "test" | "dev" | "prod";
}
}
diff --git a/packages/frontend/hooks/api/posts/index.ts b/packages/frontend/hooks/api/posts/index.ts
index fb99cae..6f84e88 100644
--- a/packages/frontend/hooks/api/posts/index.ts
+++ b/packages/frontend/hooks/api/posts/index.ts
@@ -11,16 +11,17 @@ export const usePosts = () => {
},
initialPageParam: undefined,
getNextPageParam: (lastPage: any, pages: any) => {
- if (lastPage.items[5]?.token_id < lastPage.totalSupply) {
+ if (lastPage.items[5]?.tokenId < lastPage.totalSupply) {
return pages.length + 1;
}
},
});
};
-export const usePost = (id: string) => {
+export const usePost = (id: number, enabled: boolean) => {
return useQuery({
queryKey: [id],
queryFn: () => fetchPost(id),
+ enabled: enabled,
});
};
diff --git a/packages/frontend/hooks/api/posts/queries.ts b/packages/frontend/hooks/api/posts/queries.ts
index 4e01968..bbd19ba 100644
--- a/packages/frontend/hooks/api/posts/queries.ts
+++ b/packages/frontend/hooks/api/posts/queries.ts
@@ -12,7 +12,7 @@ export const fetchPosts = async ({
}
};
-export const fetchPost = async (id: string) => {
+export const fetchPost = async (id: number) => {
try {
return await fetcher(`/api/metis/posts/${id}`);
} catch (error) {
diff --git a/packages/frontend/next.config.js b/packages/frontend/next.config.js
index 6ba241d..7b4b84e 100644
--- a/packages/frontend/next.config.js
+++ b/packages/frontend/next.config.js
@@ -18,11 +18,11 @@ const nextConfig = {
},
{
protocol: "https",
- hostname: "ipfs.io",
- pathname: "/ipfs/**",
+ hostname: "**.mypinata.cloud/**",
},
],
minimumCacheTTL: 31536000,
+ unoptimized: true,
},
};
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index b79a18b..757f846 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -30,7 +30,6 @@
"react": "^18.1.0",
"react-dom": "^18.1.0",
"react-icons": "^5.3.0",
- "react-player": "^2.11.2",
"react-query": "^3.39.3",
"sharp": "^0.33.0",
"tabler-icons-react": "^1.56.0",
diff --git a/packages/frontend/pages/_app.tsx b/packages/frontend/pages/_app.tsx
index 225d07d..a224ac6 100644
--- a/packages/frontend/pages/_app.tsx
+++ b/packages/frontend/pages/_app.tsx
@@ -3,7 +3,6 @@ import "@rainbow-me/rainbowkit/styles.css";
import type { NextComponentType } from "next";
import type AppProps from "next/app";
-
import { MantineProvider } from "@mantine/core";
import { NotificationsProvider } from "@mantine/notifications";
import { getDefaultConfig, RainbowKitProvider } from "@rainbow-me/rainbowkit";
@@ -26,7 +25,7 @@ export interface MyWalletOptions {
}
const config = getDefaultConfig({
- appName: "Pin Save",
+ appName: "PinSave",
projectId: process.env.NEXT_PUBLIC_WALLETCONNECT_ID,
chains: [metis],
ssr: true,
diff --git a/packages/frontend/pages/api/metis/pages/[number].tsx b/packages/frontend/pages/api/metis/pages/[number].tsx
index 5b6a488..eb2b4b9 100644
--- a/packages/frontend/pages/api/metis/pages/[number].tsx
+++ b/packages/frontend/pages/api/metis/pages/[number].tsx
@@ -1,9 +1,9 @@
-import { fetchDecodedPost } from "@/services/fetchCid";
-import { getContractInfo } from "@/utils/contracts";
-
import type { NextApiRequest, NextApiResponse } from "next";
import { Contract, JsonRpcProvider } from "ethers";
+import { fetchDecodedPost } from "@/services/fetchCid";
+import { getContractInfo } from "@/utils/contracts";
+
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
@@ -36,7 +36,7 @@ export default async function handler(
try {
for (let i = lowerLimit; upperLimit >= i; i++) {
result = await contract.getPostCid(i);
- const item = await fetchDecodedPost(result);
+ const item = await fetchDecodedPost(result, 150);
items.push({ tokenId: i, ...item });
}
} catch {
diff --git a/packages/frontend/pages/api/metis/posts/[id].tsx b/packages/frontend/pages/api/metis/posts/[id].tsx
index 5b47a4f..01b94e6 100644
--- a/packages/frontend/pages/api/metis/posts/[id].tsx
+++ b/packages/frontend/pages/api/metis/posts/[id].tsx
@@ -2,6 +2,7 @@ import type { NextApiRequest, NextApiResponse } from "next";
import { Contract, JsonRpcProvider } from "ethers";
import { ObjectJsonMetadata, fetchDecodedPost } from "@/services/fetchCid";
+
import { getContractInfo } from "@/utils/contracts";
export default async function handler(
@@ -19,8 +20,10 @@ export default async function handler(
const contract: Contract = new Contract(address, abi, provider);
const postCid: string = await contract.getPostCid(id);
- const objectJsonMetadata: ObjectJsonMetadata =
- await fetchDecodedPost(postCid);
+ const objectJsonMetadata: ObjectJsonMetadata = await fetchDecodedPost(
+ postCid,
+ 500
+ );
const owner: string = await contract.getPostOwner(id);
const postAuthor: string = await contract.getPostAuthor(id);
diff --git a/packages/frontend/pages/index.tsx b/packages/frontend/pages/index.tsx
index d1b94df..1117742 100644
--- a/packages/frontend/pages/index.tsx
+++ b/packages/frontend/pages/index.tsx
@@ -1,54 +1,30 @@
import type { NextPage } from "next";
-import { useRef, useCallback, useMemo } from "react";
-import { Box, Center, Title, Loader } from "@mantine/core";
+import Link from "next/link";
+import { Contract, JsonRpcProvider } from "ethers";
+import { Box, Button, Center, Title } from "@mantine/core";
-import type { Post } from "@/services/upload";
import PostCard from "@/components/Posts/PostCard";
import { PageSEO } from "@/components/SEO";
-import { usePosts } from "@/hooks/api";
+import { fetchDecodedPost } from "@/services/fetchCid";
+import { getContractInfo } from "@/utils/contracts";
-const Home: NextPage = () => {
- const {
- data: posts,
- isFetching,
- isLoading,
- fetchNextPage,
- hasNextPage,
- } = usePosts();
+interface Props {
+ posts: PostReduced[];
+}
- const observer = useRef(null);
-
- const lastElement = useCallback(
- (node: HTMLDivElement) => {
- if (isLoading) return;
-
- if (observer.current) observer.current.disconnect();
-
- observer.current = new IntersectionObserver((entries) => {
- if (entries[0].isIntersecting && hasNextPage && !isFetching) {
- fetchNextPage();
- }
- });
-
- if (node) observer.current.observe(node);
- },
- [fetchNextPage, hasNextPage, isFetching, isLoading]
- );
-
- const fetchedPosts = useMemo(() => {
- return posts?.pages.reduce((acc, page) => {
- return [...acc, ...page.items];
- }, []);
- }, [posts]);
+type PostReduced = {
+ image: string;
+ name: string;
+ tokenId: number;
+};
+const Home: NextPage = ({ posts }) => {
return (
-
PinSave Home Page
-
{
gridTemplateRows: "masonry",
}}
>
- {fetchedPosts &&
- fetchedPosts?.map((post: Post) => {
- return (
-
-
-
- );
- })}
+ {posts.map((post: PostReduced) => {
+ return (
+
+
+
+ );
+ })}
-
- {isFetching && (
-
-
-
- )}
+
+
+
+
+
);
};
export default Home;
+
+export const getStaticProps = async () => {
+ const { address, abi } = getContractInfo();
+ const provider = new JsonRpcProvider(
+ "https://metis-mainnet.public.blastapi.io"
+ );
+ const contract: Contract = new Contract(address, abi, provider);
+
+ const posts: PostReduced[] = [];
+
+ for (let i = 1; i <= 24; i++) {
+ const result = await contract.getPostCid(i);
+ const post = await fetchDecodedPost(result, 150);
+ posts.push({ image: post.image, name: post.name, tokenId: i });
+ }
+ return {
+ props: {
+ posts,
+ },
+ revalidate: 60,
+ };
+};
diff --git a/packages/frontend/pages/page/[page].tsx b/packages/frontend/pages/page/[page].tsx
new file mode 100644
index 0000000..edfb3bf
--- /dev/null
+++ b/packages/frontend/pages/page/[page].tsx
@@ -0,0 +1,140 @@
+import type { GetStaticPaths, NextPage } from "next";
+import Link from "next/link";
+import { Contract, JsonRpcProvider } from "ethers";
+import { Box, Button, Center, Title } from "@mantine/core";
+
+import PostCard from "@/components/Posts/PostCard";
+import { PageSEO } from "@/components/SEO";
+import { fetchDecodedPost } from "@/services/fetchCid";
+import { getContractInfo } from "@/utils/contracts";
+
+interface Props {
+ posts: PostReduced[];
+ hasNextPage: boolean;
+ page: number;
+}
+
+type PostReduced = {
+ image: string;
+ name: string;
+ tokenId: number;
+};
+
+const Home: NextPage = ({ posts, hasNextPage, page }) => {
+ return (
+
+
+
+
+ PinSave Page {page}
+
+
+
+ {posts.map((post: PostReduced) => {
+ return (
+
+
+
+ );
+ })}
+
+
+ {hasNextPage ? (
+
+
+
+
+
+
+
+
+ ) : (
+
+
+
+
+
+ )}
+
+
+ );
+};
+
+export default Home;
+
+export const getStaticPaths = (async () => {
+ const { address, abi } = getContractInfo();
+ const provider = new JsonRpcProvider(
+ "https://andromeda.metis.io/?owner=1088"
+ );
+ const contract: Contract = new Contract(address, abi, provider);
+ const totalSupply: number = Number(await contract.totalSupply());
+
+ const pages: number = Math.floor(totalSupply / 24);
+
+ const paths = Array.from({ length: pages }, (_, i) => ({
+ params: { page: String(pages - i) },
+ }));
+
+ return {
+ paths: paths,
+ fallback: "blocking",
+ };
+}) satisfies GetStaticPaths;
+
+export const getStaticProps = async (context: { params: { page: string } }) => {
+ const page: number = Number(context.params.page);
+
+ await new Promise((resolve) => setTimeout(resolve, 100 * page));
+
+ const { address, abi } = getContractInfo();
+
+ let providerString;
+ if (page % 2 === 0) {
+ providerString = process.env.NEXT_PUBLIC_ALCHEMY_METIS_FIRST;
+ } else {
+ providerString = process.env.NEXT_PUBLIC_ALCHEMY_METIS_SECOND;
+ }
+ const provider = new JsonRpcProvider(providerString);
+
+ const contract: Contract = new Contract(address, abi, provider);
+
+ const totalSupply: number = Number(await contract.totalSupply());
+ const posts: PostReduced[] = [];
+
+ const indexStart = page * 24 + 1;
+ const indexEndNominal = indexStart + 23;
+
+ let hasNextPage = true;
+
+ let indexEnd = indexEndNominal;
+ if (indexEndNominal >= totalSupply) {
+ indexEnd = totalSupply;
+ hasNextPage = false;
+ }
+
+ for (let i = indexStart; i <= indexEnd; i++) {
+ const result: string = await contract.getPostCid(i);
+ const post = await fetchDecodedPost(result, 150);
+ await new Promise((resolve) => setTimeout(resolve, 100));
+ posts.push({ image: post.image, name: post.name, tokenId: i });
+ }
+ return {
+ props: {
+ posts,
+ hasNextPage,
+ page,
+ },
+ revalidate: 60,
+ };
+};
diff --git a/packages/frontend/pages/post/[id].tsx b/packages/frontend/pages/post/[id].tsx
index dcd1fbc..74989a9 100644
--- a/packages/frontend/pages/post/[id].tsx
+++ b/packages/frontend/pages/post/[id].tsx
@@ -1,4 +1,5 @@
-import { ActionIcon, SimpleGrid, LoadingOverlay } from "@mantine/core";
+import type { NextPage } from "next";
+import { ActionIcon, SimpleGrid } from "@mantine/core";
import { NextRouter, useRouter } from "next/router";
import { ArrowLeft } from "tabler-icons-react";
@@ -7,19 +8,32 @@ import DisplayMedia from "@/components/Post/DisplayMedia";
import { PageSEO } from "@/components/SEO";
import { usePost } from "@/hooks/api";
-const PostPage = () => {
+type Post = {
+ tokenId: number;
+ owner: string;
+ author: string;
+ cid: string;
+};
+
+interface Props {
+ post: Post;
+}
+
+const PostPage: NextPage = () => {
const router: NextRouter = useRouter();
- const postId: string = String(router.query.id);
- const tag: string = `metis: ${postId}`;
- const { data: postQueried, isLoading } = usePost(postId);
+ const postId: number = Number(router.query.id);
+
+ const tag: string = `Metis: ${postId}`;
+
+ const { data, isFetched } = usePost(postId, !!postId);
+
return (
-
- {postQueried && (
+ {isFetched && (
router.back()}
@@ -37,8 +51,8 @@ const PostPage = () => {
{ maxWidth: "md", cols: 1, spacing: "md" },
]}
>
-
-
+
+
)}
diff --git a/packages/frontend/pages/profile/[address].tsx b/packages/frontend/pages/profile/[address].tsx
index 1831323..3b1776b 100644
--- a/packages/frontend/pages/profile/[address].tsx
+++ b/packages/frontend/pages/profile/[address].tsx
@@ -24,7 +24,7 @@ function Post() {
const config = createConfig({
chains: [mainnet],
transports: {
- [mainnet.id]: http(process.env.NEXT_PUBLIC_ALCHEMY),
+ [mainnet.id]: http(process.env.NEXT_PUBLIC_ALCHEMY_MAINNET),
},
});
@@ -43,8 +43,8 @@ function Post() {
return (
diff --git a/packages/frontend/pages/upload.tsx b/packages/frontend/pages/upload.tsx
index b67c6f8..d88e356 100644
--- a/packages/frontend/pages/upload.tsx
+++ b/packages/frontend/pages/upload.tsx
@@ -7,8 +7,8 @@ const Upload: NextPage = () => {
return (
diff --git a/packages/frontend/services/fetchCid.ts b/packages/frontend/services/fetchCid.ts
index 60e7cd7..c8288d6 100644
--- a/packages/frontend/services/fetchCid.ts
+++ b/packages/frontend/services/fetchCid.ts
@@ -1,7 +1,7 @@
-import { parseCid } from "@/services/parseCid";
-
import { PinataSDK } from "pinata";
+import { parseCid } from "@/services/parseCid";
+
export type ObjectJsonMetadata = {
name: string;
description: string;
@@ -29,27 +29,41 @@ export async function fetchMetadataPinata(cidMetadata: string) {
return pinataObject.data as ObjectJsonMetadata;
}
-export async function fetchImagePinata(cidImage: string) {
+export async function fetchImagePinata(
+ cidImage: string,
+ imageResolution: number
+) {
const cid = parseCid(cidImage);
const pinata = new PinataSDK({
pinataJwt: process.env.NEXT_PUBLIC_PINATA_JWT,
pinataGateway: process.env.NEXT_PUBLIC_GATEWAY_URL,
});
- const url = await pinata.gateways.createSignedURL({
- cid: cid,
- expires: 1800,
- });
+ const url = await pinata.gateways
+ .createSignedURL({
+ cid: cid,
+ expires: 864000,
+ })
+ .optimizeImage({
+ width: imageResolution,
+ height: imageResolution,
+ format: "webp",
+ });
+
return url;
}
-export async function fetchDecodedPost(cidMetadata: string) {
+export async function fetchDecodedPost(
+ cidMetadata: string,
+ imageResolution: number
+) {
try {
const objectJsonMetadata: ObjectJsonMetadata =
await fetchMetadataPinata(cidMetadata);
try {
const decodedImage: string = await fetchImagePinata(
- objectJsonMetadata.image
+ objectJsonMetadata.image,
+ imageResolution
);
const output = {
...objectJsonMetadata,
diff --git a/packages/frontend/tsconfig.json b/packages/frontend/tsconfig.json
index fc0347b..2e5544d 100644
--- a/packages/frontend/tsconfig.json
+++ b/packages/frontend/tsconfig.json
@@ -17,6 +17,7 @@
"baseUrl": ".",
"paths": {
"@/components/*": ["components/*"],
+ "@/context/*": ["context/*"],
"@/contracts/*": ["contracts/*"],
"@/utils/*": ["utils/*"],
"@/services/*": ["services/*"],
diff --git a/packages/frontend/vercel.json b/packages/frontend/vercel.json
new file mode 100644
index 0000000..23ad23f
--- /dev/null
+++ b/packages/frontend/vercel.json
@@ -0,0 +1,10 @@
+{
+ "functions": {
+ "pages/api/metis/**/*": {
+ "maxDuration": 60
+ },
+ "pages/**/*": {
+ "maxDuration": 60
+ }
+ }
+}
diff --git a/packages/frontend/yarn.lock b/packages/frontend/yarn.lock
index a61af58..019f231 100644
--- a/packages/frontend/yarn.lock
+++ b/packages/frontend/yarn.lock
@@ -3412,7 +3412,7 @@ deep-object-diff@^1.1.9:
resolved "https://registry.yarnpkg.com/deep-object-diff/-/deep-object-diff-1.1.9.tgz#6df7ef035ad6a0caa44479c536ed7b02570f4595"
integrity sha512-Rn+RuwkmkDwCi2/oXOFS9Gsr5lJZu/yTGpK7wAaAIE75CC+LCGEZHpY6VQJa/RoJcrmaA/docWJZvYohlNkWPA==
-deepmerge@^4.0.0, deepmerge@^4.2.2:
+deepmerge@^4.2.2:
version "4.3.1"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a"
integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==
@@ -5331,11 +5331,6 @@ lit@2.8.0:
lit-element "^3.3.0"
lit-html "^2.8.0"
-load-script@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/load-script/-/load-script-1.0.0.tgz#0491939e0bee5643ee494a7e3da3d2bac70c6ca4"
- integrity sha512-kPEjMFtZvwL9TaZo0uZ2ml+Ye9HUMmPwbYRJ324qF9tqMejwykJ5ggTyvzmrbBeapCAbk98BSbTeovHEEP1uCA==
-
load-tsconfig@^0.2.3:
version "0.2.5"
resolved "https://registry.yarnpkg.com/load-tsconfig/-/load-tsconfig-0.2.5.tgz#453b8cd8961bfb912dea77eb6c168fe8cca3d3a1"
@@ -5402,11 +5397,6 @@ media-query-parser@^2.0.2:
dependencies:
"@babel/runtime" "^7.12.5"
-memoize-one@^5.1.1:
- version "5.2.1"
- resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e"
- integrity sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==
-
merge-stream@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
@@ -6074,7 +6064,7 @@ progress@2.0.3:
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
-prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1:
+prop-types@^15.6.2, prop-types@^15.8.1:
version "15.8.1"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
@@ -6182,17 +6172,6 @@ react-is@^16.13.1, react-is@^16.7.0:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
-react-player@^2.11.2:
- version "2.16.0"
- resolved "https://registry.yarnpkg.com/react-player/-/react-player-2.16.0.tgz#89070700b03f5a5ded9f0b3165d4717390796481"
- integrity sha512-mAIPHfioD7yxO0GNYVFD1303QFtI3lyyQZLY229UEAp/a10cSW+hPcakg0Keq8uWJxT2OiT/4Gt+Lc9bD6bJmQ==
- dependencies:
- deepmerge "^4.0.0"
- load-script "^1.0.0"
- memoize-one "^5.1.1"
- prop-types "^15.7.2"
- react-fast-compare "^3.0.1"
-
react-popper@^2.2.5:
version "2.3.0"
resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-2.3.0.tgz#17891c620e1320dce318bad9fede46a5f71c70ba"
diff --git a/packages/hardhat/hardhat.config.js b/packages/hardhat/hardhat.config.js
index 8cd36e2..eb06c86 100644
--- a/packages/hardhat/hardhat.config.js
+++ b/packages/hardhat/hardhat.config.js
@@ -29,53 +29,53 @@ module.exports = {
networks: {
mumbai: {
url: `https://polygon-mumbai.g.alchemy.com/v2/${process.env.ALCHEMY_API_KEY}`,
- accounts: [process.env.PRIVATE_KEY, process.env.PRIVATE_KEY_2],
+ accounts: [process.env.PRIVATE_KEY],
chainId: 80001,
gas: 8000000,
gasPrice: 1000000000,
},
l16: {
url: "https://rpc.l16.lukso.network",
- accounts: [process.env.PRIVATE_KEY, process.env.PRIVATE_KEY_2],
+ accounts: [process.env.PRIVATE_KEY],
chainId: 2828,
gas: 500000000000000,
gasPrice: 300000000000,
},
l14: {
url: "https://rpc.l14.lukso.network",
- accounts: [process.env.PRIVATE_KEY, process.env.PRIVATE_KEY_2],
+ accounts: [process.env.PRIVATE_KEY],
chainId: 22,
gas: 50000000,
gasPrice: 1000000000000,
},
evmos: {
url: "https://eth.bd.evmos.dev:8545",
- accounts: [process.env.PRIVATE_KEY, process.env.PRIVATE_KEY_2],
+ accounts: [process.env.PRIVATE_KEY],
chainId: 9000,
},
fantom: {
url: "https://rpc.ankr.com/fantom/",
- accounts: [process.env.PRIVATE_KEY, process.env.PRIVATE_KEY_2],
+ accounts: [process.env.PRIVATE_KEY],
chainId: 250,
},
bsc: {
url: "https://bscrpc.com",
- accounts: [process.env.PRIVATE_KEY, process.env.PRIVATE_KEY_2],
+ accounts: [process.env.PRIVATE_KEY],
chainId: 56,
},
metis: {
url: "https://andromeda.metis.io",
- accounts: [process.env.PRIVATE_KEY, process.env.PRIVATE_KEY_2],
+ accounts: [process.env.PRIVATE_KEY],
chainId: 1088,
},
mantle: {
url: "https://rpc.testnet.mantle.xyz/",
- accounts: [process.env.PRIVATE_KEY, process.env.PRIVATE_KEY_2],
+ accounts: [process.env.PRIVATE_KEY],
chainId: 5001,
},
filecoin: {
url: "https://rpc.ankr.com/filecoin",
- accounts: [process.env.PRIVATE_KEY, process.env.PRIVATE_KEY_2],
+ accounts: [process.env.PRIVATE_KEY],
chainId: 314,
},
optimism: {
diff --git a/packages/hardhat/package.json b/packages/hardhat/package.json
index e0476d9..235818b 100644
--- a/packages/hardhat/package.json
+++ b/packages/hardhat/package.json
@@ -30,6 +30,7 @@
"eslint-plugin-babel": "^5.3.1",
"eslint-plugin-prettier": "^5.0.0",
"hardhat-gas-reporter": "^2.2.2",
+ "hardhat-verify": "^1.0.0",
"solidity-coverage": "^0.8.14",
"ts-node": "^10.9.1",
"typechain": "^8.1.1"
diff --git a/packages/hardhat/yarn.lock b/packages/hardhat/yarn.lock
index fdf7001..97bd01d 100644
--- a/packages/hardhat/yarn.lock
+++ b/packages/hardhat/yarn.lock
@@ -3574,6 +3574,11 @@ hardhat-gas-reporter@^2.2.2:
sha1 "^1.1.1"
viem "2.7.14"
+hardhat-verify@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/hardhat-verify/-/hardhat-verify-1.0.0.tgz#13c8e4e015cfb64a2637a7de5c9fa108023b3e2b"
+ integrity sha512-mLCAVyXTz3M9mDDv8qDNsEQfiopURX/eAwpS6jXyqeCUUtgr1eeYfrzigeT/Hut6RO8d1CmUuW9BHvG8cEL1fg==
+
hardhat@2.22.17:
version "2.22.17"
resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.22.17.tgz#96036bbe6bad8eb6a6b65c54dc5fbc1324541612"