forked from cerc-io/laconic-wallet-web
Compare commits
No commits in common. "162efe377cd32ea0d3e0716fc7b7ffca23b43ae6" and "4d5fd722fb3784dea6a6fa89984fb7ee9aaebf67" have entirely different histories.
162efe377c
...
4d5fd722fb
@ -3,7 +3,7 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cerc-io/registry-sdk": "^0.2.5",
|
"@cerc-io/registry-sdk": "^0.2.3",
|
||||||
"@cosmjs/amino": "^0.32.3",
|
"@cosmjs/amino": "^0.32.3",
|
||||||
"@cosmjs/crypto": "^0.32.3",
|
"@cosmjs/crypto": "^0.32.3",
|
||||||
"@cosmjs/proto-signing": "^0.32.3",
|
"@cosmjs/proto-signing": "^0.32.3",
|
||||||
|
@ -23,6 +23,7 @@ import AddSession from './screens/AddSession';
|
|||||||
import WalletConnect from './screens/WalletConnect';
|
import WalletConnect from './screens/WalletConnect';
|
||||||
import ApproveTransaction from './screens/ApproveTransaction';
|
import ApproveTransaction from './screens/ApproveTransaction';
|
||||||
import { StackParamsList } from './types';
|
import { StackParamsList } from './types';
|
||||||
|
import { web3wallet } from './utils/wallet-connect/WalletConnectUtils';
|
||||||
import { EIP155_SIGNING_METHODS } from './utils/wallet-connect/EIP155Data';
|
import { EIP155_SIGNING_METHODS } from './utils/wallet-connect/EIP155Data';
|
||||||
import { getSignParamsMessage } from './utils/wallet-connect/helpers';
|
import { getSignParamsMessage } from './utils/wallet-connect/helpers';
|
||||||
import ApproveTransfer from './screens/ApproveTransfer';
|
import ApproveTransfer from './screens/ApproveTransfer';
|
||||||
@ -39,7 +40,7 @@ const App = (): React.JSX.Element => {
|
|||||||
const navigation =
|
const navigation =
|
||||||
useNavigation<StackNavigationProp<StackParamsList>>();
|
useNavigation<StackNavigationProp<StackParamsList>>();
|
||||||
|
|
||||||
const { web3wallet, setActiveSessions } = useWalletConnect();
|
const { setActiveSessions } = useWalletConnect();
|
||||||
const { accounts, setCurrentIndex } = useAccounts();
|
const { accounts, setCurrentIndex } = useAccounts();
|
||||||
const { networksData, selectedNetwork, setSelectedNetwork } = useNetworks();
|
const { networksData, selectedNetwork, setSelectedNetwork } = useNetworks();
|
||||||
const [modalVisible, setModalVisible] = useState(false);
|
const [modalVisible, setModalVisible] = useState(false);
|
||||||
@ -61,7 +62,7 @@ const App = (): React.JSX.Element => {
|
|||||||
setModalVisible(true);
|
setModalVisible(true);
|
||||||
setCurrentProposal(proposal);
|
setCurrentProposal(proposal);
|
||||||
},
|
},
|
||||||
[accounts, web3wallet],
|
[accounts],
|
||||||
);
|
);
|
||||||
|
|
||||||
const onSessionRequest = useCallback(
|
const onSessionRequest = useCallback(
|
||||||
@ -195,14 +196,13 @@ const App = (): React.JSX.Element => {
|
|||||||
setSelectedNetwork,
|
setSelectedNetwork,
|
||||||
setCurrentIndex,
|
setCurrentIndex,
|
||||||
selectedNetwork,
|
selectedNetwork,
|
||||||
web3wallet
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
const onSessionDelete = useCallback(() => {
|
const onSessionDelete = useCallback(() => {
|
||||||
const sessions = web3wallet!.getActiveSessions();
|
const sessions = web3wallet!.getActiveSessions();
|
||||||
setActiveSessions(sessions);
|
setActiveSessions(sessions);
|
||||||
}, [setActiveSessions, web3wallet]);
|
}, [setActiveSessions]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
web3wallet?.on('session_proposal', onSessionProposal);
|
web3wallet?.on('session_proposal', onSessionProposal);
|
||||||
|
@ -11,7 +11,7 @@ import styles from '../styles/stylesheet';
|
|||||||
import HDPathDialog from './HDPathDialog';
|
import HDPathDialog from './HDPathDialog';
|
||||||
import AccountDetails from './AccountDetails';
|
import AccountDetails from './AccountDetails';
|
||||||
import { useAccounts } from '../context/AccountsContext';
|
import { useAccounts } from '../context/AccountsContext';
|
||||||
import { useWalletConnect } from '../context/WalletConnectContext';
|
import { web3wallet } from '../utils/wallet-connect/WalletConnectUtils';
|
||||||
import { useNetworks } from '../context/NetworksContext';
|
import { useNetworks } from '../context/NetworksContext';
|
||||||
import ConfirmDialog from './ConfirmDialog';
|
import ConfirmDialog from './ConfirmDialog';
|
||||||
import { getNamespaces } from '../utils/wallet-connect/helpers';
|
import { getNamespaces } from '../utils/wallet-connect/helpers';
|
||||||
@ -26,8 +26,6 @@ const Accounts = () => {
|
|||||||
useAccounts();
|
useAccounts();
|
||||||
const { networksData, selectedNetwork, setNetworksData, setSelectedNetwork } =
|
const { networksData, selectedNetwork, setNetworksData, setSelectedNetwork } =
|
||||||
useNetworks();
|
useNetworks();
|
||||||
|
|
||||||
const {web3wallet} = useWalletConnect();
|
|
||||||
const [expanded, setExpanded] = useState(false);
|
const [expanded, setExpanded] = useState(false);
|
||||||
const [isAccountCreating, setIsAccountCreating] = useState(false);
|
const [isAccountCreating, setIsAccountCreating] = useState(false);
|
||||||
const [hdDialog, setHdDialog] = useState(false);
|
const [hdDialog, setHdDialog] = useState(false);
|
||||||
@ -76,7 +74,7 @@ const Accounts = () => {
|
|||||||
// Call the updateSessions function when the 'accounts' dependency changes
|
// Call the updateSessions function when the 'accounts' dependency changes
|
||||||
updateSessions();
|
updateSessions();
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [accounts, web3wallet]);
|
}, [accounts]);
|
||||||
|
|
||||||
const addAccountHandler = async () => {
|
const addAccountHandler = async () => {
|
||||||
setIsAccountCreating(true);
|
setIsAccountCreating(true);
|
||||||
|
@ -7,6 +7,7 @@ import { buildApprovedNamespaces, getSdkError } from '@walletconnect/utils';
|
|||||||
|
|
||||||
import { PairingModalProps } from '../types';
|
import { PairingModalProps } from '../types';
|
||||||
import styles from '../styles/stylesheet';
|
import styles from '../styles/stylesheet';
|
||||||
|
import { web3wallet } from '../utils/wallet-connect/WalletConnectUtils';
|
||||||
import { useAccounts } from '../context/AccountsContext';
|
import { useAccounts } from '../context/AccountsContext';
|
||||||
import { useWalletConnect } from '../context/WalletConnectContext';
|
import { useWalletConnect } from '../context/WalletConnectContext';
|
||||||
import { useNetworks } from '../context/NetworksContext';
|
import { useNetworks } from '../context/NetworksContext';
|
||||||
@ -85,7 +86,7 @@ const PairingModal = ({
|
|||||||
});
|
});
|
||||||
}, [currentProposal]);
|
}, [currentProposal]);
|
||||||
|
|
||||||
const { setActiveSessions, web3wallet } = useWalletConnect();
|
const { setActiveSessions } = useWalletConnect();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const getSupportedNamespaces = async () => {
|
const getSupportedNamespaces = async () => {
|
||||||
@ -130,7 +131,6 @@ const PairingModal = ({
|
|||||||
currentIndex,
|
currentIndex,
|
||||||
setCurrentProposal,
|
setCurrentProposal,
|
||||||
setModalVisible,
|
setModalVisible,
|
||||||
web3wallet
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const namespaces = useMemo(() => {
|
const namespaces = useMemo(() => {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Dialog, DialogTitle, DialogContent, DialogActions, Button, Typography } from '@mui/material';
|
import { Button, Dialog, Portal, Text } from 'react-native-paper';
|
||||||
|
|
||||||
const TxErrorDialog = ({
|
const TxErrorDialog = ({
|
||||||
error,
|
error,
|
||||||
@ -11,15 +11,17 @@ const TxErrorDialog = ({
|
|||||||
hideDialog: () => void;
|
hideDialog: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<Dialog open={visible} onClose={hideDialog}>
|
<Portal>
|
||||||
<DialogTitle>Transaction Error</DialogTitle>
|
<Dialog visible={visible} onDismiss={hideDialog}>
|
||||||
<DialogContent>
|
<Dialog.Title>Transaction Error</Dialog.Title>
|
||||||
<Typography variant="body1">{error}</Typography>
|
<Dialog.Content>
|
||||||
</DialogContent>
|
<Text variant="bodyMedium">{error}</Text>
|
||||||
<DialogActions>
|
</Dialog.Content>
|
||||||
<Button onClick={hideDialog}>OK</Button>
|
<Dialog.Actions>
|
||||||
</DialogActions>
|
<Button onPress={hideDialog}>OK</Button>
|
||||||
</Dialog>
|
</Dialog.Actions>
|
||||||
|
</Dialog>
|
||||||
|
</Portal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,16 +1,14 @@
|
|||||||
import React, { createContext, useContext, useEffect, useState } from 'react';
|
import React, { createContext, useContext, useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { SessionTypes } from '@walletconnect/types';
|
import { SessionTypes } from '@walletconnect/types';
|
||||||
import { IWeb3Wallet } from '@walletconnect/web3wallet';
|
|
||||||
|
|
||||||
import { WalletConnectContextProps } from '../types';
|
import { WalletConnectContextProps } from '../types';
|
||||||
|
import { web3wallet } from '../utils/wallet-connect/WalletConnectUtils';
|
||||||
import useInitialization from '../hooks/useInitialization';
|
import useInitialization from '../hooks/useInitialization';
|
||||||
|
|
||||||
const WalletConnectContext = createContext<WalletConnectContextProps>({
|
const WalletConnectContext = createContext<WalletConnectContextProps>({
|
||||||
activeSessions: {},
|
activeSessions: {},
|
||||||
setActiveSessions: () => {},
|
setActiveSessions: () => {},
|
||||||
web3wallet: {} as IWeb3Wallet,
|
|
||||||
setWeb3wallet: () => {},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const useWalletConnect = () => {
|
const useWalletConnect = () => {
|
||||||
@ -19,14 +17,12 @@ const useWalletConnect = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const WalletConnectProvider = ({ children }: { children: React.ReactNode }) => {
|
const WalletConnectProvider = ({ children }: { children: React.ReactNode }) => {
|
||||||
const [web3wallet, setWeb3wallet] = useState<IWeb3Wallet | undefined>();
|
useInitialization();
|
||||||
|
|
||||||
useInitialization(setWeb3wallet);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const sessions = (web3wallet && web3wallet.getActiveSessions()) || {};
|
const sessions = (web3wallet && web3wallet.getActiveSessions()) || {};
|
||||||
setActiveSessions(sessions);
|
setActiveSessions(sessions);
|
||||||
}, [web3wallet]);
|
}, []);
|
||||||
|
|
||||||
const [activeSessions, setActiveSessions] = useState<
|
const [activeSessions, setActiveSessions] = useState<
|
||||||
Record<string, SessionTypes.Struct>
|
Record<string, SessionTypes.Struct>
|
||||||
@ -37,8 +33,6 @@ const WalletConnectProvider = ({ children }: { children: React.ReactNode }) => {
|
|||||||
value={{
|
value={{
|
||||||
activeSessions,
|
activeSessions,
|
||||||
setActiveSessions,
|
setActiveSessions,
|
||||||
web3wallet,
|
|
||||||
setWeb3wallet,
|
|
||||||
}}>
|
}}>
|
||||||
{children}
|
{children}
|
||||||
</WalletConnectContext.Provider>
|
</WalletConnectContext.Provider>
|
||||||
|
@ -1,23 +1,17 @@
|
|||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { IWeb3Wallet } from '@walletconnect/web3wallet';
|
|
||||||
|
|
||||||
import { createWeb3Wallet } from '../utils/wallet-connect/WalletConnectUtils';
|
import { createWeb3Wallet } from '../utils/wallet-connect/WalletConnectUtils';
|
||||||
|
|
||||||
export default function useInitialization(
|
export default function useInitialization() {
|
||||||
setWeb3wallet: React.Dispatch<React.SetStateAction<IWeb3Wallet | undefined>>,
|
|
||||||
) {
|
|
||||||
const [initialized, setInitialized] = useState(false);
|
const [initialized, setInitialized] = useState(false);
|
||||||
|
|
||||||
const onInitialize = useCallback(async () => {
|
const onInitialize = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
const web3walletInstance = await createWeb3Wallet();
|
await createWeb3Wallet();
|
||||||
setWeb3wallet(web3walletInstance);
|
|
||||||
setInitialized(true);
|
setInitialized(true);
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
console.error('Error for initializing', err);
|
console.error('Error for initializing', err);
|
||||||
}
|
}
|
||||||
}, [setWeb3wallet]);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!initialized) {
|
if (!initialized) {
|
||||||
|
@ -15,7 +15,14 @@ import { WalletConnectProvider } from './context/WalletConnectContext';
|
|||||||
globalThis.Buffer = Buffer;
|
globalThis.Buffer = Buffer;
|
||||||
|
|
||||||
const linking = {
|
const linking = {
|
||||||
prefixes: ['https://wallet.laconic.com']
|
prefixes: ['https://wallet.laconic.com'],
|
||||||
|
config: {
|
||||||
|
screens: {
|
||||||
|
SignRequest: {
|
||||||
|
path: 'sign/:namespace/:chaindId/:address/:message',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const theme = {
|
const theme = {
|
||||||
|
@ -409,7 +409,6 @@ const AddNetwork = () => {
|
|||||||
mode="contained"
|
mode="contained"
|
||||||
loading={isSubmitting}
|
loading={isSubmitting}
|
||||||
disabled={isSubmitting}
|
disabled={isSubmitting}
|
||||||
style={styles.networksButton}
|
|
||||||
onPress={handleSubmit(submit)}>
|
onPress={handleSubmit(submit)}>
|
||||||
{isSubmitting ? 'Adding' : 'Submit'}
|
{isSubmitting ? 'Adding' : 'Submit'}
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useCallback, useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { View } from 'react-native';
|
import { View } from 'react-native';
|
||||||
import { Button, Text, TextInput } from 'react-native-paper';
|
import { Button, Text, TextInput } from 'react-native-paper';
|
||||||
|
|
||||||
@ -8,7 +8,6 @@ import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
|||||||
import { web3WalletPair } from '../utils/wallet-connect/WalletConnectUtils';
|
import { web3WalletPair } from '../utils/wallet-connect/WalletConnectUtils';
|
||||||
import styles from '../styles/stylesheet';
|
import styles from '../styles/stylesheet';
|
||||||
import { StackParamsList } from '../types';
|
import { StackParamsList } from '../types';
|
||||||
import { useWalletConnect } from '../context/WalletConnectContext';
|
|
||||||
|
|
||||||
const AddSession = () => {
|
const AddSession = () => {
|
||||||
const navigation =
|
const navigation =
|
||||||
@ -16,17 +15,11 @@ const AddSession = () => {
|
|||||||
|
|
||||||
const [currentWCURI, setCurrentWCURI] = useState<string>('');
|
const [currentWCURI, setCurrentWCURI] = useState<string>('');
|
||||||
|
|
||||||
const {web3wallet} = useWalletConnect();
|
const pair = async () => {
|
||||||
|
const pairing = await web3WalletPair({ uri: currentWCURI });
|
||||||
const pair = useCallback(async () => {
|
|
||||||
if (!web3wallet) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const pairing = await web3WalletPair(web3wallet, { uri: currentWCURI });
|
|
||||||
navigation.navigate('WalletConnect');
|
navigation.navigate('WalletConnect');
|
||||||
return pairing;
|
return pairing;
|
||||||
}, [currentWCURI, navigation, web3wallet]);
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.appContainer}>
|
<View style={styles.appContainer}>
|
||||||
|
@ -23,7 +23,7 @@ import {
|
|||||||
approveWalletConnectRequest,
|
approveWalletConnectRequest,
|
||||||
rejectWalletConnectRequest,
|
rejectWalletConnectRequest,
|
||||||
} from '../utils/wallet-connect/wallet-connect-requests';
|
} from '../utils/wallet-connect/wallet-connect-requests';
|
||||||
import { useWalletConnect } from '../context/WalletConnectContext';
|
import { web3wallet } from '../utils/wallet-connect/WalletConnectUtils';
|
||||||
import { MEMO } from './ApproveTransfer';
|
import { MEMO } from './ApproveTransfer';
|
||||||
import TxErrorDialog from '../components/TxErrorDialog';
|
import TxErrorDialog from '../components/TxErrorDialog';
|
||||||
import AccountDetails from '../components/AccountDetails';
|
import AccountDetails from '../components/AccountDetails';
|
||||||
@ -50,7 +50,7 @@ const ApproveTransaction = ({ route }: ApproveTransactionProps) => {
|
|||||||
const [account, setAccount] = useState<Account>();
|
const [account, setAccount] = useState<Account>();
|
||||||
const [cosmosStargateClient, setCosmosStargateClient] =
|
const [cosmosStargateClient, setCosmosStargateClient] =
|
||||||
useState<LaconicClient>();
|
useState<LaconicClient>();
|
||||||
const [cosmosGasLimit, setCosmosGasLimit] = useState<string>('');
|
const [cosmosGasLimit, setCosmosGasLimit] = useState<string>();
|
||||||
const [fees, setFees] = useState<string>();
|
const [fees, setFees] = useState<string>();
|
||||||
const [txError, setTxError] = useState<string>();
|
const [txError, setTxError] = useState<string>();
|
||||||
const [isTxErrorDialogOpen, setIsTxErrorDialogOpen] = useState(false);
|
const [isTxErrorDialogOpen, setIsTxErrorDialogOpen] = useState(false);
|
||||||
@ -65,8 +65,6 @@ const ApproveTransaction = ({ route }: ApproveTransactionProps) => {
|
|||||||
);
|
);
|
||||||
const namespace = requestedNetwork!.namespace;
|
const namespace = requestedNetwork!.namespace;
|
||||||
|
|
||||||
const { web3wallet } = useWalletConnect();
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (namespace !== COSMOS) {
|
if (namespace !== COSMOS) {
|
||||||
return;
|
return;
|
||||||
@ -108,7 +106,7 @@ const ApproveTransaction = ({ route }: ApproveTransactionProps) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
setClient();
|
setClient();
|
||||||
}, [account, requestedNetwork, chainId, namespace, requestEventId, topic, web3wallet]);
|
}, [account, requestedNetwork, chainId, namespace, requestEventId, topic]);
|
||||||
|
|
||||||
const retrieveData = useCallback(
|
const retrieveData = useCallback(
|
||||||
async (requestAddress: string) => {
|
async (requestAddress: string) => {
|
||||||
@ -160,7 +158,7 @@ const ApproveTransaction = ({ route }: ApproveTransactionProps) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
getCosmosGas();
|
getCosmosGas();
|
||||||
}, [cosmosStargateClient, transactionMessage, requestEventId, topic, web3wallet]);
|
}, [cosmosStargateClient, transactionMessage, requestEventId, topic]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const gasPrice = GasPrice.fromString(
|
const gasPrice = GasPrice.fromString(
|
||||||
@ -201,7 +199,7 @@ const ApproveTransaction = ({ route }: ApproveTransactionProps) => {
|
|||||||
denom: requestedNetwork!.nativeDenom!,
|
denom: requestedNetwork!.nativeDenom!,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
gas: cosmosGasLimit,
|
gas: cosmosGasLimit!,
|
||||||
},
|
},
|
||||||
txMsg: transactionMessage,
|
txMsg: transactionMessage,
|
||||||
};
|
};
|
||||||
|
@ -33,7 +33,7 @@ import {
|
|||||||
rejectWalletConnectRequest,
|
rejectWalletConnectRequest,
|
||||||
WalletConnectRequests,
|
WalletConnectRequests,
|
||||||
} from '../utils/wallet-connect/wallet-connect-requests';
|
} from '../utils/wallet-connect/wallet-connect-requests';
|
||||||
import { useWalletConnect } from '../context/WalletConnectContext';
|
import { web3wallet } from '../utils/wallet-connect/WalletConnectUtils';
|
||||||
import DataBox from '../components/DataBox';
|
import DataBox from '../components/DataBox';
|
||||||
import { getPathKey } from '../utils/misc';
|
import { getPathKey } from '../utils/misc';
|
||||||
import { useNetworks } from '../context/NetworksContext';
|
import { useNetworks } from '../context/NetworksContext';
|
||||||
@ -53,7 +53,6 @@ type SignRequestProps = NativeStackScreenProps<
|
|||||||
|
|
||||||
const ApproveTransfer = ({ route }: SignRequestProps) => {
|
const ApproveTransfer = ({ route }: SignRequestProps) => {
|
||||||
const { networksData } = useNetworks();
|
const { networksData } = useNetworks();
|
||||||
const { web3wallet } = useWalletConnect();
|
|
||||||
|
|
||||||
const requestSession = route.params.requestSessionData;
|
const requestSession = route.params.requestSessionData;
|
||||||
const requestName = requestSession.peer.metadata.name;
|
const requestName = requestSession.peer.metadata.name;
|
||||||
@ -70,8 +69,8 @@ const ApproveTransfer = ({ route }: SignRequestProps) => {
|
|||||||
const [isTxLoading, setIsTxLoading] = useState(false);
|
const [isTxLoading, setIsTxLoading] = useState(false);
|
||||||
const [cosmosStargateClient, setCosmosStargateClient] =
|
const [cosmosStargateClient, setCosmosStargateClient] =
|
||||||
useState<SigningStargateClient>();
|
useState<SigningStargateClient>();
|
||||||
const [fees, setFees] = useState<string>('');
|
const [fees, setFees] = useState<string>();
|
||||||
const [cosmosGasLimit, setCosmosGasLimit] = useState<string>('');
|
const [cosmosGasLimit, setCosmosGasLimit] = useState<string>();
|
||||||
const [txError, setTxError] = useState<string>();
|
const [txError, setTxError] = useState<string>();
|
||||||
const [isTxErrorDialogOpen, setIsTxErrorDialogOpen] = useState(false);
|
const [isTxErrorDialogOpen, setIsTxErrorDialogOpen] = useState(false);
|
||||||
const [ethGasPrice, setEthGasPrice] = useState<BigNumber | null>();
|
const [ethGasPrice, setEthGasPrice] = useState<BigNumber | null>();
|
||||||
@ -212,7 +211,7 @@ const ApproveTransfer = ({ route }: SignRequestProps) => {
|
|||||||
// If requested chain is EVM compatible, set loading to false when ethMaxFee and ethPriorityFee have been populated
|
// If requested chain is EVM compatible, set loading to false when ethMaxFee and ethPriorityFee have been populated
|
||||||
(ethMaxFee !== undefined && ethMaxPriorityFee !== undefined) ||
|
(ethMaxFee !== undefined && ethMaxPriorityFee !== undefined) ||
|
||||||
// Or if requested chain is a cosmos chain, set loading to false when cosmosGasLimit has been populated
|
// Or if requested chain is a cosmos chain, set loading to false when cosmosGasLimit has been populated
|
||||||
!cosmosGasLimit
|
cosmosGasLimit !== undefined
|
||||||
) {
|
) {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
@ -311,11 +310,11 @@ const ApproveTransfer = ({ route }: SignRequestProps) => {
|
|||||||
// This amount is total fees required for transaction
|
// This amount is total fees required for transaction
|
||||||
amount: [
|
amount: [
|
||||||
{
|
{
|
||||||
amount: fees,
|
amount: fees!,
|
||||||
denom: requestedNetwork!.nativeDenom!,
|
denom: requestedNetwork!.nativeDenom!,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
gas: cosmosGasLimit,
|
gas: cosmosGasLimit!,
|
||||||
},
|
},
|
||||||
sendMsg,
|
sendMsg,
|
||||||
memo: MEMO,
|
memo: MEMO,
|
||||||
|
@ -187,7 +187,6 @@ const EditNetwork = ({ route }: EditNetworkProps) => {
|
|||||||
mode="contained"
|
mode="contained"
|
||||||
loading={isSubmitting}
|
loading={isSubmitting}
|
||||||
disabled={isSubmitting}
|
disabled={isSubmitting}
|
||||||
style={styles.networksButton}
|
|
||||||
onPress={handleSubmit(submit)}>
|
onPress={handleSubmit(submit)}>
|
||||||
{isSubmitting ? 'Adding' : 'Submit'}
|
{isSubmitting ? 'Adding' : 'Submit'}
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -16,6 +16,7 @@ import styles from '../styles/stylesheet';
|
|||||||
import { useAccounts } from '../context/AccountsContext';
|
import { useAccounts } from '../context/AccountsContext';
|
||||||
import { useWalletConnect } from '../context/WalletConnectContext';
|
import { useWalletConnect } from '../context/WalletConnectContext';
|
||||||
import { NetworksDataState, StackParamsList } from '../types';
|
import { NetworksDataState, StackParamsList } from '../types';
|
||||||
|
import { web3wallet } from '../utils/wallet-connect/WalletConnectUtils';
|
||||||
import { useNetworks } from '../context/NetworksContext';
|
import { useNetworks } from '../context/NetworksContext';
|
||||||
|
|
||||||
const WCLogo = () => {
|
const WCLogo = () => {
|
||||||
@ -32,7 +33,7 @@ const HomeScreen = () => {
|
|||||||
|
|
||||||
const { networksData, selectedNetwork, setSelectedNetwork, setNetworksData } =
|
const { networksData, selectedNetwork, setSelectedNetwork, setNetworksData } =
|
||||||
useNetworks();
|
useNetworks();
|
||||||
const { setActiveSessions, web3wallet } = useWalletConnect();
|
const { setActiveSessions } = useWalletConnect();
|
||||||
|
|
||||||
const navigation =
|
const navigation =
|
||||||
useNavigation<NativeStackNavigationProp<StackParamsList>>();
|
useNavigation<NativeStackNavigationProp<StackParamsList>>();
|
||||||
@ -114,7 +115,6 @@ const HomeScreen = () => {
|
|||||||
setCurrentIndex,
|
setCurrentIndex,
|
||||||
setNetworksData,
|
setNetworksData,
|
||||||
setSelectedNetwork,
|
setSelectedNetwork,
|
||||||
web3wallet
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const updateNetwork = (networkData: NetworksDataState) => {
|
const updateNetwork = (networkData: NetworksDataState) => {
|
||||||
|
@ -19,7 +19,7 @@ import {
|
|||||||
rejectWalletConnectRequest,
|
rejectWalletConnectRequest,
|
||||||
WalletConnectRequests,
|
WalletConnectRequests,
|
||||||
} from '../utils/wallet-connect/wallet-connect-requests';
|
} from '../utils/wallet-connect/wallet-connect-requests';
|
||||||
import { useWalletConnect } from '../context/WalletConnectContext';
|
import { web3wallet } from '../utils/wallet-connect/WalletConnectUtils';
|
||||||
import { EIP155_SIGNING_METHODS } from '../utils/wallet-connect/EIP155Data';
|
import { EIP155_SIGNING_METHODS } from '../utils/wallet-connect/EIP155Data';
|
||||||
import { useNetworks } from '../context/NetworksContext';
|
import { useNetworks } from '../context/NetworksContext';
|
||||||
import { COSMOS_METHODS } from '../utils/wallet-connect/COSMOSData';
|
import { COSMOS_METHODS } from '../utils/wallet-connect/COSMOSData';
|
||||||
@ -28,7 +28,6 @@ type SignRequestProps = NativeStackScreenProps<StackParamsList, 'SignRequest'>;
|
|||||||
|
|
||||||
const SignRequest = ({ route }: SignRequestProps) => {
|
const SignRequest = ({ route }: SignRequestProps) => {
|
||||||
const { networksData } = useNetworks();
|
const { networksData } = useNetworks();
|
||||||
const {web3wallet} = useWalletConnect();
|
|
||||||
|
|
||||||
const requestSession = route.params.requestSessionData;
|
const requestSession = route.params.requestSessionData;
|
||||||
const requestName = requestSession?.peer?.metadata?.name;
|
const requestName = requestSession?.peer?.metadata?.name;
|
||||||
@ -49,7 +48,7 @@ const SignRequest = ({ route }: SignRequestProps) => {
|
|||||||
const isCosmosSignDirect = useMemo(() => {
|
const isCosmosSignDirect = useMemo(() => {
|
||||||
const requestParams = route.params.requestEvent;
|
const requestParams = route.params.requestEvent;
|
||||||
|
|
||||||
if (!requestParams?.id) {
|
if (!requestParams) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,7 +58,7 @@ const SignRequest = ({ route }: SignRequestProps) => {
|
|||||||
const isEthSendTransaction = useMemo(() => {
|
const isEthSendTransaction = useMemo(() => {
|
||||||
const requestParams = route.params.requestEvent;
|
const requestParams = route.params.requestEvent;
|
||||||
|
|
||||||
if (!requestParams?.id) {
|
if (!requestParams) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,13 +85,21 @@ const SignRequest = ({ route }: SignRequestProps) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setAccount(requestAccount);
|
if (requestAccount !== account) {
|
||||||
setMessage(decodeURIComponent(requestMessage));
|
setAccount(requestAccount);
|
||||||
setNamespace(requestNamespace);
|
}
|
||||||
setChainId(requestChainId);
|
if (requestMessage !== message) {
|
||||||
|
setMessage(decodeURIComponent(requestMessage));
|
||||||
|
}
|
||||||
|
if (requestNamespace !== namespace) {
|
||||||
|
setNamespace(requestNamespace);
|
||||||
|
}
|
||||||
|
if (requestChainId !== chainId) {
|
||||||
|
setChainId(requestChainId);
|
||||||
|
}
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
},
|
},
|
||||||
[navigation],
|
[account, message, navigation, namespace, chainId],
|
||||||
);
|
);
|
||||||
|
|
||||||
const sanitizePath = useCallback(
|
const sanitizePath = useCallback(
|
||||||
|
@ -5,10 +5,11 @@ import { List, Text } from 'react-native-paper';
|
|||||||
import { getSdkError } from '@walletconnect/utils';
|
import { getSdkError } from '@walletconnect/utils';
|
||||||
|
|
||||||
import { useWalletConnect } from '../context/WalletConnectContext';
|
import { useWalletConnect } from '../context/WalletConnectContext';
|
||||||
|
import { web3wallet } from '../utils/wallet-connect/WalletConnectUtils';
|
||||||
import styles from '../styles/stylesheet';
|
import styles from '../styles/stylesheet';
|
||||||
|
|
||||||
export default function WalletConnect() {
|
export default function WalletConnect() {
|
||||||
const { web3wallet, activeSessions, setActiveSessions } = useWalletConnect();
|
const { activeSessions, setActiveSessions } = useWalletConnect();
|
||||||
|
|
||||||
const disconnect = async (sessionId: string) => {
|
const disconnect = async (sessionId: string) => {
|
||||||
await web3wallet!.disconnectSession({
|
await web3wallet!.disconnectSession({
|
||||||
@ -23,7 +24,7 @@ export default function WalletConnect() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const sessions = web3wallet?.getActiveSessions() || {};
|
const sessions = web3wallet?.getActiveSessions() || {};
|
||||||
setActiveSessions(sessions);
|
setActiveSessions(sessions);
|
||||||
}, [web3wallet, setActiveSessions]);
|
}, [setActiveSessions]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
|
@ -5,7 +5,6 @@ const styles = StyleSheet.create({
|
|||||||
marginTop: 20,
|
marginTop: 20,
|
||||||
width: 150,
|
width: 150,
|
||||||
alignSelf: 'center',
|
alignSelf: 'center',
|
||||||
marginBottom: 40
|
|
||||||
},
|
},
|
||||||
signLink: {
|
signLink: {
|
||||||
alignItems: 'flex-end',
|
alignItems: 'flex-end',
|
||||||
@ -43,7 +42,6 @@ const styles = StyleSheet.create({
|
|||||||
},
|
},
|
||||||
signButton: {
|
signButton: {
|
||||||
marginTop: 20,
|
marginTop: 20,
|
||||||
marginBottom: 20,
|
|
||||||
width: 150,
|
width: 150,
|
||||||
alignSelf: 'center',
|
alignSelf: 'center',
|
||||||
},
|
},
|
||||||
@ -145,7 +143,6 @@ const styles = StyleSheet.create({
|
|||||||
buttonContainer: {
|
buttonContainer: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
marginLeft: 20,
|
marginLeft: 20,
|
||||||
marginTop: 10,
|
|
||||||
marginBottom: 10,
|
marginBottom: 10,
|
||||||
justifyContent: 'space-evenly',
|
justifyContent: 'space-evenly',
|
||||||
},
|
},
|
||||||
@ -207,9 +204,9 @@ const styles = StyleSheet.create({
|
|||||||
},
|
},
|
||||||
subHeading: {
|
subHeading: {
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
|
fontWeight: 'bold',
|
||||||
marginBottom: 10,
|
marginBottom: 10,
|
||||||
marginTop: 10,
|
marginTop: 10,
|
||||||
fontSize: 20
|
|
||||||
},
|
},
|
||||||
centerText: {
|
centerText: {
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
@ -256,30 +253,12 @@ const styles = StyleSheet.create({
|
|||||||
balancePadding: {
|
balancePadding: {
|
||||||
padding: 8,
|
padding: 8,
|
||||||
},
|
},
|
||||||
noActiveSessions: {
|
noActiveSessions: { display: 'flex', alignItems: 'center', marginTop: 12 },
|
||||||
display: 'flex',
|
disconnectSession: { display: 'flex', justifyContent: 'center' },
|
||||||
alignItems: 'center',
|
sessionItem: { paddingLeft: 12, borderBottomWidth: 0.5 },
|
||||||
marginTop: 20,
|
sessionsContainer: { paddingLeft: 12, borderBottomWidth: 0.5 },
|
||||||
marginBottom: 20,
|
|
||||||
},
|
|
||||||
disconnectSession: {
|
|
||||||
display: 'flex',
|
|
||||||
justifyContent: 'center',
|
|
||||||
},
|
|
||||||
sessionItem: {
|
|
||||||
paddingLeft: 12,
|
|
||||||
borderBottomWidth: 0.5,
|
|
||||||
},
|
|
||||||
sessionsContainer: {
|
|
||||||
paddingLeft: 12,
|
|
||||||
borderBottomWidth: 0.5,
|
|
||||||
},
|
|
||||||
walletConnectUriText: { padding: 10 },
|
walletConnectUriText: { padding: 10 },
|
||||||
walletConnectLogo: {
|
walletConnectLogo: { width: 24, height: 15, margin: 0 },
|
||||||
width: 24,
|
|
||||||
height: 15,
|
|
||||||
margin: 0,
|
|
||||||
},
|
|
||||||
selectNetworkText: {
|
selectNetworkText: {
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
marginVertical: 10,
|
marginVertical: 10,
|
||||||
@ -296,11 +275,7 @@ const styles = StyleSheet.create({
|
|||||||
padding: 8,
|
padding: 8,
|
||||||
},
|
},
|
||||||
linkContainer: {
|
linkContainer: {
|
||||||
paddingBottom: 72,
|
paddingBottom: 72
|
||||||
},
|
|
||||||
networksButton: {
|
|
||||||
marginTop: 12,
|
|
||||||
marginBottom: 20,
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { PopulatedTransaction } from 'ethers';
|
import { PopulatedTransaction } from 'ethers';
|
||||||
|
|
||||||
import { SignClientTypes, SessionTypes } from '@walletconnect/types';
|
import { SignClientTypes, SessionTypes } from '@walletconnect/types';
|
||||||
import { IWeb3Wallet, Web3WalletTypes } from '@walletconnect/web3wallet';
|
import { Web3WalletTypes } from '@walletconnect/web3wallet';
|
||||||
import { EncodeObject } from '@cosmjs/proto-signing';
|
import { EncodeObject } from '@cosmjs/proto-signing';
|
||||||
|
|
||||||
export type StackParamsList = {
|
export type StackParamsList = {
|
||||||
@ -129,6 +129,4 @@ export interface WalletConnectContextProps {
|
|||||||
setActiveSessions: (
|
setActiveSessions: (
|
||||||
activeSessions: Record<string, SessionTypes.Struct>,
|
activeSessions: Record<string, SessionTypes.Struct>,
|
||||||
) => void;
|
) => void;
|
||||||
web3wallet: IWeb3Wallet | undefined;
|
|
||||||
setWeb3wallet: React.Dispatch<React.SetStateAction<IWeb3Wallet | undefined>>;
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import { DENOM } from '@cerc-io/registry-sdk'
|
|
||||||
|
|
||||||
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';
|
||||||
|
|
||||||
@ -12,10 +10,10 @@ export const DEFAULT_NETWORKS = [
|
|||||||
namespace: COSMOS,
|
namespace: COSMOS,
|
||||||
rpcUrl: process.env.REACT_APP_LACONICD_RPC_URL!,
|
rpcUrl: process.env.REACT_APP_LACONICD_RPC_URL!,
|
||||||
blockExplorerUrl: '',
|
blockExplorerUrl: '',
|
||||||
nativeDenom: DENOM,
|
nativeDenom: 'photon',
|
||||||
addressPrefix: 'laconic',
|
addressPrefix: 'laconic',
|
||||||
coinType: '118',
|
coinType: '118',
|
||||||
gasPrice: '1',
|
gasPrice: '0.01',
|
||||||
isDefault: true,
|
isDefault: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -14,7 +14,7 @@ export async function createWeb3Wallet() {
|
|||||||
projectId: process.env.REACT_APP_WALLET_CONNECT_PROJECT_ID,
|
projectId: process.env.REACT_APP_WALLET_CONNECT_PROJECT_ID,
|
||||||
});
|
});
|
||||||
|
|
||||||
const web3wallet = await Web3Wallet.init({
|
web3wallet = await Web3Wallet.init({
|
||||||
core,
|
core,
|
||||||
metadata: {
|
metadata: {
|
||||||
name: 'Laconic Wallet',
|
name: 'Laconic Wallet',
|
||||||
@ -23,14 +23,9 @@ export async function createWeb3Wallet() {
|
|||||||
icons: ['https://avatars.githubusercontent.com/u/92608123'],
|
icons: ['https://avatars.githubusercontent.com/u/92608123'],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return web3wallet;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function web3WalletPair(
|
export async function web3WalletPair(params: { uri: string }) {
|
||||||
web3wallet: IWeb3Wallet,
|
|
||||||
params: { uri: string },
|
|
||||||
) {
|
|
||||||
if (web3wallet) {
|
if (web3wallet) {
|
||||||
return await web3wallet.core.pairing.pair({ uri: params.uri });
|
return await web3wallet.core.pairing.pair({ uri: params.uri });
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,6 @@ services:
|
|||||||
WALLET_CONNECT_ID: ${WALLET_CONNECT_ID}
|
WALLET_CONNECT_ID: ${WALLET_CONNECT_ID}
|
||||||
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}
|
|
||||||
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
|
||||||
|
@ -9,13 +9,11 @@ echo "Using the following env variables:"
|
|||||||
echo "WALLET_CONNECT_ID: ${WALLET_CONNECT_ID}"
|
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}"
|
|
||||||
|
|
||||||
# Build with required env
|
# Build with required env
|
||||||
REACT_APP_WALLET_CONNECT_PROJECT_ID=$WALLET_CONNECT_ID \
|
REACT_APP_WALLET_CONNECT_PROJECT_ID=$WALLET_CONNECT_ID \
|
||||||
REACT_APP_DEFAULT_GAS_PRICE=$CERC_DEFAULT_GAS_PRICE \
|
REACT_APP_DEFAULT_GAS_PRICE=$CERC_DEFAULT_GAS_PRICE \
|
||||||
REACT_APP_GAS_ADJUSTMENT=$CERC_GAS_ADJUSTMENT \
|
REACT_APP_GAS_ADJUSTMENT=$CERC_GAS_ADJUSTMENT \
|
||||||
REACT_APP_LACONICD_RPC_URL=$CERC_LACONICD_RPC_URL \
|
|
||||||
yarn build
|
yarn build
|
||||||
|
|
||||||
http-server --proxy http://localhost:80? -p 80 /app/build
|
http-server --proxy http://localhost:80? -p 80 /app/build
|
||||||
|
@ -57,9 +57,6 @@ Instructions for running the `laconic-wallet-web` using [laconic-so](https://git
|
|||||||
# Gas adjustment (default: 2)
|
# Gas adjustment (default: 2)
|
||||||
# Reference: https://github.com/cosmos/cosmos-sdk/issues/16020
|
# Reference: https://github.com/cosmos/cosmos-sdk/issues/16020
|
||||||
CERC_GAS_ADJUSTMENT=
|
CERC_GAS_ADJUSTMENT=
|
||||||
|
|
||||||
# RPC endpoint of laconicd node (default: https://laconicd.laconic.com)
|
|
||||||
CERC_LACONICD_RPC_URL=
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Start the deployment
|
## Start the deployment
|
||||||
|
@ -1263,10 +1263,10 @@
|
|||||||
deepmerge "^3.2.0"
|
deepmerge "^3.2.0"
|
||||||
hoist-non-react-statics "^3.3.0"
|
hoist-non-react-statics "^3.3.0"
|
||||||
|
|
||||||
"@cerc-io/registry-sdk@^0.2.5":
|
"@cerc-io/registry-sdk@^0.2.3":
|
||||||
version "0.2.5"
|
version "0.2.3"
|
||||||
resolved "https://git.vdb.to/api/packages/cerc-io/npm/%40cerc-io%2Fregistry-sdk/-/0.2.5/registry-sdk-0.2.5.tgz#9ca19fecb2923520dd6a19946c309ecb2ec780a2"
|
resolved "https://git.vdb.to/api/packages/cerc-io/npm/%40cerc-io%2Fregistry-sdk/-/0.2.3/registry-sdk-0.2.3.tgz#73e955b4d49d7c97eea40c351bbc21f98bb330f9"
|
||||||
integrity sha512-/KXAYf9gStaX/rRBMCEeDCexEIpTOFHeHzMK9B3xfCT+SyYZE9WC9GpX299LzBYJKKPsb0/JvnDfip9S1igJtA==
|
integrity sha512-8fXRdyiTXn8WsJ8r3DCSBYzUBNEZYPPk5JGUrEmkGQhKOJr+ZeakN+2t6HrqEVB9IMYTJK9BtVLPA0KlaXILYA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@cosmjs/amino" "^0.28.1"
|
"@cosmjs/amino" "^0.28.1"
|
||||||
"@cosmjs/crypto" "^0.28.1"
|
"@cosmjs/crypto" "^0.28.1"
|
||||||
|
Loading…
Reference in New Issue
Block a user