diff --git a/package.json b/package.json
index 613ddcb..9e8eb4e 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "ghost-dao-interface",
"private": true,
- "version": "0.7.59-beta-2",
+ "version": "0.8.0",
"type": "module",
"scripts": {
"dev": "vite",
@@ -31,7 +31,7 @@
"@tanstack/react-query-devtools": "^5.67.2",
"@wagmi/core": "^2.17.3",
"polkadot-api": "^3.3.0",
- "@polkadot-api/descriptors": "file:./.papi/descriptors",
+ "@polkadot-api/descriptors": "file:.papi/descriptors",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-ga4": "^2.1.0",
diff --git a/src/containers/Bridge/BridgeIn.jsx b/src/containers/Bridge/BridgeIn.jsx
index c81a565..d949a45 100644
--- a/src/containers/Bridge/BridgeIn.jsx
+++ b/src/containers/Bridge/BridgeIn.jsx
@@ -447,7 +447,7 @@ const BridgeIn = ({ chainId, address, config, connect }) => {
disabled={actionDisabled}
onClick={startGhostBridge}
>
- Bond
+ {isPending ? "Pending..." : "Bridge-In"}
diff --git a/src/containers/History/BridgeInModal.jsx b/src/containers/History/BridgeInModal.jsx
index 96b32b1..f5928da 100644
--- a/src/containers/History/BridgeInModal.jsx
+++ b/src/containers/History/BridgeInModal.jsx
@@ -3,6 +3,9 @@ import { useMemo, useState } from "react";
import { useTransactionReceipt, useBlockNumber, useTransaction } from 'wagmi';
import { decodeEventLog, decodeFunctionData } from 'viem';
import { Box, Typography, Link, Skeleton, useTheme } from "@mui/material";
+import { ss58Encode } from "@polkadot-labs/hdkd-helpers";
+import { hexToBytes, bytesToHex } from "viem";
+import { blake2AsU8a } from "@polkadot/util-crypto";
import CheckIcon from '@mui/icons-material/Check';
import SyncIcon from '@mui/icons-material/Sync';
@@ -18,7 +21,8 @@ import GhostStyledIcon from "../../components/Icon/GhostIcon";
import { PrimaryButton } from "../../components/Button";
import { abi as GatekeeperAbi } from "../../abi/GhostGatekeeper.json";
-import { useWeaverState } from "../../hooks/staking"
+import { executePapiBareTransaction } from "../../hooks/helpers";
+import { useWeaverRoot, useWeaverProof, useWeaverSlotValues, useWeaverMaximumSlots } from "../../hooks/staking"
import { useUnstableProvider } from "../../hooks/ghost";
import { DecimalBigNumber } from "../../helpers/DecimalBigNumber";
import { shorten, formatNumber, formatCurrency } from "../../helpers";
@@ -26,6 +30,7 @@ import { shorten, formatNumber, formatCurrency } from "../../helpers";
const BridgeInModal = ({ chainId, isOpen, close, config, historyRecord }) => {
if (historyRecord === null) return null;
+ const [isPending, setIsPending] = useState(false);
const [copiedIndex, setCopiedIndex] = useState(null);
const { casperApi, client, account } = useUnstableProvider();
const { data: blockNumber } = useBlockNumber({ watch: true });
@@ -35,11 +40,6 @@ const BridgeInModal = ({ chainId, isOpen, close, config, historyRecord }) => {
hash: historyRecord.txHash,
});
- const functionData = useMemo(() => {
- if (!txData) return null;
- return decodeFunctionData({ abi: GatekeeperAbi, data: txData.input })
- }, [txData]);
-
const copyToClipboard = (text, index) => {
navigator.clipboard.writeText(text).then(() => {
setCopiedIndex(index);
@@ -55,6 +55,15 @@ const BridgeInModal = ({ chainId, isOpen, close, config, historyRecord }) => {
}
);
+ const functionArgs = useMemo(() => {
+ if (!txData || !chainProperties) return null;
+ const decoded = decodeFunctionData({ abi: GatekeeperAbi, data: txData.input });
+ const prefix = chainProperties.properties.ss58Format;
+ const receiverRaw = decoded?.args.at(0);
+ const receiver = receiverRaw ? ss58Encode(receiverRaw, prefix) : null;
+ return { receiver, amount: decoded?.args.at(1) };
+ }, [txData, chainProperties]);
+
const { data: networkData } = useSWR(
casperApi ? ['networkData', casperApi, chainId] : null,
async () => {
@@ -67,6 +76,19 @@ const BridgeInModal = ({ chainId, isOpen, close, config, historyRecord }) => {
}
);
+ const { data: loomState } = useSWR(
+ casperApi ? ['loomState', casperApi, chainId] : null,
+ async () => {
+ const key = BigInt(chainId);
+ const res = await casperApi.query.GhostWeaver.LoomStates.getValue(key, 0n);
+ return res;
+ },
+ {
+ refreshInterval: 12000,
+ revalidateOnFocus: true
+ }
+ );
+
const chainInfo = useMemo(() => {
const client = config?.getClient();
return {
@@ -108,33 +130,36 @@ const BridgeInModal = ({ chainId, isOpen, close, config, historyRecord }) => {
return allDecodedLogs.find(l => l.eventName === 'ThreadInserted');
}, [allDecodedLogs]);
- // TODO: revisit, we need other block to be used here
- // TODO: how to verify the fact that merkle root is correct???
- // const xxxx = useWeaverState(chainId, receipt.blockNumber, bridgeInInfo?.args);
+ const { maximumSlots } = useWeaverMaximumSlots(chainId);
+ const { merkleRoot: evmMerkleRoot } = useWeaverRoot(chainId, loomState?.at_block, bridgeInInfo?.args);
+ const { merkleProof } = useWeaverProof(chainId, loomState?.at_block, bridgeInInfo?.args);
+ const { slotValues } = useWeaverSlotValues(chainId, loomState?.at_block, bridgeInInfo?.args);
- // TODO: functionData could be here
const localString = new Date(historyRecord.submitted).toLocaleString();
- const receiver = "sfKCC7CEfJLTgGopghSPB5EtNfLVUu34nMtiW3Taf1GuwVz3Z";
- const rawAmount = 1000000000000000000n;
- const bridgedAmount = new DecimalBigNumber(
- rawAmount,
- chainProperties?.properties.tokenDecimals ?? 18
- );
+ const bridgedAmount = useMemo(() => {
+ if (!functionArgs) return null;
+ return new DecimalBigNumber(
+ functionArgs.amount,
+ chainProperties?.properties.tokenDecimals ?? 18
+ )
+ }, [functionArgs, chainProperties]);
const estimatedFee = useMemo(() => {
- if (!networkData) return null
+ if (!networkData) return null;
return networkData.incoming_share / 10000000;
}, [networkData]);
const estimatedReceived = useMemo(() => {
+ if (!functionArgs) return null;
+
const hundredPercent = 1000000000;
const incomingShare = networkData ? networkData.incoming_share : 0;
const result = hundredPercent - incomingShare;
- const value = Number(rawAmount) * result / hundredPercent;
+ const value = Number(functionArgs.amount) * result / hundredPercent;
return new DecimalBigNumber(value, chainProperties?.properties.tokenDecimals ?? 18)
- }, [networkData, chainProperties]);
+ }, [functionArgs, networkData, chainProperties]);
const finalization = useMemo(() => {
if (!receipt || !blockNumber || !networkData) return null;
@@ -142,32 +167,124 @@ const BridgeInModal = ({ chainId, isOpen, close, config, historyRecord }) => {
return Math.max(0, Number(finalityDelay + receipt.blockNumber - blockNumber));
}, [receipt, blockNumber, networkData]);
- // TODO: networkId, session,
- // TODO: EvmThreadProof: slotIndex global % SLOTS, proofIndex % SLOTS, receiver sf, amount raw
+ const rootIncluded = useMemo(() => {
+ if (!loomState || !receipt) return false;
+ return evmMerkleRoot === loomState.root_hash && receipt.blockNumber < loomState.at_block;
+ }, [loomState, receipt, evmMerkleRoot]);
- // TODO: compute locally the arguments
- // https://git.ghostchain.io/ghostchain/ghost-node/src/commit/cc7721e3899f9740e35f65f3feb4cdc621926166/pallets/weaver/src/types.rs#L400
+ const weaverArgs = useMemo(() => {
+ if (!bridgeInInfo) return null;
+ return {
+ sessionIndex: bridgeInInfo.args.session,
+ slotIndex: bridgeInInfo.args.index % maximumSlots,
+ valueIndex: bridgeInInfo.args.index / maximumSlots,
+ };
+ }, [maximumSlots, bridgeInInfo]);
+
+ const pulledThreadKey = useMemo(() => {
+ if (!chainId || !weaverArgs) return "0x0000000000000000000000000000000000000000000000000000000000000000";
+
+ const padU64 = (val) => "0x" + BigInt(val).toString(16).padStart(64, "0");
+
+ const metadata = [
+ padU64(weaverArgs.sessionIndex),
+ padU64(weaverArgs.valueIndex),
+ padU64(weaverArgs.slotIndex),
+ padU64(chainId)
+ ].map(hexToBytes);
+
+ let currentHash = metadata[0];
+
+ for (let i = 1; i < metadata.length; i++) {
+ const combined = new Uint8Array(64);
+ combined.set(currentHash, 0);
+ combined.set(metadata[i], 32);
+ currentHash = blake2AsU8a(combined);
+ }
+
+ return bytesToHex(currentHash);
+ }, [chainId, weaverArgs]);
+
+ const { data: threadPulled, mutate: threadPulledMutate } = useSWR(
+ casperApi ? ['threadPulled', casperApi, pulledThreadKey, chainId] : null,
+ async () => {
+ const key = BigInt(chainId);
+ const res = await casperApi.query.GhostWeaver.PulledThreads.getValue(pulledThreadKey);
+ return res;
+ },
+ {
+ revalidateOnFocus: true
+ }
+ );
+
+ const pullCurrentThread = async () => {
+ setIsPending(true);
+
+ try {
+ const txArgs = {
+ network_id: BigInt(chainId),
+ session: weaverArgs.sessionIndex,
+ thread_proof: {
+ type: "EvmThreadProof",
+ value: {
+ slot_index: weaverArgs.valueIndex,
+ proof_index: weaverArgs.slotIndex,
+ receiver: functionArgs.receiver,
+ amount: functionArgs.amount,
+ proof: merkleProof,
+ slots: slotValues,
+ }
+ }
+ };
+
+ const tx = casperApi.tx.GhostWeaver.pull_thread(txArgs);
+ const bareTx = await tx.getBareTx();
+
+ await executePapiBareTransaction({ client, bareTx });
+ await threadPulledMutate();
+ } finally {
+ setIsPending(false);
+ }
+ }
return (
- {bridgeInInfo && "Bridge-In"}
- {!bridgeInInfo && "Parsing Transaction Hash"}
- >
+ headerContent={
+
+ {bridgeInInfo
+ ?
+ Bridge-In
+
+
+ {bridgeInInfo ? shorten(historyRecord.txHash, 7, -5) : ""}
+
+
+
+ : Parsing Transaction Hash
+ }
+
}
- onClose={() => close()}
+ onClose={() => isPending ? {} : close()}
>
{
/>
copyToClipboard(receiver, 0)}
+ onClick={() => functionArgs?.receiver ? copyToClipboard(functionArgs.receiver, 0) : {}}
>
- {bridgeInInfo ? shorten(receiver, 11, -8) : }
+ {functionArgs?.receiver ? shorten(functionArgs.receiver, 11, -8) : }
{
:
}
/>
-
-
-
- {bridgeInInfo ? shorten(historyRecord.txHash, 11, -8) : }
-
-
- :
- }
- />
@@ -250,9 +354,14 @@ const BridgeInModal = ({ chainId, isOpen, close, config, historyRecord }) => {
}}
>
{formatCurrency(bridgedAmount, 5, chainProperties?.properties.tokenSymbol ?? "")}}
+ balance={bridgedAmount
+ ?
+ {formatCurrency(bridgedAmount.toString(), 5, chainProperties?.properties.tokenSymbol ?? "")}
+
+ :
+ }
/>
{
{formatCurrency(estimatedReceived.toString(), 5, chainProperties?.properties.tokenSymbol ?? "")}}
+ balance={estimatedReceived
+ ?
+ {formatCurrency(estimatedReceived.toString(), 5, chainProperties?.properties.tokenSymbol ?? "")}
+
+ :
+ }
/>
- Claim Bridge-In
+
+ {isPending
+ ? "Pending..."
+ : threadPulled === null ? "Transaction Claimed" : "Claim Bridge-In"
+ }
+
)
}
-const BridgeInProgress = ({ chainId, casperApi, finalization, blockNumber }) => {
+const BridgeInProgress = ({ chainId, submissionBlock, claimed, casperApi, finalization, loomState, rootIncluded }) => {
const theme = useTheme();
const { data: weaverAuthorities } = useSWR(
@@ -286,6 +409,19 @@ const BridgeInProgress = ({ chainId, casperApi, finalization, blockNumber }) =>
}
);
+ const { data: networkAttestations } = useSWR(
+ casperApi ? ['networkAttestations', casperApi] : null,
+ async () => {
+ const key = BigInt(chainId);
+ const res = await casperApi.query.GhostWeaver.NetworkAttestations.getValue(key);
+ return res;
+ },
+ {
+ refreshInterval: 12000,
+ revalidateOnFocus: true
+ }
+ );
+
const { data: tapestryDraft } = useSWR(
casperApi ? ['tapestryDraft', casperApi, chainId] : null,
async () => {
@@ -312,45 +448,29 @@ const BridgeInProgress = ({ chainId, casperApi, finalization, blockNumber }) =>
}
);
- const { data: loomState } = useSWR(
- casperApi ? ['loomState', casperApi, chainId] : null,
- async () => {
- const key = BigInt(chainId);
- const res = await casperApi.query.GhostWeaver.LoomStates.getValue(key, 0n);
- return res;
- },
- {
- refreshInterval: 12000,
- revalidateOnFocus: true
- }
- );
-
- const rootIncluded = useMemo(() => {
- if (!loomState || !blockNumber) return false;
- if (blockNumber >= loomState.at_block) return true;
- return false;
- });
-
return (
{finalization === null || finalization > 0
?
:
}
-
+
)
}
@@ -374,7 +494,7 @@ const ProofSubmission = ({ theme, claimed, rootIncluded }) => {
width: "35px",
height: "35px",
transformOrigin: "center",
- animation: rootIncluded && 'waitingForClaim 4s ease-in-out infinite',
+ animation: (rootIncluded && !claimed) && 'waitingForClaim 4s ease-in-out infinite',
'@keyframes waitingForClaim': {
'0%, 100%': { opacity: 0.4 },
'50%': { opacity: 1 },
@@ -387,21 +507,22 @@ const ProofSubmission = ({ theme, claimed, rootIncluded }) => {
{rootIncluded
? claimed ? "Success" : "Claim Ready"
- : "Pending..."
+ : "Claim Pending..."
}
)
}
-const RootVerification = ({ theme, tapestryDraft, rootIncluded, totalAuthorities, tapestryStrand }) => {
+const RootVerification = ({ theme, finalization, tapestryDraft, rootIncluded, totalAuthorities, tapestryStrand }) => {
+ const finalizationPending = useMemo(() => finalization > 0, [finalization]);
+
const rootVerificationText = useMemo(() => {
if (rootIncluded) return "Synced";
- if (!tapestryStrand) return "Pending...";
+ if (!tapestryDraft || finalizationPending) return "Pending...";
let maxOnesCount = 0;
-
- tapestryStrand.forEach(item => {
+ tapestryStrand?.forEach(item => {
const dataArray = item[1]?.data || [];
const totalOnes = dataArray.reduce((sum, num) => {
const onesInNumber = num.toString(2).split('1').length - 1;
@@ -414,14 +535,14 @@ const RootVerification = ({ theme, tapestryDraft, rootIncluded, totalAuthorities
});
return `${maxOnesCount} / ${totalAuthorities}`;
- }, [rootIncluded, tapestryStrand, totalAuthorities]);
+ }, [rootIncluded, tapestryDraft, tapestryStrand, totalAuthorities, finalizationPending]);
return (
{
+const SyncTarget = ({ weaverAuthorities, networkAttestations, tapestryDraft, submissionBlock, loomState, rootIncluded, theme }) => {
const syncTargetText = useMemo(() => {
if (rootIncluded) return "Synced";
- if (!tapestryDraft || !loomState) return "Pending...";
- if (blockNumber >= loomState.at_block) return "Synced"
- return `${formatNumber(Number(blockNumber - loomState.at_block))} block behind`;
- }, [rootIncluded, tapestryDraft, loomState, blockNumber]);
+ if (!tapestryDraft || !loomState) {
+ const total = weaverAuthorities ? weaverAuthorities.length : 0;
+ const count = networkAttestations
+ ? networkAttestations.filter(v => v !== undefined).length
+ : 0;
+ return `${count} / ${total}`
+ }
+
+ if (submissionBlock < loomState.at_block) return "Synced";
+
+ return `${formatNumber(Number(submissionBlock - loomState.at_block))} block behind`;
+ }, [rootIncluded, tapestryDraft, loomState, weaverAuthorities, submissionBlock, networkAttestations]);
return (
{
+const HistoryItem = ({ item, chainId, config, currentTime, eraseRecord, setViewModalOpen, isSmallScreen, finalityDelay }) => {
const theme = useTheme();
const isVerySmallScreen = useMediaQuery("(max-width: 450px)");
@@ -57,19 +60,15 @@ const HistoryItem = ({ item, chainId, config, currentTime, eraseRecord, setViewM
}, [config]);
const chipStatus = useMemo(() => {
- const delay = 3600000; // useEvmNetwork({ evmChainId: chainId })
- const width = isVerySmallScreen ? "21px" : "100px"
+ const delay = Number(finalityDelay * networkAvgBlockSpeed(chainId)) * 1000;
+ const width = isVerySmallScreen ? "21px" : "100px";
if (item.submitted + delay > currentTime) {
return
}
- // darkGray for final
-
- return
- }, [item, currentTime, isVerySmallScreen])
-
- // TODO: put correct values into TokenStack based on type
+ return
+ }, [item, currentTime, isVerySmallScreen, finalityDelay])
if (isSmallScreen || isVerySmallScreen) {
return (
@@ -153,13 +152,30 @@ const HistoryList = ({ chainId, storedTransactions, historyType, config, eraseRe
const [viewModalOpen, setViewModalOpen] = useState(null);
const { CONSTANTS } = useLocalStorage();
+ const { casperApi } = useUnstableProvider();
+
+ const { data: networkData, error, isLoading } = useSWR(
+ casperApi ? ['networkData', casperApi, chainId] : null,
+ async () => {
+ const key = BigInt(chainId);
+ const res = await casperApi.query.GhostNetworks.Networks.getValue(key);
+ return res;
+ },
+ {
+ revalidateOnFocus: true
+ }
+ );
+
+ const finalityDelay = useMemo(() => {
+ return networkData ? networkData.finality_delay : 420n;
+ }, [networkData]);
useEffect(() => {
const intervalId = setInterval(() => {
setCurrentTime(Date.now());
- }, 5000);
+ }, 1000);
return () => clearInterval(intervalId);
- }, [])
+ }, []);
const historyContainer = useMemo(() => {
if (isSmallScreen) {
@@ -173,6 +189,7 @@ const HistoryList = ({ chainId, storedTransactions, historyType, config, eraseRe
config={config}
chainId={chainId}
currentTime={currentTime}
+ finalityDelay={finalityDelay}
eraseRecord={() => eraseRecord(i)}
setViewModalOpen={() => setViewModalOpen(i)}
isSmallScreen={isSmallScreen}
@@ -201,6 +218,7 @@ const HistoryList = ({ chainId, storedTransactions, historyType, config, eraseRe
item={item}
config={config}
currentTime={currentTime}
+ finalityDelay={finalityDelay}
eraseRecord={() => eraseRecord(i)}
setViewModalOpen={() => setViewModalOpen(i)}
isSmallScreen={isSmallScreen}
diff --git a/src/hooks/ghost/UnstableProvider.jsx b/src/hooks/ghost/UnstableProvider.jsx
index 9abac39..0da6a07 100644
--- a/src/hooks/ghost/UnstableProvider.jsx
+++ b/src/hooks/ghost/UnstableProvider.jsx
@@ -11,44 +11,7 @@ import { startFromWorker } from "polkadot-api/smoldot/from-worker"
import SmWorker from "polkadot-api/smoldot/worker?worker"
import { casper as casperMetadata } from "@polkadot-api/descriptors";
-
-// TODO: move me to file
-const chainSpec = `{
- "badBlocks": null,
- "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/bootnode70.chain.ghostchain.io/tcp/30334/p2p/12D3KooWP3h5dSdqcpvsCr7fp1jyfqDj291QDZ68a4gY6VQ8T1nW",
- "/dns/bootnode70.chain.ghostchain.io/tcp/30335/ws/p2p/12D3KooWP3h5dSdqcpvsCr7fp1jyfqDj291QDZ68a4gY6VQ8T1nW",
- "/dns/bootnode70.chain.ghostchain.io/tcp/30336/wss/p2p/12D3KooWP3h5dSdqcpvsCr7fp1jyfqDj291QDZ68a4gY6VQ8T1nW"
- ],
- "chainType": "Live",
- "forkBlocks": null,
- "genesis": {
- "stateRootHash": "0xb4784a3c8f2f29ba3f191d563f1166f08f4e0337e114679c0f24ef77d06e10e4"
- },
- "id": "casper_staging_testnet",
- "lightSyncState": {
- "babeEpochChanges": "0x042c315b655c7144df4bfdc41837eeacb84d80aee06eaba51f6941a0c7a26cb1bf5d090000015e9de408000000000ea2e40800000000049b2e88b8ce43a677c1ca90f62134800b7e510c0c353dad719cad82faf6136cfb0d0e0000010ea2e40800000000bea6e408000000000000082c315b655c7144df4bfdc41837eeacb84d80aee06eaba51f6941a0c7a26cb1bf5d0900000103000000000000005e9de40800000000b00400000000000014daaaaab6a6e574099e24ae9bb75b543610edef9d374fa85a378edb573b47615f010000000000000074fa7381a7a74b316afb6793a00387eed9d95d46a69866cbb316b5d9c918af0e0100000000000000e0f0a776ecc9fa5e1f22e2fa001fe3fba5aea52b9444bc894b45589d4213247501000000000000006c4dd88b43e2011cf9a6a73d53446336ac9e04cdd4ca23587df63187ac455e4901000000000000003c944c704cae203619b9e7a5a4b6742736da6a8e76c762291bebdc7652cfec2f01000000000000002ff7c765405e9c11a59525a08173bb1fa59e5e281090f8e4d1824469f513d0cc01000000000000000400000000000000029b2e88b8ce43a677c1ca90f62134800b7e510c0c353dad719cad82faf6136cfb0d0e00000104000000000000000ea2e40800000000b00400000000000014daaaaab6a6e574099e24ae9bb75b543610edef9d374fa85a378edb573b47615f010000000000000074fa7381a7a74b316afb6793a00387eed9d95d46a69866cbb316b5d9c918af0e0100000000000000e0f0a776ecc9fa5e1f22e2fa001fe3fba5aea52b9444bc894b45589d4213247501000000000000006c4dd88b43e2011cf9a6a73d53446336ac9e04cdd4ca23587df63187ac455e4901000000000000003c944c704cae203619b9e7a5a4b6742736da6a8e76c762291bebdc7652cfec2f0100000000000000dea465ff8bf16938cc9ead89bf9fdb5d9c495fad61fd952b2067ebdc51c4e1c10100000000000000040000000000000002",
- "babeFinalizedBlockWeight": 1039,
- "finalizedBlockHeader": "0x6ed6c2debc2e5fd4e7db0d14426d1b904e6c101f2c62a71b8e891c217ba79079f9433280c0257a860f793833bd6bed6b9f86094400aa8e5ea24a81752f0f046ff5ed560f7231760c622ae35d3968fbab033ed34d950b57a7937ae45c411d1d384748080642414245b50103000000004fa0e40800000000a2c7d71b4e1f94c0208c9730ab93955b08c8d7ac86d2fc6ec5f62cc3272a5f1ec0e28c798461372109ee3e12f03ff66ea5ad9cac5b1c37d7280d2333d0226f0865ea77a801bad79785a8ca83c87f0dbf1e36936a6f65cd7b125a41c52f38f80105424142450101caaa6fe5f9df57930e2ac08aef43b75d5477d410c73c3a8e6dea9d5503b9fd25eae1d12b6604194389e22f3f4265d581eb162d1dc5a9034247fe33765dddbe8c",
- "grandpaAuthoritySet": "0x1455446f9a7aa99ced06b317c80ce90d56b84e56526775683af2525969e8da0b640100000000000000236d2fa03f4ed8cb65de7e514d7540159b328f1c170dd402b094ad7fbf5472180100000000000000b63c5a0cf342b9b04931bc8ed74d7d0165ab99ab5f8a4514797d4b299a4501fe01000000000000008f9ea20bf4a807a8e710f7559dece86e94672b5b361de157bdaa5c1f37849f8d01000000000000000aa3a88f6b777c95c3dfe7e997b76798413f16aa325f34824cae0c9102b281d5010000000000000000000000000000000001fe1000000000"
- },
- "name": "Casper Staging Testnet",
- "properties": {
- "ss58Format": 1996,
- "tokenDecimals": 18,
- "tokenSymbol": "CSPR"
- },
- "protocolId": "cspr",
- "telemetryEndpoints": [
- [
- "/dns/telemetry.ghostchain.io/tcp/443/x-parity-wss/%2Fsubmit%2F",
- 0
- ]
- ]
-}`;
+import { casperChainSpec } from "./chainSpecs";
const GHOST_CONNECT_EXTENSION = "GHOST Connect Browser Extension"
const MAX_BLOCK_TIMEOUT = 15000
@@ -71,7 +34,7 @@ export const UnstableProviderProvider = ({ children }) => {
try {
const smoldot = startFromWorker(new SmWorker());
provider = createClientOther(
- getSmProvider(() => smoldot.addChain({ chainSpec })),
+ getSmProvider(() => smoldot.addChain({ chainSpec: casperChainSpec })),
)
if (isMounted) {
diff --git a/src/hooks/ghost/chainSpecs.js b/src/hooks/ghost/chainSpecs.js
new file mode 100644
index 0000000..62b3023
--- /dev/null
+++ b/src/hooks/ghost/chainSpecs.js
@@ -0,0 +1,36 @@
+export const casperChainSpec = `{
+ "badBlocks": null,
+ "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/bootnode70.chain.ghostchain.io/tcp/30334/p2p/12D3KooWP3h5dSdqcpvsCr7fp1jyfqDj291QDZ68a4gY6VQ8T1nW",
+ "/dns/bootnode70.chain.ghostchain.io/tcp/30335/ws/p2p/12D3KooWP3h5dSdqcpvsCr7fp1jyfqDj291QDZ68a4gY6VQ8T1nW",
+ "/dns/bootnode70.chain.ghostchain.io/tcp/30336/wss/p2p/12D3KooWP3h5dSdqcpvsCr7fp1jyfqDj291QDZ68a4gY6VQ8T1nW"
+ ],
+ "chainType": "Live",
+ "forkBlocks": null,
+ "genesis": {
+ "stateRootHash": "0xb4784a3c8f2f29ba3f191d563f1166f08f4e0337e114679c0f24ef77d06e10e4"
+ },
+ "id": "casper_staging_testnet",
+ "lightSyncState": {
+ "babeEpochChanges": "0x042c315b655c7144df4bfdc41837eeacb84d80aee06eaba51f6941a0c7a26cb1bf5d090000015e9de408000000000ea2e40800000000049b2e88b8ce43a677c1ca90f62134800b7e510c0c353dad719cad82faf6136cfb0d0e0000010ea2e40800000000bea6e408000000000000082c315b655c7144df4bfdc41837eeacb84d80aee06eaba51f6941a0c7a26cb1bf5d0900000103000000000000005e9de40800000000b00400000000000014daaaaab6a6e574099e24ae9bb75b543610edef9d374fa85a378edb573b47615f010000000000000074fa7381a7a74b316afb6793a00387eed9d95d46a69866cbb316b5d9c918af0e0100000000000000e0f0a776ecc9fa5e1f22e2fa001fe3fba5aea52b9444bc894b45589d4213247501000000000000006c4dd88b43e2011cf9a6a73d53446336ac9e04cdd4ca23587df63187ac455e4901000000000000003c944c704cae203619b9e7a5a4b6742736da6a8e76c762291bebdc7652cfec2f01000000000000002ff7c765405e9c11a59525a08173bb1fa59e5e281090f8e4d1824469f513d0cc01000000000000000400000000000000029b2e88b8ce43a677c1ca90f62134800b7e510c0c353dad719cad82faf6136cfb0d0e00000104000000000000000ea2e40800000000b00400000000000014daaaaab6a6e574099e24ae9bb75b543610edef9d374fa85a378edb573b47615f010000000000000074fa7381a7a74b316afb6793a00387eed9d95d46a69866cbb316b5d9c918af0e0100000000000000e0f0a776ecc9fa5e1f22e2fa001fe3fba5aea52b9444bc894b45589d4213247501000000000000006c4dd88b43e2011cf9a6a73d53446336ac9e04cdd4ca23587df63187ac455e4901000000000000003c944c704cae203619b9e7a5a4b6742736da6a8e76c762291bebdc7652cfec2f0100000000000000dea465ff8bf16938cc9ead89bf9fdb5d9c495fad61fd952b2067ebdc51c4e1c10100000000000000040000000000000002",
+ "babeFinalizedBlockWeight": 1039,
+ "finalizedBlockHeader": "0x6ed6c2debc2e5fd4e7db0d14426d1b904e6c101f2c62a71b8e891c217ba79079f9433280c0257a860f793833bd6bed6b9f86094400aa8e5ea24a81752f0f046ff5ed560f7231760c622ae35d3968fbab033ed34d950b57a7937ae45c411d1d384748080642414245b50103000000004fa0e40800000000a2c7d71b4e1f94c0208c9730ab93955b08c8d7ac86d2fc6ec5f62cc3272a5f1ec0e28c798461372109ee3e12f03ff66ea5ad9cac5b1c37d7280d2333d0226f0865ea77a801bad79785a8ca83c87f0dbf1e36936a6f65cd7b125a41c52f38f80105424142450101caaa6fe5f9df57930e2ac08aef43b75d5477d410c73c3a8e6dea9d5503b9fd25eae1d12b6604194389e22f3f4265d581eb162d1dc5a9034247fe33765dddbe8c",
+ "grandpaAuthoritySet": "0x1455446f9a7aa99ced06b317c80ce90d56b84e56526775683af2525969e8da0b640100000000000000236d2fa03f4ed8cb65de7e514d7540159b328f1c170dd402b094ad7fbf5472180100000000000000b63c5a0cf342b9b04931bc8ed74d7d0165ab99ab5f8a4514797d4b299a4501fe01000000000000008f9ea20bf4a807a8e710f7559dece86e94672b5b361de157bdaa5c1f37849f8d01000000000000000aa3a88f6b777c95c3dfe7e997b76798413f16aa325f34824cae0c9102b281d5010000000000000000000000000000000001fe1000000000"
+ },
+ "name": "Casper Staging Testnet",
+ "properties": {
+ "ss58Format": 1996,
+ "tokenDecimals": 18,
+ "tokenSymbol": "CSPR"
+ },
+ "protocolId": "cspr",
+ "telemetryEndpoints": [
+ [
+ "/dns/telemetry.ghostchain.io/tcp/443/x-parity-wss/%2Fsubmit%2F",
+ 0
+ ]
+ ]
+}`;
diff --git a/src/hooks/helpers.js b/src/hooks/helpers.js
index d1cabaa..6f8dd36 100644
--- a/src/hooks/helpers.js
+++ b/src/hooks/helpers.js
@@ -259,3 +259,40 @@ export const executeOnChainTransaction = async ({
return undefined;
}
}
+
+export const executePapiBareTransaction = async ({
+ client,
+ bareTx,
+ messages = {
+ loadingMsg: "Broadcasting transaction to available peers...",
+ inBlockMsg: "Transaction included in block, waiting for finality...",
+ successMsg: "Transaction successfully finalized!",
+ errorMsg: "Transaction failed.",
+ },
+}) => {
+ try {
+ return await new Promise((resolve, reject) => {
+ const subscription = client.submitAndWatch(bareTx).subscribe({
+ next: (event) => {
+ if (event.type === "broadcasted") {
+ toast.success(messages.loadingMsg);
+ } else if (event.type === "inBestBlock") {
+ toast.success(messages.inBlockMsg);
+ } else if (event.type === "finalized") {
+ toast.success(messages.successMsg);
+ subscription.unsubscribe();
+ resolve(event);
+ }
+ },
+ error: (err) => {
+ toast.error(`${messages.errorMsg} ${err.message || err}`);
+ subscription.unsubscribe();
+ reject(err);
+ },
+ });
+ });
+ } catch (err) {
+ console.error("Bare PAPI Transaction failed:", err);
+ return undefined;
+ }
+};
diff --git a/src/hooks/staking/index.js b/src/hooks/staking/index.js
index e5ae28b..538eb24 100644
--- a/src/hooks/staking/index.js
+++ b/src/hooks/staking/index.js
@@ -17,38 +17,68 @@ import { DecimalBigNumber } from "../../helpers/DecimalBigNumber";
import { executeOnChainTransaction, getTokenDecimals } from "../helpers";
import { useCirculatingSupply } from "../tokens";
-export const useWeaverState = (chainId, blockNumber, context) => {
- const { session, index } = context;
+export const useWeaverMaximumSlots = (chainId) => {
const { gatekeeperAddress } = useGatekeeperAddress(chainId);
- const { data: merkleRoot } = useReadContract({
+ const { data } = useReadContract({
abi: GatekeeperAbi,
address: gatekeeperAddress,
- functionName: "getRoot",
- args: [index, blockNumber],
- scopeKey: `getRoot-${chainId}-${session}-${gatekeeperAddress}`,
+ functionName: "SLOTS",
+ scopeKey: `SLOTS-${chainId}-${gatekeeperAddress}`,
chainId: chainId,
});
- const { data: merkleProof } = useReadContract({
+ return { maximumSlots: data ? data : 1n };
+}
+
+export const useWeaverRoot = (chainId, blockNumber, context) => {
+ const session = context?.session;
+ const { gatekeeperAddress } = useGatekeeperAddress(chainId);
+
+ const { data } = useReadContract({
+ abi: GatekeeperAbi,
+ address: gatekeeperAddress,
+ functionName: "getRoot",
+ args: [session, blockNumber],
+ scopeKey: `getRoot-${chainId}-${session}-${blockNumber}-${gatekeeperAddress}`,
+ chainId: chainId,
+ });
+
+ return { merkleRoot: data?.at(0), nextSession: data?.at(1) }
+}
+
+export const useWeaverProof = (chainId, blockNumber, context) => {
+ const index = context?.index;
+ const session = context?.session;
+ const { gatekeeperAddress } = useGatekeeperAddress(chainId);
+
+ const { data: merkleProofInner } = useReadContract({
abi: GatekeeperAbi,
address: gatekeeperAddress,
functionName: "getProof",
args: [index, session, blockNumber],
- scopeKey: `getProof-${chainId}-${session}-${gatekeeperAddress}`,
+ scopeKey: `getProof-${chainId}-${session}-${index}-${blockNumber}-${gatekeeperAddress}`,
chainId: chainId,
});
- const { data: slotValues } = useReadContract({
+ return { merkleProof: merkleProofInner }
+}
+
+export const useWeaverSlotValues = (chainId, blockNumber, context) => {
+ const index = context?.index;
+ const session = context?.session;
+ const { gatekeeperAddress } = useGatekeeperAddress(chainId);
+
+ const { data: slotValuesInner } = useReadContract({
abi: GatekeeperAbi,
address: gatekeeperAddress,
functionName: "getSlotValues",
args: [index, session, blockNumber],
- scopeKey: `getSlotValues-${chainId}-${session}-${gatekeeperAddress}`,
+ scopeKey: `getSlotValues-${chainId}-${session}-${index}-${blockNumber}-${gatekeeperAddress}`,
chainId: chainId,
});
- return null
+ return { slotValues: slotValuesInner }
}
export const useGatekeeperMinimum = (chainId) => {