Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7be5899ad5 |
+1
-4
@@ -1,8 +1,5 @@
|
||||
REACT_APP_WALLET_CONNECT_PROJECT_ID=
|
||||
|
||||
REACT_APP_DEFAULT_GAS_PRICE=0.025
|
||||
# Reference: https://github.com/cosmos/cosmos-sdk/issues/16020
|
||||
REACT_APP_GAS_ADJUSTMENT=2
|
||||
REACT_APP_LACONICD_RPC_URL=https://laconicd-sapo.laconic.com
|
||||
|
||||
REACT_APP_DEPLOY_APP_URL=
|
||||
REACT_APP_LACONICD_RPC_URL=https://laconicd.laconic.com
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
build
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"plugins": ["react"],
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest",
|
||||
"plugin:react/recommended"
|
||||
],
|
||||
"settings": {
|
||||
"react": {
|
||||
"version": "detect"
|
||||
}
|
||||
},
|
||||
"rules": {
|
||||
"indent": ["error", 2, { "SwitchCase": 1 }]
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
yarn lint
|
||||
+41
-41
@@ -2,52 +2,52 @@
|
||||
const webpack = require('webpack')
|
||||
|
||||
module.exports = function override(config, env) {
|
||||
config.module.rules.push({
|
||||
test: /\.js$/,
|
||||
exclude: /node_modules[/\\](?!react-native-vector-icons)/,
|
||||
use: {
|
||||
loader: "babel-loader",
|
||||
options: {
|
||||
// Disable reading babel configuration
|
||||
babelrc: false,
|
||||
configFile: false,
|
||||
config.module.rules.push({
|
||||
test: /\.js$/,
|
||||
exclude: /node_modules[/\\](?!react-native-vector-icons)/,
|
||||
use: {
|
||||
loader: "babel-loader",
|
||||
options: {
|
||||
// Disable reading babel configuration
|
||||
babelrc: false,
|
||||
configFile: false,
|
||||
|
||||
// The configuration for compilation
|
||||
presets: [
|
||||
["@babel/preset-env", { useBuiltIns: "usage", "corejs": "3" }],
|
||||
"@babel/preset-react",
|
||||
"@babel/preset-flow",
|
||||
"@babel/preset-typescript",
|
||||
],
|
||||
plugins: [
|
||||
"@babel/plugin-proposal-class-properties",
|
||||
"@babel/plugin-proposal-object-rest-spread",
|
||||
"@babel/plugin-transform-modules-commonjs"
|
||||
]
|
||||
// The configuration for compilation
|
||||
presets: [
|
||||
["@babel/preset-env", { useBuiltIns: "usage", "corejs": "3" }],
|
||||
"@babel/preset-react",
|
||||
"@babel/preset-flow",
|
||||
"@babel/preset-typescript",
|
||||
],
|
||||
plugins: [
|
||||
"@babel/plugin-proposal-class-properties",
|
||||
"@babel/plugin-proposal-object-rest-spread",
|
||||
"@babel/plugin-transform-modules-commonjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
config.plugins.push(
|
||||
new webpack.ProvidePlugin({
|
||||
Buffer: ["buffer", "Buffer"],
|
||||
config.plugins.push(
|
||||
new webpack.ProvidePlugin({
|
||||
Buffer: ["buffer", "Buffer"],
|
||||
})
|
||||
)
|
||||
|
||||
config.module.rules.push({
|
||||
test: /\.(jpg|png|woff|woff2|eot|ttf|svg)$/,
|
||||
type: 'asset/resource'
|
||||
})
|
||||
)
|
||||
|
||||
config.module.rules.push({
|
||||
test: /\.(jpg|png|woff|woff2|eot|ttf|svg)$/,
|
||||
type: 'asset/resource'
|
||||
})
|
||||
config.resolve.fallback = {
|
||||
crypto: require.resolve("crypto-browserify"),
|
||||
stream: require.resolve("stream-browserify"),
|
||||
http: require.resolve('stream-http'),
|
||||
https: require.resolve('https-browserify'),
|
||||
url: false
|
||||
}
|
||||
|
||||
config.resolve.fallback = {
|
||||
crypto: require.resolve("crypto-browserify"),
|
||||
stream: require.resolve("stream-browserify"),
|
||||
http: require.resolve('stream-http'),
|
||||
https: require.resolve('https-browserify'),
|
||||
url: false
|
||||
}
|
||||
config.resolve.alias['react-native$'] = require.resolve('react-native-web');
|
||||
|
||||
config.resolve.alias['react-native$'] = require.resolve('react-native-web');
|
||||
|
||||
return config;
|
||||
return config;
|
||||
};
|
||||
|
||||
+1
-12
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "web-wallet",
|
||||
"version": "0.1.3",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@cerc-io/registry-sdk": "^0.2.5",
|
||||
@@ -13,8 +13,6 @@
|
||||
"@ethersproject/shims": "^5.7.0",
|
||||
"@hookform/resolvers": "^3.3.4",
|
||||
"@json-rpc-tools/utils": "^1.7.6",
|
||||
"@mui/icons-material": "^5.16.7",
|
||||
"@mui/lab": "^5.0.0-alpha.173",
|
||||
"@mui/material": "^5.16.4",
|
||||
"@react-navigation/elements": "^1.3.30",
|
||||
"@react-navigation/native": "^6.1.10",
|
||||
@@ -51,8 +49,6 @@
|
||||
"start": "react-app-rewired start",
|
||||
"build": "react-app-rewired build",
|
||||
"test": "react-app-rewired test",
|
||||
"lint": "eslint .",
|
||||
"prepare": "husky",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
"eslintConfig": {
|
||||
@@ -79,14 +75,7 @@
|
||||
"@types/node": "^16.7.13",
|
||||
"@types/react": "^18.0.0",
|
||||
"@types/react-dom": "^18.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.13.2",
|
||||
"@typescript-eslint/parser": "^6.13.2",
|
||||
"crypto-browserify": "^3.12.0",
|
||||
"eslint": "^8.3.0",
|
||||
"eslint-config-react-app": "^7.0.1",
|
||||
"eslint-plugin-react": "^7.33.2",
|
||||
"eslint-webpack-plugin": "^3.1.1",
|
||||
"husky": "^9.0.11",
|
||||
"react-app-rewired": "^2.2.1",
|
||||
"stream-browserify": "^3.0.0"
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
const config = {
|
||||
arrowParens: "always",
|
||||
printWidth: 80,
|
||||
semi: false,
|
||||
singleQuote: true,
|
||||
tabWidth: 2,
|
||||
trailingComma: "es5",
|
||||
importOrderSeparation: true,
|
||||
importOrderSortSpecifiers: true,
|
||||
plugins: ["@trivago/prettier-plugin-sort-imports"],
|
||||
importOrder: [
|
||||
"<THIRD_PARTY_MODULES>",
|
||||
"^(pages|components|utils|icons|test|graphql)/(.*)$",
|
||||
"^[./]",
|
||||
],
|
||||
};
|
||||
|
||||
export default config;
|
||||
@@ -1,10 +0,0 @@
|
||||
<svg width="115" height="20" viewBox="0 0 115 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.37388 10.5194C5.70149 8.19185 7.14225 4.97748 7.1416 1.42853C7.14246 0.94681 7.11586 0.470456 7.063 0L-0.000488281 0.000643078L-0.000273922 13.5723C-0.000917354 15.2174 0.62632 16.863 1.88091 18.1175C3.1356 19.3721 4.78235 20.0001 6.42772 19.9993L6.42729 19.9997L19.9995 20L19.999 12.9355C19.5296 12.8838 19.0532 12.857 18.5704 12.8569C15.0224 12.8574 11.8079 14.298 9.48026 16.6255C7.78654 18.2768 5.07093 18.2771 3.39812 16.6043C1.72638 14.9325 1.72562 12.2161 3.37388 10.5194ZM18.5344 1.46863C16.5837 -0.481929 13.4146 -0.48268 11.4633 1.46863C9.512 3.41984 9.51276 6.58895 11.4633 8.53941C13.415 10.491 16.5831 10.4907 18.5344 8.53941C20.4857 6.5882 20.4861 3.42016 18.5344 1.46863Z" fill="#FBFBFB"/>
|
||||
<path d="M31.4741 18.5838H39.2552V16.3302H34.075V1.41351H31.4741V18.5838Z" fill="#FBFBFB"/>
|
||||
<path d="M49.8108 1.41351H45.4976L40.9893 18.5838H43.6769L44.8039 14.2913H50.3744L51.5014 18.5838H54.3191L49.8108 1.41351ZM45.3458 12.145L47.6 3.2593H47.6866L49.8541 12.145H45.3458Z" fill="#FBFBFB"/>
|
||||
<path d="M62.9292 8.06885H65.9636C65.9636 3.17534 64.3813 1.07196 60.6967 1.07196C56.8169 1.07196 55.1479 3.73341 55.1479 9.97909C55.1479 16.2462 56.8169 18.9291 60.6967 18.9291C64.3813 18.9291 65.9636 16.8901 65.9853 12.1468H62.9508C62.9292 15.8599 62.474 16.7828 60.6967 16.7828C58.6593 16.7828 58.1607 15.4307 58.1824 9.97909C58.1824 4.54896 58.6809 3.19678 60.6967 3.21823C62.474 3.21823 62.9292 4.18413 62.9292 8.06885Z" fill="#FBFBFB"/>
|
||||
<path d="M73.7781 1.07209C77.7229 1.09364 79.4135 3.77643 79.4135 10.0007C79.4135 16.2249 77.7229 18.9078 73.7781 18.9292C69.8117 18.9507 68.1211 16.2678 68.1211 10.0007C68.1211 3.73354 69.8117 1.05064 73.7781 1.07209ZM71.1555 10.0007C71.1555 15.4308 71.6757 16.783 73.7781 16.783C75.8589 16.783 76.3791 15.4308 76.3791 10.0007C76.3791 4.54909 75.8589 3.19691 73.7781 3.21847C71.6757 3.23992 71.1555 4.59209 71.1555 10.0007Z" fill="#FBFBFB"/>
|
||||
<path d="M85.0819 18.5624L82.481 18.5838V1.41351H87.0544L91.3243 15.4073H91.3676V1.41351H93.968V18.5838H89.677L85.1254 3.51689H85.0819V18.5624Z" fill="#FBFBFB"/>
|
||||
<path d="M100.468 1.41351H97.8677V18.5838H100.468V1.41351Z" fill="#FBFBFB"/>
|
||||
<path d="M111.139 8.06885H114.174C114.174 3.17534 112.591 1.07196 108.906 1.07196C105.028 1.07196 103.358 3.73341 103.358 9.97909C103.358 16.2462 105.028 18.9291 108.906 18.9291C112.591 18.9291 114.174 16.8901 114.195 12.1468H111.161C111.139 15.8599 110.684 16.7828 108.906 16.7828C106.869 16.7828 106.371 15.4307 106.393 9.97909C106.393 4.54896 106.891 3.19678 108.906 3.21823C110.684 3.21823 111.139 4.18413 111.139 8.06885Z" fill="#FBFBFB"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.7 KiB |
+21
-71
@@ -1,24 +1,21 @@
|
||||
<!doctype html>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta name="description" content="Laconic Wallet Web App" />
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=DM+Mono:ital,wght@0,300;0,400;0,500;1,300;1,400;1,500&display=swap"
|
||||
rel="stylesheet" />
|
||||
<!--
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Laconic Wallet Web App"
|
||||
/>
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<!--
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
@@ -27,58 +24,12 @@
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>Laconic Wallet</title>
|
||||
<style>
|
||||
#app {
|
||||
background-color: #0f0f0f;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
background-color: #0f0f0f;
|
||||
}
|
||||
|
||||
.loader-wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
.loader {
|
||||
border: 16px solid #e3e3e3;
|
||||
border-top: 16px solid #6750a4;
|
||||
border-radius: 50%;
|
||||
width: 140px;
|
||||
height: 140px;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root">
|
||||
<div class="loader-wrapper">
|
||||
<div class="loader"></div>
|
||||
</div>
|
||||
</div>
|
||||
<!--
|
||||
<title>Laconic Wallet</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
@@ -88,6 +39,5 @@
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -21,5 +21,5 @@
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#0f0f0f"
|
||||
"background_color": "#ffffff"
|
||||
}
|
||||
|
||||
+70
-155
@@ -1,70 +1,60 @@
|
||||
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { Button, Snackbar, Surface, Text } from "react-native-paper";
|
||||
import { TxBody, AuthInfo } from "cosmjs-types/cosmos/tx/v1beta1/tx";
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { Button, Snackbar, Text } from 'react-native-paper';
|
||||
import { TxBody, AuthInfo } from 'cosmjs-types/cosmos/tx/v1beta1/tx';
|
||||
|
||||
import { SignClientTypes } from "@walletconnect/types";
|
||||
import { useNavigation } from "@react-navigation/native";
|
||||
import { DirectSecp256k1Wallet } from "@cosmjs/proto-signing";
|
||||
import { SigningStargateClient } from "@cosmjs/stargate";
|
||||
import { SignClientTypes } from '@walletconnect/types';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import {
|
||||
createStackNavigator,
|
||||
StackNavigationProp,
|
||||
} from "@react-navigation/stack";
|
||||
import { getSdkError } from "@walletconnect/utils";
|
||||
import { Web3WalletTypes } from "@walletconnect/web3wallet";
|
||||
import { formatJsonRpcResult } from "@json-rpc-tools/utils";
|
||||
createStackNavigator,
|
||||
StackNavigationProp,
|
||||
} from '@react-navigation/stack';
|
||||
import { getSdkError } from '@walletconnect/utils';
|
||||
import { Web3WalletTypes } from '@walletconnect/web3wallet';
|
||||
import { formatJsonRpcResult } from '@json-rpc-tools/utils';
|
||||
|
||||
import PairingModal from "./components/PairingModal";
|
||||
import { useWalletConnect } from "./context/WalletConnectContext";
|
||||
import { useAccounts } from "./context/AccountsContext";
|
||||
import InvalidPath from "./screens/InvalidPath";
|
||||
import SignMessage from "./screens/SignMessage";
|
||||
import HomeScreen from "./screens/HomeScreen";
|
||||
import SignRequest from "./screens/SignRequest";
|
||||
import AddSession from "./screens/AddSession";
|
||||
import WalletConnect from "./screens/WalletConnect";
|
||||
import ApproveTransaction from "./screens/ApproveTransaction";
|
||||
import { StackParamsList } from "./types";
|
||||
import { EIP155_SIGNING_METHODS } from "./utils/wallet-connect/EIP155Data";
|
||||
import { getSignParamsMessage } from "./utils/wallet-connect/helpers";
|
||||
import ApproveTransfer from "./screens/ApproveTransfer";
|
||||
import AddNetwork from "./screens/AddNetwork";
|
||||
import EditNetwork from "./screens/EditNetwork";
|
||||
import { COSMOS, EIP155 } from "./utils/constants";
|
||||
import { useNetworks } from "./context/NetworksContext";
|
||||
import { NETWORK_METHODS } from "./utils/wallet-connect/common-data";
|
||||
import { COSMOS_METHODS } from "./utils/wallet-connect/COSMOSData";
|
||||
import styles from "./styles/stylesheet";
|
||||
import { Header } from "./components/Header";
|
||||
import { WalletEmbed } from "./screens/WalletEmbed";
|
||||
import { AutoSignIn } from "./screens/AutoSignIn";
|
||||
import { checkSufficientFunds, getPathKey, sendMessage } from "./utils/misc";
|
||||
import useAccountsData from "./hooks/useAccountsData";
|
||||
import { useWebViewHandler } from "./hooks/useWebViewHandler";
|
||||
import PairingModal from './components/PairingModal';
|
||||
import { useWalletConnect } from './context/WalletConnectContext';
|
||||
import { useAccounts } from './context/AccountsContext';
|
||||
import InvalidPath from './screens/InvalidPath';
|
||||
import SignMessage from './screens/SignMessage';
|
||||
import HomeScreen from './screens/HomeScreen';
|
||||
import SignRequest from './screens/SignRequest';
|
||||
import AddSession from './screens/AddSession';
|
||||
import WalletConnect from './screens/WalletConnect';
|
||||
import ApproveTransaction from './screens/ApproveTransaction';
|
||||
import { StackParamsList } from './types';
|
||||
import { EIP155_SIGNING_METHODS } from './utils/wallet-connect/EIP155Data';
|
||||
import { getSignParamsMessage } from './utils/wallet-connect/helpers';
|
||||
import ApproveTransfer from './screens/ApproveTransfer';
|
||||
import AddNetwork from './screens/AddNetwork';
|
||||
import EditNetwork from './screens/EditNetwork';
|
||||
import { COSMOS, EIP155 } from './utils/constants';
|
||||
import { useNetworks } from './context/NetworksContext';
|
||||
import { NETWORK_METHODS } from './utils/wallet-connect/common-data';
|
||||
import { COSMOS_METHODS } from './utils/wallet-connect/COSMOSData';
|
||||
|
||||
const Stack = createStackNavigator<StackParamsList>();
|
||||
|
||||
const App = (): React.JSX.Element => {
|
||||
const navigation = useNavigation<StackNavigationProp<StackParamsList>>();
|
||||
const navigation =
|
||||
useNavigation<StackNavigationProp<StackParamsList>>();
|
||||
|
||||
const { web3wallet, setActiveSessions } = useWalletConnect();
|
||||
const { accounts, setCurrentIndex } = useAccounts();
|
||||
const { networksData, selectedNetwork, setSelectedNetwork } = useNetworks();
|
||||
const { getAccountsData } = useAccountsData();
|
||||
|
||||
const [modalVisible, setModalVisible] = useState(false);
|
||||
const [toastVisible, setToastVisible] = useState(false);
|
||||
const [currentProposal, setCurrentProposal] = useState<
|
||||
SignClientTypes.EventArguments["session_proposal"] | undefined
|
||||
SignClientTypes.EventArguments['session_proposal'] | undefined
|
||||
>();
|
||||
|
||||
const onSessionProposal = useCallback(
|
||||
async (proposal: SignClientTypes.EventArguments["session_proposal"]) => {
|
||||
async (proposal: SignClientTypes.EventArguments['session_proposal']) => {
|
||||
if (!accounts.length || !accounts.length) {
|
||||
const { id } = proposal;
|
||||
await web3wallet!.rejectSession({
|
||||
id,
|
||||
reason: getSdkError("UNSUPPORTED_ACCOUNTS"),
|
||||
reason: getSdkError('UNSUPPORTED_ACCOUNTS'),
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -84,11 +74,10 @@ const App = (): React.JSX.Element => {
|
||||
switch (request.method) {
|
||||
case NETWORK_METHODS.GET_NETWORKS:
|
||||
const currentNetworkId = networksData.find(
|
||||
(networkData) =>
|
||||
networkData.networkId === selectedNetwork!.networkId,
|
||||
networkData => networkData.networkId === selectedNetwork!.networkId,
|
||||
)?.networkId;
|
||||
|
||||
const networkNamesData = networksData.map((networkData) => {
|
||||
const networkNamesData = networksData.map(networkData => {
|
||||
return {
|
||||
id: networkData.networkId,
|
||||
name: networkData.networkName,
|
||||
@@ -109,13 +98,13 @@ const App = (): React.JSX.Element => {
|
||||
case NETWORK_METHODS.CHANGE_NETWORK:
|
||||
const networkNameData = request.params[0];
|
||||
const network = networksData.find(
|
||||
(networkData) => networkData.networkId === networkNameData.id,
|
||||
networkData => networkData.networkId === networkNameData.id,
|
||||
);
|
||||
setCurrentIndex(0);
|
||||
setSelectedNetwork(network);
|
||||
|
||||
const response = formatJsonRpcResult(id, {
|
||||
response: "true",
|
||||
response: 'true',
|
||||
});
|
||||
|
||||
await web3wallet!.respondSessionRequest({
|
||||
@@ -125,7 +114,7 @@ const App = (): React.JSX.Element => {
|
||||
break;
|
||||
|
||||
case EIP155_SIGNING_METHODS.ETH_SEND_TRANSACTION:
|
||||
navigation.navigate("ApproveTransfer", {
|
||||
navigation.navigate('ApproveTransfer', {
|
||||
transaction: request.params[0],
|
||||
requestEvent,
|
||||
requestSessionData,
|
||||
@@ -133,7 +122,7 @@ const App = (): React.JSX.Element => {
|
||||
break;
|
||||
|
||||
case EIP155_SIGNING_METHODS.PERSONAL_SIGN:
|
||||
navigation.navigate("SignRequest", {
|
||||
navigation.navigate('SignRequest', {
|
||||
namespace: EIP155,
|
||||
address: request.params[1],
|
||||
message: getSignParamsMessage(request.params),
|
||||
@@ -147,19 +136,19 @@ const App = (): React.JSX.Element => {
|
||||
txbody: TxBody.toJSON(
|
||||
TxBody.decode(
|
||||
Uint8Array.from(
|
||||
Buffer.from(request.params.signDoc.bodyBytes, "hex"),
|
||||
Buffer.from(request.params.signDoc.bodyBytes, 'hex'),
|
||||
),
|
||||
),
|
||||
),
|
||||
authInfo: AuthInfo.toJSON(
|
||||
AuthInfo.decode(
|
||||
Uint8Array.from(
|
||||
Buffer.from(request.params.signDoc.authInfoBytes, "hex"),
|
||||
Buffer.from(request.params.signDoc.authInfoBytes, 'hex'),
|
||||
),
|
||||
),
|
||||
),
|
||||
};
|
||||
navigation.navigate("SignRequest", {
|
||||
navigation.navigate('SignRequest', {
|
||||
namespace: COSMOS,
|
||||
address: request.params.signerAddress,
|
||||
message: JSON.stringify(message, undefined, 2),
|
||||
@@ -169,7 +158,7 @@ const App = (): React.JSX.Element => {
|
||||
break;
|
||||
|
||||
case COSMOS_METHODS.COSMOS_SIGN_AMINO:
|
||||
navigation.navigate("SignRequest", {
|
||||
navigation.navigate('SignRequest', {
|
||||
namespace: COSMOS,
|
||||
address: request.params.signerAddress,
|
||||
message: request.params.signDoc.memo,
|
||||
@@ -179,7 +168,7 @@ const App = (): React.JSX.Element => {
|
||||
break;
|
||||
|
||||
case COSMOS_METHODS.COSMOS_SEND_TOKENS:
|
||||
navigation.navigate("ApproveTransfer", {
|
||||
navigation.navigate('ApproveTransfer', {
|
||||
transaction: request.params[0],
|
||||
requestEvent,
|
||||
requestSessionData,
|
||||
@@ -188,7 +177,7 @@ const App = (): React.JSX.Element => {
|
||||
|
||||
case COSMOS_METHODS.COSMOS_SEND_TRANSACTION:
|
||||
const { transactionMessage, signer } = request.params;
|
||||
navigation.navigate("ApproveTransaction", {
|
||||
navigation.navigate('ApproveTransaction', {
|
||||
transactionMessage,
|
||||
signer,
|
||||
requestEvent,
|
||||
@@ -197,7 +186,7 @@ const App = (): React.JSX.Element => {
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error("Invalid method");
|
||||
throw new Error('Invalid method');
|
||||
}
|
||||
},
|
||||
[
|
||||
@@ -206,7 +195,7 @@ const App = (): React.JSX.Element => {
|
||||
setSelectedNetwork,
|
||||
setCurrentIndex,
|
||||
selectedNetwork,
|
||||
web3wallet,
|
||||
web3wallet
|
||||
],
|
||||
);
|
||||
|
||||
@@ -216,74 +205,18 @@ const App = (): React.JSX.Element => {
|
||||
}, [setActiveSessions, web3wallet]);
|
||||
|
||||
useEffect(() => {
|
||||
web3wallet?.on("session_proposal", onSessionProposal);
|
||||
web3wallet?.on("session_request", onSessionRequest);
|
||||
web3wallet?.on("session_delete", onSessionDelete);
|
||||
web3wallet?.on('session_proposal', onSessionProposal);
|
||||
web3wallet?.on('session_request', onSessionRequest);
|
||||
web3wallet?.on('session_delete', onSessionDelete);
|
||||
return () => {
|
||||
web3wallet?.off("session_proposal", onSessionProposal);
|
||||
web3wallet?.off("session_request", onSessionRequest);
|
||||
web3wallet?.off("session_delete", onSessionDelete);
|
||||
web3wallet?.off('session_proposal', onSessionProposal);
|
||||
web3wallet?.off('session_request', onSessionRequest);
|
||||
web3wallet?.off('session_delete', onSessionDelete);
|
||||
};
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const handleCheckBalance = async (event: MessageEvent) => {
|
||||
if (event.data.type !== 'CHECK_BALANCE') return;
|
||||
|
||||
const { chainId, amount } = event.data;
|
||||
const network = networksData.find(net => net.chainId === chainId);
|
||||
|
||||
if (!network) {
|
||||
console.error('Network not found');
|
||||
throw new Error('Requested network not supported.');
|
||||
}
|
||||
|
||||
if (network.namespace !== COSMOS) {
|
||||
throw new Error('Unsupported network');
|
||||
}
|
||||
|
||||
const accounts = await getAccountsData(chainId);
|
||||
const account = accounts[0];
|
||||
|
||||
if (!account) {
|
||||
throw new Error(`No accounts in network ${chainId}`);
|
||||
}
|
||||
|
||||
|
||||
const cosmosPrivKey = (
|
||||
await getPathKey(`${network.namespace}:${chainId}`, account.index)
|
||||
).privKey;
|
||||
|
||||
const sender = await DirectSecp256k1Wallet.fromKey(
|
||||
Buffer.from(cosmosPrivKey.split('0x')[1], 'hex'),
|
||||
network.addressPrefix
|
||||
);
|
||||
|
||||
const client = await SigningStargateClient.connectWithSigner(network.rpcUrl!, sender);
|
||||
|
||||
const balance = await client.getBalance(
|
||||
account.address,
|
||||
network.nativeDenom!.toLowerCase()
|
||||
);
|
||||
|
||||
const areFundsSufficient = checkSufficientFunds(amount, balance.amount);
|
||||
|
||||
sendMessage(event.source as Window, 'IS_SUFFICIENT', areFundsSufficient, event.origin);
|
||||
};
|
||||
|
||||
window.addEventListener('message', handleCheckBalance);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('message', handleCheckBalance);
|
||||
};
|
||||
}, [networksData, getAccountsData]);
|
||||
|
||||
const showWalletConnect = useMemo(() => accounts.length > 0, [accounts]);
|
||||
|
||||
useWebViewHandler();
|
||||
|
||||
return (
|
||||
<Surface style={styles.appSurface}>
|
||||
<>
|
||||
<Stack.Navigator
|
||||
screenOptions={{
|
||||
headerBackTitleVisible: true,
|
||||
@@ -294,9 +227,7 @@ const App = (): React.JSX.Element => {
|
||||
component={HomeScreen}
|
||||
options={{
|
||||
// eslint-disable-next-line react/no-unstable-nested-components
|
||||
header: () => (
|
||||
<Header title="Wallet" showWalletConnect={showWalletConnect} />
|
||||
),
|
||||
headerTitle: () => <Text variant="titleLarge">Laconic Wallet</Text>,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
@@ -304,7 +235,7 @@ const App = (): React.JSX.Element => {
|
||||
component={SignMessage}
|
||||
options={{
|
||||
// eslint-disable-next-line react/no-unstable-nested-components
|
||||
header: () => <Header title="Wallet" />,
|
||||
headerTitle: () => <Text variant="titleLarge">Sign Message</Text>,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
@@ -312,7 +243,7 @@ const App = (): React.JSX.Element => {
|
||||
component={SignRequest}
|
||||
options={{
|
||||
// eslint-disable-next-line react/no-unstable-nested-components
|
||||
header: () => <Header title="Wallet" />,
|
||||
headerTitle: () => <Text variant="titleLarge">Sign Request</Text>,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
@@ -323,11 +254,11 @@ const App = (): React.JSX.Element => {
|
||||
headerTitle: () => <Text variant="titleLarge">Bad Request</Text>,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
<Stack.Screen
|
||||
name="AddSession"
|
||||
component={AddSession}
|
||||
options={{
|
||||
header: () => <Header title="Wallet" />,
|
||||
title: 'New session',
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
@@ -340,9 +271,8 @@ const App = (): React.JSX.Element => {
|
||||
headerRight: () => (
|
||||
<Button
|
||||
onPress={() => {
|
||||
navigation.navigate("AddSession");
|
||||
}}
|
||||
>
|
||||
navigation.navigate('AddSession');
|
||||
}}>
|
||||
{<Text>PAIR</Text>}
|
||||
</Button>
|
||||
),
|
||||
@@ -352,42 +282,28 @@ const App = (): React.JSX.Element => {
|
||||
name="ApproveTransfer"
|
||||
component={ApproveTransfer}
|
||||
options={{
|
||||
header: () => <Header title="Wallet" />,
|
||||
title: 'Approve transfer',
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="AddNetwork"
|
||||
component={AddNetwork}
|
||||
options={{
|
||||
header: () => <Header title="Wallet" />,
|
||||
title: 'Add Network',
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="EditNetwork"
|
||||
component={EditNetwork}
|
||||
options={{
|
||||
header: () => <Header title="Wallet" />,
|
||||
title: 'Edit Network',
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="ApproveTransaction"
|
||||
component={ApproveTransaction}
|
||||
options={{
|
||||
header: () => <Header title="Wallet" />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="wallet-embed"
|
||||
component={WalletEmbed}
|
||||
options={{
|
||||
header: () => <></>,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="auto-sign-in"
|
||||
component={AutoSignIn}
|
||||
options={{
|
||||
header: () => <></>,
|
||||
title: 'Approve Transaction',
|
||||
}}
|
||||
/>
|
||||
</Stack.Navigator>
|
||||
@@ -401,11 +317,10 @@ const App = (): React.JSX.Element => {
|
||||
<Snackbar
|
||||
visible={toastVisible}
|
||||
onDismiss={() => setToastVisible(false)}
|
||||
duration={3000}
|
||||
>
|
||||
duration={3000}>
|
||||
Session approved
|
||||
</Snackbar>
|
||||
</Surface>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import React from "react";
|
||||
import React from 'react';
|
||||
import { View } from 'react-native';
|
||||
import { Text } from 'react-native-paper';
|
||||
|
||||
import { Account } from "../types";
|
||||
import { Box, Stack, Typography } from "@mui/material";
|
||||
import { Account } from '../types';
|
||||
import styles from '../styles/stylesheet';
|
||||
|
||||
interface AccountDetailsProps {
|
||||
account: Account | undefined;
|
||||
@@ -9,28 +11,20 @@ interface AccountDetailsProps {
|
||||
|
||||
const AccountDetails: React.FC<AccountDetailsProps> = ({ account }) => {
|
||||
return (
|
||||
<Box sx={{ marginY: 4 }}>
|
||||
<Stack spacing={1}>
|
||||
<Stack flexDirection="row">
|
||||
<Typography color="secondary" sx={{ mr: 1 }}>
|
||||
Address:
|
||||
</Typography>
|
||||
<Typography color="text.primary">{account?.address}</Typography>
|
||||
</Stack>
|
||||
</Stack>
|
||||
<Stack flexDirection="row">
|
||||
<Typography color="secondary" sx={{ mr: 1 }}>
|
||||
Public Key:
|
||||
</Typography>
|
||||
<Typography color="text.primary">{account?.pubKey}</Typography>
|
||||
</Stack>
|
||||
<Stack flexDirection="row">
|
||||
<Typography color="secondary" sx={{ mr: 1 }}>
|
||||
HD Path:
|
||||
</Typography>
|
||||
<Typography color="text.primary">{account?.hdPath}</Typography>
|
||||
</Stack>
|
||||
</Box>
|
||||
<View style={styles.accountContainer}>
|
||||
<Text variant="bodyLarge" selectable={true}>
|
||||
<Text style={styles.highlight}>Address: </Text>
|
||||
{account?.address}
|
||||
</Text>
|
||||
<Text variant="bodyLarge" selectable={true}>
|
||||
<Text style={styles.highlight}>Public Key: </Text>
|
||||
{account?.pubKey}
|
||||
</Text>
|
||||
<Text variant="bodyLarge">
|
||||
<Text style={styles.highlight}>HD Path: </Text>
|
||||
{account?.hdPath}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
+102
-88
@@ -1,30 +1,22 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { TouchableOpacity, View } from "react-native";
|
||||
import { List } from "react-native-paper";
|
||||
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { TouchableOpacity, View } from 'react-native';
|
||||
import { Button, List, Text, useTheme } from 'react-native-paper';
|
||||
|
||||
import { useNavigation } from "@react-navigation/native";
|
||||
import { NativeStackNavigationProp } from "@react-navigation/native-stack";
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
|
||||
import { StackParamsList, Account } from "../types";
|
||||
import { addAccount } from "../utils/accounts";
|
||||
import HDPathDialog from "./HDPathDialog";
|
||||
import AccountDetails from "./AccountDetails";
|
||||
import { useAccounts } from "../context/AccountsContext";
|
||||
import { useWalletConnect } from "../context/WalletConnectContext";
|
||||
import { useNetworks } from "../context/NetworksContext";
|
||||
import ConfirmDialog from "./ConfirmDialog";
|
||||
import { getNamespaces } from "../utils/wallet-connect/helpers";
|
||||
import ShowPKDialog from "./ShowPKDialog";
|
||||
import { setInternetCredentials } from "../utils/key-store";
|
||||
import {
|
||||
Accordion,
|
||||
AccordionSummary,
|
||||
Button,
|
||||
Link,
|
||||
Stack,
|
||||
} from "@mui/material";
|
||||
import { LoadingButton } from "@mui/lab";
|
||||
import { StackParamsList, Account } from '../types';
|
||||
import { addAccount } from '../utils/accounts';
|
||||
import styles from '../styles/stylesheet';
|
||||
import HDPathDialog from './HDPathDialog';
|
||||
import AccountDetails from './AccountDetails';
|
||||
import { useAccounts } from '../context/AccountsContext';
|
||||
import { useWalletConnect } from '../context/WalletConnectContext';
|
||||
import { useNetworks } from '../context/NetworksContext';
|
||||
import ConfirmDialog from './ConfirmDialog';
|
||||
import { getNamespaces } from '../utils/wallet-connect/helpers';
|
||||
import ShowPKDialog from './ShowPKDialog';
|
||||
import { setInternetCredentials } from '../utils/key-store';
|
||||
|
||||
const Accounts = () => {
|
||||
const navigation =
|
||||
@@ -35,14 +27,16 @@ const Accounts = () => {
|
||||
const { networksData, selectedNetwork, setNetworksData, setSelectedNetwork } =
|
||||
useNetworks();
|
||||
|
||||
const { web3wallet } = useWalletConnect();
|
||||
const {web3wallet} = useWalletConnect();
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
const [isAccountCreating, setIsAccountCreating] = useState(false);
|
||||
const [hdDialog, setHdDialog] = useState(false);
|
||||
const [pathCode, setPathCode] = useState("");
|
||||
const [pathCode, setPathCode] = useState('');
|
||||
const [deleteNetworkDialog, setDeleteNetworkDialog] =
|
||||
useState<boolean>(false);
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
const handlePress = () => setExpanded(!expanded);
|
||||
|
||||
const hideDeleteNetworkDialog = () => setDeleteNetworkDialog(false);
|
||||
@@ -94,7 +88,7 @@ const Accounts = () => {
|
||||
};
|
||||
|
||||
const renderAccountItems = () =>
|
||||
accounts.map((account) => (
|
||||
accounts.map(account => (
|
||||
<List.Item
|
||||
key={account.index}
|
||||
title={`Account ${account.index + 1}`}
|
||||
@@ -107,12 +101,12 @@ const Accounts = () => {
|
||||
|
||||
const handleRemove = async () => {
|
||||
const updatedNetworks = networksData.filter(
|
||||
(networkData) => selectedNetwork!.networkId !== networkData.networkId,
|
||||
networkData => selectedNetwork!.networkId !== networkData.networkId,
|
||||
);
|
||||
|
||||
await setInternetCredentials(
|
||||
"networks",
|
||||
"_",
|
||||
'networks',
|
||||
'_',
|
||||
JSON.stringify(updatedNetworks),
|
||||
);
|
||||
|
||||
@@ -131,74 +125,94 @@ const Accounts = () => {
|
||||
updateAccounts={updateAccounts}
|
||||
pathCode={pathCode}
|
||||
/>
|
||||
<Accordion expanded={expanded} onClick={handlePress}>
|
||||
<AccordionSummary
|
||||
expandIcon={<ExpandMoreIcon />}
|
||||
>{`Account ${currentIndex + 1}`}</AccordionSummary>
|
||||
<List.Accordion
|
||||
title={`Account ${currentIndex + 1}`}
|
||||
expanded={expanded}
|
||||
onPress={handlePress}>
|
||||
{renderAccountItems()}
|
||||
</Accordion>
|
||||
|
||||
<Stack direction="row" spacing={3} sx={{ mt: 4 }}>
|
||||
<LoadingButton
|
||||
variant="contained"
|
||||
onClick={addAccountHandler}
|
||||
loading={isAccountCreating}
|
||||
disabled={isAccountCreating}
|
||||
>
|
||||
{isAccountCreating ? "Adding" : "Add Account"}
|
||||
</LoadingButton>
|
||||
</List.Accordion>
|
||||
|
||||
<View style={styles.addAccountButton}>
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={() => {
|
||||
mode="contained"
|
||||
onPress={addAccountHandler}
|
||||
loading={isAccountCreating}
|
||||
disabled={isAccountCreating}>
|
||||
{isAccountCreating ? 'Adding' : 'Add Account'}
|
||||
</Button>
|
||||
</View>
|
||||
|
||||
<View style={styles.addAccountButton}>
|
||||
<Button
|
||||
mode="contained"
|
||||
onPress={() => {
|
||||
setHdDialog(true);
|
||||
setPathCode(`m/44'/${selectedNetwork!.coinType}'/`);
|
||||
}}
|
||||
>
|
||||
}}>
|
||||
Add Account from HD path
|
||||
</Button>
|
||||
</Stack>
|
||||
</View>
|
||||
|
||||
<AccountDetails account={accounts[currentIndex]} />
|
||||
<Stack direction="row" spacing={4}>
|
||||
<TouchableOpacity
|
||||
onPress={() => {
|
||||
navigation.navigate("SignMessage", {
|
||||
selectedNamespace: selectedNetwork!.namespace,
|
||||
selectedChainId: selectedNetwork!.chainId,
|
||||
accountInfo: accounts[currentIndex],
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Link>Sign Message</Link>
|
||||
</TouchableOpacity>
|
||||
|
||||
<TouchableOpacity
|
||||
onPress={() => {
|
||||
navigation.navigate("AddNetwork");
|
||||
}}
|
||||
>
|
||||
<Link>Add Network</Link>
|
||||
</TouchableOpacity>
|
||||
|
||||
<TouchableOpacity
|
||||
onPress={() => {
|
||||
navigation.navigate("EditNetwork", {
|
||||
selectedNetwork: selectedNetwork!,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Link>Edit Network</Link>
|
||||
</TouchableOpacity>
|
||||
|
||||
{!selectedNetwork!.isDefault && (
|
||||
<View style={styles.linkContainer}>
|
||||
<View style={styles.signLink}>
|
||||
<TouchableOpacity
|
||||
onPress={() => {
|
||||
setDeleteNetworkDialog(true);
|
||||
}}
|
||||
>
|
||||
<Link>Delete Network</Link>
|
||||
navigation.navigate('SignMessage', {
|
||||
selectedNamespace: selectedNetwork!.namespace,
|
||||
selectedChainId: selectedNetwork!.chainId,
|
||||
accountInfo: accounts[currentIndex],
|
||||
});
|
||||
}}>
|
||||
<Text
|
||||
variant="titleSmall"
|
||||
style={[styles.hyperlink, { color: theme.colors.primary }]}>
|
||||
Sign Message
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
<View style={styles.signLink}>
|
||||
<TouchableOpacity
|
||||
onPress={() => {
|
||||
navigation.navigate('AddNetwork');
|
||||
}}>
|
||||
<Text
|
||||
variant="titleSmall"
|
||||
style={[styles.hyperlink, { color: theme.colors.primary }]}>
|
||||
Add Network
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
<View style={styles.signLink}>
|
||||
<TouchableOpacity
|
||||
onPress={() => {
|
||||
navigation.navigate('EditNetwork', {
|
||||
selectedNetwork: selectedNetwork!,
|
||||
});
|
||||
}}>
|
||||
<Text
|
||||
variant="titleSmall"
|
||||
style={[styles.hyperlink, { color: theme.colors.primary }]}>
|
||||
Edit Network
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
{!selectedNetwork!.isDefault && (
|
||||
<View style={styles.signLink}>
|
||||
<TouchableOpacity
|
||||
onPress={() => {
|
||||
setDeleteNetworkDialog(true);
|
||||
}}>
|
||||
<Text
|
||||
variant="titleSmall"
|
||||
style={[styles.hyperlink, { color: theme.colors.primary }]}>
|
||||
Delete Network
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
)}
|
||||
<ConfirmDialog
|
||||
title="Delete Network"
|
||||
@@ -207,7 +221,7 @@ const Accounts = () => {
|
||||
onConfirm={handleRemove}
|
||||
/>
|
||||
<ShowPKDialog />
|
||||
</Stack>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import { Dialog, DialogTitle, DialogContent, DialogActions, Button, Typography } from '@mui/material';
|
||||
import { ResetDialogProps } from '../types';
|
||||
import styles from "../styles/stylesheet";
|
||||
|
||||
const ConfirmDialog = ({
|
||||
title,
|
||||
@@ -11,15 +10,15 @@ const ConfirmDialog = ({
|
||||
}: ResetDialogProps) => {
|
||||
return (
|
||||
<Dialog open={visible} onClose={hideDialog}>
|
||||
<DialogTitle style={{...styles.resetDialogTitle}} >{title}</DialogTitle>
|
||||
<DialogContent style={{...styles.resetDialogContent}}>
|
||||
<Typography>Are you sure?</Typography>
|
||||
<DialogTitle>{title}</DialogTitle>
|
||||
<DialogContent>
|
||||
<Typography variant="body1">Are you sure?</Typography>
|
||||
</DialogContent>
|
||||
<DialogActions style={{...styles.resetDialogActionRow}}>
|
||||
<Button style={{...styles.buttonRed, ...styles.button}} color="error" onClick={onConfirm}>
|
||||
<DialogActions>
|
||||
<Button color="error" onClick={onConfirm}>
|
||||
Yes
|
||||
</Button>
|
||||
<Button style={{...styles.buttonBlue, ...styles.button}} onClick={hideDialog}>No</Button>
|
||||
<Button onClick={hideDialog}>No</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
import { Box, BoxProps } from "@mui/material";
|
||||
import React, { PropsWithChildren } from "react";
|
||||
|
||||
export const Container: React.FC<
|
||||
PropsWithChildren<{ boxProps?: BoxProps }>
|
||||
> = ({ children, boxProps = {} }) => (
|
||||
<Box
|
||||
{...boxProps}
|
||||
sx={{
|
||||
width: "100%",
|
||||
maxWidth: "752px",
|
||||
marginX: "auto",
|
||||
backgroundColor: "background.paper",
|
||||
padding: 3,
|
||||
borderRadius: 2,
|
||||
...boxProps.sx,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
);
|
||||
@@ -1,8 +1,9 @@
|
||||
import React from "react";
|
||||
import { View } from "react-native";
|
||||
import { LoadingButton } from "@mui/lab";
|
||||
import { View } from 'react-native';
|
||||
import React from 'react';
|
||||
import { Button } from 'react-native-paper';
|
||||
|
||||
import { CreateWalletProps } from "../types";
|
||||
import { CreateWalletProps } from '../types';
|
||||
import styles from '../styles/stylesheet';
|
||||
|
||||
const CreateWallet = ({
|
||||
isWalletCreating,
|
||||
@@ -10,15 +11,14 @@ const CreateWallet = ({
|
||||
}: CreateWalletProps) => {
|
||||
return (
|
||||
<View>
|
||||
<View>
|
||||
<LoadingButton
|
||||
variant="contained"
|
||||
<View style={styles.createWalletContainer}>
|
||||
<Button
|
||||
mode="contained"
|
||||
loading={isWalletCreating}
|
||||
disabled={isWalletCreating}
|
||||
onClick={createWalletHandler}
|
||||
>
|
||||
{isWalletCreating ? "Creating" : "CREATE WALLET"}
|
||||
</LoadingButton>
|
||||
onPress={createWalletHandler}>
|
||||
{isWalletCreating ? 'Creating' : 'Create Wallet'}
|
||||
</Button>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
|
||||
+14
-30
@@ -1,44 +1,28 @@
|
||||
import React from "react";
|
||||
import {
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
Button,
|
||||
Typography,
|
||||
} from "@mui/material";
|
||||
import React from 'react';
|
||||
import { Dialog, DialogActions, DialogContent, DialogTitle, Button, Typography } from '@mui/material';
|
||||
|
||||
import styles from "../styles/stylesheet";
|
||||
import GridView from "./Grid";
|
||||
import { CustomDialogProps } from "../types";
|
||||
import styles from '../styles/stylesheet';
|
||||
import GridView from './Grid';
|
||||
import { CustomDialogProps } from '../types';
|
||||
|
||||
const DialogComponent = ({
|
||||
visible,
|
||||
hideDialog,
|
||||
contentText,
|
||||
}: CustomDialogProps) => {
|
||||
const words = contentText.split(" ");
|
||||
|
||||
const copyMnemonic = () => {
|
||||
navigator.clipboard.writeText(contentText);
|
||||
};
|
||||
const DialogComponent = ({ visible, hideDialog, contentText }: CustomDialogProps) => {
|
||||
const words = contentText.split(' ');
|
||||
|
||||
return (
|
||||
<Dialog open={visible} onClose={hideDialog}>
|
||||
<DialogTitle style={{...styles.mnemonicTitle}}>Mnemonic</DialogTitle>
|
||||
<DialogContent style={{...styles.mnemonicContainer}}>
|
||||
<Typography component="div">
|
||||
Your mnemonic provides full access to your wallet and funds.<br/>
|
||||
Make sure to note it down.
|
||||
<DialogTitle>Mnemonic</DialogTitle>
|
||||
<DialogContent>
|
||||
<Typography variant="h6" component="div" style={{ color: 'rgba(0, 0, 0, 0.87)' }}>
|
||||
Your mnemonic provides full access to your wallet and funds. Make sure to note it down.
|
||||
</Typography>
|
||||
<Typography component="div" style={{ ...styles.mnemonicDialogWarning }}>
|
||||
<Typography variant="h6" component="div" style={{ ...styles.dialogWarning }}>
|
||||
Do not share your mnemonic with anyone
|
||||
</Typography>
|
||||
<GridView words={words} />
|
||||
</DialogContent>
|
||||
<DialogActions style={{...styles.mnemonicButtonRow}}>
|
||||
<Button style={{...styles.mnemonicButton}} onClick={copyMnemonic}>Copy</Button>
|
||||
<Button style={{...styles.mnemonicButton}} onClick={hideDialog}>Done</Button>
|
||||
<DialogActions>
|
||||
<Button onClick={hideDialog}>Done</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
|
||||
@@ -7,7 +7,7 @@ import { GridViewProps } from '../types';
|
||||
|
||||
const GridView = ({ words }: GridViewProps) => {
|
||||
return (
|
||||
<View style={styles.mnemonicGridContainer}>
|
||||
<View style={styles.gridContainer}>
|
||||
{words.map((word, index) => (
|
||||
<View key={index} style={styles.gridItem}>
|
||||
<Text>{index + 1}. </Text>
|
||||
|
||||
+23
-25
@@ -1,12 +1,11 @@
|
||||
import React, { useState } from "react";
|
||||
import { ScrollView, View, Text } from "react-native";
|
||||
import { TextInput } from "react-native-paper";
|
||||
import React, { useState } from 'react';
|
||||
import { ScrollView, View, Text } from 'react-native';
|
||||
import { Button, TextInput } from 'react-native-paper';
|
||||
|
||||
import { addAccountFromHDPath } from "../utils/accounts";
|
||||
import { Account, NetworksDataState, PathState } from "../types";
|
||||
import styles from "../styles/stylesheet";
|
||||
import { useAccounts } from "../context/AccountsContext";
|
||||
import { LoadingButton } from "@mui/lab";
|
||||
import { addAccountFromHDPath } from '../utils/accounts';
|
||||
import { Account, NetworksDataState, PathState } from '../types';
|
||||
import styles from '../styles/stylesheet';
|
||||
import { useAccounts } from '../context/AccountsContext';
|
||||
|
||||
const HDPath = ({
|
||||
pathCode,
|
||||
@@ -22,19 +21,19 @@ const HDPath = ({
|
||||
const { setCurrentIndex } = useAccounts();
|
||||
const [isAccountCreating, setIsAccountCreating] = useState(false);
|
||||
const [path, setPath] = useState<PathState>({
|
||||
firstNumber: "",
|
||||
secondNumber: "",
|
||||
thirdNumber: "",
|
||||
firstNumber: '',
|
||||
secondNumber: '',
|
||||
thirdNumber: '',
|
||||
});
|
||||
|
||||
const handleChange = (key: keyof PathState, value: string) => {
|
||||
if (key === "secondNumber" && value !== "" && !["0", "1"].includes(value)) {
|
||||
if (key === 'secondNumber' && value !== '' && !['0', '1'].includes(value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
setPath({
|
||||
...path,
|
||||
[key]: value.replace(/[^0-9]/g, ""),
|
||||
[key]: value.replace(/[^0-9]/g, ''),
|
||||
});
|
||||
};
|
||||
|
||||
@@ -51,7 +50,7 @@ const HDPath = ({
|
||||
hideDialog();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error creating account:", error);
|
||||
console.error('Error creating account:', error);
|
||||
} finally {
|
||||
setIsAccountCreating(false);
|
||||
}
|
||||
@@ -64,15 +63,15 @@ const HDPath = ({
|
||||
<TextInput
|
||||
keyboardType="numeric"
|
||||
mode="outlined"
|
||||
onChangeText={(text) => handleChange("firstNumber", text)}
|
||||
onChangeText={text => handleChange('firstNumber', text)}
|
||||
value={path.firstNumber}
|
||||
style={styles.HDtextInput}
|
||||
/>
|
||||
<Text style={styles.HDtext}>{"'/"}</Text>
|
||||
<Text style={styles.HDtext}>'/</Text>
|
||||
<TextInput
|
||||
keyboardType="numeric"
|
||||
mode="outlined"
|
||||
onChangeText={(text) => handleChange("secondNumber", text)}
|
||||
onChangeText={text => handleChange('secondNumber', text)}
|
||||
value={path.secondNumber}
|
||||
style={styles.HDtextInput}
|
||||
/>
|
||||
@@ -80,20 +79,19 @@ const HDPath = ({
|
||||
<TextInput
|
||||
keyboardType="numeric"
|
||||
mode="outlined"
|
||||
onChangeText={(text) => handleChange("thirdNumber", text)}
|
||||
onChangeText={text => handleChange('thirdNumber', text)}
|
||||
value={path.thirdNumber}
|
||||
style={styles.HDtextInput}
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.HDbuttonContainer}>
|
||||
<LoadingButton
|
||||
variant="contained"
|
||||
onClick={createFromHDPathHandler}
|
||||
<Button
|
||||
mode="contained"
|
||||
onPress={createFromHDPathHandler}
|
||||
loading={isAccountCreating}
|
||||
disabled={isAccountCreating}
|
||||
>
|
||||
{isAccountCreating ? "Adding" : "Add Account"}
|
||||
</LoadingButton>
|
||||
disabled={isAccountCreating}>
|
||||
{isAccountCreating ? 'Adding' : 'Add Account'}
|
||||
</Button>
|
||||
</View>
|
||||
</ScrollView>
|
||||
);
|
||||
|
||||
@@ -1,113 +0,0 @@
|
||||
import {
|
||||
Button,
|
||||
Divider,
|
||||
Link,
|
||||
Stack,
|
||||
SvgIcon,
|
||||
Typography,
|
||||
} from "@mui/material";
|
||||
import React from "react";
|
||||
import { useNavigation } from "@react-navigation/native";
|
||||
import { Image } from "react-native";
|
||||
import { NativeStackNavigationProp } from "@react-navigation/native-stack";
|
||||
import { StackParamsList } from "../types";
|
||||
import styles from "../styles/stylesheet";
|
||||
|
||||
const WCLogo = () => {
|
||||
return (
|
||||
<Image
|
||||
style={styles.walletConnectLogo}
|
||||
source={require("../assets/WalletConnect-Icon-Blueberry.png")}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const Header: React.FC<{
|
||||
title: string;
|
||||
showWalletConnect?: boolean;
|
||||
}> = ({ title, showWalletConnect }) => {
|
||||
const navigation =
|
||||
useNavigation<NativeStackNavigationProp<StackParamsList>>();
|
||||
return (
|
||||
<Stack
|
||||
direction="row"
|
||||
sx={{
|
||||
backgroundColor: "background.paper",
|
||||
pl: 2,
|
||||
alignItems: "center",
|
||||
py: 1,
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
spacing={1}
|
||||
>
|
||||
<Stack direction="row">
|
||||
<Button
|
||||
component={Link}
|
||||
onClick={() => {
|
||||
navigation.navigate("Home");
|
||||
}}
|
||||
>
|
||||
<SvgIcon sx={{ height: 20, width: 100 }}>
|
||||
<svg
|
||||
width="115"
|
||||
height="20"
|
||||
viewBox="0 0 115 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M3.37388 10.5194C5.70149 8.19185 7.14225 4.97748 7.1416 1.42853C7.14246 0.94681 7.11586 0.470456 7.063 0L-0.000488281 0.000643078L-0.000273922 13.5723C-0.000917354 15.2174 0.62632 16.863 1.88091 18.1175C3.1356 19.3721 4.78235 20.0001 6.42772 19.9993L6.42729 19.9997L19.9995 20L19.999 12.9355C19.5296 12.8838 19.0532 12.857 18.5704 12.8569C15.0224 12.8574 11.8079 14.298 9.48026 16.6255C7.78654 18.2768 5.07093 18.2771 3.39812 16.6043C1.72638 14.9325 1.72562 12.2161 3.37388 10.5194ZM18.5344 1.46863C16.5837 -0.481929 13.4146 -0.48268 11.4633 1.46863C9.512 3.41984 9.51276 6.58895 11.4633 8.53941C13.415 10.491 16.5831 10.4907 18.5344 8.53941C20.4857 6.5882 20.4861 3.42016 18.5344 1.46863Z"
|
||||
fill="#FBFBFB"
|
||||
/>
|
||||
<path
|
||||
d="M31.4741 18.5838H39.2552V16.3302H34.075V1.41351H31.4741V18.5838Z"
|
||||
fill="#FBFBFB"
|
||||
/>
|
||||
<path
|
||||
d="M49.8108 1.41351H45.4976L40.9893 18.5838H43.6769L44.8039 14.2913H50.3744L51.5014 18.5838H54.3191L49.8108 1.41351ZM45.3458 12.145L47.6 3.2593H47.6866L49.8541 12.145H45.3458Z"
|
||||
fill="#FBFBFB"
|
||||
/>
|
||||
<path
|
||||
d="M62.9292 8.06885H65.9636C65.9636 3.17534 64.3813 1.07196 60.6967 1.07196C56.8169 1.07196 55.1479 3.73341 55.1479 9.97909C55.1479 16.2462 56.8169 18.9291 60.6967 18.9291C64.3813 18.9291 65.9636 16.8901 65.9853 12.1468H62.9508C62.9292 15.8599 62.474 16.7828 60.6967 16.7828C58.6593 16.7828 58.1607 15.4307 58.1824 9.97909C58.1824 4.54896 58.6809 3.19678 60.6967 3.21823C62.474 3.21823 62.9292 4.18413 62.9292 8.06885Z"
|
||||
fill="#FBFBFB"
|
||||
/>
|
||||
<path
|
||||
d="M73.7781 1.07209C77.7229 1.09364 79.4135 3.77643 79.4135 10.0007C79.4135 16.2249 77.7229 18.9078 73.7781 18.9292C69.8117 18.9507 68.1211 16.2678 68.1211 10.0007C68.1211 3.73354 69.8117 1.05064 73.7781 1.07209ZM71.1555 10.0007C71.1555 15.4308 71.6757 16.783 73.7781 16.783C75.8589 16.783 76.3791 15.4308 76.3791 10.0007C76.3791 4.54909 75.8589 3.19691 73.7781 3.21847C71.6757 3.23992 71.1555 4.59209 71.1555 10.0007Z"
|
||||
fill="#FBFBFB"
|
||||
/>
|
||||
<path
|
||||
d="M85.0819 18.5624L82.481 18.5838V1.41351H87.0544L91.3243 15.4073H91.3676V1.41351H93.968V18.5838H89.677L85.1254 3.51689H85.0819V18.5624Z"
|
||||
fill="#FBFBFB"
|
||||
/>
|
||||
<path
|
||||
d="M100.468 1.41351H97.8677V18.5838H100.468V1.41351Z"
|
||||
fill="#FBFBFB"
|
||||
/>
|
||||
<path
|
||||
d="M111.139 8.06885H114.174C114.174 3.17534 112.591 1.07196 108.906 1.07196C105.028 1.07196 103.358 3.73341 103.358 9.97909C103.358 16.2462 105.028 18.9291 108.906 18.9291C112.591 18.9291 114.174 16.8901 114.195 12.1468H111.161C111.139 15.8599 110.684 16.7828 108.906 16.7828C106.869 16.7828 106.371 15.4307 106.393 9.97909C106.393 4.54896 106.891 3.19678 108.906 3.21823C110.684 3.21823 111.139 4.18413 111.139 8.06885Z"
|
||||
fill="#FBFBFB"
|
||||
/>
|
||||
</svg>
|
||||
</SvgIcon>
|
||||
</Button>
|
||||
<Divider
|
||||
flexItem
|
||||
orientation="vertical"
|
||||
color="#FBFBFB"
|
||||
sx={{ height: "20px", width: "1px", alignSelf: "center" }}
|
||||
/>
|
||||
<Typography fontSize="1.25rem" sx={{ paddingLeft: 1 }}>
|
||||
{title}
|
||||
</Typography>
|
||||
</Stack>
|
||||
|
||||
{showWalletConnect && (
|
||||
<Button onClick={() => navigation.navigate("WalletConnect")}>
|
||||
{<WCLogo />}
|
||||
</Button>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
@@ -1,84 +0,0 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import styles from "../styles/stylesheet";
|
||||
import {
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
TextField,
|
||||
Grid,
|
||||
Button,
|
||||
Typography,
|
||||
} from "@mui/material";
|
||||
|
||||
const ImportWalletDialog = ({
|
||||
visible,
|
||||
hideDialog,
|
||||
importWalletHandler,
|
||||
}: {
|
||||
visible: boolean;
|
||||
hideDialog: () => void;
|
||||
importWalletHandler: (recoveryPhrase: string) => Promise<void>;
|
||||
}) => {
|
||||
const [words, setWords] = useState(Array(12).fill(""));
|
||||
|
||||
const handleWordChange = (index: number, value: string) => {
|
||||
const newWords = [...words];
|
||||
newWords[index] = value;
|
||||
setWords(newWords);
|
||||
};
|
||||
|
||||
const handlePaste = (event: React.ClipboardEvent<HTMLDivElement>) => {
|
||||
const pastedText = event.clipboardData.getData("Text");
|
||||
const splitWords = pastedText.trim().split(/\s+/);
|
||||
|
||||
if (splitWords.length === 12) {
|
||||
setWords(splitWords);
|
||||
event.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setWords(Array(12).fill(""));
|
||||
}, [visible]);
|
||||
|
||||
return (
|
||||
<Dialog open={visible} onClose={hideDialog}>
|
||||
<DialogTitle style={styles.mnemonicTitle}>
|
||||
Import your wallet from your mnemonic
|
||||
</DialogTitle>
|
||||
<DialogContent style={styles.mnemonicContainer}>
|
||||
<Typography component="div">
|
||||
(You can paste your entire mnemonic into the first textbox)
|
||||
</Typography>
|
||||
<Grid container spacing={2}>
|
||||
{words.map((word, index) => (
|
||||
<Grid item xs={6} sm={4} key={index}>
|
||||
<TextField
|
||||
value={word}
|
||||
onChange={(e) => handleWordChange(index, e.target.value)}
|
||||
onPaste={index === 0 ? handlePaste : undefined}
|
||||
placeholder={`Word ${index + 1}`}
|
||||
fullWidth
|
||||
margin="normal"
|
||||
/>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</DialogContent>
|
||||
<DialogActions style={styles.mnemonicButtonRow}>
|
||||
<Button
|
||||
onClick={() => importWalletHandler(words.join(" "))}
|
||||
style={styles.mnemonicButton}
|
||||
>
|
||||
Import Wallet
|
||||
</Button>
|
||||
<Button onClick={hideDialog} style={styles.mnemonicButton}>
|
||||
Cancel
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default ImportWalletDialog;
|
||||
@@ -1,34 +0,0 @@
|
||||
import { Button, Typography } from "@mui/material";
|
||||
import React, { PropsWithChildren } from "react";
|
||||
import { Container } from "./Container";
|
||||
import { ArrowBack } from "@mui/icons-material";
|
||||
import { NativeStackNavigationProp } from "@react-navigation/native-stack";
|
||||
import { useNavigation } from "@react-navigation/native";
|
||||
import { StackParamsList } from "../types";
|
||||
|
||||
export const Layout: React.FC<PropsWithChildren<{ title: string }>> = ({
|
||||
children,
|
||||
title,
|
||||
}) => {
|
||||
const navigation =
|
||||
useNavigation<NativeStackNavigationProp<StackParamsList>>();
|
||||
|
||||
return (
|
||||
<Container
|
||||
boxProps={{ sx: { backgroundColor: "inherit", padding: 0, mt: 3 } }}
|
||||
>
|
||||
<Button
|
||||
startIcon={<ArrowBack />}
|
||||
color="info"
|
||||
sx={{ mb: 4 }}
|
||||
onClick={() => navigation.navigate("Home")}
|
||||
>
|
||||
Home
|
||||
</Button>
|
||||
<Typography variant="h4" sx={{ mb: 4 }}>
|
||||
{title}
|
||||
</Typography>
|
||||
<Container>{children}</Container>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
@@ -1,51 +0,0 @@
|
||||
import React from "react";
|
||||
import {
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
Button,
|
||||
Typography,
|
||||
} from "@mui/material";
|
||||
|
||||
import styles from "../styles/stylesheet";
|
||||
import GridView from "./Grid";
|
||||
import { CustomDialogProps } from "../types";
|
||||
|
||||
const MnemonicDialog = ({
|
||||
visible,
|
||||
hideDialog,
|
||||
contentText,
|
||||
}: CustomDialogProps) => {
|
||||
const words = contentText.split(" ");
|
||||
|
||||
const copyMnemonic = () => {
|
||||
navigator.clipboard.writeText(contentText);
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={visible} onClose={hideDialog}>
|
||||
<DialogTitle style={{ ...styles.mnemonicTitle }}>Mnemonic</DialogTitle>
|
||||
<DialogContent style={{ ...styles.mnemonicContainer }}>
|
||||
<Typography component="div">
|
||||
Your mnemonic provides full access to your wallet and funds. <br />
|
||||
Make sure to note it down.
|
||||
</Typography>
|
||||
<Typography component="div" style={styles.mnemonicDialogWarning}>
|
||||
Do not share your mnemonic with anyone
|
||||
</Typography>
|
||||
<GridView words={words} />
|
||||
</DialogContent>
|
||||
<DialogActions style={{ ...styles.mnemonicButtonRow }}>
|
||||
<Button style={{ ...styles.mnemonicButton }} onClick={copyMnemonic}>
|
||||
Copy
|
||||
</Button>
|
||||
<Button style={{ ...styles.mnemonicButton }} onClick={hideDialog}>
|
||||
Done
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export { MnemonicDialog };
|
||||
@@ -1,12 +1,10 @@
|
||||
import React, { useState } from "react";
|
||||
import { View } from "react-native";
|
||||
import { List } from "react-native-paper";
|
||||
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
||||
import React, { useState } from 'react';
|
||||
import { View } from 'react-native';
|
||||
import { List } from 'react-native-paper';
|
||||
|
||||
import { NetworkDropdownProps, NetworksDataState } from "../types";
|
||||
import styles from "../styles/stylesheet";
|
||||
import { useNetworks } from "../context/NetworksContext";
|
||||
import { Accordion, AccordionSummary } from "@mui/material";
|
||||
import { NetworkDropdownProps, NetworksDataState } from '../types';
|
||||
import styles from '../styles/stylesheet';
|
||||
import { useNetworks } from '../context/NetworksContext';
|
||||
|
||||
const NetworkDropdown = ({ updateNetwork }: NetworkDropdownProps) => {
|
||||
const { networksData, selectedNetwork, setSelectedNetwork } = useNetworks();
|
||||
@@ -21,19 +19,18 @@ const NetworkDropdown = ({ updateNetwork }: NetworkDropdownProps) => {
|
||||
|
||||
return (
|
||||
<View style={styles.networkDropdown}>
|
||||
<Accordion expanded={expanded} onClick={() => setExpanded(!expanded)}>
|
||||
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
|
||||
{selectedNetwork!.networkName}
|
||||
</AccordionSummary>
|
||||
|
||||
{networksData.map((networkData) => (
|
||||
<List.Accordion
|
||||
title={selectedNetwork!.networkName}
|
||||
expanded={expanded}
|
||||
onPress={() => setExpanded(!expanded)}>
|
||||
{networksData.map(networkData => (
|
||||
<List.Item
|
||||
key={networkData.networkId}
|
||||
title={networkData.networkName}
|
||||
onPress={() => handleNetworkPress(networkData)}
|
||||
/>
|
||||
))}
|
||||
</Accordion>
|
||||
</List.Accordion>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -206,7 +206,7 @@ const PairingModal = ({
|
||||
<>
|
||||
{icon.endsWith('.svg') ? (
|
||||
<View style={styles.dappLogo}>
|
||||
<Text>SvgURI requstIcon</Text>
|
||||
<Text>SvgURI requestIcon</Text>
|
||||
</View>
|
||||
) : (
|
||||
<Image style={styles.dappLogo} source={{ uri: icon }} />
|
||||
@@ -229,25 +229,27 @@ const PairingModal = ({
|
||||
<Modal visible={visible} animationType="slide" transparent>
|
||||
<View style={styles.modalOuterContainer}>
|
||||
<View style={styles.modalContentContainer}>
|
||||
<ScrollView showsVerticalScrollIndicator={true}>
|
||||
<View style={styles.container}>
|
||||
{icon && (
|
||||
<>
|
||||
{icon.endsWith('.svg') ? (
|
||||
<View style={styles.dappLogo}>
|
||||
<Text>SvgURI requestIcon</Text>
|
||||
</View>
|
||||
) : (
|
||||
<Image style={styles.dappLogo} source={{ uri: icon }} />
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
<Text variant="titleMedium">{dappName}</Text>
|
||||
<Text variant="bodyMedium">{url}</Text>
|
||||
<View style={styles.marginVertical8} />
|
||||
<Text variant="titleMedium">Connect to this site?</Text>
|
||||
</View>
|
||||
|
||||
<ScrollView showsVerticalScrollIndicator={true} style={styles.scrollViewContainer}>
|
||||
<View style={styles.container}>
|
||||
{icon && (
|
||||
<>
|
||||
{icon.endsWith('.svg') ? (
|
||||
<View style={styles.dappLogo}>
|
||||
<Text>SvgURI requstIcon</Text>
|
||||
</View>
|
||||
) : (
|
||||
<Image style={styles.dappLogo} source={{ uri: icon }} />
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
<Text variant="titleMedium">{dappName}</Text>
|
||||
<Text variant="bodyMedium">{url}</Text>
|
||||
<View style={styles.marginVertical8} />
|
||||
<Text variant="titleMedium">Connect to this site?</Text>
|
||||
|
||||
{walletConnectData.walletConnectMethods.length > 0 && (
|
||||
<View>
|
||||
<Text variant="titleMedium">Chains:</Text>
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import React, { useState } from "react";
|
||||
import { TouchableOpacity, View } from "react-native";
|
||||
import { Button, Link, Typography } from "@mui/material";
|
||||
import Dialog from "@mui/material/Dialog";
|
||||
import DialogTitle from "@mui/material/DialogTitle";
|
||||
import DialogContent from "@mui/material/DialogContent";
|
||||
import DialogActions from "@mui/material/DialogActions";
|
||||
import React, { useState } from 'react';
|
||||
import { TouchableOpacity, View } from 'react-native';
|
||||
import { Button, Typography } from '@mui/material';
|
||||
import Dialog from '@mui/material/Dialog';
|
||||
import DialogTitle from '@mui/material/DialogTitle';
|
||||
import DialogContent from '@mui/material/DialogContent';
|
||||
import DialogActions from '@mui/material/DialogActions';
|
||||
|
||||
import styles from "../styles/stylesheet";
|
||||
import { getPathKey } from "../utils/misc";
|
||||
import { useNetworks } from "../context/NetworksContext";
|
||||
import { useAccounts } from "../context/AccountsContext";
|
||||
import styles from '../styles/stylesheet';
|
||||
import { getPathKey } from '../utils/misc';
|
||||
import { useNetworks } from '../context/NetworksContext';
|
||||
import { useAccounts } from '../context/AccountsContext';
|
||||
import { Text, useTheme } from 'react-native-paper';
|
||||
|
||||
const ShowPKDialog = () => {
|
||||
const { currentIndex } = useAccounts();
|
||||
@@ -18,6 +19,8 @@ const ShowPKDialog = () => {
|
||||
const [privateKey, setPrivateKey] = useState<string>();
|
||||
const [showPKDialog, setShowPKDialog] = useState<boolean>(false);
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
const handleShowPrivateKey = async () => {
|
||||
const pathKey = await getPathKey(
|
||||
`${selectedNetwork!.namespace}:${selectedNetwork!.chainId}`,
|
||||
@@ -38,9 +41,12 @@ const ShowPKDialog = () => {
|
||||
<TouchableOpacity
|
||||
onPress={() => {
|
||||
setShowPKDialog(true);
|
||||
}}
|
||||
>
|
||||
<Link>Show Private Key</Link>
|
||||
}}>
|
||||
<Text
|
||||
variant="titleSmall"
|
||||
style={[styles.hyperlink, { color: theme.colors.primary }]}>
|
||||
Show Private Key
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<View>
|
||||
@@ -60,8 +66,8 @@ const ShowPKDialog = () => {
|
||||
variant="body1"
|
||||
style={styles.dataBoxData}
|
||||
sx={{
|
||||
wordWrap: "break-word",
|
||||
whiteSpace: "initial"
|
||||
wordWrap: 'break-word',
|
||||
whiteSpace: 'initial',
|
||||
}}
|
||||
>
|
||||
{privateKey}
|
||||
@@ -70,19 +76,24 @@ const ShowPKDialog = () => {
|
||||
)}
|
||||
<View>
|
||||
<Typography variant="body1" style={styles.dialogWarning}>
|
||||
Warning: Never disclose this key. Anyone with your private keys can steal
|
||||
any assets held in your account.
|
||||
<Typography component="span">
|
||||
Warning:
|
||||
</Typography>
|
||||
Never disclose this key. Anyone with your private keys can
|
||||
steal any assets held in your account.
|
||||
</Typography>
|
||||
</View>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
{!privateKey ? (
|
||||
<>
|
||||
<Button style={{...styles.buttonRed, ...styles.button}} onClick={handleShowPrivateKey}>Yes</Button>
|
||||
<Button style={{...styles.buttonBlue, ...styles.button}} onClick={hideShowPKDialog}>No</Button>
|
||||
<Button onClick={handleShowPrivateKey} color="error">
|
||||
Yes
|
||||
</Button>
|
||||
<Button onClick={hideShowPKDialog}>No</Button>
|
||||
</>
|
||||
) : (
|
||||
<Button style={{...styles.buttonBlue, ...styles.button}} onClick={hideShowPKDialog}>Ok</Button>
|
||||
<Button onClick={hideShowPKDialog}>Ok</Button>
|
||||
)}
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
|
||||
@@ -7,12 +7,12 @@ const AccountsContext = createContext<{
|
||||
setAccounts: (account: Account[]) => void;
|
||||
currentIndex: number;
|
||||
setCurrentIndex: (index: number) => void;
|
||||
}>({
|
||||
accounts: [],
|
||||
setAccounts: () => {},
|
||||
currentIndex: 0,
|
||||
setCurrentIndex: () => {},
|
||||
});
|
||||
}>({
|
||||
accounts: [],
|
||||
setAccounts: () => {},
|
||||
currentIndex: 0,
|
||||
setCurrentIndex: () => {},
|
||||
});
|
||||
|
||||
const useAccounts = () => {
|
||||
const accountsContext = useContext(AccountsContext);
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import React, { createContext, useContext, useEffect, useState } from 'react';
|
||||
|
||||
import { NetworksDataState } from '../types';
|
||||
import { retrieveNetworksData } from '../utils/accounts';
|
||||
import { retrieveNetworksData, storeNetworkData } from '../utils/accounts';
|
||||
import { DEFAULT_NETWORKS, EIP155 } from '../utils/constants';
|
||||
import { setInternetCredentials } from '../utils/key-store';
|
||||
|
||||
const NetworksContext = createContext<{
|
||||
networksData: NetworksDataState[];
|
||||
@@ -14,52 +13,42 @@ const NetworksContext = createContext<{
|
||||
setSelectedNetwork: React.Dispatch<
|
||||
React.SetStateAction<NetworksDataState | undefined>
|
||||
>;
|
||||
}>({
|
||||
networksData: [],
|
||||
setNetworksData: () => {},
|
||||
networkType: '',
|
||||
setNetworkType: () => {},
|
||||
selectedNetwork: {} as NetworksDataState,
|
||||
setSelectedNetwork: () => {},
|
||||
});
|
||||
}>({
|
||||
networksData: [],
|
||||
setNetworksData: () => {},
|
||||
networkType: '',
|
||||
setNetworkType: () => {},
|
||||
selectedNetwork: {} as NetworksDataState,
|
||||
setSelectedNetwork: () => {},
|
||||
});
|
||||
|
||||
const useNetworks = () => {
|
||||
const networksContext = useContext(NetworksContext);
|
||||
return networksContext;
|
||||
};
|
||||
|
||||
const DEFAULT_NETWORKS_DATA = DEFAULT_NETWORKS.map((defaultNetwork, index) => (
|
||||
{
|
||||
...defaultNetwork,
|
||||
networkId: index.toString()
|
||||
})
|
||||
);
|
||||
|
||||
const NetworksProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
const [networksData, setNetworksData] = useState<NetworksDataState[]>(DEFAULT_NETWORKS_DATA);
|
||||
const [networksData, setNetworksData] = useState<NetworksDataState[]>([]);
|
||||
const [networkType, setNetworkType] = useState<string>(EIP155);
|
||||
const [selectedNetwork, setSelectedNetwork] = useState<NetworksDataState>();
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
let retrievedNetworks = await retrieveNetworksData();
|
||||
|
||||
const retrievedNetworks = await retrieveNetworksData();
|
||||
if (retrievedNetworks.length === 0) {
|
||||
setInternetCredentials(
|
||||
'networks',
|
||||
'_',
|
||||
JSON.stringify(DEFAULT_NETWORKS_DATA),
|
||||
);
|
||||
|
||||
retrievedNetworks = DEFAULT_NETWORKS_DATA;
|
||||
for (const defaultNetwork of DEFAULT_NETWORKS) {
|
||||
await storeNetworkData(defaultNetwork);
|
||||
}
|
||||
}
|
||||
|
||||
setNetworksData(retrievedNetworks);
|
||||
setSelectedNetwork(retrievedNetworks[0]);
|
||||
const retrievedNewNetworks = await retrieveNetworksData();
|
||||
setNetworksData(retrievedNewNetworks);
|
||||
setSelectedNetwork(retrievedNewNetworks[0]);
|
||||
};
|
||||
|
||||
fetchData();
|
||||
}, []);
|
||||
if (networksData.length === 0) {
|
||||
fetchData();
|
||||
}
|
||||
}, [networksData]);
|
||||
|
||||
useEffect(() => {
|
||||
setSelectedNetwork(prevSelectedNetwork => {
|
||||
|
||||
Vendored
-36
@@ -1,36 +0,0 @@
|
||||
// Extends the Window interface for Android WebView communication
|
||||
declare global {
|
||||
interface Window {
|
||||
// Android bridge callbacks for signature and accounts related events
|
||||
Android?: {
|
||||
// Called when signature is successfully generated
|
||||
onSignatureComplete?: (signature: string) => void;
|
||||
|
||||
// Called when signature generation fails
|
||||
onSignatureError?: (error: string) => void;
|
||||
|
||||
// Called when signature process is cancelled
|
||||
onSignatureCancelled?: () => void;
|
||||
|
||||
// Called when accounts are ready for use
|
||||
onAccountsReady?: () => void;
|
||||
|
||||
// Called when transfer is successfully completed
|
||||
onTransferComplete?: (result: string) => void;
|
||||
|
||||
// Called when transfer fails
|
||||
onTransferError?: (error: string) => void;
|
||||
|
||||
// Called when transfer is cancelled
|
||||
onTransferCancelled?: () => void;
|
||||
};
|
||||
|
||||
// Handles incoming signature requests from Android
|
||||
receiveSignRequestFromAndroid?: (message: string) => void;
|
||||
|
||||
// Handles incoming transfer requests from Android
|
||||
receiveTransferRequestFromAndroid?: (to: string, amount: string) => void;
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
||||
@@ -1,23 +0,0 @@
|
||||
import { useCallback } from "react";
|
||||
|
||||
import { retrieveAccounts } from "../utils/accounts";
|
||||
import { useNetworks } from "../context/NetworksContext";
|
||||
|
||||
const useAccountsData = () => {
|
||||
const { networksData } = useNetworks();
|
||||
|
||||
const getAccountsData = useCallback(async (chainId: string) => {
|
||||
const targetNetwork = networksData.find(network => network.chainId === chainId);
|
||||
|
||||
if (!targetNetwork) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const accounts = await retrieveAccounts(targetNetwork);
|
||||
return accounts || [];
|
||||
}, [networksData]);
|
||||
|
||||
return { getAccountsData };
|
||||
};
|
||||
|
||||
export default useAccountsData;
|
||||
@@ -1,71 +0,0 @@
|
||||
import { useEffect, useCallback } from "react";
|
||||
|
||||
import { createWallet } from "../utils/accounts";
|
||||
import { sendMessage } from "../utils/misc";
|
||||
import useAccountsData from "./useAccountsData";
|
||||
import { useNetworks } from "../context/NetworksContext";
|
||||
|
||||
const useGetOrCreateAccounts = () => {
|
||||
const { networksData } = useNetworks();
|
||||
const { getAccountsData } = useAccountsData();
|
||||
|
||||
// Wrap the function in useCallback to prevent recreation on each render
|
||||
const getOrCreateAccountsForChain = useCallback(async (chainId: string) => {
|
||||
let accountsData = await getAccountsData(chainId);
|
||||
|
||||
if (accountsData.length === 0) {
|
||||
console.log("Accounts not found, creating wallet...");
|
||||
await createWallet(networksData);
|
||||
accountsData = await getAccountsData(chainId);
|
||||
}
|
||||
|
||||
return accountsData;
|
||||
}, [networksData, getAccountsData]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleCreateAccounts = async (event: MessageEvent) => {
|
||||
if (event.data.type !== 'REQUEST_CREATE_OR_GET_ACCOUNTS') return;
|
||||
|
||||
const accountsData = await getOrCreateAccountsForChain(event.data.chainId);
|
||||
|
||||
sendMessage(
|
||||
event.source as Window, 'WALLET_ACCOUNTS_DATA',
|
||||
accountsData.map(account => account.address),
|
||||
event.origin
|
||||
);
|
||||
};
|
||||
|
||||
const autoCreateAccounts = async () => {
|
||||
const defaultChainId = networksData[0]?.chainId;
|
||||
|
||||
if (!defaultChainId) {
|
||||
console.log('useGetOrCreateAccounts: No default chainId found');
|
||||
return;
|
||||
}
|
||||
|
||||
await getOrCreateAccountsForChain(defaultChainId);
|
||||
|
||||
// Notify Android that accounts are ready
|
||||
if (window.Android?.onAccountsReady) {
|
||||
window.Android.onAccountsReady();
|
||||
} else {
|
||||
console.log('useGetOrCreateAccounts: Android bridge not available');
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('message', handleCreateAccounts);
|
||||
|
||||
const isAndroidWebView = !!(window.Android);
|
||||
|
||||
// TODO: Call method to auto create accounts from android app
|
||||
if (isAndroidWebView) {
|
||||
autoCreateAccounts();
|
||||
}
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('message', handleCreateAccounts);
|
||||
};
|
||||
}, [networksData, getAccountsData, getOrCreateAccountsForChain]);
|
||||
};
|
||||
|
||||
export default useGetOrCreateAccounts;
|
||||
@@ -1,176 +0,0 @@
|
||||
import { useEffect, useCallback } from 'react';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
|
||||
import { useAccounts } from '../context/AccountsContext';
|
||||
import { useNetworks } from '../context/NetworksContext';
|
||||
import { StackParamsList } from '../types';
|
||||
import useGetOrCreateAccounts from './useGetOrCreateAccounts';
|
||||
import { retrieveAccountsForNetwork } from '../utils/accounts';
|
||||
|
||||
export const useWebViewHandler = () => {
|
||||
// Navigation and context hooks
|
||||
const navigation = useNavigation<NativeStackNavigationProp<StackParamsList>>();
|
||||
const { selectedNetwork } = useNetworks();
|
||||
const { accounts, currentIndex } = useAccounts();
|
||||
|
||||
// Initialize accounts
|
||||
useGetOrCreateAccounts();
|
||||
|
||||
// Core navigation handler
|
||||
const navigateToSignRequest = useCallback((message: string) => {
|
||||
try {
|
||||
// Validation checks
|
||||
if (!selectedNetwork?.namespace || !selectedNetwork?.chainId) {
|
||||
window.Android?.onSignatureError?.('Invalid network configuration');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!accounts?.length) {
|
||||
window.Android?.onSignatureError?.('No accounts available');
|
||||
return;
|
||||
}
|
||||
|
||||
const currentAccount = accounts[currentIndex];
|
||||
if (!currentAccount) {
|
||||
window.Android?.onSignatureError?.('Current account not found');
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the path and validate with regex
|
||||
const path = `/sign/${selectedNetwork.namespace}/${selectedNetwork.chainId}/${currentAccount.address}/${encodeURIComponent(message)}`;
|
||||
const pathRegex = /^\/sign\/(eip155|cosmos)\/(.+)\/(.+)\/(.+)$/;
|
||||
const match = path.match(pathRegex);
|
||||
|
||||
if (!match) {
|
||||
window.Android?.onSignatureError?.('Invalid signing path');
|
||||
return;
|
||||
}
|
||||
|
||||
const [, pathNamespace, pathChainId, pathAddress, pathMessage] = match;
|
||||
|
||||
// Reset navigation stack and navigate to sign request
|
||||
navigation.reset({
|
||||
index: 0,
|
||||
routes: [
|
||||
{
|
||||
name: 'SignRequest',
|
||||
path,
|
||||
params: {
|
||||
namespace: pathNamespace,
|
||||
chainId: pathChainId,
|
||||
address: pathAddress,
|
||||
message: decodeURIComponent(pathMessage),
|
||||
accountInfo: currentAccount,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
} catch (error) {
|
||||
window.Android?.onSignatureError?.(`Navigation error: ${error}`);
|
||||
}
|
||||
}, [selectedNetwork, accounts, currentIndex, navigation]);
|
||||
|
||||
// Handle incoming transfer requests
|
||||
const navigateToTransfer = useCallback(async (to: string, amount: string) => {
|
||||
if (!accounts || accounts.length === 0) {
|
||||
console.error('No accounts available');
|
||||
if (window.Android?.onTransferError) {
|
||||
window.Android.onTransferError('No accounts available');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const currentAccount = accounts[currentIndex];
|
||||
if (!currentAccount) {
|
||||
console.error('Current account not found');
|
||||
if (window.Android?.onTransferError) {
|
||||
window.Android.onTransferError('Current account not found');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Use Cosmos Hub Testnet network
|
||||
const cosmosHubTestnet = {
|
||||
namespace: 'cosmos',
|
||||
chainId: 'provider',
|
||||
addressPrefix: 'cosmos'
|
||||
};
|
||||
|
||||
try {
|
||||
// Get all accounts for Cosmos Hub Testnet
|
||||
const cosmosAccounts = await retrieveAccountsForNetwork(
|
||||
`${cosmosHubTestnet.namespace}:${cosmosHubTestnet.chainId}`,
|
||||
'0' // Use the first account
|
||||
);
|
||||
|
||||
if (!cosmosAccounts || cosmosAccounts.length === 0) {
|
||||
console.error('No Cosmos Hub Testnet accounts found');
|
||||
if (window.Android?.onTransferError) {
|
||||
window.Android.onTransferError('No Cosmos Hub Testnet accounts found');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const cosmosAccount = cosmosAccounts[0]; // Use the first account
|
||||
|
||||
const path = `/transfer/${cosmosHubTestnet.namespace}/${cosmosHubTestnet.chainId}/${cosmosAccount.address}/${to}/${amount}`;
|
||||
|
||||
const pathRegex = /^\/transfer\/(eip155|cosmos)\/(.+)\/(.+)\/(.+)\/(.+)$/;
|
||||
if (!pathRegex.test(path)) {
|
||||
console.error('Path does not match expected pattern:', path);
|
||||
if (window.Android?.onTransferError) {
|
||||
window.Android.onTransferError('Invalid path format');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const match = path.match(pathRegex);
|
||||
if (!match) {
|
||||
console.error('Failed to parse path:', path);
|
||||
if (window.Android?.onTransferError) {
|
||||
window.Android.onTransferError('Failed to parse path');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
navigation.reset({
|
||||
index: 0,
|
||||
routes: [
|
||||
{
|
||||
name: 'ApproveTransfer',
|
||||
path: `/transfer/${cosmosHubTestnet.namespace}/${cosmosHubTestnet.chainId}/${cosmosAccount.address}/${to}/${amount}`,
|
||||
params: {
|
||||
namespace: cosmosHubTestnet.namespace,
|
||||
chainId: `${cosmosHubTestnet.namespace}:${cosmosHubTestnet.chainId}`,
|
||||
transaction: {
|
||||
from: cosmosAccount.address,
|
||||
to: to,
|
||||
value: amount,
|
||||
data: ''
|
||||
},
|
||||
accountInfo: cosmosAccount,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Navigation error:', error);
|
||||
if (window.Android?.onTransferError) {
|
||||
window.Android.onTransferError(`Navigation error: ${error}`);
|
||||
}
|
||||
}
|
||||
}, [accounts, currentIndex, navigation]);
|
||||
|
||||
useEffect(() => {
|
||||
// Assign the function to the window object
|
||||
window.receiveSignRequestFromAndroid = navigateToSignRequest;
|
||||
|
||||
window.receiveTransferRequestFromAndroid = navigateToTransfer;
|
||||
|
||||
return () => {
|
||||
window.receiveSignRequestFromAndroid = undefined;
|
||||
window.receiveTransferRequestFromAndroid = undefined;
|
||||
};
|
||||
}, [navigateToSignRequest, navigateToTransfer]); // Only the function reference as dependency
|
||||
};
|
||||
+38
-120
@@ -1,141 +1,59 @@
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import {
|
||||
PaperProvider,
|
||||
MD3DarkTheme as DefaultTheme,
|
||||
} from "react-native-paper";
|
||||
import { NavigationContainer, DarkTheme } from "@react-navigation/native";
|
||||
import { Platform } from "react-native";
|
||||
import { Buffer } from "buffer";
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import { PaperProvider, MD3LightTheme as DefaultTheme, } from 'react-native-paper';
|
||||
import { NavigationContainer } from '@react-navigation/native';
|
||||
import { Platform } from 'react-native';
|
||||
import { Buffer } from 'buffer';
|
||||
|
||||
import "./index.css";
|
||||
import App from "./App";
|
||||
import { AccountsProvider } from "./context/AccountsContext";
|
||||
import { NetworksProvider } from "./context/NetworksContext";
|
||||
import reportWebVitals from "./reportWebVitals";
|
||||
import { WalletConnectProvider } from "./context/WalletConnectContext";
|
||||
import { createTheme, ThemeProvider } from "@mui/material";
|
||||
import './index.css';
|
||||
import App from './App';
|
||||
import { AccountsProvider } from './context/AccountsContext';
|
||||
import { NetworksProvider } from './context/NetworksContext';
|
||||
import reportWebVitals from './reportWebVitals';
|
||||
import { WalletConnectProvider } from './context/WalletConnectContext';
|
||||
|
||||
globalThis.Buffer = Buffer;
|
||||
|
||||
const linking = {
|
||||
prefixes: ["https://wallet.laconic.com"],
|
||||
prefixes: ['https://wallet.laconic.com']
|
||||
};
|
||||
|
||||
const theme = {
|
||||
...DefaultTheme,
|
||||
dark: true,
|
||||
dark: false,
|
||||
};
|
||||
|
||||
const navigationTheme: typeof DarkTheme = {
|
||||
...DarkTheme,
|
||||
colors: {
|
||||
...DarkTheme.colors,
|
||||
primary: "#0000F4",
|
||||
background: "#0F0F0F",
|
||||
card: "#18181A",
|
||||
},
|
||||
};
|
||||
|
||||
const muiTheme = createTheme({
|
||||
components: {
|
||||
MuiAccordion: {
|
||||
defaultProps: {
|
||||
sx: {
|
||||
border: "1px solid #48474F",
|
||||
borderBottomRightRadius: 3,
|
||||
borderBottomLeftRadius: 3,
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiButton: {
|
||||
defaultProps: {
|
||||
color: "primary",
|
||||
sx: {
|
||||
fontFamily: `DM Mono, monospace`,
|
||||
fontWeight: 400,
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiLink: {
|
||||
defaultProps: {
|
||||
color: "text.primary",
|
||||
fontSize: "14px",
|
||||
},
|
||||
},
|
||||
MuiTypography: {
|
||||
defaultProps: {
|
||||
color: "text.primary",
|
||||
fontWeight: 400,
|
||||
},
|
||||
},
|
||||
MuiPaper: {
|
||||
defaultProps: {
|
||||
sx: {
|
||||
backgroundImage: "none",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
palette: {
|
||||
mode: "dark",
|
||||
primary: {
|
||||
main: "#0000F4",
|
||||
},
|
||||
secondary: {
|
||||
main: "#A2A2FF",
|
||||
},
|
||||
error: {
|
||||
main: "#B20710",
|
||||
},
|
||||
background: {
|
||||
default: "#0F0F0F",
|
||||
paper: "#18181A",
|
||||
},
|
||||
text: {
|
||||
primary: "#FBFBFB",
|
||||
},
|
||||
info: {
|
||||
main: "#FBFBFB",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const root = ReactDOM.createRoot(
|
||||
document.getElementById("root") as HTMLElement,
|
||||
document.getElementById('root') as HTMLElement
|
||||
);
|
||||
root.render(
|
||||
<div id="app">
|
||||
<PaperProvider theme={theme}>
|
||||
<NetworksProvider>
|
||||
<AccountsProvider>
|
||||
<WalletConnectProvider>
|
||||
<NavigationContainer
|
||||
linking={linking}
|
||||
documentTitle={{
|
||||
formatter: () => `Laconic | Wallet`,
|
||||
}}
|
||||
theme={navigationTheme}
|
||||
>
|
||||
<React.Fragment>
|
||||
{Platform.OS === "web" ? (
|
||||
<style type="text/css">{`
|
||||
<PaperProvider theme={theme}>
|
||||
<NetworksProvider>
|
||||
<AccountsProvider>
|
||||
<WalletConnectProvider>
|
||||
<NavigationContainer
|
||||
linking={linking}
|
||||
documentTitle={{
|
||||
formatter: () =>
|
||||
`Laconic Wallet`,
|
||||
}}
|
||||
>
|
||||
<React.Fragment>
|
||||
{Platform.OS === 'web' ? (
|
||||
<style type="text/css">{`
|
||||
@font-face {
|
||||
font-family: 'MaterialCommunityIcons';
|
||||
src: url(${require("react-native-vector-icons/Fonts/MaterialCommunityIcons.ttf")}) format('truetype');
|
||||
src: url(${require('react-native-vector-icons/Fonts/MaterialCommunityIcons.ttf')}) format('truetype');
|
||||
}
|
||||
`}</style>
|
||||
) : null}
|
||||
<ThemeProvider theme={muiTheme}>
|
||||
<App />
|
||||
</ThemeProvider>
|
||||
</React.Fragment>
|
||||
</NavigationContainer>
|
||||
</WalletConnectProvider>
|
||||
</AccountsProvider>
|
||||
</NetworksProvider>
|
||||
</PaperProvider>
|
||||
</div>,
|
||||
) : null}
|
||||
<App />
|
||||
</React.Fragment>
|
||||
</NavigationContainer>
|
||||
</WalletConnectProvider>
|
||||
</AccountsProvider>
|
||||
</NetworksProvider>
|
||||
</PaperProvider>
|
||||
);
|
||||
|
||||
// If you want to start measuring performance in your app, pass a function
|
||||
|
||||
+200
-230
@@ -1,20 +1,21 @@
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import { useForm, Controller, useWatch, FieldErrors } from "react-hook-form";
|
||||
import { TextInput, HelperText } from "react-native-paper";
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { View } from 'react-native';
|
||||
import { useForm, Controller, useWatch, FieldErrors } from 'react-hook-form';
|
||||
import { TextInput, Button, HelperText } from 'react-native-paper';
|
||||
|
||||
import { HDNode } from "ethers/lib/utils";
|
||||
import { chains } from "chain-registry";
|
||||
import { useDebouncedCallback } from "use-debounce";
|
||||
import { z } from "zod";
|
||||
import { HDNode } from 'ethers/lib/utils';
|
||||
import { chains } from 'chain-registry';
|
||||
import { useDebouncedCallback } from 'use-debounce';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { NativeStackNavigationProp } from "@react-navigation/native-stack";
|
||||
import { useNavigation } from "@react-navigation/native";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
|
||||
import { StackParamsList } from "../types";
|
||||
import { SelectNetworkType } from "../components/SelectNetworkType";
|
||||
import { storeNetworkData } from "../utils/accounts";
|
||||
import { useNetworks } from "../context/NetworksContext";
|
||||
import { StackParamsList } from '../types';
|
||||
import { SelectNetworkType } from '../components/SelectNetworkType';
|
||||
import { storeNetworkData } from '../utils/accounts';
|
||||
import { useNetworks } from '../context/NetworksContext';
|
||||
import {
|
||||
COSMOS,
|
||||
EIP155,
|
||||
@@ -22,16 +23,14 @@ import {
|
||||
EMPTY_FIELD_ERROR,
|
||||
INVALID_URL_ERROR,
|
||||
IS_NUMBER_REGEX,
|
||||
} from "../utils/constants";
|
||||
import { getCosmosAccounts } from "../utils/accounts";
|
||||
import ETH_CHAINS from "../assets/ethereum-chains.json";
|
||||
} from '../utils/constants';
|
||||
import { getCosmosAccounts } from '../utils/accounts';
|
||||
import ETH_CHAINS from '../assets/ethereum-chains.json';
|
||||
import {
|
||||
getInternetCredentials,
|
||||
setInternetCredentials,
|
||||
} from "../utils/key-store";
|
||||
import { Divider, Grid } from "@mui/material";
|
||||
import { LoadingButton } from "@mui/lab";
|
||||
import { Layout } from "../components/Layout";
|
||||
} from '../utils/key-store';
|
||||
import styles from '../styles/stylesheet';
|
||||
|
||||
const ethNetworkDataSchema = z.object({
|
||||
chainId: z.string().nonempty({ message: EMPTY_FIELD_ERROR }),
|
||||
@@ -40,7 +39,7 @@ const ethNetworkDataSchema = z.object({
|
||||
blockExplorerUrl: z
|
||||
.string()
|
||||
.url({ message: INVALID_URL_ERROR })
|
||||
.or(z.literal("")),
|
||||
.or(z.literal('')),
|
||||
coinType: z
|
||||
.string()
|
||||
.nonempty({ message: EMPTY_FIELD_ERROR })
|
||||
@@ -55,7 +54,7 @@ const cosmosNetworkDataSchema = z.object({
|
||||
blockExplorerUrl: z
|
||||
.string()
|
||||
.url({ message: INVALID_URL_ERROR })
|
||||
.or(z.literal("")),
|
||||
.or(z.literal('')),
|
||||
coinType: z
|
||||
.string()
|
||||
.nonempty({ message: EMPTY_FIELD_ERROR })
|
||||
@@ -86,13 +85,13 @@ const AddNetwork = () => {
|
||||
setValue,
|
||||
reset,
|
||||
} = useForm<z.infer<typeof networksFormDataSchema>>({
|
||||
mode: "onChange",
|
||||
mode: 'onChange',
|
||||
resolver: zodResolver(networksFormDataSchema),
|
||||
});
|
||||
|
||||
const watchChainId = useWatch({
|
||||
control,
|
||||
name: "chainId",
|
||||
name: 'chainId',
|
||||
});
|
||||
|
||||
const updateNetworkType = (newNetworkType: string) => {
|
||||
@@ -102,16 +101,16 @@ const AddNetwork = () => {
|
||||
const fetchChainDetails = useDebouncedCallback((chainId: string) => {
|
||||
if (namespace === EIP155) {
|
||||
const ethChainDetails = ETH_CHAINS.find(
|
||||
(chain) => chain.chainId === Number(chainId),
|
||||
chain => chain.chainId === Number(chainId),
|
||||
);
|
||||
if (!ethChainDetails) {
|
||||
return;
|
||||
}
|
||||
setValue("networkName", ethChainDetails.name);
|
||||
setValue("rpcUrl", ethChainDetails.rpc[0]);
|
||||
setValue("blockExplorerUrl", ethChainDetails.explorers?.[0].url || "");
|
||||
setValue("coinType", String(ethChainDetails.slip44 ?? "60"));
|
||||
setValue("currencySymbol", ethChainDetails.nativeCurrency.symbol);
|
||||
setValue('networkName', ethChainDetails.name);
|
||||
setValue('rpcUrl', ethChainDetails.rpc[0]);
|
||||
setValue('blockExplorerUrl', ethChainDetails.explorers?.[0].url || '');
|
||||
setValue('coinType', String(ethChainDetails.slip44 ?? '60'));
|
||||
setValue('currencySymbol', ethChainDetails.nativeCurrency.symbol);
|
||||
return;
|
||||
}
|
||||
const cosmosChainDetails = chains.find(
|
||||
@@ -120,14 +119,14 @@ const AddNetwork = () => {
|
||||
if (!cosmosChainDetails) {
|
||||
return;
|
||||
}
|
||||
setValue("networkName", cosmosChainDetails.pretty_name);
|
||||
setValue("rpcUrl", cosmosChainDetails.apis?.rpc?.[0]?.address || "");
|
||||
setValue("blockExplorerUrl", cosmosChainDetails.explorers?.[0].url || "");
|
||||
setValue("addressPrefix", cosmosChainDetails.bech32_prefix);
|
||||
setValue("coinType", String(cosmosChainDetails.slip44 ?? "118"));
|
||||
setValue("nativeDenom", cosmosChainDetails.fees?.fee_tokens[0].denom || "");
|
||||
setValue('networkName', cosmosChainDetails.pretty_name);
|
||||
setValue('rpcUrl', cosmosChainDetails.apis?.rpc?.[0]?.address || '');
|
||||
setValue('blockExplorerUrl', cosmosChainDetails.explorers?.[0].url || '');
|
||||
setValue('addressPrefix', cosmosChainDetails.bech32_prefix);
|
||||
setValue('coinType', String(cosmosChainDetails.slip44 ?? '118'));
|
||||
setValue('nativeDenom', cosmosChainDetails.fees?.fee_tokens[0].denom || '');
|
||||
setValue(
|
||||
"gasPrice",
|
||||
'gasPrice',
|
||||
String(
|
||||
cosmosChainDetails.fees?.fee_tokens[0].average_gas_price ||
|
||||
String(process.env.DEFAULT_GAS_PRICE),
|
||||
@@ -143,11 +142,11 @@ const AddNetwork = () => {
|
||||
isDefault: false,
|
||||
};
|
||||
|
||||
const mnemonicServer = await getInternetCredentials("mnemonicServer");
|
||||
const mnemonicServer = await getInternetCredentials('mnemonicServer');
|
||||
const mnemonic = mnemonicServer;
|
||||
|
||||
if (!mnemonic) {
|
||||
throw new Error("Mnemonic not found");
|
||||
throw new Error('Mnemonic not found');
|
||||
}
|
||||
|
||||
const hdNode = HDNode.fromMnemonic(mnemonic);
|
||||
@@ -173,7 +172,7 @@ const AddNetwork = () => {
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error("Unsupported namespace");
|
||||
throw new Error('Unsupported namespace');
|
||||
}
|
||||
|
||||
const accountInfo = `${hdPath},${node.privateKey},${node.publicKey},${address}`;
|
||||
@@ -184,22 +183,22 @@ const AddNetwork = () => {
|
||||
await Promise.all([
|
||||
setInternetCredentials(
|
||||
`accounts/${newNetworkData.namespace}:${newNetworkData.chainId}/0`,
|
||||
"_",
|
||||
'_',
|
||||
accountInfo,
|
||||
),
|
||||
setInternetCredentials(
|
||||
`addAccountCounter/${newNetworkData.namespace}:${newNetworkData.chainId}`,
|
||||
"_",
|
||||
"1",
|
||||
'_',
|
||||
'1',
|
||||
),
|
||||
setInternetCredentials(
|
||||
`accountIndices/${newNetworkData.namespace}:${newNetworkData.chainId}`,
|
||||
"_",
|
||||
"0",
|
||||
'_',
|
||||
'0',
|
||||
),
|
||||
]);
|
||||
|
||||
navigation.navigate("Home");
|
||||
navigation.navigate('Home');
|
||||
},
|
||||
[navigation, namespace, setNetworksData],
|
||||
);
|
||||
@@ -213,237 +212,208 @@ const AddNetwork = () => {
|
||||
}, [namespace, reset]);
|
||||
|
||||
return (
|
||||
<Layout title="Add Network">
|
||||
<View style={styles.appContainer}>
|
||||
<SelectNetworkType updateNetworkType={updateNetworkType} />
|
||||
<Divider flexItem sx={{ my: 4 }} />
|
||||
|
||||
<Grid container spacing={2} sx={{ px: 1 }}>
|
||||
<Grid item xs={6}>
|
||||
<Controller
|
||||
control={control}
|
||||
name="chainId"
|
||||
defaultValue=""
|
||||
render={({ field: { onChange, onBlur, value } }) => (
|
||||
<>
|
||||
<TextInput
|
||||
mode="outlined"
|
||||
value={value}
|
||||
label="Chain ID"
|
||||
onBlur={onBlur}
|
||||
onChangeText={textValue => onChange(textValue)}
|
||||
/>
|
||||
<HelperText type="error">{errors.chainId?.message}</HelperText>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
control={control}
|
||||
defaultValue=""
|
||||
name="networkName"
|
||||
render={({ field: { onChange, onBlur, value } }) => (
|
||||
<>
|
||||
<TextInput
|
||||
mode="outlined"
|
||||
label="Network Name"
|
||||
value={value}
|
||||
onBlur={onBlur}
|
||||
onChangeText={textValue => onChange(textValue)}
|
||||
/>
|
||||
<HelperText type="error">{errors.networkName?.message}</HelperText>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
control={control}
|
||||
name="rpcUrl"
|
||||
defaultValue=""
|
||||
render={({ field: { onChange, onBlur, value } }) => (
|
||||
<>
|
||||
<TextInput
|
||||
mode="outlined"
|
||||
label="New RPC URL"
|
||||
onBlur={onBlur}
|
||||
value={value}
|
||||
onChangeText={textValue => onChange(textValue)}
|
||||
/>
|
||||
<HelperText type="error">{errors.rpcUrl?.message}</HelperText>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Controller
|
||||
control={control}
|
||||
defaultValue=""
|
||||
name="blockExplorerUrl"
|
||||
render={({ field: { onChange, onBlur, value } }) => (
|
||||
<>
|
||||
<TextInput
|
||||
mode="outlined"
|
||||
value={value}
|
||||
label="Block Explorer URL (Optional)"
|
||||
onBlur={onBlur}
|
||||
onChangeText={textValue => onChange(textValue)}
|
||||
/>
|
||||
<HelperText type="error">
|
||||
{errors.blockExplorerUrl?.message}
|
||||
</HelperText>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
control={control}
|
||||
name="coinType"
|
||||
defaultValue=""
|
||||
render={({ field: { onChange, onBlur, value } }) => (
|
||||
<>
|
||||
<TextInput
|
||||
mode="outlined"
|
||||
value={value}
|
||||
label="Coin Type"
|
||||
onBlur={onBlur}
|
||||
onChangeText={onChange}
|
||||
/>
|
||||
<HelperText type="error">{errors.coinType?.message}</HelperText>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
{namespace === EIP155 ? (
|
||||
<Controller
|
||||
control={control}
|
||||
name="currencySymbol"
|
||||
defaultValue=""
|
||||
render={({ field: { onChange, onBlur, value } }) => (
|
||||
<>
|
||||
<TextInput
|
||||
mode="outlined"
|
||||
value={value}
|
||||
label="Currency Symbol"
|
||||
onBlur={onBlur}
|
||||
onChangeText={textValue => onChange(textValue)}
|
||||
/>
|
||||
<HelperText type="error">
|
||||
{
|
||||
(errors as FieldErrors<z.infer<typeof ethNetworkDataSchema>>)
|
||||
.currencySymbol?.message
|
||||
}
|
||||
</HelperText>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<Controller
|
||||
control={control}
|
||||
name="chainId"
|
||||
name="nativeDenom"
|
||||
defaultValue=""
|
||||
render={({ field: { onChange, onBlur, value } }) => (
|
||||
<>
|
||||
<TextInput
|
||||
mode="outlined"
|
||||
value={value}
|
||||
label="Chain ID"
|
||||
label="Native Denom"
|
||||
onBlur={onBlur}
|
||||
onChangeText={(textValue) => onChange(textValue)}
|
||||
/>
|
||||
<HelperText type="error">{errors.chainId?.message}</HelperText>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={6}>
|
||||
<Controller
|
||||
control={control}
|
||||
defaultValue=""
|
||||
name="networkName"
|
||||
render={({ field: { onChange, onBlur, value } }) => (
|
||||
<>
|
||||
<TextInput
|
||||
mode="outlined"
|
||||
label="Network Name"
|
||||
value={value}
|
||||
onBlur={onBlur}
|
||||
onChangeText={(textValue) => onChange(textValue)}
|
||||
onChangeText={textValue => onChange(textValue)}
|
||||
/>
|
||||
<HelperText type="error">
|
||||
{errors.networkName?.message}
|
||||
{
|
||||
(
|
||||
errors as FieldErrors<
|
||||
z.infer<typeof cosmosNetworkDataSchema>
|
||||
>
|
||||
).nativeDenom?.message
|
||||
}
|
||||
</HelperText>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={6}>
|
||||
<Controller
|
||||
control={control}
|
||||
name="rpcUrl"
|
||||
name="addressPrefix"
|
||||
defaultValue=""
|
||||
render={({ field: { onChange, onBlur, value } }) => (
|
||||
<>
|
||||
<TextInput
|
||||
mode="outlined"
|
||||
label="New RPC URL"
|
||||
onBlur={onBlur}
|
||||
value={value}
|
||||
onChangeText={(textValue) => onChange(textValue)}
|
||||
/>
|
||||
<HelperText type="error">{errors.rpcUrl?.message}</HelperText>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={6}>
|
||||
<Controller
|
||||
control={control}
|
||||
defaultValue=""
|
||||
name="blockExplorerUrl"
|
||||
render={({ field: { onChange, onBlur, value } }) => (
|
||||
<>
|
||||
<TextInput
|
||||
mode="outlined"
|
||||
value={value}
|
||||
label="Block Explorer URL (Optional)"
|
||||
label="Address Prefix"
|
||||
onBlur={onBlur}
|
||||
onChangeText={(textValue) => onChange(textValue)}
|
||||
onChangeText={textValue => onChange(textValue)}
|
||||
/>
|
||||
<HelperText type="error">
|
||||
{errors.blockExplorerUrl?.message}
|
||||
{
|
||||
(
|
||||
errors as FieldErrors<
|
||||
z.infer<typeof cosmosNetworkDataSchema>
|
||||
>
|
||||
).addressPrefix?.message
|
||||
}
|
||||
</HelperText>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={namespace === EIP155 ? 12 : 6}>
|
||||
<Controller
|
||||
control={control}
|
||||
name="coinType"
|
||||
name="gasPrice"
|
||||
defaultValue=""
|
||||
render={({ field: { onChange, onBlur, value } }) => (
|
||||
<>
|
||||
<TextInput
|
||||
mode="outlined"
|
||||
value={value}
|
||||
label="Coin Type"
|
||||
label="Gas Price"
|
||||
onBlur={onBlur}
|
||||
onChangeText={onChange}
|
||||
/>
|
||||
<HelperText type="error">{errors.coinType?.message}</HelperText>
|
||||
<HelperText type="error">
|
||||
{
|
||||
(
|
||||
errors as FieldErrors<
|
||||
z.infer<typeof cosmosNetworkDataSchema>
|
||||
>
|
||||
).gasPrice?.message
|
||||
}
|
||||
</HelperText>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
{namespace === EIP155 ? (
|
||||
<Grid item xs={12}>
|
||||
<Controller
|
||||
control={control}
|
||||
name="currencySymbol"
|
||||
defaultValue=""
|
||||
render={({ field: { onChange, onBlur, value } }) => (
|
||||
<>
|
||||
<TextInput
|
||||
mode="outlined"
|
||||
value={value}
|
||||
label="Currency Symbol"
|
||||
onBlur={onBlur}
|
||||
onChangeText={(textValue) => onChange(textValue)}
|
||||
/>
|
||||
<HelperText type="error">
|
||||
{
|
||||
(
|
||||
errors as FieldErrors<
|
||||
z.infer<typeof ethNetworkDataSchema>
|
||||
>
|
||||
).currencySymbol?.message
|
||||
}
|
||||
</HelperText>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
) : (
|
||||
<>
|
||||
<Grid item xs={6}>
|
||||
<Controller
|
||||
control={control}
|
||||
name="nativeDenom"
|
||||
defaultValue=""
|
||||
render={({ field: { onChange, onBlur, value } }) => (
|
||||
<>
|
||||
<TextInput
|
||||
mode="outlined"
|
||||
value={value}
|
||||
label="Native Denom"
|
||||
onBlur={onBlur}
|
||||
onChangeText={(textValue) => onChange(textValue)}
|
||||
/>
|
||||
<HelperText type="error">
|
||||
{
|
||||
(
|
||||
errors as FieldErrors<
|
||||
z.infer<typeof cosmosNetworkDataSchema>
|
||||
>
|
||||
).nativeDenom?.message
|
||||
}
|
||||
</HelperText>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Controller
|
||||
control={control}
|
||||
name="addressPrefix"
|
||||
defaultValue=""
|
||||
render={({ field: { onChange, onBlur, value } }) => (
|
||||
<>
|
||||
<TextInput
|
||||
mode="outlined"
|
||||
value={value}
|
||||
label="Address Prefix"
|
||||
onBlur={onBlur}
|
||||
onChangeText={(textValue) => onChange(textValue)}
|
||||
/>
|
||||
<HelperText type="error">
|
||||
{
|
||||
(
|
||||
errors as FieldErrors<
|
||||
z.infer<typeof cosmosNetworkDataSchema>
|
||||
>
|
||||
).addressPrefix?.message
|
||||
}
|
||||
</HelperText>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Controller
|
||||
control={control}
|
||||
name="gasPrice"
|
||||
defaultValue=""
|
||||
render={({ field: { onChange, onBlur, value } }) => (
|
||||
<>
|
||||
<TextInput
|
||||
mode="outlined"
|
||||
value={value}
|
||||
label="Gas Price"
|
||||
onBlur={onBlur}
|
||||
onChangeText={onChange}
|
||||
/>
|
||||
<HelperText type="error">
|
||||
{
|
||||
(
|
||||
errors as FieldErrors<
|
||||
z.infer<typeof cosmosNetworkDataSchema>
|
||||
>
|
||||
).gasPrice?.message
|
||||
}
|
||||
</HelperText>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
</>
|
||||
)}
|
||||
</Grid>
|
||||
<LoadingButton
|
||||
variant="contained"
|
||||
</>
|
||||
)}
|
||||
<Button
|
||||
mode="contained"
|
||||
loading={isSubmitting}
|
||||
disabled={isSubmitting}
|
||||
onClick={handleSubmit(submit)}
|
||||
sx={{ minWidth: "200px", px: 4, py: 1, mt: 2 }}
|
||||
>
|
||||
{isSubmitting ? "Adding" : "Submit"}
|
||||
</LoadingButton>
|
||||
</Layout>
|
||||
style={styles.networksButton}
|
||||
onPress={handleSubmit(submit)}>
|
||||
{isSubmitting ? 'Adding' : 'Submit'}
|
||||
</Button>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
+17
-19
@@ -1,24 +1,22 @@
|
||||
import React, { useCallback, useState } from "react";
|
||||
import { View } from "react-native";
|
||||
import { Text, TextInput } from "react-native-paper";
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { View } from 'react-native';
|
||||
import { Button, Text, TextInput } from 'react-native-paper';
|
||||
|
||||
import { useNavigation } from "@react-navigation/native";
|
||||
import { NativeStackNavigationProp } from "@react-navigation/native-stack";
|
||||
import { Box, Button } from "@mui/material";
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
|
||||
import { web3WalletPair } from "../utils/wallet-connect/WalletConnectUtils";
|
||||
import styles from "../styles/stylesheet";
|
||||
import { StackParamsList } from "../types";
|
||||
import { useWalletConnect } from "../context/WalletConnectContext";
|
||||
import { Layout } from "../components/Layout";
|
||||
import { web3WalletPair } from '../utils/wallet-connect/WalletConnectUtils';
|
||||
import styles from '../styles/stylesheet';
|
||||
import { StackParamsList } from '../types';
|
||||
import { useWalletConnect } from '../context/WalletConnectContext';
|
||||
|
||||
const AddSession = () => {
|
||||
const navigation =
|
||||
useNavigation<NativeStackNavigationProp<StackParamsList>>();
|
||||
|
||||
const [currentWCURI, setCurrentWCURI] = useState<string>("");
|
||||
const [currentWCURI, setCurrentWCURI] = useState<string>('');
|
||||
|
||||
const { web3wallet } = useWalletConnect();
|
||||
const {web3wallet} = useWalletConnect();
|
||||
|
||||
const pair = useCallback(async () => {
|
||||
if (!web3wallet) {
|
||||
@@ -26,12 +24,12 @@ const AddSession = () => {
|
||||
}
|
||||
|
||||
const pairing = await web3WalletPair(web3wallet, { uri: currentWCURI });
|
||||
navigation.navigate("WalletConnect");
|
||||
navigation.navigate('WalletConnect');
|
||||
return pairing;
|
||||
}, [currentWCURI, navigation, web3wallet]);
|
||||
|
||||
return (
|
||||
<Layout title="Add Session">
|
||||
<View style={styles.appContainer}>
|
||||
<View style={styles.inputContainer}>
|
||||
<Text variant="titleMedium">Enter WalletConnect URI</Text>
|
||||
<TextInput
|
||||
@@ -43,13 +41,13 @@ const AddSession = () => {
|
||||
style={styles.walletConnectUriText}
|
||||
/>
|
||||
|
||||
<Box sx={{ mt: 2 }}>
|
||||
<Button variant="contained" onClick={pair}>
|
||||
<View style={styles.signButton}>
|
||||
<Button mode="contained" onPress={pair}>
|
||||
Pair Session
|
||||
</Button>
|
||||
</Box>
|
||||
</View>
|
||||
</View>
|
||||
</Layout>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
export default AddSession;
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { Image, ScrollView, View } from 'react-native';
|
||||
import { Button, Text, TextInput } from 'react-native-paper';
|
||||
import { MsgCreateValidator } from 'cosmjs-types/cosmos/staking/v1beta1/tx';
|
||||
|
||||
import {
|
||||
NativeStackNavigationProp,
|
||||
NativeStackScreenProps,
|
||||
} from '@react-navigation/native-stack';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { DirectSecp256k1Wallet, EncodeObject } from '@cosmjs/proto-signing';
|
||||
import { DirectSecp256k1Wallet } from '@cosmjs/proto-signing';
|
||||
import { LaconicClient } from '@cerc-io/registry-sdk';
|
||||
import { GasPrice, calculateFee } from '@cosmjs/stargate';
|
||||
import { formatJsonRpcError } from '@json-rpc-tools/utils';
|
||||
@@ -41,6 +40,7 @@ const ApproveTransaction = ({ route }: ApproveTransactionProps) => {
|
||||
const requestName = requestSession.peer.metadata.name;
|
||||
const requestIcon = requestSession.peer.metadata.icons[0];
|
||||
const requestURL = requestSession.peer.metadata.url;
|
||||
const transactionMessage = route.params.transactionMessage;
|
||||
const signer = route.params.signer;
|
||||
const requestEvent = route.params.requestEvent;
|
||||
const chainId = requestEvent.params.chainId;
|
||||
@@ -67,20 +67,6 @@ const ApproveTransaction = ({ route }: ApproveTransactionProps) => {
|
||||
|
||||
const { web3wallet } = useWalletConnect();
|
||||
|
||||
const transactionMessage = useMemo((): EncodeObject => {
|
||||
const inputTxMsg = route.params.transactionMessage;
|
||||
|
||||
// If it's a MsgCreateValidator, decode the tx msg value using MsgCreateValidator type
|
||||
if (inputTxMsg.typeUrl.includes('MsgCreateValidator')) {
|
||||
return {
|
||||
typeUrl: inputTxMsg.typeUrl,
|
||||
value: MsgCreateValidator.fromJSON(inputTxMsg.value)
|
||||
}
|
||||
}
|
||||
|
||||
return inputTxMsg;
|
||||
}, [route.params.transactionMessage]);
|
||||
|
||||
useEffect(() => {
|
||||
if (namespace !== COSMOS) {
|
||||
return;
|
||||
@@ -152,7 +138,7 @@ const ApproveTransaction = ({ route }: ApproveTransactionProps) => {
|
||||
return;
|
||||
}
|
||||
const gasEstimation = await cosmosStargateClient!.simulate(
|
||||
signer,
|
||||
transactionMessage.value.participant!,
|
||||
[transactionMessage],
|
||||
MEMO,
|
||||
);
|
||||
@@ -174,7 +160,7 @@ const ApproveTransaction = ({ route }: ApproveTransactionProps) => {
|
||||
}
|
||||
};
|
||||
getCosmosGas();
|
||||
}, [cosmosStargateClient, transactionMessage, requestEventId, topic, web3wallet, signer]);
|
||||
}, [cosmosStargateClient, transactionMessage, requestEventId, topic, web3wallet]);
|
||||
|
||||
useEffect(() => {
|
||||
const gasPrice = GasPrice.fromString(
|
||||
@@ -253,13 +239,6 @@ const ApproveTransaction = ({ route }: ApproveTransactionProps) => {
|
||||
navigation.navigate('Home');
|
||||
};
|
||||
|
||||
const replacer = (key: string, value: any): any => {
|
||||
if (value instanceof Uint8Array) {
|
||||
return Buffer.from(value).toString('hex');
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ScrollView contentContainerStyle={styles.approveTransaction}>
|
||||
@@ -285,7 +264,7 @@ const ApproveTransaction = ({ route }: ApproveTransactionProps) => {
|
||||
</Text>
|
||||
<View style={styles.messageBody}>
|
||||
<Text variant="bodyLarge">
|
||||
{JSON.stringify(transactionMessage, replacer, 2)}
|
||||
{JSON.stringify(transactionMessage, null, 2)}
|
||||
</Text>
|
||||
</View>
|
||||
<>
|
||||
|
||||
+141
-299
@@ -46,29 +46,24 @@ export const MEMO = 'Sending signed tx from Laconic Wallet';
|
||||
// Reference: https://ethereum.org/en/developers/docs/gas/#what-is-gas-limit
|
||||
const ETH_MINIMUM_GAS = 21000;
|
||||
|
||||
type ApproveTransferProps = NativeStackScreenProps<StackParamsList, 'ApproveTransfer'> & {
|
||||
route: {
|
||||
params: {
|
||||
transaction: any;
|
||||
requestEvent?: {
|
||||
params: {
|
||||
chainId: string;
|
||||
request: {
|
||||
method: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
requestSessionData?: any;
|
||||
chainId?: string;
|
||||
};
|
||||
path?: string;
|
||||
};
|
||||
};
|
||||
type SignRequestProps = NativeStackScreenProps<
|
||||
StackParamsList,
|
||||
'ApproveTransfer'
|
||||
>;
|
||||
|
||||
const ApproveTransfer = ({ route }: ApproveTransferProps) => {
|
||||
const ApproveTransfer = ({ route }: SignRequestProps) => {
|
||||
const { networksData } = useNetworks();
|
||||
const { web3wallet } = useWalletConnect();
|
||||
|
||||
const requestSession = route.params.requestSessionData;
|
||||
const requestName = requestSession.peer.metadata.name;
|
||||
const requestIcon = requestSession.peer.metadata.icons[0];
|
||||
const requestURL = requestSession.peer.metadata.url;
|
||||
const transaction = route.params.transaction;
|
||||
const requestEvent = route.params.requestEvent;
|
||||
const chainId = requestEvent.params.chainId;
|
||||
const requestMethod = requestEvent.params.request.method;
|
||||
|
||||
const [account, setAccount] = useState<Account>();
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [balance, setBalance] = useState<string>('');
|
||||
@@ -85,80 +80,6 @@ const ApproveTransfer = ({ route }: ApproveTransferProps) => {
|
||||
const [ethMaxPriorityFee, setEthMaxPriorityFee] =
|
||||
useState<BigNumber | null>();
|
||||
|
||||
const navigation =
|
||||
useNavigation<NativeStackNavigationProp<StackParamsList>>();
|
||||
|
||||
// Extract data from route params or path
|
||||
const requestSession = route.params?.requestSessionData;
|
||||
const requestName = requestSession?.peer?.metadata?.name;
|
||||
const requestIcon = requestSession?.peer?.metadata?.icons?.[0];
|
||||
const requestURL = requestSession?.peer?.metadata?.url;
|
||||
const transaction = route.params?.transaction;
|
||||
const requestEvent = route.params?.requestEvent;
|
||||
const chainId = requestEvent?.params?.chainId || route.params?.chainId;
|
||||
const requestMethod = requestEvent?.params?.request?.method;
|
||||
|
||||
const sanitizePath = useCallback((path: string) => {
|
||||
const regex = /^\/transfer\/(eip155|cosmos)\/(.+)\/(.+)\/(.+)\/(.+)$/;
|
||||
const match = path.match(regex);
|
||||
if (match) {
|
||||
const [, pathNamespace, pathChainId, pathAddress, pathTo, pathAmount] = match;
|
||||
return {
|
||||
namespace: pathNamespace,
|
||||
chainId: pathChainId,
|
||||
address: pathAddress,
|
||||
to: pathTo,
|
||||
amount: pathAmount,
|
||||
};
|
||||
} else {
|
||||
navigation.navigate('InvalidPath');
|
||||
}
|
||||
return null;
|
||||
}, [navigation]);
|
||||
|
||||
const retrieveData = useCallback(async (requestNamespace: string, requestChainId: string, requestAddress: string) => {
|
||||
const requestAccount = await retrieveSingleAccount(
|
||||
requestNamespace,
|
||||
requestChainId,
|
||||
requestAddress,
|
||||
);
|
||||
if (!requestAccount) {
|
||||
navigation.navigate('InvalidPath');
|
||||
return;
|
||||
}
|
||||
|
||||
setAccount(requestAccount);
|
||||
}, [navigation]);
|
||||
|
||||
useEffect(() => {
|
||||
if (route.path) {
|
||||
const sanitizedRoute = sanitizePath(route.path);
|
||||
if (sanitizedRoute) {
|
||||
retrieveData(
|
||||
sanitizedRoute.namespace,
|
||||
sanitizedRoute.chainId,
|
||||
sanitizedRoute.address,
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (requestEvent) {
|
||||
const requestedNetwork = networksData.find(
|
||||
networkData => {
|
||||
return `${networkData.namespace}:${networkData.chainId}` === chainId;
|
||||
}
|
||||
);
|
||||
if (requestedNetwork && transaction?.from) {
|
||||
retrieveData(
|
||||
requestedNetwork.namespace,
|
||||
requestedNetwork.chainId,
|
||||
transaction.from,
|
||||
);
|
||||
}
|
||||
}
|
||||
}, [retrieveData, sanitizePath, route, networksData, requestEvent, chainId, transaction]);
|
||||
|
||||
const isSufficientFunds = useMemo(() => {
|
||||
if (!transaction.value) {
|
||||
return;
|
||||
@@ -218,7 +139,7 @@ const ApproveTransfer = ({ route }: ApproveTransferProps) => {
|
||||
).privKey;
|
||||
|
||||
const sender = await DirectSecp256k1Wallet.fromKey(
|
||||
Uint8Array.from(Buffer.from(cosmosPrivKey.split('0x')[1], 'hex')),
|
||||
Buffer.from(cosmosPrivKey.split('0x')[1], 'hex'),
|
||||
requestedNetwork?.addressPrefix,
|
||||
);
|
||||
|
||||
@@ -264,6 +185,26 @@ const ApproveTransfer = ({ route }: ApproveTransferProps) => {
|
||||
}
|
||||
}, [requestedNetwork, namespace]);
|
||||
|
||||
const navigation =
|
||||
useNavigation<NativeStackNavigationProp<StackParamsList>>();
|
||||
|
||||
const retrieveData = useCallback(
|
||||
async (requestAddress: string) => {
|
||||
const requestAccount = await retrieveSingleAccount(
|
||||
requestedNetwork!.namespace,
|
||||
requestedNetwork!.chainId,
|
||||
requestAddress,
|
||||
);
|
||||
if (!requestAccount) {
|
||||
navigation.navigate('InvalidPath');
|
||||
return;
|
||||
}
|
||||
|
||||
setAccount(requestAccount);
|
||||
},
|
||||
[navigation, requestedNetwork],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
// Set loading to false when gas values for requested chain are fetched
|
||||
// If requested chain is EVM compatible, the cosmos gas values will be undefined and vice-versa, hence the condition checks only one of them at the same time
|
||||
@@ -305,6 +246,9 @@ const ApproveTransfer = ({ route }: ApproveTransferProps) => {
|
||||
requestedNetwork,
|
||||
ethMaxFee,
|
||||
]);
|
||||
useEffect(() => {
|
||||
retrieveData(transaction.from!);
|
||||
}, [retrieveData, transaction]);
|
||||
|
||||
const isEIP1559 = useMemo(() => {
|
||||
if (cosmosGasLimit) {
|
||||
@@ -316,101 +260,6 @@ const ApproveTransfer = ({ route }: ApproveTransferProps) => {
|
||||
return false;
|
||||
}, [cosmosGasLimit, ethMaxFee, ethMaxPriorityFee]);
|
||||
|
||||
const handleIntent = async () => {
|
||||
if (!account) {
|
||||
throw new Error('Account is not valid');
|
||||
}
|
||||
|
||||
if (route.path) {
|
||||
const sanitizedRoute = sanitizePath(route.path);
|
||||
if (!sanitizedRoute) {
|
||||
throw new Error('Invalid path');
|
||||
}
|
||||
|
||||
const requestedNetwork = networksData.find(
|
||||
networkData => networkData.chainId === sanitizedRoute.chainId,
|
||||
);
|
||||
|
||||
if (!requestedNetwork) {
|
||||
throw new Error('Network not found');
|
||||
}
|
||||
|
||||
const cosmosPrivKey = (
|
||||
await getPathKey(
|
||||
`${requestedNetwork.namespace}:${requestedNetwork.chainId}`,
|
||||
account.index,
|
||||
)
|
||||
).privKey;
|
||||
|
||||
const sender = await DirectSecp256k1Wallet.fromKey(
|
||||
Uint8Array.from(Buffer.from(cosmosPrivKey.split('0x')[1], 'hex')),
|
||||
requestedNetwork.addressPrefix,
|
||||
);
|
||||
|
||||
const client = await SigningStargateClient.connectWithSigner(
|
||||
requestedNetwork.rpcUrl!,
|
||||
sender,
|
||||
);
|
||||
|
||||
const sendMsg: MsgSendEncodeObject = {
|
||||
typeUrl: '/cosmos.bank.v1beta1.MsgSend',
|
||||
value: {
|
||||
fromAddress: account.address,
|
||||
toAddress: sanitizedRoute.to,
|
||||
amount: [
|
||||
{
|
||||
amount: String(sanitizedRoute.amount),
|
||||
denom: requestedNetwork.nativeDenom!,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const gasEstimation = await client.simulate(
|
||||
account.address,
|
||||
[sendMsg],
|
||||
MEMO,
|
||||
);
|
||||
|
||||
const gasLimit = String(
|
||||
Math.round(gasEstimation * Number(process.env.REACT_APP_GAS_ADJUSTMENT)),
|
||||
);
|
||||
|
||||
const gasPrice = GasPrice.fromString(
|
||||
requestedNetwork.gasPrice! + requestedNetwork.nativeDenom,
|
||||
);
|
||||
|
||||
const cosmosFees = calculateFee(Number(gasLimit), gasPrice);
|
||||
|
||||
const result = await client.signAndBroadcast(
|
||||
account.address,
|
||||
[sendMsg],
|
||||
{
|
||||
amount: [
|
||||
{
|
||||
amount: cosmosFees.amount[0].amount,
|
||||
denom: requestedNetwork.nativeDenom!,
|
||||
},
|
||||
],
|
||||
gas: gasLimit,
|
||||
},
|
||||
MEMO,
|
||||
);
|
||||
|
||||
// Convert BigInt values to strings before sending to Android
|
||||
const serializedResult = JSON.stringify(result, (key, value) =>
|
||||
typeof value === 'bigint' ? value.toString() : value
|
||||
);
|
||||
|
||||
// Send the result back to Android and close dialog
|
||||
if (window.Android?.onTransferComplete) {
|
||||
window.Android.onTransferComplete(serializedResult);
|
||||
} else {
|
||||
alert(`Transaction: ${serializedResult}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const acceptRequestHandler = async () => {
|
||||
setIsTxLoading(true);
|
||||
try {
|
||||
@@ -418,80 +267,77 @@ const ApproveTransfer = ({ route }: ApproveTransferProps) => {
|
||||
throw new Error('account not found');
|
||||
}
|
||||
|
||||
if (requestEvent) {
|
||||
// Handle WalletConnect request
|
||||
if (ethGasLimit && ethGasLimit.lt(ETH_MINIMUM_GAS)) {
|
||||
throw new Error(`Atleast ${ETH_MINIMUM_GAS} gas limit is required`);
|
||||
}
|
||||
|
||||
if (ethMaxFee && ethMaxPriorityFee && ethMaxFee.lte(ethMaxPriorityFee)) {
|
||||
throw new Error(
|
||||
`Max fee per gas (${ethMaxFee.toNumber()}) cannot be lower than or equal to max priority fee per gas (${ethMaxPriorityFee.toNumber()})`,
|
||||
);
|
||||
}
|
||||
|
||||
let options: WalletConnectRequests;
|
||||
|
||||
switch (requestMethod) {
|
||||
case EIP155_SIGNING_METHODS.ETH_SEND_TRANSACTION:
|
||||
if (
|
||||
ethMaxFee === undefined ||
|
||||
ethMaxPriorityFee === undefined ||
|
||||
ethGasPrice === undefined
|
||||
) {
|
||||
throw new Error('Gas values not found');
|
||||
}
|
||||
|
||||
options = {
|
||||
type: 'eth_sendTransaction',
|
||||
provider: provider!,
|
||||
ethGasLimit: BigNumber.from(ethGasLimit),
|
||||
ethGasPrice: ethGasPrice ? ethGasPrice.toHexString() : null,
|
||||
maxFeePerGas: ethMaxFee,
|
||||
maxPriorityFeePerGas: ethMaxPriorityFee,
|
||||
};
|
||||
break;
|
||||
case COSMOS_METHODS.COSMOS_SEND_TOKENS:
|
||||
if (!cosmosStargateClient) {
|
||||
throw new Error('Cosmos stargate client not found');
|
||||
}
|
||||
|
||||
options = {
|
||||
type: 'cosmos_sendTokens',
|
||||
signingStargateClient: cosmosStargateClient,
|
||||
cosmosFee: {
|
||||
amount: [
|
||||
{
|
||||
amount: fees,
|
||||
denom: requestedNetwork!.nativeDenom!,
|
||||
},
|
||||
],
|
||||
gas: cosmosGasLimit,
|
||||
},
|
||||
sendMsg,
|
||||
memo: MEMO,
|
||||
};
|
||||
break;
|
||||
default:
|
||||
throw new Error('Invalid method');
|
||||
}
|
||||
|
||||
const response = await approveWalletConnectRequest(
|
||||
requestEvent,
|
||||
account!,
|
||||
namespace,
|
||||
requestedNetwork!.chainId,
|
||||
options,
|
||||
);
|
||||
|
||||
const { topic } = requestEvent;
|
||||
await web3wallet!.respondSessionRequest({ topic, response });
|
||||
navigation.navigate('Home');
|
||||
} else {
|
||||
// Handle direct intent
|
||||
await handleIntent();
|
||||
navigation.navigate('Home');
|
||||
if (ethGasLimit && ethGasLimit.lt(ETH_MINIMUM_GAS)) {
|
||||
throw new Error(`Atleast ${ETH_MINIMUM_GAS} gas limit is required`);
|
||||
}
|
||||
|
||||
if (ethMaxFee && ethMaxPriorityFee && ethMaxFee.lte(ethMaxPriorityFee)) {
|
||||
throw new Error(
|
||||
`Max fee per gas (${ethMaxFee.toNumber()}) cannot be lower than or equal to max priority fee per gas (${ethMaxPriorityFee.toNumber()})`,
|
||||
);
|
||||
}
|
||||
|
||||
let options: WalletConnectRequests;
|
||||
|
||||
switch (requestMethod) {
|
||||
case EIP155_SIGNING_METHODS.ETH_SEND_TRANSACTION:
|
||||
if (
|
||||
ethMaxFee === undefined ||
|
||||
ethMaxPriorityFee === undefined ||
|
||||
ethGasPrice === undefined
|
||||
) {
|
||||
throw new Error('Gas values not found');
|
||||
}
|
||||
|
||||
options = {
|
||||
type: 'eth_sendTransaction',
|
||||
provider: provider!,
|
||||
ethGasLimit: BigNumber.from(ethGasLimit),
|
||||
ethGasPrice: ethGasPrice ? ethGasPrice.toHexString() : null,
|
||||
maxFeePerGas: ethMaxFee,
|
||||
maxPriorityFeePerGas: ethMaxPriorityFee,
|
||||
};
|
||||
break;
|
||||
case COSMOS_METHODS.COSMOS_SEND_TOKENS:
|
||||
if (!cosmosStargateClient) {
|
||||
throw new Error('Cosmos stargate client not found');
|
||||
}
|
||||
|
||||
options = {
|
||||
type: 'cosmos_sendTokens',
|
||||
signingStargateClient: cosmosStargateClient,
|
||||
// StdFee object
|
||||
cosmosFee: {
|
||||
// This amount is total fees required for transaction
|
||||
amount: [
|
||||
{
|
||||
amount: fees,
|
||||
denom: requestedNetwork!.nativeDenom!,
|
||||
},
|
||||
],
|
||||
gas: cosmosGasLimit,
|
||||
},
|
||||
sendMsg,
|
||||
memo: MEMO,
|
||||
};
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error('Invalid method');
|
||||
}
|
||||
|
||||
const response = await approveWalletConnectRequest(
|
||||
requestEvent,
|
||||
account,
|
||||
namespace,
|
||||
requestedNetwork!.chainId,
|
||||
options,
|
||||
);
|
||||
|
||||
const { topic } = requestEvent;
|
||||
await web3wallet!.respondSessionRequest({ topic, response });
|
||||
navigation.navigate('Home');
|
||||
} catch (error) {
|
||||
if (!(error instanceof Error)) {
|
||||
throw error;
|
||||
@@ -504,26 +350,20 @@ const ApproveTransfer = ({ route }: ApproveTransferProps) => {
|
||||
};
|
||||
|
||||
const rejectRequestHandler = async () => {
|
||||
if (requestEvent) {
|
||||
const response = rejectWalletConnectRequest(requestEvent);
|
||||
const { topic } = requestEvent;
|
||||
await web3wallet!.respondSessionRequest({
|
||||
topic,
|
||||
response,
|
||||
});
|
||||
}
|
||||
const response = rejectWalletConnectRequest(requestEvent);
|
||||
const { topic } = requestEvent;
|
||||
await web3wallet!.respondSessionRequest({
|
||||
topic,
|
||||
response,
|
||||
});
|
||||
|
||||
if (window.Android?.onTransferCancelled) {
|
||||
window.Android.onTransferCancelled();
|
||||
} else {
|
||||
navigation.navigate('Home');
|
||||
}
|
||||
navigation.navigate('Home');
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const getAccountBalance = async () => {
|
||||
try {
|
||||
if (!account || !requestedNetwork) {
|
||||
if (!account) {
|
||||
return;
|
||||
}
|
||||
if (namespace === EIP155) {
|
||||
@@ -533,20 +373,20 @@ const ApproveTransfer = ({ route }: ApproveTransferProps) => {
|
||||
const fetchedBalance = await provider.getBalance(account.address);
|
||||
setBalance(fetchedBalance ? fetchedBalance.toString() : '0');
|
||||
} else {
|
||||
if (!cosmosStargateClient) {
|
||||
return;
|
||||
}
|
||||
const cosmosBalance = await cosmosStargateClient.getBalance(
|
||||
const cosmosBalance = await cosmosStargateClient?.getBalance(
|
||||
account.address,
|
||||
requestedNetwork.nativeDenom!.toLowerCase(),
|
||||
requestedNetwork!.nativeDenom!.toLowerCase(),
|
||||
);
|
||||
setBalance(cosmosBalance?.amount || '0');
|
||||
|
||||
setBalance(cosmosBalance?.amount!);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching balance:', error);
|
||||
setBalance('0');
|
||||
// Don't show error dialog for balance fetch failures
|
||||
// Just set balance to 0 and let the transaction proceed
|
||||
if (!(error instanceof Error)) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
setTxError(error.message);
|
||||
setIsTxErrorDialogOpen(true);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -668,18 +508,16 @@ const ApproveTransfer = ({ route }: ApproveTransferProps) => {
|
||||
) : (
|
||||
<>
|
||||
<ScrollView contentContainerStyle={styles.appContainer}>
|
||||
{requestSession && (
|
||||
<View style={styles.dappDetails}>
|
||||
{requestIcon && (
|
||||
<Image
|
||||
style={styles.dappLogo}
|
||||
source={requestIcon ? { uri: requestIcon } : undefined}
|
||||
/>
|
||||
)}
|
||||
<Text>{requestName}</Text>
|
||||
<Text variant="bodyMedium">{requestURL}</Text>
|
||||
</View>
|
||||
)}
|
||||
<View style={styles.dappDetails}>
|
||||
{requestIcon && (
|
||||
<Image
|
||||
style={styles.dappLogo}
|
||||
source={requestIcon ? { uri: requestIcon } : undefined}
|
||||
/>
|
||||
)}
|
||||
<Text>{requestName}</Text>
|
||||
<Text variant="bodyMedium">{requestURL}</Text>
|
||||
</View>
|
||||
<View style={styles.dataBoxContainer}>
|
||||
<Text style={styles.dataBoxLabel}>From</Text>
|
||||
<View style={styles.dataBox}>
|
||||
@@ -690,7 +528,11 @@ const ApproveTransfer = ({ route }: ApproveTransferProps) => {
|
||||
label={`Balance (${
|
||||
namespace === EIP155 ? 'wei' : requestedNetwork!.nativeDenom
|
||||
})`}
|
||||
data={balance || '0'}
|
||||
data={
|
||||
balance === '' || balance === undefined
|
||||
? 'Loading balance...'
|
||||
: `${balance}`
|
||||
}
|
||||
/>
|
||||
{transaction && (
|
||||
<View style={styles.approveTransfer}>
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
import React, { useEffect } from 'react';
|
||||
|
||||
import { useNetworks } from '../context/NetworksContext';
|
||||
import { signMessage } from '../utils/sign-message';
|
||||
import { EIP155 } from '../utils/constants';
|
||||
import { sendMessage } from '../utils/misc';
|
||||
import useAccountsData from '../hooks/useAccountsData';
|
||||
import useGetOrCreateAccounts from '../hooks/useGetOrCreateAccounts';
|
||||
|
||||
export const AutoSignIn = () => {
|
||||
const { networksData } = useNetworks();
|
||||
|
||||
const { getAccountsData } = useAccountsData();
|
||||
|
||||
useEffect(() => {
|
||||
const handleSignIn = async (event: MessageEvent) => {
|
||||
if (event.data.type !== 'AUTO_SIGN_IN') return;
|
||||
|
||||
if (event.origin !== process.env.REACT_APP_DEPLOY_APP_URL) {
|
||||
console.log('Unauthorized app.');
|
||||
return;
|
||||
}
|
||||
|
||||
const accountsData = await getAccountsData(event.data.chainId);
|
||||
|
||||
if (!accountsData.length) {
|
||||
return
|
||||
}
|
||||
|
||||
const signature = await signMessage({ message: event.data.message, accountId: accountsData[0].index, chainId: event.data.chainId, namespace: EIP155 })
|
||||
|
||||
sendMessage(event.source as Window, 'SIGN_IN_RESPONSE', { message: event.data.message, signature }, event.origin);
|
||||
};
|
||||
|
||||
window.addEventListener('message', handleSignIn);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('message', handleSignIn);
|
||||
};
|
||||
}, [networksData, getAccountsData]);
|
||||
|
||||
// Custom hook for adding listener to get accounts data
|
||||
useGetOrCreateAccounts();
|
||||
|
||||
return (
|
||||
<>
|
||||
</>
|
||||
)
|
||||
};
|
||||
+114
-128
@@ -1,28 +1,27 @@
|
||||
import React, { useCallback } from "react";
|
||||
import { useForm, Controller, FieldErrors } from "react-hook-form";
|
||||
import { TextInput, HelperText } from "react-native-paper";
|
||||
import { z } from "zod";
|
||||
import React, { useCallback } from 'react';
|
||||
import { View } from 'react-native';
|
||||
import { useForm, Controller, FieldErrors } from 'react-hook-form';
|
||||
import { TextInput, Button, HelperText, Text } from 'react-native-paper';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import {
|
||||
NativeStackNavigationProp,
|
||||
NativeStackScreenProps,
|
||||
} from "@react-navigation/native-stack";
|
||||
import { useNavigation } from "@react-navigation/native";
|
||||
} from '@react-navigation/native-stack';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
|
||||
import { setInternetCredentials } from "../utils/key-store";
|
||||
import { StackParamsList } from "../types";
|
||||
import { retrieveNetworksData } from "../utils/accounts";
|
||||
import { useNetworks } from "../context/NetworksContext";
|
||||
import { setInternetCredentials } from '../utils/key-store';
|
||||
import { StackParamsList } from '../types';
|
||||
import styles from '../styles/stylesheet';
|
||||
import { retrieveNetworksData } from '../utils/accounts';
|
||||
import { useNetworks } from '../context/NetworksContext';
|
||||
import {
|
||||
COSMOS,
|
||||
EIP155,
|
||||
EMPTY_FIELD_ERROR,
|
||||
INVALID_URL_ERROR,
|
||||
} from "../utils/constants";
|
||||
import { Divider, Grid, Typography } from "@mui/material";
|
||||
import { LoadingButton } from "@mui/lab";
|
||||
import { Layout } from "../components/Layout";
|
||||
} from '../utils/constants';
|
||||
|
||||
const ethNetworksFormSchema = z.object({
|
||||
// Adding type field for resolving typescript error
|
||||
@@ -32,7 +31,7 @@ const ethNetworksFormSchema = z.object({
|
||||
blockExplorerUrl: z
|
||||
.string()
|
||||
.url({ message: INVALID_URL_ERROR })
|
||||
.or(z.literal("")),
|
||||
.or(z.literal('')),
|
||||
});
|
||||
|
||||
const cosmosNetworksFormDataSchema = z.object({
|
||||
@@ -42,14 +41,14 @@ const cosmosNetworksFormDataSchema = z.object({
|
||||
blockExplorerUrl: z
|
||||
.string()
|
||||
.url({ message: INVALID_URL_ERROR })
|
||||
.or(z.literal("")),
|
||||
.or(z.literal('')),
|
||||
gasPrice: z
|
||||
.string()
|
||||
.nonempty({ message: EMPTY_FIELD_ERROR })
|
||||
.regex(/^\d+(\.\d+)?$/),
|
||||
});
|
||||
|
||||
type EditNetworkProps = NativeStackScreenProps<StackParamsList, "EditNetwork">;
|
||||
type EditNetworkProps = NativeStackScreenProps<StackParamsList, 'EditNetwork'>;
|
||||
|
||||
const EditNetwork = ({ route }: EditNetworkProps) => {
|
||||
const { setNetworksData } = useNetworks();
|
||||
@@ -68,7 +67,7 @@ const EditNetwork = ({ route }: EditNetworkProps) => {
|
||||
formState: { errors, isSubmitting },
|
||||
handleSubmit,
|
||||
} = useForm<z.infer<typeof networksFormDataSchema>>({
|
||||
mode: "onChange",
|
||||
mode: 'onChange',
|
||||
resolver: zodResolver(networksFormDataSchema),
|
||||
});
|
||||
|
||||
@@ -78,134 +77,121 @@ const EditNetwork = ({ route }: EditNetworkProps) => {
|
||||
const { type, ...dataWithoutType } = data;
|
||||
const newNetworkData = { ...networkData, ...dataWithoutType };
|
||||
const index = retrievedNetworksData.findIndex(
|
||||
(network) => network.networkId === networkData.networkId,
|
||||
network => network.networkId === networkData.networkId,
|
||||
);
|
||||
|
||||
retrievedNetworksData.splice(index, 1, newNetworkData);
|
||||
|
||||
await setInternetCredentials(
|
||||
"networks",
|
||||
"_",
|
||||
'networks',
|
||||
'_',
|
||||
JSON.stringify(retrievedNetworksData),
|
||||
);
|
||||
|
||||
setNetworksData(retrievedNetworksData);
|
||||
|
||||
navigation.navigate("Home");
|
||||
navigation.navigate('Home');
|
||||
},
|
||||
[networkData, navigation, setNetworksData],
|
||||
);
|
||||
const isCosmos = networkData.namespace === COSMOS;
|
||||
|
||||
return (
|
||||
<Layout title="Edit Network">
|
||||
<Typography fontSize="1.5rem" fontWeight={500}>
|
||||
Edit {networkData?.networkName} details
|
||||
</Typography>
|
||||
<Divider flexItem sx={{ my: 4 }} />
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={6}>
|
||||
<Controller
|
||||
control={control}
|
||||
defaultValue={networkData.networkName}
|
||||
name="networkName"
|
||||
render={({ field: { onChange, onBlur, value } }) => (
|
||||
<>
|
||||
<TextInput
|
||||
mode="outlined"
|
||||
label="Network Name"
|
||||
value={value}
|
||||
onBlur={onBlur}
|
||||
onChangeText={(textValue) => onChange(textValue)}
|
||||
/>
|
||||
<HelperText type="error">
|
||||
{errors.networkName?.message}
|
||||
</HelperText>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Controller
|
||||
control={control}
|
||||
name="rpcUrl"
|
||||
defaultValue={networkData.rpcUrl}
|
||||
render={({ field: { onChange, onBlur, value } }) => (
|
||||
<>
|
||||
<TextInput
|
||||
mode="outlined"
|
||||
label="New RPC URL"
|
||||
onBlur={onBlur}
|
||||
value={value}
|
||||
onChangeText={(textValue) => onChange(textValue)}
|
||||
/>
|
||||
<HelperText type="error">{errors.rpcUrl?.message}</HelperText>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={isCosmos ? 6 : 12}>
|
||||
<Controller
|
||||
control={control}
|
||||
defaultValue={networkData.blockExplorerUrl}
|
||||
name="blockExplorerUrl"
|
||||
render={({ field: { onChange, onBlur, value } }) => (
|
||||
<>
|
||||
<TextInput
|
||||
mode="outlined"
|
||||
value={value}
|
||||
label="Block Explorer URL (Optional)"
|
||||
onBlur={onBlur}
|
||||
onChangeText={(textValue) => onChange(textValue)}
|
||||
/>
|
||||
<HelperText type="error">
|
||||
{errors.blockExplorerUrl?.message}
|
||||
</HelperText>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
{isCosmos && (
|
||||
<Grid item xs={6}>
|
||||
<Controller
|
||||
control={control}
|
||||
name="gasPrice"
|
||||
defaultValue={networkData.gasPrice}
|
||||
render={({ field: { onChange, onBlur, value } }) => (
|
||||
<>
|
||||
<TextInput
|
||||
mode="outlined"
|
||||
value={value}
|
||||
label="Gas Price"
|
||||
onBlur={onBlur}
|
||||
onChangeText={onChange}
|
||||
/>
|
||||
<HelperText type="error">
|
||||
{
|
||||
(
|
||||
errors as FieldErrors<
|
||||
z.infer<typeof cosmosNetworksFormDataSchema>
|
||||
>
|
||||
).gasPrice?.message
|
||||
}
|
||||
</HelperText>
|
||||
</>
|
||||
)}
|
||||
<View style={styles.appContainer}>
|
||||
<View>
|
||||
<Text style={styles.subHeading}>
|
||||
Edit {networkData?.networkName} details
|
||||
</Text>
|
||||
</View>
|
||||
<Controller
|
||||
control={control}
|
||||
defaultValue={networkData.networkName}
|
||||
name="networkName"
|
||||
render={({ field: { onChange, onBlur, value } }) => (
|
||||
<>
|
||||
<TextInput
|
||||
mode="outlined"
|
||||
label="Network Name"
|
||||
value={value}
|
||||
onBlur={onBlur}
|
||||
onChangeText={textValue => onChange(textValue)}
|
||||
/>
|
||||
</Grid>
|
||||
<HelperText type="error">{errors.networkName?.message}</HelperText>
|
||||
</>
|
||||
)}
|
||||
</Grid>
|
||||
<LoadingButton
|
||||
variant="contained"
|
||||
/>
|
||||
<Controller
|
||||
control={control}
|
||||
name="rpcUrl"
|
||||
defaultValue={networkData.rpcUrl}
|
||||
render={({ field: { onChange, onBlur, value } }) => (
|
||||
<>
|
||||
<TextInput
|
||||
mode="outlined"
|
||||
label="New RPC URL"
|
||||
onBlur={onBlur}
|
||||
value={value}
|
||||
onChangeText={textValue => onChange(textValue)}
|
||||
/>
|
||||
<HelperText type="error">{errors.rpcUrl?.message}</HelperText>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Controller
|
||||
control={control}
|
||||
defaultValue={networkData.blockExplorerUrl}
|
||||
name="blockExplorerUrl"
|
||||
render={({ field: { onChange, onBlur, value } }) => (
|
||||
<>
|
||||
<TextInput
|
||||
mode="outlined"
|
||||
value={value}
|
||||
label="Block Explorer URL (Optional)"
|
||||
onBlur={onBlur}
|
||||
onChangeText={textValue => onChange(textValue)}
|
||||
/>
|
||||
<HelperText type="error">
|
||||
{errors.blockExplorerUrl?.message}
|
||||
</HelperText>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
{networkData.namespace === COSMOS && (
|
||||
<Controller
|
||||
control={control}
|
||||
name="gasPrice"
|
||||
defaultValue={networkData.gasPrice}
|
||||
render={({ field: { onChange, onBlur, value } }) => (
|
||||
<>
|
||||
<TextInput
|
||||
mode="outlined"
|
||||
value={value}
|
||||
label="Gas Price"
|
||||
onBlur={onBlur}
|
||||
onChangeText={onChange}
|
||||
/>
|
||||
<HelperText type="error">
|
||||
{
|
||||
(
|
||||
errors as FieldErrors<
|
||||
z.infer<typeof cosmosNetworksFormDataSchema>
|
||||
>
|
||||
).gasPrice?.message
|
||||
}
|
||||
</HelperText>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
<Button
|
||||
mode="contained"
|
||||
loading={isSubmitting}
|
||||
disabled={isSubmitting}
|
||||
onClick={handleSubmit(submit)}
|
||||
sx={{ minWidth: "200px", px: 4, py: 1, mt: 2 }}
|
||||
>
|
||||
{isSubmitting ? "Adding" : "Submit"}
|
||||
</LoadingButton>
|
||||
</Layout>
|
||||
style={styles.networksButton}
|
||||
onPress={handleSubmit(submit)}>
|
||||
{isSubmitting ? 'Adding' : 'Submit'}
|
||||
</Button>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
+87
-107
@@ -1,43 +1,66 @@
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import { View, ActivityIndicator } from "react-native";
|
||||
import { Text } from "react-native-paper";
|
||||
import { Button, Divider, Portal, Snackbar } from "@mui/material";
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { View, ActivityIndicator, Image } from 'react-native';
|
||||
import { Button, Text } from 'react-native-paper';
|
||||
|
||||
import { getSdkError } from "@walletconnect/utils";
|
||||
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { getSdkError } from '@walletconnect/utils';
|
||||
|
||||
import { createWallet, resetWallet, retrieveAccounts } from "../utils/accounts";
|
||||
import { NetworkDropdown } from "../components/NetworkDropdown";
|
||||
import Accounts from "../components/Accounts";
|
||||
import CreateWallet from "../components/CreateWallet";
|
||||
import ConfirmDialog from "../components/ConfirmDialog";
|
||||
import styles from "../styles/stylesheet";
|
||||
import { useAccounts } from "../context/AccountsContext";
|
||||
import { useWalletConnect } from "../context/WalletConnectContext";
|
||||
import { NetworksDataState } from "../types";
|
||||
import { useNetworks } from "../context/NetworksContext";
|
||||
import ImportWalletDialog from "../components/ImportWalletDialog";
|
||||
import { MnemonicDialog } from "../components/MnemonicDialog";
|
||||
import { Container } from "../components/Container";
|
||||
import { IS_IMPORT_WALLET_ENABLED } from "../utils/constants";
|
||||
import { createWallet, resetWallet, retrieveAccounts } from '../utils/accounts';
|
||||
import { DialogComponent } from '../components/Dialog';
|
||||
import { NetworkDropdown } from '../components/NetworkDropdown';
|
||||
import Accounts from '../components/Accounts';
|
||||
import CreateWallet from '../components/CreateWallet';
|
||||
import ConfirmDialog from '../components/ConfirmDialog';
|
||||
import styles from '../styles/stylesheet';
|
||||
import { useAccounts } from '../context/AccountsContext';
|
||||
import { useWalletConnect } from '../context/WalletConnectContext';
|
||||
import { NetworksDataState, StackParamsList } from '../types';
|
||||
import { useNetworks } from '../context/NetworksContext';
|
||||
|
||||
const WCLogo = () => {
|
||||
return (
|
||||
<Image
|
||||
style={styles.walletConnectLogo}
|
||||
source={require('../assets/WalletConnect-Icon-Blueberry.png')}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const HomeScreen = () => {
|
||||
const { setAccounts, setCurrentIndex } = useAccounts();
|
||||
const { accounts, setAccounts, setCurrentIndex } = useAccounts();
|
||||
|
||||
const { networksData, selectedNetwork, setSelectedNetwork, setNetworksData } =
|
||||
useNetworks();
|
||||
const { setActiveSessions, web3wallet } = useWalletConnect();
|
||||
|
||||
const navigation =
|
||||
useNavigation<NativeStackNavigationProp<StackParamsList>>();
|
||||
useEffect(() => {
|
||||
if (accounts.length > 0) {
|
||||
navigation.setOptions({
|
||||
// eslint-disable-next-line react/no-unstable-nested-components
|
||||
headerRight: () => (
|
||||
<Button onPress={() => navigation.navigate('WalletConnect')}>
|
||||
{<WCLogo />}
|
||||
</Button>
|
||||
),
|
||||
});
|
||||
} else {
|
||||
navigation.setOptions({
|
||||
headerRight: undefined,
|
||||
});
|
||||
}
|
||||
}, [navigation, accounts]);
|
||||
|
||||
const [isWalletCreated, setIsWalletCreated] = useState<boolean>(false);
|
||||
const [isWalletCreating, setIsWalletCreating] = useState<boolean>(false);
|
||||
const [importWalletDialog, setImportWalletDialog] = useState<boolean>(false);
|
||||
const [mnemonicDialog, setMnemonicDialog] = useState<boolean>(false);
|
||||
const [walletDialog, setWalletDialog] = useState<boolean>(false);
|
||||
const [resetWalletDialog, setResetWalletDialog] = useState<boolean>(false);
|
||||
const [toastVisible, setToastVisible] = useState(false);
|
||||
const [invalidMnemonicError, setInvalidMnemonicError] = useState("");
|
||||
const [isAccountsFetched, setIsAccountsFetched] = useState<boolean>(true);
|
||||
const [phrase, setPhrase] = useState("");
|
||||
const [phrase, setPhrase] = useState('');
|
||||
|
||||
const hideMnemonicDialog = () => setMnemonicDialog(false);
|
||||
const hideWalletDialog = () => setWalletDialog(false);
|
||||
const hideResetDialog = () => setResetWalletDialog(false);
|
||||
|
||||
const fetchAccounts = useCallback(async () => {
|
||||
@@ -60,27 +83,12 @@ const HomeScreen = () => {
|
||||
const mnemonic = await createWallet(networksData);
|
||||
if (mnemonic) {
|
||||
fetchAccounts();
|
||||
setMnemonicDialog(true);
|
||||
setWalletDialog(true);
|
||||
setPhrase(mnemonic);
|
||||
setSelectedNetwork(networksData[0]);
|
||||
}
|
||||
};
|
||||
|
||||
const importWalletHandler = async (recoveryPhrase: string) => {
|
||||
try {
|
||||
const mnemonic = await createWallet(networksData, recoveryPhrase);
|
||||
if (mnemonic) {
|
||||
fetchAccounts();
|
||||
setPhrase(mnemonic);
|
||||
setSelectedNetwork(networksData[0]);
|
||||
setImportWalletDialog(false);
|
||||
}
|
||||
} catch (error: any) {
|
||||
setInvalidMnemonicError((error.message as string).toUpperCase());
|
||||
setToastVisible(true);
|
||||
}
|
||||
};
|
||||
|
||||
const confirmResetWallet = useCallback(async () => {
|
||||
setIsWalletCreated(false);
|
||||
setIsWalletCreating(false);
|
||||
@@ -91,10 +99,10 @@ const HomeScreen = () => {
|
||||
await resetWallet();
|
||||
const sessions = web3wallet!.getActiveSessions();
|
||||
|
||||
Object.keys(sessions).forEach(async (sessionId) => {
|
||||
Object.keys(sessions).forEach(async sessionId => {
|
||||
await web3wallet!.disconnectSession({
|
||||
topic: sessionId,
|
||||
reason: getSdkError("USER_DISCONNECTED"),
|
||||
reason: getSdkError('USER_DISCONNECTED'),
|
||||
});
|
||||
});
|
||||
setActiveSessions({});
|
||||
@@ -106,7 +114,7 @@ const HomeScreen = () => {
|
||||
setCurrentIndex,
|
||||
setNetworksData,
|
||||
setSelectedNetwork,
|
||||
web3wallet,
|
||||
web3wallet
|
||||
]);
|
||||
|
||||
const updateNetwork = (networkData: NetworksDataState) => {
|
||||
@@ -120,74 +128,46 @@ const HomeScreen = () => {
|
||||
|
||||
return (
|
||||
<View style={styles.appContainer}>
|
||||
<Container>
|
||||
{!isAccountsFetched ? (
|
||||
<View style={styles.spinnerContainer}>
|
||||
<Text style={styles.LoadingText}>Loading...</Text>
|
||||
<ActivityIndicator size="large" color="#0000ff" />
|
||||
{!isAccountsFetched ? (
|
||||
<View style={styles.spinnerContainer}>
|
||||
<Text style={styles.LoadingText}>Loading...</Text>
|
||||
<ActivityIndicator size="large" color="#0000ff" />
|
||||
</View>
|
||||
) : isWalletCreated && selectedNetwork ? (
|
||||
<>
|
||||
<NetworkDropdown updateNetwork={updateNetwork} />
|
||||
<View style={styles.accountComponent}>
|
||||
<Accounts />
|
||||
</View>
|
||||
) : isWalletCreated && selectedNetwork ? (
|
||||
<>
|
||||
<NetworkDropdown updateNetwork={updateNetwork} />
|
||||
<View style={styles.accountComponent}>
|
||||
<Accounts />
|
||||
</View>
|
||||
<Divider flexItem sx={{ my: 4 }} />
|
||||
<View style={styles.resetContainer}>
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={() => {
|
||||
style={styles.resetButton}
|
||||
mode="contained"
|
||||
buttonColor="#B82B0D"
|
||||
onPress={() => {
|
||||
setResetWalletDialog(true);
|
||||
}}
|
||||
color="error"
|
||||
>
|
||||
}}>
|
||||
Reset Wallet
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<CreateWallet
|
||||
isWalletCreating={isWalletCreating}
|
||||
createWalletHandler={createWalletHandler}
|
||||
/>
|
||||
{IS_IMPORT_WALLET_ENABLED && (
|
||||
<View style={styles.createWalletContainer}>
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={() => setImportWalletDialog(true)}
|
||||
>
|
||||
Import Wallet
|
||||
</Button>
|
||||
</View>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<ImportWalletDialog
|
||||
visible={importWalletDialog}
|
||||
hideDialog={() => setImportWalletDialog(false)}
|
||||
importWalletHandler={importWalletHandler}
|
||||
</View>
|
||||
</>
|
||||
) : (
|
||||
<CreateWallet
|
||||
isWalletCreating={isWalletCreating}
|
||||
createWalletHandler={createWalletHandler}
|
||||
/>
|
||||
<MnemonicDialog
|
||||
visible={mnemonicDialog}
|
||||
hideDialog={hideMnemonicDialog}
|
||||
contentText={phrase}
|
||||
/>
|
||||
<ConfirmDialog
|
||||
title="Reset wallet"
|
||||
visible={resetWalletDialog}
|
||||
hideDialog={hideResetDialog}
|
||||
onConfirm={confirmResetWallet}
|
||||
/>
|
||||
</Container>
|
||||
<Portal>
|
||||
<Snackbar
|
||||
open={toastVisible}
|
||||
autoHideDuration={3000}
|
||||
message={invalidMnemonicError}
|
||||
onClose={() => setToastVisible(false)}
|
||||
anchorOrigin={{ horizontal: "center", vertical: "bottom" }}
|
||||
ContentProps={{ style: { backgroundColor: "red", color: "white" } }}
|
||||
/>
|
||||
</Portal>
|
||||
)}
|
||||
<DialogComponent
|
||||
visible={walletDialog}
|
||||
hideDialog={hideWalletDialog}
|
||||
contentText={phrase}
|
||||
/>
|
||||
<ConfirmDialog
|
||||
title="Reset wallet"
|
||||
visible={resetWalletDialog}
|
||||
hideDialog={hideResetDialog}
|
||||
onConfirm={confirmResetWallet}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
+31
-30
@@ -1,22 +1,22 @@
|
||||
import React, { useState } from "react";
|
||||
import { Text, TextInput } from "react-native-paper";
|
||||
import { Button, Divider, Stack } from "@mui/material";
|
||||
import React, { useState } from 'react';
|
||||
import { View } from 'react-native';
|
||||
import { Button, Text, TextInput } from 'react-native-paper';
|
||||
|
||||
import { NativeStackScreenProps } from "@react-navigation/native-stack";
|
||||
import { NativeStackScreenProps } from '@react-navigation/native-stack';
|
||||
|
||||
import { StackParamsList } from "../types";
|
||||
import { signMessage } from "../utils/sign-message";
|
||||
import AccountDetails from "../components/AccountDetails";
|
||||
import { Layout } from "../components/Layout";
|
||||
import { StackParamsList } from '../types';
|
||||
import styles from '../styles/stylesheet';
|
||||
import { signMessage } from '../utils/sign-message';
|
||||
import AccountDetails from '../components/AccountDetails';
|
||||
|
||||
type SignProps = NativeStackScreenProps<StackParamsList, "SignMessage">;
|
||||
type SignProps = NativeStackScreenProps<StackParamsList, 'SignMessage'>;
|
||||
|
||||
const SignMessage = ({ route }: SignProps) => {
|
||||
const namespace = route.params.selectedNamespace;
|
||||
const chainId = route.params.selectedChainId;
|
||||
const account = route.params.accountInfo;
|
||||
|
||||
const [message, setMessage] = useState<string>("");
|
||||
const [message, setMessage] = useState<string>('');
|
||||
|
||||
const signMessageHandler = async () => {
|
||||
const signedMessage = await signMessage({
|
||||
@@ -29,30 +29,31 @@ const SignMessage = ({ route }: SignProps) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<Layout title="Sign Message">
|
||||
<Text variant="titleMedium">
|
||||
{account && `Account ${account.index + 1}`}
|
||||
</Text>
|
||||
<AccountDetails account={account} />
|
||||
<View style={styles.signPage}>
|
||||
<View style={styles.accountInfo}>
|
||||
<View>
|
||||
<Text variant="titleMedium">
|
||||
{account && `Account ${account.index + 1}`}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={styles.accountContainer}>
|
||||
<AccountDetails account={account} />
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<Stack spacing={4}>
|
||||
<Divider flexItem />
|
||||
<TextInput
|
||||
mode="outlined"
|
||||
placeholder="Enter your message"
|
||||
onChangeText={(text) => setMessage(text)}
|
||||
value={message}
|
||||
/>
|
||||
<TextInput
|
||||
mode="outlined"
|
||||
placeholder="Enter your message"
|
||||
onChangeText={text => setMessage(text)}
|
||||
value={message}
|
||||
/>
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={signMessageHandler}
|
||||
sx={{ width: "200px", px: 4, py: 1, mt: 2 }}
|
||||
>
|
||||
<View style={styles.signButton}>
|
||||
<Button mode="contained" onPress={signMessageHandler}>
|
||||
Sign
|
||||
</Button>
|
||||
</Stack>
|
||||
</Layout>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -202,13 +202,7 @@ const SignRequest = ({ route }: SignRequestProps) => {
|
||||
chainId,
|
||||
accountId: account.index,
|
||||
});
|
||||
|
||||
// Send the result back to Android and close dialog
|
||||
if (window.Android?.onSignatureComplete) {
|
||||
window.Android.onSignatureComplete(signedMessage || "");
|
||||
} else {
|
||||
alert(`Signature: ${signedMessage}`);
|
||||
}
|
||||
alert(`Signature ${signedMessage}`);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -236,11 +230,7 @@ const SignRequest = ({ route }: SignRequestProps) => {
|
||||
}
|
||||
|
||||
setIsRejecting(false);
|
||||
if (window.Android?.onSignatureCancelled) {
|
||||
window.Android.onSignatureCancelled();
|
||||
} else {
|
||||
navigation.navigate('Home');
|
||||
}
|
||||
navigation.navigate('Home');
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { Image, TouchableOpacity, View } from "react-native";
|
||||
import { List, Text } from "react-native-paper";
|
||||
import React, { useEffect } from 'react';
|
||||
import { Image, TouchableOpacity, View } from 'react-native';
|
||||
import { List, Text } from 'react-native-paper';
|
||||
|
||||
import { getSdkError } from "@walletconnect/utils";
|
||||
import { getSdkError } from '@walletconnect/utils';
|
||||
|
||||
import { useWalletConnect } from "../context/WalletConnectContext";
|
||||
import styles from "../styles/stylesheet";
|
||||
import { Layout } from "../components/Layout";
|
||||
import { useWalletConnect } from '../context/WalletConnectContext';
|
||||
import styles from '../styles/stylesheet';
|
||||
|
||||
export default function WalletConnect() {
|
||||
const { web3wallet, activeSessions, setActiveSessions } = useWalletConnect();
|
||||
@@ -14,7 +13,7 @@ export default function WalletConnect() {
|
||||
const disconnect = async (sessionId: string) => {
|
||||
await web3wallet!.disconnectSession({
|
||||
topic: sessionId,
|
||||
reason: getSdkError("USER_DISCONNECTED"),
|
||||
reason: getSdkError('USER_DISCONNECTED'),
|
||||
});
|
||||
const sessions = web3wallet?.getActiveSessions() || {};
|
||||
setActiveSessions(sessions);
|
||||
@@ -27,10 +26,12 @@ export default function WalletConnect() {
|
||||
}, [web3wallet, setActiveSessions]);
|
||||
|
||||
return (
|
||||
<Layout title="Active Sessions">
|
||||
<View>
|
||||
{Object.keys(activeSessions).length > 0 ? (
|
||||
<>
|
||||
<View style={styles.sessionsContainer} />
|
||||
<View style={styles.sessionsContainer}>
|
||||
<Text variant="titleMedium">Active Sessions</Text>
|
||||
</View>
|
||||
<List.Section>
|
||||
{Object.entries(activeSessions).map(([sessionId, session]) => (
|
||||
<List.Item
|
||||
@@ -43,7 +44,7 @@ export default function WalletConnect() {
|
||||
// eslint-disable-next-line react/no-unstable-nested-components
|
||||
left={() => (
|
||||
<>
|
||||
{session.peer.metadata.icons[0].endsWith(".svg") ? (
|
||||
{session.peer.metadata.icons[0].endsWith('.svg') ? (
|
||||
<View style={styles.dappLogo}>
|
||||
<Text>SvgURI peerMetaDataIcon</Text>
|
||||
</View>
|
||||
@@ -59,8 +60,7 @@ export default function WalletConnect() {
|
||||
right={() => (
|
||||
<TouchableOpacity
|
||||
onPress={() => disconnect(sessionId)}
|
||||
style={styles.disconnectSession}
|
||||
>
|
||||
style={styles.disconnectSession}>
|
||||
<List.Icon icon="close" />
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
@@ -73,6 +73,6 @@ export default function WalletConnect() {
|
||||
<Text>You have no active sessions</Text>
|
||||
</View>
|
||||
)}
|
||||
</Layout>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,317 +0,0 @@
|
||||
import React, { useEffect, useState, useCallback, useRef } from 'react';
|
||||
import { ScrollView, View } from 'react-native';
|
||||
import {
|
||||
ActivityIndicator,
|
||||
Button,
|
||||
Text,
|
||||
TextInput,
|
||||
} from 'react-native-paper';
|
||||
import { BigNumber } from 'ethers';
|
||||
|
||||
import { DirectSecp256k1Wallet } from '@cosmjs/proto-signing';
|
||||
import {
|
||||
calculateFee,
|
||||
GasPrice,
|
||||
SigningStargateClient,
|
||||
} from '@cosmjs/stargate';
|
||||
|
||||
import { retrieveSingleAccount } from '../utils/accounts';
|
||||
import AccountDetails from '../components/AccountDetails';
|
||||
import styles from '../styles/stylesheet';
|
||||
import DataBox from '../components/DataBox';
|
||||
import { checkSufficientFunds, getPathKey, sendMessage } from '../utils/misc';
|
||||
import { useNetworks } from '../context/NetworksContext';
|
||||
import TxErrorDialog from '../components/TxErrorDialog';
|
||||
import { MEMO } from '../screens/ApproveTransfer';
|
||||
import { Account, NetworksDataState } from '../types';
|
||||
import useGetOrCreateAccounts from '../hooks/useGetOrCreateAccounts';
|
||||
import useAccountsData from '../hooks/useAccountsData';
|
||||
|
||||
type TransactionDetails = {
|
||||
chainId: string;
|
||||
fromAddress: string;
|
||||
toAddress: string;
|
||||
amount: string;
|
||||
account: Account
|
||||
balance: string;
|
||||
requestedNetwork: NetworksDataState
|
||||
};
|
||||
|
||||
export const WalletEmbed = () => {
|
||||
const [isTxRequested, setIsTxRequested] = useState<boolean>(false);
|
||||
const [transactionDetails, setTransactionDetails] = useState<TransactionDetails | null>(null);
|
||||
const [fees, setFees] = useState<string>('');
|
||||
const [gasLimit, setGasLimit] = useState<string>('');
|
||||
const [isTxLoading, setIsTxLoading] = useState(false);
|
||||
const [txError, setTxError] = useState<string | null>(null);
|
||||
const txEventRef = useRef<MessageEvent | null>(null);
|
||||
|
||||
const { networksData } = useNetworks();
|
||||
const { getAccountsData } = useAccountsData();
|
||||
|
||||
useEffect(() => {
|
||||
const handleGetAccounts = async (event: MessageEvent) => {
|
||||
if (event.data.type !== 'REQUEST_WALLET_ACCOUNTS') return;
|
||||
|
||||
const accountsData = await getAccountsData(event.data.chainId);
|
||||
|
||||
if (accountsData.length === 0) {
|
||||
sendMessage(event.source as Window, 'ERROR', 'Wallet accounts not found', event.origin);
|
||||
return;
|
||||
}
|
||||
|
||||
sendMessage(
|
||||
event.source as Window,
|
||||
'WALLET_ACCOUNTS_DATA',
|
||||
accountsData.map(account => account.address),
|
||||
event.origin
|
||||
);
|
||||
};
|
||||
|
||||
window.addEventListener('message', handleGetAccounts);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('message', handleGetAccounts);
|
||||
};
|
||||
}, [getAccountsData]);
|
||||
|
||||
// Custom hook for adding listener to get accounts data
|
||||
useGetOrCreateAccounts();
|
||||
|
||||
const handleTxRequested = useCallback(
|
||||
async (event: MessageEvent) => {
|
||||
try {
|
||||
if (event.data.type !== 'REQUEST_TX') return;
|
||||
|
||||
txEventRef.current = event;
|
||||
|
||||
const { chainId, fromAddress, toAddress, amount } = event.data;
|
||||
const network = networksData.find(net => net.chainId === chainId);
|
||||
|
||||
if (!network) {
|
||||
console.error('Network not found');
|
||||
throw new Error('Requested network not supported.');
|
||||
}
|
||||
|
||||
const account = await retrieveSingleAccount(network.namespace, network.chainId, fromAddress);
|
||||
if (!account) {
|
||||
throw new Error('Account not found for the requested address.');
|
||||
}
|
||||
|
||||
const cosmosPrivKey = (
|
||||
await getPathKey(`${network.namespace}:${chainId}`, account.index)
|
||||
).privKey;
|
||||
|
||||
const sender = await DirectSecp256k1Wallet.fromKey(
|
||||
Buffer.from(cosmosPrivKey.split('0x')[1], 'hex'),
|
||||
network.addressPrefix
|
||||
);
|
||||
|
||||
const client = await SigningStargateClient.connectWithSigner(network.rpcUrl!, sender);
|
||||
|
||||
const balance = await client.getBalance(
|
||||
account.address,
|
||||
network.nativeDenom!.toLowerCase()
|
||||
);
|
||||
|
||||
const sendMsg = {
|
||||
typeUrl: '/cosmos.bank.v1beta1.MsgSend',
|
||||
value: {
|
||||
fromAddress: fromAddress,
|
||||
toAddress: toAddress,
|
||||
amount: [
|
||||
{
|
||||
amount: String(amount),
|
||||
denom: network.nativeDenom!,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
setTransactionDetails({
|
||||
chainId,
|
||||
fromAddress,
|
||||
toAddress,
|
||||
amount,
|
||||
account,
|
||||
balance: balance.amount,
|
||||
requestedNetwork: network,
|
||||
});
|
||||
|
||||
if (!checkSufficientFunds(amount, balance.amount)) {
|
||||
console.log("Insufficient funds detected. Throwing error.");
|
||||
throw new Error('Insufficient funds');
|
||||
}
|
||||
|
||||
const gasEstimation = await client.simulate(fromAddress, [sendMsg], MEMO);
|
||||
const gasLimit = String(
|
||||
Math.round(gasEstimation * Number(process.env.REACT_APP_GAS_ADJUSTMENT))
|
||||
);
|
||||
setGasLimit(gasLimit);
|
||||
|
||||
const gasPrice = GasPrice.fromString(`${network.gasPrice}${network.nativeDenom}`);
|
||||
const cosmosFees = calculateFee(Number(gasLimit), gasPrice);
|
||||
setFees(cosmosFees.amount[0].amount);
|
||||
|
||||
setIsTxRequested(true);
|
||||
} catch (error) {
|
||||
if (!(error instanceof Error)) {
|
||||
throw error;
|
||||
}
|
||||
setTxError(error.message);
|
||||
}
|
||||
}, [networksData]);
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('message', handleTxRequested);
|
||||
return () => window.removeEventListener('message', handleTxRequested);
|
||||
}, [handleTxRequested]);
|
||||
|
||||
const acceptRequestHandler = async () => {
|
||||
try {
|
||||
setIsTxLoading(true);
|
||||
if (!transactionDetails) {
|
||||
throw new Error('Tx details not set');
|
||||
}
|
||||
const balanceBigNum = BigNumber.from(transactionDetails.balance);
|
||||
const amountBigNum = BigNumber.from(String(transactionDetails.amount));
|
||||
if (amountBigNum.gte(balanceBigNum)) {
|
||||
throw new Error('Insufficient funds');
|
||||
}
|
||||
|
||||
const cosmosPrivKey = (
|
||||
await getPathKey(`${transactionDetails.requestedNetwork.namespace}:${transactionDetails.chainId}`, transactionDetails.account.index)
|
||||
).privKey;
|
||||
|
||||
const sender = await DirectSecp256k1Wallet.fromKey(
|
||||
Buffer.from(cosmosPrivKey.split('0x')[1], 'hex'),
|
||||
transactionDetails.requestedNetwork.addressPrefix
|
||||
);
|
||||
|
||||
const client = await SigningStargateClient.connectWithSigner(
|
||||
transactionDetails.requestedNetwork.rpcUrl!,
|
||||
sender
|
||||
);
|
||||
|
||||
const fee = calculateFee(
|
||||
Number(gasLimit),
|
||||
GasPrice.fromString(`${transactionDetails.requestedNetwork.gasPrice}${transactionDetails.requestedNetwork.nativeDenom}`)
|
||||
);
|
||||
|
||||
const txResult = await client.sendTokens(
|
||||
transactionDetails.fromAddress,
|
||||
transactionDetails.toAddress,
|
||||
[{ amount: String(transactionDetails.amount), denom: transactionDetails.requestedNetwork.nativeDenom! }],
|
||||
fee
|
||||
);
|
||||
|
||||
const event = txEventRef.current;
|
||||
if (event?.source) {
|
||||
sendMessage(event.source as Window, 'TRANSACTION_RESPONSE', txResult.transactionHash, event.origin);
|
||||
} else {
|
||||
console.error('No event source available to send message');
|
||||
}
|
||||
} catch (error) {
|
||||
if (!(error instanceof Error)) {
|
||||
throw error;
|
||||
}
|
||||
setTxError(error.message);
|
||||
} finally {
|
||||
setIsTxLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const rejectRequestHandler = () => {
|
||||
const event = txEventRef.current;
|
||||
|
||||
setIsTxRequested(false);
|
||||
setTransactionDetails(null);
|
||||
if (event?.source) {
|
||||
sendMessage(event.source as Window, 'TRANSACTION_RESPONSE', null, event.origin);
|
||||
} else {
|
||||
console.error('No event source available to send message');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{isTxRequested && transactionDetails ? (
|
||||
<>
|
||||
<ScrollView contentContainerStyle={styles.appContainer}>
|
||||
<View style={styles.dataBoxContainer}>
|
||||
<Text style={styles.dataBoxLabel}>From</Text>
|
||||
<View style={styles.dataBox}>
|
||||
<AccountDetails account={transactionDetails.account} />
|
||||
</View>
|
||||
</View>
|
||||
<DataBox
|
||||
label={`Balance (${transactionDetails.requestedNetwork.nativeDenom})`}
|
||||
data={
|
||||
transactionDetails.balance === '' ||
|
||||
transactionDetails.balance === undefined
|
||||
? 'Loading balance...'
|
||||
: `${transactionDetails.balance}`
|
||||
}
|
||||
/>
|
||||
<View style={styles.approveTransfer}>
|
||||
<DataBox label="To" data={transactionDetails.toAddress} />
|
||||
<DataBox
|
||||
label={`Amount (${transactionDetails.requestedNetwork.nativeDenom})`}
|
||||
data={transactionDetails.amount}
|
||||
/>
|
||||
<TextInput
|
||||
mode="outlined"
|
||||
label="Fee"
|
||||
value={fees}
|
||||
onChangeText={setFees}
|
||||
style={styles.transactionFeesInput}
|
||||
/>
|
||||
<TextInput
|
||||
mode="outlined"
|
||||
label="Gas Limit"
|
||||
value={gasLimit}
|
||||
onChangeText={value =>
|
||||
/^\d+$/.test(value) ? setGasLimit(value) : null
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
</ScrollView>
|
||||
<View style={styles.buttonContainer}>
|
||||
<Button
|
||||
mode="contained"
|
||||
onPress={acceptRequestHandler}
|
||||
loading={isTxLoading}
|
||||
disabled={!transactionDetails.balance || !fees || isTxLoading}
|
||||
>
|
||||
{isTxLoading ? 'Processing' : 'Yes'}
|
||||
</Button>
|
||||
<Button
|
||||
mode="contained"
|
||||
onPress={rejectRequestHandler}
|
||||
buttonColor="#B82B0D"
|
||||
disabled={isTxLoading}
|
||||
>
|
||||
No
|
||||
</Button>
|
||||
</View>
|
||||
</>
|
||||
) : (
|
||||
<View style={styles.spinnerContainer}>
|
||||
<View style={{ marginTop: 50 }}></View>
|
||||
<ActivityIndicator size="large" color="#0000ff" />
|
||||
</View>
|
||||
)}
|
||||
<TxErrorDialog
|
||||
error={txError!}
|
||||
visible={!!txError}
|
||||
hideDialog={() => {
|
||||
setTxError(null)
|
||||
if (window.parent) {
|
||||
sendMessage(window.parent, 'TRANSACTION_RESPONSE', null, '*');
|
||||
sendMessage(window.parent, 'closeIframe', null, '*');
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
+80
-128
@@ -1,21 +1,22 @@
|
||||
import { StyleSheet } from "react-native";
|
||||
import { StyleSheet } from 'react-native';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
createWalletContainer: {
|
||||
marginTop: 20,
|
||||
alignSelf: "center",
|
||||
marginBottom: 30,
|
||||
width: 150,
|
||||
alignSelf: 'center',
|
||||
marginBottom: 40
|
||||
},
|
||||
signLink: {
|
||||
alignItems: "flex-end",
|
||||
alignItems: 'flex-end',
|
||||
marginTop: 24,
|
||||
},
|
||||
hyperlink: {
|
||||
fontWeight: "500",
|
||||
textDecorationLine: "underline",
|
||||
fontWeight: '500',
|
||||
textDecorationLine: 'underline',
|
||||
},
|
||||
highlight: {
|
||||
fontWeight: "700",
|
||||
fontWeight: '700',
|
||||
},
|
||||
accountContainer: {
|
||||
padding: 8,
|
||||
@@ -23,32 +24,28 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
addAccountButton: {
|
||||
marginTop: 24,
|
||||
alignSelf: "center",
|
||||
alignSelf: 'center',
|
||||
},
|
||||
accountComponent: {
|
||||
flex: 4,
|
||||
},
|
||||
appSurface: {
|
||||
backgroundColor: "#0f0f0f",
|
||||
},
|
||||
appContainer: {
|
||||
flexGrow: 1,
|
||||
marginTop: 24,
|
||||
paddingHorizontal: 24,
|
||||
backgroundColor: "#0f0f0f",
|
||||
},
|
||||
resetContainer: {
|
||||
flex: 1,
|
||||
justifyContent: "center",
|
||||
justifyContent: 'center',
|
||||
},
|
||||
resetButton: {
|
||||
alignSelf: "center",
|
||||
alignSelf: 'center',
|
||||
},
|
||||
signButton: {
|
||||
marginTop: 20,
|
||||
marginBottom: 20,
|
||||
width: 150,
|
||||
alignSelf: "center",
|
||||
alignSelf: 'center',
|
||||
},
|
||||
signPage: {
|
||||
paddingHorizontal: 24,
|
||||
@@ -74,81 +71,33 @@ const styles = StyleSheet.create({
|
||||
borderRadius: 10,
|
||||
},
|
||||
dialogWarning: {
|
||||
color: "#FFA3A8",
|
||||
},
|
||||
resetDialogTitle: {
|
||||
width: 500,
|
||||
backgroundColor: "#18181A",
|
||||
},
|
||||
resetDialogContent: {
|
||||
backgroundColor: "#18181A",
|
||||
},
|
||||
resetDialogActionRow: {
|
||||
backgroundColor: "#18181A",
|
||||
},
|
||||
button: {
|
||||
color: "#fff",
|
||||
margin: 10,
|
||||
},
|
||||
buttonRed: {
|
||||
backgroundColor: "#B20710",
|
||||
},
|
||||
buttonBlue: {
|
||||
backgroundColor: "#0000F4",
|
||||
},
|
||||
mnemonicTitle: {
|
||||
backgroundColor: "#18181A",
|
||||
},
|
||||
mnemonicContainer: {
|
||||
backgroundColor: "#18181A",
|
||||
},
|
||||
mnemonicDialogWarning: {
|
||||
color: "#FFA3A8",
|
||||
marginTop: 10,
|
||||
},
|
||||
mnemonicButtonRow: {
|
||||
paddingRight: 40,
|
||||
backgroundColor: "#18181A",
|
||||
},
|
||||
mnemonicButton: {
|
||||
backgroundColor: "#0000F4",
|
||||
color: "white",
|
||||
padding: 2,
|
||||
marginBottom: 20,
|
||||
},
|
||||
mnemonicGridContainer: {
|
||||
flexDirection: "row",
|
||||
flexWrap: "wrap",
|
||||
justifyContent: "center",
|
||||
marginTop: 20,
|
||||
paddingBottom: 30,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: "#29292E",
|
||||
color: 'red',
|
||||
},
|
||||
gridContainer: {
|
||||
flexDirection: "row",
|
||||
flexWrap: "wrap",
|
||||
justifyContent: "center",
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
gridItem: {
|
||||
width: "30%",
|
||||
margin: 4,
|
||||
padding: 4,
|
||||
borderRadius: 4,
|
||||
alignItems: "center",
|
||||
justifyContent: "flex-start",
|
||||
backgroundColor: "#29292E",
|
||||
width: '25%',
|
||||
margin: 8,
|
||||
padding: 6,
|
||||
borderWidth: 1,
|
||||
borderColor: '#ccc',
|
||||
borderRadius: 8,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-start',
|
||||
},
|
||||
HDcontainer: {
|
||||
marginTop: 24,
|
||||
paddingHorizontal: 8,
|
||||
},
|
||||
HDrowContainer: {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
},
|
||||
HDtext: {
|
||||
color: "#FBFBFB",
|
||||
color: 'black',
|
||||
fontSize: 18,
|
||||
margin: 4,
|
||||
},
|
||||
@@ -158,15 +107,15 @@ const styles = StyleSheet.create({
|
||||
HDbuttonContainer: {
|
||||
marginTop: 20,
|
||||
width: 200,
|
||||
alignSelf: "flex-start",
|
||||
alignSelf: 'center',
|
||||
},
|
||||
spinnerContainer: {
|
||||
flex: 1,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
LoadingText: {
|
||||
color: "black",
|
||||
color: 'black',
|
||||
fontSize: 18,
|
||||
padding: 10,
|
||||
},
|
||||
@@ -174,9 +123,9 @@ const styles = StyleSheet.create({
|
||||
borderWidth: 1,
|
||||
borderRadius: 5,
|
||||
marginTop: 50,
|
||||
height: "auto",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
height: 'auto',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
padding: 10,
|
||||
},
|
||||
requestDirectMessage: {
|
||||
@@ -185,51 +134,52 @@ const styles = StyleSheet.create({
|
||||
marginTop: 20,
|
||||
marginBottom: 50,
|
||||
height: 500,
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
padding: 8,
|
||||
},
|
||||
approveTransfer: {
|
||||
height: "40%",
|
||||
height: '40%',
|
||||
marginBottom: 30,
|
||||
},
|
||||
buttonContainer: {
|
||||
flexDirection: "row",
|
||||
flexDirection: 'row',
|
||||
marginLeft: 20,
|
||||
marginTop: 10,
|
||||
marginBottom: 10,
|
||||
justifyContent: "space-evenly",
|
||||
justifyContent: 'space-evenly',
|
||||
},
|
||||
badRequestContainer: {
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
padding: 20,
|
||||
},
|
||||
invalidMessageText: {
|
||||
color: "black",
|
||||
color: 'black',
|
||||
fontSize: 16,
|
||||
textAlign: "center",
|
||||
textAlign: 'center',
|
||||
marginBottom: 20,
|
||||
},
|
||||
container: {
|
||||
flex: 1,
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
marginBottom: 10,
|
||||
paddingHorizontal: 20,
|
||||
minHeight: 100,
|
||||
},
|
||||
modalContentContainer: {
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
borderRadius: 34,
|
||||
borderBottomStartRadius: 0,
|
||||
borderBottomEndRadius: 0,
|
||||
borderWidth: 1,
|
||||
width: "100%",
|
||||
height: "50%",
|
||||
position: "absolute",
|
||||
backgroundColor: "#0f0f0f",
|
||||
width: '100%',
|
||||
height: '50%',
|
||||
position: 'absolute',
|
||||
backgroundColor: 'white',
|
||||
bottom: 0,
|
||||
},
|
||||
modalOuterContainer: { flex: 1 },
|
||||
@@ -238,32 +188,32 @@ const styles = StyleSheet.create({
|
||||
height: 50,
|
||||
borderRadius: 8,
|
||||
marginVertical: 16,
|
||||
overflow: "hidden",
|
||||
overflow: 'hidden',
|
||||
},
|
||||
space: {
|
||||
width: 50,
|
||||
},
|
||||
flexRow: {
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
marginTop: 20,
|
||||
paddingHorizontal: 16,
|
||||
marginBottom: 10,
|
||||
},
|
||||
marginVertical8: {
|
||||
marginVertical: 8,
|
||||
textAlign: "center",
|
||||
textAlign: 'center',
|
||||
},
|
||||
subHeading: {
|
||||
textAlign: "center",
|
||||
textAlign: 'center',
|
||||
marginBottom: 10,
|
||||
marginTop: 10,
|
||||
fontSize: 20,
|
||||
fontSize: 20
|
||||
},
|
||||
centerText: {
|
||||
textAlign: "center",
|
||||
textAlign: 'center',
|
||||
},
|
||||
messageBody: {
|
||||
borderWidth: 1,
|
||||
@@ -276,48 +226,46 @@ const styles = StyleSheet.create({
|
||||
marginTop: 20,
|
||||
},
|
||||
dappDetails: {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
},
|
||||
dataBoxContainer: {
|
||||
marginBottom: 10,
|
||||
backgroundColor: "#29292E",
|
||||
border: "none",
|
||||
},
|
||||
dataBoxLabel: {
|
||||
fontSize: 18,
|
||||
fontWeight: "bold",
|
||||
fontWeight: 'bold',
|
||||
marginBottom: 3,
|
||||
color: "white",
|
||||
color: 'black',
|
||||
},
|
||||
dataBox: {
|
||||
borderWidth: 1,
|
||||
borderColor: "#ccc",
|
||||
borderColor: '#ccc',
|
||||
padding: 10,
|
||||
borderRadius: 5,
|
||||
},
|
||||
dataBoxData: {
|
||||
fontSize: 16,
|
||||
color: "white",
|
||||
color: 'black',
|
||||
},
|
||||
transactionText: {
|
||||
padding: 8,
|
||||
fontSize: 18,
|
||||
fontWeight: "bold",
|
||||
color: "black",
|
||||
fontWeight: 'bold',
|
||||
color: 'black',
|
||||
},
|
||||
balancePadding: {
|
||||
padding: 8,
|
||||
},
|
||||
noActiveSessions: {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
marginTop: 20,
|
||||
marginBottom: 20,
|
||||
},
|
||||
disconnectSession: {
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
sessionItem: {
|
||||
paddingLeft: 12,
|
||||
@@ -334,7 +282,7 @@ const styles = StyleSheet.create({
|
||||
margin: 0,
|
||||
},
|
||||
selectNetworkText: {
|
||||
fontWeight: "bold",
|
||||
fontWeight: 'bold',
|
||||
marginVertical: 10,
|
||||
},
|
||||
transactionFeesInput: { marginBottom: 10 },
|
||||
@@ -345,7 +293,7 @@ const styles = StyleSheet.create({
|
||||
paddingVertical: 5,
|
||||
},
|
||||
transactionLabel: {
|
||||
fontWeight: "700",
|
||||
fontWeight: '700',
|
||||
padding: 8,
|
||||
},
|
||||
linkContainer: {
|
||||
@@ -355,6 +303,10 @@ const styles = StyleSheet.create({
|
||||
marginTop: 12,
|
||||
marginBottom: 20,
|
||||
},
|
||||
scrollViewContainer: {
|
||||
height: 60,
|
||||
marginTop: 20,
|
||||
},
|
||||
});
|
||||
|
||||
export default styles;
|
||||
|
||||
@@ -13,10 +13,8 @@ export type StackParamsList = {
|
||||
};
|
||||
SignRequest: {
|
||||
namespace: string;
|
||||
chainId?: string;
|
||||
address: string;
|
||||
message: string;
|
||||
accountInfo?: Account;
|
||||
requestEvent?: Web3WalletTypes.SessionRequest;
|
||||
requestSessionData?: SessionTypes.Struct;
|
||||
};
|
||||
@@ -38,8 +36,6 @@ export type StackParamsList = {
|
||||
requestEvent: Web3WalletTypes.SessionRequest;
|
||||
requestSessionData: SessionTypes.Struct;
|
||||
};
|
||||
"wallet-embed": undefined;
|
||||
"auto-sign-in": undefined;
|
||||
};
|
||||
|
||||
export type Account = {
|
||||
|
||||
+12
-21
@@ -8,10 +8,10 @@ import { utils } from 'ethers';
|
||||
import { HDNode } from 'ethers/lib/utils';
|
||||
|
||||
import {
|
||||
setInternetCredentials,
|
||||
resetInternetCredentials,
|
||||
getInternetCredentials,
|
||||
} from './key-store';
|
||||
setInternetCredentials,
|
||||
resetInternetCredentials,
|
||||
getInternetCredentials,
|
||||
} from './key-store';
|
||||
import { Secp256k1HdWallet } from '@cosmjs/amino';
|
||||
import { AccountData } from '@cosmjs/proto-signing';
|
||||
import { stringToPath } from '@cosmjs/crypto';
|
||||
@@ -27,23 +27,12 @@ import { COSMOS, EIP155 } from './constants';
|
||||
|
||||
const createWallet = async (
|
||||
networksData: NetworksDataState[],
|
||||
recoveryPhrase?: string,
|
||||
): Promise<string> => {
|
||||
const mnemonic = recoveryPhrase ? recoveryPhrase : utils.entropyToMnemonic(utils.randomBytes(16));
|
||||
|
||||
const hdNode = HDNode.fromMnemonic(mnemonic);
|
||||
const mnemonic = utils.entropyToMnemonic(utils.randomBytes(16));
|
||||
await setInternetCredentials('mnemonicServer', 'mnemonic', mnemonic);
|
||||
|
||||
await createWalletFromMnemonic(networksData, hdNode, mnemonic);
|
||||
const hdNode = HDNode.fromMnemonic(mnemonic);
|
||||
|
||||
return mnemonic;
|
||||
};
|
||||
|
||||
const createWalletFromMnemonic = async (
|
||||
networksData: NetworksDataState[],
|
||||
hdNode: HDNode,
|
||||
mnemonic: string
|
||||
): Promise<void> => {
|
||||
for (const network of networksData) {
|
||||
const hdPath = `m/44'/${network.coinType}'/0'/0/0`;
|
||||
const node = hdNode.derivePath(hdPath);
|
||||
@@ -64,7 +53,7 @@ const createWalletFromMnemonic = async (
|
||||
throw new Error('Unsupported namespace');
|
||||
}
|
||||
|
||||
const accountInfo = `${hdPath},${node.privateKey},${node.publicKey},${address}`;
|
||||
const accountInfo = `${hdPath},${node.privateKey},${node.publicKey},${address}`;
|
||||
|
||||
await Promise.all([
|
||||
setInternetCredentials(
|
||||
@@ -84,6 +73,8 @@ const createWalletFromMnemonic = async (
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
return mnemonic;
|
||||
};
|
||||
|
||||
const addAccount = async (
|
||||
@@ -206,9 +197,9 @@ const retrieveAccounts = async (
|
||||
return;
|
||||
}
|
||||
const loadedAccounts = await retrieveAccountsForNetwork(
|
||||
`${currentNetworkData.namespace}:${currentNetworkData.chainId}`,
|
||||
accountIndices,
|
||||
)
|
||||
`${currentNetworkData.namespace}:${currentNetworkData.chainId}`,
|
||||
accountIndices,
|
||||
)
|
||||
|
||||
return loadedAccounts;
|
||||
};
|
||||
|
||||
+7
-23
@@ -1,34 +1,20 @@
|
||||
import { COSMOS_TESTNET_CHAINS } from './wallet-connect/COSMOSData';
|
||||
import { EIP155_CHAINS } from './wallet-connect/EIP155Data';
|
||||
import { NetworksFormData } from '../types';
|
||||
|
||||
export const EIP155 = 'eip155';
|
||||
export const COSMOS = 'cosmos';
|
||||
|
||||
export const DEFAULT_NETWORKS: NetworksFormData[] = [
|
||||
export const DEFAULT_NETWORKS = [
|
||||
{
|
||||
chainId: 'laconic-testnet-2',
|
||||
networkName: 'laconicd testnet-2',
|
||||
chainId: 'laconic_9000-1',
|
||||
networkName: 'laconicd',
|
||||
namespace: COSMOS,
|
||||
rpcUrl: process.env.REACT_APP_LACONICD_RPC_URL!,
|
||||
blockExplorerUrl: '',
|
||||
nativeDenom: 'alnt',
|
||||
addressPrefix: 'laconic',
|
||||
coinType: '118',
|
||||
gasPrice: '0.001',
|
||||
isDefault: true,
|
||||
},
|
||||
{
|
||||
chainId: 'laconic_9000-1',
|
||||
networkName: 'laconicd',
|
||||
namespace: COSMOS,
|
||||
rpcUrl: "https://laconicd.laconic.com",
|
||||
blockExplorerUrl: '',
|
||||
nativeDenom: 'alnt',
|
||||
addressPrefix: 'laconic',
|
||||
coinType: '118',
|
||||
gasPrice: '1',
|
||||
isDefault: false,
|
||||
isDefault: true,
|
||||
},
|
||||
{
|
||||
chainId: '1',
|
||||
@@ -41,10 +27,10 @@ export const DEFAULT_NETWORKS: NetworksFormData[] = [
|
||||
isDefault: true,
|
||||
},
|
||||
{
|
||||
chainId: 'provider',
|
||||
networkName: COSMOS_TESTNET_CHAINS['cosmos:provider'].name,
|
||||
chainId: 'theta-testnet-001',
|
||||
networkName: COSMOS_TESTNET_CHAINS['cosmos:theta-testnet-001'].name,
|
||||
namespace: COSMOS,
|
||||
rpcUrl: COSMOS_TESTNET_CHAINS['cosmos:provider'].rpc,
|
||||
rpcUrl: COSMOS_TESTNET_CHAINS['cosmos:theta-testnet-001'].rpc,
|
||||
blockExplorerUrl: '',
|
||||
nativeDenom: 'uatom',
|
||||
addressPrefix: 'cosmos',
|
||||
@@ -60,5 +46,3 @@ export const EMPTY_FIELD_ERROR = 'Field cannot be empty';
|
||||
export const INVALID_URL_ERROR = 'Invalid URL';
|
||||
|
||||
export const IS_NUMBER_REGEX = /^\d+$/;
|
||||
|
||||
export const IS_IMPORT_WALLET_ENABLED = false;
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
const setInternetCredentials = (name:string, username:string, password:string) => {
|
||||
localStorage.setItem(name, password);
|
||||
localStorage.setItem(name, password);
|
||||
};
|
||||
|
||||
const getInternetCredentials = (name:string) : string | null => {
|
||||
return localStorage.getItem(name);
|
||||
return localStorage.getItem(name);
|
||||
};
|
||||
|
||||
const resetInternetCredentials = (name:string) => {
|
||||
localStorage.removeItem(name);
|
||||
localStorage.removeItem(name);
|
||||
};
|
||||
|
||||
export {
|
||||
setInternetCredentials,
|
||||
getInternetCredentials,
|
||||
resetInternetCredentials
|
||||
setInternetCredentials,
|
||||
getInternetCredentials,
|
||||
resetInternetCredentials
|
||||
}
|
||||
|
||||
+4
-22
@@ -1,11 +1,7 @@
|
||||
/* Importing this library provides react native with a secure random source.
|
||||
For more information, "visit https://docs.ethers.org/v5/cookbook/react-native/#cookbook-reactnative-security" */
|
||||
import 'react-native-get-random-values';
|
||||
import { BigNumber } from 'ethers';
|
||||
|
||||
import { AccountData } from '@cosmjs/amino';
|
||||
import { DirectSecp256k1HdWallet } from '@cosmjs/proto-signing';
|
||||
import { stringToPath } from '@cosmjs/crypto';
|
||||
import '@ethersproject/shims';
|
||||
|
||||
import {
|
||||
@@ -13,6 +9,10 @@ import {
|
||||
resetInternetCredentials,
|
||||
setInternetCredentials,
|
||||
} from './key-store';
|
||||
|
||||
import { AccountData } from '@cosmjs/amino';
|
||||
import { DirectSecp256k1HdWallet } from '@cosmjs/proto-signing';
|
||||
import { stringToPath } from '@cosmjs/crypto';
|
||||
import { EIP155 } from './constants';
|
||||
import { NetworksDataState } from '../types';
|
||||
|
||||
@@ -149,28 +149,10 @@ const resetKeyServers = async (namespace: string) => {
|
||||
});
|
||||
};
|
||||
|
||||
const sendMessage = (
|
||||
source: Window | null,
|
||||
type: string,
|
||||
data: any,
|
||||
origin: string
|
||||
): void => {
|
||||
source?.postMessage({ type, data }, origin);
|
||||
};
|
||||
|
||||
const checkSufficientFunds = (amount: string, balance: string) => {
|
||||
const amountBigNum = BigNumber.from(String(amount));
|
||||
const balanceBigNum = BigNumber.from(balance);
|
||||
|
||||
return balanceBigNum.gte(amountBigNum);
|
||||
};
|
||||
|
||||
export {
|
||||
getMnemonic,
|
||||
getPathKey,
|
||||
updateAccountIndices,
|
||||
getHDPath,
|
||||
resetKeyServers,
|
||||
sendMessage,
|
||||
checkSufficientFunds,
|
||||
};
|
||||
|
||||
@@ -3,7 +3,6 @@ For more information, "visit https://docs.ethers.org/v5/cookbook/react-native/#c
|
||||
import 'react-native-get-random-values';
|
||||
|
||||
import '@ethersproject/shims';
|
||||
import { fromBech32 } from '@cosmjs/encoding';
|
||||
|
||||
import { Wallet } from 'ethers';
|
||||
import { SignDoc } from 'cosmjs-types/cosmos/tx/v1beta1/tx';
|
||||
@@ -25,7 +24,7 @@ const signMessage = async ({
|
||||
case EIP155:
|
||||
return await signEthMessage(message, accountId, chainId);
|
||||
case COSMOS:
|
||||
return await signCosmosMessage(message, path.path, path.address);
|
||||
return await signCosmosMessage(message, path.path);
|
||||
default:
|
||||
throw new Error('Invalid wallet type');
|
||||
}
|
||||
@@ -52,13 +51,10 @@ const signEthMessage = async (
|
||||
const signCosmosMessage = async (
|
||||
message: string,
|
||||
path: string,
|
||||
cosmosAddress: string,
|
||||
): Promise<string | undefined> => {
|
||||
try {
|
||||
const mnemonic = await getMnemonic();
|
||||
const addressPrefix = fromBech32(cosmosAddress).prefix
|
||||
|
||||
const cosmosAccount = await getCosmosAccounts(mnemonic, path, addressPrefix);
|
||||
const cosmosAccount = await getCosmosAccounts(mnemonic, path);
|
||||
const address = cosmosAccount.data.address;
|
||||
const cosmosSignature = await cosmosAccount.cosmosWallet.signAmino(
|
||||
address,
|
||||
|
||||
@@ -19,10 +19,10 @@ export const COSMOS_TESTNET_CHAINS: Record<
|
||||
namespace: string;
|
||||
}
|
||||
> = {
|
||||
'cosmos:provider': {
|
||||
chainId: 'provider',
|
||||
'cosmos:theta-testnet-001': {
|
||||
chainId: 'theta-testnet-001',
|
||||
name: 'Cosmos Hub Testnet',
|
||||
rpc: 'https://rpc-rs.cosmos.nodestake.top',
|
||||
rpc: 'https://rpc-t.cosmos.nodestake.top',
|
||||
namespace: 'cosmos',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -7,9 +7,8 @@ import { getSdkError } from '@walletconnect/utils';
|
||||
import {
|
||||
SigningStargateClient,
|
||||
StdFee,
|
||||
MsgSendEncodeObject
|
||||
MsgSendEncodeObject,
|
||||
} from '@cosmjs/stargate';
|
||||
import { fromBech32 } from '@cosmjs/encoding';
|
||||
import { EncodeObject } from '@cosmjs/proto-signing';
|
||||
import { LaconicClient } from '@cerc-io/registry-sdk';
|
||||
import { Buffer } from 'buffer';
|
||||
@@ -20,7 +19,6 @@ import { Account } from '../../types';
|
||||
import { getMnemonic, getPathKey } from '../misc';
|
||||
import { getCosmosAccounts } from '../accounts';
|
||||
import { COSMOS_METHODS } from './COSMOSData';
|
||||
import { COSMOS } from '../constants';
|
||||
|
||||
interface EthSendTransaction {
|
||||
type: 'eth_sendTransaction';
|
||||
@@ -82,13 +80,7 @@ export async function approveWalletConnectRequest(
|
||||
const path = (await getPathKey(`${namespace}:${chainId}`, account.index))
|
||||
.path;
|
||||
const mnemonic = await getMnemonic();
|
||||
|
||||
let addressPrefix: string | undefined
|
||||
if (namespace === COSMOS) {
|
||||
addressPrefix = fromBech32(account.address).prefix
|
||||
}
|
||||
|
||||
const cosmosAccount = await getCosmosAccounts(mnemonic, path, addressPrefix);
|
||||
const cosmosAccount = await getCosmosAccounts(mnemonic, path);
|
||||
const address = account.address;
|
||||
|
||||
switch (request.method) {
|
||||
@@ -105,18 +97,18 @@ export async function approveWalletConnectRequest(
|
||||
const updatedTransaction =
|
||||
options.maxFeePerGas && options.maxPriorityFeePerGas
|
||||
? {
|
||||
...sendTransaction,
|
||||
gasLimit: options.ethGasLimit,
|
||||
maxFeePerGas: options.maxFeePerGas,
|
||||
maxPriorityFeePerGas: options.maxPriorityFeePerGas,
|
||||
type: 2,
|
||||
}
|
||||
...sendTransaction,
|
||||
gasLimit: options.ethGasLimit,
|
||||
maxFeePerGas: options.maxFeePerGas,
|
||||
maxPriorityFeePerGas: options.maxPriorityFeePerGas,
|
||||
type: 2,
|
||||
}
|
||||
: {
|
||||
...sendTransaction,
|
||||
gasLimit: options.ethGasLimit,
|
||||
gasPrice: options.ethGasPrice,
|
||||
type: 0,
|
||||
};
|
||||
...sendTransaction,
|
||||
gasLimit: options.ethGasLimit,
|
||||
gasPrice: options.ethGasPrice,
|
||||
type: 0,
|
||||
};
|
||||
|
||||
const connectedWallet = wallet.connect(options.provider);
|
||||
|
||||
|
||||
@@ -6,11 +6,9 @@ services:
|
||||
environment:
|
||||
CERC_SCRIPT_DEBUG: ${CERC_SCRIPT_DEBUG}
|
||||
WALLET_CONNECT_ID: ${WALLET_CONNECT_ID}
|
||||
WALLET_CONNECT_VERIFY_CODE: ${WALLET_CONNECT_VERIFY_CODE}
|
||||
CERC_DEFAULT_GAS_PRICE: ${CERC_DEFAULT_GAS_PRICE:-0.025}
|
||||
CERC_GAS_ADJUSTMENT: ${CERC_GAS_ADJUSTMENT:-2}
|
||||
CERC_LACONICD_RPC_URL: ${CERC_LACONICD_RPC_URL:-https://laconicd.laconic.com}
|
||||
CERC_DEPLOY_APP_URL: ${CERC_DEPLOY_APP_URL}
|
||||
command: ["bash", "/scripts/run.sh"]
|
||||
volumes:
|
||||
- ../config/app/run.sh:/scripts/run.sh
|
||||
|
||||
@@ -10,23 +10,12 @@ echo "WALLET_CONNECT_ID: ${WALLET_CONNECT_ID}"
|
||||
echo "CERC_DEFAULT_GAS_PRICE: ${CERC_DEFAULT_GAS_PRICE}"
|
||||
echo "CERC_GAS_ADJUSTMENT: ${CERC_GAS_ADJUSTMENT}"
|
||||
echo "CERC_LACONICD_RPC_URL: ${CERC_LACONICD_RPC_URL}"
|
||||
echo "CERC_DEPLOY_APP_URL: ${CERC_DEPLOY_APP_URL}"
|
||||
|
||||
# Build with required env
|
||||
REACT_APP_WALLET_CONNECT_PROJECT_ID=$WALLET_CONNECT_ID \
|
||||
REACT_APP_DEFAULT_GAS_PRICE=$CERC_DEFAULT_GAS_PRICE \
|
||||
REACT_APP_GAS_ADJUSTMENT=$CERC_GAS_ADJUSTMENT \
|
||||
REACT_APP_LACONICD_RPC_URL=$CERC_LACONICD_RPC_URL \
|
||||
REACT_APP_DEPLOY_APP_URL=$CERC_DEPLOY_APP_URL \
|
||||
yarn build
|
||||
|
||||
# Define the directory and file path
|
||||
FILE_PATH="/app/build/.well-known/walletconnect.txt"
|
||||
|
||||
# Create the directory if it doesn't exist
|
||||
mkdir -p "$(dirname "$FILE_PATH")"
|
||||
# Write verification code to the file
|
||||
echo "$WALLET_CONNECT_VERIFY_CODE" > "$FILE_PATH"
|
||||
|
||||
# Serve build dir
|
||||
http-server --proxy http://localhost:80? -p 80 /app/build
|
||||
|
||||
@@ -51,9 +51,6 @@ Instructions for running the `laconic-wallet-web` using [laconic-so](https://git
|
||||
|
||||
# Optional
|
||||
|
||||
# WalletConnect code for hostname verification
|
||||
WALLET_CONNECT_VERIFY_CODE=
|
||||
|
||||
# Default gas price for txs (default: 0.025)
|
||||
CERC_DEFAULT_GAS_PRICE=
|
||||
|
||||
@@ -63,10 +60,6 @@ Instructions for running the `laconic-wallet-web` using [laconic-so](https://git
|
||||
|
||||
# RPC endpoint of laconicd node (default: https://laconicd.laconic.com)
|
||||
CERC_LACONICD_RPC_URL=
|
||||
|
||||
# Deploy app URL used for checking origin of the messages for auto-sign-in route
|
||||
# Deploy app repo: https://git.vdb.to/cerc-io/snowballtools-base
|
||||
CERC_DEPLOY_APP_URL=
|
||||
```
|
||||
|
||||
## Start the deployment
|
||||
|
||||
@@ -1746,14 +1746,14 @@
|
||||
resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz#5e13fac887f08c44f76b0ccaf3370eb00fec9bb6"
|
||||
integrity sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==
|
||||
|
||||
"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0":
|
||||
"@eslint-community/eslint-utils@^4.2.0":
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59"
|
||||
integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==
|
||||
dependencies:
|
||||
eslint-visitor-keys "^3.3.0"
|
||||
|
||||
"@eslint-community/regexpp@^4.4.0", "@eslint-community/regexpp@^4.5.1", "@eslint-community/regexpp@^4.6.1":
|
||||
"@eslint-community/regexpp@^4.4.0", "@eslint-community/regexpp@^4.6.1":
|
||||
version "4.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.11.0.tgz#b0ffd0312b4a3fd2d6f77237e7248a5ad3a680ae"
|
||||
integrity sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==
|
||||
@@ -2125,33 +2125,6 @@
|
||||
"@ethersproject/properties" "^5.7.0"
|
||||
"@ethersproject/strings" "^5.7.0"
|
||||
|
||||
"@floating-ui/core@^1.6.0":
|
||||
version "1.6.7"
|
||||
resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.6.7.tgz#7602367795a390ff0662efd1c7ae8ca74e75fb12"
|
||||
integrity sha512-yDzVT/Lm101nQ5TCVeK65LtdN7Tj4Qpr9RTXJ2vPFLqtLxwOrpoxAHAJI8J3yYWUc40J0BDBheaitK5SJmno2g==
|
||||
dependencies:
|
||||
"@floating-ui/utils" "^0.2.7"
|
||||
|
||||
"@floating-ui/dom@^1.0.0":
|
||||
version "1.6.10"
|
||||
resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.6.10.tgz#b74c32f34a50336c86dcf1f1c845cf3a39e26d6f"
|
||||
integrity sha512-fskgCFv8J8OamCmyun8MfjB1Olfn+uZKjOKZ0vhYF3gRmEUXcGOjxWL8bBr7i4kIuPZ2KD2S3EUIOxnjC8kl2A==
|
||||
dependencies:
|
||||
"@floating-ui/core" "^1.6.0"
|
||||
"@floating-ui/utils" "^0.2.7"
|
||||
|
||||
"@floating-ui/react-dom@^2.0.8":
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-2.1.1.tgz#cca58b6b04fc92b4c39288252e285e0422291fb0"
|
||||
integrity sha512-4h84MJt3CHrtG18mGsXuLCHMrug49d7DFkU0RMIyshRveBeyV2hmV/pDaF2Uxtu8kgq5r46llp5E5FQiR0K2Yg==
|
||||
dependencies:
|
||||
"@floating-ui/dom" "^1.0.0"
|
||||
|
||||
"@floating-ui/utils@^0.2.7":
|
||||
version "0.2.7"
|
||||
resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.7.tgz#d0ece53ce99ab5a8e37ebdfe5e32452a2bfc073e"
|
||||
integrity sha512-X8R8Oj771YRl/w+c1HqAC1szL8zWQRwFvgDwT129k9ACdBoud/+/rX9V0qiMl6LWUdP9voC2nDVZYPMQQsb6eA==
|
||||
|
||||
"@hapi/hoek@^9.0.0", "@hapi/hoek@^9.3.0":
|
||||
version "9.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb"
|
||||
@@ -2584,44 +2557,11 @@
|
||||
tweetnacl "^1.0.3"
|
||||
tweetnacl-util "^0.15.1"
|
||||
|
||||
"@mui/base@5.0.0-beta.40":
|
||||
version "5.0.0-beta.40"
|
||||
resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-beta.40.tgz#1f8a782f1fbf3f84a961e954c8176b187de3dae2"
|
||||
integrity sha512-I/lGHztkCzvwlXpjD2+SNmvNQvB4227xBXhISPjEaJUXGImOQ9f3D2Yj/T3KasSI/h0MLWy74X0J6clhPmsRbQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.23.9"
|
||||
"@floating-ui/react-dom" "^2.0.8"
|
||||
"@mui/types" "^7.2.14"
|
||||
"@mui/utils" "^5.15.14"
|
||||
"@popperjs/core" "^2.11.8"
|
||||
clsx "^2.1.0"
|
||||
prop-types "^15.8.1"
|
||||
|
||||
"@mui/core-downloads-tracker@^5.16.4":
|
||||
version "5.16.4"
|
||||
resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-5.16.4.tgz#a34de72acd7e81fdbcc7eeb07786205e90dda148"
|
||||
integrity sha512-rNdHXhclwjEZnK+//3SR43YRx0VtjdHnUFhMSGYmAMJve+KiwEja/41EYh8V3pZKqF2geKyfcFUenTfDTYUR4w==
|
||||
|
||||
"@mui/icons-material@^5.16.7":
|
||||
version "5.16.7"
|
||||
resolved "https://registry.yarnpkg.com/@mui/icons-material/-/icons-material-5.16.7.tgz#e27f901af792065efc9f3d75d74a66af7529a10a"
|
||||
integrity sha512-UrGwDJCXEszbDI7yV047BYU5A28eGJ79keTCP4cc74WyncuVrnurlmIRxaHL8YK+LI1Kzq+/JM52IAkNnv4u+Q==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.23.9"
|
||||
|
||||
"@mui/lab@^5.0.0-alpha.173":
|
||||
version "5.0.0-alpha.173"
|
||||
resolved "https://registry.yarnpkg.com/@mui/lab/-/lab-5.0.0-alpha.173.tgz#a0f9696d93a765b48d69a7da5aaca0affa510ae8"
|
||||
integrity sha512-Gt5zopIWwxDgGy/MXcp6GueD84xFFugFai4hYiXY0zowJpTVnIrTQCQXV004Q7rejJ7aaCntX9hpPJqCrioshA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.23.9"
|
||||
"@mui/base" "5.0.0-beta.40"
|
||||
"@mui/system" "^5.16.5"
|
||||
"@mui/types" "^7.2.15"
|
||||
"@mui/utils" "^5.16.5"
|
||||
clsx "^2.1.0"
|
||||
prop-types "^15.8.1"
|
||||
|
||||
"@mui/material@^5.16.4":
|
||||
version "5.16.4"
|
||||
resolved "https://registry.yarnpkg.com/@mui/material/-/material-5.16.4.tgz#992d630637d9d38620e4937fb11d0a97965fdabf"
|
||||
@@ -2649,15 +2589,6 @@
|
||||
"@mui/utils" "^5.16.4"
|
||||
prop-types "^15.8.1"
|
||||
|
||||
"@mui/private-theming@^5.16.6":
|
||||
version "5.16.6"
|
||||
resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-5.16.6.tgz#547671e7ae3f86b68d1289a0b90af04dfcc1c8c9"
|
||||
integrity sha512-rAk+Rh8Clg7Cd7shZhyt2HGTTE5wYKNSJ5sspf28Fqm/PZ69Er9o6KX25g03/FG2dfpg5GCwZh/xOojiTfm3hw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.23.9"
|
||||
"@mui/utils" "^5.16.6"
|
||||
prop-types "^15.8.1"
|
||||
|
||||
"@mui/styled-engine@^5.16.4":
|
||||
version "5.16.4"
|
||||
resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-5.16.4.tgz#a7a8c9079c307bab91ccd65ed5dd1496ddf2a3ab"
|
||||
@@ -2668,16 +2599,6 @@
|
||||
csstype "^3.1.3"
|
||||
prop-types "^15.8.1"
|
||||
|
||||
"@mui/styled-engine@^5.16.6":
|
||||
version "5.16.6"
|
||||
resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-5.16.6.tgz#60110c106dd482dfdb7e2aa94fd6490a0a3f8852"
|
||||
integrity sha512-zaThmS67ZmtHSWToTiHslbI8jwrmITcN93LQaR2lKArbvS7Z3iLkwRoiikNWutx9MBs8Q6okKvbZq1RQYB3v7g==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.23.9"
|
||||
"@emotion/cache" "^11.11.0"
|
||||
csstype "^3.1.3"
|
||||
prop-types "^15.8.1"
|
||||
|
||||
"@mui/system@^5.16.4":
|
||||
version "5.16.4"
|
||||
resolved "https://registry.yarnpkg.com/@mui/system/-/system-5.16.4.tgz#c03f971ed273f0ad06c69c949c05e866ad211407"
|
||||
@@ -2692,37 +2613,11 @@
|
||||
csstype "^3.1.3"
|
||||
prop-types "^15.8.1"
|
||||
|
||||
"@mui/system@^5.16.5":
|
||||
version "5.16.7"
|
||||
resolved "https://registry.yarnpkg.com/@mui/system/-/system-5.16.7.tgz#4583ca5bf3b38942e02c15a1e622ba869ac51393"
|
||||
integrity sha512-Jncvs/r/d/itkxh7O7opOunTqbbSSzMTHzZkNLM+FjAOg+cYAZHrPDlYe1ZGKUYORwwb2XexlWnpZp0kZ4AHuA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.23.9"
|
||||
"@mui/private-theming" "^5.16.6"
|
||||
"@mui/styled-engine" "^5.16.6"
|
||||
"@mui/types" "^7.2.15"
|
||||
"@mui/utils" "^5.16.6"
|
||||
clsx "^2.1.0"
|
||||
csstype "^3.1.3"
|
||||
prop-types "^15.8.1"
|
||||
|
||||
"@mui/types@^7.2.14", "@mui/types@^7.2.15":
|
||||
"@mui/types@^7.2.15":
|
||||
version "7.2.15"
|
||||
resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.2.15.tgz#dadd232fe9a70be0d526630675dff3b110f30b53"
|
||||
integrity sha512-nbo7yPhtKJkdf9kcVOF8JZHPZTmqXjJ/tI0bdWgHg5tp9AnIN4Y7f7wm9T+0SyGYJk76+GYZ8Q5XaTYAsUHN0Q==
|
||||
|
||||
"@mui/utils@^5.15.14", "@mui/utils@^5.16.5", "@mui/utils@^5.16.6":
|
||||
version "5.16.6"
|
||||
resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-5.16.6.tgz#905875bbc58d3dcc24531c3314a6807aba22a711"
|
||||
integrity sha512-tWiQqlhxAt3KENNiSRL+DIn9H5xNVK6Jjf70x3PnfQPz1MPBdh7yyIcAyVBT9xiw7hP3SomRhPR7hzBMBCjqEA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.23.9"
|
||||
"@mui/types" "^7.2.15"
|
||||
"@types/prop-types" "^15.7.12"
|
||||
clsx "^2.1.1"
|
||||
prop-types "^15.8.1"
|
||||
react-is "^18.3.1"
|
||||
|
||||
"@mui/utils@^5.16.4":
|
||||
version "5.16.4"
|
||||
resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-5.16.4.tgz#8e50e27a630e3d8eeb3e9d3bc31cbb0e4956f5fd"
|
||||
@@ -3939,7 +3834,7 @@
|
||||
expect "^29.0.0"
|
||||
pretty-format "^29.0.0"
|
||||
|
||||
"@types/json-schema@*", "@types/json-schema@^7.0.12", "@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9":
|
||||
"@types/json-schema@*", "@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9":
|
||||
version "7.0.15"
|
||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
|
||||
integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==
|
||||
@@ -4078,7 +3973,7 @@
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/semver@^7.3.12", "@types/semver@^7.5.0":
|
||||
"@types/semver@^7.3.12":
|
||||
version "7.5.8"
|
||||
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e"
|
||||
integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==
|
||||
@@ -4180,23 +4075,6 @@
|
||||
semver "^7.3.7"
|
||||
tsutils "^3.21.0"
|
||||
|
||||
"@typescript-eslint/eslint-plugin@^6.13.2":
|
||||
version "6.21.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz#30830c1ca81fd5f3c2714e524c4303e0194f9cd3"
|
||||
integrity sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==
|
||||
dependencies:
|
||||
"@eslint-community/regexpp" "^4.5.1"
|
||||
"@typescript-eslint/scope-manager" "6.21.0"
|
||||
"@typescript-eslint/type-utils" "6.21.0"
|
||||
"@typescript-eslint/utils" "6.21.0"
|
||||
"@typescript-eslint/visitor-keys" "6.21.0"
|
||||
debug "^4.3.4"
|
||||
graphemer "^1.4.0"
|
||||
ignore "^5.2.4"
|
||||
natural-compare "^1.4.0"
|
||||
semver "^7.5.4"
|
||||
ts-api-utils "^1.0.1"
|
||||
|
||||
"@typescript-eslint/experimental-utils@^5.0.0":
|
||||
version "5.62.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-5.62.0.tgz#14559bf73383a308026b427a4a6129bae2146741"
|
||||
@@ -4214,17 +4092,6 @@
|
||||
"@typescript-eslint/typescript-estree" "5.62.0"
|
||||
debug "^4.3.4"
|
||||
|
||||
"@typescript-eslint/parser@^6.13.2":
|
||||
version "6.21.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.21.0.tgz#af8fcf66feee2edc86bc5d1cf45e33b0630bf35b"
|
||||
integrity sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==
|
||||
dependencies:
|
||||
"@typescript-eslint/scope-manager" "6.21.0"
|
||||
"@typescript-eslint/types" "6.21.0"
|
||||
"@typescript-eslint/typescript-estree" "6.21.0"
|
||||
"@typescript-eslint/visitor-keys" "6.21.0"
|
||||
debug "^4.3.4"
|
||||
|
||||
"@typescript-eslint/scope-manager@5.62.0":
|
||||
version "5.62.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz#d9457ccc6a0b8d6b37d0eb252a23022478c5460c"
|
||||
@@ -4233,14 +4100,6 @@
|
||||
"@typescript-eslint/types" "5.62.0"
|
||||
"@typescript-eslint/visitor-keys" "5.62.0"
|
||||
|
||||
"@typescript-eslint/scope-manager@6.21.0":
|
||||
version "6.21.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz#ea8a9bfc8f1504a6ac5d59a6df308d3a0630a2b1"
|
||||
integrity sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "6.21.0"
|
||||
"@typescript-eslint/visitor-keys" "6.21.0"
|
||||
|
||||
"@typescript-eslint/type-utils@5.62.0":
|
||||
version "5.62.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz#286f0389c41681376cdad96b309cedd17d70346a"
|
||||
@@ -4251,26 +4110,11 @@
|
||||
debug "^4.3.4"
|
||||
tsutils "^3.21.0"
|
||||
|
||||
"@typescript-eslint/type-utils@6.21.0":
|
||||
version "6.21.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz#6473281cfed4dacabe8004e8521cee0bd9d4c01e"
|
||||
integrity sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==
|
||||
dependencies:
|
||||
"@typescript-eslint/typescript-estree" "6.21.0"
|
||||
"@typescript-eslint/utils" "6.21.0"
|
||||
debug "^4.3.4"
|
||||
ts-api-utils "^1.0.1"
|
||||
|
||||
"@typescript-eslint/types@5.62.0":
|
||||
version "5.62.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f"
|
||||
integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==
|
||||
|
||||
"@typescript-eslint/types@6.21.0":
|
||||
version "6.21.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.21.0.tgz#205724c5123a8fef7ecd195075fa6e85bac3436d"
|
||||
integrity sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==
|
||||
|
||||
"@typescript-eslint/typescript-estree@5.62.0":
|
||||
version "5.62.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b"
|
||||
@@ -4284,20 +4128,6 @@
|
||||
semver "^7.3.7"
|
||||
tsutils "^3.21.0"
|
||||
|
||||
"@typescript-eslint/typescript-estree@6.21.0":
|
||||
version "6.21.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz#c47ae7901db3b8bddc3ecd73daff2d0895688c46"
|
||||
integrity sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "6.21.0"
|
||||
"@typescript-eslint/visitor-keys" "6.21.0"
|
||||
debug "^4.3.4"
|
||||
globby "^11.1.0"
|
||||
is-glob "^4.0.3"
|
||||
minimatch "9.0.3"
|
||||
semver "^7.5.4"
|
||||
ts-api-utils "^1.0.1"
|
||||
|
||||
"@typescript-eslint/utils@5.62.0", "@typescript-eslint/utils@^5.58.0":
|
||||
version "5.62.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.62.0.tgz#141e809c71636e4a75daa39faed2fb5f4b10df86"
|
||||
@@ -4312,19 +4142,6 @@
|
||||
eslint-scope "^5.1.1"
|
||||
semver "^7.3.7"
|
||||
|
||||
"@typescript-eslint/utils@6.21.0":
|
||||
version "6.21.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.21.0.tgz#4714e7a6b39e773c1c8e97ec587f520840cd8134"
|
||||
integrity sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==
|
||||
dependencies:
|
||||
"@eslint-community/eslint-utils" "^4.4.0"
|
||||
"@types/json-schema" "^7.0.12"
|
||||
"@types/semver" "^7.5.0"
|
||||
"@typescript-eslint/scope-manager" "6.21.0"
|
||||
"@typescript-eslint/types" "6.21.0"
|
||||
"@typescript-eslint/typescript-estree" "6.21.0"
|
||||
semver "^7.5.4"
|
||||
|
||||
"@typescript-eslint/visitor-keys@5.62.0":
|
||||
version "5.62.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz#2174011917ce582875954ffe2f6912d5931e353e"
|
||||
@@ -4333,14 +4150,6 @@
|
||||
"@typescript-eslint/types" "5.62.0"
|
||||
eslint-visitor-keys "^3.3.0"
|
||||
|
||||
"@typescript-eslint/visitor-keys@6.21.0":
|
||||
version "6.21.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz#87a99d077aa507e20e238b11d56cc26ade45fe47"
|
||||
integrity sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "6.21.0"
|
||||
eslint-visitor-keys "^3.4.1"
|
||||
|
||||
"@ungap/structured-clone@^1.2.0":
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406"
|
||||
@@ -7292,7 +7101,7 @@ eslint-plugin-react-hooks@^4.3.0:
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz#c829eb06c0e6f484b3fbb85a97e57784f328c596"
|
||||
integrity sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==
|
||||
|
||||
eslint-plugin-react@^7.27.1, eslint-plugin-react@^7.33.2:
|
||||
eslint-plugin-react@^7.27.1:
|
||||
version "7.35.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.35.0.tgz#00b1e4559896710e58af6358898f2ff917ea4c41"
|
||||
integrity sha512-v501SSMOWv8gerHkk+IIQBkcGRGrO2nfybfj5pLxuJNFTPxxA3PSryhXTK+9pNbtkggheDdsC0E9Q8CuPk6JKA==
|
||||
@@ -8514,11 +8323,6 @@ human-signals@^5.0.0:
|
||||
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-5.0.0.tgz#42665a284f9ae0dade3ba41ebc37eb4b852f3a28"
|
||||
integrity sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==
|
||||
|
||||
husky@^9.0.11:
|
||||
version "9.1.4"
|
||||
resolved "https://registry.yarnpkg.com/husky/-/husky-9.1.4.tgz#926fd19c18d345add5eab0a42b2b6d9a80259b34"
|
||||
integrity sha512-bho94YyReb4JV7LYWRWxZ/xr6TtOTt8cMfmQ39MQYJ7f/YE268s3GdghGwi+y4zAeqewE5zYLvuhV0M0ijsDEA==
|
||||
|
||||
hyphenate-style-name@^1.0.3:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.1.0.tgz#1797bf50369588b47b72ca6d5e65374607cf4436"
|
||||
@@ -8565,7 +8369,7 @@ ieee754@^1.1.13, ieee754@^1.2.1:
|
||||
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
|
||||
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
|
||||
|
||||
ignore@^5.2.0, ignore@^5.2.4:
|
||||
ignore@^5.2.0:
|
||||
version "5.3.1"
|
||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef"
|
||||
integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==
|
||||
@@ -10570,13 +10374,6 @@ minimalistic-crypto-utils@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
|
||||
integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==
|
||||
|
||||
minimatch@9.0.3:
|
||||
version "9.0.3"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825"
|
||||
integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==
|
||||
dependencies:
|
||||
brace-expansion "^2.0.1"
|
||||
|
||||
minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
|
||||
@@ -13849,11 +13646,6 @@ tryer@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8"
|
||||
integrity sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==
|
||||
|
||||
ts-api-utils@^1.0.1:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz#4b490e27129f1e8e686b45cc4ab63714dc60eea1"
|
||||
integrity sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==
|
||||
|
||||
ts-interface-checker@^0.1.9:
|
||||
version "0.1.13"
|
||||
resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699"
|
||||
|
||||
Reference in New Issue
Block a user