diff --git a/src/App.tsx b/src/App.tsx
index df8f894..ddc8073 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -7,6 +7,7 @@ import { setupWebsockets } from './helpers/api';
import store from './store';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
import SingleBlock from './components/SingleBlock';
+import SearchKernel from './components/SearchKernel';
export default function App() {
useEffect(() => {
@@ -26,6 +27,7 @@ export default function App() {
+
diff --git a/src/components/SearchKernel.css b/src/components/SearchKernel.css
new file mode 100644
index 0000000..a52481d
--- /dev/null
+++ b/src/components/SearchKernel.css
@@ -0,0 +1,20 @@
+.SearchKernel {
+ padding: 2em;
+ min-height: 800px;
+}
+
+.SearchKernel h1 {
+ font-weight: bold;
+ font-family: Avenir, sans-serif;
+ font-size: 30px;
+}
+
+.SearchKernel h2 {
+ font-weight: bold;
+ font-family: Avenir, sans-serif;
+ font-size: 20px;
+}
+
+.SearchKernel a {
+ text-decoration: none;
+}
diff --git a/src/components/SearchKernel.tsx b/src/components/SearchKernel.tsx
new file mode 100644
index 0000000..4bc9271
--- /dev/null
+++ b/src/components/SearchKernel.tsx
@@ -0,0 +1,92 @@
+/* eslint-disable @typescript-eslint/camelcase */
+import React, { useEffect, useState } from 'react';
+import './SearchKernel.css';
+import { useParams, Link, Redirect } from 'react-router-dom';
+import { searchKernel } from '../helpers/api';
+import { ReactComponent as LoadingBars } from '../assets/bars.svg';
+
+interface Status {
+ status: string;
+ message: string;
+ hash?: string;
+}
+
+const renderStatus = (status: Status) => {
+ switch (status.status) {
+ case 'redirect':
+ return ;
+ case 'complete':
+ return (
+
+
+
{status.message}
+ Redirecting...
+
+ );
+ case 'loading':
+ return (
+
+
+
{status.message}
+
+ );
+ default:
+ return (
+
+ {status.message} Go Back
+
+ );
+ }
+};
+
+const SearchKernel = () => {
+ const { nonce, signature } = useParams();
+ const [status, setStatus] = useState({ status: 'loading', message: '', hash: '' } as Status);
+ useEffect(() => {
+ let timer;
+
+ if (!nonce || !signature) {
+ setStatus({
+ status: 'error',
+ message: 'Invalid nonce or signature'
+ });
+ return;
+ }
+ setStatus({
+ status: 'loading',
+ message: 'Searching for transaction...'
+ });
+ searchKernel(nonce, signature)
+ .then((response) => {
+ if (response.blocks.length > 0) {
+ const block = response.blocks[0];
+ const height = block.block.header.height;
+ const hash = block.block.header.hash;
+ const message = `Found in block #${height}.`;
+ setStatus({
+ status: 'complete',
+ message,
+ hash
+ });
+ timer = setTimeout(() => setStatus({ status: 'redirect', message, hash }), 2000);
+ } else {
+ setStatus({
+ status: 'error',
+ message: 'There was an error finding that transaction.'
+ });
+ }
+ })
+ .catch((e) => {
+ console.error(e);
+ setStatus({
+ status: 'error',
+ message: 'Transaction not found'
+ });
+ });
+ if (timer) return window.clearTimeout(timer);
+ }, [nonce, signature]);
+
+ return {renderStatus(status)}
;
+};
+
+export default SearchKernel;
diff --git a/src/helpers/api.ts b/src/helpers/api.ts
index bc4367d..ff6f29f 100644
--- a/src/helpers/api.ts
+++ b/src/helpers/api.ts
@@ -89,6 +89,15 @@ export async function fetchSingleBlock(blockId: string | number): Promise {
+ try {
+ const response = await fetch(`${apiUrl}/kernel/${publicNonce}/${signature}`);
+ return await response.json();
+ } catch (e) {
+ return e;
+ }
+}
+
export interface Constants {
coinbase_lock_height: number;
blockchain_version: number;