diff --git a/.nvmrc b/.nvmrc index 91f7588a..bb8c76c6 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v18.20.0 +v22.11.0 diff --git a/package.json b/package.json index e6f2db0c..432d1a4b 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "@material-ui/core": "^4.11.3", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "^4.0.0-alpha.57", - "@microsoft/signalr": "^5.0.9", + "@microsoft/signalr": "9.0.6", "@mui/icons-material": "^5.14.14", "@mui/material": "^5.14.14", "@mui/x-date-pickers": "^5.0.2", @@ -47,7 +47,6 @@ "assert-never": "^1.3.0", "bignumber.js": "^9.0.1", "blockies-ts": "^1.0.0", - "caniuse-lite": "", "connectkit": "^1.8.2", "crypto-browserify": "^3.12.0", "dayjs": "^1.11.11", diff --git a/src/modules/explorer/components/DAOStatsRow.tsx b/src/modules/explorer/components/DAOStatsRow.tsx index e692e144..2b347043 100644 --- a/src/modules/explorer/components/DAOStatsRow.tsx +++ b/src/modules/explorer/components/DAOStatsRow.tsx @@ -145,7 +145,7 @@ const DAOStatsRowTezos = () => { Current Cycle - {cycleInfo?.currentCycle} + {cycleInfo?.currentCycle ?? "-"} diff --git a/src/services/bakingBad/context/TZKTSubscriptions.tsx b/src/services/bakingBad/context/TZKTSubscriptions.tsx index 4384087d..0e3f77c9 100644 --- a/src/services/bakingBad/context/TZKTSubscriptions.tsx +++ b/src/services/bakingBad/context/TZKTSubscriptions.tsx @@ -31,16 +31,24 @@ const TZKTSubscriptionsProvider: React.FC = ({ children }) => { useEffect(() => { if (network.startsWith("etherlink")) return ;(async () => { - socketRef.current = new HubConnectionBuilder().withUrl(getUrl(network)).build() + socketRef.current = new HubConnectionBuilder().withUrl(getUrl(network)).withAutomaticReconnect().build() - await socketRef.current.start() + try { + await socketRef.current.start() + } catch (e) { + console.warn("TZKT SignalR start failed", e) + return + } - // listen for incoming message socketRef.current.on("blocks", (blockMessage: BlockMessage) => { setBlock(blockMessage.state) }) - await socketRef.current.invoke("SubscribeToBlocks") + try { + await socketRef.current.invoke("SubscribeToBlocks") + } catch (e) { + console.warn("TZKT SignalR subscribe failed", e) + } })() return () => { diff --git a/src/services/services/dao/hooks/useDAO.ts b/src/services/services/dao/hooks/useDAO.ts index 3c6548c6..916a627d 100644 --- a/src/services/services/dao/hooks/useDAO.ts +++ b/src/services/services/dao/hooks/useDAO.ts @@ -10,7 +10,7 @@ import { unpackExtraNumValue, CycleInfo } from "services/contracts/baseDAO" import { LambdaDAO } from "services/contracts/baseDAO/lambdaDAO" import { parseUnits } from "services/contracts/utils" import { getDAO } from "services/services/dao/services" -import { useBlockchainInfo } from "../../../contracts/baseDAO/hooks/useBlockchainInfo" +import { getNetworkStats, getNetworkHead } from "../../../bakingBad/stats" import { fetchLiteData } from "services/services/lite/lite-services" import { EtherlinkContext } from "services/wagmi/context" @@ -20,7 +20,6 @@ export const useDAO = (address: string) => { const [daoData, setDaoData] = useState(null) const [cycleInfo, setCycleInfo] = useState() const { network } = useTezos() - const { data: blockchainInfo } = useBlockchainInfo() const { daos: etherlinkOnchainDAOs, selectDao: selectEtherlinkDao, @@ -120,26 +119,61 @@ export const useDAO = (address: string) => { useEffect(() => { ;(async () => { - if ((data as any)?.address === "onchain-etherlink") { - console.log("No cycle info for etherlink") - return - } else if (data && blockchainInfo) { - const blockTimeAverage = blockchainInfo.constants.timeBetweenBlocks - const blocksFromStart = block - data.data.start_level - const periodsFromStart = Math.floor(blocksFromStart / Number(data.data.period)) - const type = periodsFromStart % 2 == 0 ? "voting" : "proposing" - const blocksLeft = Number(data.data.period) - (blocksFromStart % Number(data.data.period)) + if (!data) return + const daoNetwork = data.data.network as Network + if ((data as any)?.address === "onchain-etherlink" || daoNetwork?.startsWith("etherlink")) return - setCycleInfo({ - blocksLeft, - type, - timeEstimateForNextBlock: blockTimeAverage, - currentCycle: periodsFromStart, - currentLevel: block - }) + let effectiveBlock = 0 + if (network === daoNetwork && block > 0) { + effectiveBlock = block + } else { + try { + effectiveBlock = await getNetworkHead(daoNetwork) + } catch (e) { + return + } } + if (effectiveBlock <= 0) return + + let blockTimeAverage = 30 + try { + const stats = await getNetworkStats(daoNetwork) + blockTimeAverage = stats.constants.timeBetweenBlocks + } catch {} + + const period = Number(data.data.period) + // Include flush delay in the total cycle length + const proposalFlushLevel = Number(data.data.proposal_flush_level) + const proposalExpiredLevel = Number(data.data.proposal_expired_level) + const flushDelayBlocks = Math.max(proposalFlushLevel - 2 * period, 0) + const proposalBlocksToExpire = Math.max(proposalExpiredLevel - proposalFlushLevel, 0) + const totalCycleBlocks = period + flushDelayBlocks + proposalBlocksToExpire + // Debug logs for cycle calculation inputs + console.log("useDAO.ts cycle inputs", { + CurrentBlock: effectiveBlock, + BlockAtWhichDaoMinted: data.data.start_level, + TotalDaoLifeCycleBlocks: totalCycleBlocks + }) + const blocksFromStart = effectiveBlock - data.data.start_level + if (blocksFromStart < 0) return + if (totalCycleBlocks <= 0) return + // Use full cycle (voting period + flush delay) for cycle count + const cyclesFromStart = Math.floor(blocksFromStart / totalCycleBlocks) + // Keep UI period type based on the current voting/proposing period only + const periodsFromStartForType = Math.floor(blocksFromStart / period) + const type = periodsFromStartForType % 2 === 0 ? "voting" : "proposing" + // Blocks left in the current voting/proposal period + const blocksLeft = period - (blocksFromStart % period) + + setCycleInfo({ + blocksLeft, + type, + timeEstimateForNextBlock: blockTimeAverage, + currentCycle: cyclesFromStart, + currentLevel: effectiveBlock + }) })() - }, [data, blockchainInfo, block, network]) + }, [data, block, network]) useEffect(() => { const dao = etherlinkDaoSelected diff --git a/yarn.lock b/yarn.lock index ad84f74e..914f887f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3235,16 +3235,16 @@ semver "^7.5.4" uuid "^9.0.1" -"@microsoft/signalr@^5.0.9": - version "5.0.17" - resolved "https://registry.yarnpkg.com/@microsoft/signalr/-/signalr-5.0.17.tgz#c6d3038538b8e21b95a012a7eaa22f44d5cc72f4" - integrity sha512-zTjFxjh67WWCe35ZipsqkktM5mM+MsckyyI2ZvFmYWR7ibpUoAyZI1DFdYfwXfsyBdlykDaW84o9lrB+9tDpaA== +"@microsoft/signalr@9.0.6": + version "9.0.6" + resolved "https://registry.yarnpkg.com/@microsoft/signalr/-/signalr-9.0.6.tgz#67dd074941c2ec2b857f607504c920a0c6274e27" + integrity sha512-DrhgzFWI9JE4RPTsHYRxh4yr+OhnwKz8bnJe7eIi7mLLjqhJpEb62CiUy/YbFvLqLzcGzlzz1QWgVAW0zyipMQ== dependencies: abort-controller "^3.0.0" - eventsource "^1.0.7" - fetch-cookie "^0.7.3" + eventsource "^2.0.2" + fetch-cookie "^2.0.3" node-fetch "^2.6.7" - ws "^6.0.0" + ws "^7.5.10" "@motionone/animation@^10.12.0", "@motionone/animation@^10.15.1", "@motionone/animation@^10.18.0": version "10.18.0" @@ -5967,11 +5967,6 @@ astral-regex@^2.0.0: resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== -async-limiter@~1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" - integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== - async-mutex@^0.2.6: version "0.2.6" resolved "https://registry.yarnpkg.com/async-mutex/-/async-mutex-0.2.6.tgz#0d7a3deb978bc2b984d5908a2038e1ae2e54ff40" @@ -6655,20 +6650,10 @@ caniuse-api@^3.0.0: lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-lite@: - version "1.0.30001651" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001651.tgz#52de59529e8b02b1aedcaaf5c05d9e23c0c28138" - integrity sha512-9Cf+Xv1jJNe1xPZLGuUXLNkE1BoDkqRqYyFJ9TDYSqhduqA4hu4oR9HluGoWYQC/aj8WHjsGVV+bwkh0+tegRg== - -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001587, caniuse-lite@^1.0.30001646, caniuse-lite@^1.0.30001663: - version "1.0.30001668" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001668.tgz#98e214455329f54bf7a4d70b49c9794f0fbedbed" - integrity sha512-nWLrdxqCdblixUO+27JtGJJE/txpJlyUy5YN1u53wLZkP0emYCo5zgS6QYft7VUYR42LGgi/S5hdLZTrnyIddw== - -caniuse-lite@^1.0.30001599: - version "1.0.30001600" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001600.tgz#93a3ee17a35aa6a9f0c6ef1b2ab49507d1ab9079" - integrity sha512-+2S9/2JFhYmYaDpZvo0lKkfvuKIglrx68MwOBqMGHhQsNkLjB5xtc/TGoEPs+MxjSyN/72qer2g97nzR641mOQ== +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001587, caniuse-lite@^1.0.30001599, caniuse-lite@^1.0.30001646, caniuse-lite@^1.0.30001663: + version "1.0.30001741" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001741.tgz" + integrity sha512-QGUGitqsc8ARjLdgAfxETDhRbJ0REsP6O3I96TAth/mVjh2cYzN2u+3AzPP3aVSm2FehEItaJw1xd+IGBXWeSw== case-sensitive-paths-webpack-plugin@^2.4.0: version "2.4.0" @@ -8305,11 +8290,6 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" -es6-denodeify@^0.1.1: - version "0.1.5" - resolved "https://registry.yarnpkg.com/es6-denodeify/-/es6-denodeify-0.1.5.tgz#31d4d5fe9c5503e125460439310e16a2a3f39c1f" - integrity sha512-731Rf4NqlPvhkT1pIF7r8vZxESJlWocNpXLuyPlVnfEGXlwuJaMvU5WpyyDjpudDC2cgXVX849xljzvQqBg1QQ== - escalade@^3.1.1, escalade@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" @@ -8829,10 +8809,10 @@ events@3.3.0, events@^3.2.0, events@^3.3.0: resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== -eventsource@^1.0.7: - version "1.1.2" - resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-1.1.2.tgz#bc75ae1c60209e7cb1541231980460343eaea7c2" - integrity sha512-xAH3zWhgO2/3KIniEKYPr8plNSzlGINOUqYj0m0u7AB81iRw8b/3E73W6AuU+6klLbaSFmZnaETQ2lXPfAydrA== +eventsource@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-2.0.2.tgz#76dfcc02930fb2ff339520b6d290da573a9e8508" + integrity sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA== evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: version "1.0.3" @@ -9063,13 +9043,13 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" -fetch-cookie@^0.7.3: - version "0.7.3" - resolved "https://registry.yarnpkg.com/fetch-cookie/-/fetch-cookie-0.7.3.tgz#b8d023f421dd2b2f4a0eca9cd7318a967ed4eed8" - integrity sha512-rZPkLnI8x5V+zYAiz8QonAHsTb4BY+iFowFBI1RFn0zrO343AVp9X7/yUj/9wL6Ef/8fLls8b/vGtzUvmyAUGA== +fetch-cookie@^2.0.3: + version "2.2.0" + resolved "https://registry.yarnpkg.com/fetch-cookie/-/fetch-cookie-2.2.0.tgz#01086b6b5b1c3e08f15ffd8647b02ca100377365" + integrity sha512-h9AgfjURuCgA2+2ISl8GbavpUdR+WGAM2McW/ovn4tVccegp8ZqCKWSBR8uRdM8dDNlx5WdKRWxBYUwteLDCNQ== dependencies: - es6-denodeify "^0.1.1" - tough-cookie "^2.3.3" + set-cookie-parser "^2.4.8" + tough-cookie "^4.0.0" fflate@^0.4.8: version "0.4.8" @@ -14319,7 +14299,7 @@ pseudomap@^1.0.2: resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" integrity sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ== -psl@^1.1.28, psl@^1.1.33: +psl@^1.1.33: version "1.9.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== @@ -15458,6 +15438,11 @@ set-blocking@^2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== +set-cookie-parser@^2.4.8: + version "2.7.1" + resolved "https://registry.yarnpkg.com/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz#3016f150072202dfbe90fadee053573cc89d2943" + integrity sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ== + set-function-length@^1.2.1: version "1.2.2" resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" @@ -16431,14 +16416,6 @@ toposort@^2.0.2: resolved "https://registry.yarnpkg.com/toposort/-/toposort-2.0.2.tgz#ae21768175d1559d48bef35420b2f4962f09c330" integrity sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg== -tough-cookie@^2.3.3: - version "2.5.0" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" - integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== - dependencies: - psl "^1.1.28" - punycode "^2.1.1" - tough-cookie@^4.0.0: version "4.1.4" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.4.tgz#945f1461b45b5a8c76821c33ea49c3ac192c1b36" @@ -17695,14 +17672,7 @@ ws@8.17.1, ws@~8.17.1: resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b" integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ== -ws@^6.0.0: - version "6.2.2" - resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.2.tgz#dd5cdbd57a9979916097652d78f1cc5faea0c32e" - integrity sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw== - dependencies: - async-limiter "~1.0.0" - -ws@^7.4.6: +ws@^7.4.6, ws@^7.5.10: version "7.5.10" resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9" integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==