From 983a9ca2f806bfbbbdb7b1f6d91e204e23ebedb5 Mon Sep 17 00:00:00 2001 From: Leonidas Conde <80922146+LeonardoDizConde@users.noreply.github.com> Date: Tue, 14 May 2024 16:00:03 -0300 Subject: [PATCH] CU-86dt1fxtx - Implement Contextual Messages on WcSdk on both sides --- .../CU-86dt1fxtx-1_2024-05-14-18-59.json | 10 + .../CU-86dt1fxtx-1_2024-05-14-18-59.json | 10 + .../CU-86dt1fxtx-1_2024-05-14-18-59.json | 10 + e2e/package.json | 4 +- e2e/playwright.config.ts | 2 +- e2e/src/constants/GenericData.ts | 1 + e2e/src/pageCommonSteps/WalletReactSteps.ts | 6 +- e2e/tests/DappMethods.spec.ts | 19 +- .../src/components/HelloWorld.tsx | 19 ++ .../src/components/RequestCard.tsx | 8 + .../wc-wallet-react/src/constants/default.ts | 2 +- packages/wallet-connect-sdk-core/src/index.ts | 199 +++++------------- .../wallet-connect-sdk-react/src/index.tsx | 10 + .../src/WcSdkStore.ts | 6 + 14 files changed, 150 insertions(+), 156 deletions(-) create mode 100644 common/changes/@cityofzion/wallet-connect-sdk-core/CU-86dt1fxtx-1_2024-05-14-18-59.json create mode 100644 common/changes/@cityofzion/wallet-connect-sdk-react/CU-86dt1fxtx-1_2024-05-14-18-59.json create mode 100644 common/changes/@cityofzion/wallet-connect-sdk-svelte/CU-86dt1fxtx-1_2024-05-14-18-59.json diff --git a/common/changes/@cityofzion/wallet-connect-sdk-core/CU-86dt1fxtx-1_2024-05-14-18-59.json b/common/changes/@cityofzion/wallet-connect-sdk-core/CU-86dt1fxtx-1_2024-05-14-18-59.json new file mode 100644 index 0000000..f2b3a66 --- /dev/null +++ b/common/changes/@cityofzion/wallet-connect-sdk-core/CU-86dt1fxtx-1_2024-05-14-18-59.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@cityofzion/wallet-connect-sdk-core", + "comment": "Implemented a Contextual Messages on WcSdk", + "type": "patch" + } + ], + "packageName": "@cityofzion/wallet-connect-sdk-core" +} \ No newline at end of file diff --git a/common/changes/@cityofzion/wallet-connect-sdk-react/CU-86dt1fxtx-1_2024-05-14-18-59.json b/common/changes/@cityofzion/wallet-connect-sdk-react/CU-86dt1fxtx-1_2024-05-14-18-59.json new file mode 100644 index 0000000..910602b --- /dev/null +++ b/common/changes/@cityofzion/wallet-connect-sdk-react/CU-86dt1fxtx-1_2024-05-14-18-59.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@cityofzion/wallet-connect-sdk-react", + "comment": "Implemented a Contextual Messages on WcSdk", + "type": "patch" + } + ], + "packageName": "@cityofzion/wallet-connect-sdk-react" +} \ No newline at end of file diff --git a/common/changes/@cityofzion/wallet-connect-sdk-svelte/CU-86dt1fxtx-1_2024-05-14-18-59.json b/common/changes/@cityofzion/wallet-connect-sdk-svelte/CU-86dt1fxtx-1_2024-05-14-18-59.json new file mode 100644 index 0000000..1224cf1 --- /dev/null +++ b/common/changes/@cityofzion/wallet-connect-sdk-svelte/CU-86dt1fxtx-1_2024-05-14-18-59.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@cityofzion/wallet-connect-sdk-svelte", + "comment": "Implemented a Contextual Messages on WcSdk", + "type": "patch" + } + ], + "packageName": "@cityofzion/wallet-connect-sdk-svelte" +} \ No newline at end of file diff --git a/e2e/package.json b/e2e/package.json index 8d2989d..57c3ff7 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -13,8 +13,8 @@ "prepare": "npm install serve -g && pnpm exec playwright install", "report:clean": "rm -rf playwright-report test-results tests-results", "report:open": "npx pnpm playwright show-report", - "test": "playwright test --headed", - "test:headless": "playwright test" + "test": "playwright test --headed --trace on", + "test:headless": "playwright test --trace on" }, "devDependencies": { "@playwright/test": "^1.38.1", diff --git a/e2e/playwright.config.ts b/e2e/playwright.config.ts index cb88f1b..40a6c91 100644 --- a/e2e/playwright.config.ts +++ b/e2e/playwright.config.ts @@ -3,7 +3,7 @@ import { JSON_REPORT_PATH, TESTS_DIR } from './src/constants/PathsDefinitions' import { MAX_RETRIES, RUN_CONCURRENTLY_COMMAND } from './src/constants/DevConstants' const config: PlaywrightTestConfig = { - fullyParallel: true, + fullyParallel: false, testMatch: '**/*.spec.ts', webServer: { command: RUN_CONCURRENTLY_COMMAND, diff --git a/e2e/src/constants/GenericData.ts b/e2e/src/constants/GenericData.ts index 1ea8a7f..d01ca5c 100644 --- a/e2e/src/constants/GenericData.ts +++ b/e2e/src/constants/GenericData.ts @@ -1 +1,2 @@ export const ACCOUNT_PASSWORD: string = "Password: Not '12345' - because we're not living in 1995!" +export const DAPP_METHOD_CONTEXT_MESSAGE: string = "Accept this and your balance goes 'to the moon'!" diff --git a/e2e/src/pageCommonSteps/WalletReactSteps.ts b/e2e/src/pageCommonSteps/WalletReactSteps.ts index 7f418a1..5b65555 100644 --- a/e2e/src/pageCommonSteps/WalletReactSteps.ts +++ b/e2e/src/pageCommonSteps/WalletReactSteps.ts @@ -14,7 +14,10 @@ export async function connectDappToReactWallet(walletPage: ExampleProject, dappU await walletPage.page.getByTestId('connect-dapp__dapp-uri-input').fill(dappUri.trim()) // Fill in the dapp URI await walletPage.awaitAndClickTestId('proposal-card__approve') // Click the approve button } -export async function acceptPendingRequestToReactWallet(walletPage: ExampleProject) { +export async function acceptPendingRequestToReactWallet( + walletPage: ExampleProject, + actionBeforeAcceptMethod?: (walletPage: ExampleProject) => Promise | void, +) { let retries = 0 let requests = [] do { @@ -24,5 +27,6 @@ export async function acceptPendingRequestToReactWallet(walletPage: ExampleProje } while (retries < MAX_RETRIES && requests.length == 0) if (requests.length === 0) throw Error('No pending requests are found') await walletPage.awaitAndClickTestId('default-card__pending-request') + if (actionBeforeAcceptMethod) await actionBeforeAcceptMethod(walletPage) await walletPage.awaitAndClickTestId('request-card__approve') } diff --git a/e2e/tests/DappMethods.spec.ts b/e2e/tests/DappMethods.spec.ts index 528afc3..3e41da8 100644 --- a/e2e/tests/DappMethods.spec.ts +++ b/e2e/tests/DappMethods.spec.ts @@ -1,8 +1,9 @@ import { expect, test } from '@playwright/test' import { DAPP_REACT, WALLET_REACT } from '../src/constants/ProjectsDefinitions' import { connectReactDappToNewReactAccount } from '../src/helpers/CommonStepsHelper' -import { getAnyFromInnerHTML } from '../src/helpers/CleanerHelper' +import { getAnyFromInnerHTML, getCleanInnerHTML } from '../src/helpers/CleanerHelper' import { acceptPendingRequestToReactWallet } from '../src/pageCommonSteps/WalletReactSteps' +import { DAPP_METHOD_CONTEXT_MESSAGE } from '../src/constants/GenericData' test('Create a new account and connect with a dapp (React)', async ({ context }) => { // Define the dapp and wallet pages @@ -152,3 +153,19 @@ test('Test Wipe Methods on dapp (React)', async ({ context }) => { const pendingRequests = await walletPage.page.getByTestId('default-card__pending-request').all() expect(pendingRequests.length).toBe(0) // Verify if has no pending Requests }) + +test('Test send a contextual with a Verify Success on dapp (React)', async ({ context }) => { + // Define the dapp and wallet pages + const dappPage = DAPP_REACT + const walletPage = WALLET_REACT + await connectReactDappToNewReactAccount(context, dappPage, walletPage) + await dappPage.awaitAndClickTestId('hello-world__verify-with-context') + await acceptPendingRequestToReactWallet(walletPage, async (walletPageBeforeAcceptMethod) => { + const contextMessage = await walletPageBeforeAcceptMethod.awaitAndGetTestId('request-card__contextual-message') + expect(contextMessage).toBeDefined() + expect(await getCleanInnerHTML(contextMessage)).toBe(DAPP_METHOD_CONTEXT_MESSAGE) + }) + const response = await getAnyFromInnerHTML(await dappPage.awaitAndGetTestId('hello-world__method-response')) + expect(response).toBeDefined() // Verify if the response had a return + expect(response as boolean).toBeTruthy() // Verify if the response returned true +}) diff --git a/examples/wc-dapp-react/src/components/HelloWorld.tsx b/examples/wc-dapp-react/src/components/HelloWorld.tsx index f036164..93d479f 100644 --- a/examples/wc-dapp-react/src/components/HelloWorld.tsx +++ b/examples/wc-dapp-react/src/components/HelloWorld.tsx @@ -440,6 +440,22 @@ function HelloWorld() { } } + const verifySuccessWithContext = async () => { + try { + const resp = await wcSdk.withContext("Accept this and your balance goes 'to the moon'!").verifyMessage({ + publicKey: '031757edb62014dea820a0b33a156f6a59fc12bd966202f0e49357c81f26f5de34', + data: 'aeb234ed1639e9fcc95a102633b1c70ca9f9b97e9592cc74bfc40cbc7fefdb19ae8c6b49ebd410dbcbeec6b5906e503d528e34cd5098cc7929dbcbbaf23c5d77', + salt: '052a55a8d56b73b342a8e41da3050b09', + messageHex: + '010001f0a0303532613535613864353662373362333432613865343164613330353062303965794a68624763694f694a49557a49314e694973496e523563434936496b705856434a392e65794a6c654841694f6a45324e444d304e7a63324e6a4d73496d6c68644349364d5459304d7a4d354d5449324d33302e7253315f73735230364c426778744831504862774c306d7a6557563950686d5448477a324849524f4a4f340000', + }) + console.log(resp) + setResponse(JSON.stringify(resp, null, 2)) + } catch (e) { + onError(e) + } + } + const onError = (error: any) => { Toastify({ text: error.message, @@ -528,6 +544,9 @@ function HelloWorld() { + diff --git a/examples/wc-wallet-react/src/components/RequestCard.tsx b/examples/wc-wallet-react/src/components/RequestCard.tsx index c044dd0..c72f52a 100644 --- a/examples/wc-wallet-react/src/components/RequestCard.tsx +++ b/examples/wc-wallet-react/src/components/RequestCard.tsx @@ -31,6 +31,8 @@ export default function RequestCard( props.closeRequest() } + const contextualMessage = String(request.params.contextualMessage).trim() + const items = Array.isArray(request.params) ? request.params : request.params.invocations @@ -59,6 +61,12 @@ export default function RequestCard( Neo3 + + Method Contextual Message + + + {contextualMessage} + Method diff --git a/examples/wc-wallet-react/src/constants/default.ts b/examples/wc-wallet-react/src/constants/default.ts index 140caa6..8424e35 100644 --- a/examples/wc-wallet-react/src/constants/default.ts +++ b/examples/wc-wallet-react/src/constants/default.ts @@ -33,7 +33,7 @@ export const DEFAULT_APP_METADATA = { export const DEFAULT_CHAIN: Chain = 'testnet' export const DEFAULT_NETWORKS: Record = { - testnet: { url: 'https://testnet1.neo.coz.io:443', name: 'Testnet' }, + testnet: { url: 'https://testnet2.neo.coz.io:443', name: 'Testnet' }, mainnet: { url: 'http://seed1.neo.org:10332', name: 'Mainnet' }, private: { url: null, name: 'Private Network' }, } diff --git a/packages/wallet-connect-sdk-core/src/index.ts b/packages/wallet-connect-sdk-core/src/index.ts index 7ad6e39..b64faab 100644 --- a/packages/wallet-connect-sdk-core/src/index.ts +++ b/packages/wallet-connect-sdk-core/src/index.ts @@ -50,6 +50,7 @@ export type Method = | 'calculateFee' | 'signTransaction' | 'wipeRequests' + | 'withContext' /** * A number that will be compared by the wallet to check if it is compatible with the dApp @@ -111,6 +112,31 @@ class WcSdk implements Neo3Invoker, Neo3Signer { */ private _session: SessionTypes.Struct | null = null + /** + * A contextual message to inform the user about method invoked + */ + private contextualMessage: string | undefined = undefined + + /** + * Sends a request to the blockchain + */ + private async sendRequest(method: Method, params: any): Promise { + const request = { + id: 1, + jsonrpc: '2.0', + method, + params: { ...params, contextualMessage: this.contextualMessage }, + } + + delete this.contextualMessage + + return await this.signClient.request({ + topic: this.session?.topic ?? '', + chainId: this.getChainId() ?? '', + request, + }) + } + /** * The EventEmitter to listen for some property changes */ @@ -293,18 +319,8 @@ class WcSdk implements Neo3Invoker, Neo3Signer { */ async signTransaction(params: ContractInvocationMulti | BuiltTransaction): Promise { this.validateContractInvocationMulti(params) - const request = { - id: 1, - jsonrpc: '2.0', - method: 'signTransaction', - params, - } - const resp = await this.signClient.request({ - topic: this.session?.topic ?? '', - chainId: this.getChainId() ?? '', - request, - }) + const resp = await this.sendRequest('signTransaction', params) if (!resp) { throw new WcSdkError(resp) @@ -357,18 +373,7 @@ class WcSdk implements Neo3Invoker, Neo3Signer { async invokeFunction(params: ContractInvocationMulti): Promise { this.validateContractInvocationMulti(params) - const request = { - id: 1, - jsonrpc: '2.0', - method: 'invokeFunction', - params, - } - - const resp: unknown = await this.signClient.request({ - topic: this.session?.topic ?? '', - chainId: this.getChainId() ?? '', - request, - }) + const resp = await this.sendRequest('invokeFunction', params) if (typeof resp !== 'string') { throw new WcSdkError(resp) @@ -384,18 +389,8 @@ class WcSdk implements Neo3Invoker, Neo3Signer { */ async calculateFee(params: ContractInvocationMulti): Promise { this.validateContractInvocationMulti(params) - const request = { - id: 1, - jsonrpc: '2.0', - method: 'calculateFee', - params, - } - const resp = await this.signClient.request({ - topic: this.session?.topic ?? '', - chainId: this.getChainId() ?? '', - request, - }) + const resp = await this.sendRequest('calculateFee', params) if (!resp) { throw new WcSdkError(resp) @@ -447,18 +442,7 @@ class WcSdk implements Neo3Invoker, Neo3Signer { async testInvoke(params: ContractInvocationMulti): Promise { this.validateContractInvocationMulti(params) - const request = { - id: 1, - jsonrpc: '2.0', - method: 'testInvoke', - params, - } - - const resp: InvokeResult | null = await this.signClient.request({ - topic: this.session?.topic ?? '', - chainId: this.getChainId() ?? '', - request, - }) + const resp: InvokeResult = (await this.sendRequest('testInvoke', params)) as InvokeResult if (!resp || (resp && resp.state !== 'HALT')) { throw new WcSdkError(resp) @@ -474,18 +458,7 @@ class WcSdk implements Neo3Invoker, Neo3Signer { * @return the signed message object */ async signMessage(params: SignMessagePayload): Promise { - const request = { - id: 1, - jsonrpc: '2.0', - method: 'signMessage', - params, - } - - const resp = await this.signClient.request({ - topic: this.session?.topic ?? '', - chainId: this.getChainId() ?? '', - request, - }) + const resp = await this.sendRequest('signMessage', params) if (!resp) { throw new WcSdkError(resp) @@ -501,18 +474,7 @@ class WcSdk implements Neo3Invoker, Neo3Signer { * @return true if the signedMessage is acknowledged by the account */ async verifyMessage(params: SignedMessage): Promise { - const request = { - id: 1, - jsonrpc: '2.0', - method: 'verifyMessage', - params, - } - - const resp = await this.signClient.request({ - topic: this.session?.topic ?? '', - chainId: this.getChainId() ?? '', - request, - }) + const resp = await this.sendRequest('verifyMessage', params) if (resp === null || resp === undefined) { throw new WcSdkError(resp) @@ -530,18 +492,7 @@ class WcSdk implements Neo3Invoker, Neo3Signer { * @return the call result promise */ async traverseIterator(sessionId: string, iteratorId: string, count: number): Promise { - const request = { - id: 1, - jsonrpc: '2.0', - method: 'traverseIterator', - params: [sessionId, iteratorId, count], - } - - const resp = await this.signClient.request({ - topic: this.session?.topic ?? '', - chainId: this.getChainId() ?? '', - request, - }) + const resp = await this.sendRequest('traverseIterator', [sessionId, iteratorId, count]) if (!resp) { throw new WcSdkError(resp) @@ -555,18 +506,7 @@ class WcSdk implements Neo3Invoker, Neo3Signer { * @return wallet information */ async getWalletInfo(): Promise { - const request = { - id: 1, - jsonrpc: '2.0', - method: 'getWalletInfo', - params: [], - } - - const resp = await this.signClient.request({ - topic: this.session?.topic ?? '', - chainId: this.getChainId() ?? '', - request, - }) + const resp = await this.sendRequest('getWalletInfo', []) if (!resp) { throw new WcSdkError(resp) @@ -580,18 +520,7 @@ class WcSdk implements Neo3Invoker, Neo3Signer { * @return network information */ async getNetworkVersion(): Promise { - const request = { - id: 1, - jsonrpc: '2.0', - method: 'getNetworkVersion', - params: [], - } - - const resp = await this.signClient.request({ - topic: this.session?.topic ?? '', - chainId: this.getChainId() ?? '', - request, - }) + const resp = await this.sendRequest('getNetworkVersion', []) if (!resp) { throw new WcSdkError(resp) @@ -601,17 +530,7 @@ class WcSdk implements Neo3Invoker, Neo3Signer { } async encrypt(message: string, publicKeys: string[]): Promise { - const request = { - id: 1, - jsonrpc: '2.0', - method: 'encrypt', - params: [message, publicKeys], - } - const resp = await this.signClient.request({ - topic: this.session?.topic ?? '', - chainId: this.getChainId() ?? '', - request, - }) + const resp = await this.sendRequest('encrypt', [message, publicKeys]) if (!resp) { throw new WcSdkError(resp) @@ -621,17 +540,7 @@ class WcSdk implements Neo3Invoker, Neo3Signer { } async decrypt(payload: EncryptedPayload): Promise { - const request = { - id: 1, - jsonrpc: '2.0', - method: 'decrypt', - params: [payload], - } - const resp = await this.signClient.request({ - topic: this.session?.topic ?? '', - chainId: this.getChainId() ?? '', - request, - }) + const resp = await this.sendRequest('encrypt', [payload]) if (!resp) { throw new WcSdkError(resp) @@ -641,17 +550,7 @@ class WcSdk implements Neo3Invoker, Neo3Signer { } async decryptFromArray(payloads: EncryptedPayload[]): Promise { - const request = { - id: 1, - jsonrpc: '2.0', - method: 'decryptFromArray', - params: [payloads], - } - const resp = await this.signClient.request({ - topic: this.session?.topic ?? '', - chainId: this.getChainId() ?? '', - request, - }) + const resp = await this.sendRequest('decryptFromArray', [payloads]) if (!resp) { throw new WcSdkError(resp) @@ -661,17 +560,7 @@ class WcSdk implements Neo3Invoker, Neo3Signer { } async wipeRequests(): Promise { - const request = { - id: 1, - jsonrpc: '2.0', - method: 'wipeRequests', - params: [], - } - const resp = await this.signClient.request({ - topic: this.session?.topic ?? '', - chainId: this.getChainId() ?? '', - request, - }) + const resp = await this.sendRequest('wipeRequests', []) if (!resp) { throw new WcSdkError(resp) @@ -680,6 +569,16 @@ class WcSdk implements Neo3Invoker, Neo3Signer { return resp as string[] } + /** + * Sets the context message for the next method invocation in the Wallet Connect SDK. + * @param {string} contextualMessage - A message to inform the user about the method being invoked. + * @returns {WcSdk} - An instance of Wallet Connect with the updated context message. + */ + withContext(contextualMessage: string): WcSdk { + this.contextualMessage = contextualMessage + return this + } + private validateContractInvocationMulti(request: ContractInvocationMulti): boolean { // verify fields this.objectValidation(request, ['signers', 'invocations']) diff --git a/packages/wallet-connect-sdk-react/src/index.tsx b/packages/wallet-connect-sdk-react/src/index.tsx index 6170160..a486a49 100644 --- a/packages/wallet-connect-sdk-react/src/index.tsx +++ b/packages/wallet-connect-sdk-react/src/index.tsx @@ -112,6 +112,8 @@ interface IWalletConnectContext extends Neo3Invoker, Neo3Signer { getNetworkVersion: () => Promise wipeRequests: () => Promise + + withContext: (contextualMessage: string) => WcSdk } export const WalletConnectContext = React.createContext({} as IWalletConnectContext) @@ -263,6 +265,13 @@ export const WalletConnectProvider: React.FC<{ return getSdkOrError().wipeRequests() }, [getSdkOrError]) + const withContext = useCallback( + (contextualMessage: string) => { + return getSdkOrError().withContext(contextualMessage) + }, + [getSdkOrError], + ) + const setupWcClient = useCallback(async () => { if (!options) return const wcSdk = await WcSdk.init(options) @@ -308,6 +317,7 @@ export const WalletConnectProvider: React.FC<{ signTransaction, calculateFee, wipeRequests, + withContext, } return {children} diff --git a/packages/wallet-connect-sdk-svelte/src/WcSdkStore.ts b/packages/wallet-connect-sdk-svelte/src/WcSdkStore.ts index efb2542..52bafdb 100644 --- a/packages/wallet-connect-sdk-svelte/src/WcSdkStore.ts +++ b/packages/wallet-connect-sdk-svelte/src/WcSdkStore.ts @@ -113,6 +113,8 @@ export interface IWalletConnectStore extends Neo3Invoker, Neo3Signer { * @return network information */ getNetworkVersion: () => Promise + + withContext: (contextualMessage: string) => WcSdk } export class WCSDKStore implements IWalletConnectStore { @@ -216,6 +218,10 @@ export class WCSDKStore implements IWalletConnectStore { return await this.SdkOrError.wipeRequests() } + withContext(contextualMessage: string): WcSdk { + return this.SdkOrError.withContext(contextualMessage) + } + get session() { return derived(this.sessionWritable, (session) => session) }