diff --git a/mobile/App.tsx b/mobile/App.tsx index 82977e1e..69f5dff3 100644 --- a/mobile/App.tsx +++ b/mobile/App.tsx @@ -3,6 +3,7 @@ import { View, ActivityIndicator, StyleSheet } from 'react-native'; import { StatusBar } from 'expo-status-bar'; import { initializeI18n } from './src/i18n'; import { LanguageProvider } from './src/contexts/LanguageContext'; +import { PolkadotProvider } from './src/contexts/PolkadotContext'; import AppNavigator from './src/navigation/AppNavigator'; import { KurdistanColors } from './src/theme/colors'; @@ -34,10 +35,12 @@ export default function App() { } return ( - - - - + + + + + + ); } diff --git a/mobile/package-lock.json b/mobile/package-lock.json index 02fbe593..3fe98527 100644 --- a/mobile/package-lock.json +++ b/mobile/package-lock.json @@ -8,7 +8,12 @@ "name": "mobile", "version": "1.0.0", "dependencies": { + "@polkadot/api": "^16.5.2", + "@polkadot/keyring": "^13.5.8", + "@polkadot/util": "^13.5.8", + "@polkadot/util-crypto": "^13.5.8", "@react-native-async-storage/async-storage": "^2.2.0", + "@react-navigation/bottom-tabs": "^7.8.5", "@react-navigation/native": "^7.1.20", "@react-navigation/stack": "^7.6.4", "expo": "~54.0.23", @@ -19,7 +24,8 @@ "react-i18next": "^16.3.3", "react-native": "0.81.5", "react-native-safe-area-context": "^5.6.2", - "react-native-screens": "^4.18.0" + "react-native-screens": "^4.18.0", + "react-native-vector-icons": "^10.3.0" }, "devDependencies": { "@types/react": "~19.1.0", @@ -2772,6 +2778,33 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@noble/curves": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz", + "integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -2782,6 +2815,607 @@ "node": ">=14" } }, + "node_modules/@polkadot-api/json-rpc-provider": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@polkadot-api/json-rpc-provider/-/json-rpc-provider-0.0.1.tgz", + "integrity": "sha512-/SMC/l7foRjpykLTUTacIH05H3mr9ip8b5xxfwXlVezXrNVLp3Cv0GX6uItkKd+ZjzVPf3PFrDF2B2/HLSNESA==", + "license": "MIT", + "optional": true + }, + "node_modules/@polkadot-api/json-rpc-provider-proxy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/json-rpc-provider-proxy/-/json-rpc-provider-proxy-0.1.0.tgz", + "integrity": "sha512-8GSFE5+EF73MCuLQm8tjrbCqlgclcHBSRaswvXziJ0ZW7iw3UEMsKkkKvELayWyBuOPa2T5i1nj6gFOeIsqvrg==", + "license": "MIT", + "optional": true + }, + "node_modules/@polkadot-api/metadata-builders": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@polkadot-api/metadata-builders/-/metadata-builders-0.3.2.tgz", + "integrity": "sha512-TKpfoT6vTb+513KDzMBTfCb/ORdgRnsS3TDFpOhAhZ08ikvK+hjHMt5plPiAX/OWkm1Wc9I3+K6W0hX5Ab7MVg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@polkadot-api/substrate-bindings": "0.6.0", + "@polkadot-api/utils": "0.1.0" + } + }, + "node_modules/@polkadot-api/observable-client": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@polkadot-api/observable-client/-/observable-client-0.3.2.tgz", + "integrity": "sha512-HGgqWgEutVyOBXoGOPp4+IAq6CNdK/3MfQJmhCJb8YaJiaK4W6aRGrdQuQSTPHfERHCARt9BrOmEvTXAT257Ug==", + "license": "MIT", + "optional": true, + "dependencies": { + "@polkadot-api/metadata-builders": "0.3.2", + "@polkadot-api/substrate-bindings": "0.6.0", + "@polkadot-api/utils": "0.1.0" + }, + "peerDependencies": { + "@polkadot-api/substrate-client": "0.1.4", + "rxjs": ">=7.8.0" + } + }, + "node_modules/@polkadot-api/substrate-bindings": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/substrate-bindings/-/substrate-bindings-0.6.0.tgz", + "integrity": "sha512-lGuhE74NA1/PqdN7fKFdE5C1gNYX357j1tWzdlPXI0kQ7h3kN0zfxNOpPUN7dIrPcOFZ6C0tRRVrBylXkI6xPw==", + "license": "MIT", + "optional": true, + "dependencies": { + "@noble/hashes": "^1.3.1", + "@polkadot-api/utils": "0.1.0", + "@scure/base": "^1.1.1", + "scale-ts": "^1.6.0" + } + }, + "node_modules/@polkadot-api/substrate-client": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@polkadot-api/substrate-client/-/substrate-client-0.1.4.tgz", + "integrity": "sha512-MljrPobN0ZWTpn++da9vOvt+Ex+NlqTlr/XT7zi9sqPtDJiQcYl+d29hFAgpaeTqbeQKZwz3WDE9xcEfLE8c5A==", + "license": "MIT", + "optional": true, + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.0.1", + "@polkadot-api/utils": "0.1.0" + } + }, + "node_modules/@polkadot-api/utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/utils/-/utils-0.1.0.tgz", + "integrity": "sha512-MXzWZeuGxKizPx2Xf/47wx9sr/uxKw39bVJUptTJdsaQn/TGq+z310mHzf1RCGvC1diHM8f593KrnDgc9oNbJA==", + "license": "MIT", + "optional": true + }, + "node_modules/@polkadot/api": { + "version": "16.5.2", + "resolved": "https://registry.npmjs.org/@polkadot/api/-/api-16.5.2.tgz", + "integrity": "sha512-EOkxs7KTgcytIhIxlIhIMV8EQQ/5F3bFs4hIRIqVFPJhNQn3tbq130HiJbQmvOnlxa3PXCEu7XVoCL0zkV08YQ==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/api-augment": "16.5.2", + "@polkadot/api-base": "16.5.2", + "@polkadot/api-derive": "16.5.2", + "@polkadot/keyring": "^13.5.8", + "@polkadot/rpc-augment": "16.5.2", + "@polkadot/rpc-core": "16.5.2", + "@polkadot/rpc-provider": "16.5.2", + "@polkadot/types": "16.5.2", + "@polkadot/types-augment": "16.5.2", + "@polkadot/types-codec": "16.5.2", + "@polkadot/types-create": "16.5.2", + "@polkadot/types-known": "16.5.2", + "@polkadot/util": "^13.5.8", + "@polkadot/util-crypto": "^13.5.8", + "eventemitter3": "^5.0.1", + "rxjs": "^7.8.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/api-augment": { + "version": "16.5.2", + "resolved": "https://registry.npmjs.org/@polkadot/api-augment/-/api-augment-16.5.2.tgz", + "integrity": "sha512-gDExOFPNHERqhnc7/4Fikvx63lOR7bsMUs5lXfNi6H5X773zIecnH+QbgILK6OfB8w+HCiUoUriKyYvFsI0zrA==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/api-base": "16.5.2", + "@polkadot/rpc-augment": "16.5.2", + "@polkadot/types": "16.5.2", + "@polkadot/types-augment": "16.5.2", + "@polkadot/types-codec": "16.5.2", + "@polkadot/util": "^13.5.8", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/api-base": { + "version": "16.5.2", + "resolved": "https://registry.npmjs.org/@polkadot/api-base/-/api-base-16.5.2.tgz", + "integrity": "sha512-YbXY4/ocZVXjx3a3H3HzGa7qrZ2itttkZz4q9Rrba0QFPyeN06KnaDLqDSo3mvJ4EVbhpuYgiNp10/tBb1+Llw==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/rpc-core": "16.5.2", + "@polkadot/types": "16.5.2", + "@polkadot/util": "^13.5.8", + "rxjs": "^7.8.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/api-derive": { + "version": "16.5.2", + "resolved": "https://registry.npmjs.org/@polkadot/api-derive/-/api-derive-16.5.2.tgz", + "integrity": "sha512-QBL7Yu4qa+nWWBEgpzmxbNoVC2uXFv7WQGPgH0pT/37hfcMXtCwQ9p37e1dGDxkB6oq0Jt4YJCNwVUxTZfi2vg==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/api": "16.5.2", + "@polkadot/api-augment": "16.5.2", + "@polkadot/api-base": "16.5.2", + "@polkadot/rpc-core": "16.5.2", + "@polkadot/types": "16.5.2", + "@polkadot/types-codec": "16.5.2", + "@polkadot/util": "^13.5.8", + "@polkadot/util-crypto": "^13.5.8", + "rxjs": "^7.8.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/keyring": { + "version": "13.5.8", + "resolved": "https://registry.npmjs.org/@polkadot/keyring/-/keyring-13.5.8.tgz", + "integrity": "sha512-BiTvXuLVxDpUw0c2E0Jr9H/QQ1p8YM7XV4XUucodtV/hrDHHpfp5jNg6zeeRTpU+qSkOYQmgL2dzw0hyWORcUQ==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/util": "13.5.8", + "@polkadot/util-crypto": "13.5.8", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "13.5.8", + "@polkadot/util-crypto": "13.5.8" + } + }, + "node_modules/@polkadot/networks": { + "version": "13.5.8", + "resolved": "https://registry.npmjs.org/@polkadot/networks/-/networks-13.5.8.tgz", + "integrity": "sha512-e8wPLmTC/YtowkbkTG1BbeDy7PBKcclePSTZe72Xctx8kVssmAX6lKUQNk7tgu1BttGOhn6x9M8RXBBD4zB9Vw==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/util": "13.5.8", + "@substrate/ss58-registry": "^1.51.0", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/rpc-augment": { + "version": "16.5.2", + "resolved": "https://registry.npmjs.org/@polkadot/rpc-augment/-/rpc-augment-16.5.2.tgz", + "integrity": "sha512-wFMkvWNy3Cjjat+dVDnKeXP8brZK/WxEuDHYuEoyDziJstuBQdMoXhO97W3gvsXDp7OVI61xqLmsnEYo+HXwTw==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/rpc-core": "16.5.2", + "@polkadot/types": "16.5.2", + "@polkadot/types-codec": "16.5.2", + "@polkadot/util": "^13.5.8", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/rpc-core": { + "version": "16.5.2", + "resolved": "https://registry.npmjs.org/@polkadot/rpc-core/-/rpc-core-16.5.2.tgz", + "integrity": "sha512-Q+vcaqvLyVtRyKn7OuoEMJEB5EA1cq7axVwLpljbTzYwV1qENlubkEFR5TViVyg/zPH8G7eJ2hHzMgXf/14e0w==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/rpc-augment": "16.5.2", + "@polkadot/rpc-provider": "16.5.2", + "@polkadot/types": "16.5.2", + "@polkadot/util": "^13.5.8", + "rxjs": "^7.8.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/rpc-provider": { + "version": "16.5.2", + "resolved": "https://registry.npmjs.org/@polkadot/rpc-provider/-/rpc-provider-16.5.2.tgz", + "integrity": "sha512-NEgGQUwOjlMb+83BAOCuWTNrVJ7zTCX2y828bh18XWOpF8sCltjrqYu6ZYaUucFa43emJMBlrm5M+jhJI37ofQ==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/keyring": "^13.5.8", + "@polkadot/types": "16.5.2", + "@polkadot/types-support": "16.5.2", + "@polkadot/util": "^13.5.8", + "@polkadot/util-crypto": "^13.5.8", + "@polkadot/x-fetch": "^13.5.8", + "@polkadot/x-global": "^13.5.8", + "@polkadot/x-ws": "^13.5.8", + "eventemitter3": "^5.0.1", + "mock-socket": "^9.3.1", + "nock": "^13.5.5", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@substrate/connect": "0.8.11" + } + }, + "node_modules/@polkadot/types": { + "version": "16.5.2", + "resolved": "https://registry.npmjs.org/@polkadot/types/-/types-16.5.2.tgz", + "integrity": "sha512-Lsie9bCE/CxmxG76bpYnRU4/UcRTi5q9zYPtAjt9GbgPpUSr17mMqsWAitq+qFYF29Bxo9EvAbFkj9QxoFWNsA==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/keyring": "^13.5.8", + "@polkadot/types-augment": "16.5.2", + "@polkadot/types-codec": "16.5.2", + "@polkadot/types-create": "16.5.2", + "@polkadot/util": "^13.5.8", + "@polkadot/util-crypto": "^13.5.8", + "rxjs": "^7.8.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/types-augment": { + "version": "16.5.2", + "resolved": "https://registry.npmjs.org/@polkadot/types-augment/-/types-augment-16.5.2.tgz", + "integrity": "sha512-Psl96Fiolg3lVpRO/gbnPqBwXw6RNNbsRotvjG39O6r6OFiwkB61hfhIfaRSa+rSETQFDBpfa5O60hFA8w6Jvw==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/types": "16.5.2", + "@polkadot/types-codec": "16.5.2", + "@polkadot/util": "^13.5.8", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/types-codec": { + "version": "16.5.2", + "resolved": "https://registry.npmjs.org/@polkadot/types-codec/-/types-codec-16.5.2.tgz", + "integrity": "sha512-buhc+JckA1Xcaq8GssSLqsb6hTdEV87zT8X2ZWdn4MGPDfpZKAQAqWON51dYD/thfqclW502G7UMu1SynwXPjg==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/util": "^13.5.8", + "@polkadot/x-bigint": "^13.5.8", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/types-create": { + "version": "16.5.2", + "resolved": "https://registry.npmjs.org/@polkadot/types-create/-/types-create-16.5.2.tgz", + "integrity": "sha512-4Y+ZC/qXP6wH2GizJqr6WGRyiVLbr1EwbKXLc6jkGe5UsEHczx/B4ZQq3z1SOkIOgOsZ2EyH7R6HmH15lJXI+Q==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/types-codec": "16.5.2", + "@polkadot/util": "^13.5.8", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/types-known": { + "version": "16.5.2", + "resolved": "https://registry.npmjs.org/@polkadot/types-known/-/types-known-16.5.2.tgz", + "integrity": "sha512-5gaZngP/PiR751ZjulkOuz4dbql+hOFpotGX4hxhKfw4fVr0P0tdPmgKkC7koev93j3y16NdzIVhA3HaoEmEIg==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/networks": "^13.5.8", + "@polkadot/types": "16.5.2", + "@polkadot/types-codec": "16.5.2", + "@polkadot/types-create": "16.5.2", + "@polkadot/util": "^13.5.8", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/types-support": { + "version": "16.5.2", + "resolved": "https://registry.npmjs.org/@polkadot/types-support/-/types-support-16.5.2.tgz", + "integrity": "sha512-l9cTx9aDY9Qk2QuYgzn/npuNzVYag3mfqQG4vUt7/6qtDHVVCfyreGUBz5RHueYjem7R4m9byh6aFh0m6ljZgg==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/util": "^13.5.8", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/util": { + "version": "13.5.8", + "resolved": "https://registry.npmjs.org/@polkadot/util/-/util-13.5.8.tgz", + "integrity": "sha512-5xEfNoum/Ct+gYWN3AYvBQ8vq8KiS4HsY3BexPUPXvSXSx3Id/JYA5oFLYnkuRp8hwoQGjX0wqUJ6Hp8D8LHKw==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-bigint": "13.5.8", + "@polkadot/x-global": "13.5.8", + "@polkadot/x-textdecoder": "13.5.8", + "@polkadot/x-textencoder": "13.5.8", + "@types/bn.js": "^5.1.6", + "bn.js": "^5.2.1", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/util-crypto": { + "version": "13.5.8", + "resolved": "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-13.5.8.tgz", + "integrity": "sha512-3nnyqyZsrYkO3RkQn9opUnrJrQTR5/5LXgT3u/gCXrLPwjj6x8P7CZYJT2fn8aUVXbQe9iGM0SAs1mbG3aDCCQ==", + "license": "Apache-2.0", + "dependencies": { + "@noble/curves": "^1.3.0", + "@noble/hashes": "^1.3.3", + "@polkadot/networks": "13.5.8", + "@polkadot/util": "13.5.8", + "@polkadot/wasm-crypto": "^7.5.2", + "@polkadot/wasm-util": "^7.5.2", + "@polkadot/x-bigint": "13.5.8", + "@polkadot/x-randomvalues": "13.5.8", + "@scure/base": "^1.1.7", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "13.5.8" + } + }, + "node_modules/@polkadot/wasm-bridge": { + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-bridge/-/wasm-bridge-7.5.2.tgz", + "integrity": "sha512-P9qLGa+PFnBaaPLAYaYfzRV2kgavKJgzYXOIT4ESTeGfPyPQ51DWe4WKQACXHcZLJ2875okC0jHH9bu2ueUsQw==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/wasm-util": "7.5.2", + "tslib": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*", + "@polkadot/x-randomvalues": "*" + } + }, + "node_modules/@polkadot/wasm-crypto": { + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto/-/wasm-crypto-7.5.2.tgz", + "integrity": "sha512-1/J+qu0D1R6Xe5AKZitTGQNmYCmWm+oYFXWx/YG0OjFmEDqMblBwcgh2skwqE83IR87DIwYjHrLt34UMuPx39A==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/wasm-bridge": "7.5.2", + "@polkadot/wasm-crypto-asmjs": "7.5.2", + "@polkadot/wasm-crypto-init": "7.5.2", + "@polkadot/wasm-crypto-wasm": "7.5.2", + "@polkadot/wasm-util": "7.5.2", + "tslib": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*", + "@polkadot/x-randomvalues": "*" + } + }, + "node_modules/@polkadot/wasm-crypto-asmjs": { + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-asmjs/-/wasm-crypto-asmjs-7.5.2.tgz", + "integrity": "sha512-bb5k2yPuvSu1iyhTyTs9w0X3CUC2tYyqXL9o1Pfi9yiwNtlRFP1RScjSbYMBP7lmibOJ466i1P26w4UYYV/P0g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*" + } + }, + "node_modules/@polkadot/wasm-crypto-init": { + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-init/-/wasm-crypto-init-7.5.2.tgz", + "integrity": "sha512-mT+UVAjwDL8VW0pjAn1TjC3XdkaXm3WZ8vLwCjIeWnrsW33QCVLwGrcaUGWejrTTWAzZmfTGdtIDfaSM5QUSNQ==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/wasm-bridge": "7.5.2", + "@polkadot/wasm-crypto-asmjs": "7.5.2", + "@polkadot/wasm-crypto-wasm": "7.5.2", + "@polkadot/wasm-util": "7.5.2", + "tslib": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*", + "@polkadot/x-randomvalues": "*" + } + }, + "node_modules/@polkadot/wasm-crypto-wasm": { + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-wasm/-/wasm-crypto-wasm-7.5.2.tgz", + "integrity": "sha512-Tf2HvEJ//HnGalel/FaHfd8wvHYsqY0IYVu5nCeLmbo7HGNR+apEXKIJ7W0kcXg+2U7JhxpT8KspMJg+e3f0kA==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/wasm-util": "7.5.2", + "tslib": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*" + } + }, + "node_modules/@polkadot/wasm-util": { + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-util/-/wasm-util-7.5.2.tgz", + "integrity": "sha512-DIKy6CMiPsbguU5nUHz/hnD05eZmkT7R/E70cotk+QMaQWT189syJ05Z/YGQD/JdPoRf4uVNA5xHCWLikmzwZQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*" + } + }, + "node_modules/@polkadot/x-bigint": { + "version": "13.5.8", + "resolved": "https://registry.npmjs.org/@polkadot/x-bigint/-/x-bigint-13.5.8.tgz", + "integrity": "sha512-4ltTNgFDZoPnuQBrP7Z3m3imQ3xKb7jKScAT/Gy89h9siLYyJdZ+qawZfO1cll6fqYlka+k7USqGeyOEqoyCfg==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-global": "13.5.8", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/x-fetch": { + "version": "13.5.8", + "resolved": "https://registry.npmjs.org/@polkadot/x-fetch/-/x-fetch-13.5.8.tgz", + "integrity": "sha512-htNuY8zFw5vzNS2mFm9P22oBJg7Az8Xbg3fMmR/A6ZDhAfxfM6IbX9pPHkNJY2Wng3tysrY5VMOUMb1IIrSf3w==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-global": "13.5.8", + "node-fetch": "^3.3.2", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/x-global": { + "version": "13.5.8", + "resolved": "https://registry.npmjs.org/@polkadot/x-global/-/x-global-13.5.8.tgz", + "integrity": "sha512-KDK3CEG/RvfCu3w4HZ/iv6c49XrN5Hz/3mXUQdLfR+TFKADdNCoIhMZ9f7vHYgdnB9tlY9s6Dn2svY99h1wRiw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/x-randomvalues": { + "version": "13.5.8", + "resolved": "https://registry.npmjs.org/@polkadot/x-randomvalues/-/x-randomvalues-13.5.8.tgz", + "integrity": "sha512-u9Nw5wP2mruo2AzRLWmK4KrYStsaTWH86H96O/6aRSsse6E3QCoqTzwDTDHBT05PWekbDNa7qwKmgKw4UNJfPw==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-global": "13.5.8", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "13.5.8", + "@polkadot/wasm-util": "*" + } + }, + "node_modules/@polkadot/x-textdecoder": { + "version": "13.5.8", + "resolved": "https://registry.npmjs.org/@polkadot/x-textdecoder/-/x-textdecoder-13.5.8.tgz", + "integrity": "sha512-Uzz6spRDzzQDQBN6PNpjz0HVp2kqhQVJRh1ShLP9rBg+nH4we9VGriWGG5stkgNKjRGT0Z7cvx0FupRQoNDU4A==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-global": "13.5.8", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/x-textencoder": { + "version": "13.5.8", + "resolved": "https://registry.npmjs.org/@polkadot/x-textencoder/-/x-textencoder-13.5.8.tgz", + "integrity": "sha512-2jcVte6mUy+GXjpZsS7dFca8C2r8EGgaG5o7mVQZ+PjauD06O/UP2g48UuDJHGe1QCJN0f0WaoD+RNw9tOF2yQ==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-global": "13.5.8", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/x-ws": { + "version": "13.5.8", + "resolved": "https://registry.npmjs.org/@polkadot/x-ws/-/x-ws-13.5.8.tgz", + "integrity": "sha512-tEs69W3O7Y2lPGihOFWwSE91GkaMEAzJhkDouTfacBKwD6O2b1/Im97jBdxQBmi7kN3pAWGXXTk9sz8TCh30Ug==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-global": "13.5.8", + "tslib": "^2.8.0", + "ws": "^8.18.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/x-ws/node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/@react-native-async-storage/async-storage": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-2.2.0.tgz", @@ -3048,6 +3682,24 @@ "integrity": "sha512-0HuJ8YtqlTVRXGZuGeBejLE04wSQsibpTI+RGOyVqxZvgtlLLC/Ssw0UmbHhT4lYMp2fhdtvKZSs5emWB1zR/g==", "license": "MIT" }, + "node_modules/@react-navigation/bottom-tabs": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/@react-navigation/bottom-tabs/-/bottom-tabs-7.8.5.tgz", + "integrity": "sha512-Zm9UOTfEtBLL7Wm+JBc0v/lh72cen9a8WVN5KSCEN7EtiQIPXbQUZg1ktEzme600HhxvaNZzzSz0X+w2E5nG5w==", + "license": "MIT", + "dependencies": { + "@react-navigation/elements": "^2.8.2", + "color": "^4.2.3", + "sf-symbols-typescript": "^2.1.0" + }, + "peerDependencies": { + "@react-navigation/native": "^7.1.20", + "react": ">= 18.2.0", + "react-native": "*", + "react-native-safe-area-context": ">= 4.0.0", + "react-native-screens": ">= 4.0.0" + } + }, "node_modules/@react-navigation/core": { "version": "7.13.1", "resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-7.13.1.tgz", @@ -3165,6 +3817,15 @@ "react-native-screens": ">= 4.0.0" } }, + "node_modules/@scure/base": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz", + "integrity": "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==", + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -3189,6 +3850,59 @@ "@sinonjs/commons": "^3.0.0" } }, + "node_modules/@substrate/connect": { + "version": "0.8.11", + "resolved": "https://registry.npmjs.org/@substrate/connect/-/connect-0.8.11.tgz", + "integrity": "sha512-ofLs1PAO9AtDdPbdyTYj217Pe+lBfTLltdHDs3ds8no0BseoLeAGxpz1mHfi7zB4IxI3YyAiLjH6U8cw4pj4Nw==", + "deprecated": "versions below 1.x are no longer maintained", + "license": "GPL-3.0-only", + "optional": true, + "dependencies": { + "@substrate/connect-extension-protocol": "^2.0.0", + "@substrate/connect-known-chains": "^1.1.5", + "@substrate/light-client-extension-helpers": "^1.0.0", + "smoldot": "2.0.26" + } + }, + "node_modules/@substrate/connect-extension-protocol": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@substrate/connect-extension-protocol/-/connect-extension-protocol-2.2.2.tgz", + "integrity": "sha512-t66jwrXA0s5Goq82ZtjagLNd7DPGCNjHeehRlE/gcJmJ+G56C0W+2plqOMRicJ8XGR1/YFnUSEqUFiSNbjGrAA==", + "license": "GPL-3.0-only", + "optional": true + }, + "node_modules/@substrate/connect-known-chains": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/@substrate/connect-known-chains/-/connect-known-chains-1.10.3.tgz", + "integrity": "sha512-OJEZO1Pagtb6bNE3wCikc2wrmvEU5x7GxFFLqqbz1AJYYxSlrPCGu4N2og5YTExo4IcloNMQYFRkBGue0BKZ4w==", + "license": "GPL-3.0-only", + "optional": true + }, + "node_modules/@substrate/light-client-extension-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@substrate/light-client-extension-helpers/-/light-client-extension-helpers-1.0.0.tgz", + "integrity": "sha512-TdKlni1mBBZptOaeVrKnusMg/UBpWUORNDv5fdCaJklP4RJiFOzBCrzC+CyVI5kQzsXBisZ+2pXm+rIjS38kHg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@polkadot-api/json-rpc-provider": "^0.0.1", + "@polkadot-api/json-rpc-provider-proxy": "^0.1.0", + "@polkadot-api/observable-client": "^0.3.0", + "@polkadot-api/substrate-client": "^0.1.2", + "@substrate/connect-extension-protocol": "^2.0.0", + "@substrate/connect-known-chains": "^1.1.5", + "rxjs": "^7.8.1" + }, + "peerDependencies": { + "smoldot": "2.x" + } + }, + "node_modules/@substrate/ss58-registry": { + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/@substrate/ss58-registry/-/ss58-registry-1.51.0.tgz", + "integrity": "sha512-TWDurLiPxndFgKjVavCniytBIw+t4ViOi7TYp9h/D0NMmkEc9klFTo+827eyEJ0lELpqO207Ey7uGxUa+BS1jQ==", + "license": "Apache-2.0" + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -3230,6 +3944,15 @@ "@babel/types": "^7.28.2" } }, + "node_modules/@types/bn.js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.2.0.tgz", + "integrity": "sha512-DLbJ1BPqxvQhIGbeu8VbUC1DiAiahHtAYvA0ZEAa4P31F7IaArc8z3C3BRQdWX4mtLQuABG4yzp76ZrS02Ui1Q==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/graceful-fs": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", @@ -3827,6 +4550,12 @@ "node": ">=0.6" } }, + "node_modules/bn.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.2.tgz", + "integrity": "sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw==", + "license": "MIT" + }, "node_modules/bplist-creator": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.1.0.tgz", @@ -4400,6 +5129,15 @@ "devOptional": true, "license": "MIT" }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -4626,6 +5364,12 @@ "node": ">=6" } }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "license": "MIT" + }, "node_modules/exec-async": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/exec-async/-/exec-async-2.2.0.tgz", @@ -5302,6 +6046,29 @@ "bser": "2.1.1" } }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -5397,6 +6164,18 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/freeport-async": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/freeport-async/-/freeport-async-2.0.0.tgz", @@ -6331,6 +7110,12 @@ "node": ">=6" } }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "license": "ISC" + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -7211,6 +7996,15 @@ "node": ">=10" } }, + "node_modules/mock-socket": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/mock-socket/-/mock-socket-9.3.1.tgz", + "integrity": "sha512-qxBgB7Qa2sEQgHFjj0dSigq7fX4k6Saisd5Nelwp2q8mlbAFh5dHV9JTTlF8viYJLSSWgMCZFUom8PJcMNBoJw==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -7261,6 +8055,58 @@ "integrity": "sha512-SrQrok4CATudVzBS7coSz26QRSmlK9TzzoFbeKfcPBUFPjcQM9Rqvr/DlJkOrwI/0KcgvMub1n1g5Jt9EgRn4A==", "license": "MIT" }, + "node_modules/nock": { + "version": "13.5.6", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.5.6.tgz", + "integrity": "sha512-o2zOYiCpzRqSzPj0Zt/dQ/DqZeYoaQ7TUonc/xUPjCGl9WeHpNbxgVvOquXYAaJzI0M9BXV3HTzG0p8IUAbBTQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.1.0", + "json-stringify-safe": "^5.0.1", + "propagate": "^2.0.0" + }, + "engines": { + "node": ">= 10.13" + } + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, "node_modules/node-forge": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", @@ -7718,6 +8564,32 @@ "node": ">= 6" } }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/propagate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", + "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -7957,6 +8829,143 @@ "react-native": "*" } }, + "node_modules/react-native-vector-icons": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/react-native-vector-icons/-/react-native-vector-icons-10.3.0.tgz", + "integrity": "sha512-IFQ0RE57819hOUdFvgK4FowM5aMXg7C7XKsuGLevqXkkIJatc3QopN0wYrb2IrzUgmdpfP+QVIbI3S6h7M0btw==", + "deprecated": "react-native-vector-icons package has moved to a new model of per-icon-family packages. See the https://github.com/oblador/react-native-vector-icons/blob/master/MIGRATION.md on how to migrate", + "license": "MIT", + "dependencies": { + "prop-types": "^15.7.2", + "yargs": "^16.1.1" + }, + "bin": { + "fa-upgrade.sh": "bin/fa-upgrade.sh", + "fa5-upgrade": "bin/fa5-upgrade.sh", + "fa6-upgrade": "bin/fa6-upgrade.sh", + "generate-icon": "bin/generate-icon.js" + } + }, + "node_modules/react-native-vector-icons/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/react-native-vector-icons/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/react-native-vector-icons/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/react-native-vector-icons/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/react-native-vector-icons/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/react-native-vector-icons/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/react-native-vector-icons/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/react-native-vector-icons/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/react-native-vector-icons/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/react-native-vector-icons/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, "node_modules/react-native/node_modules/@react-native/virtualized-lists": { "version": "0.81.5", "resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.81.5.tgz", @@ -8283,6 +9292,15 @@ "node": "*" } }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -8309,6 +9327,13 @@ "integrity": "sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ==", "license": "BlueOak-1.0.0" }, + "node_modules/scale-ts": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/scale-ts/-/scale-ts-1.6.1.tgz", + "integrity": "sha512-PBMc2AWc6wSEqJYBDPcyCLUj9/tMKnLX70jLOSndMtcUoLQucP/DM0vnQo1wJAYjTrQiq8iG9rD0q6wFzgjH7g==", + "license": "MIT", + "optional": true + }, "node_modules/scheduler": { "version": "0.26.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", @@ -8426,6 +9451,15 @@ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "license": "ISC" }, + "node_modules/sf-symbols-typescript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/sf-symbols-typescript/-/sf-symbols-typescript-2.1.0.tgz", + "integrity": "sha512-ezT7gu/SHTPIOEEoG6TF+O0m5eewl0ZDAO4AtdBi5HjsrUI6JdCG17+Q8+aKp0heM06wZKApRCn5olNbs0Wb/A==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -8515,6 +9549,38 @@ "node": ">=8.0.0" } }, + "node_modules/smoldot": { + "version": "2.0.26", + "resolved": "https://registry.npmjs.org/smoldot/-/smoldot-2.0.26.tgz", + "integrity": "sha512-F+qYmH4z2s2FK+CxGj8moYcd1ekSIKH8ywkdqlOz88Dat35iB1DIYL11aILN46YSGMzQW/lbJNS307zBSDN5Ig==", + "license": "GPL-3.0-or-later WITH Classpath-exception-2.0", + "optional": true, + "dependencies": { + "ws": "^8.8.1" + } + }, + "node_modules/smoldot/node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -9018,6 +10084,12 @@ "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", "license": "Apache-2.0" }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -9249,6 +10321,15 @@ "defaults": "^1.0.3" } }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, "node_modules/webidl-conversions": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", diff --git a/mobile/package.json b/mobile/package.json index 0809e6ad..aac06cd6 100644 --- a/mobile/package.json +++ b/mobile/package.json @@ -9,7 +9,12 @@ "web": "expo start --web" }, "dependencies": { + "@polkadot/api": "^16.5.2", + "@polkadot/keyring": "^13.5.8", + "@polkadot/util": "^13.5.8", + "@polkadot/util-crypto": "^13.5.8", "@react-native-async-storage/async-storage": "^2.2.0", + "@react-navigation/bottom-tabs": "^7.8.5", "@react-navigation/native": "^7.1.20", "@react-navigation/stack": "^7.6.4", "expo": "~54.0.23", @@ -20,7 +25,8 @@ "react-i18next": "^16.3.3", "react-native": "0.81.5", "react-native-safe-area-context": "^5.6.2", - "react-native-screens": "^4.18.0" + "react-native-screens": "^4.18.0", + "react-native-vector-icons": "^10.3.0" }, "devDependencies": { "@types/react": "~19.1.0", diff --git a/mobile/src/contexts/PolkadotContext.tsx b/mobile/src/contexts/PolkadotContext.tsx new file mode 100644 index 00000000..da19cb7d --- /dev/null +++ b/mobile/src/contexts/PolkadotContext.tsx @@ -0,0 +1,265 @@ +import React, { createContext, useContext, useEffect, useState, ReactNode } from 'react'; +import { ApiPromise, WsProvider } from '@polkadot/api'; +import { Keyring } from '@polkadot/keyring'; +import { KeyringPair } from '@polkadot/keyring/types'; +import AsyncStorage from '@react-native-async-storage/async-storage'; +import { cryptoWaitReady } from '@polkadot/util-crypto'; + +interface Account { + address: string; + name: string; + meta?: { + name?: string; + }; +} + +interface PolkadotContextType { + api: ApiPromise | null; + isApiReady: boolean; + isConnected: boolean; + accounts: Account[]; + selectedAccount: Account | null; + setSelectedAccount: (account: Account | null) => void; + connectWallet: () => Promise; + disconnectWallet: () => void; + createWallet: (name: string, mnemonic?: string) => Promise<{ address: string; mnemonic: string }>; + getKeyPair: (address: string) => Promise; + error: string | null; +} + +const PolkadotContext = createContext(undefined); + +const WALLET_STORAGE_KEY = '@pezkuwi_wallets'; +const SELECTED_ACCOUNT_KEY = '@pezkuwi_selected_account'; + +interface PolkadotProviderProps { + children: ReactNode; + endpoint?: string; +} + +export const PolkadotProvider: React.FC = ({ + children, + endpoint = 'wss://beta-rpc.pezkuwi.art', // Beta testnet RPC +}) => { + const [api, setApi] = useState(null); + const [isApiReady, setIsApiReady] = useState(false); + const [accounts, setAccounts] = useState([]); + const [selectedAccount, setSelectedAccount] = useState(null); + const [error, setError] = useState(null); + const [keyring, setKeyring] = useState(null); + + // Initialize crypto and keyring + useEffect(() => { + const initCrypto = async () => { + try { + await cryptoWaitReady(); + const kr = new Keyring({ type: 'sr25519' }); + setKeyring(kr); + console.log('βœ… Crypto libraries initialized'); + } catch (err) { + console.error('❌ Failed to initialize crypto:', err); + setError('Failed to initialize crypto libraries'); + } + }; + + initCrypto(); + }, []); + + // Initialize Polkadot API + useEffect(() => { + const initApi = async () => { + try { + console.log('πŸ”— Connecting to Pezkuwi node:', endpoint); + + const provider = new WsProvider(endpoint); + const apiInstance = await ApiPromise.create({ provider }); + + await apiInstance.isReady; + + setApi(apiInstance); + setIsApiReady(true); + setError(null); + + console.log('βœ… Connected to Pezkuwi node'); + + // Get chain info + const [chain, nodeName, nodeVersion] = await Promise.all([ + apiInstance.rpc.system.chain(), + apiInstance.rpc.system.name(), + apiInstance.rpc.system.version(), + ]); + + console.log(`πŸ“‘ Chain: ${chain}`); + console.log(`πŸ–₯️ Node: ${nodeName} v${nodeVersion}`); + } catch (err) { + console.error('❌ Failed to connect to node:', err); + setError(`Failed to connect to node: ${endpoint}`); + setIsApiReady(false); + } + }; + + initApi(); + + return () => { + if (api) { + api.disconnect(); + } + }; + }, [endpoint]); + + // Load stored accounts on mount + useEffect(() => { + const loadAccounts = async () => { + try { + const stored = await AsyncStorage.getItem(WALLET_STORAGE_KEY); + if (stored) { + const wallets = JSON.parse(stored); + setAccounts(wallets); + + // Load selected account + const selectedAddr = await AsyncStorage.getItem(SELECTED_ACCOUNT_KEY); + if (selectedAddr) { + const account = wallets.find((w: Account) => w.address === selectedAddr); + if (account) { + setSelectedAccount(account); + } + } + } + } catch (err) { + console.error('Failed to load accounts:', err); + } + }; + + loadAccounts(); + }, []); + + // Create a new wallet + const createWallet = async ( + name: string, + mnemonic?: string + ): Promise<{ address: string; mnemonic: string }> => { + if (!keyring) { + throw new Error('Keyring not initialized'); + } + + try { + // Generate or use provided mnemonic + const mnemonicPhrase = mnemonic || Keyring.prototype.generateMnemonic(); + + // Create account from mnemonic + const pair = keyring.addFromMnemonic(mnemonicPhrase, { name }); + + const newAccount: Account = { + address: pair.address, + name, + meta: { name }, + }; + + // Store account (address only, not the seed!) + const updatedAccounts = [...accounts, newAccount]; + setAccounts(updatedAccounts); + await AsyncStorage.setItem(WALLET_STORAGE_KEY, JSON.stringify(updatedAccounts)); + + // Store encrypted seed separately + const seedKey = `@pezkuwi_seed_${pair.address}`; + await AsyncStorage.setItem(seedKey, mnemonicPhrase); + + console.log('βœ… Wallet created:', pair.address); + + return { + address: pair.address, + mnemonic: mnemonicPhrase, + }; + } catch (err) { + console.error('❌ Failed to create wallet:', err); + throw new Error('Failed to create wallet'); + } + }; + + // Get keypair for signing transactions + const getKeyPair = async (address: string): Promise => { + if (!keyring) { + throw new Error('Keyring not initialized'); + } + + try { + // Load seed from storage + const seedKey = `@pezkuwi_seed_${address}`; + const mnemonic = await AsyncStorage.getItem(seedKey); + + if (!mnemonic) { + console.error('No seed found for address:', address); + return null; + } + + // Recreate keypair from mnemonic + const pair = keyring.addFromMnemonic(mnemonic); + return pair; + } catch (err) { + console.error('Failed to get keypair:', err); + return null; + } + }; + + // Connect wallet (load existing accounts) + const connectWallet = async () => { + try { + setError(null); + + if (accounts.length === 0) { + setError('No wallets found. Please create a wallet first.'); + return; + } + + // Auto-select first account if none selected + if (!selectedAccount && accounts.length > 0) { + setSelectedAccount(accounts[0]); + await AsyncStorage.setItem(SELECTED_ACCOUNT_KEY, accounts[0].address); + } + + console.log(`βœ… Connected with ${accounts.length} account(s)`); + } catch (err) { + console.error('❌ Wallet connection failed:', err); + setError('Failed to connect wallet'); + } + }; + + // Disconnect wallet + const disconnectWallet = () => { + setSelectedAccount(null); + AsyncStorage.removeItem(SELECTED_ACCOUNT_KEY); + console.log('πŸ”Œ Wallet disconnected'); + }; + + // Update selected account storage when it changes + useEffect(() => { + if (selectedAccount) { + AsyncStorage.setItem(SELECTED_ACCOUNT_KEY, selectedAccount.address); + } + }, [selectedAccount]); + + const value: PolkadotContextType = { + api, + isApiReady, + isConnected: isApiReady, + accounts, + selectedAccount, + setSelectedAccount, + connectWallet, + disconnectWallet, + createWallet, + getKeyPair, + error, + }; + + return {children}; +}; + +// Hook to use Polkadot context +export const usePolkadot = (): PolkadotContextType => { + const context = useContext(PolkadotContext); + if (!context) { + throw new Error('usePolkadot must be used within PolkadotProvider'); + } + return context; +}; diff --git a/mobile/src/navigation/AppNavigator.tsx b/mobile/src/navigation/AppNavigator.tsx index fd803e7a..5186a6d6 100644 --- a/mobile/src/navigation/AppNavigator.tsx +++ b/mobile/src/navigation/AppNavigator.tsx @@ -9,15 +9,13 @@ import { KurdistanColors } from '../theme/colors'; import WelcomeScreen from '../screens/WelcomeScreen'; import SignInScreen from '../screens/SignInScreen'; import SignUpScreen from '../screens/SignUpScreen'; -import DashboardScreen from '../screens/DashboardScreen'; -import SettingsScreen from '../screens/SettingsScreen'; +import BottomTabNavigator from './BottomTabNavigator'; export type RootStackParamList = { Welcome: undefined; SignIn: undefined; SignUp: undefined; - Dashboard: undefined; - Settings: undefined; + MainApp: undefined; }; const Stack = createStackNavigator(); @@ -100,27 +98,8 @@ const AppNavigator: React.FC = () => { ) : ( - // Show main app if authenticated - <> - - {(props) => ( - console.log('Navigate to Wallet')} - onNavigateToSettings={() => props.navigation.navigate('Settings')} - /> - )} - - - {(props) => ( - props.navigation.goBack()} - onLogout={handleLogout} - /> - )} - - + // Show main app (bottom tabs) if authenticated + )} diff --git a/mobile/src/navigation/BottomTabNavigator.tsx b/mobile/src/navigation/BottomTabNavigator.tsx new file mode 100644 index 00000000..f6c974e9 --- /dev/null +++ b/mobile/src/navigation/BottomTabNavigator.tsx @@ -0,0 +1,160 @@ +import React from 'react'; +import { View, Text, TouchableOpacity, StyleSheet, Platform } from 'react-native'; +import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; +import { KurdistanColors } from '../theme/colors'; + +// Screens +import DashboardScreen from '../screens/DashboardScreen'; +import WalletScreen from '../screens/WalletScreen'; +import BeCitizenScreen from '../screens/BeCitizenScreen'; +import ReferralScreen from '../screens/ReferralScreen'; +import ProfileScreen from '../screens/ProfileScreen'; + +export type BottomTabParamList = { + Home: undefined; + Wallet: undefined; + BeCitizen: undefined; + Referral: undefined; + Profile: undefined; +}; + +const Tab = createBottomTabNavigator(); + +// Custom Tab Bar Button for Center Button +const CustomTabBarButton: React.FC<{ + children: React.ReactNode; + onPress?: () => void; +}> = ({ children, onPress }) => ( + + {children} + +); + +const BottomTabNavigator: React.FC = () => { + return ( + + ( + + {focused ? '🏠' : '🏚️'} + + ), + }} + /> + + ( + + {focused ? 'πŸ’°' : 'πŸ‘›'} + + ), + }} + /> + + ( + + πŸ›οΈ + + ), + tabBarButton: (props) => , + }} + /> + + ( + + {focused ? '🀝' : 'πŸ‘₯'} + + ), + }} + /> + + ( + + {focused ? 'πŸ‘€' : 'πŸ‘¨'} + + ), + }} + /> + + ); +}; + +const styles = StyleSheet.create({ + tabBar: { + backgroundColor: KurdistanColors.spi, + borderTopWidth: 1, + borderTopColor: '#E0E0E0', + height: Platform.OS === 'ios' ? 85 : 65, + paddingBottom: Platform.OS === 'ios' ? 20 : 8, + paddingTop: 8, + shadowColor: '#000', + shadowOffset: { width: 0, height: -2 }, + shadowOpacity: 0.1, + shadowRadius: 8, + elevation: 10, + }, + tabBarLabel: { + fontSize: 11, + fontWeight: '600', + }, + icon: { + fontSize: 24, + }, + customButtonContainer: { + top: -20, + justifyContent: 'center', + alignItems: 'center', + }, + customButton: { + width: 70, + height: 70, + borderRadius: 35, + backgroundColor: KurdistanColors.kesk, + justifyContent: 'center', + alignItems: 'center', + shadowColor: KurdistanColors.kesk, + shadowOffset: { width: 0, height: 4 }, + shadowOpacity: 0.4, + shadowRadius: 8, + elevation: 8, + borderWidth: 4, + borderColor: KurdistanColors.spi, + }, + centerIcon: { + fontSize: 32, + }, +}); + +export default BottomTabNavigator; diff --git a/mobile/src/screens/BeCitizenScreen.tsx b/mobile/src/screens/BeCitizenScreen.tsx new file mode 100644 index 00000000..b90d0387 --- /dev/null +++ b/mobile/src/screens/BeCitizenScreen.tsx @@ -0,0 +1,498 @@ +import React, { useState } from 'react'; +import { + View, + Text, + TouchableOpacity, + StyleSheet, + SafeAreaView, + ScrollView, + StatusBar, + TextInput, + Alert, +} from 'react-native'; +import { LinearGradient } from 'expo-linear-gradient'; +import { useTranslation } from 'react-i18next'; +import AppColors, { KurdistanColors } from '../theme/colors'; + +const BeCitizenScreen: React.FC = () => { + const { t } = useTranslation(); + const [isExistingCitizen, setIsExistingCitizen] = useState(false); + const [currentStep, setCurrentStep] = useState<'choice' | 'new' | 'existing'>('choice'); + + // New Citizen Form State + const [fullName, setFullName] = useState(''); + const [fatherName, setFatherName] = useState(''); + const [motherName, setMotherName] = useState(''); + const [tribe, setTribe] = useState(''); + const [region, setRegion] = useState(''); + const [email, setEmail] = useState(''); + const [profession, setProfession] = useState(''); + const [referralCode, setReferralCode] = useState(''); + + // Existing Citizen Login State + const [citizenId, setCitizenId] = useState(''); + const [password, setPassword] = useState(''); + + const handleNewCitizenApplication = () => { + if (!fullName || !fatherName || !motherName || !email) { + Alert.alert('Error', 'Please fill in all required fields'); + return; + } + + // TODO: Implement actual citizenship registration on blockchain + Alert.alert( + 'Application Submitted', + 'Your citizenship application has been submitted for review. You will receive a confirmation soon.', + [ + { + text: 'OK', + onPress: () => { + // Reset form + setFullName(''); + setFatherName(''); + setMotherName(''); + setTribe(''); + setRegion(''); + setEmail(''); + setProfession(''); + setReferralCode(''); + setCurrentStep('choice'); + }, + }, + ] + ); + }; + + const handleExistingCitizenLogin = () => { + if (!citizenId || !password) { + Alert.alert('Error', 'Please enter Citizen ID and Password'); + return; + } + + // TODO: Implement actual citizenship verification + Alert.alert('Success', 'Welcome back, Citizen!', [ + { + text: 'OK', + onPress: () => { + setCitizenId(''); + setPassword(''); + setCurrentStep('choice'); + }, + }, + ]); + }; + + if (currentStep === 'choice') { + return ( + + + + + + + πŸ›οΈ + + Be a Citizen + + Join the Pezkuwi decentralized nation + + + + + setCurrentStep('new')} + activeOpacity={0.8} + > + πŸ“ + New Citizen + + Apply for citizenship and join our community + + + + setCurrentStep('existing')} + activeOpacity={0.8} + > + πŸ” + Existing Citizen + + Access your citizenship account + + + + + + Citizenship Benefits + + βœ“ + Voting rights in governance + + + βœ“ + Access to exclusive services + + + βœ“ + Referral rewards program + + + βœ“ + Community recognition + + + + + + ); + } + + if (currentStep === 'new') { + return ( + + + + setCurrentStep('choice')} + > + ← Back + + + New Citizen Application + + Please provide your information to apply for citizenship + + + + Full Name * + + + + + Father's Name * + + + + + Mother's Name * + + + + + Tribe + + + + + Region + + + + + Email * + + + + + Profession + + + + + Referral Code + + + + + Submit Application + + + + + + ); + } + + // Existing Citizen Login + return ( + + + + setCurrentStep('choice')} + > + ← Back + + + Citizen Login + + Access your citizenship account + + + + Citizen ID + + + + + Password + + + + + Login + + + + ); +}; + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: '#F5F5F5', + }, + gradient: { + flex: 1, + }, + scrollContent: { + flexGrow: 1, + padding: 20, + paddingTop: 60, + }, + header: { + alignItems: 'center', + marginBottom: 40, + }, + logoContainer: { + width: 100, + height: 100, + borderRadius: 50, + backgroundColor: KurdistanColors.spi, + justifyContent: 'center', + alignItems: 'center', + marginBottom: 20, + shadowColor: '#000', + shadowOffset: { width: 0, height: 4 }, + shadowOpacity: 0.3, + shadowRadius: 8, + elevation: 8, + }, + logoText: { + fontSize: 48, + }, + title: { + fontSize: 28, + fontWeight: 'bold', + color: KurdistanColors.spi, + marginBottom: 8, + }, + subtitle: { + fontSize: 16, + color: KurdistanColors.spi, + textAlign: 'center', + opacity: 0.9, + }, + choiceContainer: { + gap: 16, + marginBottom: 40, + }, + choiceCard: { + backgroundColor: KurdistanColors.spi, + borderRadius: 20, + padding: 24, + alignItems: 'center', + shadowColor: '#000', + shadowOffset: { width: 0, height: 4 }, + shadowOpacity: 0.2, + shadowRadius: 8, + elevation: 6, + }, + choiceIcon: { + fontSize: 48, + marginBottom: 16, + }, + choiceTitle: { + fontSize: 20, + fontWeight: 'bold', + color: KurdistanColors.kesk, + marginBottom: 8, + }, + choiceDescription: { + fontSize: 14, + color: '#666', + textAlign: 'center', + }, + infoSection: { + backgroundColor: 'rgba(255, 255, 255, 0.2)', + borderRadius: 16, + padding: 20, + }, + infoTitle: { + fontSize: 18, + fontWeight: '600', + color: KurdistanColors.spi, + marginBottom: 16, + }, + benefitItem: { + flexDirection: 'row', + alignItems: 'center', + marginBottom: 12, + }, + benefitIcon: { + fontSize: 16, + color: KurdistanColors.spi, + marginRight: 12, + fontWeight: 'bold', + }, + benefitText: { + fontSize: 14, + color: KurdistanColors.spi, + flex: 1, + }, + formContainer: { + flex: 1, + padding: 20, + }, + backButton: { + marginBottom: 20, + }, + backButtonText: { + fontSize: 16, + color: KurdistanColors.kesk, + fontWeight: '600', + }, + formTitle: { + fontSize: 24, + fontWeight: 'bold', + color: KurdistanColors.reş, + marginBottom: 8, + }, + formSubtitle: { + fontSize: 14, + color: '#666', + marginBottom: 24, + }, + inputGroup: { + marginBottom: 20, + }, + label: { + fontSize: 14, + fontWeight: '600', + color: KurdistanColors.reş, + marginBottom: 8, + }, + input: { + backgroundColor: KurdistanColors.spi, + borderRadius: 12, + padding: 16, + fontSize: 16, + borderWidth: 1, + borderColor: '#E0E0E0', + }, + submitButton: { + backgroundColor: KurdistanColors.kesk, + borderRadius: 12, + padding: 16, + alignItems: 'center', + marginTop: 20, + shadowColor: KurdistanColors.kesk, + shadowOffset: { width: 0, height: 4 }, + shadowOpacity: 0.3, + shadowRadius: 6, + elevation: 6, + }, + submitButtonText: { + fontSize: 18, + fontWeight: 'bold', + color: KurdistanColors.spi, + }, + spacer: { + height: 40, + }, +}); + +export default BeCitizenScreen; diff --git a/mobile/src/screens/SettingsScreen.tsx b/mobile/src/screens/ProfileScreen.tsx similarity index 100% rename from mobile/src/screens/SettingsScreen.tsx rename to mobile/src/screens/ProfileScreen.tsx diff --git a/mobile/src/screens/ReferralScreen.tsx b/mobile/src/screens/ReferralScreen.tsx new file mode 100644 index 00000000..aac73623 --- /dev/null +++ b/mobile/src/screens/ReferralScreen.tsx @@ -0,0 +1,531 @@ +import React, { useState } from 'react'; +import { + View, + Text, + TouchableOpacity, + StyleSheet, + SafeAreaView, + ScrollView, + StatusBar, + Share, + Alert, + Clipboard, +} from 'react-native'; +import { LinearGradient } from 'expo-linear-gradient'; +import { useTranslation } from 'react-i18next'; +import AppColors, { KurdistanColors } from '../theme/colors'; + +interface ReferralStats { + totalReferrals: number; + activeReferrals: number; + totalEarned: string; + pendingRewards: string; +} + +interface Referral { + id: string; + address: string; + joinedDate: string; + status: 'active' | 'pending'; + earned: string; +} + +const ReferralScreen: React.FC = () => { + const { t } = useTranslation(); + const [isConnected, setIsConnected] = useState(false); + + // Mock referral code - will be generated from blockchain + const referralCode = 'PZK-XYZABC123'; + + // Mock stats - will be fetched from pallet_referral + const stats: ReferralStats = { + totalReferrals: 0, + activeReferrals: 0, + totalEarned: '0.00 HEZ', + pendingRewards: '0.00 HEZ', + }; + + // Mock referrals - will be fetched from blockchain + const referrals: Referral[] = []; + + const handleConnectWallet = () => { + // TODO: Implement Polkadot.js wallet connection + setIsConnected(true); + Alert.alert('Connected', 'Your wallet has been connected to the referral system!'); + }; + + const handleCopyCode = () => { + Clipboard.setString(referralCode); + Alert.alert('Copied!', 'Referral code copied to clipboard'); + }; + + const handleShareCode = async () => { + try { + const result = await Share.share({ + message: `Join Pezkuwi using my referral code: ${referralCode}\n\nGet rewards for becoming a citizen!`, + title: 'Join Pezkuwi', + }); + + if (result.action === Share.sharedAction) { + console.log('Shared successfully'); + } + } catch (error) { + console.error('Error sharing:', error); + } + }; + + if (!isConnected) { + return ( + + + + + + 🀝 + + Referral Program + + Connect your wallet to access your referral dashboard + + + Connect Wallet + + + + + ); + } + + return ( + + + + {/* Header */} + + Referral Program + Earn rewards by inviting friends + + + + {/* Referral Code Card */} + + Your Referral Code + + {referralCode} + + + + πŸ“‹ + Copy + + + πŸ“€ + Share + + + + + {/* Stats Grid */} + + + {stats.totalReferrals} + Total Referrals + + + {stats.activeReferrals} + Active + + + + + + {stats.totalEarned} + Total Earned + + + {stats.pendingRewards} + Pending + + + + {/* How It Works */} + + How It Works + + + 1 + + + Share Your Code + + Share your unique referral code with friends + + + + + + + 2 + + + Friend Joins + + They use your code when applying for citizenship + + + + + + + 3 + + + Earn Rewards + + Get HEZ tokens when they become active citizens + + + + + + {/* Referrals List */} + + Your Referrals + {referrals.length === 0 ? ( + + πŸ‘₯ + No referrals yet + + Start inviting friends to earn rewards! + + + ) : ( + referrals.map((referral) => ( + + + + {referral.address.substring(0, 8)}... + {referral.address.substring(referral.address.length - 6)} + + {referral.joinedDate} + + + + {referral.status} + + {referral.earned} + + + )) + )} + + + + ); +}; + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: '#F5F5F5', + }, + connectGradient: { + flex: 1, + }, + connectContainer: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + padding: 20, + }, + logoContainer: { + width: 100, + height: 100, + borderRadius: 50, + backgroundColor: KurdistanColors.spi, + justifyContent: 'center', + alignItems: 'center', + marginBottom: 20, + shadowColor: '#000', + shadowOffset: { width: 0, height: 4 }, + shadowOpacity: 0.3, + shadowRadius: 8, + elevation: 8, + }, + logoText: { + fontSize: 48, + }, + connectTitle: { + fontSize: 28, + fontWeight: 'bold', + color: KurdistanColors.spi, + marginBottom: 12, + }, + connectSubtitle: { + fontSize: 16, + color: KurdistanColors.spi, + textAlign: 'center', + opacity: 0.9, + marginBottom: 40, + }, + connectButton: { + backgroundColor: KurdistanColors.spi, + paddingHorizontal: 40, + paddingVertical: 16, + borderRadius: 12, + shadowColor: '#000', + shadowOffset: { width: 0, height: 4 }, + shadowOpacity: 0.3, + shadowRadius: 6, + elevation: 6, + }, + connectButtonText: { + fontSize: 18, + fontWeight: 'bold', + color: KurdistanColors.sor, + }, + header: { + padding: 20, + paddingTop: 40, + }, + headerTitle: { + fontSize: 24, + fontWeight: 'bold', + color: KurdistanColors.spi, + marginBottom: 4, + }, + headerSubtitle: { + fontSize: 14, + color: KurdistanColors.spi, + opacity: 0.9, + }, + content: { + flex: 1, + padding: 20, + }, + codeCard: { + backgroundColor: KurdistanColors.spi, + borderRadius: 16, + padding: 20, + marginBottom: 20, + shadowColor: '#000', + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.1, + shadowRadius: 8, + elevation: 4, + }, + codeLabel: { + fontSize: 14, + color: '#666', + marginBottom: 12, + }, + codeContainer: { + backgroundColor: '#F5F5F5', + borderRadius: 12, + padding: 16, + marginBottom: 16, + }, + codeText: { + fontSize: 20, + fontWeight: 'bold', + color: KurdistanColors.sor, + textAlign: 'center', + fontFamily: 'monospace', + }, + codeActions: { + flexDirection: 'row', + gap: 12, + }, + codeButton: { + flex: 1, + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + padding: 12, + borderRadius: 8, + gap: 8, + }, + copyButton: { + backgroundColor: '#F0F0F0', + }, + shareButton: { + backgroundColor: KurdistanColors.sor, + }, + codeButtonIcon: { + fontSize: 16, + }, + codeButtonText: { + fontSize: 14, + fontWeight: '600', + color: KurdistanColors.reş, + }, + statsGrid: { + flexDirection: 'row', + gap: 12, + marginBottom: 12, + }, + statCard: { + flex: 1, + backgroundColor: KurdistanColors.spi, + borderRadius: 12, + padding: 16, + alignItems: 'center', + shadowColor: '#000', + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.05, + shadowRadius: 6, + elevation: 3, + }, + statValue: { + fontSize: 24, + fontWeight: 'bold', + color: KurdistanColors.sor, + marginBottom: 4, + }, + statLabel: { + fontSize: 12, + color: '#666', + }, + section: { + marginTop: 20, + }, + sectionTitle: { + fontSize: 18, + fontWeight: '600', + color: KurdistanColors.reş, + marginBottom: 16, + }, + stepCard: { + flexDirection: 'row', + backgroundColor: KurdistanColors.spi, + borderRadius: 12, + padding: 16, + marginBottom: 12, + shadowColor: '#000', + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.05, + shadowRadius: 6, + elevation: 3, + }, + stepNumber: { + width: 40, + height: 40, + borderRadius: 20, + backgroundColor: KurdistanColors.sor, + justifyContent: 'center', + alignItems: 'center', + marginRight: 16, + }, + stepNumberText: { + fontSize: 18, + fontWeight: 'bold', + color: KurdistanColors.spi, + }, + stepContent: { + flex: 1, + }, + stepTitle: { + fontSize: 16, + fontWeight: '600', + color: KurdistanColors.reş, + marginBottom: 4, + }, + stepDescription: { + fontSize: 14, + color: '#666', + }, + emptyState: { + alignItems: 'center', + padding: 40, + }, + emptyStateIcon: { + fontSize: 48, + marginBottom: 16, + }, + emptyStateText: { + fontSize: 16, + fontWeight: '600', + color: KurdistanColors.reş, + marginBottom: 8, + }, + emptyStateSubtext: { + fontSize: 14, + color: '#666', + textAlign: 'center', + }, + referralCard: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + backgroundColor: KurdistanColors.spi, + borderRadius: 12, + padding: 16, + marginBottom: 12, + shadowColor: '#000', + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.05, + shadowRadius: 6, + elevation: 3, + }, + referralInfo: { + flex: 1, + }, + referralAddress: { + fontSize: 14, + fontWeight: '600', + color: KurdistanColors.reş, + marginBottom: 4, + fontFamily: 'monospace', + }, + referralDate: { + fontSize: 12, + color: '#666', + }, + referralStats: { + alignItems: 'flex-end', + }, + referralStatus: { + fontSize: 12, + fontWeight: '600', + marginBottom: 4, + textTransform: 'uppercase', + }, + statusActive: { + color: KurdistanColors.kesk, + }, + statusPending: { + color: KurdistanColors.zer, + }, + referralEarned: { + fontSize: 14, + fontWeight: '600', + color: KurdistanColors.sor, + }, +}); + +export default ReferralScreen; diff --git a/mobile/src/screens/WalletScreen.tsx b/mobile/src/screens/WalletScreen.tsx new file mode 100644 index 00000000..faa16e27 --- /dev/null +++ b/mobile/src/screens/WalletScreen.tsx @@ -0,0 +1,883 @@ +import React, { useState, useEffect } from 'react'; +import { + View, + Text, + TouchableOpacity, + StyleSheet, + SafeAreaView, + ScrollView, + StatusBar, + Modal, + TextInput, + Alert, + ActivityIndicator, +} from 'react-native'; +import { LinearGradient } from 'expo-linear-gradient'; +import { useTranslation } from 'react-i18next'; +import AppColors, { KurdistanColors } from '../theme/colors'; +import { usePolkadot } from '../contexts/PolkadotContext'; + +interface Token { + symbol: string; + name: string; + balance: string; + value: string; + change: string; + logo: string; + isLive: boolean; // true = canlΔ± blockchain, false = coming soon +} + +const WalletScreen: React.FC = () => { + const { t } = useTranslation(); + const { + api, + isApiReady, + accounts, + selectedAccount, + connectWallet, + disconnectWallet, + createWallet, + getKeyPair, + error: polkadotError + } = usePolkadot(); + + const [selectedToken, setSelectedToken] = useState(null); + const [sendModalVisible, setSendModalVisible] = useState(false); + const [receiveModalVisible, setReceiveModalVisible] = useState(false); + const [createWalletModalVisible, setCreateWalletModalVisible] = useState(false); + const [recipientAddress, setRecipientAddress] = useState(''); + const [sendAmount, setSendAmount] = useState(''); + const [walletName, setWalletName] = useState(''); + const [isSending, setIsSending] = useState(false); + const [isLoadingBalances, setIsLoadingBalances] = useState(false); + + // Token balances from blockchain + const [balances, setBalances] = useState<{ [key: string]: string }>({ + HEZ: '0.00', + PEZ: '0.00', + wUSDT: '0.00', + }); + + const tokens: Token[] = [ + { + symbol: 'HEZ', + name: 'Pezkuwi', + balance: balances.HEZ, + value: '$0.00', + change: '+0.00%', + logo: 'πŸ’Ž', + isLive: true, + }, + { + symbol: 'PEZ', + name: 'Pezkuwi Token', + balance: balances.PEZ, + value: '$0.00', + change: '+0.00%', + logo: 'πŸͺ™', + isLive: true, + }, + { + symbol: 'wUSDT', + name: 'Wrapped USDT', + balance: balances.wUSDT, + value: '$0.00', + change: '+0.00%', + logo: 'πŸ’΅', + isLive: true, + }, + { + symbol: 'ETH', + name: 'Ethereum', + balance: '0.00', + value: '$0.00', + change: '+0.00%', + logo: '⟠', + isLive: false, + }, + { + symbol: 'BTC', + name: 'Bitcoin', + balance: '0.00', + value: '$0.00', + change: '+0.00%', + logo: 'β‚Ώ', + isLive: false, + }, + { + symbol: 'DOT', + name: 'Polkadot', + balance: '0.00', + value: '$0.00', + change: '+0.00%', + logo: '●', + isLive: false, + }, + { + symbol: 'BNB', + name: 'Binance Coin', + balance: '0.00', + value: '$0.00', + change: '+0.00%', + logo: 'β—†', + isLive: false, + }, + ]; + + // Fetch token balances from blockchain + useEffect(() => { + const fetchBalances = async () => { + if (!api || !isApiReady || !selectedAccount) { + return; + } + + setIsLoadingBalances(true); + try { + // Fetch HEZ balance (native token) + const accountInfo: any = await api.query.system.account(selectedAccount.address); + const freeBalance = accountInfo.data.free.toString(); + const hezBalance = (Number(freeBalance) / 1e12).toFixed(2); + + // Fetch PEZ balance (asset ID 1) + let pezBalance = '0.00'; + try { + if (api.query.assets?.account) { + const pezAsset: any = await api.query.assets.account(1, selectedAccount.address); + if (pezAsset.isSome) { + const pezData = pezAsset.unwrap(); + pezBalance = (Number(pezData.balance.toString()) / 1e12).toFixed(2); + } + } + } catch (err) { + console.log('PEZ asset not found or not accessible'); + } + + // Fetch wUSDT balance (asset ID 2) + let wusdtBalance = '0.00'; + try { + if (api.query.assets?.account) { + const wusdtAsset: any = await api.query.assets.account(2, selectedAccount.address); + if (wusdtAsset.isSome) { + const wusdtData = wusdtAsset.unwrap(); + wusdtBalance = (Number(wusdtData.balance.toString()) / 1e12).toFixed(2); + } + } + } catch (err) { + console.log('wUSDT asset not found or not accessible'); + } + + setBalances({ + HEZ: hezBalance, + PEZ: pezBalance, + wUSDT: wusdtBalance, + }); + } catch (err) { + console.error('Failed to fetch balances:', err); + Alert.alert('Error', 'Failed to fetch token balances'); + } finally { + setIsLoadingBalances(false); + } + }; + + fetchBalances(); + + // Refresh balances every 30 seconds + const interval = setInterval(fetchBalances, 30000); + return () => clearInterval(interval); + }, [api, isApiReady, selectedAccount]); + + const handleConnectWallet = async () => { + try { + if (accounts.length === 0) { + // No wallet exists, show create wallet modal + setCreateWalletModalVisible(true); + return; + } + + // Connect existing wallet + await connectWallet(); + Alert.alert('Connected', 'Wallet connected successfully!'); + } catch (err) { + console.error('Failed to connect wallet:', err); + Alert.alert('Error', 'Failed to connect wallet'); + } + }; + + const handleCreateWallet = async () => { + if (!walletName.trim()) { + Alert.alert('Error', 'Please enter a wallet name'); + return; + } + + try { + const { address, mnemonic } = await createWallet(walletName); + setCreateWalletModalVisible(false); + setWalletName(''); + + Alert.alert( + 'Wallet Created', + `Your wallet has been created!\n\nAddress: ${address.substring(0, 10)}...\n\nIMPORTANT: Save your recovery phrase:\n${mnemonic}\n\nStore it securely - you'll need it to recover your wallet!`, + [{ text: 'OK', onPress: () => connectWallet() }] + ); + } catch (err) { + console.error('Failed to create wallet:', err); + Alert.alert('Error', 'Failed to create wallet'); + } + }; + + const handleTokenPress = (token: Token) => { + if (!token.isLive) { + Alert.alert( + 'Coming Soon', + `${token.name} (${token.symbol}) support is coming soon!`, + [{ text: 'OK' }] + ); + return; + } + setSelectedToken(token); + }; + + const handleSend = () => { + if (!selectedToken) return; + setSendModalVisible(true); + }; + + const handleReceive = () => { + setReceiveModalVisible(true); + }; + + const handleConfirmSend = async () => { + if (!recipientAddress || !sendAmount || !selectedToken || !selectedAccount || !api) { + Alert.alert('Error', 'Please enter recipient address and amount'); + return; + } + + const amount = parseFloat(sendAmount); + if (isNaN(amount) || amount <= 0) { + Alert.alert('Error', 'Please enter a valid amount'); + return; + } + + Alert.alert( + 'Confirm Transaction', + `Send ${sendAmount} ${selectedToken.symbol} to ${recipientAddress.substring(0, 10)}...?`, + [ + { text: 'Cancel', style: 'cancel' }, + { + text: 'Confirm', + onPress: async () => { + setIsSending(true); + try { + const keypair = await getKeyPair(selectedAccount.address); + if (!keypair) { + throw new Error('Failed to load keypair'); + } + + // Convert amount to blockchain units (12 decimals) + const amountInUnits = BigInt(Math.floor(amount * 1e12)); + + let tx; + if (selectedToken.symbol === 'HEZ') { + // Send native token + tx = api.tx.balances.transfer(recipientAddress, amountInUnits); + } else if (selectedToken.symbol === 'PEZ') { + // Send PEZ asset (asset ID 1) + tx = api.tx.assets.transfer(1, recipientAddress, amountInUnits); + } else if (selectedToken.symbol === 'wUSDT') { + // Send wUSDT asset (asset ID 2) + tx = api.tx.assets.transfer(2, recipientAddress, amountInUnits); + } else { + throw new Error('Unsupported token'); + } + + // Sign and send transaction + await tx.signAndSend(keypair, ({ status, events }: any) => { + if (status.isInBlock) { + console.log(`Transaction included in block: ${status.asInBlock}`); + } else if (status.isFinalized) { + console.log(`Transaction finalized: ${status.asFinalized}`); + + setSendModalVisible(false); + setRecipientAddress(''); + setSendAmount(''); + setIsSending(false); + + Alert.alert( + 'Success', + `Successfully sent ${sendAmount} ${selectedToken.symbol}!` + ); + + // Refresh balances + // The useEffect will automatically refresh after 30s, but we could trigger it manually here + } + }); + } catch (err) { + console.error('Transaction failed:', err); + setIsSending(false); + Alert.alert('Error', `Transaction failed: ${err instanceof Error ? err.message : 'Unknown error'}`); + } + }, + }, + ] + ); + }; + + // Show loading state while API is initializing + if (!isApiReady) { + return ( + + + + Connecting to blockchain... + + + ); + } + + // Show connect/create wallet screen if no account is selected + if (!selectedAccount) { + return ( + + + + + + πŸ‘› + + {t('wallet.title')} + + {accounts.length === 0 + ? 'Create a new wallet to get started' + : 'Connect your wallet to manage your tokens'} + + + + {accounts.length === 0 ? 'Create Wallet' : t('wallet.connect')} + + + {polkadotError && ( + {polkadotError} + )} + + + + {/* Create Wallet Modal */} + setCreateWalletModalVisible(false)} + > + + + Create New Wallet + + + setCreateWalletModalVisible(false)} + > + {t('common.cancel')} + + + Create + + + + + + + ); + } + + return ( + + + + {/* Header */} + + {t('wallet.title')} + + {t('wallet.address')}: + + {selectedAccount.address.substring(0, 8)}...{selectedAccount.address.substring(selectedAccount.address.length - 6)} + + + {isLoadingBalances && ( + + )} + + {t('wallet.disconnect')} + + + + {/* Tokens List */} + + Your Tokens + {tokens.map((token, index) => ( + handleTokenPress(token)} + activeOpacity={0.7} + > + + + {token.logo} + + + {token.symbol} + {token.name} + + + + {token.balance} + {token.value} + + {!token.isLive && ( + + Coming Soon + + )} + + ))} + + + {/* Action Buttons */} + {selectedToken && selectedToken.isLive && ( + + + πŸ“€ + {t('wallet.send')} + + + πŸ“₯ + {t('wallet.receive')} + + + )} + + {/* Send Modal */} + !isSending && setSendModalVisible(false)} + > + + + + Send {selectedToken?.symbol} + + + Available: {selectedToken?.balance} {selectedToken?.symbol} + + + + + setSendModalVisible(false)} + disabled={isSending} + > + {t('common.cancel')} + + + {isSending ? ( + + ) : ( + {t('common.confirm')} + )} + + + + + + + {/* Receive Modal */} + setReceiveModalVisible(false)} + > + + + Receive {selectedToken?.symbol} + QR Code Here + {selectedAccount.address} + setReceiveModalVisible(false)} + > + {t('common.close')} + + + + + + ); +}; + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: '#F5F5F5', + }, + loadingContainer: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + padding: 20, + }, + loadingText: { + marginTop: 16, + fontSize: 16, + color: '#666', + }, + errorText: { + marginTop: 20, + fontSize: 14, + color: KurdistanColors.sor, + textAlign: 'center', + paddingHorizontal: 20, + }, + connectGradient: { + flex: 1, + }, + connectContainer: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + padding: 20, + }, + logoContainer: { + width: 100, + height: 100, + borderRadius: 50, + backgroundColor: KurdistanColors.spi, + justifyContent: 'center', + alignItems: 'center', + marginBottom: 20, + shadowColor: '#000', + shadowOffset: { width: 0, height: 4 }, + shadowOpacity: 0.3, + shadowRadius: 8, + elevation: 8, + }, + logoText: { + fontSize: 48, + }, + connectTitle: { + fontSize: 28, + fontWeight: 'bold', + color: KurdistanColors.spi, + marginBottom: 12, + }, + connectSubtitle: { + fontSize: 16, + color: KurdistanColors.spi, + textAlign: 'center', + opacity: 0.9, + marginBottom: 40, + }, + connectButton: { + backgroundColor: KurdistanColors.spi, + paddingHorizontal: 40, + paddingVertical: 16, + borderRadius: 12, + shadowColor: '#000', + shadowOffset: { width: 0, height: 4 }, + shadowOpacity: 0.3, + shadowRadius: 6, + elevation: 6, + }, + connectButtonText: { + fontSize: 18, + fontWeight: 'bold', + color: KurdistanColors.kesk, + }, + header: { + padding: 20, + paddingTop: 40, + }, + headerTitle: { + fontSize: 24, + fontWeight: 'bold', + color: KurdistanColors.spi, + marginBottom: 12, + }, + addressContainer: { + flexDirection: 'row', + alignItems: 'center', + marginBottom: 12, + }, + addressLabel: { + fontSize: 14, + color: KurdistanColors.spi, + opacity: 0.9, + marginRight: 8, + }, + addressText: { + fontSize: 14, + color: KurdistanColors.spi, + fontFamily: 'monospace', + }, + disconnectButton: { + alignSelf: 'flex-start', + paddingHorizontal: 16, + paddingVertical: 8, + backgroundColor: 'rgba(255, 255, 255, 0.2)', + borderRadius: 8, + }, + disconnectButtonText: { + fontSize: 12, + color: KurdistanColors.spi, + fontWeight: '600', + }, + tokensContainer: { + flex: 1, + padding: 20, + }, + sectionTitle: { + fontSize: 18, + fontWeight: '600', + color: KurdistanColors.reş, + marginBottom: 16, + }, + tokenCard: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + backgroundColor: KurdistanColors.spi, + borderRadius: 16, + padding: 16, + marginBottom: 12, + shadowColor: '#000', + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.05, + shadowRadius: 6, + elevation: 3, + }, + tokenCardDisabled: { + opacity: 0.6, + }, + tokenInfo: { + flexDirection: 'row', + alignItems: 'center', + flex: 1, + }, + tokenLogoContainer: { + width: 48, + height: 48, + borderRadius: 24, + backgroundColor: '#F0F0F0', + justifyContent: 'center', + alignItems: 'center', + marginRight: 12, + }, + tokenLogo: { + fontSize: 24, + }, + tokenDetails: { + flex: 1, + }, + tokenSymbol: { + fontSize: 16, + fontWeight: '600', + color: KurdistanColors.reş, + marginBottom: 4, + }, + tokenName: { + fontSize: 14, + color: '#666', + }, + tokenBalance: { + alignItems: 'flex-end', + }, + balanceAmount: { + fontSize: 16, + fontWeight: '600', + color: KurdistanColors.reş, + marginBottom: 4, + }, + balanceValue: { + fontSize: 14, + color: '#666', + }, + comingSoonBadge: { + position: 'absolute', + top: 8, + right: 8, + backgroundColor: KurdistanColors.zer, + paddingHorizontal: 8, + paddingVertical: 4, + borderRadius: 4, + }, + comingSoonText: { + fontSize: 10, + fontWeight: 'bold', + color: KurdistanColors.reş, + }, + actionsContainer: { + flexDirection: 'row', + padding: 20, + gap: 12, + }, + actionButton: { + flex: 1, + backgroundColor: KurdistanColors.kesk, + borderRadius: 12, + padding: 16, + alignItems: 'center', + shadowColor: KurdistanColors.kesk, + shadowOffset: { width: 0, height: 4 }, + shadowOpacity: 0.3, + shadowRadius: 6, + elevation: 6, + }, + actionIcon: { + fontSize: 24, + marginBottom: 8, + }, + actionText: { + fontSize: 14, + fontWeight: '600', + color: KurdistanColors.spi, + }, + modalOverlay: { + flex: 1, + backgroundColor: 'rgba(0, 0, 0, 0.5)', + justifyContent: 'center', + alignItems: 'center', + padding: 20, + }, + modalContent: { + backgroundColor: KurdistanColors.spi, + borderRadius: 20, + padding: 24, + width: '100%', + maxWidth: 400, + }, + modalTitle: { + fontSize: 20, + fontWeight: 'bold', + color: KurdistanColors.reş, + marginBottom: 12, + textAlign: 'center', + }, + balanceHint: { + fontSize: 14, + color: '#666', + marginBottom: 20, + textAlign: 'center', + }, + input: { + backgroundColor: '#F5F5F5', + borderRadius: 12, + padding: 16, + fontSize: 16, + marginBottom: 16, + borderWidth: 1, + borderColor: '#E0E0E0', + }, + modalButtons: { + flexDirection: 'row', + gap: 12, + }, + modalButton: { + flex: 1, + padding: 16, + borderRadius: 12, + alignItems: 'center', + }, + cancelButton: { + backgroundColor: '#E0E0E0', + }, + cancelButtonText: { + fontSize: 16, + fontWeight: '600', + color: '#666', + }, + confirmButton: { + backgroundColor: KurdistanColors.kesk, + }, + confirmButtonText: { + fontSize: 16, + fontWeight: '600', + color: KurdistanColors.spi, + }, + disabledButton: { + opacity: 0.5, + }, + qrPlaceholder: { + width: 200, + height: 200, + backgroundColor: '#F0F0F0', + borderRadius: 12, + alignSelf: 'center', + marginBottom: 20, + textAlign: 'center', + lineHeight: 200, + fontSize: 16, + color: '#999', + }, + addressDisplay: { + fontSize: 12, + fontFamily: 'monospace', + color: '#666', + textAlign: 'center', + marginBottom: 20, + padding: 12, + backgroundColor: '#F5F5F5', + borderRadius: 8, + }, +}); + +export default WalletScreen;