Compare commits

...

7 Commits

Author SHA1 Message Date
8cad0db3b8
fixes for Firefox incompatibility with button text and select dropdown
Signed-off-by: Uncle Fatso <uncle.fatso@ghostchain.io>
2025-08-20 15:23:54 +03:00
9e3e01fecf
update chainspec and populate with extra bootnodes
Signed-off-by: Uncle Fatso <uncle.fatso@ghostchain.io>
2025-08-17 16:02:09 +03:00
20c73d53b6
change chain names in manifests
Signed-off-by: Uncle Fatso <uncle.fatso@ghostchain.io>
2025-08-12 20:12:49 +03:00
d74d7affe4
fix the builder script
Signed-off-by: Uncle Fatso <uncle.fatso@ghostchain.io>
2025-08-12 20:12:15 +03:00
74f922fd3e
change network names and remove hardcoded chain data
Signed-off-by: Uncle Fatso <uncle.fatso@ghostchain.io>
2025-08-12 19:33:17 +03:00
eff6e61a39
update to the latest CASPER deployment
Signed-off-by: Uncle Fatso <uncle.fatso@ghostchain.io>
2025-08-12 19:31:40 +03:00
57704e9604
remove redundant console.log
Signed-off-by: Uncle Fatso <uncle.fatso@ghostchain.io>
2025-07-23 15:45:08 +03:00
22 changed files with 844 additions and 192 deletions

File diff suppressed because one or more lines are too long

View File

@ -1,10 +1,10 @@
{ {
"author": "Ghost Team <someone@ghostchain.io>", "author": "GHOST Team <someone@ghostchain.io>",
"description": "Wallet for Ghost and Casper blockchains light clients", "description": "Wallet for GHOST and CASPER blockchains light clients",
"homepage_url": "https://github.com/ghostchain/ghost-wallet", "homepage_url": "https://git.ghostchain.io/ghostchain/ghost-extension-wallet",
"name": "Ghost Wallet", "name": "GHOST Wallet",
"short_name": "ghost-wallet", "short_name": "ghost-wallet",
"version": "0.0.0", "version": "0.0.1",
"manifest_version": 3, "manifest_version": 3,
"permissions": ["notifications", "storage", "tabs", "alarms"], "permissions": ["notifications", "storage", "tabs", "alarms"],
"background": { "background": {
@ -12,7 +12,7 @@
"type": "module" "type": "module"
}, },
"action": { "action": {
"default_title": "Ghost Wallet", "default_title": "GHOST Wallet",
"default_popup": "ui/assets/wallet-popup.html" "default_popup": "ui/assets/wallet-popup.html"
}, },
"options_ui": { "options_ui": {

View File

@ -1,10 +1,10 @@
{ {
"author": "Ghost Team <someone@ghostchain.io>", "author": "GHOST Team <someone@ghostchain.io>",
"description": "Wallet for Ghost and Casper blockchains light clients", "description": "Wallet for GHOST and CASPER blockchains light clients",
"homepage_url": "https://github.com/ghostchain/ghost-wallet", "homepage_url": "https://git.ghostchain.io/ghostchain/ghost-extension-wallet",
"name": "Ghost Wallet", "name": "GHOST Wallet",
"short_name": "ghost-wallet", "short_name": "ghost-wallet",
"version": "0.0.0", "version": "0.0.1",
"manifest_version": 3, "manifest_version": 3,
"permissions": ["notifications", "storage", "tabs", "alarms"], "permissions": ["notifications", "storage", "tabs", "alarms"],
"background": { "background": {
@ -12,7 +12,7 @@
"type": "module" "type": "module"
}, },
"action": { "action": {
"default_title": "Ghost Wallet", "default_title": "GHOST Wallet",
"default_popup": "ui/assets/wallet-popup.html" "default_popup": "ui/assets/wallet-popup.html"
}, },
"options_ui": { "options_ui": {
@ -37,7 +37,7 @@
}, },
"browser_specific_settings": { "browser_specific_settings": {
"gecko": { "gecko": {
"id": "{9b4d20ed-b18a-4237-b5d0-ca71c2ce2060}" "id": "{14b458b2-3221-4800-a36f-ae1ad1756ae2}"
} }
}, },
"web_accessible_resources": [ "web_accessible_resources": [

View File

@ -1,7 +1,7 @@
<!doctype html> <!doctype html>
<html> <html>
<head> <head>
<title>Ghost Wallet Options</title> <title>GHOST Wallet Options</title>
<meta charset="utf-8" /> <meta charset="utf-8" />
</head> </head>
<body style="background-color: #f9f9f9"> <body style="background-color: #f9f9f9">

View File

@ -1,7 +1,7 @@
<!doctype html> <!doctype html>
<html> <html>
<head> <head>
<title>Ghost Wallet</title> <title>GHOST Wallet</title>
<meta charset="utf-8" /> <meta charset="utf-8" />
</head> </head>
<body> <body>

View File

@ -3,8 +3,11 @@
NAME=$(grep "name" package.json | grep -oP '(?<=: ")[^"]+') NAME=$(grep "name" package.json | grep -oP '(?<=: ")[^"]+')
VERSION=$(grep "version" package.json | grep -oP '(?<=: ")[^"]+') VERSION=$(grep "version" package.json | grep -oP '(?<=: ")[^"]+')
BROWSERS=("chrome" "firefox") BROWSERS=("chrome" "firefox")
SCRIPT_DIR=$(dirname "$(realpath "$0")")
for BROWSER in "${BROWSERS[@]}"; do for BROWSER in "${BROWSERS[@]}"; do
FILENAME="$NAME-$BROWSER@v$VERSION.zip"
pnpm build:$BROWSER pnpm build:$BROWSER
python -m zipfile -c releases/$NAME-$BROWSER@v$VERSION /dist python -m zipfile --create "$SCRIPT_DIR/releases/$FILENAME" "$SCRIPT_DIR/dist"
python -m zipfile --test "$SCRIPT_DIR/releases/$FILENAME"
done done

View File

@ -1,6 +1,6 @@
{ {
"name": "ghost-wallet", "name": "ghost-wallet",
"version": "0.0.27", "version": "0.1.4",
"description": "Browser extension to manage ghost blockchain light clients.", "description": "Browser extension to manage ghost blockchain light clients.",
"main": "dist/src/index.js", "main": "dist/src/index.js",
"author": "Uncle f4ts0 <f4ts0@ghostchain.io>", "author": "Uncle f4ts0 <f4ts0@ghostchain.io>",
@ -146,6 +146,7 @@
"date-fns": "^4.1.0", "date-fns": "^4.1.0",
"embla-carousel-react": "^8.5.1", "embla-carousel-react": "^8.5.1",
"input-otp": "^1.2.4", "input-otp": "^1.2.4",
"lodash": "^4.17.21",
"lucide-react": "^0.468.0", "lucide-react": "^0.468.0",
"next-themes": "^0.4.1", "next-themes": "^0.4.1",
"react": "^18.3.1", "react": "^18.3.1",

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +1,11 @@
import { useEffect, useState } from "react" import { useEffect, useState } from "react"
import { MdDeleteOutline } from "react-icons/md" import { MdDeleteOutline } from "react-icons/md"
import * as environment from "../environment" import * as environment from "@/environment"
import "./Bootnodes.css" import "./Bootnodes.css"
import { Title, Switch } from "." import { Title, Switch } from "."
import { helper } from "@substrate/light-client-extension-helpers/extension-page" import { helper } from "@substrate/light-client-extension-helpers/extension-page"
import { wellKnownGenesisHashByChainId } from "../constants" import { wellKnownGenesisHashByChainId, networks } from "@/constants"
import { import {
Select, Select,
@ -50,7 +50,7 @@ const saveToLocalStorage = async (
} }
export const Bootnodes = () => { export const Bootnodes = () => {
const [selectedChain, setSelectedChain] = useState<string>("casper_staging_testnet") const [selectedChain, setSelectedChain] = useState<string>(networks[0].value)
const [defaultBn, setDefaultBn] = useState<BootnodesType[]>([]) const [defaultBn, setDefaultBn] = useState<BootnodesType[]>([])
const [customBn, setCustomBn] = useState<BootnodesType[]>([]) const [customBn, setCustomBn] = useState<BootnodesType[]>([])
const [customBnInput, setCustomBnInput] = useState<string>("") const [customBnInput, setCustomBnInput] = useState<string>("")
@ -164,9 +164,15 @@ export const Bootnodes = () => {
</SelectTrigger> </SelectTrigger>
<SelectContent className="sm:w-[250px] w-[100%]"> <SelectContent className="sm:w-[250px] w-[100%]">
<SelectGroup> <SelectGroup>
<SelectItem data-testid="scheme-casper_staging_testnet" value="casper_staging_testnet"> {networks.map(network => (
Casper <SelectItem
key={network.chainId}
data-testid={`scheme-${network.value}`}
value={network.value}
>
{network.label}
</SelectItem> </SelectItem>
))}
</SelectGroup> </SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>

View File

@ -9,13 +9,13 @@ const buttonVariants = cva(
{ {
variants: { variants: {
variant: { variant: {
default: "bg-primary text-primary-foreground hover:bg-foreground hover:text-background", default: "bg-primary text-primary-foreground hover:bg-foreground hover:text-background active:text-primary-foreground",
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary-foreground hover:text-secondary", secondary: "bg-secondary text-secondary-foreground hover:bg-secondary-foreground hover:text-secondary active:text-secondary-foreground",
ghost: "text-primary hover:bg-accent hover:text-background", ghost: "text-primary hover:bg-accent hover:text-background active:text-primary",
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90", destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90 active:text-destructive-foreground",
outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground", outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline", link: "text-primary underline-offset-4 hover:underline active:text-primary",
}, },
size: { size: {
default: "h-10 px-4 py-2", default: "h-10 px-4 py-2",

View File

@ -4,7 +4,129 @@ import { Check, ChevronDown, ChevronUp } from "lucide-react"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
const Select = SelectPrimitive.Root type SafeSelectProps = React.ComponentProps<typeof SelectPrimitive.Root> & {
debounceMs?: number;
leaveGraceMs?: number;
allowCloseAfterItemMs?: number
}
const Select = ({
children,
debounceMs = 200,
leaveGraceMs = 150,
allowCloseAfterItemMs = 250,
onOpenChange,
onValueChange,
open: controlledOpen,
...rest
}: SafeSelectProps) => {
const lastToggleRef = React.useRef<number>(0)
const isControlled = controlledOpen !== undefined
const [isOpen, setIsOpen] = React.useState<boolean>(false)
const pointerInside = React.useRef<boolean>(false)
const pointerLeaveTimer = React.useRef<number | null>(null)
const lastItemActivate = React.useRef<number>(0)
const handlePointerEnterContent = React.useCallback(() => {
pointerInside.current = true
if (pointerLeaveTimer.current) {
window.clearTimeout(pointerLeaveTimer.current)
pointerLeaveTimer.current = null
}
}, [])
const handlePointerLeaveContent = React.useCallback(() => {
if (pointerLeaveTimer.current) window.clearTimeout(pointerLeaveTimer.current)
pointerLeaveTimer.current = window.setTimeout(() => {
pointerInside.current = false
pointerLeaveTimer.current = null
}, leaveGraceMs) as unknown as number
}, [leaveGraceMs])
const handleItemActivate = React.useCallback(() => {
lastItemActivate.current = Date.now()
}, [])
const enhancedChildren = React.Children.map(children, (child) => {
if (!React.isValidElement(child)) return child
const type = (child.type as any)?.displayName || (child.type as any)?.name
if (
child.type === SelectPrimitive.Content ||
type === SelectPrimitive.Content?.displayName
) {
return React.cloneElement(child, {
onPointerEnter: (...args: any[]) => {
handlePointerEnterContent()
console.log("item enter")
const fn = (child.props as any).onPointerEnter
if (fn) fn(...args)
},
onPointerLeave: (...args: any[]) => {
handlePointerLeaveContent()
console.log("item leave")
const fn = (child.props as any).onPointerLeave
if (fn) fn(...args)
},
forceMount: true,
...child.props,
})
}
if (
child.type === SelectPrimitive.Item ||
type === SelectPrimitive.Item?.displayName
) {
return React.cloneElement(child, {
onPointerDown: (e: PointerEvent | React.PointerEvent) => {
handleItemActivate();
console.log("item active")
const fn = (child.props as any).onPointerDown;
if (fn) fn(e);
},
...child.props,
})
}
return child;
})
const setOpenInterval = React.useCallback(
(value: boolean) => {
const now = Date.now()
if (now - lastToggleRef.current < debounceMs) return
if (!value) {
if (now - lastItemActivate.current < allowCloseAfterItemMs) {
lastToggleRef.current = now
if (!isControlled) setIsOpen(false)
onOpenChange?.(false)
return
}
if (pointerInside.current) return
}
lastToggleRef.current = now
if (!isControlled) setIsOpen(value)
if (onOpenChange) onOpenChange(value)
},
[debounceMs, isControlled, onOpenChange, allowCloseAfterItemMs]
)
return (
<SelectPrimitive.Root
{...rest}
open={isControlled ? controlledOpen : isOpen}
onOpenChange={setOpenInterval}
onValueChange={(value) => {
onValueChange?.(value)
setIsOpen(false)
}}
>
{enhancedChildren}
</SelectPrimitive.Root>
)
}
const SelectGroup = SelectPrimitive.Group const SelectGroup = SelectPrimitive.Group

View File

@ -1,17 +1,45 @@
export const wellKnownGenesisHashByChainId: Record<string, string> = { type Network = {
casper_staging_testnet: "0x07074eb5f47a6f4dd70430674e5174d5414bc055292b90392fb6f0a28c7524d1", chainId: string
label: string
value: string
logo: string
prefix: number
decimals: number
} }
export const wellKnownChainIdByGenesisHash: Record<string, string> = { export const networks: Network[] = [
"0x07074eb5f47a6f4dd70430674e5174d5414bc055292b90392fb6f0a28c7524d1": "casper_staging_testnet", {
chainId: "0xa217f4ee58a944470e9633ca5bd6d28a428ed64cd9b6f3e413565f359f89af90",
value: "casper_staging_testnet",
label: "CASPER",
logo: "https://cryptologos.cc/logos/ghostchain-logo.svg",
prefix: 1996,
decimals: 18,
} }
]
export const wellKnownPrefixByGenesisHash: Record<string, number> = { export const wellKnownGenesisHashByChainId: Record<string, string> =
"0x07074eb5f47a6f4dd70430674e5174d5414bc055292b90392fb6f0a28c7524d1": 1996, networks.reduce((wellKnownChainIds, { chainId, value }) => {
} wellKnownChainIds[value] = chainId
return wellKnownChainIds
}, {})
export const wellKnownDecimalsByGenesisHash: Record<string, number> = { export const wellKnownChainIdByGenesisHash: Record<string, string> =
"0x07074eb5f47a6f4dd70430674e5174d5414bc055292b90392fb6f0a28c7524d1": 18, networks.reduce((wellKnownChainName, { chainId, value }) => {
} wellKnownChainName[chainId] = value
return wellKnownChainName
}, {})
export const wellKnownPrefixByGenesisHash: Record<string, number> =
networks.reduce((wellKnownPrefixes, { chainId, prefix }) => {
wellKnownPrefixes[chainId] = prefix
return wellKnownPrefixes
}, {})
export const wellKnownDecimalsByGenesisHash: Record<string, number> =
networks.reduce((wellKnownDecimals, { decimals, chainId }) => {
wellKnownDecimals[chainId] = decimals
return wellKnownDecimals
}, {})
export const CHANNEL_ID = "ghost-extension" export const CHANNEL_ID = "ghost-extension"

View File

@ -177,7 +177,7 @@ export const Options: FunctionComponent = () => {
> >
<FaGithub className="w-8 h-8" /> <FaGithub className="w-8 h-8" />
<div className="block float-left text-xs text-left"> <div className="block float-left text-xs text-left">
<div className="md:block hidden text-primary">Ghost Wallet Git</div> <div className="md:block hidden text-primary">GHOST Wallet Git</div>
<div className="text-accent">v {pckg.version}</div> <div className="text-accent">v {pckg.version}</div>
</div> </div>
</Link> </Link>

View File

@ -81,7 +81,6 @@ export const AccountDetails: React.FC = () => {
const wrappedRequestSecretKey = pendingWrapper(requestSecretKey) const wrappedRequestSecretKey = pendingWrapper(requestSecretKey)
const wrappedDeleteCryptoKey = pendingWrapper(deleteCryptoKey) const wrappedDeleteCryptoKey = pendingWrapper(deleteCryptoKey)
console.log(api)
return ( return (
<Layout2 innerClassName="bg-muted"> <Layout2 innerClassName="bg-muted">
<Header /> <Header />

View File

@ -26,7 +26,7 @@ import {
} from "@polkadot-labs/hdkd" } from "@polkadot-labs/hdkd"
import React from "react" import React from "react"
import { Header, BottomNavBar } from "../../components" import { Header, BottomNavBar } from "../../components"
import { networks } from "./networks" import { networks } from "@/constants"
type Scheme = "Sr25519" | "Ed25519" | "Ecdsa" type Scheme = "Sr25519" | "Ed25519" | "Ecdsa"
@ -98,7 +98,7 @@ export const Accounts = () => {
) )
const [selectedCryptoKeyName, setSelectedCryptoKeyName] = useState<string | undefined>() const [selectedCryptoKeyName, setSelectedCryptoKeyName] = useState<string | undefined>()
const [isPending, setIsPending] = useState<boolean>() const [isPending, setIsPending] = useState<boolean>(false)
const [error, setError] = useState<string>() const [error, setError] = useState<string>()
useEffect(() => { useEffect(() => {
@ -170,7 +170,7 @@ export const Accounts = () => {
<Select <Select
disabled={!cryptoKeys || cryptoKeys.length === 0} disabled={!cryptoKeys || cryptoKeys.length === 0}
value={selectedCryptoKeyName} value={selectedCryptoKeyName}
onValueChange={(v) => setSelectedCryptoKeyName(v)} onValueChange={(value) => setSelectedCryptoKeyName(value)}
> >
<SelectTrigger className="w-[235px]" data-testid="accounts-select"> <SelectTrigger className="w-[235px]" data-testid="accounts-select">
<SelectValue placeholder="Select Crypto Key" /> <SelectValue placeholder="Select Crypto Key" />

View File

@ -5,7 +5,7 @@ import {
generateMnemonic, generateMnemonic,
mnemonicToEntropy, mnemonicToEntropy,
} from "@polkadot-labs/hdkd-helpers" } from "@polkadot-labs/hdkd-helpers"
import { networks } from "./networks" import { networks } from "@/constants"
import { SubmitHandler, useForm } from "react-hook-form" import { SubmitHandler, useForm } from "react-hook-form"
import { toHex } from "@polkadot-api/utils" import { toHex } from "@polkadot-api/utils"
import { useNavigate } from "react-router-dom" import { useNavigate } from "react-router-dom"

View File

@ -20,7 +20,7 @@ import {
ed25519CreateDerive, ed25519CreateDerive,
sr25519CreateDerive, sr25519CreateDerive,
} from "@polkadot-labs/hdkd" } from "@polkadot-labs/hdkd"
import { networks } from "./networks" import { networks } from "@/constants"
import { Layout2 } from "@/components/Layout2" import { Layout2 } from "@/components/Layout2"
import { BottomNavBar, Header } from "../../components" import { BottomNavBar, Header } from "../../components"
import { Button } from "@/components/ui/button" import { Button } from "@/components/ui/button"
@ -169,7 +169,6 @@ export function ImportAccounts() {
} }
const onSubmit: SubmitHandler<FormFields> = async (data) => { const onSubmit: SubmitHandler<FormFields> = async (data) => {
console.log("started")
let errorOccured = null let errorOccured = null
const keynameExists = await rpc.client.getCryptoKey(data.keyname) const keynameExists = await rpc.client.getCryptoKey(data.keyname)
@ -410,10 +409,9 @@ export function ImportAccounts() {
<FormItem className="mb-4"> <FormItem className="mb-4">
<FormControl> <FormControl>
<Select <Select
{...field}
disabled={isSubmitting} disabled={isSubmitting}
onValueChange={(scheme) => field.onChange(scheme)} onValueChange={(scheme) => field.onChange(scheme)}
name={field.name}
value={field.value}
> >
<SelectTrigger <SelectTrigger
className={`${field.value ? "text-primary" : "text-muted-foreground"} w-full`} className={`${field.value ? "text-primary" : "text-muted-foreground"} w-full`}

View File

@ -1,17 +0,0 @@
export type Network = {
chainId: string
label: string
value: string
logo: string
prefix: number
}
export const networks: Network[] = [
{
chainId: "0x07074eb5f47a6f4dd70430674e5174d5414bc055292b90392fb6f0a28c7524d1",
value: "casper_staging_testnet",
label: "Casper",
logo: "https://cryptologos.cc/logos/ghostchain-logo.svg",
prefix: 1996,
}
]

View File

@ -46,7 +46,7 @@ export const Welcome = () => {
<li className="flex items-center gap-3"> <li className="flex items-center gap-3">
<Signal className="w-8 h-8 text-primary" aria-hidden="true" /> <Signal className="w-8 h-8 text-primary" aria-hidden="true" />
<p className="ml-3 text-base"> <p className="ml-3 text-base">
Be connected to the Ghost and Casper networks from your browser Be connected to the GHOST and CASPER networks from your browser
</p> </p>
</li> </li>
<li className="flex items-center gap-3"> <li className="flex items-center gap-3">
@ -64,7 +64,7 @@ export const Welcome = () => {
<li className="flex items-center gap-3"> <li className="flex items-center gap-3">
<GlobeLock className="w-8 h-8 text-primary" aria-hidden="true" /> <GlobeLock className="w-8 h-8 text-primary" aria-hidden="true" />
<p className="ml-3 text-base"> <p className="ml-3 text-base">
Make your browser be part of the Ghost and Casper networks Make your browser be part of the GHOST and CASPER networks
</p> </p>
</li> </li>
</ul> </ul>

View File

@ -1,3 +1,5 @@
import { networks } from "@/constants"
export type StorageEntry = { type: "braveSetting" } export type StorageEntry = { type: "braveSetting" }
export type StorageEntryType<E extends StorageEntry> = export type StorageEntryType<E extends StorageEntry> =
@ -7,7 +9,7 @@ export async function getDefaultBootnodes(
chain: string, chain: string,
): Promise<string[] | undefined> { ): Promise<string[] | undefined> {
if ( if (
["casper_staging_testnet"].includes(chain) networks.map(network => network.value).includes(chain)
) { ) {
const bootNodes = ( const bootNodes = (
await ( await (

View File

@ -16,7 +16,7 @@ import type { InPageRpcSpec } from "./types"
const PROVIDER_INFO = { const PROVIDER_INFO = {
uuid: crypto.randomUUID(), uuid: crypto.randomUUID(),
name: "Ghost Wallet Browser Extension", name: "GHOST Wallet Browser Extension",
icon: "data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'/>", icon: "data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'/>",
rdns: "io.ghostchain.GhostWalletExtension", rdns: "io.ghostchain.GhostWalletExtension",
} }