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
5 changes: 5 additions & 0 deletions .changeset/hungry-kings-applaud.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@3loop/transaction-decoder": minor
---

Change interpretation from jsonata to js code using QuickJS
7 changes: 2 additions & 5 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,30 +11,27 @@
},
"dependencies": {
"@3loop/transaction-decoder": "workspace:*",
"@monaco-editor/react": "^4.6.0",
"@prisma/client": "^5.2.0",
"@radix-ui/react-dialog": "^1.0.4",
"@radix-ui/react-hover-card": "^1.0.6",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-popover": "^1.0.6",
"@radix-ui/react-select": "^2.0.0",
"@radix-ui/react-separator": "^1.0.3",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-tabs": "^1.0.4",
"@tanstack/react-query": "^4.33.0",
"@types/node": "^20.3.1",
"@types/react": "18.2.21",
"@types/react-dom": "18.2.7",
"@vercel/analytics": "^1.0.2",
"autoprefixer": "10.4.15",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"cmdk": "^0.2.0",
"effect": "^2.0.3",
"eslint": "^8.47.0",
"eslint-config-next": "13.4.19",
"jsonata": "^2.0.3",
"lucide-react": "^0.274.0",
"monaco-editor": "^0.47.0",
"next": "13.4.19",
"postcss": "8.4.29",
"react": "18.2.0",
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/app/contract/[contract]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export default async function Home({

return (
<div>
<div className="py-4 grid w-full max-w-sm items-center gap-1.5">
<div className="py-4 grid w-full max-w-sm items-center gap-1.5 overflow-y-auto">
<Label htmlFor="contractAddress">Contract address</Label>
<Input
disabled
Expand Down
25 changes: 13 additions & 12 deletions apps/web/src/app/contract/[contract]/table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,18 @@ import {
} from "@/components/ui/table";
import React from "react";
import { DecodedTx, Interpreter } from "@3loop/transaction-decoder";
import { findAndRunInterpreter, defaultinterpreters } from "@/lib/interpreter";
import {
findAndRunInterpreter,
defaultInterpreters,
Interpretation,
} from "@/lib/interpreter";

function getAvaliableinterpreters() {
if (typeof window === "undefined") return undefined;

let res: Interpreter[] = [];

for (const interpreter of defaultinterpreters) {
for (const interpreter of defaultInterpreters) {
const stored = window.localStorage.getItem(interpreter.id);
if (stored) {
const updatedSchema = JSON.parse(stored);
Expand All @@ -35,28 +39,25 @@ function getAvaliableinterpreters() {
}

export default function TxTable({ txs }: { txs: DecodedTx[] }) {
const [result, setResult] = React.useState<
{
tx: DecodedTx;
interpretation: any;
}[]
>([]);
const [intepretors] = React.useState(getAvaliableinterpreters);
const [result, setResult] = React.useState<Interpretation[]>([]);
const [interpreters] = React.useState(getAvaliableinterpreters);

React.useEffect(() => {
async function run() {
if (intepretors == null) return;
if (interpreters == null) return;

const withIntepretations = await Promise.all(
txs.map((tx) => {
return findAndRunInterpreter(tx, intepretors);
return findAndRunInterpreter(tx, interpreters);
}),
);

console.log(withIntepretations[0]);

setResult(withIntepretations);
}
run();
}, [intepretors, txs]);
}, [interpreters, txs]);

return (
<Table>
Expand Down
20 changes: 6 additions & 14 deletions apps/web/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ import type { Metadata } from "next";
import { Inter } from "next/font/google";
import { MainNav } from "@/components/ui/main-nav";
import { Analytics } from "@vercel/analytics/react";
import { aaveV2, DEFAULT_CHAIN_ID } from "../app/data";
import { NetworkSelect } from "@/components/ui/network-select";
import { aaveV2 } from "../app/data";

const navLinks = [
{
Expand Down Expand Up @@ -35,9 +34,12 @@ export default function RootLayout({
return (
<html lang="en">
<body className={inter.className}>
<div className="flex h-full flex-col">
<div className="flex h-screen flex-col">
<div className="container flex justify-between py-4 flex-row items-center space-y-0 md:h-16">
<h2 className="text-lg font-semibold">Loop Decoder</h2>

<MainNav className="mx-6" navLinks={navLinks} />

<div className="flex flex-row space-x-4">
<a
target="_blank"
Expand All @@ -57,17 +59,7 @@ export default function RootLayout({
</div>
<Separator />

<div className="space-between flex items-center">
<div className="container flex flex-row py-4">
<MainNav className="mx-6" navLinks={navLinks} />

<div className="ml-auto mr-4">
<NetworkSelect defaultValue={DEFAULT_CHAIN_ID.toString()} />
</div>
</div>
</div>

<div className="container h-full py-6">{children}</div>
<div className="container h-full py-4">{children}</div>
</div>
<Analytics />
</body>
Expand Down
166 changes: 75 additions & 91 deletions apps/web/src/app/tx/[chainID]/[hash]/form.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,17 @@
"use client";
import * as React from "react";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import { DEFAULT_CHAIN_ID, transactions } from "../../../data";
import { useLocalStorage } from "usehooks-ts";
import { SidebarNav } from "@/components/ui/sidebar-nav";
import { QuestionMarkCircledIcon } from "@radix-ui/react-icons";
import {
HoverCard,
HoverCardTrigger,
HoverCardContent,
} from "@/components/ui/hover-card";
import { PlayIcon } from "@radix-ui/react-icons";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { useRouter } from "next/navigation";
import { DecodedTx, Interpreter } from "@3loop/transaction-decoder";
import { interpretTx } from "@/lib/interpreter";
import { Interpretation, interpretTx } from "@/lib/interpreter";
import CodeBlock from "@/components/ui/code-block";
import { NetworkSelect } from "@/components/ui/network-select";

export const sidebarNavItems = transactions.map((tx) => {
return {
Expand All @@ -37,7 +33,7 @@ export default function DecodingForm({
currentHash,
currentChainID,
}: FormProps) {
const [result, setResult] = React.useState<string>();
const [result, setResult] = React.useState<Interpretation>();
const [schema, setSchema] = useLocalStorage(
defaultInterpreter?.id ?? "unknown",
defaultInterpreter?.schema,
Expand All @@ -51,7 +47,7 @@ export default function DecodingForm({
router.push(`/tx/${currentChainID}/${hash}`);
};

React.useEffect(() => {
const onRun = React.useCallback(() => {
if (schema && defaultInterpreter != null && decoded != null) {
const newInterpreter = {
...defaultInterpreter,
Expand All @@ -64,90 +60,78 @@ export default function DecodingForm({
}
}, [schema, decoded, defaultInterpreter]);

// Run the interpreter on page load
React.useEffect(() => {
if (
schema &&
defaultInterpreter != null &&
decoded != null &&
result == null
) {
onRun();
}
}, [schema, decoded, defaultInterpreter, result, onRun]);

return (
<div className="grid h-full items-stretch gap-6 md:grid-cols-[1fr_200px]">
<div className="md:order-1">
<div className="flex-col space-y-4 flex">
<div className="flex flex-col space-y-4">
<div className="grid h-full gap-6 lg:grid-cols-2">
<div className="flex flex-col space-y-4">
<div className="grid w-full items-center gap-1.5">
<Label htmlFor="transactionHash">Transaction</Label>
<form onSubmit={onSubmit}>
<div className="flex w-full items-center space-x-2">
<Input
className="flex-1 flex"
id="hash"
name="hash"
placeholder={`Paste Ethereum transaction hash or click on examples`}
defaultValue={currentHash}
/>
<Button type="submit">Decode</Button>
</div>
</form>
</div>

<div className="flex flex-1 flex-col space-y-2 min-h-[250px]">
<Label htmlFor="input">Decoded transaction</Label>
<Textarea
id="decoding"
placeholder="The decoded transaction will appear here"
className="flex-1"
disabled
value={
decoded ? JSON.stringify(decoded, null, 2) : undefined
}
/>
</div>
<div className="flex flex-1 flex-col space-y-2 min-h-[250px]">
<div className="flex flex-row">
<Label htmlFor="intepretation">
Intepretation (
<a
href="https://docs.jsonata.org/overview.html"
target="_blank"
rel="noopener noreferrer"
className="text-blue-500 hover:underline"
>
JSONata
</a>{" "}
syntax)
</Label>
<HoverCard>
<HoverCardTrigger asChild>
<QuestionMarkCircledIcon />
</HoverCardTrigger>
<HoverCardContent className="w-80">
<div className="flex justify-between space-x-4">
<p className="text-base">
<a
href="https://docs.jsonata.org/overview.html"
target="_blank"
rel="noopener noreferrer"
className="text-blue-500 hover:underline"
>
JSONata
</a>
{` is a lightweight query and transformation language
for JSON data.`}
</p>
</div>
</HoverCardContent>
</HoverCard>
</div>
<Textarea
id="intepretation"
className="flex-1"
value={schema}
onChange={(e) => setSchema(e.target.value)}
placeholder="Write a custom intepretation here or select one from the list on the right."
/>
</div>
</div>
<div className="mt-[21px] min-h-[400px] rounded-md border bg-muted lg:min-h-[700px] overflow-scroll">
<pre className="p-4">{JSON.stringify(result, null, 2)}</pre>
</div>
<div className="md:order-1 flex flex-col space-y-4">
<form onSubmit={onSubmit}>
<div className="flex w-full items-center space-x-2">
<NetworkSelect defaultValue={DEFAULT_CHAIN_ID.toString()} />

<Input
className="flex-1 flex"
id="hash"
name="hash"
placeholder={`Paste Ethereum transaction hash or click on examples`}
defaultValue={currentHash}
/>
<Button type="submit">Decode</Button>
<Button variant={"outline"} onClick={onRun} type="button">
<PlayIcon className="mr-2 h-4 w-4" />
Interpret
</Button>
</div>
</form>

<div className="grid gap-6 lg:grid-cols-2 lg:grid-rows-2 h-full">
<div className="flex flex-col gap-2 col-span-2">
<Label>Interpretation:</Label>

<CodeBlock
language="javascript"
value={schema}
onChange={(value) => setSchema(value)}
lineNumbers={true}
readonly={false}
/>
</div>

<div className="flex flex-col gap-2 ">
<Label>Decoded transaction:</Label>
<CodeBlock
language="json"
value={JSON.stringify(decoded, null, 2)}
readonly={true}
lineNumbers={false}
/>
</div>

<div className="flex flex-col gap-2">
<div className="flex flex-row justify-between items-center">
<Label>Result:</Label>
</div>

<CodeBlock
language="json"
value={
result?.error
? result?.error
: JSON.stringify(result?.interpretation, null, 2)
}
readonly={true}
lineNumbers={false}
/>
</div>
</div>
</div>
Expand Down
Loading