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;