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

[LW-9482] Fix/cip30 sign data #807

Merged
merged 4 commits into from
Jan 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -112,15 +112,15 @@ export const DappConfirmData = (): React.ReactElement => {
try {
HardwareLedger.LedgerKeyAgent.establishDeviceConnection(Wallet.KeyManagement.CommunicationType.Web)
.then(() => {
exposeApi<Pick<UserPromptService, 'allowSignTx'>>(
exposeApi<Pick<UserPromptService, 'allowSignData'>>(
{
api$: of({
async allowSignTx(): Promise<boolean> {
async allowSignData(): Promise<boolean> {
return Promise.resolve(true);
}
}),
baseChannel: DAPP_CHANNELS.userPrompt,
properties: { allowSignTx: RemoteApiPropertyType.MethodReturningPromise }
properties: { allowSignData: RemoteApiPropertyType.MethodReturningPromise }
},
{ logger: console, runtime }
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ export const SignData = (): React.ReactElement => {
utils: { setPreviousView }
} = useViewsFlowContext();
const redirectToSignFailure = useRedirection(dAppRoutePaths.dappTxSignFailure);
const redirectToSignSuccess = useRedirection(dAppRoutePaths.dappTxSignSuccess);
const { executeWithPassword } = useWalletManager();
const [isLoading, setIsLoading] = useState(false);
const [password, setPassword] = useState<string>();
Expand Down Expand Up @@ -49,13 +48,12 @@ export const SignData = (): React.ReactElement => {
},
{ logger: console, runtime }
);
redirectToSignSuccess();
} catch {
redirectToSignFailure();
} finally {
setIsLoading(false);
}
}, [password, redirectToSignFailure, keyAgentData, redirectToSignSuccess]);
}, [password, redirectToSignFailure, keyAgentData]);

const onConfirm = useCallback(
() => executeWithPassword(password, handleVerifyPass, false),
Expand Down Expand Up @@ -85,10 +83,20 @@ export const SignData = (): React.ReactElement => {
</Spin>
</div>
<div className={styles.actions}>
<Button onClick={onConfirm} disabled={confirmIsDisabled || isLoading} className={styles.actionBtn}>
<Button
onClick={onConfirm}
disabled={confirmIsDisabled || isLoading}
className={styles.actionBtn}
data-testid="sign-transaction-confirm"
>
{t('dapp.confirm.btn.confirm')}
</Button>
<Button onClick={setPreviousView} color="secondary" className={styles.actionBtn}>
<Button
onClick={setPreviousView}
color="secondary"
className={styles.actionBtn}
data-testid="sign-transaction-cancel"
>
{t('dapp.confirm.btn.cancel')}
</Button>
</div>
Expand Down
32 changes: 23 additions & 9 deletions packages/cardano/src/wallet/lib/cardano-wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,31 +127,45 @@ export const createCardanoWalletsByChain = async (
return { wallet, keyAgent: activeKeyAgent, keyAgentsByChain };
};

const executeSigningCallbackIfExists = (signCallback?: (result: boolean) => void, response?: boolean) => {
if (signCallback)
setTimeout(() => {
signCallback(response);
}, 0);
};

const createAsyncKeyAgentWithCallback = (
keyAgent: KeyManagement.KeyAgent,
signCallback?: (result: boolean) => void
): KeyManagement.AsyncKeyAgent => {
const asyncKeyAgent = KeyManagement.util.createAsyncKeyAgent(keyAgent);
// TODO: LW-7807 revise the sdk cip30 implementation

const wrappedSign = (...args: Parameters<KeyManagement.KeyAgent['signTransaction']>) =>
keyAgent
.signTransaction(...args)
.then(async (sigs) => {
if (signCallback)
setTimeout(() => {
signCallback(true);
}, 0);
executeSigningCallbackIfExists(signCallback, true);
return sigs;
})
.catch((error) => {
executeSigningCallbackIfExists(signCallback, false);
throw new Error(error);
});

const wrappedSignData = (...args: Parameters<KeyManagement.KeyAgent['signBlob']>) =>
keyAgent
.signBlob(...args)
.then(async (sigs) => {
executeSigningCallbackIfExists(signCallback, true);
return sigs;
})
.catch((error) => {
if (signCallback)
setTimeout(() => {
signCallback(false);
}, 0);
executeSigningCallbackIfExists(signCallback, false);
throw new Error(error);
});

return { ...asyncKeyAgent, signTransaction: wrappedSign.bind(keyAgent) };
return { ...asyncKeyAgent, signTransaction: wrappedSign.bind(keyAgent), signBlob: wrappedSignData.bind(keyAgent) };
};

/**
Expand Down
11 changes: 11 additions & 0 deletions packages/e2e-tests/src/assert/consoleAssert.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { expect } from 'chai';
import consoleManager, { ConsoleLogEntry } from '../utils/consoleManager';

class ConsoleAssert {
assertNoErrorsInConsole = async () => {
const logs: ConsoleLogEntry[] = await consoleManager.getLogs();
const errors: ConsoleLogEntry[] = logs.filter((log) => log.level === 'error');
expect(errors).is.empty;
};
}
export default new ConsoleAssert();
5 changes: 5 additions & 0 deletions packages/e2e-tests/src/elements/dappConnector/testDAppPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class TestDAppPage {
private SEND_ADA_RUN_BUTTON = '[data-testid="send-ada-run-button"]';
private SEND_TOKEN_RUN_BUTTON = '[data-testid="send-token-run-button"]';
private SET_COLLATERAL_BUTTON = '[data-testid="collateral-run-button"]';
private SIGN_DATA_BUTTON = '[data-testid="sign-data-run-button"]';

get walletItem(): ChainablePromiseElement<WebdriverIO.Element> {
return $(this.WALLET_ITEM);
Expand Down Expand Up @@ -121,6 +122,10 @@ class TestDAppPage {
get setCollateralButton(): ChainablePromiseElement<WebdriverIO.Element> {
return $(this.SET_COLLATERAL_BUTTON);
}

get signDataButton(): ChainablePromiseElement<WebdriverIO.Element> {
return $(this.SIGN_DATA_BUTTON);
}
}

export default new TestDAppPage();
18 changes: 17 additions & 1 deletion packages/e2e-tests/src/features/DAppConnector.feature
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Feature: DAppConnector - Common
And I reclaim collateral (if active) in extended mode

@LW-3760 @Testnet @Mainnet
Scenario: Extended View - Limited wallet information when wallet is not connected
Scenario: Limited wallet information when wallet is not connected
When I open test DApp
Then I see Lace wallet info in DApp when not connected

Expand Down Expand Up @@ -184,6 +184,22 @@ Feature: DAppConnector - Common
When I open test DApp
Then I see DApp authorization window

@LW-9481 @Testnet @Mainnet
Scenario: Signing data / no errors in console
Given I am on Tokens extended page
And I save token: "Cardano" balance
And I open and authorize test DApp with "Only once" setting
And I enable console logs collection
When I click "Sign data" button in test DApp
Then I see DApp connector "Confirm transaction" page in dark mode
And I click "Confirm" button on "Confirm transaction" page
And I see DApp connector "Sign transaction" page
And I fill correct password
And I click "Confirm" button on "Sign transaction" page
And I see DApp connector "All done" page
And I verify there are no errors in console logs
And I see Lace wallet info in DApp when connected

@LW-8403 @LW-8406
Scenario: Automatically trigger collateral setup - happy path
Given I am on Settings extended page
Expand Down
2 changes: 2 additions & 0 deletions packages/e2e-tests/src/hooks/scenarioTagRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { clearBackgroundStorageKey } from '../utils/browserStorage';
import { closeAllTabsExceptOriginalOne } from '../utils/window';
import networkManager from '../utils/networkManager';
import { browser } from '@wdio/globals';
import consoleManager from '../utils/consoleManager';

// eslint-disable-next-line no-unused-vars
Before(async () => {
Expand All @@ -14,6 +15,7 @@ Before(async () => {

After(async () => {
await networkManager.closeOpenedCdpSessions();
await consoleManager.closeOpenedCdpSessions();
await browser.disableInterceptor();
testContext.clearContext();
await clearBackgroundStorageKey(); // FIXME: does not work for onboarding scenarios - error is thrown
Expand Down
19 changes: 19 additions & 0 deletions packages/e2e-tests/src/steps/commonSteps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ import { visit } from '../utils/pageUtils';
import CommonDrawerElements from '../elements/CommonDrawerElements';
import DAppConnectorPageObject from '../pageobject/dAppConnectorPageObject';
import settingsExtendedPageObject from '../pageobject/settingsExtendedPageObject';
import consoleManager from '../utils/consoleManager';
import consoleAssert from '../assert/consoleAssert';

Given(/^Lace is ready for test$/, async () => {
await settingsExtendedPageObject.waitUntilSyncingModalDisappears();
Expand Down Expand Up @@ -283,3 +285,20 @@ Then(/^Clipboard contains address of wallet: "([^"]*)"$/, async (walletName: str
Then(/^Clipboard contains text: "([^"]*)"$/, async (expectedString: string) => {
await commonAssert.assertClipboardContains(expectedString);
});

When(/^I (enable|disable) console logs collection$/, async (action: 'enable' | 'disable') => {
switch (action) {
case 'enable':
await consoleManager.startLogsCollection();
break;
case 'disable':
await consoleManager.closeOpenedCdpSessions();
break;
default:
throw new Error('Unsupported option');
}
});

Then(/^I verify there are no errors in console logs$/, async () => {
await consoleAssert.assertNoErrorsInConsole();
});
13 changes: 11 additions & 2 deletions packages/e2e-tests/src/steps/dAppConnectorSteps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,10 +192,19 @@ Then(/^I de-authorize test DApp in (extended|popup) mode$/, async (mode: 'extend
await DAppConnectorPageObject.deauthorizeDApp(DAppConnectorPageObject.TEST_DAPP_NAME, mode);
});

When(/^I click "Set Collateral" button in test DApp$/, async () => {
When(/^I click "(Set Collateral|Sign data)" button in test DApp$/, async (button: 'Set Collateral' | 'Sign data') => {
await DAppConnectorPageObject.switchToTestDAppWindow();
await browser.pause(1000);
await TestDAppPage.setCollateralButton.click();
switch (button) {
case 'Set Collateral':
await TestDAppPage.setCollateralButton.click();
break;
case 'Sign data':
await TestDAppPage.signDataButton.click();
break;
default:
throw new Error(`Unsupported button: ${button}`);
}
});

Then(/^I click "(Send ADA|Send Token)" "Run" button in test DApp$/, async (runButton: 'Send ADA' | 'Send Token') => {
Expand Down
54 changes: 54 additions & 0 deletions packages/e2e-tests/src/utils/consoleManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { CDPSession } from 'puppeteer-core/lib/esm/puppeteer/common/Connection';
import { browser } from '@wdio/globals';

export interface ConsoleLogEntry {
source: string;
level: string;
text: string;
url: string;
line: string;
column: string;
}

export class ConsoleManager {
private readonly CONSOLE_ENABLE = 'Console.enable';
private static cdpSessions: CDPSession[] = [];
private static capturedLogs: ConsoleLogEntry[] = [];

startLogsCollection = async (): Promise<void> => {
await this.clearLogs();
await browser.call(async () => {
const puppeteer = await browser.getPuppeteer();
const targets = puppeteer
.targets()
.filter(
(target) => target.type() === 'page' || target.type() === 'service_worker' || target.type() === 'other'
);
targets.map(async (target) => {
const client: CDPSession = (await target.createCDPSession()) as unknown as CDPSession;
ConsoleManager.cdpSessions.push(client);
await client.send(this.CONSOLE_ENABLE);
client.on('Console.messageAdded', async (entry: any) => {
if (entry.message.level !== 'debug') {
ConsoleManager.capturedLogs.push(entry.message);
}
});
});
});
};

clearLogs = async (): Promise<void> => {
ConsoleManager.capturedLogs = [];
};

getLogs = async (): Promise<ConsoleLogEntry[]> => ConsoleManager.capturedLogs;
closeOpenedCdpSessions = async (): Promise<void> => {
await this.clearLogs();
ConsoleManager.cdpSessions.map(async (session) => {
if (session.connection()) await session.detach();
});
ConsoleManager.cdpSessions = [];
};
}

export default new ConsoleManager();
8 changes: 4 additions & 4 deletions packages/e2e-tests/src/utils/networkManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export class NetworkManager {
const puppeteer = await browser.getPuppeteer();
const targets = puppeteer.targets().filter((target) => target.type() === 'page');
targets.map(async (target) => {
const client = await target.createCDPSession();
const client: CDPSession = (await target.createCDPSession()) as unknown as CDPSession;
NetworkManager.cdpSessions.push(client);
await client.send(this.NETWORK_ENABLE);
client.on('Network.requestWillBeSent', (params: any) => {
Expand Down Expand Up @@ -47,7 +47,7 @@ export class NetworkManager {
(target) => target.type() === 'page' || target.type() === 'service_worker' || target.type() === 'other'
);
targets.map(async (target) => {
const client = await target.createCDPSession();
const client: CDPSession = (await target.createCDPSession()) as unknown as CDPSession;
NetworkManager.cdpSessions.push(client);
await client.send(this.NETWORK_ENABLE);
await client.send('Network.emulateNetworkConditions', {
Expand All @@ -70,7 +70,7 @@ export class NetworkManager {
(target) => target.type() === 'page' || target.type() === 'service_worker' || target.type() === 'other'
);
targets.map(async (target) => {
const client = await target.createCDPSession();
const client: CDPSession = (await target.createCDPSession()) as unknown as CDPSession;
NetworkManager.cdpSessions.push(client);
await client.send('Fetch.enable', {
patterns: [{ urlPattern }]
Expand All @@ -96,7 +96,7 @@ export class NetworkManager {
(target) => target.type() === 'page' || target.type() === 'service_worker' || target.type() === 'other'
);
targets.map(async (target) => {
const client = await target.createCDPSession();
const client: CDPSession = (await target.createCDPSession()) as unknown as CDPSession;
NetworkManager.cdpSessions.push(client);
await client.send(this.NETWORK_ENABLE);
client.on('Network.responseReceived', async (request) => {
Expand Down