feat: add XCM teleport and CI/CD deployment workflow

Features:
- Add XCMTeleportModal for cross-chain HEZ transfers
- Support Asset Hub and People Chain teleports
- Add "Fund Fees" button with user-friendly tooltips
- Use correct XCM V3 format with teyrchain junction

Fixes:
- Fix PEZ transfer to use Asset Hub API
- Silence unnecessary pallet availability warnings
- Fix transaction loading performance (10 blocks limit)
- Remove Supabase admin_roles dependency

CI/CD:
- Add auto-deploy to VPS on main branch push
- Add version bumping on deploy
- Upload build artifacts for deployment
This commit is contained in:
2026-02-04 11:35:25 +03:00
parent 9d57473000
commit 02094a3635
18 changed files with 1049 additions and 113 deletions
+3 -16
View File
@@ -134,23 +134,10 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
return true;
}
// SECONDARY: Check Supabase admin_roles (if wallet not in whitelist)
const { data: { user } } = await supabase.auth.getUser();
if (user) {
const { data, error } = await supabase
.from('admin_roles')
.select('role')
.eq('user_id', user.id)
.maybeSingle();
// SECONDARY: Supabase admin_roles check disabled (table may not exist)
// Admin access is primarily wallet-based via the whitelist above
if (!error && data && ['admin', 'super_admin'].includes(data.role)) {
if (import.meta.env.DEV) console.log('✅ Admin access granted (Supabase-based)');
setIsAdmin(true);
return true;
}
}
if (import.meta.env.DEV) console.log('❌ Admin access denied');
if (import.meta.env.DEV) console.log('❌ Admin access denied (wallet not in whitelist)');
setIsAdmin(false);
return false;
} catch (err) {
+57 -1
View File
@@ -5,14 +5,17 @@ import type { InjectedAccountWithMeta } from '@pezkuwi/extension-inject/types';
import { DEFAULT_ENDPOINT } from '../../../shared/blockchain/pezkuwi';
import { isMobileApp, getNativeWalletAddress, getNativeAccountName } from '@/lib/mobile-bridge';
// Asset Hub endpoint for PEZ token queries
// Parachain endpoints
const ASSET_HUB_ENDPOINT = 'wss://asset-hub-rpc.pezkuwichain.io';
const PEOPLE_CHAIN_ENDPOINT = 'wss://people-rpc.pezkuwichain.io';
interface PezkuwiContextType {
api: ApiPromise | null;
assetHubApi: ApiPromise | null;
peopleApi: ApiPromise | null;
isApiReady: boolean;
isAssetHubReady: boolean;
isPeopleReady: boolean;
isConnected: boolean;
accounts: InjectedAccountWithMeta[];
selectedAccount: InjectedAccountWithMeta | null;
@@ -36,8 +39,10 @@ export const PezkuwiProvider: React.FC<PezkuwiProviderProps> = ({
}) => {
const [api, setApi] = useState<ApiPromise | null>(null);
const [assetHubApi, setAssetHubApi] = useState<ApiPromise | null>(null);
const [peopleApi, setPeopleApi] = useState<ApiPromise | null>(null);
const [isApiReady, setIsApiReady] = useState(false);
const [isAssetHubReady, setIsAssetHubReady] = useState(false);
const [isPeopleReady, setIsPeopleReady] = useState(false);
const [accounts, setAccounts] = useState<InjectedAccountWithMeta[]>([]);
const [selectedAccount, setSelectedAccount] = useState<InjectedAccountWithMeta | null>(null);
const [error, setError] = useState<string | null>(null);
@@ -105,6 +110,17 @@ export const PezkuwiProvider: React.FC<PezkuwiProviderProps> = ({
if (import.meta.env.DEV) console.log(`📡 Chain: ${chain}`);
if (import.meta.env.DEV) console.log(`🖥️ Node: ${nodeName} v${nodeVersion}`);
// Debug: Check Junction type definition
try {
const junctionType = apiInstance.createType('XcmV3Junction');
console.log('🔍 XCM Junction type keys:', (junctionType as any).defKeys || Object.keys(junctionType.toJSON() || {}));
// Expose api for console debugging
(window as any).__PEZKUWI_API__ = apiInstance;
console.log('💡 API exposed as window.__PEZKUWI_API__ for debugging');
} catch (e) {
console.log('⚠️ Could not check Junction type:', e);
}
}
// Fetch sudo key from blockchain
@@ -168,8 +184,43 @@ export const PezkuwiProvider: React.FC<PezkuwiProviderProps> = ({
}
};
// Initialize People Chain API for identity/citizenship
const initPeopleApi = async () => {
try {
if (import.meta.env.DEV) {
console.log('🔗 Connecting to People Chain:', PEOPLE_CHAIN_ENDPOINT);
}
const provider = new WsProvider(PEOPLE_CHAIN_ENDPOINT);
const peopleApiInstance = await ApiPromise.create({
provider,
signedExtensions: {
AuthorizeCall: {
extrinsic: {},
payload: {},
},
},
});
await peopleApiInstance.isReady;
setPeopleApi(peopleApiInstance);
setIsPeopleReady(true);
if (import.meta.env.DEV) {
console.log('✅ Connected to People Chain for identity');
}
} catch (err) {
if (import.meta.env.DEV) {
console.error('❌ Failed to connect to People Chain:', err);
}
// Don't set error - Identity features just won't work
}
};
initApi();
initAssetHubApi();
initPeopleApi();
return () => {
if (api) {
@@ -178,6 +229,9 @@ export const PezkuwiProvider: React.FC<PezkuwiProviderProps> = ({
if (assetHubApi) {
assetHubApi.disconnect();
}
if (peopleApi) {
peopleApi.disconnect();
}
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [endpoint]);
@@ -357,8 +411,10 @@ export const PezkuwiProvider: React.FC<PezkuwiProviderProps> = ({
const value: PezkuwiContextType = {
api,
assetHubApi,
peopleApi,
isApiReady,
isAssetHubReady,
isPeopleReady,
isConnected: isApiReady, // Alias for backward compatibility
accounts,
selectedAccount,
+2 -7
View File
@@ -53,15 +53,10 @@ export const WebSocketProvider: React.FC<{ children: React.ReactNode }> = ({ chi
const connectionAttempts = useRef(0);
const connect = useCallback((endpointIndex: number = 0) => {
// If we've tried all endpoints, show error once and stop
// If we've tried all endpoints, stop silently (WebSocket is optional)
if (endpointIndex >= ENDPOINTS.length) {
if (!hasShownFinalError.current) {
if (import.meta.env.DEV) console.error('❌ All WebSocket endpoints failed');
toast({
title: "Real-time Connection Unavailable",
description: "Could not connect to WebSocket server. Live updates will be disabled.",
variant: "destructive",
});
// WebSocket service is optional - fail silently
hasShownFinalError.current = true;
}
return;