Improve checks and warnings while connecting to wallets #10

Merged
nabarun merged 2 commits from sk-reset-state into main 2025-07-30 03:12:15 +00:00
3 changed files with 57 additions and 24 deletions

View File

@ -23,10 +23,10 @@ NEXT_PUBLIC_REGISTRY_GQL_ENDPOINT=https://laconicd-mainnet-1.laconic.com/graphql
NEXT_PUBLIC_ALNT_COST_LRN=lrn://laconic/pricing/alnt
NEXT_PUBLIC_DEPLOYMENT_COST_LRN=lrn://laconic/pricing/webapp-deployment
REGISTRY_GAS_PRICE=0.001
REGISTRY_BOND_ID=5d82586d156fb6671a9170d92f930a72a49a29afb45e30e16fff2100e30776e2
REGISTRY_AUTHORITY=laconic-deploy
REGISTRY_BOND_ID=5d82586d156fb6671a9170d92f930a72a49a29afb45e30e16fff2100e30776e2
REGISTRY_USER_KEY=
# Application Configuration
DEPLOYER_LRN=
NEXT_PUBLIC_DOMAIN_SUFFIX=
DEPLOYER_LRN=lrn://vaasl-provider/deployers/webapp-deployer-api.apps.vaasl.io
NEXT_PUBLIC_DOMAIN_SUFFIX=apps.vaasl.io

View File

@ -1,6 +1,6 @@
'use client';
import { useCallback, useEffect, useState, useRef } from 'react';
import { useCallback, useEffect, useState, useRef, useMemo } from 'react';
import dynamic from 'next/dynamic';
import { WalletMultiButton } from '@solana/wallet-adapter-react-ui';
@ -27,6 +27,14 @@ const PaymentModal = dynamic(() => import('@/components/PaymentModal'), { ssr: f
// RPC endpoint reference: https://docs.gorbagana.wtf/testnet-v2-devnet.html
const GORBAGANA_GENESIS_HASH = '533uBE9RRquhTBqEX58oV52FdTTsReMdAvaUvP6hNjsn';
// Use following curl request to get Solana chain genesis hash:
// curl https://api.mainnet-beta.solana.com \
// -X POST \
// -H "Content-Type: application/json" \
// --data '{"jsonrpc":"2.0","id":1,"method":"getGenesisHash"}'
// RPC endpoint reference: https://solana.com/docs/references/clusters#on-a-high-level
const SOLANA_GENESIS_HASH = '5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d';
export default function Home() {
const { wallet, connected, publicKey, disconnect } = useWallet();
@ -42,7 +50,8 @@ export default function Home() {
const [appName, setAppName] = useState<string | null>(null);
const [repoUrl, setRepoUrl] = useState<string | null>(null);
const [error, setError] = useState<string | null>(null);
const [incorrectChainWarining, setIncorrectChainWarining] = useState<string | null>(null);
const [incorrectChainWarning, setIncorrectChainWarning] = useState<string | null>(null);
const [isFetchingChainGenesisHash, setIsFetchingChainGenesisHash] = useState(false);
useEffect(() => {
if (!IS_NAT_GOR_TRANSFER_ENABLED) {
@ -51,20 +60,38 @@ export default function Home() {
}, [setSelectedPaymentMethod]);
useEffect(() => {
if (!wallet || wallet.adapter.name !== BackpackWalletName || selectedPaymentMethod !== PaymentMethod.NAT_GOR) {
setIncorrectChainWarning(null);
if (!wallet || wallet.adapter.name !== BackpackWalletName || !connected || !selectedPaymentMethod) {
return;
}
const warnOnIncorrectChain = async () => {
// @ts-expect-error: backpack exists on window object
const genesisHash = await window.backpack.solana.connection.getGenesisHash();
if (genesisHash !== GORBAGANA_GENESIS_HASH) {
setIncorrectChainWarining("WARNING: Unsupported chain selected in wallet. Please switch to Gorbagana chain")
setIsFetchingChainGenesisHash(true);
try {
// @ts-expect-error: backpack exists on window object
const genesisHash = await window.backpack.solana.connection.getGenesisHash();
const expectedGenesisHash = selectedPaymentMethod === PaymentMethod.NAT_GOR
? GORBAGANA_GENESIS_HASH
: SOLANA_GENESIS_HASH;
const expectedChainName = selectedPaymentMethod === PaymentMethod.NAT_GOR
? "Gorbagana"
: "Solana";
if (genesisHash !== expectedGenesisHash) {
setIncorrectChainWarning(
`Unsupported network selected in wallet. Please switch to network for ${expectedChainName} chain and reconnect the wallet.`
);
}
} finally {
setIsFetchingChainGenesisHash(false);
}
}
warnOnIncorrectChain();
}, [wallet, selectedPaymentMethod]);
}, [wallet, selectedPaymentMethod, connected]);
// Track previous payment method to detect switches
const previousPaymentMethodRef = useRef<PaymentMethod | null>(null);
@ -90,7 +117,7 @@ export default function Home() {
};
// Helper function to check if current wallet is compatible with selected payment method
const isWalletCompatible = () => {
const isWalletCompatible = useMemo(() => {
if (!selectedPaymentMethod || !wallet) return false;
const walletName = wallet.adapter.name.toLowerCase();
@ -99,9 +126,9 @@ export default function Home() {
if (selectedPaymentMethod === PaymentMethod.NAT_GOR) {
return isBackpack; // Only Backpack for native GOR
} else {
return !isBackpack; // Only non-Backpack wallets for SPL tokens
return true; // Only non-Backpack wallets for SPL tokens
}
};
}, [selectedPaymentMethod, wallet]);
const handlePaymentComplete = useCallback(async (hash: string, paymentMethod: PaymentMethod) => {
if (!publicKey || !url) {
@ -193,7 +220,7 @@ export default function Home() {
<div className="text-left">
<h3 className="font-semibold text-lg mb-2">{process.env.NEXT_PUBLIC_SOLANA_TOKEN_SYMBOL} Token</h3>
<p className="text-xs mt-1" style={{ color: 'var(--muted-foreground)' }}>
Compatible with: Phantom, Solflare
Compatible with: All Solana compatible wallets
</p>
</div>
</button>
@ -225,23 +252,28 @@ export default function Home() {
<div className="flex flex-col items-center space-y-3">
<div className="flex items-center">
<span className="w-3 h-3 rounded-full mr-2" style={{
backgroundColor: isWalletCompatible() ? 'var(--success)' : 'var(--destructive)'
backgroundColor: isWalletCompatible ? 'var(--success)' : 'var(--destructive)'
}}></span>
<p className="font-medium" style={{
color: isWalletCompatible() ? 'var(--success)' : 'var(--destructive)'
color: isWalletCompatible ? 'var(--success)' : 'var(--destructive)'
}}>
{isWalletCompatible() ? 'Compatible' : 'Incompatible'} Wallet ({wallet?.adapter.name})
{isWalletCompatible ? 'Compatible' : 'Incompatible'} Wallet ({wallet?.adapter.name})
</p>
</div>
{!isWalletCompatible() && (
{!isWalletCompatible && (
<p className="text-sm text-amber-400">
This wallet is not compatible with {PAYMENT_METHOD_LABELS[selectedPaymentMethod]} payments.
Please select a different wallet or payment method.
</p>
)}
{incorrectChainWarining && (
{ isFetchingChainGenesisHash && (
<p className="text-sm text-muted-foreground">
Checking wallet network compatibility...
</p>
)}
{incorrectChainWarning && (
<p className="text-sm text-amber-400">
{incorrectChainWarining}
{incorrectChainWarning}
</p>
)}
<div className="w-full p-3 rounded-md" style={{ background: 'var(--card-bg)', border: '1px solid var(--card-border)' }}>
@ -276,18 +308,19 @@ export default function Home() {
</div>
)}
</div>
{/* Step 3: URL Input */}
<div className="mb-8 p-6 rounded-lg" style={{
background: 'var(--muted-light)',
borderLeft: '4px solid var(--primary)',
opacity: (connected && isWalletCompatible()) ? '1' : '0.6'
opacity: (connected && isWalletCompatible) ? '1' : '0.6'
}}>
<h2 className="text-lg font-semibold mb-4 flex items-center">
Enter URL to Deploy
</h2>
<URLForm
onSubmit={handleUrlSubmit}
disabled={!connected || !isWalletCompatible() || status === 'creating'}
disabled={!connected || !isWalletCompatible || isFetchingChainGenesisHash || status === 'creating' || Boolean(incorrectChainWarning)}
/>
</div>

View File

@ -103,7 +103,7 @@ export default function URLForm({ onSubmit, disabled }: URLFormProps) {
opacity: (disabled || !url) ? '0.7' : '1',
}}
>
{disabled ? 'Connect Wallet First' : 'Deploy URL'}
Deploy URL
</button>
</form>
);