-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
295 additions
and
52 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 |
---|---|---|
@@ -1,13 +1,15 @@ | ||
<!doctype html> | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8" /> | ||
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
<title>Vite + React + TS</title> | ||
<title>Laos Signature Test</title> | ||
</head> | ||
<body> | ||
<div id="root"></div> | ||
<body | ||
class="m-0 flex min-h-screen min-w-[320px] place-items-center dark:bg-gray-900 dark:text-gray-100" | ||
> | ||
<div id="root" class="mx-auto max-w-screen-xl p-8"></div> | ||
<script type="module" src="/src/main.tsx"></script> | ||
</body> | ||
</html> |
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
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 |
---|---|---|
@@ -1,34 +1,137 @@ | ||
import { useState } from "react" | ||
import { ApiPromise, Keyring, WsProvider } from "@polkadot/api" | ||
import { Metadata } from "@polkadot/types" | ||
import { useCallback, useEffect, useState } from "react" | ||
|
||
const presetWsUrls = [ | ||
"wss://rpc.laos.laosfoundation.io", | ||
"wss://rpc.laosmercury.gorengine.com", | ||
"wss://wss.api.moonbeam.network", | ||
] | ||
import { presetWsUrls } from "./constants" | ||
import { SeedInput } from "./SeedInput" | ||
import { metadataFromOpaque } from "./util" | ||
import { WsSelect } from "./WsSelect" | ||
|
||
function App() { | ||
const [wsUrl, setWsUrl] = useState(presetWsUrls[0]) | ||
const keyring = new Keyring() | ||
|
||
const defaultSeed = "test test test test test test test test test test test junk" | ||
const defaultWsUrl = presetWsUrls[0] | ||
|
||
export const App = () => { | ||
const [seed, setSeed] = useState(defaultSeed) | ||
const [wsUrl, setWsUrl] = useState(defaultWsUrl) | ||
|
||
const [address, setAddress] = useState("") | ||
useEffect(() => { | ||
try { | ||
const keypair = keyring.createFromUri(`${seed}/m/44'/60'/0'/0/0`, {}, "ethereum") | ||
setAddress(keypair.address.toString()) | ||
} catch { | ||
setAddress("") | ||
} | ||
}, [seed]) | ||
|
||
const [balance, setBalance] = useState<string | null>(null) | ||
useEffect(() => { | ||
try { | ||
const keypair = keyring.createFromUri(`${seed}/m/44'/60'/0'/0/0`, {}, "ethereum") | ||
|
||
const abort = new AbortController() | ||
|
||
const api = new ApiPromise({ provider: new WsProvider(wsUrl) }) | ||
abort.signal.onabort = () => api.disconnect() | ||
|
||
setBalance("loading") | ||
|
||
// | ||
;(async () => { | ||
try { | ||
await api.isReadyOrError | ||
if (abort.signal.aborted) return | ||
|
||
const balance = await api.query.system.account(keypair.address) | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
setBalance((balance as any)?.data?.free?.toString?.() ?? "0") | ||
} catch { | ||
setBalance(null) | ||
} | ||
})() | ||
|
||
return () => abort.abort() | ||
} catch { | ||
setBalance(null) | ||
} | ||
}, [seed, wsUrl]) | ||
|
||
const [output, setOutput] = useState("") | ||
const addOutput = useCallback((data: string) => setOutput((o) => o + data + "\n"), []) | ||
|
||
const test = useCallback(() => { | ||
const keypair = keyring.createFromUri(`${seed}/m/44'/60'/0'/0/0`, {}, "ethereum") | ||
|
||
const abort = new AbortController() | ||
|
||
const api = new ApiPromise({ provider: new WsProvider(wsUrl) }) | ||
abort.signal.onabort = () => api.disconnect() | ||
|
||
addOutput("Creating extrinsic...") | ||
|
||
// | ||
;(async () => { | ||
try { | ||
await api.isReadyOrError | ||
if (abort.signal.aborted) return | ||
|
||
const opaqueMetadata = await api.call.metadata.metadataAtVersion(15) | ||
const metadataRpc = metadataFromOpaque(opaqueMetadata.toU8a()) | ||
// addOutput("metadataRpc: " + metadataRpc) | ||
|
||
const metadata = new Metadata(api.registry, metadataRpc) | ||
// addOutput("metadata: " + JSON.stringify(metadata.toJSON(), null, 2)) | ||
|
||
api.registry.setMetadata(metadata) | ||
|
||
const extrinsic = await api.tx.balances | ||
.transferKeepAlive(keypair.address, 0) | ||
.signAsync(keypair) | ||
|
||
addOutput( | ||
`Extrinsic signature: ${extrinsic.signature}\n(length ${extrinsic.signature.byteLength})` | ||
) | ||
} catch (cause) { | ||
addOutput(`Failed: ${String(cause)}`) | ||
} | ||
})() | ||
|
||
return () => abort.abort() | ||
}, [addOutput, seed, wsUrl]) | ||
|
||
return ( | ||
<> | ||
<label htmlFor="wsurl">ws url</label> | ||
<input | ||
id="wsurl" | ||
type="text" | ||
<div className="flex min-w-[640px] flex-col gap-4"> | ||
<h1 className="text-xl">Laos Signature Test</h1> | ||
<SeedInput | ||
value={seed} | ||
onClick={() => setSeed("")} | ||
onChange={(e) => setSeed(e.currentTarget.value)} | ||
onReset={() => setSeed(defaultSeed)} | ||
/> | ||
<div className="text-sm"> | ||
{address ? "Address: " : "Invalid seed or key"} | ||
{address} | ||
</div> | ||
<div className="-mt-4 text-sm"> | ||
{balance !== null ? "Balance: " : "No balance"} | ||
{balance} | ||
</div> | ||
<WsSelect | ||
value={wsUrl} | ||
onClick={() => setWsUrl("")} | ||
onChange={(e) => setWsUrl(e.currentTarget.value)} | ||
onReset={() => setWsUrl(defaultWsUrl)} | ||
/> | ||
<div></div> | ||
<h1>Vite + React</h1> | ||
<div className="card"> | ||
{/* <button onClick={() => setCount((count) => count + 1)}>count is {count}</button> */} | ||
<p> | ||
Edit <code>src/App.tsx</code> and save to test HMR | ||
</p> | ||
</div> | ||
<p className="read-the-docs">Click on the Vite and React logos to learn more</p> | ||
</> | ||
<button | ||
className="ocus:outline-none rounded border border-indigo-600 bg-indigo-600 px-12 py-3 text-sm font-medium text-white hover:text-gray-100 focus:ring active:opacity-85" | ||
onClick={test} | ||
> | ||
Test | ||
</button> | ||
{output && <pre className="p-4 text-xs dark:bg-gray-800">{output}</pre>} | ||
</div> | ||
) | ||
} | ||
|
||
export default App |
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,54 @@ | ||
export const SeedInput = ({ | ||
value, | ||
onClick, | ||
onChange, | ||
onReset, | ||
}: Pick< | ||
React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, | ||
"value" | "onClick" | "onChange" | ||
> & { onReset: () => void }) => ( | ||
<div> | ||
<label htmlFor="wsUrl" className="block text-sm font-medium text-gray-900 dark:text-gray-200"> | ||
seedOrKey | ||
</label> | ||
|
||
<div className="relative mt-1.5"> | ||
<input | ||
type="text" | ||
id="seedOrKey" | ||
className="w-full rounded-lg border-gray-300 p-2 pe-10 text-gray-700 sm:text-sm dark:bg-gray-800 dark:text-gray-300 [&::-webkit-calendar-picker-indicator]:opacity-0" | ||
placeholder="Enter a seed or key" | ||
value={value} | ||
onClick={onClick} | ||
onChange={onChange} | ||
/> | ||
|
||
<span className="absolute inset-y-0 end-2 flex w-8 items-center"> | ||
<svg | ||
xmlns="http://www.w3.org/2000/svg" | ||
fill="none" | ||
viewBox="0 0 24 24" | ||
strokeWidth="1.5" | ||
stroke="currentColor" | ||
className="size-5 text-gray-500" | ||
/> | ||
<svg | ||
xmlns="http://www.w3.org/2000/svg" | ||
fill="none" | ||
viewBox="0 0 21 21" | ||
fillRule="evenodd" | ||
stroke="currentColor" | ||
strokeLinecap="round" | ||
strokeLinejoin="round" | ||
className="size-5 cursor-pointer text-gray-500 hover:text-gray-300" | ||
onClick={onReset} | ||
> | ||
<g transform="matrix(0 1 1 0 2.5 2.5)"> | ||
<path d="m3.98652376 1.07807068c-2.38377179 1.38514556-3.98652376 3.96636605-3.98652376 6.92192932 0 4.418278 3.581722 8 8 8s8-3.581722 8-8-3.581722-8-8-8" /> | ||
<path d="m4 1v4h-4" transform="matrix(1 0 0 -1 0 6)" /> | ||
</g> | ||
</svg> | ||
</span> | ||
</div> | ||
</div> | ||
) |
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,72 @@ | ||
import { presetWsUrls } from "./constants" | ||
|
||
export const WsSelect = ({ | ||
value, | ||
onClick, | ||
onChange, | ||
onReset, | ||
}: Pick< | ||
React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, | ||
"value" | "onClick" | "onChange" | ||
> & { onReset: () => void }) => ( | ||
<div> | ||
<label htmlFor="wsUrl" className="block text-sm font-medium text-gray-900 dark:text-gray-200"> | ||
wsUrl | ||
</label> | ||
|
||
<div className="relative mt-1.5"> | ||
<input | ||
type="text" | ||
list="presetWsUrls" | ||
id="wsUrl" | ||
className="w-full rounded-lg border-gray-300 p-2 pe-10 text-gray-700 sm:text-sm dark:bg-gray-800 dark:text-gray-300 [&::-webkit-calendar-picker-indicator]:opacity-0" | ||
placeholder="Enter a websocket url" | ||
value={value} | ||
onClick={onClick} | ||
onChange={onChange} | ||
/> | ||
|
||
<span className="absolute inset-y-0 end-2 flex w-8 items-center"> | ||
<svg | ||
xmlns="http://www.w3.org/2000/svg" | ||
fill="none" | ||
viewBox="0 0 24 24" | ||
strokeWidth="1.5" | ||
stroke="currentColor" | ||
className="size-5 text-gray-500" | ||
> | ||
<path | ||
strokeLinecap="round" | ||
strokeLinejoin="round" | ||
d="M8.25 15L12 18.75 15.75 15m-7.5-6L12 5.25 15.75 9" | ||
/> | ||
</svg> | ||
|
||
<svg | ||
xmlns="http://www.w3.org/2000/svg" | ||
fill="none" | ||
viewBox="0 0 21 21" | ||
fillRule="evenodd" | ||
stroke="currentColor" | ||
strokeLinecap="round" | ||
strokeLinejoin="round" | ||
className="size-5 cursor-pointer text-gray-500 hover:text-gray-300" | ||
onClick={onReset} | ||
> | ||
<g transform="matrix(0 1 1 0 2.5 2.5)"> | ||
<path d="m3.98652376 1.07807068c-2.38377179 1.38514556-3.98652376 3.96636605-3.98652376 6.92192932 0 4.418278 3.581722 8 8 8s8-3.581722 8-8-3.581722-8-8-8" /> | ||
<path d="m4 1v4h-4" transform="matrix(1 0 0 -1 0 6)" /> | ||
</g> | ||
</svg> | ||
</span> | ||
</div> | ||
|
||
<datalist id="presetWsUrls"> | ||
{presetWsUrls.map((url) => ( | ||
<option key={url} value={url}> | ||
{url} | ||
</option> | ||
))} | ||
</datalist> | ||
</div> | ||
) |
This file was deleted.
Oops, something went wrong.
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,5 @@ | ||
export const presetWsUrls = [ | ||
"wss://rpc.laos.laosfoundation.io", | ||
"wss://rpc.laosmercury.gorengine.com", | ||
"wss://wss.api.moonbeam.network", | ||
] |
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 |
---|---|---|
@@ -1,18 +1,3 @@ | ||
@tailwind base; | ||
@tailwind components; | ||
@tailwind utilities; | ||
|
||
#root { | ||
max-width: 1280px; | ||
margin: 0 auto; | ||
padding: 2rem; | ||
text-align: center; | ||
} | ||
|
||
body { | ||
margin: 0; | ||
display: flex; | ||
place-items: center; | ||
min-width: 320px; | ||
min-height: 100vh; | ||
} |
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 |
---|---|---|
@@ -1,10 +1,12 @@ | ||
import { StrictMode } from 'react' | ||
import { createRoot } from 'react-dom/client' | ||
import App from './App.tsx' | ||
import './index.css' | ||
import { StrictMode } from "react" | ||
import { createRoot } from "react-dom/client" | ||
|
||
createRoot(document.getElementById('root')!).render( | ||
import { App } from "./App.tsx" | ||
|
||
import "./index.css" | ||
|
||
createRoot(document.getElementById("root")!).render( | ||
<StrictMode> | ||
<App /> | ||
</StrictMode>, | ||
</StrictMode> | ||
) |
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,20 @@ | ||
import { TypeRegistry } from "@polkadot/types" | ||
import { u8aToNumber } from "@polkadot/util" | ||
|
||
export const metadataFromOpaque = (opaque: Uint8Array) => { | ||
try { | ||
// pjs codec for OpaqueMetadata doesn't allow us to decode the actual Metadata, find it ourselves | ||
const u8aBytes = opaque | ||
for (let i = 0; i < 20; i++) { | ||
// skip until we find the magic number that is used as prefix of metadata objects (usually in the first 10 bytes) | ||
if (u8aToNumber(u8aBytes.slice(i, i + 4)) !== 0x6174656d) continue | ||
|
||
const metadata = new TypeRegistry().createType("Metadata", u8aBytes.slice(i)) | ||
|
||
return metadata.toHex() | ||
} | ||
throw new Error("Magic number not found") | ||
} catch (cause) { | ||
throw new Error("Failed to decode metadata from OpaqueMetadata", { cause }) | ||
} | ||
} |
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