Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 4 additions & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
name: continuous-integration

on: [push, pull_request]
on:
push:
branches: [master, main]
pull_request:

jobs:
CI:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ jobs:
if: steps.playwright-cache.outputs.cache-hit != 'true'

- name: Run Playwright tests
continue-on-error: true
run: yarn playwright test --project=chromium
env:
REACT_APP_TESTING_BASE: https://deploy-preview-${{ github.event.pull_request.number }}--tezos-homebase.netlify.app
Expand Down
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v22.11.0
v22.12.0
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ There are many ways you can contribute to our project:
- Report bugs or suggest features by opening an [issue](https://github.com/dOrgTech/homebase-app/issues).
- Fix bugs or implement features by submitting a [pull request](https://github.com/dOrgTech/homebase-app/pulls).
- Improve the documentation or the user interface by editing the files directly on GitHub or forking the repo.
- Join our [Discord server](https://discord.gg/9cduRr5) and chat with us about the project.
- Join our [Discord server](https://discord.gg/yqv8ABG2EN) and chat with us about the project.

## Development setup

Expand Down
110 changes: 110 additions & 0 deletions MIGRATION-GHOSTNET-TO-SHADOWNET.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# Migration: Tezos Ghostnet → Shadownet

> **Note:** This file should be removed once the migration is complete and merged.

## Overview

Tezos is replacing Ghostnet with Shadownet as the long-term testnet. This document tracks what needs to change for Homebase to support Shadownet.

Reference: https://teztnets.com/shadownet-about

## Shadownet Technical Details

- **RPC:** `https://rpc.shadownet.teztnets.com`
- **Faucet:** `https://faucet.shadownet.teztnets.com`
- **Network Name:** `TEZOS_SHADOWNET_2025-08-07T20:00:00Z`
- **Activation Date:** 2025-08-07

## External Service Compatibility

| Service | Status | Endpoint |
|---------|--------|----------|
| TzKT API | ✅ Ready | `api.shadownet.tzkt.io` |
| TzKT Explorer | ✅ Ready | `shadownet.tzkt.io` |
| Shadownet Faucet | ✅ Ready | `faucet.shadownet.teztnets.com` |
| SmartPy RPC | ❓ Unknown | May not support shadownet |
| Beacon Wallet SDK | ✅ Ready | Upgraded to 4.7.0 with native `NetworkType.SHADOWNET` |
| Better Call Dev | ❓ Unknown | May not support shadownet |

---

## Changes Required

### 1. Frontend App (this repo)

#### Files to modify:

| File | Change |
|------|--------|
| `src/services/beacon/utils.ts` | Replace `ghostnet` with `shadownet` in Network type, RPC nodes, colors, and wallet config |
| `src/services/config/constants.ts` | Rename env key to `REACT_APP_RPC_NETWORK_SHADOWNET` |
| `src/modules/common/Footer.tsx` | Update explorer link to `shadownet.tzkt.io` |
| `src/modules/creator/state/context.tsx` | Update network checks from `ghostnet` to `shadownet` |
| `src/services/tzprofiles/hooks/useProfileClaim.tsx` | Update network validation |
| `.env` | Update env var name |

### 2. Hasura/Indexer

The Homebase indexer needs to be reconfigured:

- [ ] Point indexer to shadownet RPC (`https://rpc.shadownet.teztnets.com`)
- [ ] Reset/migrate database (shadownet is a new chain)
- [ ] Redeploy Hasura instance
- [ ] Update any hardcoded network references

**Indexer endpoints:**
- V1: `https://v3-homebase-indexer.tezos-homebase.io/v1/graphql`
- V2: `https://v2-homebase-indexer.w3api.dev/v1/graphql`

### 3. Smart Contracts

All contracts must be **redeployed on Shadownet** (it's a completely new chain):

- [ ] Token contracts (FA1.2/FA2)
- [ ] BaseDAO contracts
- [ ] Registry contracts
- [ ] Metadata carrier contracts
- [ ] DAO deployer service contracts

### 4. Environment Variables

```bash
# Old
REACT_APP_RPC_NETWORK_GHOSTNET=https://ghostnet.smartpy.io

# New
REACT_APP_RPC_NETWORK_SHADOWNET=https://rpc.shadownet.teztnets.com
```

---

## Testing Checklist

- [x] Get tez from shadownet faucet
- [x] Test wallet connection with Temple/Kukai (Kukai works with Beacon SDK 4.7.0)
- [x] Fix: Wallet was connecting to mainnet instead of shadownet (now passes network to `requestPermissions`)
- [ ] Test token creation (on correct network)
- [ ] Test DAO creation flow
- [ ] Test staking
- [ ] Test proposal creation
- [ ] Test voting
- [ ] Test proposal execution
- [ ] Verify TzKT links work correctly
- [ ] Verify indexer picks up new DAOs

---

## Migration Strategy

**Approach: Replace ghostnet with shadownet**

Since Ghostnet is being deprecated, we're replacing it entirely rather than adding shadownet alongside it. Users will lose access to existing ghostnet DAOs, but those would become inaccessible anyway once Ghostnet shuts down.

---

## Rollback Plan

If issues arise:
1. Revert this branch
2. Re-enable ghostnet configuration
3. Investigate and fix shadownet issues in a new branch
2 changes: 1 addition & 1 deletion craco.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ module.exports = {
fs: false,
};

// add persistent cache
// persistent cache for faster rebuilds
config.cache = {
type: "filesystem",
buildDependencies: { config: [__filename] },
Expand Down
8 changes: 4 additions & 4 deletions e2e/etherlink-default-network.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,17 @@ test.describe("url-driven default network", () => {
test("does not override an existing user-selected network", async ({ page }) => {
await page.goto("/")
await page.evaluate(() => {
localStorage.setItem("homebase:network", "ghostnet")
localStorage.setItem("homebase:network", "shadownet")
})

await page.goto(`/explorer/etherlink/dao/${EXAMPLE_ETHERLINK_DAO}/overview`)
await page.waitForLoadState("domcontentloaded")

const storedNetwork = await page.evaluate(() => localStorage.getItem("homebase:network"))
expect(storedNetwork).toBe("ghostnet")
expect(storedNetwork).toBe("shadownet")

// Tezos network label remains as Ghostnet (DAO metadata may switch later; bootstrap should not)
await expect(page.getByText(/Tezos Ghostnet/i).first()).toBeVisible()
// Tezos network label remains as Shadownet (DAO metadata may switch later; bootstrap should not)
await expect(page.getByText(/Tezos Shadownet/i).first()).toBeVisible()
})
})

10 changes: 2 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
"prepare": "husky install"
},
"dependencies": {
"@airgap/beacon-sdk": "^4.2.2",
"@airgap/beacon-types": "^4.2.2",
"@airgap/beacon-sdk": "4.7.0",
"@airgap/beacon-types": "4.7.0",
"@craco/craco": "^7.1.0",
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
Expand Down Expand Up @@ -130,17 +130,11 @@
"resolutions": {
"@types/react": "~17.0.3",
"react-error-overlay": "6.0.9",
"@walletconnect/core": "2.14.0",
"@walletconnect/types": "2.14.0",
"@walletconnect/utils": "2.14.0",
"viem": "2.17.4",
"wagmi": "2.10.10",
"mipd": "0.0.7"
},
"overrides": {
"@walletconnect/core": "2.14.0",
"@walletconnect/types": "2.14.0",
"@walletconnect/utils": "2.14.0",
"viem": "2.17.4",
"wagmi": "2.10.10"
},
Expand Down
65 changes: 62 additions & 3 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import React from "react"
import React, { useEffect } from "react"
import "App.css"
import { BrowserRouter as Router, Redirect, Route, Switch } from "react-router-dom"
import { QueryClient, QueryClientProvider } from "react-query"
import { QueryClient, QueryClientProvider, QueryCache, MutationCache } from "react-query"

import { Box, ThemeProvider, styled } from "@material-ui/core"
import { SnackbarProvider } from "notistack"
import { SnackbarProvider, useSnackbar } from "notistack"

import { DAOExplorerRouter } from "modules/explorer/router"
import { CreatorProvider } from "modules/creator/state"
Expand All @@ -22,8 +22,21 @@ import { EnvKey, HUMANITEZ_DAO, getEnv } from "services/config"
import { DAOCreatorRouter } from "modules/creator/router"
import { CommunityCreator } from "modules/lite/creator"
import { EtherlinkDAOCreatorRouter } from "modules/etherlink/router"
import { useTezos } from "services/beacon/hooks/useTezos"
import {
DISCORD_SUPPORT_LINK,
SUPPORT_SHOWN_KEY,
hasSupportThresholdBeenMet,
trackAppError
} from "services/supportNotification"

const queryClient = new QueryClient({
queryCache: new QueryCache({
onError: () => trackAppError()
}),
mutationCache: new MutationCache({
onError: () => trackAppError()
}),
defaultOptions: {
queries: {
retryDelay: attemptIndex => Math.min(1000 * 2 ** attemptIndex, 60000),
Expand Down Expand Up @@ -67,6 +80,51 @@ const InfoSnackbar = styled("div")({
// PostHog tracking is now handled globally via PostHogProvider in index.tsx
// Visit tracking can be done via PostHog's auto-capture or custom events

const SupportNotificationListener: React.FC = () => {
const { enqueueSnackbar } = useSnackbar()
const { account } = useTezos()

const showSupportNotification = React.useCallback(() => {
if (sessionStorage.getItem(SUPPORT_SHOWN_KEY) === "true") return
sessionStorage.setItem(SUPPORT_SHOWN_KEY, "true")

enqueueSnackbar("Having trouble? Get support on our Discord.", {
variant: "info",
autoHideDuration: 8000,
action: (
<a
href={DISCORD_SUPPORT_LINK}
target="_blank"
rel="noopener noreferrer"
style={{ color: "#fff", display: "flex", alignItems: "center" }}
>
Open Discord
</a>
)
})
}, [enqueueSnackbar])

// Check on login if errors already accumulated before wallet was connected
useEffect(() => {
if (!account) return
if (hasSupportThresholdBeenMet()) {
showSupportNotification()
}
}, [account, showSupportNotification])

// Listen for new errors after login
useEffect(() => {
if (!account) return

const handler = () => showSupportNotification()

window.addEventListener("homebase-support-needed", handler)
return () => window.removeEventListener("homebase-support-needed", handler)
}, [account, showSupportNotification])

return null
}

const App: React.FC = () => {
return (
<ThemeProvider theme={theme}>
Expand All @@ -84,6 +142,7 @@ const App: React.FC = () => {
>
{/* <TanStackQueryClientProvider client={tsQueryClient}> */}
<QueryClientProvider client={queryClient}>
<SupportNotificationListener />
<ActionSheetProvider>
<Box bgcolor="primary.dark" position="absolute" width="100%">
<Router>
Expand Down
9 changes: 5 additions & 4 deletions src/modules/common/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,15 +73,16 @@ const Container = styled(Grid)(({ theme }) => ({
export const ExplorerFooter: React.FC = () => {
const theme = useTheme()
const isMobileSmall = useMediaQuery(theme.breakpoints.down("sm"))
const { network } = useTezos()
const { network, account } = useTezos()
const discordLink = account ? "https://discord.gg/ZRVk6zu3xR" : "https://discord.gg/yqv8ABG2EN"

const goToFAQ = () => {
window.open("https://faq.tezos-homebase.io/homebase-faq/", "_blank")
}

const goToExplorer = () => {
return network === "ghostnet"
? window.open("https://ghostnet.tzkt.io/", "_blank")
return network === "shadownet"
? window.open("https://shadownet.tzkt.io/", "_blank")
: window.open("https://tzkt.io/", "_blank")
}

Expand All @@ -94,7 +95,7 @@ export const ExplorerFooter: React.FC = () => {
}

const goToDiscord = () => {
window.open("https://discord.gg/autFpNaFYu", "_blank")
window.open(discordLink, "_blank")
}

const goToYoutube = () => {
Expand Down
5 changes: 5 additions & 0 deletions src/modules/common/hooks/useNotification.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Button, styled } from "@material-ui/core"
import { OptionsObject, useSnackbar } from "notistack"
import { Close, OpenInNew } from "@material-ui/icons"
import { ExternalLink } from "modules/common/ExternalLink"
import { trackAppError } from "services/supportNotification"

const CloseIcon = styled(Close)({
color: "#fff"
Expand Down Expand Up @@ -43,6 +44,10 @@ export const useNotification = () => {
action: <NotificationActions detailsLink={detailsLink} onClose={() => closeSnackbar(key)} />
})

if (options.variant === "error") {
trackAppError()
}

return { key, closeSnackbar }
}

Expand Down
36 changes: 18 additions & 18 deletions src/modules/creator/state/context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,15 @@ export const INITIAL_MIGRATION_STATE: MigrationParams = {
const getInitialState = (data: MigrationParams) => {
const network = getTezosNetwork()

data.votingSettings.votingBlocksDay = network === networkNameMap.ghostnet ? 0 : 3
data.votingSettings.votingBlocksHours = network === networkNameMap.ghostnet ? 0 : 0
data.votingSettings.votingBlocksMinutes = network === networkNameMap.ghostnet ? 5 : 0
data.votingSettings.proposalFlushBlocksDay = network === networkNameMap.ghostnet ? 0 : 1
data.votingSettings.proposalFlushBlocksHours = network === networkNameMap.ghostnet ? 0 : 0
data.votingSettings.proposalFlushBlocksMinutes = network === networkNameMap.ghostnet ? 5 : 0
data.votingSettings.proposalExpiryBlocksDay = network === networkNameMap.ghostnet ? 0 : 6
data.votingSettings.proposalExpiryBlocksHours = network === networkNameMap.ghostnet ? 0 : 0
data.votingSettings.proposalExpiryBlocksMinutes = network === networkNameMap.ghostnet ? 5 : 0
data.votingSettings.votingBlocksDay = network === networkNameMap.shadownet ? 0 : 3
data.votingSettings.votingBlocksHours = network === networkNameMap.shadownet ? 0 : 0
data.votingSettings.votingBlocksMinutes = network === networkNameMap.shadownet ? 5 : 0
data.votingSettings.proposalFlushBlocksDay = network === networkNameMap.shadownet ? 0 : 1
data.votingSettings.proposalFlushBlocksHours = network === networkNameMap.shadownet ? 0 : 0
data.votingSettings.proposalFlushBlocksMinutes = network === networkNameMap.shadownet ? 5 : 0
data.votingSettings.proposalExpiryBlocksDay = network === networkNameMap.shadownet ? 0 : 6
data.votingSettings.proposalExpiryBlocksHours = network === networkNameMap.shadownet ? 0 : 0
data.votingSettings.proposalExpiryBlocksMinutes = network === networkNameMap.shadownet ? 5 : 0

return data
}
Expand Down Expand Up @@ -156,15 +156,15 @@ export const reducer = (state: CreatorState, action: CreatorAction): CreatorStat
}

const updateInitialState = (network: string, values: MigrationParams) => {
values.votingSettings.votingBlocksDay = network === networkNameMap.ghostnet ? 0 : 3
values.votingSettings.votingBlocksHours = network === networkNameMap.ghostnet ? 0 : 0
values.votingSettings.votingBlocksMinutes = network === networkNameMap.ghostnet ? 5 : 0
values.votingSettings.proposalFlushBlocksDay = network === networkNameMap.ghostnet ? 0 : 1
values.votingSettings.proposalFlushBlocksHours = network === networkNameMap.ghostnet ? 0 : 0
values.votingSettings.proposalFlushBlocksMinutes = network === networkNameMap.ghostnet ? 5 : 0
values.votingSettings.proposalExpiryBlocksDay = network === networkNameMap.ghostnet ? 0 : 6
values.votingSettings.proposalExpiryBlocksHours = network === networkNameMap.ghostnet ? 0 : 0
values.votingSettings.proposalExpiryBlocksMinutes = network === networkNameMap.ghostnet ? 5 : 0
values.votingSettings.votingBlocksDay = network === networkNameMap.shadownet ? 0 : 3
values.votingSettings.votingBlocksHours = network === networkNameMap.shadownet ? 0 : 0
values.votingSettings.votingBlocksMinutes = network === networkNameMap.shadownet ? 5 : 0
values.votingSettings.proposalFlushBlocksDay = network === networkNameMap.shadownet ? 0 : 1
values.votingSettings.proposalFlushBlocksHours = network === networkNameMap.shadownet ? 0 : 0
values.votingSettings.proposalFlushBlocksMinutes = network === networkNameMap.shadownet ? 5 : 0
values.votingSettings.proposalExpiryBlocksDay = network === networkNameMap.shadownet ? 0 : 6
values.votingSettings.proposalExpiryBlocksHours = network === networkNameMap.shadownet ? 0 : 0
values.votingSettings.proposalExpiryBlocksMinutes = network === networkNameMap.shadownet ? 5 : 0

return values
}
Expand Down
Loading
Loading