Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "packages/contracts/lib/ERC1155-puppet"]
path = packages/contracts/lib/ERC1155-puppet
url = https://github.com/MrDeadCe11/ERC1155-puppet
7 changes: 5 additions & 2 deletions mprocs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ procs:
client:
cwd: packages/client
shell: pnpm run dev
api:
cwd: packages/api
shell: pnpm run dev
contracts:
cwd: packages/contracts
shell: pnpm mud dev-contracts --rpc http://127.0.0.1:8545
shell: pnpm run dev
anvil:
cwd: packages/contracts
shell: anvil --base-fee 0 --block-time 2
shell: anvil --base-fee 0 --block-time 1
1 change: 1 addition & 0 deletions packages/api/.env.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
PINATA_JWT=
5 changes: 5 additions & 0 deletions packages/api/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.vercel

# local env files
.env
.env*.local
66 changes: 66 additions & 0 deletions packages/api/api/uploadFile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import formidable from "formidable";
import fs from "fs";
import Jimp from "jimp";
import { VercelRequest, VercelResponse } from "@vercel/node";

import { uploadToPinata } from "../lib/fileStorage";

type FormFile = {
_writeStream: {
path: string;
};
};

export default async function uploadFile(
req: VercelRequest,
res: VercelResponse
) {
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Methods", "POST, OPTIONS");

if (!(req.method === "POST" || req.method == "OPTIONS")) {
return res.status(405).json({ error: "Method not allowed" });
}

const fileName = req.query.name as string;
const form = formidable({});

let isSvg = false;
form.on("file", (_, file) => {
const mimeType = file.mimetype;

if (mimeType === "image/svg+xml") {
isSvg = true;
}
});

try {
const [, files] = await form.parse(req);
const formFile = files[fileName] as [FormFile] | undefined;

if (!formFile) {
return res.status(400).json({ error: "No file provided" });
}

let fileContents: Buffer;

if (isSvg) {
fileContents = fs.readFileSync(formFile[0]._writeStream.path);
} else {
const image = await Jimp.read(formFile[0]._writeStream.path);
fileContents = await image
.resize(700, Jimp.AUTO)
.getBufferAsync(Jimp.MIME_PNG);
}

const cid = await uploadToPinata(fileContents, `${fileName}.png`);
if (!cid) {
return res.status(500).json({ error: "Error uploading file" });
}

return res.status(200).json({ cid });
} catch (error) {
console.error(error);
return res.status(500).json({ error: "Something went wrong" });
}
}
32 changes: 32 additions & 0 deletions packages/api/api/uploadMetadata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { VercelRequest, VercelResponse } from "@vercel/node";

import { uploadToPinata } from "../lib/fileStorage";

const uploadMetadata = async (req: VercelRequest, res: VercelResponse) => {
try {
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Methods", "POST, OPTIONS");
res.setHeader("Access-Control-Allow-Headers", "Content-Type");

if (!(req.method === "POST" || req.method == "OPTIONS")) {
return res.status(405).json({ error: "Method not allowed" });
}

const fileName = req.query.name as string;
const metadata = req.body;

console.log(metadata);

const fileContents = Buffer.from(JSON.stringify(metadata));
const cid = await uploadToPinata(fileContents, fileName);
if (!cid) {
return res.status(500).json({ error: "Error uploading file" });
}
return res.status(200).json({ cid });
} catch (error) {
console.error(error);
return res.status(500).json({ error: "Something went wrong" });
}
};

export default uploadMetadata;
42 changes: 42 additions & 0 deletions packages/api/lib/fileStorage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import pinataSDK, { PinataPinOptions } from "@pinata/sdk";
import { Readable } from "stream";

const PINATA_JWT = process.env.PINATA_JWT;

if (!PINATA_JWT) {
throw new Error(`Invalid/Missing environment variable: "PINATA_JWT"`);
}

const bufferToStream = (buffer: Buffer) => {
const readable = new Readable();
readable._read = () => {};
readable.push(buffer);
readable.push(null);
return readable;
};

export const uploadToPinata = async (
file: Buffer,
fileName: string
): Promise<string> => {
try {
const pinata = new pinataSDK({ pinataJWTKey: PINATA_JWT });
const readableStreamForFile = bufferToStream(file);
const options: PinataPinOptions = {
pinataMetadata: {
name: fileName,
},
};

const response = await pinata.pinFileToIPFS(readableStreamForFile, options);
const { IpfsHash } = response;
if (!IpfsHash) {
throw new Error("Error pinning file to IPFS");
}

return IpfsHash;
} catch (error) {
console.error(error);
return "";
}
};
34 changes: 34 additions & 0 deletions packages/api/local.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
if (process.env.NODE_ENV !== "production") {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const express = require("express");
const app = express();
// eslint-disable-next-line @typescript-eslint/no-var-requires
// const bodyParser = require("body-parser");
const port = 8080;

app.use(express.json());

// eslint-disable-next-line @typescript-eslint/no-explicit-any
app.use("/api/hello", (req: any, res: any) => {
// eslint-disable-next-line @typescript-eslint/no-var-requires
require("./api/hello").default(req, res);
});

// eslint-disable-next-line @typescript-eslint/no-explicit-any
app.use("/api/uploadFile", (req: any, res: any) => {
// eslint-disable-next-line @typescript-eslint/no-var-requires
require("./api/uploadFile").default(req, res);
});

// eslint-disable-next-line @typescript-eslint/no-explicit-any
app.use("/api/uploadMetadata", (req: any, res: any) => {
// eslint-disable-next-line @typescript-eslint/no-var-requires
require("./api/uploadMetadata").default(req, res);
});

app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});
} else {
throw new Error("local.js should not be used in production");
}
29 changes: 29 additions & 0 deletions packages/api/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"name": "api",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "NODE_ENV=development nodemon --watch api --ext ts --exec 'ts-node -r dotenv/config local.ts'"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@pinata/sdk": "^2.1.0",
"@vercel/node": "^3.1.7",
"dotenv": "^16.4.5",
"express": "^4.19.2",
"formidable": "^3.5.1",
"jimp": "^0.22.12",
"vercel": "^34.2.7"
},
"devDependencies": {
"@types/express": "^4.17.21",
"@types/formidable": "^3.4.5",
"@types/node": "^18.19.33",
"nodemon": "^3.1.4",
"ts-node": "^10.9.2",
"typescript": "5.3.3"
}
}
11 changes: 11 additions & 0 deletions packages/api/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"outDir": "./dist"
},
"include": ["api/**/*.ts"]
}
13 changes: 13 additions & 0 deletions packages/api/vercel.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"functions": {
"api/**/*.ts": {
"runtime": "@vercel/[email protected]"
}
},
"routes": [
{
"src": "/api/(.*)",
"dest": "/api/$1"
}
]
}
1 change: 1 addition & 0 deletions packages/client/.env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ VITE_WALLET_CONNECT_PROJECT_ID=
VITE_CHAIN_ID=31337
VITE_HTTPS_RPC_URL=
VITE_WS_RPC_URL=
VITE_API_URL=
1 change: 1 addition & 0 deletions packages/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"@types/react-router-dom": "^5.3.3",
"contracts": "workspace:*",
"framer-motion": "^11.2.6",
"next": "^14.2.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-icons": "^5.2.1",
Expand Down
17 changes: 1 addition & 16 deletions packages/client/src/components/ConnectWalletModal.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {
Button,
Modal,
ModalBody,
ModalCloseButton,
Expand All @@ -12,7 +11,6 @@ import {
import { useMemo } from 'react';
import { useAccount, useWalletClient } from 'wagmi';

import { useMUD } from '../contexts/MUDContext';
import { ConnectWalletButton } from './ConnectWalletButton';
import { DelegationButton } from './DelegationButton';

Expand All @@ -25,21 +23,8 @@ export const ConnectWalletModal = ({
}): JSX.Element => {
const { data: externalWalletClient } = useWalletClient();
const { isConnected, address } = useAccount();
const { delegatorAddress } = useMUD();

const bodyContent = useMemo(() => {
if (externalWalletClient && delegatorAddress) {
return (
<VStack p={4} spacing={10}>
<Button onClick={onClose}>Continue</Button>
<DelegationButton
externalWalletClient={externalWalletClient}
onClose={onClose}
/>
</VStack>
);
}

if (address && externalWalletClient && isConnected) {
return (
<VStack p={4} spacing={10}>
Expand Down Expand Up @@ -78,7 +63,7 @@ export const ConnectWalletModal = ({
<ConnectWalletButton />
</VStack>
);
}, [address, externalWalletClient, delegatorAddress, isConnected, onClose]);
}, [address, externalWalletClient, isConnected, onClose]);

return (
<Modal isOpen={isOpen} onClose={onClose}>
Expand Down
4 changes: 3 additions & 1 deletion packages/client/src/components/DelegationButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const DelegationButton = ({
const navigate = useNavigate();
const { chains, switchChain } = useSwitchChain();
const { chainId } = useAccount();
const { burnerAddress, network } = useMUD();
const { burnerAddress, getBurner, network } = useMUD();
const { renderError, renderSuccess } = useToast();

const [isDelegating, setIsDelegating] = useState(false);
Expand All @@ -43,6 +43,7 @@ export const DelegationButton = ({
onClose();
}

getBurner();
navigate('/character-creation');
} catch (error) {
renderError(error, 'Failed to delegate');
Expand All @@ -52,6 +53,7 @@ export const DelegationButton = ({
}, [
burnerAddress,
externalWalletClient,
getBurner,
navigate,
network,
onClose,
Expand Down
Loading