Skip to content
Merged
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
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
4 changes: 2 additions & 2 deletions src/modules/common/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ export const ExplorerFooter: React.FC = () => {
}

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 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
2 changes: 1 addition & 1 deletion src/services/aci/endpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ async function parseContractScript(c: any, initTokenTable: Record<string, tokenV

async function getContractEndpoints(network: string, contractAddress: string) {
try {
const tezosNetwork = network === "ghostnet" ? "ghostnet" : "mainnet"
const tezosNetwork = network === "shadownet" ? "shadownet" : "mainnet"
const tezos = new TezosToolkit(rpcNodes[tezosNetwork])
const contract = await tezos.contract.at(contractAddress)
const endpoints = await parseContractScript(contract, {})
Expand Down
26 changes: 18 additions & 8 deletions src/services/beacon/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@ import { MichelCodecPacker, TezosToolkit } from "@taquito/taquito"
import { Tzip16Module } from "@taquito/tzip16"
import { EnvKey, getEnv } from "services/config"

export type Network = "mainnet" | "ghostnet" | "etherlink_shadownet" | "etherlink_mainnet"
export type Network = "mainnet" | "shadownet" | "etherlink_shadownet" | "etherlink_mainnet"

export const rpcNodes: Record<Network, string> = {
mainnet: getEnv(EnvKey.REACT_APP_RPC_NETWORK_MAINNET) || "https://mainnet.api.tez.ie",
ghostnet: getEnv(EnvKey.REACT_APP_RPC_NETWORK_GHOSTNET) || "https://ghostnet.smartpy.io",
shadownet: getEnv(EnvKey.REACT_APP_RPC_NETWORK_SHADOWNET) || "https://rpc.shadownet.teztnets.com",
etherlink_shadownet: "https://node.shadownet.etherlink.com",
etherlink_mainnet: "https://node.mainnet.etherlink.com"
}

export const networkDotColorMap: Record<Network, string> = {
mainnet: "#9EEE5D",
ghostnet: "#291F79",
shadownet: "#291F79",
etherlink_mainnet: "#9EEE5D",
etherlink_shadownet: "#291F79"
}
Expand Down Expand Up @@ -59,12 +59,20 @@ export const getTezosNetwork = (): Network => {

export const createWallet = (network: Network) => {
const networkType = getNetworkTypeByEnvNetwork(network)

// For Shadownet, include the RPC URL so wallets know exactly which network to connect to
const networkConfig =
network === "shadownet"
? {
type: networkType,
rpcUrl: rpcNodes.shadownet
}
: { type: networkType }

return new BeaconWallet({
name: "Homebase",
iconUrl: "https://tezostaquito.io/img/favicon.png",
network: {
type: networkType
},
network: networkConfig,
walletConnectOptions: {
projectId: "1641355e825aeaa926e843dd38b04f6f", // Project ID can be customised
relayUrl: "wss://relay.walletconnect.com" // WC2 relayUrl can be customised
Expand All @@ -81,8 +89,8 @@ export const createTezos = (network: Network) => {

export const getNetworkTypeByEnvNetwork = (envNetwork: Network): NetworkType => {
switch (envNetwork) {
case "ghostnet":
return NetworkType.GHOSTNET
case "shadownet":
return NetworkType.SHADOWNET

case "mainnet":
return NetworkType.MAINNET
Expand All @@ -100,6 +108,8 @@ export const connectWithBeacon = async (
}> => {
const wallet = createWallet(envNetwork)

// Network is already configured in createWallet via DAppClientOptions.network
// In Beacon SDK 4.7.0+, requestPermissions only accepts scopes, not network
await wallet.requestPermissions()

const accounts: any[] = JSON.parse(localStorage.getItem("beacon:accounts") as string)
Expand Down
2 changes: 1 addition & 1 deletion src/services/config/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export enum EnvKey {
REACT_APP_API_URL = "REACT_APP_API_URL",
REACT_APP_BASE_URL = "REACT_APP_BASE_URL",
REACT_APP_DAO_DEPLOYER_API = "REACT_APP_DAO_DEPLOYER_API",
REACT_APP_RPC_NETWORK_GHOSTNET = "REACT_APP_RPC_NETWORK_GHOSTNET",
REACT_APP_RPC_NETWORK_SHADOWNET = "REACT_APP_RPC_NETWORK_SHADOWNET",
REACT_APP_RPC_NETWORK_MAINNET = "REACT_APP_RPC_NETWORK_MAINNET",
REACT_APP_PROPOSAL_WEBHOOK_URL = "REACT_APP_PROPOSAL_WEBHOOK_URL",
REACT_APP_ENABLED_FEATURES = "REACT_APP_ENABLED_FEATURES",
Expand Down
2 changes: 1 addition & 1 deletion src/services/tzprofiles/hooks/useProfileClaim.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Claim } from "../claims/types"
export const useProfileClaim = (tzAddress: string) => {
const { network } = useTezos()

const isTezosNetwork = !!network && (network.includes("tezos") || network === "mainnet" || network === "ghostnet")
const isTezosNetwork = !!network && (network.includes("tezos") || network === "mainnet" || network === "shadownet")
const isTezosAddress = typeof tzAddress === "string" && tzAddress.startsWith("tz")

const result = useQuery<Claim, Error>(
Expand Down
3 changes: 1 addition & 2 deletions src/services/wagmi/config.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { createConfig, http } from "wagmi"
import { etherlink, hardhat } from "wagmi/chains"
import { defineChain } from "viem"
import { metaMask, injected, safe } from "wagmi/connectors"

import { DeployContract } from "./token"
import { getDefaultConfig } from "connectkit"

// Custom chain definition for Etherlink Shadownet
// Define custom Etherlink Shadownet chain
export const etherlinkShadownet = defineChain({
id: 127823,
name: "Etherlink Shadownet",
Expand Down
2 changes: 2 additions & 0 deletions src/services/wagmi/ethers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import { BrowserProvider, JsonRpcSigner } from "ethers"
import { type HttpTransport } from "viem"

export function publicClientToProvider(publicClient: any) {
if (!publicClient) return undefined
const { chain, transport } = publicClient
if (!chain || !transport) return undefined
const network = {
chainId: chain.id,
name: chain.name,
Expand Down
Loading
Loading