Skip to content

Commit

Permalink
Merge pull request #5474 from LiskHQ/5469-walletconnect-improvements
Browse files Browse the repository at this point in the history
Wallet connect improvements
  • Loading branch information
oskarleonard authored Nov 22, 2023
2 parents 2777bf5 + 66b6fe4 commit 711c326
Show file tree
Hide file tree
Showing 15 changed files with 142 additions and 44 deletions.
6 changes: 4 additions & 2 deletions libs/wcm/hooks/useSession.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ export const useSession = ({ isEnabled = true } = {}) => {
}
}, []);

const respond = useCallback(async ({ payload }) => {
const requestEvent = events.find((e) => e.name === EVENTS.SESSION_REQUEST);
const respond = useCallback(async ({ payload, event }) => {
const requestEvent = event || events.find((e) => e.name === EVENTS.SESSION_REQUEST);
const topic = requestEvent.meta.topic;
const response = formatJsonRpcResult(requestEvent.meta.id, payload);

Expand All @@ -82,6 +82,8 @@ export const useSession = ({ isEnabled = true } = {}) => {
topic,
response,
});
setSessionRequest(null);
removeEvent(requestEvent);
return {
status: STATUS.SUCCESS,
data,
Expand Down
7 changes: 7 additions & 0 deletions libs/wcm/utils/jsonRPCFormat.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,13 @@ export const formatJsonRpcError = (id, error) => ({
error: formatErrorMessage(error),
});

export const USER_REJECT_ERROR = JSON.stringify({
error: {
code: 5000,
message: 'User rejected.',
},
});

export const formatJsonRpcResult = (id, result) => ({
id,
jsonrpc: '2.0',
Expand Down
13 changes: 10 additions & 3 deletions setup/react/app/MainRouter.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useEffect } from 'react';
import { withRouter } from 'react-router';
import { Route, Switch } from 'react-router-dom';
import { addSearchParamsToUrl } from 'src/utils/searchParams';
import { addSearchParamsToUrl, removeSearchParamsFromUrl } from 'src/utils/searchParams';
import { useEvents } from '@libs/wcm/hooks/useEvents';
import { EVENTS } from '@libs/wcm/constants/lifeCycle';
import routesMap from 'src/routes/routesMap';
Expand All @@ -15,16 +15,23 @@ const MainRouter = ({ history }) => {
const { events } = useEvents();
const routesList = Object.keys(routes);

const showRequestModal = (modalName, event) => {
removeSearchParamsFromUrl(history, ['modal', 'eventId']);
setTimeout(() => {
addSearchParamsToUrl(history, { modal: modalName, eventId: event.meta.id });
}, 100);
};

useEffect(() => {
const event = events.length && events[events.length - 1];

if (event.name === EVENTS.SESSION_REQUEST) {
const method = event.meta?.params?.request?.method;

if (method === 'sign_message') {
addSearchParamsToUrl(history, { modal: 'requestSignMessageDialog' });
showRequestModal('requestSignMessageDialog', event);
} else {
addSearchParamsToUrl(history, { modal: 'requestView' });
showRequestModal('requestView', event);
}
}
}, [events]);
Expand Down
2 changes: 1 addition & 1 deletion src/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@
"Copy and paste generator key value from CLI": "Copy and paste generator key value from CLI",
"Copy and return to application": "Copy and return to application",
"Copy link": "Copy link",
"Copy signature": "Copy signature",
"Copy signed transaction": "Copy signed transaction",
"Copy to clipboard": "Copy to clipboard",
"Count": "Count",
"Country": "Country",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import MultiStep from '@common/components/OldMultiStep';
import TxSignatureCollector from '@transaction/components/TxSignatureCollector';
import SignedMessage from '@message/components/signedMessage';
import { RequestSignMessageConfirmation } from '@blockchainApplication/connection/components/RequestSignMessageDialog/RequestSignMessageConfirmation';
import { USER_REJECT_ERROR } from '@libs/wcm/utils/jsonRPCFormat';
import styles from './RequestSignMessageDialog.css';
import RequestSummary from '../RequestSummary';

Expand All @@ -23,7 +24,7 @@ const RequestSignMessageDialog = () => {
const [isErrorView, setIsErrorView] = useState(false);
const { t } = useTranslation();
const { events } = useEvents();
const { sessionRequest } = useSession();
const { sessionRequest, respond } = useSession();
const [currentAccount] = useCurrentAccount();
const history = useHistory();
const reduxDispatch = useDispatch();
Expand Down Expand Up @@ -52,12 +53,21 @@ const RequestSignMessageDialog = () => {
reduxDispatch(emptyTransactionsData());
}, []);

/* istanbul ignore next */
const onCloseIcon = async () => {
const isStatusView = multiStepPosition === 3;
if (!isStatusView) {
await respond({ payload: USER_REJECT_ERROR });
}
};

return (
<Dialog
className={classNames(styles.RequestSignMessageDialog, {
[styles.passwordStep]: isPasswordStep,
})}
hasClose
onCloseIcon={onCloseIcon}
size={isErrorView && 'sm'}
>
{!isPasswordStep && !isErrorView && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ import { useAccounts } from '@account/hooks/useAccounts';
import { emptyTransactionsData } from 'src/redux/actions';
import { extractAddressFromPublicKey, truncateAddress } from '@wallet/utils/account';
import { SIGNING_METHODS } from '@libs/wcm/constants/permissions';
import { EVENTS, ERROR_CASES } from '@libs/wcm/constants/lifeCycle';
import { formatJsonRpcError } from '@libs/wcm/utils/jsonRPCFormat';
import { EVENTS } from '@libs/wcm/constants/lifeCycle';
import { useAppsMetaTokens } from '@token/fungible/hooks/queries/useAppsMetaTokens';
import { toTransactionJSON } from '@transaction/utils/encoding';
import { useBlockchainApplicationMeta } from '@blockchainApplication/manage/hooks/queries/useBlockchainApplicationMeta';
Expand All @@ -26,12 +25,12 @@ import { useEvents } from '@libs/wcm/hooks/useEvents';
import { useSchemas } from '@transaction/hooks/queries/useSchemas';
import { useDeprecatedAccount } from '@account/hooks/useDeprecatedAccount';
import { PrimaryButton, SecondaryButton, TertiaryButton } from 'src/theme/buttons';
import { getSdkError } from '@walletconnect/utils';
import { decodeTransaction } from '@transaction/utils';
import Box from 'src/theme/box';
import routes from 'src/routes/routes';
import BlockchainAppDetailsHeader from '@blockchainApplication/explore/components/BlockchainAppDetailsHeader';
import WarningNotification from '@common/components/warningNotification';
import { USER_REJECT_ERROR } from '@libs/wcm/utils/jsonRPCFormat';
import { ReactComponent as SwitchIcon } from '../../../../../../setup/react/assets/images/icons/switch-icon.svg';
import EmptyState from './EmptyState';
import styles from './requestSummary.css';
Expand All @@ -40,12 +39,6 @@ const getTitle = (key, t) =>
Object.values(SIGNING_METHODS).find((item) => item.key === key)?.title ?? t('Method not found.');
const defaultToken = { symbol: 'LSK' };

export const rejectLiskRequest = (request) => {
const { id } = request;

return formatJsonRpcError(id, getSdkError(ERROR_CASES.USER_REJECTED_METHODS).message);
};

// eslint-disable-next-line max-statements
const RequestSummary = ({ nextStep, history, message }) => {
const { t } = useTranslation();
Expand All @@ -56,7 +49,7 @@ const RequestSummary = ({ nextStep, history, message }) => {
const [transaction, setTransaction] = useState(null);
const [senderAccount, setSenderAccount] = useState(null);
const [errorMessage, setErrorMessage] = useState('');
const { sessionRequest } = useSession({ isEnabled: !message });
const { sessionRequest, respond } = useSession({ isEnabled: !message });
const reduxDispatch = useDispatch();
const metaData = useBlockchainApplicationMeta();
useDeprecatedAccount(senderAccount);
Expand Down Expand Up @@ -114,8 +107,8 @@ const RequestSummary = ({ nextStep, history, message }) => {
});
}
};
const rejectHandler = () => {
rejectLiskRequest(request);
const rejectHandler = async () => {
await respond({ payload: USER_REJECT_ERROR });
removeSearchParamsFromUrl(history, ['modal', 'status', 'name', 'action']);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ useEvents.mockReturnValue({

describe('RequestSummary', () => {
const reject = jest.fn();
const mockRespond = jest.fn();
beforeEach(() => {
useBlockchainApplicationMeta.mockReturnValue({
data: {
Expand All @@ -114,7 +115,11 @@ describe('RequestSummary', () => {
jest
.spyOn(accountUtils, 'extractAddressFromPublicKey')
.mockReturnValue(mockCurrentAccount.metadata.address);
useSession.mockReturnValue({ reject, sessionRequest: defaultContext.sessionRequest });
useSession.mockReturnValue({
reject,
sessionRequest: defaultContext.sessionRequest,
respond: mockRespond,
});
codec.codec.decode.mockReturnValue({});

it('Display the requesting app information', () => {
Expand All @@ -125,11 +130,11 @@ describe('RequestSummary', () => {
expect(screen.getByRole('link')).toHaveAttribute('href', 'http://example.com');
});

it('Reject the request if the reject button is clicked', () => {
it('Reject the request if the reject button is clicked', async () => {
renderWithQueryClientAndWC(RequestSummary, { nextStep, history });
const button = screen.getAllByRole('button')[0];
fireEvent.click(button);
expect(history.push).toHaveBeenCalled();
expect(mockRespond).toHaveBeenCalled();
});

it('Normalize the rawTx object and send it to the next step', () => {
Expand Down Expand Up @@ -209,4 +214,15 @@ describe('RequestSummary', () => {
expect(screen.queryByText('Signature request')).toBeFalsy();
expect(screen.queryByText('test app')).toBeFalsy();
});

it('Should call history.push when clicking Add account', () => {
useAccounts.mockReturnValue({
getAccountByAddress: () => null,
accounts: mockSavedAccounts,
});
renderWithQueryClientAndWC(RequestSummary, { nextStep, history });
const button = screen.getByText('Add account');
fireEvent.click(button);
expect(history.push).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,39 @@ import TxSignatureCollector from '@transaction/components/TxSignatureCollector';
import Dialog from 'src/theme/dialog/dialog';
import Summary from '@wallet/components/RequestSignSummary';
import Status from '@wallet/components/RequestSignStatus';
import { useSession } from '@libs/wcm/hooks/useSession';
import { USER_REJECT_ERROR } from '@libs/wcm/utils/jsonRPCFormat';
import RequestSummary from '../RequestSummary';
import styles from './requestView.css';

const RequestView = ({ history }) => {
const [isStepTxSignatureCollector, setIsStepTxSignatureCollector] = useState(false);
const { respond } = useSession();

const [currentStep, setCurrentStep] = useState(0);
const backToWallet = () => {
history.push(routes.wallet.path);
};

const onMultiStepChange = useCallback(({ step: { current } }) => {
setIsStepTxSignatureCollector([2, 3].includes(current));
setCurrentStep(current);
}, []);

const onCloseIcon = async () => {
const isStatusView = currentStep === 3;
if (!isStatusView) {
await respond({ payload: USER_REJECT_ERROR });
}
};

const isStepTxSignatureCollector = [2, 3].includes(currentStep);

return (
<Dialog hasClose className={styles.dialogWrapper} size={isStepTxSignatureCollector && 'sm'}>
<Dialog
hasClose
onCloseIcon={onCloseIcon}
className={styles.dialogWrapper}
size={isStepTxSignatureCollector && 'sm'}
>
<MultiStep
onChange={onMultiStepChange}
key="RequestView"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,24 @@ import { shallow } from 'enzyme';
import MultiStep from '@common/components/OldMultiStep';
import TxSignatureCollector from '@transaction/components/TxSignatureCollector';
import Dialog from 'src/theme/dialog/dialog';
import { useSession } from '@libs/wcm/hooks/useSession';
import Summary from '@wallet/components/RequestSignSummary';
import Status from '@wallet/components/RequestSignStatus';
import RequestSummary from '../RequestSummary';
import RequestView from './RequestView';

jest.mock('@libs/wcm/hooks/usePairings');
jest.mock('@libs/wcm/hooks/useSession', () => ({
respond: jest.fn(),
}));
jest.mock('@walletconnect/utils', () => ({
getSdkError: jest.fn((str) => str),
}));
jest.mock('@libs/wcm/hooks/useSession');

describe('RequestView', () => {
const mockRespond = jest.fn();
useSession.mockReturnValue({
respond: mockRespond,
});

it('should render properly getting data from URL', () => {
const wrapper = shallow(<RequestView history={{}} />);

Expand Down
4 changes: 4 additions & 0 deletions src/modules/message/components/signMessageView/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import { shallow } from 'enzyme';
import accounts from '@tests/constants/wallets';
import SignMessage from './index';

jest.mock('@walletconnect/utils', () => ({
getSdkError: jest.fn((str) => str),
}));

describe('Sign Message Component', () => {
const props = {
account: accounts.genesis,
Expand Down
9 changes: 8 additions & 1 deletion src/modules/message/components/signedMessage/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { removeSearchParamsFromUrl } from 'src/utils/searchParams';
import { statusMessages } from '@transaction/configuration/statusConfig';
import getIllustration from '@transaction/components/TxBroadcaster/illustrationsMap';
import Icon from '@theme/Icon';
import { useSession } from '@libs/wcm/hooks/useSession';
import styles from './signedMessage.css';

const Error = ({ t, error, reset }) => {
Expand Down Expand Up @@ -56,11 +57,13 @@ const Success = ({ t, signature, copied, copy, onPrev }) => {
);
};

// eslint-disable-next-line max-statements
const SignedMessage = ({ signature, error, onPrev, reset }) => {
const history = useHistory();
const ref = useRef();
const { t } = useTranslation();
const [copied, setCopied] = useState(false);
const ref = useRef();
const { respond } = useSession();

const copy = () => {
setCopied(true);
Expand All @@ -69,6 +72,10 @@ const SignedMessage = ({ signature, error, onPrev, reset }) => {

useEffect(() => () => clearTimeout(ref.current), []);

useEffect(() => {
respond({ payload: error || signature });
}, []);

if (error) {
return <Error t={t} error={error} reset={reset} />;
}
Expand Down
14 changes: 14 additions & 0 deletions src/modules/message/components/signedMessage/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,23 @@ import React from 'react';
import { mount } from 'enzyme';
import { act } from 'react-dom/test-utils';
import accounts from '@tests/constants/wallets';
import { useSession } from '@libs/wcm/hooks/useSession';
import Status from '.';

jest.mock('@walletconnect/utils', () => ({
getSdkError: jest.fn((str) => str),
}));
jest.mock('@libs/wcm/hooks/useSession');

describe('Sign Message: Status', () => {
const proposal = {};
const respond = jest.fn(() => ({
status: 'SUCCESS',
data: proposal,
}));

useSession.mockReturnValue({ respond });

const baseProps = {
account: accounts.genesis,
t: (str) => str,
Expand Down
Loading

0 comments on commit 711c326

Please sign in to comment.