Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wallet connect improvements #5474

Merged
merged 20 commits into from
Nov 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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