Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
30063db
Change config
GusevPM Jul 28, 2025
d630f1f
Add Hyperlane page
xdeq Aug 2, 2025
3434858
fix sent&received icon colors
xdeq Aug 4, 2025
e9ab591
fix(hyperlane): add @click.stop for nested links
xdeq Aug 4, 2025
43704f5
feat(hyperlane): add link to cmd
xdeq Aug 4, 2025
4fb1d96
Merge branch 'dev' into dev-mainnet
GusevPM Aug 12, 2025
095dc6a
Merge branch 'dev' into dev-mainnet
GusevPM Aug 12, 2025
e062c5b
Merge pull request #153 from celenium-io/dev
xdeq Aug 13, 2025
26f16ac
Merge pull request #154 from celenium-io/dev
xdeq Aug 13, 2025
963132e
Merge branch 'dev' into dev-mainnet
GusevPM Aug 14, 2025
94b74e0
refactor: optimize line charts
sstark21 Aug 15, 2025
7452905
refactor: optimize bar charts
sstark21 Aug 15, 2025
99272af
Merge branch 'master' into refactor-chart-optimization
sstark21 Aug 15, 2025
46818a9
Add blobstream link
GusevPM Aug 18, 2025
cd53acf
upd(LeftSidebar): Add link to Widgets
xdeq Aug 19, 2025
1958431
fix: bar charts
sstark21 Aug 19, 2025
1a88740
Add widget, info on a block page and mocks
GusevPM Aug 20, 2025
b65549d
Checks restart?
GusevPM Aug 20, 2025
07161a0
fixiki
sstark21 Aug 20, 2025
ef77b74
working draft
sstark21 Aug 22, 2025
4376f1e
fixiki
sstark21 Aug 22, 2025
7507dff
H1 for stats, fix blob fetching
GusevPM Aug 24, 2025
9dbeeba
Rework ranking
GusevPM Aug 24, 2025
82211e3
Fix rank sorting
GusevPM Aug 25, 2025
0bdfdf6
mid of optimization
sstark21 Aug 25, 2025
c291ff1
Add env
GusevPM Aug 25, 2025
e973ad9
prepare rollups
sstark21 Aug 26, 2025
fea738e
add namespaceCharts + addressCharts
sstark21 Aug 26, 2025
c115c66
Change MB to MiB
GusevPM Aug 29, 2025
0e6a013
Handle env vars
GusevPM Aug 29, 2025
557eafc
Handle blobstream env
GusevPM Aug 29, 2025
2c87051
Upd readme (draft)
GusevPM Aug 29, 2025
1295be2
Upd readme
GusevPM Aug 30, 2025
5ebeed2
Add docker files
GusevPM Aug 30, 2025
2b8ee34
Add gh action
GusevPM Aug 30, 2025
502f215
Rm build.yml
GusevPM Aug 30, 2025
5943821
Add docker-build.yml
GusevPM Aug 30, 2025
432a774
Add manual run
GusevPM Aug 30, 2025
4128a01
Upd package.json
GusevPM Aug 30, 2025
d730b49
Create .env
GusevPM Aug 30, 2025
a30b130
Delete .env
GusevPM Aug 30, 2025
72c39fe
Update docker-compose.yml
GusevPM Aug 30, 2025
d57538c
Upd dockerfile
GusevPM Aug 30, 2025
1b18b44
Upd dockerfile
GusevPM Aug 30, 2025
8c2af16
Upd dockerfile
GusevPM Aug 30, 2025
8a1b8c9
Upd docker files
GusevPM Aug 30, 2025
ca6a4da
Upd widget visibility
GusevPM Sep 2, 2025
f93092e
Add color parameter for charts. fix bugs
GusevPM Sep 2, 2025
160a25d
Small change
GusevPM Sep 2, 2025
405e115
Fix mobile view
GusevPM Sep 2, 2025
ef4d420
Upd docker files
GusevPM Sep 4, 2025
710a365
Fix network selector
GusevPM Sep 4, 2025
29a7f50
Merge pull request #155 from celenium-io/refactor-chart-optimization
GusevPM Sep 4, 2025
9205fdf
Merge branch 'proposal-hardfork-widgets' into dev
GusevPM Sep 4, 2025
083fef7
Merge branch 'deployment' into dev
GusevPM Sep 4, 2025
f2db54b
Upd version
GusevPM Sep 4, 2025
3e0f5af
Merge branch 'master' into dev
GusevPM Sep 4, 2025
1871784
Fixik
GusevPM Sep 4, 2025
4990e1b
Merge branch 'dev' into dev-mainnet
GusevPM Sep 4, 2025
d667a4d
Fix data fetching on rollup rank page
GusevPM Sep 4, 2025
bc35b1e
Fix data fetching on rollup rank page
GusevPM Sep 4, 2025
b1927c0
Fixiki
GusevPM Sep 5, 2025
863225d
Merge branch 'dev' into dev-mainnet
GusevPM Sep 5, 2025
93d21b8
dev
xdeq Sep 9, 2025
7fd087c
interop
xdeq Sep 9, 2025
c68bf20
Merge pull request #160 from celenium-io/feat/hyperlane
xdeq Sep 9, 2025
ff3ca5f
Fix rollup colors
GusevPM Sep 10, 2025
512c211
Merge branch 'dev-mainnet' into dev
GusevPM Sep 10, 2025
4dab3cb
Fix dev cfg
GusevPM Sep 10, 2025
6efb4d7
Ref global updates
GusevPM Sep 10, 2025
560c1b1
Hide link to docs
GusevPM Sep 10, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
node_modules/
.nuxt/
.output/
dist/

pnpm-lock.yaml

.git/
.gitignore

.dockerignore
Dockerfile
12 changes: 12 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
FROM node:20

WORKDIR /app

COPY . .

ENV NODE_OPTIONS="--max-old-space-size=4096"

RUN npm install --legacy-peer-deps
RUN npm run build

CMD ["npm", "run", "start"]
55 changes: 54 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ Clone the repository and run on the root folder:
pnpm i
pnpm dev
```

### Node.js Server

When running `nuxt build` with the Node server preset, the result will be an entry point that launches a ready-to-run Node server.
Expand Down Expand Up @@ -41,3 +40,57 @@ module.exports = {
Also, you can use different [presets](https://nuxt.com/docs/getting-started/deployment#hosting-providers). E.g. **Cloudflare Pages**: `cloudflare_pages`.

Note, some providers do not support server-side rendering.

---

### Docker Setup

You can also run the application in Docker.
Build the image and run the container:
```
docker build -t celenium-app
docker run -p 3000:3000 --env-file .env celenium-app
```
Make sure to create a ```.env``` file in the root directory or pass the required environment variables directly with ```-e```.

### Run with Docker Compose
Start with:
```
docker-compose up -d
```

By default:
- Builds the image from the local `Dockerfile`
- Runs the app on `127.0.0.1:3000`
- Automatically restarts the container on failure
- Uses `npm run start` as the startup command
- Limits logs (10 MB per file, max 5 files)

If you want to use a prebuilt image from **GitHub Container Registry**, specify a tag:
- `TAG=latest docker-compose up -d`

---

### Environment Variables

#### Required for App Startup
- **NUXT_PUBLIC_API_DEV** — indexer API (e.g. `https://api.localhost:9876/v1`).
- **NUXT_PUBLIC_WSS_DEV** — webSocket endpoint (e.g. `wss://api.localhost:9876/v1/ws`).
- **NUXT_PUBLIC_SELFHOSTED** — set to `true` when running in self-hosted mode.

#### Blobstream Configuration
- **NUXT_PUBLIC_BLOBSTREAM_MAINNET** — API for blobstream data.

#### Faucet Configuration
- **NUXT_PUBLIC_FAUCET_ADDRESS** — faucet address.
- **NUXT_PUBLIC_FAUCET_MOCHA** — faucet API for the Mocha network.
- **NUXT_PUBLIC_FAUCET_ARABICA** — faucet API for the Arabica network.
- **NUXT_PUBLIC_FAUCET_MAMMOTH** — faucet API for the Mammoth network.

#### External Services Configuration
- **NUXT_PUBLIC_BLOCKSCOUT** — used to check whether a batch exists in Blockscout. If found, a dedicated button will appear on the blob form/page.
- **NUXT_PUBLIC_NODE_STATS** — provides statistics about node types, versions, and geographic distribution across the Celestia ecosystem.
- **NUXT_PUBLIC_QUOTE** — provides price data. It is used to display the current TIA price in the header and to convert all values from TIA to USD.
- **NUXT_PUBLIC_ROLLUP_RANKING** — fetches rollup ranking data displayed on the rollup leaderboard, individual rollup pages, and a dedicated rollup ranking page. The ranking page also includes detailed calculations, as well as repository and commit statistics.
- **NUXT_PUBLIC_GITHUB** — required for retrieving repository statistics on a rollup ranking page.
- **NUXT_PUBLIC_TVL** — provides TVL (Total Value Locked) statistics for rollups and TVS (Total Value Secured) for the Celestia network. These values are displayed in the header, on the statistics page, and on individual rollup pages.
8 changes: 2 additions & 6 deletions app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,13 @@ import { useSettingsStore } from "@/store/settings.store"
import { useEnumStore } from "@/store/enums.store"
import { useLegalStore } from "@/store/legal.store"
import { useNotificationsStore } from "@/store/notifications.store"
import { useActivityStore } from "@/store/activity.store"
const nodeStore = useNodeStore()
const appStore = useAppStore()
const bookmarksStore = useBookmarksStore()
const settingsStore = useSettingsStore()
const enumStore = useEnumStore()
const legalStore = useLegalStore()
const notificationsStore = useNotificationsStore()
const activityStore = useActivityStore()

bookmarksStore.$subscribe((mutation, state) => {
localStorage.setItem("bookmarks", JSON.stringify(state.bookmarks))
Expand All @@ -43,9 +41,6 @@ settingsStore.$subscribe((mutation, state) => {
legalStore.$subscribe((mutation, state) => {
localStorage.setItem("legal", JSON.stringify(state.legal))
})
activityStore.$subscribe((mutation, state) => {
localStorage.setItem("rollups_ranking", JSON.stringify(state.rollups_ranking))
})

appStore.initConstants()

Expand Down Expand Up @@ -98,8 +93,9 @@ onMounted(async () => {
}

settingsStore.init()
activityStore.init()

appStore.initGlobalUpdates()

const runtimeConfig = useRuntimeConfig()
amp.init(runtimeConfig.public.AMP)

Expand Down
13 changes: 13 additions & 0 deletions assets/icons.json

Large diffs are not rendered by default.

17 changes: 16 additions & 1 deletion assets/styles/base.scss
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ $grayscale: (
--block-progress-fill-background: #33a853;
--logo-name: var(--txt-primary);
--bar-fill: rgb(243, 147, 45);
--validator-active: #85f891;
--validator-active: #18d2a5;
--validator-inactive: #1ca7ed;
--validator-jailed: #f8774a;
--supply: #1ca7ed;
Expand Down Expand Up @@ -378,6 +378,21 @@ body {
transform: translateY(-25px);
}

.slide-fade-enter-active,
.slide-fade-leave-active {
transition: all 0.2s ease;
position: absolute;
width: 100%;
}
.slide-fade-enter-from {
opacity: 0;
transform: translateX(-30%);
}
.slide-fade-leave-to {
opacity: 0;
transform: translateX(30%);
}

.table_column_alias {
max-width: 125px;

Expand Down
69 changes: 38 additions & 31 deletions components/Feed.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,38 +3,26 @@
import { DateTime } from "luxon"

/** Services */
import { abbreviate, comma, formatBytes, isMainnet, roundTo } from "@/services/utils"
import { abbreviate, capitilize, comma, formatBytes, isMainnet, roundTo } from "@/services/utils"
import { getRankCategory } from "@/services/constants/rollups"
import { quoteServiceURL, rollupRankingServiceURL } from "@/services/config"

/** UI */
import Tooltip from "@/components/ui/Tooltip.vue"

/** API */
import { fetchRollupsRanking } from "@/services/api/rollup"
import { fetchPriceSeries, fetchSummary, fetchTVS } from "@/services/api/stats"

/** Store */
import { useAppStore } from "@/store/app.store"
import { useActivityStore } from "@/store/activity.store"
const appStore = useAppStore()
const activityStore = useActivityStore()

const head = computed(() => appStore.lastHead)
const currentPrice = computed(() => appStore.currentPrice)

const totalFees = computed(() => head.value.total_fee / 1_000_000)
const totalFeesUSD = computed(() => totalFees.value * currentPrice.value?.close)
const topRollup = computed(() => {
let rankCategory = getRankCategory(roundTo(activityStore?.rollups_ranking?.top_rollup?.rank / 10, 0))
return {
slug: activityStore?.rollups_ranking?.top_rollup?.slug,
name: activityStore?.rollups_ranking?.top_rollup?.name,
rank: {
name: rankCategory?.name,
score: activityStore?.rollups_ranking?.top_rollup?.rank,
color: rankCategory?.color,
},
}
})

const isLoading = ref(true)
const series = ref([])
Expand All @@ -43,24 +31,43 @@ const price = reactive({
diff: 0,
side: null,
})
const showPrice = ref(!!quoteServiceURL())
const topRollup = ref(null)
const showTopRollup = ref(isMainnet() && !!rollupRankingServiceURL())
const tvs = computed(() => appStore.tvs)
const txCount24h = ref(0)
const bytesInBlocks24h = ref(0)

onMounted(async () => {
const dataSeries = await fetchPriceSeries({ from: parseInt(DateTime.now().minus({ days: 3 }).ts / 1_000) })
series.value = dataSeries
appStore.currentPrice = series.value[0]
price.value = parseFloat(series.value[0].close)

const prevDayClosePrice = parseFloat(series.value[1].close)
price.diff = (Math.abs(prevDayClosePrice - price.value) / ((prevDayClosePrice + price.value) / 2)) * 100
let side = "stay"
if (price.value - prevDayClosePrice !== 0) {
side = price.value - prevDayClosePrice > 0 ? "rise" : "fall"
if (showPrice.value) {
const dataSeries = await fetchPriceSeries({ from: parseInt(DateTime.now().minus({ days: 3 }).ts / 1_000) })
if (dataSeries.length) {
series.value = dataSeries
appStore.currentPrice = series.value[0]
price.value = parseFloat(series.value[0].close)

const prevDayClosePrice = parseFloat(series.value[1].close)
price.diff = (Math.abs(prevDayClosePrice - price.value) / ((prevDayClosePrice + price.value) / 2)) * 100
let side = "stay"
if (price.value - prevDayClosePrice !== 0) {
side = price.value - prevDayClosePrice > 0 ? "rise" : "fall"
}
price.side = side
}
}
price.side = side

if (showTopRollup.value) {
const _topRollups = await fetchRollupsRanking({ limit: 1 })
if (_topRollups.length) {
const _r = _topRollups[0]
topRollup.value = {
..._r,
category: getRankCategory(roundTo(_r.rank / 10, 0)),
name: _r.slug.split("-").map(el => capitilize(el)).join(" "),
}
}
}

const _tvs = await fetchTVS({ period: null })
if (_tvs.value) {
appStore.tvs = _tvs.value
Expand All @@ -83,15 +90,15 @@ onMounted(async () => {
<Flex tag="section" justify="center" wide :class="$style.wrapper">
<Flex align="center" justify="between" gap="24" wide :class="$style.container">
<Flex align="center" gap="12" :class="$style.stats">
<template v-if="isMainnet()">
<template v-if="showTopRollup">
<NuxtLink :to="`/rollup/rank/${topRollup?.slug}`">
<Tooltip>
<Flex align="center" gap="6" :class="$style.stat">
<Icon
v-if="topRollup?.name"
name="laurel"
size="14"
:color="topRollup?.rank?.color"
:color="topRollup?.category?.color"
:style="{ marginTop: '1px' }"
/>
<Icon v-else name="laurel" size="14" color="tertiary" :class="$style.icon" :style="{ marginTop: '1px' }" />
Expand All @@ -109,11 +116,11 @@ onMounted(async () => {
<Flex direction="column" gap="8">
<Flex align="center" justify="between" gap="8">
<Text size="12" weight="500" color="tertiary">Rank:</Text>
<Text size="12" weight="600" :color="topRollup?.rank?.color"> {{ topRollup?.rank?.name }} </Text>
<Text size="12" weight="600" :color="topRollup?.category?.color"> {{ topRollup?.category?.name }} </Text>
</Flex>
<Flex align="center" justify="between" gap="8">
<Text size="12" weight="500" color="tertiary">Score:</Text>
<Text size="12" weight="600" color="secondary"> {{ topRollup?.rank?.score }}% </Text>
<Text size="12" weight="600" color="secondary"> {{ topRollup?.rank }}% </Text>
</Flex>
</Flex>
</template>
Expand Down Expand Up @@ -191,7 +198,7 @@ onMounted(async () => {
</Tooltip>
</Flex>

<Tooltip position="end">
<Tooltip v-if="showPrice" position="end">
<Flex align="center" gap="6" :class="$style.stat">
<Icon name="coin" size="12" color="secondary" :class="$style.icon" />

Expand Down
26 changes: 23 additions & 3 deletions components/LeftSidebar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import NavLink from "@/components/modules/navigation/NavLink.vue"
import { getNetworkName } from "@/services/utils/general"
import { StatusMap } from "@/services/constants/node"
import { isMainnet, isMobile } from "@/services/utils"
import { nodeStatsURL } from "@/services/config"
import { isSelfhosted } from "@/services/config.js"

/** Store */
import { useAppStore } from "@/store/app.store"
Expand Down Expand Up @@ -95,7 +97,7 @@ const mainLinks = reactive([
name: "Nodes",
path: "/stats?tab=nodes",
queryParam: { tab: "nodes" },
show: isMainnet(),
show: isMainnet() && !!nodeStatsURL(),
},
],
},
Expand Down Expand Up @@ -126,6 +128,18 @@ const modularLinks = reactive([
},
],
},
{
icon: "hyperlane",
name: "Interop",
path: "/hyperlane",
children: [
{
name: "Transfers",
path: "/hyperlane/transfers",
show: true,
},
],
},
{
icon: "ibc",
name: "IBC",
Expand Down Expand Up @@ -164,12 +178,18 @@ const modularLinks = reactive([

const isToolsLinkCollapsed = ref(false)
const toolsLinks = reactive([
{
icon: "widgets",
name: "Widgets",
path: "https://widgets.celenium.io",
external: true,
new: true,
},
{
icon: "explorable",
name: "Terminal",
path: "https://terminal.celenium.io",
external: true,
new: true,
},
{
icon: "drop",
Expand Down Expand Up @@ -294,7 +314,7 @@ const handleOnClose = () => {
<Text v-else size="12" weight="600" color="tertiary">{{ nodeStore.percentage.toFixed(0) }}%</Text>
</Flex>

<Dropdown position="end" fullWidth>
<Dropdown position="end" fullWidth :disabled="isSelfhosted()">
<Flex align="center" gap="8" justify="between" :class="$style.network_selector">
<Flex align="center" gap="8">
<Icon name="globe" size="14" :color="head.synced ? 'brand' : 'red'" />
Expand Down
9 changes: 9 additions & 0 deletions components/cmd/CommandMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,15 @@ const rawNavigationActions = [
router.push("/rollups")
},
},
{
type: "callback",
icon: "arrow-narrow-right",
title: "Go to Hyperlane",
runText: "Open Hyperlane",
callback: () => {
router.push("/hyperlane")
},
},
{
type: "callback",
icon: "arrow-narrow-right",
Expand Down
Loading