Compare commits
No commits in common. "main" and "v0.1.2" have entirely different histories.
@ -1,9 +1,5 @@
|
|||||||
REACT_APP_WALLET_CONNECT_PROJECT_ID=
|
REACT_APP_WALLET_CONNECT_PROJECT_ID=
|
||||||
|
|
||||||
REACT_APP_DEFAULT_GAS_PRICE=0.025
|
REACT_APP_DEFAULT_GAS_PRICE=0.025
|
||||||
# Reference: https://github.com/cosmos/cosmos-sdk/issues/16020
|
# Reference: https://github.com/cosmos/cosmos-sdk/issues/16020
|
||||||
REACT_APP_GAS_ADJUSTMENT=2
|
REACT_APP_GAS_ADJUSTMENT=2
|
||||||
REACT_APP_LACONICD_RPC_URL=https://laconicd-sapo.laconic.com
|
REACT_APP_LACONICD_RPC_URL=https://laconicd.laconic.com
|
||||||
|
|
||||||
# Example: https://example-url-1.com,https://example-url-2.com
|
|
||||||
REACT_APP_ALLOWED_URLS=
|
|
||||||
|
@ -1 +0,0 @@
|
|||||||
build
|
|
@ -31,11 +31,7 @@ module.exports = function override(config, env) {
|
|||||||
config.plugins.push(
|
config.plugins.push(
|
||||||
new webpack.ProvidePlugin({
|
new webpack.ProvidePlugin({
|
||||||
Buffer: ["buffer", "Buffer"],
|
Buffer: ["buffer", "Buffer"],
|
||||||
}),
|
})
|
||||||
require("@import-meta-env/unplugin").webpack({
|
|
||||||
example: ".env.example",
|
|
||||||
env: ".env",
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
config.module.rules.push({
|
config.module.rules.push({
|
||||||
|
13
package.json
13
package.json
@ -1,12 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "web-wallet",
|
"name": "web-wallet",
|
||||||
"version": "0.1.7",
|
"version": "0.1.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cerc-io/registry-sdk": "^0.2.5",
|
"@cerc-io/registry-sdk": "^0.2.5",
|
||||||
"@cosmjs/amino": "^0.32.3",
|
"@cosmjs/amino": "^0.32.3",
|
||||||
"@cosmjs/crypto": "^0.32.3",
|
"@cosmjs/crypto": "^0.32.3",
|
||||||
"@cosmjs/encoding": "^0.33.1",
|
|
||||||
"@cosmjs/proto-signing": "^0.32.3",
|
"@cosmjs/proto-signing": "^0.32.3",
|
||||||
"@cosmjs/stargate": "^0.32.3",
|
"@cosmjs/stargate": "^0.32.3",
|
||||||
"@emotion/react": "^11.13.0",
|
"@emotion/react": "^11.13.0",
|
||||||
@ -29,7 +28,6 @@
|
|||||||
"cosmjs-types": "^0.9.0",
|
"cosmjs-types": "^0.9.0",
|
||||||
"ethers": "5.7.2",
|
"ethers": "5.7.2",
|
||||||
"https-browserify": "^1.0.0",
|
"https-browserify": "^1.0.0",
|
||||||
"json-bigint": "^1.0.0",
|
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
@ -52,7 +50,6 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "react-app-rewired start",
|
"start": "react-app-rewired start",
|
||||||
"build": "react-app-rewired build",
|
"build": "react-app-rewired build",
|
||||||
"set-env": "import-meta-env -x .env.example -p build/index.html",
|
|
||||||
"test": "react-app-rewired test",
|
"test": "react-app-rewired test",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"prepare": "husky",
|
"prepare": "husky",
|
||||||
@ -78,10 +75,6 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/plugin-transform-modules-commonjs": "^7.24.8",
|
"@babel/plugin-transform-modules-commonjs": "^7.24.8",
|
||||||
"@import-meta-env/cli": "^0.7.3",
|
|
||||||
"@import-meta-env/typescript": "^0.4.0",
|
|
||||||
"@import-meta-env/unplugin": "^0.6.2",
|
|
||||||
"@types/json-bigint": "^1.0.4",
|
|
||||||
"@types/lodash": "^4.17.7",
|
"@types/lodash": "^4.17.7",
|
||||||
"@types/node": "^16.7.13",
|
"@types/node": "^16.7.13",
|
||||||
"@types/react": "^18.0.0",
|
"@types/react": "^18.0.0",
|
||||||
@ -89,7 +82,6 @@
|
|||||||
"@typescript-eslint/eslint-plugin": "^6.13.2",
|
"@typescript-eslint/eslint-plugin": "^6.13.2",
|
||||||
"@typescript-eslint/parser": "^6.13.2",
|
"@typescript-eslint/parser": "^6.13.2",
|
||||||
"crypto-browserify": "^3.12.0",
|
"crypto-browserify": "^3.12.0",
|
||||||
"dotenv": "^16.5.0",
|
|
||||||
"eslint": "^8.3.0",
|
"eslint": "^8.3.0",
|
||||||
"eslint-config-react-app": "^7.0.1",
|
"eslint-config-react-app": "^7.0.1",
|
||||||
"eslint-plugin-react": "^7.33.2",
|
"eslint-plugin-react": "^7.33.2",
|
||||||
@ -97,6 +89,5 @@
|
|||||||
"husky": "^9.0.11",
|
"husky": "^9.0.11",
|
||||||
"react-app-rewired": "^2.2.1",
|
"react-app-rewired": "^2.2.1",
|
||||||
"stream-browserify": "^3.0.0"
|
"stream-browserify": "^3.0.0"
|
||||||
},
|
}
|
||||||
"packageManager": "yarn@1.22.19+sha1.4ba7fc5c6e704fce2066ecbfb0b0d8976fe62447"
|
|
||||||
}
|
}
|
||||||
|
@ -28,9 +28,6 @@
|
|||||||
Learn how to configure a non-root public URL by running `npm run build`.
|
Learn how to configure a non-root public URL by running `npm run build`.
|
||||||
-->
|
-->
|
||||||
<title>Laconic Wallet</title>
|
<title>Laconic Wallet</title>
|
||||||
<script>
|
|
||||||
globalThis.import_meta_env = JSON.parse('"import_meta_env_placeholder"');
|
|
||||||
</script>
|
|
||||||
<style>
|
<style>
|
||||||
#app {
|
#app {
|
||||||
background-color: #0f0f0f;
|
background-color: #0f0f0f;
|
||||||
|
101
src/App.tsx
101
src/App.tsx
@ -4,8 +4,6 @@ import { TxBody, AuthInfo } from "cosmjs-types/cosmos/tx/v1beta1/tx";
|
|||||||
|
|
||||||
import { SignClientTypes } from "@walletconnect/types";
|
import { SignClientTypes } from "@walletconnect/types";
|
||||||
import { useNavigation } from "@react-navigation/native";
|
import { useNavigation } from "@react-navigation/native";
|
||||||
import { DirectSecp256k1Wallet } from "@cosmjs/proto-signing";
|
|
||||||
import { SigningStargateClient } from "@cosmjs/stargate";
|
|
||||||
import {
|
import {
|
||||||
createStackNavigator,
|
createStackNavigator,
|
||||||
StackNavigationProp,
|
StackNavigationProp,
|
||||||
@ -30,22 +28,12 @@ import { getSignParamsMessage } from "./utils/wallet-connect/helpers";
|
|||||||
import ApproveTransfer from "./screens/ApproveTransfer";
|
import ApproveTransfer from "./screens/ApproveTransfer";
|
||||||
import AddNetwork from "./screens/AddNetwork";
|
import AddNetwork from "./screens/AddNetwork";
|
||||||
import EditNetwork from "./screens/EditNetwork";
|
import EditNetwork from "./screens/EditNetwork";
|
||||||
import { CHECK_BALANCE, COSMOS, EIP155, IS_SUFFICIENT } from "./utils/constants";
|
import { COSMOS, EIP155 } from "./utils/constants";
|
||||||
import { useNetworks } from "./context/NetworksContext";
|
import { useNetworks } from "./context/NetworksContext";
|
||||||
import { NETWORK_METHODS } from "./utils/wallet-connect/common-data";
|
import { NETWORK_METHODS } from "./utils/wallet-connect/common-data";
|
||||||
import { COSMOS_METHODS } from "./utils/wallet-connect/COSMOSData";
|
import { COSMOS_METHODS } from "./utils/wallet-connect/COSMOSData";
|
||||||
import styles from "./styles/stylesheet";
|
import styles from "./styles/stylesheet";
|
||||||
import { Header } from "./components/Header";
|
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 SignRequestEmbed from "./screens/SignRequestEmbed";
|
|
||||||
import useAddAccountEmbed from "./hooks/useAddAccountEmbed";
|
|
||||||
import useExportPKEmbed from "./hooks/useExportPrivateKeyEmbed";
|
|
||||||
import { SignTxEmbed } from "./screens/SignTxEmbed";
|
|
||||||
import useCreateNetwork from "./hooks/useCreateNetwork";
|
|
||||||
|
|
||||||
const Stack = createStackNavigator<StackParamsList>();
|
const Stack = createStackNavigator<StackParamsList>();
|
||||||
|
|
||||||
@ -55,8 +43,6 @@ const App = (): React.JSX.Element => {
|
|||||||
const { web3wallet, setActiveSessions } = useWalletConnect();
|
const { web3wallet, setActiveSessions } = useWalletConnect();
|
||||||
const { accounts, setCurrentIndex } = useAccounts();
|
const { accounts, setCurrentIndex } = useAccounts();
|
||||||
const { networksData, selectedNetwork, setSelectedNetwork } = useNetworks();
|
const { networksData, selectedNetwork, setSelectedNetwork } = useNetworks();
|
||||||
const { getAccountsData } = useAccountsData();
|
|
||||||
|
|
||||||
const [modalVisible, setModalVisible] = useState(false);
|
const [modalVisible, setModalVisible] = useState(false);
|
||||||
const [toastVisible, setToastVisible] = useState(false);
|
const [toastVisible, setToastVisible] = useState(false);
|
||||||
const [currentProposal, setCurrentProposal] = useState<
|
const [currentProposal, setCurrentProposal] = useState<
|
||||||
@ -231,65 +217,8 @@ const App = (): React.JSX.Element => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
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]);
|
const showWalletConnect = useMemo(() => accounts.length > 0, [accounts]);
|
||||||
|
|
||||||
useWebViewHandler();
|
|
||||||
useAddAccountEmbed();
|
|
||||||
useExportPKEmbed();
|
|
||||||
useCreateNetwork();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Surface style={styles.appSurface}>
|
<Surface style={styles.appSurface}>
|
||||||
<Stack.Navigator
|
<Stack.Navigator
|
||||||
@ -384,34 +313,6 @@ const App = (): React.JSX.Element => {
|
|||||||
header: () => <Header title="Wallet" />,
|
header: () => <Header title="Wallet" />,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Stack.Screen
|
|
||||||
name="wallet-embed"
|
|
||||||
component={WalletEmbed}
|
|
||||||
options={{
|
|
||||||
header: () => <></>,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Stack.Screen
|
|
||||||
name="sign-tx-request-embed"
|
|
||||||
component={SignTxEmbed}
|
|
||||||
options={{
|
|
||||||
header: () => <></>,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Stack.Screen
|
|
||||||
name="auto-sign-in"
|
|
||||||
component={AutoSignIn}
|
|
||||||
options={{
|
|
||||||
header: () => <></>,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Stack.Screen
|
|
||||||
name="sign-message-request-embed"
|
|
||||||
component={SignRequestEmbed}
|
|
||||||
options={{
|
|
||||||
header: () => <Header title="Wallet" />,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Stack.Navigator>
|
</Stack.Navigator>
|
||||||
<PairingModal
|
<PairingModal
|
||||||
visible={modalVisible}
|
visible={modalVisible}
|
||||||
|
@ -85,7 +85,7 @@ const Accounts = () => {
|
|||||||
|
|
||||||
const addAccountHandler = async () => {
|
const addAccountHandler = async () => {
|
||||||
setIsAccountCreating(true);
|
setIsAccountCreating(true);
|
||||||
const newAccount = await addAccount(selectedNetwork!.chainId);
|
const newAccount = await addAccount(selectedNetwork!);
|
||||||
setIsAccountCreating(false);
|
setIsAccountCreating(false);
|
||||||
if (newAccount) {
|
if (newAccount) {
|
||||||
updateAccounts(newAccount);
|
updateAccounts(newAccount);
|
||||||
|
@ -4,7 +4,7 @@ import { Account } from '../types';
|
|||||||
|
|
||||||
const AccountsContext = createContext<{
|
const AccountsContext = createContext<{
|
||||||
accounts: Account[];
|
accounts: Account[];
|
||||||
setAccounts: React.Dispatch<React.SetStateAction<Account[]>>;
|
setAccounts: (account: Account[]) => void;
|
||||||
currentIndex: number;
|
currentIndex: number;
|
||||||
setCurrentIndex: (index: number) => void;
|
setCurrentIndex: (index: number) => void;
|
||||||
}>({
|
}>({
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
import React, { createContext, useContext, useEffect, useState } from 'react';
|
import React, { createContext, useContext, useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { NetworksDataState } from '../types';
|
import { NetworksDataState } from '../types';
|
||||||
import { retrieveNetworksData } from '../utils/accounts';
|
import { retrieveNetworksData, storeNetworkData } from '../utils/accounts';
|
||||||
import { DEFAULT_NETWORKS, EIP155 } from '../utils/constants';
|
import { DEFAULT_NETWORKS, EIP155 } from '../utils/constants';
|
||||||
import { setInternetCredentials } from '../utils/key-store';
|
|
||||||
|
|
||||||
const NetworksContext = createContext<{
|
const NetworksContext = createContext<{
|
||||||
networksData: NetworksDataState[];
|
networksData: NetworksDataState[];
|
||||||
@ -28,38 +27,28 @@ const useNetworks = () => {
|
|||||||
return networksContext;
|
return networksContext;
|
||||||
};
|
};
|
||||||
|
|
||||||
const DEFAULT_NETWORKS_DATA = DEFAULT_NETWORKS.map((defaultNetwork, index) => (
|
|
||||||
{
|
|
||||||
...defaultNetwork,
|
|
||||||
networkId: index.toString()
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
const NetworksProvider = ({ children }: { children: React.ReactNode }) => {
|
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 [networkType, setNetworkType] = useState<string>(EIP155);
|
||||||
const [selectedNetwork, setSelectedNetwork] = useState<NetworksDataState>();
|
const [selectedNetwork, setSelectedNetwork] = useState<NetworksDataState>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
let retrievedNetworks = await retrieveNetworksData();
|
const retrievedNetworks = await retrieveNetworksData();
|
||||||
|
|
||||||
if (retrievedNetworks.length === 0) {
|
if (retrievedNetworks.length === 0) {
|
||||||
setInternetCredentials(
|
for (const defaultNetwork of DEFAULT_NETWORKS) {
|
||||||
'networks',
|
await storeNetworkData(defaultNetwork);
|
||||||
'_',
|
}
|
||||||
JSON.stringify(DEFAULT_NETWORKS_DATA),
|
|
||||||
);
|
|
||||||
|
|
||||||
retrievedNetworks = DEFAULT_NETWORKS_DATA;
|
|
||||||
}
|
}
|
||||||
|
const retrievedNewNetworks = await retrieveNetworksData();
|
||||||
setNetworksData(retrievedNetworks);
|
setNetworksData(retrievedNewNetworks);
|
||||||
setSelectedNetwork(retrievedNetworks[0]);
|
setSelectedNetwork(retrievedNewNetworks[0]);
|
||||||
};
|
};
|
||||||
|
|
||||||
fetchData();
|
if (networksData.length === 0) {
|
||||||
}, []);
|
fetchData();
|
||||||
|
}
|
||||||
|
}, [networksData]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setSelectedNetwork(prevSelectedNetwork => {
|
setSelectedNetwork(prevSelectedNetwork => {
|
||||||
|
24
src/global.d.ts
vendored
24
src/global.d.ts
vendored
@ -1,24 +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;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Handles incoming signature requests from Android
|
|
||||||
receiveSignRequestFromAndroid?: (message: 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,59 +0,0 @@
|
|||||||
import { useEffect, useCallback } from 'react';
|
|
||||||
|
|
||||||
import { useNetworks } from '../context/NetworksContext';
|
|
||||||
import { sendMessage } from '../utils/misc';
|
|
||||||
import useAccountsData from '../hooks/useAccountsData';
|
|
||||||
import { addAccount } from '../utils/accounts';
|
|
||||||
import { useAccounts } from '../context/AccountsContext';
|
|
||||||
import { Account, NetworksDataState } from '../types';
|
|
||||||
import { ADD_ACCOUNT_RESPONSE, REQUEST_ADD_ACCOUNT } from '../utils/constants';
|
|
||||||
|
|
||||||
const REACT_APP_ALLOWED_URLS = import.meta.env.REACT_APP_ALLOWED_URLS;
|
|
||||||
|
|
||||||
const useAddAccountEmbed = () => {
|
|
||||||
const { networksData } = useNetworks();
|
|
||||||
const { setAccounts, setCurrentIndex } = useAccounts();
|
|
||||||
const { getAccountsData } = useAccountsData();
|
|
||||||
|
|
||||||
const addAccountHandler = useCallback(async (network: NetworksDataState) => {
|
|
||||||
const newAccount = await addAccount(network.chainId);
|
|
||||||
if (newAccount) {
|
|
||||||
setAccounts(prev => [...prev, newAccount]);
|
|
||||||
setCurrentIndex(newAccount.index);
|
|
||||||
}
|
|
||||||
}, [setAccounts, setCurrentIndex]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const handleAddAccount = async (event: MessageEvent) => {
|
|
||||||
if (event.data.type !== REQUEST_ADD_ACCOUNT) return;
|
|
||||||
|
|
||||||
if (!REACT_APP_ALLOWED_URLS) {
|
|
||||||
console.log('Unauthorized app origin:', event.origin);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const allowedUrls = REACT_APP_ALLOWED_URLS.split(',').map(url => url.trim());
|
|
||||||
|
|
||||||
if (!allowedUrls.includes(event.origin)) {
|
|
||||||
console.log('Unauthorized app.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const network = networksData.find(network => network.chainId === event.data.chainId);
|
|
||||||
|
|
||||||
await addAccountHandler(network!);
|
|
||||||
const updatedAccounts = await getAccountsData(event.data.chainId);
|
|
||||||
const addresses = updatedAccounts.map((account: Account) => account.address);
|
|
||||||
|
|
||||||
sendMessage(event.source as Window, ADD_ACCOUNT_RESPONSE, addresses, event.origin);
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener('message', handleAddAccount);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener('message', handleAddAccount);
|
|
||||||
};
|
|
||||||
}, [networksData, getAccountsData, addAccountHandler]);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default useAddAccountEmbed;
|
|
@ -1,99 +0,0 @@
|
|||||||
import { useEffect, useCallback } from "react";
|
|
||||||
|
|
||||||
import { addNewNetwork, createWallet, checkNetworkForChainID, isWalletCreated } from "../utils/accounts";
|
|
||||||
import { useNetworks } from "../context/NetworksContext";
|
|
||||||
import { NETWORK_ADDED_RESPONSE, NETWORK_ADD_FAILED_RESPONSE, NETWORK_ALREADY_EXISTS_RESPONSE, REQUEST_ADD_NETWORK } from "../utils/constants";
|
|
||||||
import { NetworksFormData } from "../types";
|
|
||||||
import { sendMessage } from "../utils/misc";
|
|
||||||
|
|
||||||
const REACT_APP_ALLOWED_URLS = import.meta.env.REACT_APP_ALLOWED_URLS;
|
|
||||||
|
|
||||||
const useCreateNetwork = () => {
|
|
||||||
const { networksData, setNetworksData } = useNetworks();
|
|
||||||
|
|
||||||
const getOrCreateNetwork = useCallback(
|
|
||||||
async (chainId: string, networkData: NetworksFormData, sourceOrigin?: string) => {
|
|
||||||
if (!sourceOrigin) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const isCreated = await isWalletCreated();
|
|
||||||
|
|
||||||
if (!isCreated) {
|
|
||||||
console.log("Wallet not found, creating wallet...");
|
|
||||||
await createWallet(networksData);
|
|
||||||
}
|
|
||||||
|
|
||||||
const isNetworkPresent = await checkNetworkForChainID(chainId);
|
|
||||||
|
|
||||||
if (!isNetworkPresent) {
|
|
||||||
console.log("ChainId not found. Adding network");
|
|
||||||
|
|
||||||
const resolvedNetworkData = {
|
|
||||||
chainId,
|
|
||||||
namespace: networkData.namespace,
|
|
||||||
networkName: networkData.networkName,
|
|
||||||
rpcUrl: networkData.rpcUrl,
|
|
||||||
blockExplorerUrl: networkData.blockExplorerUrl || "",
|
|
||||||
addressPrefix: networkData.addressPrefix || "",
|
|
||||||
coinType: networkData.coinType,
|
|
||||||
nativeDenom: networkData.nativeDenom || "",
|
|
||||||
gasPrice: networkData.gasPrice || String(import.meta.env.REACT_APP_DEFAULT_GAS_PRICE),
|
|
||||||
currencySymbol: networkData.currencySymbol || "",
|
|
||||||
isDefault: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
const retrievedNetworksData = await addNewNetwork(resolvedNetworkData);
|
|
||||||
setNetworksData(retrievedNetworksData);
|
|
||||||
|
|
||||||
sendMessage(window.parent, NETWORK_ADDED_RESPONSE, {
|
|
||||||
type: NETWORK_ADDED_RESPONSE,
|
|
||||||
chainId
|
|
||||||
}, sourceOrigin);
|
|
||||||
} else {
|
|
||||||
sendMessage(window.parent, NETWORK_ALREADY_EXISTS_RESPONSE, {
|
|
||||||
type: NETWORK_ALREADY_EXISTS_RESPONSE,
|
|
||||||
chainId
|
|
||||||
}, sourceOrigin);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error in getOrCreateNetwork:", error);
|
|
||||||
sendMessage(window.parent, NETWORK_ADD_FAILED_RESPONSE, {
|
|
||||||
type: NETWORK_ADD_FAILED_RESPONSE,
|
|
||||||
message: error instanceof Error ? error.message : "Unknown error"
|
|
||||||
}, sourceOrigin);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[networksData, setNetworksData]
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const handleCreateNetwork = async (event: MessageEvent) => {
|
|
||||||
if (event.data.type !== REQUEST_ADD_NETWORK) return;
|
|
||||||
|
|
||||||
if (!REACT_APP_ALLOWED_URLS) {
|
|
||||||
console.log("Allowed URLs are not set");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const allowedUrls = REACT_APP_ALLOWED_URLS.split(",").map(url => url.trim());
|
|
||||||
|
|
||||||
if (!allowedUrls.includes(event.origin)) {
|
|
||||||
console.log("Unauthorized app.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { chainId, networkData } = event.data;
|
|
||||||
|
|
||||||
await getOrCreateNetwork(chainId, networkData, event.origin);
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener("message", handleCreateNetwork);
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener("message", handleCreateNetwork);
|
|
||||||
};
|
|
||||||
}, [getOrCreateNetwork]);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default useCreateNetwork;
|
|
@ -1,43 +0,0 @@
|
|||||||
import { useEffect } from 'react';
|
|
||||||
|
|
||||||
import { useAccounts } from '../context/AccountsContext';
|
|
||||||
import { getPathKey, sendMessage } from '../utils/misc';
|
|
||||||
import { ACCOUNT_PK_RESPONSE, REQUEST_ACCOUNT_PK } from '../utils/constants';
|
|
||||||
|
|
||||||
const useExportPKEmbed = () => {
|
|
||||||
const { accounts } = useAccounts();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const handleMessage = async (event: MessageEvent) => {
|
|
||||||
const { type, chainId, address } = event.data;
|
|
||||||
|
|
||||||
if (type !== REQUEST_ACCOUNT_PK) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const selectedAccount = accounts.find(account => account.address === address);
|
|
||||||
if (!selectedAccount) {
|
|
||||||
throw new Error("Account not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
const pathKey = await getPathKey(chainId, selectedAccount.index);
|
|
||||||
const privateKey = pathKey.privKey;
|
|
||||||
|
|
||||||
sendMessage(
|
|
||||||
event.source as Window,
|
|
||||||
ACCOUNT_PK_RESPONSE,
|
|
||||||
{ privateKey },
|
|
||||||
event.origin,
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error fetching private key:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener('message', handleMessage);
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener('message', handleMessage);
|
|
||||||
};
|
|
||||||
}, [accounts]);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default useExportPKEmbed;
|
|
@ -1,92 +0,0 @@
|
|||||||
import { useEffect, useCallback } from "react";
|
|
||||||
|
|
||||||
import { createWallet, isWalletCreated } from "../utils/accounts";
|
|
||||||
import { sendMessage } from "../utils/misc";
|
|
||||||
import useAccountsData from "./useAccountsData";
|
|
||||||
import { useNetworks } from "../context/NetworksContext";
|
|
||||||
import { useAccounts } from "../context/AccountsContext";
|
|
||||||
import { REQUEST_CREATE_OR_GET_ACCOUNTS, WALLET_ACCOUNTS_DATA } from "../utils/constants";
|
|
||||||
|
|
||||||
const REACT_APP_ALLOWED_URLS = import.meta.env.REACT_APP_ALLOWED_URLS;
|
|
||||||
|
|
||||||
const useGetOrCreateAccounts = () => {
|
|
||||||
const { networksData } = useNetworks();
|
|
||||||
const { getAccountsData } = useAccountsData();
|
|
||||||
const { setAccounts } = useAccounts();
|
|
||||||
|
|
||||||
// Wrap the function in useCallback to prevent recreation on each render
|
|
||||||
const getOrCreateAccountsForChain = useCallback(async (chainId: string) => {
|
|
||||||
const isCreated = await isWalletCreated();
|
|
||||||
|
|
||||||
if (!isCreated) {
|
|
||||||
console.log("Accounts not found, creating wallet...");
|
|
||||||
await createWallet(networksData);
|
|
||||||
}
|
|
||||||
|
|
||||||
const accountsData = await getAccountsData(chainId);
|
|
||||||
|
|
||||||
// Update the AccountsContext with the new accounts
|
|
||||||
setAccounts(accountsData);
|
|
||||||
|
|
||||||
return accountsData;
|
|
||||||
}, [networksData, getAccountsData, setAccounts]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const handleCreateAccounts = async (event: MessageEvent) => {
|
|
||||||
if (event.data.type !== REQUEST_CREATE_OR_GET_ACCOUNTS) return;
|
|
||||||
|
|
||||||
if (!REACT_APP_ALLOWED_URLS) {
|
|
||||||
console.log('Allowed URLs are not set');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const allowedUrls = REACT_APP_ALLOWED_URLS.split(',').map(url => url.trim());
|
|
||||||
|
|
||||||
if (!allowedUrls.includes(event.origin)) {
|
|
||||||
console.log('Unauthorized app.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const accountsData = await getOrCreateAccountsForChain(event.data.chainId);
|
|
||||||
const accountsAddressList = accountsData.map(account => account.address);
|
|
||||||
console.log('Sending WALLET_ACCOUNTS_DATA accounts:', accountsAddressList);
|
|
||||||
|
|
||||||
sendMessage(
|
|
||||||
event.source as Window, WALLET_ACCOUNTS_DATA,
|
|
||||||
accountsAddressList,
|
|
||||||
event.origin
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const autoCreateAccounts = async () => {
|
|
||||||
const defaultChainId = networksData[0]?.chainId;
|
|
||||||
|
|
||||||
if (!defaultChainId) {
|
|
||||||
console.log('useGetOrCreateAccounts: No default chainId found');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const accounts = await getOrCreateAccountsForChain(defaultChainId);
|
|
||||||
|
|
||||||
// Only notify Android when we actually have accounts
|
|
||||||
if (accounts.length > 0 && window.Android?.onAccountsReady) {
|
|
||||||
window.Android.onAccountsReady();
|
|
||||||
} else {
|
|
||||||
console.log('No accounts created or Android bridge not available');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener('message', handleCreateAccounts);
|
|
||||||
|
|
||||||
const isAndroidWebView = !!(window.Android);
|
|
||||||
|
|
||||||
if (isAndroidWebView) {
|
|
||||||
autoCreateAccounts();
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener('message', handleCreateAccounts);
|
|
||||||
};
|
|
||||||
}, [networksData, getAccountsData, getOrCreateAccountsForChain]);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default useGetOrCreateAccounts;
|
|
@ -1,81 +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';
|
|
||||||
|
|
||||||
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]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// Assign the function to the window object
|
|
||||||
window.receiveSignRequestFromAndroid = navigateToSignRequest;
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
window.receiveSignRequestFromAndroid = undefined;
|
|
||||||
};
|
|
||||||
}, [navigateToSignRequest]); // Only the function reference as dependency
|
|
||||||
};
|
|
13
src/import-meta-env.d.ts
vendored
13
src/import-meta-env.d.ts
vendored
@ -1,13 +0,0 @@
|
|||||||
// Generated by '@import-meta-env/typescript'
|
|
||||||
|
|
||||||
interface ImportMetaEnv {
|
|
||||||
readonly REACT_APP_WALLET_CONNECT_PROJECT_ID: string;
|
|
||||||
readonly REACT_APP_DEFAULT_GAS_PRICE: string;
|
|
||||||
readonly REACT_APP_GAS_ADJUSTMENT: string;
|
|
||||||
readonly REACT_APP_LACONICD_RPC_URL: string;
|
|
||||||
readonly REACT_APP_ALLOWED_URLS: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ImportMeta {
|
|
||||||
readonly env: ImportMetaEnv;
|
|
||||||
}
|
|
@ -1,6 +1,8 @@
|
|||||||
import React, { useCallback, useEffect, useState } from "react";
|
import React, { useCallback, useEffect, useState } from "react";
|
||||||
import { useForm, Controller, useWatch, FieldErrors } from "react-hook-form";
|
import { useForm, Controller, useWatch, FieldErrors } from "react-hook-form";
|
||||||
import { TextInput, HelperText } from "react-native-paper";
|
import { TextInput, HelperText } from "react-native-paper";
|
||||||
|
|
||||||
|
import { HDNode } from "ethers/lib/utils";
|
||||||
import { chains } from "chain-registry";
|
import { chains } from "chain-registry";
|
||||||
import { useDebouncedCallback } from "use-debounce";
|
import { useDebouncedCallback } from "use-debounce";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
@ -8,21 +10,27 @@ import { z } from "zod";
|
|||||||
import { NativeStackNavigationProp } from "@react-navigation/native-stack";
|
import { NativeStackNavigationProp } from "@react-navigation/native-stack";
|
||||||
import { useNavigation } from "@react-navigation/native";
|
import { useNavigation } from "@react-navigation/native";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { Divider, Grid } from "@mui/material";
|
|
||||||
import { LoadingButton } from "@mui/lab";
|
|
||||||
|
|
||||||
import { StackParamsList } from "../types";
|
import { StackParamsList } from "../types";
|
||||||
import { SelectNetworkType } from "../components/SelectNetworkType";
|
import { SelectNetworkType } from "../components/SelectNetworkType";
|
||||||
import { addNewNetwork } from "../utils/accounts";
|
import { storeNetworkData } from "../utils/accounts";
|
||||||
import { useNetworks } from "../context/NetworksContext";
|
import { useNetworks } from "../context/NetworksContext";
|
||||||
import {
|
import {
|
||||||
|
COSMOS,
|
||||||
EIP155,
|
EIP155,
|
||||||
CHAINID_DEBOUNCE_DELAY,
|
CHAINID_DEBOUNCE_DELAY,
|
||||||
EMPTY_FIELD_ERROR,
|
EMPTY_FIELD_ERROR,
|
||||||
INVALID_URL_ERROR,
|
INVALID_URL_ERROR,
|
||||||
IS_NUMBER_REGEX,
|
IS_NUMBER_REGEX,
|
||||||
} from "../utils/constants";
|
} from "../utils/constants";
|
||||||
|
import { getCosmosAccounts } from "../utils/accounts";
|
||||||
import ETH_CHAINS from "../assets/ethereum-chains.json";
|
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";
|
import { Layout } from "../components/Layout";
|
||||||
|
|
||||||
const ethNetworkDataSchema = z.object({
|
const ethNetworkDataSchema = z.object({
|
||||||
@ -122,7 +130,7 @@ const AddNetwork = () => {
|
|||||||
"gasPrice",
|
"gasPrice",
|
||||||
String(
|
String(
|
||||||
cosmosChainDetails.fees?.fee_tokens[0].average_gas_price ||
|
cosmosChainDetails.fees?.fee_tokens[0].average_gas_price ||
|
||||||
String(import.meta.env.REACT_APP_DEFAULT_GAS_PRICE),
|
String(process.env.DEFAULT_GAS_PRICE),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}, CHAINID_DEBOUNCE_DELAY);
|
}, CHAINID_DEBOUNCE_DELAY);
|
||||||
@ -135,10 +143,62 @@ const AddNetwork = () => {
|
|||||||
isDefault: false,
|
isDefault: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
const retrievedNetworksData = await addNewNetwork(newNetworkData);
|
const mnemonicServer = await getInternetCredentials("mnemonicServer");
|
||||||
|
const mnemonic = mnemonicServer;
|
||||||
|
|
||||||
|
if (!mnemonic) {
|
||||||
|
throw new Error("Mnemonic not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
const hdNode = HDNode.fromMnemonic(mnemonic);
|
||||||
|
|
||||||
|
const hdPath = `m/44'/${newNetworkData.coinType}'/0'/0/0`;
|
||||||
|
const node = hdNode.derivePath(hdPath);
|
||||||
|
let address;
|
||||||
|
|
||||||
|
switch (newNetworkData.namespace) {
|
||||||
|
case EIP155:
|
||||||
|
address = node.address;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case COSMOS:
|
||||||
|
address = (
|
||||||
|
await getCosmosAccounts(
|
||||||
|
mnemonic,
|
||||||
|
hdPath,
|
||||||
|
(newNetworkData as z.infer<typeof cosmosNetworkDataSchema>)
|
||||||
|
.addressPrefix,
|
||||||
|
)
|
||||||
|
).data.address;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Error("Unsupported namespace");
|
||||||
|
}
|
||||||
|
|
||||||
|
const accountInfo = `${hdPath},${node.privateKey},${node.publicKey},${address}`;
|
||||||
|
|
||||||
|
const retrievedNetworksData = await storeNetworkData(newNetworkData);
|
||||||
setNetworksData(retrievedNetworksData);
|
setNetworksData(retrievedNetworksData);
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
setInternetCredentials(
|
||||||
|
`accounts/${newNetworkData.namespace}:${newNetworkData.chainId}/0`,
|
||||||
|
"_",
|
||||||
|
accountInfo,
|
||||||
|
),
|
||||||
|
setInternetCredentials(
|
||||||
|
`addAccountCounter/${newNetworkData.namespace}:${newNetworkData.chainId}`,
|
||||||
|
"_",
|
||||||
|
"1",
|
||||||
|
),
|
||||||
|
setInternetCredentials(
|
||||||
|
`accountIndices/${newNetworkData.namespace}:${newNetworkData.chainId}`,
|
||||||
|
"_",
|
||||||
|
"0",
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
|
||||||
navigation.navigate("Home");
|
navigation.navigate("Home");
|
||||||
},
|
},
|
||||||
[navigation, namespace, setNetworksData],
|
[navigation, namespace, setNetworksData],
|
||||||
|
@ -159,7 +159,7 @@ const ApproveTransaction = ({ route }: ApproveTransactionProps) => {
|
|||||||
|
|
||||||
setCosmosGasLimit(
|
setCosmosGasLimit(
|
||||||
String(
|
String(
|
||||||
Math.round(gasEstimation * Number(import.meta.env.REACT_APP_GAS_ADJUSTMENT)),
|
Math.round(gasEstimation * Number(process.env.REACT_APP_GAS_ADJUSTMENT)),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -477,7 +477,7 @@ const ApproveTransfer = ({ route }: SignRequestProps) => {
|
|||||||
|
|
||||||
setCosmosGasLimit(
|
setCosmosGasLimit(
|
||||||
String(
|
String(
|
||||||
Math.round(gasEstimation * Number(import.meta.env.REACT_APP_GAS_ADJUSTMENT)),
|
Math.round(gasEstimation * Number(process.env.REACT_APP_GAS_ADJUSTMENT)),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -1,58 +0,0 @@
|
|||||||
import React, { useEffect } from 'react';
|
|
||||||
|
|
||||||
import { useNetworks } from '../context/NetworksContext';
|
|
||||||
import { signMessage } from '../utils/sign-message';
|
|
||||||
import { AUTO_SIGN_IN, EIP155, SIGN_IN_RESPONSE } from '../utils/constants';
|
|
||||||
import { sendMessage } from '../utils/misc';
|
|
||||||
import useAccountsData from '../hooks/useAccountsData';
|
|
||||||
import useGetOrCreateAccounts from '../hooks/useGetOrCreateAccounts';
|
|
||||||
|
|
||||||
const REACT_APP_ALLOWED_URLS = import.meta.env.REACT_APP_ALLOWED_URLS;
|
|
||||||
|
|
||||||
export const AutoSignIn = () => {
|
|
||||||
const { networksData } = useNetworks();
|
|
||||||
|
|
||||||
const { getAccountsData } = useAccountsData();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const handleSignIn = async (event: MessageEvent) => {
|
|
||||||
if (event.data.type !== AUTO_SIGN_IN) return;
|
|
||||||
|
|
||||||
if (!REACT_APP_ALLOWED_URLS) {
|
|
||||||
console.log('Allowed URLs are not set');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const allowedUrls = REACT_APP_ALLOWED_URLS.split(',').map(url => url.trim());
|
|
||||||
|
|
||||||
if (!allowedUrls.includes(event.origin)) {
|
|
||||||
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 (
|
|
||||||
<>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
};
|
|
@ -202,13 +202,7 @@ const SignRequest = ({ route }: SignRequestProps) => {
|
|||||||
chainId,
|
chainId,
|
||||||
accountId: account.index,
|
accountId: account.index,
|
||||||
});
|
});
|
||||||
|
alert(`Signature ${signedMessage}`);
|
||||||
// Send the result back to Android and close dialog
|
|
||||||
if (window.Android?.onSignatureComplete) {
|
|
||||||
window.Android.onSignatureComplete(signedMessage || "");
|
|
||||||
} else {
|
|
||||||
alert(`Signature: ${signedMessage}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -236,11 +230,7 @@ const SignRequest = ({ route }: SignRequestProps) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setIsRejecting(false);
|
setIsRejecting(false);
|
||||||
if (window.Android?.onSignatureCancelled) {
|
navigation.navigate('Home');
|
||||||
window.Android.onSignatureCancelled();
|
|
||||||
} else {
|
|
||||||
navigation.navigate('Home');
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -1,212 +0,0 @@
|
|||||||
import React, { useCallback, useEffect, useState } from 'react';
|
|
||||||
import { ScrollView, View } from 'react-native';
|
|
||||||
import { ActivityIndicator, Button, Text, Appbar } from 'react-native-paper';
|
|
||||||
|
|
||||||
import { useNavigation } from '@react-navigation/native';
|
|
||||||
import {
|
|
||||||
NativeStackNavigationProp,
|
|
||||||
NativeStackScreenProps,
|
|
||||||
} from '@react-navigation/native-stack';
|
|
||||||
import { getHeaderTitle } from '@react-navigation/elements';
|
|
||||||
|
|
||||||
import { Account, StackParamsList } from '../types';
|
|
||||||
import AccountDetails from '../components/AccountDetails';
|
|
||||||
import styles from '../styles/stylesheet';
|
|
||||||
import { getCosmosAccountByHDPath, retrieveSingleAccount } from '../utils/accounts';
|
|
||||||
import { getMnemonic, getPathKey, sendMessage } from '../utils/misc';
|
|
||||||
import { COSMOS, REQUEST_SIGN_MESSAGE, SIGN_MESSAGE_RESPONSE } from '../utils/constants';
|
|
||||||
import { useNetworks } from '../context/NetworksContext';
|
|
||||||
|
|
||||||
const REACT_APP_ALLOWED_URLS = import.meta.env.REACT_APP_ALLOWED_URLS;
|
|
||||||
|
|
||||||
type SignRequestProps = NativeStackScreenProps<StackParamsList, 'sign-message-request-embed'>;
|
|
||||||
|
|
||||||
const SignRequestEmbed = ({ route }: SignRequestProps) => {
|
|
||||||
const [displayAccount, setDisplayAccount] = useState<Account>();
|
|
||||||
const [message, setMessage] = useState<string>('');
|
|
||||||
const [chainId, setChainId] = useState<string>('');
|
|
||||||
const [namespace, setNamespace] = useState<string>('');
|
|
||||||
const [signDoc, setSignDoc] = useState<any>(null);
|
|
||||||
const [signerAddress, setSignerAddress] = useState<string>('');
|
|
||||||
const [origin, setOrigin] = useState<string>('');
|
|
||||||
const [sourceWindow, setSourceWindow] = useState<Window | null>(null);
|
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
|
||||||
const [isApproving, setIsApproving] = useState(false);
|
|
||||||
|
|
||||||
const { networksData } = useNetworks();
|
|
||||||
const navigation =
|
|
||||||
useNavigation<NativeStackNavigationProp<StackParamsList>>();
|
|
||||||
|
|
||||||
const signMessageHandler = async () => {
|
|
||||||
if (!signDoc || !signerAddress || !sourceWindow) return;
|
|
||||||
|
|
||||||
setIsApproving(true);
|
|
||||||
try {
|
|
||||||
if (namespace !== COSMOS) {
|
|
||||||
// TODO: Support ETH namespace
|
|
||||||
throw new Error(`namespace ${namespace} is not supported`)
|
|
||||||
}
|
|
||||||
|
|
||||||
const requestAccount = await retrieveSingleAccount(namespace, chainId, signerAddress);
|
|
||||||
const path = (await getPathKey(`${namespace}:${chainId}`, requestAccount!.index)).path;
|
|
||||||
const mnemonic = await getMnemonic();
|
|
||||||
|
|
||||||
const requestedNetworkData = networksData.find(networkData => networkData.chainId === chainId)
|
|
||||||
if (!requestedNetworkData) {
|
|
||||||
throw new Error("Requested network not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
const cosmosAccount = await getCosmosAccountByHDPath(mnemonic, path, requestedNetworkData?.addressPrefix);
|
|
||||||
|
|
||||||
const cosmosAminoSignature = await cosmosAccount.cosmosWallet.signAmino(
|
|
||||||
signerAddress,
|
|
||||||
signDoc,
|
|
||||||
);
|
|
||||||
|
|
||||||
const signature = cosmosAminoSignature.signature.signature;
|
|
||||||
|
|
||||||
sendMessage(
|
|
||||||
sourceWindow,
|
|
||||||
SIGN_MESSAGE_RESPONSE,
|
|
||||||
{ signature },
|
|
||||||
origin,
|
|
||||||
);
|
|
||||||
|
|
||||||
navigation.navigate('Home');
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Signing failed:', err);
|
|
||||||
sendMessage(
|
|
||||||
sourceWindow!,
|
|
||||||
SIGN_MESSAGE_RESPONSE,
|
|
||||||
{ error: err },
|
|
||||||
origin,
|
|
||||||
);
|
|
||||||
} finally {
|
|
||||||
setIsApproving(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const rejectRequestHandler = useCallback(async () => {
|
|
||||||
if (sourceWindow && origin) {
|
|
||||||
sendMessage(
|
|
||||||
sourceWindow,
|
|
||||||
SIGN_MESSAGE_RESPONSE,
|
|
||||||
{ error: 'User rejected the request' },
|
|
||||||
origin,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}, [sourceWindow, origin]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const handleCosmosSignMessage = async (event: MessageEvent) => {
|
|
||||||
if (event.data.type !== REQUEST_SIGN_MESSAGE) return;
|
|
||||||
|
|
||||||
|
|
||||||
if (!REACT_APP_ALLOWED_URLS) {
|
|
||||||
console.log('Allowed URLs are not set');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const allowedUrls = REACT_APP_ALLOWED_URLS.split(',').map(url => url.trim());
|
|
||||||
|
|
||||||
if (!allowedUrls.includes(event.origin)) {
|
|
||||||
console.log('Unauthorized app.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { signerAddress, signDoc } = event.data.params;
|
|
||||||
|
|
||||||
const receivedNamespace = event.data.chainId.split(':')[0]
|
|
||||||
const receivedChainId = event.data.chainId.split(':')[1]
|
|
||||||
|
|
||||||
if (receivedNamespace !== COSMOS) {
|
|
||||||
// TODO: Support ETH namespace
|
|
||||||
throw new Error(`namespace ${receivedNamespace} is not supported`)
|
|
||||||
}
|
|
||||||
|
|
||||||
setSignerAddress(signerAddress);
|
|
||||||
setSignDoc(signDoc);
|
|
||||||
setMessage(signDoc.memo || '');
|
|
||||||
setOrigin(event.origin);
|
|
||||||
setSourceWindow(event.source as Window);
|
|
||||||
setNamespace(receivedNamespace);
|
|
||||||
setChainId(receivedChainId);
|
|
||||||
|
|
||||||
const requestAccount = await retrieveSingleAccount(
|
|
||||||
receivedNamespace,
|
|
||||||
receivedChainId,
|
|
||||||
signerAddress,
|
|
||||||
);
|
|
||||||
|
|
||||||
setDisplayAccount(requestAccount);
|
|
||||||
setIsLoading(false);
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Error preparing sign request:', err);
|
|
||||||
setIsLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener('message', handleCosmosSignMessage);
|
|
||||||
return () => window.removeEventListener('message', handleCosmosSignMessage);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
navigation.setOptions({
|
|
||||||
// eslint-disable-next-line react/no-unstable-nested-components
|
|
||||||
header: ({ options, back }) => {
|
|
||||||
const title = getHeaderTitle(options, 'Sign Message');
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Appbar.Header>
|
|
||||||
{back && (
|
|
||||||
<Appbar.BackAction
|
|
||||||
onPress={async () => {
|
|
||||||
await rejectRequestHandler();
|
|
||||||
navigation.navigate('Home');
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<Appbar.Content title={title} />
|
|
||||||
</Appbar.Header>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}, [navigation, rejectRequestHandler]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{isLoading ? (
|
|
||||||
<View style={styles.spinnerContainer}>
|
|
||||||
<ActivityIndicator size="large" color="#0000ff" />
|
|
||||||
</View>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<ScrollView contentContainerStyle={styles.appContainer}>
|
|
||||||
<AccountDetails account={displayAccount} />
|
|
||||||
<View style={styles.requestMessage}>
|
|
||||||
<Text variant="bodyLarge">{message}</Text>
|
|
||||||
</View>
|
|
||||||
</ScrollView>
|
|
||||||
<View style={styles.buttonContainer}>
|
|
||||||
<Button
|
|
||||||
mode="contained"
|
|
||||||
onPress={signMessageHandler}
|
|
||||||
loading={isApproving}
|
|
||||||
disabled={isApproving}>
|
|
||||||
Yes
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
mode="contained"
|
|
||||||
onPress={rejectRequestHandler}
|
|
||||||
buttonColor="#B82B0D">
|
|
||||||
No
|
|
||||||
</Button>
|
|
||||||
</View>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default SignRequestEmbed;
|
|
@ -1,392 +0,0 @@
|
|||||||
import React, { useEffect, useState, useCallback } from 'react';
|
|
||||||
import { ScrollView, View } from 'react-native';
|
|
||||||
import {
|
|
||||||
Button,
|
|
||||||
Text,
|
|
||||||
} from 'react-native-paper';
|
|
||||||
import JSONbig from 'json-bigint';
|
|
||||||
import { AuthInfo, SignDoc } from "cosmjs-types/cosmos/tx/v1beta1/tx";
|
|
||||||
|
|
||||||
import { DirectSecp256k1Wallet, Algo, TxBodyEncodeObject, decodeOptionalPubkey } from '@cosmjs/proto-signing';
|
|
||||||
import { SigningStargateClient } from '@cosmjs/stargate';
|
|
||||||
import { toHex } from '@cosmjs/encoding';
|
|
||||||
|
|
||||||
import { getCosmosAccountByHDPath, retrieveAccounts, retrieveSingleAccount } from '../utils/accounts';
|
|
||||||
import AccountDetails from '../components/AccountDetails';
|
|
||||||
import styles from '../styles/stylesheet';
|
|
||||||
import { getMnemonic, getPathKey, sendMessage } from '../utils/misc';
|
|
||||||
import { useNetworks } from '../context/NetworksContext';
|
|
||||||
import TxErrorDialog from '../components/TxErrorDialog';
|
|
||||||
import { Account, NetworksDataState } from '../types';
|
|
||||||
import { REQUEST_SIGN_TX, REQUEST_COSMOS_ACCOUNTS, COSMOS_ACCOUNTS_RESPONSE, SIGN_TX_RESPONSE } from '../utils/constants';
|
|
||||||
|
|
||||||
// Type Definitions
|
|
||||||
interface GetAccountsRequestData {
|
|
||||||
chainId: string,
|
|
||||||
}
|
|
||||||
|
|
||||||
interface SignTxRequestData {
|
|
||||||
address: string;
|
|
||||||
signDoc: SignDoc;
|
|
||||||
txBody: TxBodyEncodeObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
type IncomingMessageData = SignTxRequestData | GetAccountsRequestData;
|
|
||||||
|
|
||||||
interface IncomingMessageEventData {
|
|
||||||
id: string;
|
|
||||||
type: typeof REQUEST_SIGN_TX | typeof REQUEST_COSMOS_ACCOUNTS;
|
|
||||||
data: IncomingMessageData;
|
|
||||||
}
|
|
||||||
|
|
||||||
type TransactionDetails = {
|
|
||||||
source: MessageEventSource;
|
|
||||||
origin: string;
|
|
||||||
signerAddress: string;
|
|
||||||
chainId: string;
|
|
||||||
account: Account;
|
|
||||||
requestedNetwork: NetworksDataState;
|
|
||||||
balance: string;
|
|
||||||
signDoc: SignDoc;
|
|
||||||
txBody: TxBodyEncodeObject;
|
|
||||||
};
|
|
||||||
|
|
||||||
interface GetAccountsResponse {
|
|
||||||
accounts: Array<{
|
|
||||||
algo: Algo;
|
|
||||||
address: string;
|
|
||||||
pubkey: string;
|
|
||||||
}>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const REACT_APP_ALLOWED_URLS = import.meta.env.REACT_APP_ALLOWED_URLS;
|
|
||||||
|
|
||||||
export const SignTxEmbed = () => {
|
|
||||||
const [isTxApprovalVisible, setIsTxApprovalVisible] = useState<boolean>(false);
|
|
||||||
const [transactionDetails, setTransactionDetails] = useState<TransactionDetails | null>(null);
|
|
||||||
const [isTxLoading, setIsTxLoading] = useState(false);
|
|
||||||
const [txError, setTxError] = useState<string | null>(null);
|
|
||||||
|
|
||||||
const { networksData } = useNetworks();
|
|
||||||
|
|
||||||
// Message Handlers
|
|
||||||
|
|
||||||
const handleGetCosmosAccountsRequest = useCallback(async (event: MessageEvent<IncomingMessageEventData>) => {
|
|
||||||
const { data } = event.data;
|
|
||||||
const source = event.source as Window;
|
|
||||||
const origin = event.origin;
|
|
||||||
const requestData = data as GetAccountsRequestData;
|
|
||||||
const mnemonic = await getMnemonic();
|
|
||||||
|
|
||||||
try {
|
|
||||||
const requestedNetworkData = networksData.find(networkData => networkData.chainId === requestData.chainId)
|
|
||||||
|
|
||||||
if(!requestedNetworkData) {
|
|
||||||
throw new Error("Network data not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
const allAccounts = await retrieveAccounts(requestedNetworkData);
|
|
||||||
|
|
||||||
if (!allAccounts || allAccounts.length === 0) {
|
|
||||||
throw new Error("Accounts not found for network")
|
|
||||||
}
|
|
||||||
|
|
||||||
const responseAccounts = await Promise.all(
|
|
||||||
allAccounts.map(async (acc) => {
|
|
||||||
const cosmosAccount = (await getCosmosAccountByHDPath(mnemonic, acc.hdPath, requestedNetworkData.addressPrefix)).data;
|
|
||||||
return {
|
|
||||||
...cosmosAccount,
|
|
||||||
pubkey: toHex(cosmosAccount.pubkey),
|
|
||||||
};
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
const response: GetAccountsResponse = { accounts: responseAccounts };
|
|
||||||
sendMessage(source, COSMOS_ACCOUNTS_RESPONSE, {data: response}, origin);
|
|
||||||
} catch (error: unknown) {
|
|
||||||
console.error(`Error handling ${REQUEST_COSMOS_ACCOUNTS}:`, error);
|
|
||||||
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
||||||
|
|
||||||
sendMessage(source, COSMOS_ACCOUNTS_RESPONSE, { error: `Failed to get accounts: ${errorMsg}` }, origin);
|
|
||||||
}
|
|
||||||
}, [networksData]);
|
|
||||||
|
|
||||||
const handleSignTxRequest = useCallback(async (event: MessageEvent<IncomingMessageEventData>) => {
|
|
||||||
const { data } = event.data;
|
|
||||||
const source = event.source as Window;
|
|
||||||
const origin = event.origin;
|
|
||||||
const requestData = data as SignTxRequestData;
|
|
||||||
|
|
||||||
setIsTxApprovalVisible(false);
|
|
||||||
setTransactionDetails(null);
|
|
||||||
setTxError(null);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { address: signerAddress, signDoc, txBody } = requestData;
|
|
||||||
|
|
||||||
const network = networksData.find(net => net.chainId === signDoc.chainId);
|
|
||||||
if (!network) throw new Error(`Network with chainId "${signDoc.chainId}" not supported.`);
|
|
||||||
|
|
||||||
const account = await retrieveSingleAccount(network.namespace, network.chainId, signerAddress);
|
|
||||||
if (!account) throw new Error(`Account not found for address "${signerAddress}" on chain "${signDoc.chainId}".`);
|
|
||||||
|
|
||||||
// Balance Check
|
|
||||||
// Use a temporary read-only client for balance
|
|
||||||
const { privKey } = await getPathKey(`${network.namespace}:${network.chainId}`, account.index)
|
|
||||||
|
|
||||||
const tempWallet = await DirectSecp256k1Wallet.fromKey(
|
|
||||||
new Uint8Array(Buffer.from(privKey.replace(/^0x/, ''), 'hex')),
|
|
||||||
network.addressPrefix
|
|
||||||
);
|
|
||||||
|
|
||||||
const client = await SigningStargateClient.connectWithSigner(network.rpcUrl!, tempWallet);
|
|
||||||
const balance = await client.getBalance(account.address, network.nativeDenom!);
|
|
||||||
client.disconnect();
|
|
||||||
|
|
||||||
if (!balance || balance.amount === "0") {
|
|
||||||
throw new Error(`${account.address} does not have any balance`)
|
|
||||||
}
|
|
||||||
|
|
||||||
setTransactionDetails({
|
|
||||||
source: source,
|
|
||||||
origin: origin,
|
|
||||||
signerAddress,
|
|
||||||
chainId: signDoc.chainId,
|
|
||||||
account,
|
|
||||||
requestedNetwork: network,
|
|
||||||
balance: balance.amount,
|
|
||||||
signDoc,
|
|
||||||
txBody
|
|
||||||
});
|
|
||||||
|
|
||||||
setIsTxApprovalVisible(true);
|
|
||||||
|
|
||||||
} catch (error: unknown) {
|
|
||||||
console.error(`Error handling ${REQUEST_SIGN_TX}:`, error);
|
|
||||||
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
||||||
|
|
||||||
sendMessage(source, SIGN_TX_RESPONSE, { error: `Failed to prepare transaction: ${errorMsg}` }, origin);
|
|
||||||
setTxError(errorMsg);
|
|
||||||
}
|
|
||||||
}, [networksData]);
|
|
||||||
|
|
||||||
const handleIncomingMessage = useCallback((event: MessageEvent) => {
|
|
||||||
if (!event.data || typeof event.data !== 'object' || !event.data.type || !event.source || event.source === window) {
|
|
||||||
return; // Basic validation
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!REACT_APP_ALLOWED_URLS) {
|
|
||||||
console.log('Allowed URLs are not set');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const allowedUrls = REACT_APP_ALLOWED_URLS.split(',').map(url => url.trim());
|
|
||||||
|
|
||||||
if (!allowedUrls.includes(event.origin)) {
|
|
||||||
console.log('Unauthorized app.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const messageData = event.data as IncomingMessageEventData;
|
|
||||||
|
|
||||||
switch (messageData.type) {
|
|
||||||
case REQUEST_COSMOS_ACCOUNTS:
|
|
||||||
handleGetCosmosAccountsRequest(event as MessageEvent<IncomingMessageEventData>);
|
|
||||||
break;
|
|
||||||
case REQUEST_SIGN_TX:
|
|
||||||
handleSignTxRequest(event as MessageEvent<IncomingMessageEventData>);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
console.warn(`Received unknown message type: ${messageData.type}`);
|
|
||||||
}
|
|
||||||
}, [handleGetCosmosAccountsRequest, handleSignTxRequest]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
window.addEventListener('message', handleIncomingMessage);
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener('message', handleIncomingMessage);
|
|
||||||
};
|
|
||||||
}, [handleIncomingMessage]);
|
|
||||||
|
|
||||||
// Action Handlers
|
|
||||||
|
|
||||||
const acceptRequestHandler = async () => {
|
|
||||||
if (!transactionDetails) {
|
|
||||||
setTxError("Transaction details are missing.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setIsTxLoading(true);
|
|
||||||
setTxError(null);
|
|
||||||
|
|
||||||
const { source, origin, requestedNetwork, chainId, account, signerAddress, signDoc } = transactionDetails;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { privKey } = await getPathKey(`${requestedNetwork.namespace}:${chainId}`, account.index);
|
|
||||||
|
|
||||||
const privateKeyBytes = Buffer.from(privKey.replace(/^0x/, ''), 'hex');
|
|
||||||
const wallet = await DirectSecp256k1Wallet.fromKey(new Uint8Array(privateKeyBytes), requestedNetwork.addressPrefix); // Wrap in Uint8Array
|
|
||||||
|
|
||||||
// Perform the actual signing
|
|
||||||
const signResponse = await wallet.signDirect(signerAddress, signDoc);
|
|
||||||
|
|
||||||
sendMessage(source as Window, SIGN_TX_RESPONSE, {data: signResponse}, origin);
|
|
||||||
|
|
||||||
setIsTxApprovalVisible(false);
|
|
||||||
setTransactionDetails(null);
|
|
||||||
|
|
||||||
} catch (error: unknown) {
|
|
||||||
console.error("Error during signDirect:", error);
|
|
||||||
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
||||||
|
|
||||||
setTxError(errorMsg);
|
|
||||||
sendMessage(source as Window, SIGN_TX_RESPONSE, { error: `Failed to sign transaction: ${errorMsg}` }, origin);
|
|
||||||
} finally {
|
|
||||||
setIsTxLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const rejectRequestHandler = () => {
|
|
||||||
if (!transactionDetails) return;
|
|
||||||
const { source, origin } = transactionDetails;
|
|
||||||
|
|
||||||
sendMessage(source as Window, SIGN_TX_RESPONSE, { error: "User rejected the signature request." }, origin);
|
|
||||||
setIsTxApprovalVisible(false);
|
|
||||||
setTransactionDetails(null);
|
|
||||||
setTxError(null);
|
|
||||||
};
|
|
||||||
|
|
||||||
const decodedAuth = React.useMemo(() => {
|
|
||||||
if (!transactionDetails) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const info = AuthInfo.decode(transactionDetails.signDoc.authInfoBytes);
|
|
||||||
return {
|
|
||||||
...info,
|
|
||||||
signerInfos: info.signerInfos.map((signerInfo) => ({
|
|
||||||
...signerInfo,
|
|
||||||
publicKey: decodeOptionalPubkey(signerInfo.publicKey),
|
|
||||||
})),
|
|
||||||
};
|
|
||||||
}, [transactionDetails]);
|
|
||||||
|
|
||||||
const formattedTxBody = React.useMemo(
|
|
||||||
() => {
|
|
||||||
if (!transactionDetails) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return JSONbig.stringify(transactionDetails.txBody, null, 2)
|
|
||||||
},
|
|
||||||
[transactionDetails]
|
|
||||||
);
|
|
||||||
|
|
||||||
const formattedAuthInfo = React.useMemo(
|
|
||||||
() => JSONbig.stringify(decodedAuth, null, 2),
|
|
||||||
[decodedAuth]
|
|
||||||
);
|
|
||||||
|
|
||||||
const formattedSignDoc = React.useMemo(
|
|
||||||
() =>
|
|
||||||
{
|
|
||||||
if (!transactionDetails) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return JSONbig.stringify(
|
|
||||||
{
|
|
||||||
...transactionDetails.signDoc,
|
|
||||||
bodyBytes: toHex(transactionDetails.signDoc.bodyBytes),
|
|
||||||
authInfoBytes: toHex(transactionDetails.signDoc.authInfoBytes),
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
2
|
|
||||||
)
|
|
||||||
},
|
|
||||||
[transactionDetails]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{isTxApprovalVisible && transactionDetails ? (
|
|
||||||
<>
|
|
||||||
<ScrollView contentContainerStyle={styles.appContainer}>
|
|
||||||
<View style={{ marginBottom: 16 }}>
|
|
||||||
<Text variant='titleLarge'>Account</Text>
|
|
||||||
<View style={styles.dataBox}>
|
|
||||||
<AccountDetails account={transactionDetails.account} />
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<View style={{ marginBottom: 16 }}>
|
|
||||||
<Text variant='titleLarge'>{`Balance (${transactionDetails.requestedNetwork.nativeDenom})`}</Text>
|
|
||||||
<View style={styles.dataBox}>
|
|
||||||
<Text variant='bodyLarge'>
|
|
||||||
{transactionDetails.balance}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<View style={{ marginBottom: 16 }}>
|
|
||||||
<Text variant='titleLarge'>Transaction Body</Text>
|
|
||||||
<ScrollView style={styles.messageBody} contentContainerStyle={{ padding: 10 }}>
|
|
||||||
<Text style={styles.codeText}>
|
|
||||||
{formattedTxBody}
|
|
||||||
</Text>
|
|
||||||
</ScrollView>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<View style={{ marginBottom: 16 }}>
|
|
||||||
<Text variant='titleLarge'>Auth Info</Text>
|
|
||||||
<ScrollView style={styles.messageBody} contentContainerStyle={{ padding: 10 }}>
|
|
||||||
<Text style={styles.codeText}>
|
|
||||||
{formattedAuthInfo}
|
|
||||||
</Text>
|
|
||||||
</ScrollView>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<View style={{ marginBottom: 16 }}>
|
|
||||||
<Text variant='titleLarge'>Transaction Data To Be Signed</Text>
|
|
||||||
<ScrollView style={styles.messageBody} contentContainerStyle={{ padding: 10 }}>
|
|
||||||
<Text style={styles.codeText}>
|
|
||||||
{formattedSignDoc}
|
|
||||||
</Text>
|
|
||||||
</ScrollView>
|
|
||||||
</View>
|
|
||||||
</ScrollView>
|
|
||||||
|
|
||||||
<View style={styles.buttonContainer}>
|
|
||||||
<Button
|
|
||||||
mode="contained"
|
|
||||||
onPress={acceptRequestHandler}
|
|
||||||
loading={isTxLoading}
|
|
||||||
disabled={isTxLoading}
|
|
||||||
style={{marginTop: 10}}
|
|
||||||
>
|
|
||||||
{isTxLoading ? 'Processing...' : 'Approve'}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
mode="contained"
|
|
||||||
onPress={rejectRequestHandler}
|
|
||||||
buttonColor="#B82B0D"
|
|
||||||
disabled={isTxLoading}
|
|
||||||
style={{ marginTop: 10 }}
|
|
||||||
>
|
|
||||||
Reject
|
|
||||||
</Button>
|
|
||||||
</View>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<View style={styles.spinnerContainer}>
|
|
||||||
<Text style={{ marginTop: 50, textAlign: 'center' }}>Waiting for request...</Text>
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
<TxErrorDialog
|
|
||||||
error={txError!}
|
|
||||||
visible={!!txError}
|
|
||||||
hideDialog={() => setTxError(null)}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
@ -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';
|
|
||||||
import { REQUEST_TX, REQUEST_WALLET_ACCOUNTS, TRANSACTION_RESPONSE, WALLET_ACCOUNTS_DATA } from '../utils/constants';
|
|
||||||
|
|
||||||
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)) {
|
|
||||||
throw new Error('Insufficient funds');
|
|
||||||
}
|
|
||||||
|
|
||||||
const gasEstimation = await client.simulate(fromAddress, [sendMsg], MEMO);
|
|
||||||
const gasLimit = String(
|
|
||||||
Math.round(gasEstimation * Number(import.meta.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, '*');
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
@ -288,7 +288,7 @@ const styles = StyleSheet.create({
|
|||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
fontWeight: "bold",
|
fontWeight: "bold",
|
||||||
marginBottom: 3,
|
marginBottom: 3,
|
||||||
color: "white",
|
color: "black",
|
||||||
},
|
},
|
||||||
dataBox: {
|
dataBox: {
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
@ -355,10 +355,6 @@ const styles = StyleSheet.create({
|
|||||||
marginTop: 12,
|
marginTop: 12,
|
||||||
marginBottom: 20,
|
marginBottom: 20,
|
||||||
},
|
},
|
||||||
codeText: {
|
|
||||||
fontFamily: 'monospace',
|
|
||||||
fontSize: 12,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export default styles;
|
export default styles;
|
||||||
|
@ -13,10 +13,8 @@ export type StackParamsList = {
|
|||||||
};
|
};
|
||||||
SignRequest: {
|
SignRequest: {
|
||||||
namespace: string;
|
namespace: string;
|
||||||
chainId?: string;
|
|
||||||
address: string;
|
address: string;
|
||||||
message: string;
|
message: string;
|
||||||
accountInfo?: Account;
|
|
||||||
requestEvent?: Web3WalletTypes.SessionRequest;
|
requestEvent?: Web3WalletTypes.SessionRequest;
|
||||||
requestSessionData?: SessionTypes.Struct;
|
requestSessionData?: SessionTypes.Struct;
|
||||||
};
|
};
|
||||||
@ -38,10 +36,6 @@ export type StackParamsList = {
|
|||||||
requestEvent: Web3WalletTypes.SessionRequest;
|
requestEvent: Web3WalletTypes.SessionRequest;
|
||||||
requestSessionData: SessionTypes.Struct;
|
requestSessionData: SessionTypes.Struct;
|
||||||
};
|
};
|
||||||
"wallet-embed": undefined;
|
|
||||||
"auto-sign-in": undefined;
|
|
||||||
"sign-message-request-embed": undefined;
|
|
||||||
"sign-tx-request-embed": undefined;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Account = {
|
export type Account = {
|
||||||
@ -64,7 +58,7 @@ export type NetworksFormData = {
|
|||||||
namespace: string;
|
namespace: string;
|
||||||
nativeDenom?: string;
|
nativeDenom?: string;
|
||||||
addressPrefix?: string;
|
addressPrefix?: string;
|
||||||
coinType: string;
|
coinType?: string;
|
||||||
gasPrice?: string;
|
gasPrice?: string;
|
||||||
isDefault: boolean;
|
isDefault: boolean;
|
||||||
};
|
};
|
||||||
|
@ -56,7 +56,7 @@ const createWalletFromMnemonic = async (
|
|||||||
|
|
||||||
case COSMOS:
|
case COSMOS:
|
||||||
address = (
|
address = (
|
||||||
await getCosmosAccountByHDPath(mnemonic, hdPath, network.addressPrefix)
|
await getCosmosAccounts(mnemonic, hdPath, network.addressPrefix)
|
||||||
).data.address;
|
).data.address;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -87,44 +87,15 @@ const createWalletFromMnemonic = async (
|
|||||||
};
|
};
|
||||||
|
|
||||||
const addAccount = async (
|
const addAccount = async (
|
||||||
chainId: string,
|
networkData: NetworksDataState,
|
||||||
): Promise<Account | undefined> => {
|
): Promise<Account | undefined> => {
|
||||||
try {
|
try {
|
||||||
let selectedNetworkAccount
|
const namespaceChainId = `${networkData.namespace}:${networkData.chainId}`;
|
||||||
const networksData = await retrieveNetworksData();
|
const id = await getNextAccountId(namespaceChainId);
|
||||||
|
const hdPath = getHDPath(namespaceChainId, `0'/0/${id}`);
|
||||||
// Add account to all networks and return account for selected network
|
const accounts = await addAccountFromHDPath(hdPath, networkData);
|
||||||
for (const network of networksData) {
|
await updateAccountCounter(namespaceChainId, id);
|
||||||
const namespaceChainId = `${network.namespace}:${network.chainId}`;
|
return accounts;
|
||||||
const id = await getNextAccountId(namespaceChainId);
|
|
||||||
const hdPath = getHDPath(namespaceChainId, `0'/0/${id}`);
|
|
||||||
const account = await addAccountFromHDPath(hdPath, network);
|
|
||||||
await updateAccountCounter(namespaceChainId, id);
|
|
||||||
|
|
||||||
if (network.chainId === chainId) {
|
|
||||||
selectedNetworkAccount = account;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return selectedNetworkAccount;
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error creating account:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const addAccountsForNetwork = async (
|
|
||||||
network: NetworksDataState,
|
|
||||||
numberOfAccounts: number,
|
|
||||||
): Promise<void> => {
|
|
||||||
try {
|
|
||||||
const namespaceChainId = `${network.namespace}:${network.chainId}`;
|
|
||||||
|
|
||||||
for (let i = 0; i < numberOfAccounts; i++) {
|
|
||||||
const id = await getNextAccountId(namespaceChainId);
|
|
||||||
const hdPath = getHDPath(namespaceChainId, `0'/0/${id}`);
|
|
||||||
await addAccountFromHDPath(hdPath, network);
|
|
||||||
await updateAccountCounter(namespaceChainId, id);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error creating account:', error);
|
console.error('Error creating account:', error);
|
||||||
}
|
}
|
||||||
@ -160,77 +131,6 @@ const addAccountFromHDPath = async (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const addNewNetwork = async (
|
|
||||||
newNetworkData: NetworksFormData
|
|
||||||
): Promise<NetworksDataState[]> => {
|
|
||||||
const mnemonicServer = await getInternetCredentials("mnemonicServer");
|
|
||||||
const mnemonic = mnemonicServer;
|
|
||||||
|
|
||||||
if (!mnemonic) {
|
|
||||||
throw new Error("Mnemonic not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
const hdNode = HDNode.fromMnemonic(mnemonic);
|
|
||||||
|
|
||||||
const hdPath = `m/44'/${newNetworkData.coinType}'/0'/0/0`;
|
|
||||||
const node = hdNode.derivePath(hdPath);
|
|
||||||
let address;
|
|
||||||
|
|
||||||
switch (newNetworkData.namespace) {
|
|
||||||
case EIP155:
|
|
||||||
address = node.address;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case COSMOS:
|
|
||||||
address = (
|
|
||||||
await getCosmosAccountByHDPath(
|
|
||||||
mnemonic,
|
|
||||||
hdPath,
|
|
||||||
newNetworkData.addressPrefix,
|
|
||||||
)
|
|
||||||
).data.address;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new Error("Unsupported namespace");
|
|
||||||
}
|
|
||||||
|
|
||||||
const accountInfo = `${hdPath},${node.privateKey},${node.publicKey},${address}`;
|
|
||||||
|
|
||||||
await Promise.all([
|
|
||||||
setInternetCredentials(
|
|
||||||
`accounts/${newNetworkData.namespace}:${newNetworkData.chainId}/0`,
|
|
||||||
"_",
|
|
||||||
accountInfo,
|
|
||||||
),
|
|
||||||
setInternetCredentials(
|
|
||||||
`addAccountCounter/${newNetworkData.namespace}:${newNetworkData.chainId}`,
|
|
||||||
"_",
|
|
||||||
"1",
|
|
||||||
),
|
|
||||||
setInternetCredentials(
|
|
||||||
`accountIndices/${newNetworkData.namespace}:${newNetworkData.chainId}`,
|
|
||||||
"_",
|
|
||||||
"0",
|
|
||||||
),
|
|
||||||
]);
|
|
||||||
|
|
||||||
const retrievedNetworksData = await storeNetworkData(newNetworkData);
|
|
||||||
|
|
||||||
// Get number of accounts in first network
|
|
||||||
const nextAccountId = await getNextAccountId(
|
|
||||||
`${retrievedNetworksData[0].namespace}:${retrievedNetworksData[0].chainId}`,
|
|
||||||
);
|
|
||||||
|
|
||||||
const selectedNetwork = retrievedNetworksData.find(
|
|
||||||
(network) => network.chainId === newNetworkData.chainId,
|
|
||||||
);
|
|
||||||
|
|
||||||
await addAccountsForNetwork(selectedNetwork!, nextAccountId - 1);
|
|
||||||
|
|
||||||
return retrievedNetworksData;
|
|
||||||
}
|
|
||||||
|
|
||||||
const storeNetworkData = async (
|
const storeNetworkData = async (
|
||||||
networkData: NetworksFormData,
|
networkData: NetworksFormData,
|
||||||
): Promise<NetworksDataState[]> => {
|
): Promise<NetworksDataState[]> => {
|
||||||
@ -251,25 +151,21 @@ const storeNetworkData = async (
|
|||||||
networkId: String(networkId),
|
networkId: String(networkId),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
await setInternetCredentials(
|
await setInternetCredentials(
|
||||||
'networks',
|
'networks',
|
||||||
'_',
|
'_',
|
||||||
JSON.stringify(updatedNetworks),
|
JSON.stringify(updatedNetworks),
|
||||||
);
|
);
|
||||||
|
|
||||||
return updatedNetworks;
|
return updatedNetworks;
|
||||||
};
|
};
|
||||||
|
|
||||||
const retrieveNetworksData = async (): Promise<NetworksDataState[]> => {
|
const retrieveNetworksData = async (): Promise<NetworksDataState[]> => {
|
||||||
const networks = await getInternetCredentials('networks');
|
const networks = await getInternetCredentials('networks');
|
||||||
|
|
||||||
if (!networks) {
|
if(!networks){
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const parsedNetworks: NetworksDataState[] = JSON.parse(networks);
|
const parsedNetworks: NetworksDataState[] = JSON.parse(networks);
|
||||||
|
|
||||||
return parsedNetworks;
|
return parsedNetworks;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -292,7 +188,6 @@ export const retrieveAccountsForNetwork = async (
|
|||||||
address,
|
address,
|
||||||
hdPath: path,
|
hdPath: path,
|
||||||
};
|
};
|
||||||
|
|
||||||
return account;
|
return account;
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -310,7 +205,6 @@ const retrieveAccounts = async (
|
|||||||
if (!accountIndices) {
|
if (!accountIndices) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadedAccounts = await retrieveAccountsForNetwork(
|
const loadedAccounts = await retrieveAccountsForNetwork(
|
||||||
`${currentNetworkData.namespace}:${currentNetworkData.chainId}`,
|
`${currentNetworkData.namespace}:${currentNetworkData.chainId}`,
|
||||||
accountIndices,
|
accountIndices,
|
||||||
@ -387,7 +281,7 @@ const accountInfoFromHDPath = async (
|
|||||||
break;
|
break;
|
||||||
case COSMOS:
|
case COSMOS:
|
||||||
address = (
|
address = (
|
||||||
await getCosmosAccountByHDPath(mnemonic, hdPath, networkData.addressPrefix)
|
await getCosmosAccounts(mnemonic, hdPath, networkData.addressPrefix)
|
||||||
).data.address;
|
).data.address;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -429,7 +323,7 @@ const updateAccountCounter = async (
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getCosmosAccountByHDPath = async (
|
const getCosmosAccounts = async (
|
||||||
mnemonic: string,
|
mnemonic: string,
|
||||||
path: string,
|
path: string,
|
||||||
prefix: string = COSMOS,
|
prefix: string = COSMOS,
|
||||||
@ -445,33 +339,10 @@ const getCosmosAccountByHDPath = async (
|
|||||||
return { cosmosWallet, data };
|
return { cosmosWallet, data };
|
||||||
};
|
};
|
||||||
|
|
||||||
const checkNetworkForChainID = async (
|
|
||||||
chainId: string,
|
|
||||||
): Promise<boolean> => {
|
|
||||||
const networks = await getInternetCredentials('networks');
|
|
||||||
|
|
||||||
if (!networks) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const networksData: NetworksFormData[] = JSON.parse(networks);
|
|
||||||
|
|
||||||
return networksData.some((network) => network.chainId === chainId);
|
|
||||||
}
|
|
||||||
|
|
||||||
const isWalletCreated = async (
|
|
||||||
): Promise<boolean> => {
|
|
||||||
const mnemonicServer = await getInternetCredentials("mnemonicServer");
|
|
||||||
const mnemonic = mnemonicServer;
|
|
||||||
|
|
||||||
return mnemonic !== null;
|
|
||||||
};
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
createWallet,
|
createWallet,
|
||||||
addAccount,
|
addAccount,
|
||||||
addAccountFromHDPath,
|
addAccountFromHDPath,
|
||||||
addAccountsForNetwork,
|
|
||||||
storeNetworkData,
|
storeNetworkData,
|
||||||
retrieveNetworksData,
|
retrieveNetworksData,
|
||||||
retrieveAccounts,
|
retrieveAccounts,
|
||||||
@ -480,8 +351,5 @@ export {
|
|||||||
accountInfoFromHDPath,
|
accountInfoFromHDPath,
|
||||||
getNextAccountId,
|
getNextAccountId,
|
||||||
updateAccountCounter,
|
updateAccountCounter,
|
||||||
getCosmosAccountByHDPath,
|
getCosmosAccounts,
|
||||||
addNewNetwork,
|
|
||||||
checkNetworkForChainID,
|
|
||||||
isWalletCreated
|
|
||||||
};
|
};
|
||||||
|
@ -1,34 +1,20 @@
|
|||||||
import { COSMOS_TESTNET_CHAINS } from './wallet-connect/COSMOSData';
|
import { COSMOS_TESTNET_CHAINS } from './wallet-connect/COSMOSData';
|
||||||
import { EIP155_CHAINS } from './wallet-connect/EIP155Data';
|
import { EIP155_CHAINS } from './wallet-connect/EIP155Data';
|
||||||
import { NetworksFormData } from '../types';
|
|
||||||
|
|
||||||
export const EIP155 = 'eip155';
|
export const EIP155 = 'eip155';
|
||||||
export const COSMOS = 'cosmos';
|
export const COSMOS = 'cosmos';
|
||||||
|
export const DEFAULT_NETWORKS = [
|
||||||
export const DEFAULT_NETWORKS: NetworksFormData[] = [
|
|
||||||
{
|
|
||||||
chainId: 'laconic-testnet-2',
|
|
||||||
networkName: 'laconicd testnet-2',
|
|
||||||
namespace: COSMOS,
|
|
||||||
rpcUrl: import.meta.env.REACT_APP_LACONICD_RPC_URL!,
|
|
||||||
blockExplorerUrl: '',
|
|
||||||
nativeDenom: 'alnt',
|
|
||||||
addressPrefix: 'laconic',
|
|
||||||
coinType: '118',
|
|
||||||
gasPrice: '0.001',
|
|
||||||
isDefault: true,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
chainId: 'laconic_9000-1',
|
chainId: 'laconic_9000-1',
|
||||||
networkName: 'laconicd',
|
networkName: 'laconicd',
|
||||||
namespace: COSMOS,
|
namespace: COSMOS,
|
||||||
rpcUrl: "https://laconicd.laconic.com",
|
rpcUrl: process.env.REACT_APP_LACONICD_RPC_URL!,
|
||||||
blockExplorerUrl: '',
|
blockExplorerUrl: '',
|
||||||
nativeDenom: 'alnt',
|
nativeDenom: 'alnt',
|
||||||
addressPrefix: 'laconic',
|
addressPrefix: 'laconic',
|
||||||
coinType: '118',
|
coinType: '118',
|
||||||
gasPrice: '1',
|
gasPrice: '1',
|
||||||
isDefault: false,
|
isDefault: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
chainId: '1',
|
chainId: '1',
|
||||||
@ -62,30 +48,3 @@ export const INVALID_URL_ERROR = 'Invalid URL';
|
|||||||
export const IS_NUMBER_REGEX = /^\d+$/;
|
export const IS_NUMBER_REGEX = /^\d+$/;
|
||||||
|
|
||||||
export const IS_IMPORT_WALLET_ENABLED = false;
|
export const IS_IMPORT_WALLET_ENABLED = false;
|
||||||
|
|
||||||
// iframe request types
|
|
||||||
export const REQUEST_COSMOS_ACCOUNTS = 'REQUEST_COSMOS_ACCOUNTS';
|
|
||||||
export const REQUEST_SIGN_TX = 'REQUEST_SIGN_TX';
|
|
||||||
export const REQUEST_SIGN_MESSAGE = 'REQUEST_SIGN_MESSAGE';
|
|
||||||
export const REQUEST_WALLET_ACCOUNTS = 'REQUEST_WALLET_ACCOUNTS';
|
|
||||||
export const REQUEST_CREATE_OR_GET_ACCOUNTS = 'REQUEST_CREATE_OR_GET_ACCOUNTS';
|
|
||||||
export const REQUEST_TX = 'REQUEST_TX';
|
|
||||||
export const REQUEST_ACCOUNT_PK = 'REQUEST_ACCOUNT_PK';
|
|
||||||
export const REQUEST_ADD_ACCOUNT = 'REQUEST_ADD_ACCOUNT';
|
|
||||||
export const AUTO_SIGN_IN = 'AUTO_SIGN_IN';
|
|
||||||
export const CHECK_BALANCE = 'CHECK_BALANCE';
|
|
||||||
export const REQUEST_ADD_NETWORK = 'REQUEST_ADD_NETWORK';
|
|
||||||
|
|
||||||
// iframe response types
|
|
||||||
export const COSMOS_ACCOUNTS_RESPONSE = 'COSMOS_ACCOUNTS_RESPONSE';
|
|
||||||
export const SIGN_TX_RESPONSE = 'SIGN_TX_RESPONSE';
|
|
||||||
export const SIGN_MESSAGE_RESPONSE = 'SIGN_MESSAGE_RESPONSE';
|
|
||||||
export const TRANSACTION_RESPONSE = 'TRANSACTION_RESPONSE';
|
|
||||||
export const SIGN_IN_RESPONSE = 'SIGN_IN_RESPONSE';
|
|
||||||
export const ACCOUNT_PK_RESPONSE = 'ACCOUNT_PK_RESPONSE';
|
|
||||||
export const ADD_ACCOUNT_RESPONSE = 'ADD_ACCOUNT_RESPONSE';
|
|
||||||
export const WALLET_ACCOUNTS_DATA = 'WALLET_ACCOUNTS_DATA';
|
|
||||||
export const IS_SUFFICIENT = 'IS_SUFFICIENT';
|
|
||||||
export const NETWORK_ADDED_RESPONSE = "NETWORK_ADDED_RESPONSE";
|
|
||||||
export const NETWORK_ALREADY_EXISTS_RESPONSE = "NETWORK_ALREADY_EXISTS_RESPONSE";
|
|
||||||
export const NETWORK_ADD_FAILED_RESPONSE = "NETWORK_ADD_FAILED_RESPONSE";
|
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||
/* Importing this library provides react native with a secure random source.
|
/* 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" */
|
For more information, "visit https://docs.ethers.org/v5/cookbook/react-native/#cookbook-reactnative-security" */
|
||||||
import 'react-native-get-random-values';
|
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 '@ethersproject/shims';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -13,6 +9,10 @@ import {
|
|||||||
resetInternetCredentials,
|
resetInternetCredentials,
|
||||||
setInternetCredentials,
|
setInternetCredentials,
|
||||||
} from './key-store';
|
} from './key-store';
|
||||||
|
|
||||||
|
import { AccountData } from '@cosmjs/amino';
|
||||||
|
import { DirectSecp256k1HdWallet } from '@cosmjs/proto-signing';
|
||||||
|
import { stringToPath } from '@cosmjs/crypto';
|
||||||
import { EIP155 } from './constants';
|
import { EIP155 } from './constants';
|
||||||
import { NetworksDataState } from '../types';
|
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 {
|
export {
|
||||||
getMnemonic,
|
getMnemonic,
|
||||||
getPathKey,
|
getPathKey,
|
||||||
updateAccountIndices,
|
updateAccountIndices,
|
||||||
getHDPath,
|
getHDPath,
|
||||||
resetKeyServers,
|
resetKeyServers,
|
||||||
sendMessage,
|
|
||||||
checkSufficientFunds,
|
|
||||||
};
|
};
|
||||||
|
@ -3,14 +3,13 @@ For more information, "visit https://docs.ethers.org/v5/cookbook/react-native/#c
|
|||||||
import 'react-native-get-random-values';
|
import 'react-native-get-random-values';
|
||||||
|
|
||||||
import '@ethersproject/shims';
|
import '@ethersproject/shims';
|
||||||
import { fromBech32 } from '@cosmjs/encoding';
|
|
||||||
|
|
||||||
import { Wallet } from 'ethers';
|
import { Wallet } from 'ethers';
|
||||||
import { SignDoc } from 'cosmjs-types/cosmos/tx/v1beta1/tx';
|
import { SignDoc } from 'cosmjs-types/cosmos/tx/v1beta1/tx';
|
||||||
|
|
||||||
import { SignMessageParams } from '../types';
|
import { SignMessageParams } from '../types';
|
||||||
import { getDirectWallet, getMnemonic, getPathKey } from './misc';
|
import { getDirectWallet, getMnemonic, getPathKey } from './misc';
|
||||||
import { getCosmosAccountByHDPath } from './accounts';
|
import { getCosmosAccounts } from './accounts';
|
||||||
import { COSMOS, EIP155 } from './constants';
|
import { COSMOS, EIP155 } from './constants';
|
||||||
|
|
||||||
const signMessage = async ({
|
const signMessage = async ({
|
||||||
@ -25,7 +24,7 @@ const signMessage = async ({
|
|||||||
case EIP155:
|
case EIP155:
|
||||||
return await signEthMessage(message, accountId, chainId);
|
return await signEthMessage(message, accountId, chainId);
|
||||||
case COSMOS:
|
case COSMOS:
|
||||||
return await signCosmosMessage(message, path.path, path.address);
|
return await signCosmosMessage(message, path.path);
|
||||||
default:
|
default:
|
||||||
throw new Error('Invalid wallet type');
|
throw new Error('Invalid wallet type');
|
||||||
}
|
}
|
||||||
@ -52,13 +51,10 @@ const signEthMessage = async (
|
|||||||
const signCosmosMessage = async (
|
const signCosmosMessage = async (
|
||||||
message: string,
|
message: string,
|
||||||
path: string,
|
path: string,
|
||||||
cosmosAddress: string,
|
|
||||||
): Promise<string | undefined> => {
|
): Promise<string | undefined> => {
|
||||||
try {
|
try {
|
||||||
const mnemonic = await getMnemonic();
|
const mnemonic = await getMnemonic();
|
||||||
const addressPrefix = fromBech32(cosmosAddress).prefix
|
const cosmosAccount = await getCosmosAccounts(mnemonic, path);
|
||||||
|
|
||||||
const cosmosAccount = await getCosmosAccountByHDPath(mnemonic, path, addressPrefix);
|
|
||||||
const address = cosmosAccount.data.address;
|
const address = cosmosAccount.data.address;
|
||||||
const cosmosSignature = await cosmosAccount.cosmosWallet.signAmino(
|
const cosmosSignature = await cosmosAccount.cosmosWallet.signAmino(
|
||||||
address,
|
address,
|
||||||
|
@ -11,7 +11,7 @@ export let core: ICore;
|
|||||||
|
|
||||||
export async function createWeb3Wallet() {
|
export async function createWeb3Wallet() {
|
||||||
core = new Core({
|
core = new Core({
|
||||||
projectId: import.meta.env.REACT_APP_WALLET_CONNECT_PROJECT_ID,
|
projectId: process.env.REACT_APP_WALLET_CONNECT_PROJECT_ID,
|
||||||
});
|
});
|
||||||
|
|
||||||
const web3wallet = await Web3Wallet.init({
|
const web3wallet = await Web3Wallet.init({
|
||||||
|
@ -9,7 +9,6 @@ import {
|
|||||||
StdFee,
|
StdFee,
|
||||||
MsgSendEncodeObject
|
MsgSendEncodeObject
|
||||||
} from '@cosmjs/stargate';
|
} from '@cosmjs/stargate';
|
||||||
import { fromBech32 } from '@cosmjs/encoding';
|
|
||||||
import { EncodeObject } from '@cosmjs/proto-signing';
|
import { EncodeObject } from '@cosmjs/proto-signing';
|
||||||
import { LaconicClient } from '@cerc-io/registry-sdk';
|
import { LaconicClient } from '@cerc-io/registry-sdk';
|
||||||
import { Buffer } from 'buffer';
|
import { Buffer } from 'buffer';
|
||||||
@ -18,9 +17,8 @@ import { EIP155_SIGNING_METHODS } from './EIP155Data';
|
|||||||
import { signDirectMessage, signEthMessage } from '../sign-message';
|
import { signDirectMessage, signEthMessage } from '../sign-message';
|
||||||
import { Account } from '../../types';
|
import { Account } from '../../types';
|
||||||
import { getMnemonic, getPathKey } from '../misc';
|
import { getMnemonic, getPathKey } from '../misc';
|
||||||
import { getCosmosAccountByHDPath } from '../accounts';
|
import { getCosmosAccounts } from '../accounts';
|
||||||
import { COSMOS_METHODS } from './COSMOSData';
|
import { COSMOS_METHODS } from './COSMOSData';
|
||||||
import { COSMOS } from '../constants';
|
|
||||||
|
|
||||||
interface EthSendTransaction {
|
interface EthSendTransaction {
|
||||||
type: 'eth_sendTransaction';
|
type: 'eth_sendTransaction';
|
||||||
@ -82,13 +80,7 @@ export async function approveWalletConnectRequest(
|
|||||||
const path = (await getPathKey(`${namespace}:${chainId}`, account.index))
|
const path = (await getPathKey(`${namespace}:${chainId}`, account.index))
|
||||||
.path;
|
.path;
|
||||||
const mnemonic = await getMnemonic();
|
const mnemonic = await getMnemonic();
|
||||||
|
const cosmosAccount = await getCosmosAccounts(mnemonic, path);
|
||||||
let addressPrefix: string | undefined
|
|
||||||
if (namespace === COSMOS) {
|
|
||||||
addressPrefix = fromBech32(account.address).prefix
|
|
||||||
}
|
|
||||||
|
|
||||||
const cosmosAccount = await getCosmosAccountByHDPath(mnemonic, path, addressPrefix);
|
|
||||||
const address = account.address;
|
const address = account.address;
|
||||||
|
|
||||||
switch (request.method) {
|
switch (request.method) {
|
||||||
|
@ -6,16 +6,12 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
CERC_SCRIPT_DEBUG: ${CERC_SCRIPT_DEBUG}
|
CERC_SCRIPT_DEBUG: ${CERC_SCRIPT_DEBUG}
|
||||||
WALLET_CONNECT_ID: ${WALLET_CONNECT_ID}
|
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_DEFAULT_GAS_PRICE: ${CERC_DEFAULT_GAS_PRICE:-0.025}
|
||||||
CERC_GAS_ADJUSTMENT: ${CERC_GAS_ADJUSTMENT:-2}
|
CERC_GAS_ADJUSTMENT: ${CERC_GAS_ADJUSTMENT:-2}
|
||||||
CERC_LACONICD_RPC_URL: ${CERC_LACONICD_RPC_URL:-https://laconicd.laconic.com}
|
CERC_LACONICD_RPC_URL: ${CERC_LACONICD_RPC_URL:-https://laconicd.laconic.com}
|
||||||
CERC_ZENITHD_RPC_URL: ${CERC_ZENITHD_RPC_URL}
|
|
||||||
CERC_ALLOWED_URLS: ${CERC_ALLOWED_URLS}
|
|
||||||
command: ["bash", "/scripts/run.sh"]
|
command: ["bash", "/scripts/run.sh"]
|
||||||
volumes:
|
volumes:
|
||||||
- ../config/app/run.sh:/scripts/run.sh
|
- ../config/app/run.sh:/scripts/run.sh
|
||||||
- ../config/app/serve.json:/app/serve.json
|
|
||||||
ports:
|
ports:
|
||||||
- "80"
|
- "80"
|
||||||
healthcheck:
|
healthcheck:
|
||||||
|
@ -10,25 +10,12 @@ echo "WALLET_CONNECT_ID: ${WALLET_CONNECT_ID}"
|
|||||||
echo "CERC_DEFAULT_GAS_PRICE: ${CERC_DEFAULT_GAS_PRICE}"
|
echo "CERC_DEFAULT_GAS_PRICE: ${CERC_DEFAULT_GAS_PRICE}"
|
||||||
echo "CERC_GAS_ADJUSTMENT: ${CERC_GAS_ADJUSTMENT}"
|
echo "CERC_GAS_ADJUSTMENT: ${CERC_GAS_ADJUSTMENT}"
|
||||||
echo "CERC_LACONICD_RPC_URL: ${CERC_LACONICD_RPC_URL}"
|
echo "CERC_LACONICD_RPC_URL: ${CERC_LACONICD_RPC_URL}"
|
||||||
echo "CERC_ALLOWED_URLS: ${CERC_ALLOWED_URLS}"
|
|
||||||
|
|
||||||
# Build with required env
|
# Build with required env
|
||||||
export REACT_APP_WALLET_CONNECT_PROJECT_ID=$WALLET_CONNECT_ID
|
REACT_APP_WALLET_CONNECT_PROJECT_ID=$WALLET_CONNECT_ID \
|
||||||
export REACT_APP_DEFAULT_GAS_PRICE=$CERC_DEFAULT_GAS_PRICE
|
REACT_APP_DEFAULT_GAS_PRICE=$CERC_DEFAULT_GAS_PRICE \
|
||||||
export REACT_APP_GAS_ADJUSTMENT=$CERC_GAS_ADJUSTMENT
|
REACT_APP_GAS_ADJUSTMENT=$CERC_GAS_ADJUSTMENT \
|
||||||
export REACT_APP_LACONICD_RPC_URL=$CERC_LACONICD_RPC_URL
|
REACT_APP_LACONICD_RPC_URL=$CERC_LACONICD_RPC_URL \
|
||||||
export REACT_APP_ALLOWED_URLS=$CERC_ALLOWED_URLS
|
yarn build
|
||||||
|
|
||||||
# Set env variables in build
|
http-server --proxy http://localhost:80? -p 80 /app/build
|
||||||
yarn set-env
|
|
||||||
|
|
||||||
# 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 with explicit config
|
|
||||||
serve -s -l 80 -c /app/serve.json /app/build
|
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
{
|
|
||||||
"headers": [
|
|
||||||
{
|
|
||||||
"source": "/index.html",
|
|
||||||
"headers": [
|
|
||||||
{
|
|
||||||
"key": "Cache-Control",
|
|
||||||
"value": "no-cache, must-revalidate"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "/static/**/*.{js,css,png,jpg,jpeg,gif,svg,ico}",
|
|
||||||
"headers": [
|
|
||||||
{
|
|
||||||
"key": "Cache-Control",
|
|
||||||
"value": "public, max-age=31536000, immutable"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,6 +1,6 @@
|
|||||||
# Originally from: https://github.com/devcontainers/images/blob/main/src/javascript-node/.devcontainer/Dockerfile
|
# Originally from: https://github.com/devcontainers/images/blob/main/src/javascript-node/.devcontainer/Dockerfile
|
||||||
# [Choice] Node.js version (use -bullseye variants on local arm64/Apple Silicon): 18, 16, 14, 18-bullseye, 16-bullseye, 14-bullseye, 18-buster, 16-buster, 14-buster
|
# [Choice] Node.js version (use -bullseye variants on local arm64/Apple Silicon): 18, 16, 14, 18-bullseye, 16-bullseye, 14-bullseye, 18-buster, 16-buster, 14-buster
|
||||||
ARG VARIANT=22-bullseye
|
ARG VARIANT=18-bullseye
|
||||||
FROM node:${VARIANT}
|
FROM node:${VARIANT}
|
||||||
|
|
||||||
ARG USERNAME=node
|
ARG USERNAME=node
|
||||||
@ -33,11 +33,11 @@ RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
|
|||||||
RUN mkdir -p /scripts
|
RUN mkdir -p /scripts
|
||||||
|
|
||||||
# Install simple web server for now (use nginx perhaps later)
|
# Install simple web server for now (use nginx perhaps later)
|
||||||
RUN yarn global add serve
|
RUN yarn global add http-server
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN yarn install && yarn build
|
RUN yarn install
|
||||||
|
|
||||||
# Expose port for http
|
# Expose port for http
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
|
@ -7,7 +7,7 @@ Instructions for running the `laconic-wallet-web` using [laconic-so](https://git
|
|||||||
* Clone the stack repo:
|
* Clone the stack repo:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
laconic-so fetch-stack git.vdb.to/LaconicNetwork/laconic-wallet-web
|
laconic-so fetch-stack git.vdb.to/cerc-io/laconic-wallet-web
|
||||||
```
|
```
|
||||||
|
|
||||||
* Build the container image:
|
* Build the container image:
|
||||||
@ -49,14 +49,8 @@ Instructions for running the `laconic-wallet-web` using [laconic-so](https://git
|
|||||||
# WalletConnect project ID, same should be used in the laconic-wallet
|
# WalletConnect project ID, same should be used in the laconic-wallet
|
||||||
WALLET_CONNECT_ID=
|
WALLET_CONNECT_ID=
|
||||||
|
|
||||||
# Allowed urls is a comma separated list of allowed urls
|
|
||||||
CERC_ALLOWED_URLS=
|
|
||||||
|
|
||||||
# Optional
|
# Optional
|
||||||
|
|
||||||
# WalletConnect code for hostname verification
|
|
||||||
WALLET_CONNECT_VERIFY_CODE=
|
|
||||||
|
|
||||||
# Default gas price for txs (default: 0.025)
|
# Default gas price for txs (default: 0.025)
|
||||||
CERC_DEFAULT_GAS_PRICE=
|
CERC_DEFAULT_GAS_PRICE=
|
||||||
|
|
||||||
@ -66,9 +60,6 @@ Instructions for running the `laconic-wallet-web` using [laconic-so](https://git
|
|||||||
|
|
||||||
# RPC endpoint of laconicd node (default: https://laconicd.laconic.com)
|
# RPC endpoint of laconicd node (default: https://laconicd.laconic.com)
|
||||||
CERC_LACONICD_RPC_URL=
|
CERC_LACONICD_RPC_URL=
|
||||||
|
|
||||||
# Zenith RPC endpoint
|
|
||||||
CERC_ZENITHD_RPC_URL=
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Start the deployment
|
## Start the deployment
|
||||||
|
194
yarn.lock
194
yarn.lock
@ -1410,15 +1410,6 @@
|
|||||||
bech32 "^1.1.4"
|
bech32 "^1.1.4"
|
||||||
readonly-date "^1.0.0"
|
readonly-date "^1.0.0"
|
||||||
|
|
||||||
"@cosmjs/encoding@^0.33.1":
|
|
||||||
version "0.33.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/@cosmjs/encoding/-/encoding-0.33.1.tgz#77d6a8e0152c658ecf07b5aee3f5968d9071da50"
|
|
||||||
integrity sha512-nuNxf29fUcQE14+1p//VVQDwd1iau5lhaW/7uMz7V2AH3GJbFJoJVaKvVyZvdFk+Cnu+s3wCqgq4gJkhRCJfKw==
|
|
||||||
dependencies:
|
|
||||||
base64-js "^1.3.0"
|
|
||||||
bech32 "^1.1.4"
|
|
||||||
readonly-date "^1.0.0"
|
|
||||||
|
|
||||||
"@cosmjs/json-rpc@^0.32.4":
|
"@cosmjs/json-rpc@^0.32.4":
|
||||||
version "0.32.4"
|
version "0.32.4"
|
||||||
resolved "https://registry.yarnpkg.com/@cosmjs/json-rpc/-/json-rpc-0.32.4.tgz#be91eb89ea78bd5dc02d0a9fa184dd6790790f0b"
|
resolved "https://registry.yarnpkg.com/@cosmjs/json-rpc/-/json-rpc-0.32.4.tgz#be91eb89ea78bd5dc02d0a9fa184dd6790790f0b"
|
||||||
@ -2197,37 +2188,6 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3"
|
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3"
|
||||||
integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==
|
integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==
|
||||||
|
|
||||||
"@import-meta-env/cli@^0.7.3":
|
|
||||||
version "0.7.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/@import-meta-env/cli/-/cli-0.7.3.tgz#95a01da7db11ee92c4802dbe04e198d602ee7296"
|
|
||||||
integrity sha512-7xSPYhpXr0tulKk7Xv332fKRmoTwNUI+6eWUwgekNeRCNUvWsy9C0MfFk2rCDc43gGDJOywb1LxulthWpxFX1g==
|
|
||||||
dependencies:
|
|
||||||
commander "13.1.0"
|
|
||||||
dotenv "^16.0.0"
|
|
||||||
glob "11.0.1"
|
|
||||||
picocolors "1.1.1"
|
|
||||||
serialize-javascript "6.0.2"
|
|
||||||
|
|
||||||
"@import-meta-env/typescript@^0.4.0":
|
|
||||||
version "0.4.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@import-meta-env/typescript/-/typescript-0.4.0.tgz#e0f0eaa8312a70bf9961b5f73ddbb6db66b8263c"
|
|
||||||
integrity sha512-SHU8u4H/RAaJgTWPOfUKUFP5Dfg3iLy6ovCTOxqbsgOQyIfS+TgRy9EFCMxmuub1ULo77FAJ5nRwf2z3BenXsA==
|
|
||||||
dependencies:
|
|
||||||
commander "^12.0.0"
|
|
||||||
dotenv "^16.0.0"
|
|
||||||
picocolors "^1.0.0"
|
|
||||||
|
|
||||||
"@import-meta-env/unplugin@^0.6.2":
|
|
||||||
version "0.6.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/@import-meta-env/unplugin/-/unplugin-0.6.2.tgz#adca83faa42e0ca846b064fd42dd3de529dde6c1"
|
|
||||||
integrity sha512-m8TEQTgWekSkhlT9lkHBKQ4TDf5l8+BWvO6q/cxcsv1AvyfsOXUOHbvjhKSiVDaz/CDDCbOWc/aOAiPFRzcXGA==
|
|
||||||
dependencies:
|
|
||||||
dotenv "^16.0.0"
|
|
||||||
magic-string "^0.30.0"
|
|
||||||
object-hash "^3.0.0"
|
|
||||||
picocolors "^1.0.0"
|
|
||||||
unplugin "^2.0.0"
|
|
||||||
|
|
||||||
"@ipld/dag-cbor@^7.0.1":
|
"@ipld/dag-cbor@^7.0.1":
|
||||||
version "7.0.3"
|
version "7.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/@ipld/dag-cbor/-/dag-cbor-7.0.3.tgz#aa31b28afb11a807c3d627828a344e5521ac4a1e"
|
resolved "https://registry.yarnpkg.com/@ipld/dag-cbor/-/dag-cbor-7.0.3.tgz#aa31b28afb11a807c3d627828a344e5521ac4a1e"
|
||||||
@ -2580,7 +2540,7 @@
|
|||||||
"@jridgewell/gen-mapping" "^0.3.5"
|
"@jridgewell/gen-mapping" "^0.3.5"
|
||||||
"@jridgewell/trace-mapping" "^0.3.25"
|
"@jridgewell/trace-mapping" "^0.3.25"
|
||||||
|
|
||||||
"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0":
|
"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14":
|
||||||
version "1.5.0"
|
version "1.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a"
|
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a"
|
||||||
integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==
|
integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==
|
||||||
@ -3979,11 +3939,6 @@
|
|||||||
expect "^29.0.0"
|
expect "^29.0.0"
|
||||||
pretty-format "^29.0.0"
|
pretty-format "^29.0.0"
|
||||||
|
|
||||||
"@types/json-bigint@^1.0.4":
|
|
||||||
version "1.0.4"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/json-bigint/-/json-bigint-1.0.4.tgz#250d29e593375499d8ba6efaab22d094c3199ef3"
|
|
||||||
integrity sha512-ydHooXLbOmxBbubnA7Eh+RpBzuaIiQjh8WGJYQB50JFGFrdxW7JzVlyEV7fAXw0T2sqJ1ysTneJbiyNLqZRAag==
|
|
||||||
|
|
||||||
"@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.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":
|
||||||
version "7.0.15"
|
version "7.0.15"
|
||||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
|
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
|
||||||
@ -4803,11 +4758,6 @@ acorn@^8.11.3, acorn@^8.2.4, acorn@^8.7.1, acorn@^8.8.2, acorn@^8.9.0:
|
|||||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248"
|
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248"
|
||||||
integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==
|
integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==
|
||||||
|
|
||||||
acorn@^8.14.1:
|
|
||||||
version "8.14.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.1.tgz#721d5dc10f7d5b5609a891773d47731796935dfb"
|
|
||||||
integrity sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==
|
|
||||||
|
|
||||||
address@^1.0.1, address@^1.1.2:
|
address@^1.0.1, address@^1.1.2:
|
||||||
version "1.2.2"
|
version "1.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/address/-/address-1.2.2.tgz#2b5248dac5485a6390532c6a517fda2e3faac89e"
|
resolved "https://registry.yarnpkg.com/address/-/address-1.2.2.tgz#2b5248dac5485a6390532c6a517fda2e3faac89e"
|
||||||
@ -5425,11 +5375,6 @@ big.js@^5.2.2:
|
|||||||
resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
|
resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
|
||||||
integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==
|
integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==
|
||||||
|
|
||||||
bignumber.js@^9.0.0:
|
|
||||||
version "9.3.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.3.0.tgz#bdba7e2a4c1a2eba08290e8dcad4f36393c92acd"
|
|
||||||
integrity sha512-EM7aMFTXbptt/wZdMlBv2t8IViwQL+h6SLHosp8Yf0dqJMTnY6iL32opnAB6kAdL0SZPuvcAzFr31o0c/R3/RA==
|
|
||||||
|
|
||||||
binary-extensions@^2.0.0:
|
binary-extensions@^2.0.0:
|
||||||
version "2.3.0"
|
version "2.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522"
|
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522"
|
||||||
@ -6050,16 +5995,6 @@ command-exists@^1.2.8:
|
|||||||
resolved "https://registry.yarnpkg.com/command-exists/-/command-exists-1.2.9.tgz#c50725af3808c8ab0260fd60b01fbfa25b954f69"
|
resolved "https://registry.yarnpkg.com/command-exists/-/command-exists-1.2.9.tgz#c50725af3808c8ab0260fd60b01fbfa25b954f69"
|
||||||
integrity sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==
|
integrity sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==
|
||||||
|
|
||||||
commander@13.1.0:
|
|
||||||
version "13.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/commander/-/commander-13.1.0.tgz#776167db68c78f38dcce1f9b8d7b8b9a488abf46"
|
|
||||||
integrity sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==
|
|
||||||
|
|
||||||
commander@^12.0.0:
|
|
||||||
version "12.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/commander/-/commander-12.1.0.tgz#01423b36f501259fdaac4d0e4d60c96c991585d3"
|
|
||||||
integrity sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==
|
|
||||||
|
|
||||||
commander@^2.20.0:
|
commander@^2.20.0:
|
||||||
version "2.20.3"
|
version "2.20.3"
|
||||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
|
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
|
||||||
@ -6927,11 +6862,6 @@ dotenv@^10.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81"
|
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81"
|
||||||
integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==
|
integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==
|
||||||
|
|
||||||
dotenv@^16.0.0, dotenv@^16.5.0:
|
|
||||||
version "16.5.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.5.0.tgz#092b49f25f808f020050051d1ff258e404c78692"
|
|
||||||
integrity sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==
|
|
||||||
|
|
||||||
duplexer@^0.1.2:
|
duplexer@^0.1.2:
|
||||||
version "0.1.2"
|
version "0.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6"
|
resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6"
|
||||||
@ -8175,18 +8105,6 @@ glob-to-regexp@^0.4.1:
|
|||||||
resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e"
|
resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e"
|
||||||
integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==
|
integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==
|
||||||
|
|
||||||
glob@11.0.1:
|
|
||||||
version "11.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/glob/-/glob-11.0.1.tgz#1c3aef9a59d680e611b53dcd24bb8639cef064d9"
|
|
||||||
integrity sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==
|
|
||||||
dependencies:
|
|
||||||
foreground-child "^3.1.0"
|
|
||||||
jackspeak "^4.0.1"
|
|
||||||
minimatch "^10.0.0"
|
|
||||||
minipass "^7.1.2"
|
|
||||||
package-json-from-dist "^1.0.0"
|
|
||||||
path-scurry "^2.0.0"
|
|
||||||
|
|
||||||
glob@^10.3.10:
|
glob@^10.3.10:
|
||||||
version "10.4.5"
|
version "10.4.5"
|
||||||
resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956"
|
resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956"
|
||||||
@ -9193,13 +9111,6 @@ jackspeak@^3.1.2:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
"@pkgjs/parseargs" "^0.11.0"
|
"@pkgjs/parseargs" "^0.11.0"
|
||||||
|
|
||||||
jackspeak@^4.0.1:
|
|
||||||
version "4.1.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-4.1.1.tgz#96876030f450502047fc7e8c7fcf8ce8124e43ae"
|
|
||||||
integrity sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==
|
|
||||||
dependencies:
|
|
||||||
"@isaacs/cliui" "^8.0.2"
|
|
||||||
|
|
||||||
jake@^10.8.5:
|
jake@^10.8.5:
|
||||||
version "10.9.2"
|
version "10.9.2"
|
||||||
resolved "https://registry.yarnpkg.com/jake/-/jake-10.9.2.tgz#6ae487e6a69afec3a5e167628996b59f35ae2b7f"
|
resolved "https://registry.yarnpkg.com/jake/-/jake-10.9.2.tgz#6ae487e6a69afec3a5e167628996b59f35ae2b7f"
|
||||||
@ -9911,13 +9822,6 @@ jsesc@~0.5.0:
|
|||||||
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d"
|
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d"
|
||||||
integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==
|
integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==
|
||||||
|
|
||||||
json-bigint@^1.0.0:
|
|
||||||
version "1.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/json-bigint/-/json-bigint-1.0.0.tgz#ae547823ac0cad8398667f8cd9ef4730f5b01ff1"
|
|
||||||
integrity sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==
|
|
||||||
dependencies:
|
|
||||||
bignumber.js "^9.0.0"
|
|
||||||
|
|
||||||
json-buffer@3.0.1:
|
json-buffer@3.0.1:
|
||||||
version "3.0.1"
|
version "3.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13"
|
resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13"
|
||||||
@ -10287,11 +10191,6 @@ lru-cache@^10.2.0:
|
|||||||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119"
|
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119"
|
||||||
integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==
|
integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==
|
||||||
|
|
||||||
lru-cache@^11.0.0:
|
|
||||||
version "11.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.1.0.tgz#afafb060607108132dbc1cf8ae661afb69486117"
|
|
||||||
integrity sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==
|
|
||||||
|
|
||||||
lru-cache@^5.1.1:
|
lru-cache@^5.1.1:
|
||||||
version "5.1.1"
|
version "5.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920"
|
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920"
|
||||||
@ -10311,13 +10210,6 @@ magic-string@^0.25.0, magic-string@^0.25.7:
|
|||||||
dependencies:
|
dependencies:
|
||||||
sourcemap-codec "^1.4.8"
|
sourcemap-codec "^1.4.8"
|
||||||
|
|
||||||
magic-string@^0.30.0:
|
|
||||||
version "0.30.17"
|
|
||||||
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.17.tgz#450a449673d2460e5bbcfba9a61916a1714c7453"
|
|
||||||
integrity sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==
|
|
||||||
dependencies:
|
|
||||||
"@jridgewell/sourcemap-codec" "^1.5.0"
|
|
||||||
|
|
||||||
make-dir@^2.0.0, make-dir@^2.1.0:
|
make-dir@^2.0.0, make-dir@^2.1.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5"
|
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5"
|
||||||
@ -10685,13 +10577,6 @@ minimatch@9.0.3:
|
|||||||
dependencies:
|
dependencies:
|
||||||
brace-expansion "^2.0.1"
|
brace-expansion "^2.0.1"
|
||||||
|
|
||||||
minimatch@^10.0.0:
|
|
||||||
version "10.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.0.1.tgz#ce0521856b453c86e25f2c4c0d03e6ff7ddc440b"
|
|
||||||
integrity sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==
|
|
||||||
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:
|
minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
|
||||||
version "3.1.2"
|
version "3.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
|
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
|
||||||
@ -11330,14 +11215,6 @@ path-scurry@^1.11.1:
|
|||||||
lru-cache "^10.2.0"
|
lru-cache "^10.2.0"
|
||||||
minipass "^5.0.0 || ^6.0.2 || ^7.0.0"
|
minipass "^5.0.0 || ^6.0.2 || ^7.0.0"
|
||||||
|
|
||||||
path-scurry@^2.0.0:
|
|
||||||
version "2.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-2.0.0.tgz#9f052289f23ad8bf9397a2a0425e7b8615c58580"
|
|
||||||
integrity sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==
|
|
||||||
dependencies:
|
|
||||||
lru-cache "^11.0.0"
|
|
||||||
minipass "^7.1.2"
|
|
||||||
|
|
||||||
path-to-regexp@0.1.7:
|
path-to-regexp@0.1.7:
|
||||||
version "0.1.7"
|
version "0.1.7"
|
||||||
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
|
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
|
||||||
@ -11369,11 +11246,6 @@ performance-now@^2.1.0:
|
|||||||
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
|
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
|
||||||
integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==
|
integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==
|
||||||
|
|
||||||
picocolors@1.1.1:
|
|
||||||
version "1.1.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
|
|
||||||
integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
|
|
||||||
|
|
||||||
picocolors@^0.2.1:
|
picocolors@^0.2.1:
|
||||||
version "0.2.1"
|
version "0.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-0.2.1.tgz#570670f793646851d1ba135996962abad587859f"
|
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-0.2.1.tgz#570670f793646851d1ba135996962abad587859f"
|
||||||
@ -11389,11 +11261,6 @@ picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3, picomatc
|
|||||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
|
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
|
||||||
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
|
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
|
||||||
|
|
||||||
picomatch@^4.0.2:
|
|
||||||
version "4.0.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.2.tgz#77c742931e8f3b8820946c76cd0c1f13730d1dab"
|
|
||||||
integrity sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==
|
|
||||||
|
|
||||||
pify@^2.3.0:
|
pify@^2.3.0:
|
||||||
version "2.3.0"
|
version "2.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
|
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
|
||||||
@ -13067,13 +12934,6 @@ serialize-error@^2.1.0:
|
|||||||
resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-2.1.0.tgz#50b679d5635cdf84667bdc8e59af4e5b81d5f60a"
|
resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-2.1.0.tgz#50b679d5635cdf84667bdc8e59af4e5b81d5f60a"
|
||||||
integrity sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw==
|
integrity sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw==
|
||||||
|
|
||||||
serialize-javascript@6.0.2, serialize-javascript@^6.0.0, serialize-javascript@^6.0.1:
|
|
||||||
version "6.0.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2"
|
|
||||||
integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==
|
|
||||||
dependencies:
|
|
||||||
randombytes "^2.1.0"
|
|
||||||
|
|
||||||
serialize-javascript@^4.0.0:
|
serialize-javascript@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa"
|
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa"
|
||||||
@ -13081,6 +12941,13 @@ serialize-javascript@^4.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
randombytes "^2.1.0"
|
randombytes "^2.1.0"
|
||||||
|
|
||||||
|
serialize-javascript@^6.0.0, serialize-javascript@^6.0.1:
|
||||||
|
version "6.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2"
|
||||||
|
integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==
|
||||||
|
dependencies:
|
||||||
|
randombytes "^2.1.0"
|
||||||
|
|
||||||
serve-index@^1.9.1:
|
serve-index@^1.9.1:
|
||||||
version "1.9.1"
|
version "1.9.1"
|
||||||
resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239"
|
resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239"
|
||||||
@ -13463,16 +13330,7 @@ string-natural-compare@^3.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4"
|
resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4"
|
||||||
integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==
|
integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==
|
||||||
|
|
||||||
"string-width-cjs@npm:string-width@^4.2.0":
|
"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
|
||||||
version "4.2.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
|
||||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
|
||||||
dependencies:
|
|
||||||
emoji-regex "^8.0.0"
|
|
||||||
is-fullwidth-code-point "^3.0.0"
|
|
||||||
strip-ansi "^6.0.1"
|
|
||||||
|
|
||||||
string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
|
|
||||||
version "4.2.3"
|
version "4.2.3"
|
||||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||||
@ -13575,7 +13433,7 @@ stringify-object@^3.3.0:
|
|||||||
is-obj "^1.0.1"
|
is-obj "^1.0.1"
|
||||||
is-regexp "^1.0.0"
|
is-regexp "^1.0.0"
|
||||||
|
|
||||||
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
|
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
||||||
version "6.0.1"
|
version "6.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
||||||
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
||||||
@ -13589,13 +13447,6 @@ strip-ansi@^5.0.0, strip-ansi@^5.2.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
ansi-regex "^4.1.0"
|
ansi-regex "^4.1.0"
|
||||||
|
|
||||||
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
|
||||||
version "6.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
|
||||||
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
|
||||||
dependencies:
|
|
||||||
ansi-regex "^5.0.1"
|
|
||||||
|
|
||||||
strip-ansi@^7.0.1:
|
strip-ansi@^7.0.1:
|
||||||
version "7.1.0"
|
version "7.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
|
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
|
||||||
@ -14268,15 +14119,6 @@ unpipe@1.0.0, unpipe@~1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
|
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
|
||||||
integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==
|
integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==
|
||||||
|
|
||||||
unplugin@^2.0.0:
|
|
||||||
version "2.3.5"
|
|
||||||
resolved "https://registry.yarnpkg.com/unplugin/-/unplugin-2.3.5.tgz#c689d806e2a15c95aeb794f285356c6bcdea4a2e"
|
|
||||||
integrity sha512-RyWSb5AHmGtjjNQ6gIlA67sHOsWpsbWpwDokLwTcejVdOjEkJZh7QKu14J00gDDVSh8kGH4KYC/TNBceXFZhtw==
|
|
||||||
dependencies:
|
|
||||||
acorn "^8.14.1"
|
|
||||||
picomatch "^4.0.2"
|
|
||||||
webpack-virtual-modules "^0.6.2"
|
|
||||||
|
|
||||||
unquote@~1.1.1:
|
unquote@~1.1.1:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/unquote/-/unquote-1.1.1.tgz#8fded7324ec6e88a0ff8b905e7c098cdc086d544"
|
resolved "https://registry.yarnpkg.com/unquote/-/unquote-1.1.1.tgz#8fded7324ec6e88a0ff8b905e7c098cdc086d544"
|
||||||
@ -14564,11 +14406,6 @@ webpack-sources@^3.2.3:
|
|||||||
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde"
|
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde"
|
||||||
integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==
|
integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==
|
||||||
|
|
||||||
webpack-virtual-modules@^0.6.2:
|
|
||||||
version "0.6.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz#057faa9065c8acf48f24cb57ac0e77739ab9a7e8"
|
|
||||||
integrity sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==
|
|
||||||
|
|
||||||
webpack@^5.64.4:
|
webpack@^5.64.4:
|
||||||
version "5.93.0"
|
version "5.93.0"
|
||||||
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.93.0.tgz#2e89ec7035579bdfba9760d26c63ac5c3462a5e5"
|
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.93.0.tgz#2e89ec7035579bdfba9760d26c63ac5c3462a5e5"
|
||||||
@ -14906,7 +14743,7 @@ workbox-window@6.6.1:
|
|||||||
"@types/trusted-types" "^2.0.2"
|
"@types/trusted-types" "^2.0.2"
|
||||||
workbox-core "6.6.1"
|
workbox-core "6.6.1"
|
||||||
|
|
||||||
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
|
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
|
||||||
version "7.0.0"
|
version "7.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
|
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
|
||||||
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
|
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
|
||||||
@ -14924,15 +14761,6 @@ wrap-ansi@^6.2.0:
|
|||||||
string-width "^4.1.0"
|
string-width "^4.1.0"
|
||||||
strip-ansi "^6.0.0"
|
strip-ansi "^6.0.0"
|
||||||
|
|
||||||
wrap-ansi@^7.0.0:
|
|
||||||
version "7.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
|
|
||||||
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
|
|
||||||
dependencies:
|
|
||||||
ansi-styles "^4.0.0"
|
|
||||||
string-width "^4.1.0"
|
|
||||||
strip-ansi "^6.0.0"
|
|
||||||
|
|
||||||
wrap-ansi@^8.1.0:
|
wrap-ansi@^8.1.0:
|
||||||
version "8.1.0"
|
version "8.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"
|
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"
|
||||||
|
Loading…
Reference in New Issue
Block a user