-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #55 from penumbra-zone/minimal-ibc-channel
Basic page and API endpoint for querying a specific IBC Channel
- Loading branch information
Showing
2 changed files
with
198 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
import { type NextRequest } from "next/server"; | ||
import db from "@/lib/db"; | ||
|
||
export async function GET(req: NextRequest) { | ||
console.log("SUCCESS: GET /api/ibc/channel"); | ||
try { | ||
const url = new URL(req.url); | ||
const channelIdParam = url.searchParams.get("q")?.trim() ?? ""; | ||
if (channelIdParam === "") { | ||
throw new Error("No channel id provided."); | ||
} | ||
console.log(`Querying indexer for IBC Channel with id ${channelIdParam}.`); | ||
const connectionId = await db.events.findFirstOrThrow({ | ||
select: { | ||
attributes: { | ||
select: { | ||
key: true, | ||
value: true, | ||
}, | ||
where: { | ||
key: { | ||
equals: "connection_id", | ||
}, | ||
}, | ||
}, | ||
}, | ||
where: { | ||
AND: [ | ||
{ | ||
type: { | ||
equals: "channel_open_init", | ||
}, | ||
}, | ||
{ | ||
attributes: { | ||
some: { | ||
value: { | ||
equals: channelIdParam, | ||
}, | ||
}, | ||
}, | ||
}, | ||
], | ||
}, | ||
}); | ||
|
||
console.log("Successfully queried connection_id for channel.", connectionId); | ||
|
||
const clientId = await db.events.findFirstOrThrow({ | ||
select: { | ||
attributes: { | ||
select: { | ||
key: true, | ||
value: true, | ||
}, | ||
where: { | ||
key: { | ||
equals: "client_id", | ||
}, | ||
}, | ||
}, | ||
}, | ||
where: { | ||
AND: [ | ||
{ | ||
type: { | ||
equals: "connection_open_init", | ||
}, | ||
}, | ||
{ | ||
attributes: { | ||
some: { | ||
value: { | ||
equals: connectionId.attributes[0].value, | ||
}, | ||
}, | ||
}, | ||
}, | ||
], | ||
}, | ||
}); | ||
|
||
console.log("Successfully queried client_id for channel.", clientId); | ||
|
||
|
||
// This will return the block id for the latest client_update event type for a given client_id. | ||
const transactions = await db.events.findMany({ | ||
select: { | ||
tx_results: { | ||
select: { | ||
tx_hash: true, | ||
}, | ||
}, | ||
}, | ||
where: { | ||
attributes: { | ||
some: { | ||
value: { | ||
equals: channelIdParam, | ||
}, | ||
}, | ||
}, | ||
}, | ||
orderBy: { | ||
block_id: "desc", | ||
}, | ||
take: 10, | ||
}); | ||
|
||
const recentTransactions = transactions.map(({ tx_results: txResults }) => ({ "hash": txResults?.tx_hash })); | ||
|
||
console.log("Successfully queried recent transactions for channel.", recentTransactions); | ||
|
||
return new Response(JSON.stringify({ "connectionId": connectionId.attributes[0].value, "clientId": clientId.attributes[0].value, recentTransactions})); | ||
|
||
} catch (error) { | ||
console.error("GET request failed.", error); | ||
return new Response("Could not query IBC Channel.", { status: 500 }); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
"use client"; | ||
|
||
import { type FC } from "react"; | ||
import axios from "axios"; | ||
import { useQuery } from "@tanstack/react-query"; | ||
import Link from "next/link"; | ||
|
||
interface PageProps { | ||
params: { | ||
id: string, | ||
} | ||
} | ||
|
||
const Page : FC<PageProps> = ({ params }) => { | ||
const { id: channelId } = params; | ||
const { data, isFetched, isError } = useQuery({ | ||
queryFn: async () => { | ||
console.log(`Fetching: GET /api/ibc/channel/${channelId}`); | ||
const { data } = await axios.get(`/api/ibc/channel?q=${channelId}`); | ||
console.log("Fetched result:", data); | ||
return data as { connectionId: string, clientId: string, recentTransactions: Array<{hash: string}>}; | ||
// TODO: enforce validation | ||
// const result = IbcChannelValidator.safeParse(data); | ||
}, | ||
queryKey: ["IbcChannel", channelId], | ||
retry: false, | ||
meta: { | ||
errorMessage: "Failed to query IBC Channel by id. Please try again.", | ||
}, | ||
}); | ||
|
||
if (isError) { | ||
return ( | ||
<div className="py-5 flex justify-center"> | ||
<h1 className="text-4xl font-semibold">No results found.</h1> | ||
</div> | ||
); | ||
} | ||
|
||
return ( | ||
<div> | ||
{isFetched ? ( | ||
<div className="flex flex-col justify-center w-full"> | ||
<h1 className="text-3xl mx-auto py-5 font-semibold">IBC Channel</h1> | ||
{// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions | ||
data ? ( | ||
<div className="bg-white rounded-sm flex flex-wrap justify-between p-5 max-w-5xl ml-auto mr-auto gap-2"> | ||
<div className="flex justify-start w-full"> | ||
<p className="w-1/6">Channel ID</p> | ||
<pre>{channelId}</pre> | ||
</div> | ||
<div className="flex justify-start w-full"> | ||
<p className="w-1/6">Client ID</p> | ||
<Link href={`/ibc/client/${data.clientId}`} className="underline"><pre>{data.clientId}</pre></Link> | ||
</div> | ||
<div className="flex justify-start w-full"> | ||
<p className="w-1/6">Connection IDs</p> | ||
<Link href={`/ibc/connection/${data.connectionId}`} className="underline"><pre>{data.connectionId}</pre></Link> | ||
</div> | ||
<div className="flex justify-start w-full"> | ||
<p className="w-1/6">Recent Transactions</p> | ||
<div className="overflow-hidden"> | ||
{data.recentTransactions.map(({ hash }, i) => <Link href={`/transaction/${hash}`} key={i} className="underline"><pre>{hash}</pre></Link>)} | ||
</div> | ||
</div> | ||
</div> | ||
) : ( | ||
<p>No results</p> | ||
)} | ||
</div> | ||
) : ( | ||
<p>loading...</p> | ||
)} | ||
</div> | ||
); | ||
}; | ||
|
||
export default Page; |