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 8 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
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
21 changes: 14 additions & 7 deletions src/modules/wallet/components/RequestSignStatus/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,21 @@ const successData = (t) => ({
),
});

const getStringifiedTransactionJSON = (signedTransaction) => {
const moduleCommand = joinModuleAndCommand(signedTransaction);
const paramSchema = moduleCommandSchemas[moduleCommand];
const transactionJSON = toTransactionJSON(signedTransaction, paramSchema);
return JSON.stringify(transactionJSON);
};
oskarleonard marked this conversation as resolved.
Show resolved Hide resolved

// eslint-disable-next-line max-statements
const RequestSignStatus = (props) => {
const { t } = useTranslation();
const ref = useRef();
const [copied, setCopied] = useState(false);
const transactions = useSelector((state) => state.transactions);
const { respond } = useSession();
const stringifiedTransactionJSON = getStringifiedTransactionJSON(transactions.signedTransaction);

const data =
!transactions.txSignatureError && transactions.signedTransaction?.signatures?.length
Expand All @@ -43,18 +52,16 @@ const RequestSignStatus = (props) => {

const onCopy = () => {
setCopied(true);
const moduleCommand = joinModuleAndCommand(transactions.signedTransaction);
const paramSchema = moduleCommandSchemas[moduleCommand];
const transactionJSON = toTransactionJSON(transactions.signedTransaction, paramSchema);
const payload = JSON.stringify(transactionJSON);
copyToClipboard(payload);
// inform to the application
respond({ payload });
copyToClipboard(stringifiedTransactionJSON);
ref.current = setTimeout(() => setCopied(false), 1000);
};

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

useEffect(() => {
respond({ payload: stringifiedTransactionJSON });
}, []);

return (
<Box className={`${styles.wrapper} transaction-status`}>
<Illustration name={data.illustration} className={styles.illustration} />
Expand Down
11 changes: 10 additions & 1 deletion src/theme/dialog/holder.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import { selectActiveToken } from 'src/redux/selectors';
import { useSession } from '@libs/wcm/hooks/useSession';
import { ACTIONS, EVENTS } from '@libs/wcm/constants/lifeCycle';
import { useEvents } from '@libs/wcm/hooks/useEvents';
import { USER_REJECT_ERROR } from '@libs/wcm/utils/jsonRPCFormat';
import { isEmpty } from 'src/utils/helpers';
import styles from './dialog.css';

// eslint-disable-next-line max-statements
Expand All @@ -22,7 +24,7 @@ const DialogHolder = ({ history }) => {
const [currentAccount] = useCurrentAccount();
const isAuthenticated = Object.keys(currentAccount).length > 0;
const activeToken = useSelector(selectActiveToken);
const { reject } = useSession();
const { reject, respond } = useSession();
const { events } = useEvents();
const networkIsSet = useSelector((state) => !!state.network.name);

Expand Down Expand Up @@ -75,11 +77,18 @@ const DialogHolder = ({ history }) => {
return null;
}

// eslint-disable-next-line max-statements
const onBackDropClick = async (e) => {
if (e.target === backdropRef.current) {
if (modalName !== 'reclaimBalance') {
removeSearchParamsFromUrl(history, ['modal'], true);
}
if (modalName === 'requestView') {
const proposalEvents = events?.find((ev) => ev.name === EVENTS.SESSION_REQUEST);
if (!isEmpty(proposalEvents)) {
await respond({ event: proposalEvents, payload: USER_REJECT_ERROR });
}
}
if (modalName === 'connectionSummary') {
const proposalEvents = events.find((ev) => ev.name === EVENTS.SESSION_PROPOSAL);

Expand Down