update app based on new chain metadata

Signed-off-by: Uncle Fatso <uncle.fatso@ghostchain.io>
This commit is contained in:
Uncle Fatso 2025-12-01 17:20:40 +03:00
parent 03e5b31d36
commit a0a076b6dc
Signed by: f4ts0
GPG Key ID: 565F4F2860226EBB
18 changed files with 15 additions and 526 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "ghost-lite", "name": "ghost-lite",
"version": "0.1.9", "version": "0.2.0",
"description": "Web application for Ghost and Casper chain.", "description": "Web application for Ghost and Casper chain.",
"author": "Uncle f4ts0 <f4ts0@ghostchain.io>", "author": "Uncle f4ts0 <f4ts0@ghostchain.io>",
"maintainers": [ "maintainers": [

View File

@ -18,7 +18,7 @@ import {
const chainData = [ const chainData = [
{ {
label: "CASPER", label: "CASPER",
value: "0xa217f4ee58a944470e9633ca5bd6d28a428ed64cd9b6f3e413565f359f89af90", value: "0x5e1190682f1a6409cdfd691c0b23a6db792864d8994e591e9c19a31d8163989f",
chainSpec: casperDevelopment, chainSpec: casperDevelopment,
} }
] ]

View File

@ -14,8 +14,6 @@ export const Header = () => {
return "Address Book" return "Address Book"
case "nominations": case "nominations":
return "Nominations" return "Nominations"
case "applause":
return "Self Applause"
default: default:
return "Health Check"; return "Health Check";
} }

View File

@ -124,12 +124,6 @@ export const Sidebar = () => {
<span className={`md:block hidden ${cName("title", currentPath, "nominations")}`} >Nominations</span> <span className={`md:block hidden ${cName("title", currentPath, "nominations")}`} >Nominations</span>
</div> </div>
</Link> </Link>
<Link to="/applause" className="relative">
<div className={cName("item", currentPath, "applause")}>
<Handshake className={cName("icon", currentPath, "applause")} />
<span className={`md:block hidden ${cName("title", currentPath, "applause")}`} >Self Applause</span>
</div>
</Link>
</ul> </ul>
<div className="w-full text-center flex-grow flex flex-col justify-end"> <div className="w-full text-center flex-grow flex flex-col justify-end">

View File

@ -1,9 +1,6 @@
export const chainSpec: string = `{ export const chainSpec: string = `{
"badBlocks": null, "badBlocks": null,
"bootNodes": [ "bootNodes": [
"/dns/bootnode007.ghostchain.io/tcp/30335/p2p/12D3KooWF9SWxz9dmy6vfndQhoxqCa7PESaoFWEiF8Jkqh4xKDRf",
"/dns/bootnode007.ghostchain.io/tcp/30336/ws/p2p/12D3KooWF9SWxz9dmy6vfndQhoxqCa7PESaoFWEiF8Jkqh4xKDRf",
"/dns/bootnode007.ghostchain.io/tcp/443/wss/p2p/12D3KooWF9SWxz9dmy6vfndQhoxqCa7PESaoFWEiF8Jkqh4xKDRf",
"/dns/bootnode007.ghostchain.io/tcp/30335/p2p/12D3KooWF9SWxz9dmy6vfndQhoxqCa7PESaoFWEiF8Jkqh4xKDRf", "/dns/bootnode007.ghostchain.io/tcp/30335/p2p/12D3KooWF9SWxz9dmy6vfndQhoxqCa7PESaoFWEiF8Jkqh4xKDRf",
"/dns/bootnode007.ghostchain.io/tcp/30336/ws/p2p/12D3KooWF9SWxz9dmy6vfndQhoxqCa7PESaoFWEiF8Jkqh4xKDRf", "/dns/bootnode007.ghostchain.io/tcp/30336/ws/p2p/12D3KooWF9SWxz9dmy6vfndQhoxqCa7PESaoFWEiF8Jkqh4xKDRf",
"/dns/bootnode007.ghostchain.io/tcp/443/wss/p2p/12D3KooWF9SWxz9dmy6vfndQhoxqCa7PESaoFWEiF8Jkqh4xKDRf", "/dns/bootnode007.ghostchain.io/tcp/443/wss/p2p/12D3KooWF9SWxz9dmy6vfndQhoxqCa7PESaoFWEiF8Jkqh4xKDRf",
@ -14,14 +11,14 @@ export const chainSpec: string = `{
"chainType": "Live", "chainType": "Live",
"forkBlocks": null, "forkBlocks": null,
"genesis": { "genesis": {
"stateRootHash": "0x9f83bb5130060f3c6e3bb0949991d63ab06d0c51b6b35ad9ea15d611527626f5" "stateRootHash": "0xe904ed56128d0b6118d50bb89808913644d02a46bc965f4b22d305fe8fd23d46"
}, },
"id": "casper_staging_testnet", "id": "casper_staging_testnet",
"lightSyncState": { "lightSyncState": {
"babeEpochChanges": "0x04526becff85b353ba123ebef2de7104aa94ea52b7cb08863238fbc7798fc8a04dd1a302000108656f1100000000686e6f1100000000048e73a9b1326a5d935d352a63d0000df66d338d9af665c821b95cfdfa0c61042431ad020001686e6f1100000000c8776f110000000004a62ccc450a1291a6cd227c785da34d0bf0a6dbbfed36b0c691d6001e2802967590b6020001c8776f110000000028816f110000000000000c526becff85b353ba123ebef2de7104aa94ea52b7cb08863238fbc7798fc8a04dd1a30200014b0000000000000008656f110000000060090000000000002892d14d05957ee12db010c16106ca43791c329a1336d55390f4728abe6d666c130100000000000000567d55825be3e1dc3cd4de2e6300fe9371c781a402e8ecee9eaffa85f8349d640100000000000000daaaaab6a6e574099e24ae9bb75b543610edef9d374fa85a378edb573b47615f0100000000000000926bef8ca2ebab7e299cdfdc4b436646b0fde9b108c5f4d86880a37f18d7957d010000000000000074fa7381a7a74b316afb6793a00387eed9d95d46a69866cbb316b5d9c918af0e0100000000000000ce0757db02225a7f77a026415ccc20a73d99f8f20757df8507aa471305557e380100000000000000c876f26ddc258dd75d048dd44b11c75654af8e43f69f18f5d8c360aacccbac520100000000000000b4ff1b597f203f02e633b5986c4395447d2bb7dc4ca194cf844aded19910c61001000000000000006c4dd88b43e2011cf9a6a73d53446336ac9e04cdd4ca23587df63187ac455e4901000000000000003c944c704cae203619b9e7a5a4b6742736da6a8e76c762291bebdc7652cfec2f01000000000000000632b7872e80f8d1a82e82ff7fc8c20702dd5d75a844280f2a97b408bdd719a801000000000000000400000000000000028e73a9b1326a5d935d352a63d0000df66d338d9af665c821b95cfdfa0c61042431ad0200014c00000000000000686e6f110000000060090000000000002892d14d05957ee12db010c16106ca43791c329a1336d55390f4728abe6d666c130100000000000000567d55825be3e1dc3cd4de2e6300fe9371c781a402e8ecee9eaffa85f8349d640100000000000000daaaaab6a6e574099e24ae9bb75b543610edef9d374fa85a378edb573b47615f0100000000000000926bef8ca2ebab7e299cdfdc4b436646b0fde9b108c5f4d86880a37f18d7957d010000000000000074fa7381a7a74b316afb6793a00387eed9d95d46a69866cbb316b5d9c918af0e0100000000000000ce0757db02225a7f77a026415ccc20a73d99f8f20757df8507aa471305557e380100000000000000c876f26ddc258dd75d048dd44b11c75654af8e43f69f18f5d8c360aacccbac520100000000000000b4ff1b597f203f02e633b5986c4395447d2bb7dc4ca194cf844aded19910c61001000000000000006c4dd88b43e2011cf9a6a73d53446336ac9e04cdd4ca23587df63187ac455e4901000000000000003c944c704cae203619b9e7a5a4b6742736da6a8e76c762291bebdc7652cfec2f0100000000000000a4f0ddc9df9a866de56f4980f1228bacfca7da19df656bae52dd04aa05aaf1f20100000000000000040000000000000002a62ccc450a1291a6cd227c785da34d0bf0a6dbbfed36b0c691d6001e2802967590b60200014d00000000000000c8776f110000000060090000000000002892d14d05957ee12db010c16106ca43791c329a1336d55390f4728abe6d666c130100000000000000567d55825be3e1dc3cd4de2e6300fe9371c781a402e8ecee9eaffa85f8349d640100000000000000daaaaab6a6e574099e24ae9bb75b543610edef9d374fa85a378edb573b47615f0100000000000000926bef8ca2ebab7e299cdfdc4b436646b0fde9b108c5f4d86880a37f18d7957d010000000000000074fa7381a7a74b316afb6793a00387eed9d95d46a69866cbb316b5d9c918af0e0100000000000000ce0757db02225a7f77a026415ccc20a73d99f8f20757df8507aa471305557e380100000000000000c876f26ddc258dd75d048dd44b11c75654af8e43f69f18f5d8c360aacccbac520100000000000000b4ff1b597f203f02e633b5986c4395447d2bb7dc4ca194cf844aded19910c61001000000000000006c4dd88b43e2011cf9a6a73d53446336ac9e04cdd4ca23587df63187ac455e4901000000000000003c944c704cae203619b9e7a5a4b6742736da6a8e76c762291bebdc7652cfec2f0100000000000000ebbe298fc531624554e8c23b389995c3e2a208a95c0085a1b3f71332700525b10100000000000000040000000000000002", "babeEpochChanges": "0x04a48cb27a9282efc41676f4ba4e5c7d016001f6d87117c19a774e7ea7f290c2a9ad120000018917871100000000e920871100000000042d59acaed5e1e969af1e3dec32e24590b41ae27b3d6487eff5df4cd0623e44c70d1c000001e920871100000000492a87110000000004fa66036d13c86f1d01cb28703545eb8834bd42521cbdfa969960e7a88e46e30f6d25000001492a871100000000a93387110000000000000c2d59acaed5e1e969af1e3dec32e24590b41ae27b3d6487eff5df4cd0623e44c70d1c0000010400000000000000e920871100000000600900000000000010daaaaab6a6e574099e24ae9bb75b543610edef9d374fa85a378edb573b47615f010000000000000074fa7381a7a74b316afb6793a00387eed9d95d46a69866cbb316b5d9c918af0e01000000000000006c4dd88b43e2011cf9a6a73d53446336ac9e04cdd4ca23587df63187ac455e4901000000000000003c944c704cae203619b9e7a5a4b6742736da6a8e76c762291bebdc7652cfec2f0100000000000000bb1fba5c1ec87a48efa4fc9b84a9eb11c8e46889a90b05b015097e28a6af3cac0100000000000000040000000000000002a48cb27a9282efc41676f4ba4e5c7d016001f6d87117c19a774e7ea7f290c2a9ad1200000103000000000000008917871100000000600900000000000010daaaaab6a6e574099e24ae9bb75b543610edef9d374fa85a378edb573b47615f010000000000000074fa7381a7a74b316afb6793a00387eed9d95d46a69866cbb316b5d9c918af0e01000000000000006c4dd88b43e2011cf9a6a73d53446336ac9e04cdd4ca23587df63187ac455e4901000000000000003c944c704cae203619b9e7a5a4b6742736da6a8e76c762291bebdc7652cfec2f01000000000000009e1edcaea9b2d7495e5ca2351e3ec23d4cb366d31deed4e881f35036a71556c30100000000000000040000000000000002fa66036d13c86f1d01cb28703545eb8834bd42521cbdfa969960e7a88e46e30f6d250000010500000000000000492a871100000000600900000000000010daaaaab6a6e574099e24ae9bb75b543610edef9d374fa85a378edb573b47615f010000000000000074fa7381a7a74b316afb6793a00387eed9d95d46a69866cbb316b5d9c918af0e01000000000000006c4dd88b43e2011cf9a6a73d53446336ac9e04cdd4ca23587df63187ac455e4901000000000000003c944c704cae203619b9e7a5a4b6742736da6a8e76c762291bebdc7652cfec2f010000000000000033b70defe8361aa0873555cf8d79dcb2326c69b8ec0731a01e9283167b3f09360100000000000000040000000000000002",
"babeFinalizedBlockWeight": 44669, "babeFinalizedBlockWeight": 2491,
"finalizedBlockHeader": "0xbf02d4785a63e5592b205e1c946cb2ad76bd54c5182bda138aae4d253e393acf2ee80a00345efc560a1009cf7a8473f9d76eda4aba62760840e9f418af3e0a7bcdb9baeb8531540185be10e8b9ce0bd8a1cb3147280ea2763635a3df78b25cc77db93ead080642414245b5010305000000e5716f1100000000aa621d0cdb23cb93faf8af690997607c51533bd273c29cb9f95abfe390dd0870e481b73aca7eb701cf7527dfe16298e75491bce0a5e4bd3209b9749ad47a0f0f75e74ea4485e4f85ac3efd4c19f81ebda4f387a4b081a0b6b517d078c3281e0f05424142450101de9031fae4009f54e5472d5889f5f96f6d1fcbb327544b038e486feff284a9137fe1a406baa26c9b97c01134ee6e055a09e452ab9ff7f9e2c67f5600cc796783", "finalizedBlockHeader": "0xe9aae3a567eb9f8f1a3eb4a37ca43a601f8151ebcb997f6a045f286897a639bfbd9c4ab35315e53ebed0b4d800824adff0a92b77c43c3179e2c9fbd380cfe85dd875a3abff1290e28bd05c291373a50a27e945b866afafe76b4ada7c1838f469785e080642414245b5010100000000ac228711000000003e4ca4a3c13e752394bd1759610332eb0a8648701175ccc7b894ef9c9132f463268c53552fb3fcf357191b14eeb9f973257bc3ba6395144a083668e01ad9bb08c2a0fc97de513b0d4aeed4d84bb040aa23006319a1ccc756a7240cfd01b5990905424142450101881f5e853b654fe6b8cc35efcc82f462d8bbb63c53302d5d14adf09e187c4305c176792092524c87d3cc967f1d9f45cee9bd74d78c84b195942b2451192e9289",
"grandpaAuthoritySet": "0x285c5797253050012e7c675107cba62e3c168e6be3698608050692d0b2b0283f1c01000000000000009dd9d939aec9fd7736a54895471f120290369c6ab475ab184fbd6257db02b95a010000000000000055446f9a7aa99ced06b317c80ce90d56b84e56526775683af2525969e8da0b6401000000000000002311743cb7b400fe3a06b9fd89577df2c3cd8d91a97f1cbca87bdbbb572258be0100000000000000236d2fa03f4ed8cb65de7e514d7540159b328f1c170dd402b094ad7fbf5472180100000000000000feeb6c72113ccbc0059560ef0bbccc3ebcf6463d6c6c2e961a6bbaf6fb057e160100000000000000c709bf6f4267e9afe1a308169c58bfe3371271321c03cf527ff30449177d42e901000000000000006f2efabe4f36c2ac7708ee1abad83fd081bd8192159a55eea6f6063551bdc8ba01000000000000008f9ea20bf4a807a8e710f7559dece86e94672b5b361de157bdaa5c1f37849f8d01000000000000000aa3a88f6b777c95c3dfe7e997b76798413f16aa325f34824cae0c9102b281d501000000000000000c0000000000000000010bba020000300000000000000000293800000100000000000000687000000200000000000000a5a800000300000000000000e4e000000400000000000000231901000500000000000000d44b010006000000000000001282010007000000000000001cb30100080000000000000065ea010009000000000000009f2202000a00000000000000c15902000b0000000000000012910200" "grandpaAuthoritySet": "0x1055446f9a7aa99ced06b317c80ce90d56b84e56526775683af2525969e8da0b640100000000000000236d2fa03f4ed8cb65de7e514d7540159b328f1c170dd402b094ad7fbf54721801000000000000008f9ea20bf4a807a8e710f7559dece86e94672b5b361de157bdaa5c1f37849f8d01000000000000000aa3a88f6b777c95c3dfe7e997b76798413f16aa325f34824cae0c9102b281d50100000000000000000000000000000000012f2700000000"
}, },
"name": "Casper Staging Testnet", "name": "Casper Staging Testnet",
"properties": { "properties": {
@ -36,4 +33,5 @@ export const chainSpec: string = `{
0 0
] ]
] ]
}` }
`

View File

@ -13,7 +13,6 @@ const HealthCheck = lazy(() => import("./HealthCheck").then(module => ({ default
const Transactions = lazy(() => import("./Transactions").then(module => ({ default: module.Transactions }))) const Transactions = lazy(() => import("./Transactions").then(module => ({ default: module.Transactions })))
const Nominations = lazy(() => import("./Nominations").then(module => ({ default: module.Nominations }))) const Nominations = lazy(() => import("./Nominations").then(module => ({ default: module.Nominations })))
const AddressBook = lazy(() => import("./AddressBook").then(module => ({ default: module.AddressBook }))) const AddressBook = lazy(() => import("./AddressBook").then(module => ({ default: module.AddressBook })))
const SelfApplause = lazy(() => import("./SelfApplause").then(module => ({ default: module.SelfApplause })))
export const App = () => { export const App = () => {
return ( return (
@ -30,7 +29,6 @@ export const App = () => {
<Route path="/health" element={<HealthCheck />} /> <Route path="/health" element={<HealthCheck />} />
<Route path="/transactions" element={<Transactions />} /> <Route path="/transactions" element={<Transactions />} />
<Route path="/book" element={<AddressBook />} /> <Route path="/book" element={<AddressBook />} />
<Route path="/applause" element={<SelfApplause />} />
<Route path="/nominations" element={<Nominations />} /> <Route path="/nominations" element={<Nominations />} />
<Route path="*" element={<Navigate to="/health" replace />} /> <Route path="*" element={<Navigate to="/health" replace />} />
</Routes> </Routes>

View File

@ -209,6 +209,8 @@ export const Nominations = () => {
const currentValidators = useCurrentValidators({ address: interestingValidator }) const currentValidators = useCurrentValidators({ address: interestingValidator })
const validatorOverview = useValidatorsOverview({ eraIndex: eraIndex?.index, address: interestingValidator }) const validatorOverview = useValidatorsOverview({ eraIndex: eraIndex?.index, address: interestingValidator })
console.log(eraRewardPoints)
const tokenDecimals: number = chainSpecV1?.properties?.tokenDecimals ?? 0 const tokenDecimals: number = chainSpecV1?.properties?.tokenDecimals ?? 0
const tokenSymbol: string = chainSpecV1?.properties?.tokenSymbol ?? "" const tokenSymbol: string = chainSpecV1?.properties?.tokenSymbol ?? ""
const ss58Format: number = chainSpecV1?.properties?.ss58Format ?? 1995 const ss58Format: number = chainSpecV1?.properties?.ss58Format ?? 1995

View File

@ -1,285 +0,0 @@
import { useState, useEffect, useMemo } from "react"
import { useSearchParams } from "react-router-dom"
import { Hand, BadgeAlert, BadgeCheck, ArrowBigRightDash } from "lucide-react"
import { u64, u128 } from "scale-ts"
import { keccak256 } from "viem"
import { decodeAddress } from "@polkadot/util-crypto"
import { Row } from "./Row"
import { Sender } from "./Accounts"
import { Input } from "../components/ui/input"
import { Button } from "../components/ui/button"
import {
useChainSpecV1,
useSystemAccount,
useUnstableProvider,
useApplauseThreshold,
useCurrentIndex,
useAuthorities,
useClapsInSession,
useReceivedClaps,
useApplausesForTransaction,
useSelfApplauseCalldata,
useTransactionStatusProvider,
SessionAuthorityInfo
} from "../hooks"
export const SelfApplause = () => {
const [searchParams] = useSearchParams()
const [networkId, setNetworkId] = useState("")
const [sessionIndex, setSessionIndex] = useState("")
const [transactionHash, setTransactionHash] = useState("")
const [receiver, setReceiver] = useState("")
const [bridgedAmount, setBridgedAmount] = useState("")
const { account, accounts, connectAccount } = useUnstableProvider()
const chainSpecV1 = useChainSpecV1()
const tokenDecimals: number = chainSpecV1?.properties?.tokenDecimals ?? 0
const tokenSymbol: string = chainSpecV1?.properties?.tokenSymbol ?? ""
const ss58Format: number = chainSpecV1?.properties?.ss58Format ?? 1995
const nextSessionIndex = useMemo(() => {
const number = +sessionIndex
return isNaN(number) ? undefined : number + 1
}, [sessionIndex])
const amountConverted = useMemo(() => {
const amountNum = parseFloat(bridgedAmount);
if (isNaN(amountNum)) {
return 0n;
}
return BigInt(Math.floor(amountNum * Math.pow(10, tokenDecimals)))
}, [bridgedAmount, tokenDecimals])
const hashedArguments = useMemo(() => {
try {
const amountEncoded = u128.enc(amountConverted)
const networkIdEncoded = u64.enc(BigInt(networkId))
const addressEncoded = decodeAddress(receiver, false, ss58Format)
const clapArgsStr = new Uint8Array([
...addressEncoded,
...amountEncoded,
...networkIdEncoded
])
return keccak256(clapArgsStr)
} catch {
return undefined
}
}, [receiver, amountConverted, ss58Format, networkId])
const {
isSubmittingTransaction,
handleTransaction,
renderStatus,
} = useTransactionStatusProvider()
const appluseThreshold = useApplauseThreshold()
const currentSession = useCurrentIndex()
const authorities = useAuthorities({ currentSession: Number(sessionIndex) })
const authoritiesNext = useAuthorities({ currentSession: nextSessionIndex })
const clapsInSession = useClapsInSession({
currentSession: Number(sessionIndex)
})
const clapsInNextSession = useClapsInSession({
currentSession: nextSessionIndex
})
const receivedClaps = useReceivedClaps({
currentSession: Number(sessionIndex),
txHash: transactionHash,
argsHash: hashedArguments
})
const receivedClapsNext = useReceivedClaps({
currentSession: nextSessionIndex,
txHash: transactionHash,
argsHash: hashedArguments
})
const transactionApplaused = useApplausesForTransaction({
currentSession: Number(sessionIndex),
txHash: transactionHash,
argsHash: hashedArguments
})
useEffect(() => {
setNetworkId(searchParams.get("networkId") ?? "")
setSessionIndex(searchParams.get("sessionIndex") ?? "")
setTransactionHash(searchParams.get("transactionHash") ?? "")
setReceiver(searchParams.get("receiver") ?? "")
setBridgedAmount(searchParams.get("amount") ?? "")
}, [searchParams])
const senderAccount = useSystemAccount({
account: account
? account.address
: undefined
})
const disabledInSession = useMemo(() => {
return clapsInSession?.filter((info: SessionAuthorityInfo) => info.disabled) ?? []
}, [clapsInSession])
const totalDisabled = useMemo(() => {
let numberOfDisabled = clapsInSession?.filter((info: SessionAuthorityInfo) => info.disabled).length ?? 0
clapsInNextSession?.forEach((info: SessionAuthorityInfo, index: number) => {
if (info.disabled) {
const authority = authoritiesNext?.at(index)
if (authority) {
const myIndex = authorities?.indexOf(authority)
if (myIndex) {
numberOfDisabled += 1
}
}
}
})
return numberOfDisabled
}, [clapsInSession, clapsInNextSession, authoritiesNext, authorities])
const totalReceivedClaps = useMemo(() => {
let totalClaps = receivedClaps?.length ?? 0
for (const index of (receivedClapsNext ?? [])) {
const authority = authoritiesNext[index];
if (authority) {
const myIndex = authorities?.indexOf(authority)
if (myIndex) {
totalClaps += 1
}
}
}
return totalClaps
}, [authorities, receivedClaps, receivedClapsNext, authoritiesNext])
const applausePossible = useMemo(() => {
const value = totalReceivedClaps * 100 / (authorities?.length ?? 0) - totalDisabled
return value > appluseThreshold
}, [authorities, totalReceivedClaps, totalDisabled, appluseThreshold])
const loadedCorrectly = useMemo(() => {
return clapsInSession && authorities && disabledInSession
}, [clapsInSession, authorities, disabledInSession])
const applyDecimals = (value = 0n, decimals = 0, tokenSymbol = "CSPR") => {
if (!value) return `0 ${tokenSymbol}`
const numberValue = Number(value) / Math.pow(10, decimals)
const formatter = new Intl.NumberFormat("en-US", {
minimumFractionDigits: 6,
maximumFractionDigits: 6,
})
return `${formatter.format(numberValue)} ${tokenSymbol}`
}
const calldata = useSelfApplauseCalldata(
Number(networkId),
Number(sessionIndex),
transactionHash,
receiver,
amountConverted
)
const handleOnSelfApplause = () => handleTransaction({ calldata: calldata, txName: "self_applause" })
return (
<div className="sm:w-[500px] w-[85%] h-fit flex flex-col flex-1 gap-2 justify-center self-center rounded py-2">
<div className="bg-muted p-4 rounded flex flex-col gap-2">
<Sender
account={account?.address ?? ""}
accounts={accounts?.map(acc => acc?.address ?? "") ?? []}
senderAccount={senderAccount}
senderBalance={applyDecimals(senderAccount?.data.free ?? 0n, tokenDecimals, tokenSymbol)}
tokenDecimals={tokenDecimals}
tokenSymbol={tokenSymbol}
connectAccount={connectAccount}
applyDecimals={applyDecimals}
/>
<Row title={"Network Id"} element={<Input
onChange={(e) => setNetworkId(e.target.value)}
value={networkId}
aria-label="NetworkId"
type="text"
className="sm:w-[300px] w-full"
placeholder={"0"}
/>} />
<Row title={"Session Index"} element={<Input
onChange={(e) => setSessionIndex(e.target.value)}
value={sessionIndex}
aria-label="SessionIndex"
type="text"
className="sm:w-[300px] w-full"
placeholder={"0"}
/>} />
<Row title={"Transaction Hash"} element={<Input
onChange={(e) => setTransactionHash(e.target.value)}
value={transactionHash}
aria-label="TransactionHash"
type="text"
className="sm:w-[300px] w-full"
placeholder={"0x..."}
/>} />
<Row title={"Receiver Address"} element={<Input
onChange={(e) => setReceiver(e.target.value)}
value={receiver}
aria-label="ReceiverAddress"
type="text"
className="sm:w-[300px] w-full"
placeholder={"sf..."}
/>} />
<Row title={"Bridged Amount"} element={<Input
onChange={(e) => setBridgedAmount(e.target.value)}
value={bridgedAmount}
aria-label="BridgedAmount"
type="text"
className="sm:w-[300px] w-full"
placeholder={"0"}
/>} />
{loadedCorrectly &&
<div className="flex justify-around items-center mt-4 mb-2">
<div className="flex flex-col justify-center items-center hover:text-secondary-foreground cursor-pointer">
<div className="text-xs">
Actual
</div>
<BadgeAlert className="w-8 h-8 inline-block" />
<div className="text-xs">
{clapsInSession.length} / {authorities.length - disabledInSession.length}
</div>
</div>
<ArrowBigRightDash className="w-6 h-6 inline-block" />
<div className="flex flex-col justify-center items-center hover:text-secondary-foreground cursor-pointer">
<div className="text-xs">
Possible
</div>
<BadgeCheck className="w-8 h-8 inline-block" />
<div className="text-xs">
{totalReceivedClaps} / {authorities ? authorities.length - totalDisabled : 0}
</div>
</div>
</div>
}
<Button
type="button"
variant="secondary"
className="text-sm p-4 w-full mt-4"
onClick={handleOnSelfApplause}
disabled={
isSubmittingTransaction || transactionApplaused ||
!loadedCorrectly || !account || !applausePossible ||
currentSession < (nextSessionIndex ?? 0)
}
>
<Hand className="w-4 h-4 inline-block mr-2" />
{transactionApplaused
? "Already applaused"
: !applausePossible
? "Impossible to applause"
: currentSession < (nextSessionIndex ?? 0)
? "Not ready yet"
: "Try applause"
}
</Button>
{renderStatus()}
</div>
</div>
)
}

View File

@ -18,8 +18,5 @@ export * from "./useNominations"
export * from "./useLedger" export * from "./useLedger"
export * from "./usePayee" export * from "./usePayee"
export * from "./useSlasingSpans" export * from "./useSlasingSpans"
export * from "./useApplausesForTransaction"
export * from "./useAuthorities" export * from "./useAuthorities"
export * from "./useClapsInSession"
export * from "./useReceivedClaps"
export * from "./useCurrentIndex" export * from "./useCurrentIndex"

View File

@ -1,54 +0,0 @@
import useSWRSubscription from "swr/subscription"
import { getDynamicBuilder, getLookupFn } from "@polkadot-api/metadata-builders"
import type { BlockInfo } from "@polkadot-api/observable-client"
import { distinct, filter, map, mergeMap } from "rxjs"
import { fromHex } from "@polkadot-api/utils";
import { useUnstableProvider } from "./useUnstableProvider"
import { useMetadata } from "./useMetadata"
interface ApplausesForTransactionInterface {
currentSession?: Number;
txHash?: string;
argsHash?: string;
}
export const useApplausesForTransaction = ({ currentSession, txHash, argsHash }: ApplausesForTransactionInterface) => {
const { chainHead$, chainId } = useUnstableProvider()
const metadata = useMetadata()
const { data: applausesForTransaction } = useSWRSubscription(
chainHead$ && txHash && argsHash && currentSession && chainId && metadata
? ["applausesForTransaction", chainHead$, txHash, argsHash, currentSession, chainId, metadata]
: null,
([_, chainHead$, txHash, argsHash, currentSession, chainId, metadata], { next }) => {
const { finalized$, storage$ } = chainHead$
const subscription = finalized$.pipe(
filter(Boolean),
mergeMap((blockInfo: BlockInfo) => {
const builder = getDynamicBuilder(getLookupFn(metadata))
const applausesForTransaction = builder.buildStorage("GhostSlowClaps", "ApplausesForTransaction")
return storage$(blockInfo?.hash, "value", () =>
applausesForTransaction?.keys.enc(
currentSession,
{ asBytes: () => fromHex(txHash) },
{ asBytes: () => fromHex(argsHash) },
)
).pipe(
filter(Boolean),
distinct(),
map((value: string) => applausesForTransaction?.value.dec(value) as boolean)
)
}),
)
.subscribe({
next(applausesForTransaction: boolean) {
next(null, applausesForTransaction)
},
error: next,
})
return () => subscription.unsubscribe()
}
)
return applausesForTransaction
}

View File

@ -227,39 +227,3 @@ export const usePayeeCalldata = (expectedPayee: string | undefined, destinationR
) )
return calldata return calldata
} }
export const useSelfApplauseCalldata = (
networkId?: Number,
sessionIndex?: Number,
transactionHash?: string,
receiver?: string,
amount?: bigint
) => {
const { client, chainId } = useUnstableProvider()
const metadata = useMetadata()
const { data: calldata } = useSWR(
client && chainId && networkId && sessionIndex && transactionHash && receiver && amount && metadata
? ["self_applause", client, chainId, metadata, networkId, sessionIndex, transactionHash, receiver, amount]
: null,
([_, client, _chainId, metadata, networkId, sessionIndex, transactionHash, receiver, amount]) => {
const builder = getDynamicBuilder(getLookupFn(metadata))
const { codec, location } = builder.buildCall("GhostSlowClaps", "self_applause")
const txHash = new Binary(fromHex(transactionHash))
return toHex(
mergeUint8(
new Uint8Array(location),
codec.enc({
network_id: BigInt(networkId?.toString() ?? "0"),
prev_session_index: sessionIndex,
transaction_hash: txHash,
receiver: receiver,
amount: amount,
}),
)
)
}
)
return calldata
}

View File

@ -1,51 +0,0 @@
import useSWRSubscription from "swr/subscription"
import { getDynamicBuilder, getLookupFn } from "@polkadot-api/metadata-builders"
import type { BlockInfo } from "@polkadot-api/observable-client"
import { distinct, filter, map, mergeMap } from "rxjs"
import { useUnstableProvider } from "./useUnstableProvider"
import { useMetadata } from "./useMetadata"
export type SessionAuthorityInfo = {
claps: number
disabled: boolean
}
interface ClapsInSessionInterface {
currentSession?: Number;
}
export const useClapsInSession = ({ currentSession }: ClapsInSessionInterface) => {
const { chainHead$, chainId } = useUnstableProvider()
const metadata = useMetadata()
const { data: clapsInSession } = useSWRSubscription(
chainHead$ && chainId && metadata
? ["clapsInSession", chainHead$, currentSession, chainId, metadata]
: null,
([_, chainHead$, currentSession, chainId, metadata], { next }) => {
const { finalized$, storage$ } = chainHead$
const subscription = finalized$.pipe(
filter(Boolean),
mergeMap((blockInfo: BlockInfo) => {
const builder = getDynamicBuilder(getLookupFn(metadata))
const clapsInSession = builder.buildStorage("GhostSlowClaps", "ClapsInSession")
return storage$(blockInfo?.hash, "value", () =>
clapsInSession?.keys.enc(currentSession)
).pipe(
filter(Boolean),
distinct(),
map((value: string) => clapsInSession?.value.dec(value) as SessionAuthorityInfo)
)
}),
)
.subscribe({
next(clapsInSession: SessionAuthorityInfo) {
next(null, clapsInSession)
},
error: next,
})
return () => subscription.unsubscribe()
}
)
return clapsInSession
}

View File

@ -21,21 +21,3 @@ export const useExistentialDeposit = () => {
) )
return existentialDeposit return existentialDeposit
} }
export const useApplauseThreshold = () => {
const { chainId } = useUnstableProvider()
const metadata = useMetadata()
const { data: existentialDeposit } = useSWR(
chainId && metadata
? ["ApplauseThreshold", chainId, metadata]
: null,
([_, chainId, metadata]) => {
const builder = getDynamicBuilder(getLookupFn(metadata))
const codec = builder.buildConstant("GhostSlowClaps", "ApplauseThreshold")
const constants = metadata?.pallets?.find(obj => obj.name === "GhostSlowClaps")?.constants
const value = constants?.find(obj => obj.name === "ApplauseThreshold")?.value
return value ? codec.dec(value) : undefined
}
)
return existentialDeposit
}

View File

@ -14,7 +14,7 @@ export type ValidatorDetails = {
export const useCurrentValidators = ({ address }: { address: string | undefined }) => { export const useCurrentValidators = ({ address }: { address: string | undefined }) => {
const { chainHead$, chainId } = useUnstableProvider() const { chainHead$, chainId } = useUnstableProvider()
const metadata = useMetadata() const metadata = useMetadata()
const { data: currentValidators } = useSWRSubscription( const { data: currentValidators, error } = useSWRSubscription(
chainHead$ && address && chainId && metadata chainHead$ && address && chainId && metadata
? ["validators", chainHead$, address, chainId, metadata] ? ["validators", chainHead$, address, chainId, metadata]
: null, : null,

View File

@ -14,6 +14,7 @@ export type EraRewardPoints = {
} }
export const useEraRewardPoints = ({ eraIndex }: { eraIndex: number | undefined }) => { export const useEraRewardPoints = ({ eraIndex }: { eraIndex: number | undefined }) => {
const { chainHead$, chainId } = useUnstableProvider() const { chainHead$, chainId } = useUnstableProvider()
const metadata = useMetadata() const metadata = useMetadata()
const { data: eraRewardPoints } = useSWRSubscription( const { data: eraRewardPoints } = useSWRSubscription(

View File

@ -1,54 +0,0 @@
import useSWRSubscription from "swr/subscription"
import { getDynamicBuilder, getLookupFn } from "@polkadot-api/metadata-builders"
import type { BlockInfo } from "@polkadot-api/observable-client"
import { distinct, filter, map, mergeMap } from "rxjs"
import { fromHex } from "@polkadot-api/utils";
import { useUnstableProvider } from "./useUnstableProvider"
import { useMetadata } from "./useMetadata"
interface ReceivedClapsInterface {
currentSession?: Number;
txHash?: string;
argsHash?: string;
}
export const useReceivedClaps = ({ currentSession, txHash, argsHash }: ReceivedClapsInterface) => {
const { chainHead$, chainId } = useUnstableProvider()
const metadata = useMetadata()
const { data: receivedClaps } = useSWRSubscription(
chainHead$ && txHash && argsHash && currentSession && chainId && metadata
? ["receivedClaps", chainHead$, txHash, argsHash, currentSession, chainId, metadata]
: null,
([_, chainHead$, txHash, argsHash, currentSession, chainId, metadata], { next }) => {
const { finalized$, storage$ } = chainHead$
const subscription = finalized$.pipe(
filter(Boolean),
mergeMap((blockInfo: BlockInfo) => {
const builder = getDynamicBuilder(getLookupFn(metadata))
const receivedClaps = builder.buildStorage("GhostSlowClaps", "ReceivedClaps")
return storage$(blockInfo?.hash, "value", () =>
receivedClaps?.keys.enc(
currentSession,
{ asBytes: () => fromHex(txHash) },
{ asBytes: () => fromHex(argsHash) },
)
).pipe(
filter(Boolean),
distinct(),
map((value: string) => receivedClaps?.value.dec(value) as Number[])
)
}),
)
.subscribe({
next(receivedClaps: Number[]) {
next(null, receivedClaps)
},
error: next,
})
return () => subscription.unsubscribe()
}
)
return receivedClaps
}

View File

@ -46,8 +46,7 @@ export const UnstableProviderProvider = ({
const [chainId, setChainId_] = useState(defaultChainId) const [chainId, setChainId_] = useState(defaultChainId)
const { data: accounts } = useSWR( const { data: accounts } = useSWR(
() => () => `providerDetail.${providerDetail!.info.uuid}.provider.getAccounts(${chainId})`,
`providerDetail.${providerDetail!.info.uuid}.provider.getAccounts(${chainId})`,
async () => (await providerDetail!.provider).getAccounts(chainId), async () => (await providerDetail!.provider).getAccounts(chainId),
) )

View File

@ -1 +1 @@
export const DEFAULT_CHAIN_ID = "0xa217f4ee58a944470e9633ca5bd6d28a428ed64cd9b6f3e413565f359f89af90" export const DEFAULT_CHAIN_ID = "0x5e1190682f1a6409cdfd691c0b23a6db792864d8994e591e9c19a31d8163989f"