Skip to content

Commit

Permalink
Multirender Decoding (#69)
Browse files Browse the repository at this point in the history
* fix multirender api route, add decoding, update Token type

* remove multirender comment

* remove comment

* update local render, pages/index

* update local rendering to match multirender

* update

* add error handling back to api routes
  • Loading branch information
mshrieve authored Jul 20, 2022
1 parent 913c24b commit 2b1af9f
Show file tree
Hide file tree
Showing 12 changed files with 101 additions and 88 deletions.
2 changes: 1 addition & 1 deletion lib/forge-std
16 changes: 8 additions & 8 deletions packages/inspector/hooks/useLocalRPC.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
import { useState, useEffect } from "react";
import { ethers, Contract } from "ethers";

import { NFTStatus, trait } from "../types";
import { FetchStatus, Trait } from "../types";

import bibosABI from "../../../out/Bibos.sol/Bibos.json";
import deployments from "../../../deployment/deployments.json";

type NFTState = {
status: NFTStatus;
status: FetchStatus;
metadata: {
image: string;
attributes: trait[];
attributes: Trait[];
};
tokenId: number;
rawSvg: string;
};

const nftStateDefault: NFTState = {
status: NFTStatus.UNFETCHED,
status: FetchStatus.UNFETCHED,
metadata: {
image: "",
attributes: [],
Expand All @@ -30,7 +30,7 @@ const LOCAL_RPC_URI = "http://localhost:8545";
export const useLocalRPC = () => {
const [bibosContract, setBibosContract] = useState<Contract>(null);
const [tokenURI, setTokenURI] = useState<string>(null);
const [status, setStatus] = useState<NFTStatus>(NFTStatus.UNFETCHED);
const [status, setStatus] = useState<FetchStatus>(FetchStatus.UNFETCHED);

useEffect(() => {
const provider = new ethers.providers.JsonRpcProvider(LOCAL_RPC_URI);
Expand All @@ -47,9 +47,9 @@ export const useLocalRPC = () => {

const handleFetchNFT = async () => {
if (bibosContract == null) return;
if (status == NFTStatus.FETCHING) return;
if (status == FetchStatus.FETCHING) return;

setStatus(NFTStatus.FETCHING);
setStatus(FetchStatus.FETCHING);

const tx = await bibosContract.mint();
tx.wait();
Expand All @@ -58,7 +58,7 @@ export const useLocalRPC = () => {
const tokenURI = await bibosContract.tokenURI(totalSupply - 1);

setTokenURI(tokenURI);
setStatus(NFTStatus.FETCHED);
setStatus(FetchStatus.FETCHED);
};

return { tokenURI, status, handleFetchNFT };
Expand Down
36 changes: 23 additions & 13 deletions packages/inspector/hooks/useLocalRender.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,45 @@
import { useState, useEffect } from "react";
import { NFTStatus, trait } from "../types";
import { FetchStatus, Token } from "../types";
import { decodeTokenURI } from "../util";

// returns the entire tokenURI
const RENDER_ENDPOINT = "/api/render";

const defaultState = {
name: "",
image: "",
attributes: [],

tokenId: null,
svg: null,
};

export const useLocalRender = () => {
const [tokenURI, setTokenURI] = useState<string>(null);
const [status, setStatus] = useState<NFTStatus>(NFTStatus.UNFETCHED);
const [token, setToken] = useState<Token>(defaultState);
const [status, setStatus] = useState<FetchStatus>(FetchStatus.UNFETCHED);

const handleFetchNFT = async () => {
if (status == NFTStatus.FETCHING) return;
const handleLocalRender = async () => {
if (status == FetchStatus.FETCHING) return;

setStatus(NFTStatus.FETCHING);
setStatus(FetchStatus.FETCHING);

const response = await fetch(RENDER_ENDPOINT);

// handle fetch error
if (response.status != 200) {
console.log("fetch error: ", response.status);
return setStatus(NFTStatus.UNFETCHED);
return setStatus(FetchStatus.UNFETCHED);
}

const tokenURI = await response.text();
console.log("tokenURI", tokenURI);
setTokenURI(tokenURI);
setStatus(NFTStatus.FETCHED);
const tokenURI = await response.json();

setToken(decodeTokenURI(tokenURI));
setStatus(FetchStatus.FETCHED);
};

useEffect(() => {
handleFetchNFT();
handleLocalRender();
}, []);

return { tokenURI, status, handleFetchNFT };
return { token, status, handleLocalRender };
};
26 changes: 13 additions & 13 deletions packages/inspector/hooks/useMultiRender.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import { useState, useEffect } from "react";
import { NFTStatus } from "../types";
import { FetchStatus, Token } from "../types";
import { decodeTokenURI } from "../util";

const MULTI_RENDER_ENDPOINT = "/api/multirender";

export const useMultiRender = (quantity: number) => {
const [tokenURIs, setTokenURIs] = useState<string[]>(null);
const [status, setStatus] = useState<NFTStatus>(NFTStatus.UNFETCHED);
const [tokens, setTokens] = useState<Token[]>(null);
const [status, setStatus] = useState<FetchStatus>(FetchStatus.UNFETCHED);

const handleFetchNFT = async () => {
if (status == NFTStatus.FETCHING) return;
const handleMultiRender = async () => {
if (status == FetchStatus.FETCHING) return;

setStatus(NFTStatus.FETCHING);
setStatus(FetchStatus.FETCHING);

const response = await fetch(MULTI_RENDER_ENDPOINT, {
method: "POST",
Expand All @@ -23,18 +24,17 @@ export const useMultiRender = (quantity: number) => {
// handle fetch error
if (response.status != 200) {
console.log("fetch error: ", response.status);
return setStatus(NFTStatus.UNFETCHED);
return setStatus(FetchStatus.UNFETCHED);
}

const json = await response.json();

setTokenURIs(json);
setStatus(NFTStatus.FETCHED);
const tokenURIs = await response.json();
setTokens(tokenURIs.map((tokenURI) => decodeTokenURI(tokenURI)));
setStatus(FetchStatus.FETCHED);
};

useEffect(() => {
handleFetchNFT();
handleMultiRender();
}, []);

return { tokenURIs, status, handleFetchNFT };
return { tokens, status, handleMultiRender };
};
2 changes: 1 addition & 1 deletion packages/inspector/hooks/useNFT.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const useNFT = (tokenURI: string) => {
useEffect(() => {
if (tokenURI == null) return;
// decode and parse metadata

const metadata = JSON.parse(decodeBase64(tokenURI));

const tokenId = metadata.tokenId;
Expand Down
14 changes: 6 additions & 8 deletions packages/inspector/pages/api/multirender.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,14 @@ export default async (request: NextApiRequest, response: NextApiResponse) =>
const command = "cd ../.. && " + SCRIPT_PATH + ` ${quantity}`;
exec(command, (_: any, stdout: string, stderr: string) => {
if (stderr) {
// log the error to console
console.log("stderr: ", stderr);
// ignore errors until solmate updates their foundry.toml
// response.status(500);
// response.send(stderr);
// return resolve();

response.status(500);
response.send(stderr);
return resolve();
}
// console.log("synthesis_engine: synthesized bibos");
// console.log(stdout);
response.send(stdout);

response.json(JSON.parse(stdout));
resolve();
});
});
13 changes: 6 additions & 7 deletions packages/inspector/pages/api/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,14 @@ export default async (request: NextApiRequest, response: NextApiResponse) =>
new Promise<void>((resolve, _) =>
exec("cd ../.. && " + LOCAL_RENDER_SCRIPT_PATH, (_: any, stdout: string, stderr: string) => {
if (stderr) {
// log the error to console
console.log("stderr: ", stderr);
// ignore errors until solmate updates their foundry.toml
// response.status(500);
// response.send(stderr);
// return resolve();

response.status(500);
response.send(stderr);
return resolve();
}
console.log("synthesis_engine: synthesized bibos");
response.send(stdout);

response.json(JSON.parse(stdout));
resolve();
})
);
35 changes: 19 additions & 16 deletions packages/inspector/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@ import CodeMirror from "@uiw/react-codemirror";
import { syntaxHighlighting, bracketMatching, foldGutter, codeFolding } from "@codemirror/language";
import { highlightDark, themeDark } from "../components/CodeMirrorTheme";

import { NFTStatus, IndexView } from "../types";
import { FetchStatus } from "../types";
import { useLocalRender } from "../hooks/useLocalRender";
import { useMultiRender } from "../hooks/useMultiRender";
import { useNFT } from "../hooks/useNFT";

import { useFormattedSvg } from "../hooks/useFormattedSVG";
import { Container } from "../components/Container";
import { Pane } from "../components/Pane";
Expand All @@ -23,13 +22,13 @@ import { ToggleButton } from "../components/ToggleButton";
import { TraitsTable } from "../components/TraitsTable";

export default function Index() {
const { tokenURI, status, handleFetchNFT } = useLocalRender();
const { metadata, tokenId, svg } = useNFT(tokenURI);
const { token, status, handleLocalRender } = useLocalRender();

const [debug, setDebug] = useState(false);
const { formattedSvg, error } = useFormattedSvg(svg);
const { formattedSvg, error } = useFormattedSvg(token.svg);

const loc = formattedSvg.split(/\r\n|\r|\n/).length;
const kb = (new TextEncoder().encode(svg).length * 0.001).toFixed(3);
const kb = (new TextEncoder().encode(token.svg).length * 0.001).toFixed(3);

return (
<Container>
Expand Down Expand Up @@ -100,22 +99,26 @@ export default function Index() {
<Pane className="items-center h-full">
<div className="flex w-full h-full items-center justify-center p-8">
<SVGViewer
tokenId={tokenId}
svg={svg}
tokenId={token.tokenId}
svg={token.svg}
debug={debug}
isLoading={status == NFTStatus.FETCHING}
isLoading={status == FetchStatus.FETCHING}
/>
</div>

<TraitsTable
loading={status == NFTStatus.UNFETCHED}
tokenId={tokenId}
name={metadata.name}
attributes={metadata.attributes}
loading={status == FetchStatus.UNFETCHED}
tokenId={token.tokenId}
name={token.name}
attributes={token.attributes}
/>
<HorizontalRule />
<Toolbar>
<Button disabled={status == NFTStatus.FETCHING} primary onClick={() => handleFetchNFT()}>
<Button
disabled={status == FetchStatus.FETCHING}
primary
onClick={() => handleLocalRender()}
>
Render
</Button>
<ToggleButton isOn={debug} onClick={() => setDebug(!debug)}>
Expand Down
14 changes: 11 additions & 3 deletions packages/inspector/types/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ export enum IndexView {
SVG,
}

export enum NFTStatus {
export enum FetchStatus {
UNFETCHED,
FETCHING,
FETCHED,
ERROR,
}

export type trait = {
export type Trait = {
trait_type: string;
value: string;
};
Expand All @@ -19,12 +19,20 @@ export type NFTState = {
metadata: {
name: string;
image: string;
attributes: trait[];
attributes: Trait[];
};
tokenId: number;
svg: string;
};

export type Token = {
name: string;
tokenId: number;
image: string;
svg: string;
attributes: Trait[];
};

export type GenericComponentProps = {
children?: React.ReactNode;
className: string;
Expand Down
6 changes: 6 additions & 0 deletions packages/inspector/util/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ import FileSaver from "file-saver";
export const decodeBase64 = (encodedMetadata: string) =>
Buffer.from(stripMimeType(encodedMetadata), "base64").toString("ascii");

export const decodeTokenURI = (tokenURI: string) => {
const token = JSON.parse(decodeBase64(tokenURI));
token.svg = decodeBase64(token.image);
return token;
};

export const stripMimeType = (encoding: string) => encoding.substr(encoding.indexOf(",") + 1);

export function isOdd(num) {
Expand Down
24 changes: 7 additions & 17 deletions scripts/local_render.sh
Original file line number Diff line number Diff line change
@@ -1,26 +1,16 @@
#!/usr/bin/env bash
# Notice: run from root directory.

DEPLOYMENTS_PATH=./deployment/deployments.json

get_libraries() {
LIBRARIES=""
for NAME in "$@"
do
ADDRESS=$(jq -r '.'$NAME'' $DEPLOYMENTS_PATH)
LIBRARIES="$LIBRARIES --libraries src/libraries/$NAME.sol:$NAME:$ADDRESS"
done
echo $LIBRARIES
}

extract_token_URI() {
echo "$1" | grep "tokenURI: string" | cut -d " " -f 3 | tr -d '"'
extract_json_output() {
echo "$1" | grep "{"
}

LOCAL_RENDER_SCRIPT_PATH="src/scripts/local_render.sol"
SCRIPT_OUTPUT="$(forge script $LOCAL_RENDER_SCRIPT_PATH:local_render)"
# extract the bibos address return value from SCRIPT_OUTPUT
TOKEN_URI="$(extract_token_URI "$SCRIPT_OUTPUT")"


SCRIPT_OUTPUT="$(forge script local_render --json)"
JSON_OUTPUT="$(extract_json_output "$SCRIPT_OUTPUT")"

# return tokenURI
echo "$TOKEN_URI"
echo "$JSON_OUTPUT" | jq .returns.tokenURI.value
1 change: 0 additions & 1 deletion scripts/multi_render.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ extract_json_output() {

# LOCAL_RENDER_SCRIPT_PATH="src/scripts/local_render.sol"
SCRIPT_OUTPUT="$(forge script local_render --json -s "run(uint256)" $1)"

JSON_OUTPUT="$(extract_json_output "$SCRIPT_OUTPUT")"


Expand Down

0 comments on commit 2b1af9f

Please sign in to comment.