From e1d3d0339516d9ac24c43db2c46b41efd35013c4 Mon Sep 17 00:00:00 2001 From: Valazan Date: Thu, 3 Oct 2024 15:23:28 +0200 Subject: [PATCH 1/6] feat: adding signer typed data with connex --- packages/dapp-kit/package.json | 1 + .../src/classes/certificate-wallet.ts | 11 ++ .../dapp-kit/src/classes/wallet-manager.ts | 13 ++ packages/dapp-kit/src/classes/wc-wallet.ts | 12 ++ .../dapp-kit/src/constants/wallet-connect.ts | 1 + packages/dapp-kit/src/dapp-kit.ts | 25 +++- packages/dapp-kit/src/types/types.d.ts | 20 ++- packages/dapp-kit/src/types/wc-types.d.ts | 3 +- .../src/utils/convert-vendor-to-signer.ts | 8 +- .../dapp-kit/src/utils/create-wc-signer.ts | 15 +++ .../dapp-kit/test/helpers/mocked-signer.ts | 4 + yarn.lock | 114 ++++++++---------- 12 files changed, 157 insertions(+), 70 deletions(-) diff --git a/packages/dapp-kit/package.json b/packages/dapp-kit/package.json index fe542aa9..aeb4e66c 100644 --- a/packages/dapp-kit/package.json +++ b/packages/dapp-kit/package.json @@ -31,6 +31,7 @@ "@walletconnect/modal": "2.6.2", "@walletconnect/sign-client": "2.10.2", "@walletconnect/utils": "2.10.2", + "ethers": "^6.13.3", "events": "^3.3.0", "valtio": "1.11.2" }, diff --git a/packages/dapp-kit/src/classes/certificate-wallet.ts b/packages/dapp-kit/src/classes/certificate-wallet.ts index 787e0ed6..67b7bfe1 100644 --- a/packages/dapp-kit/src/classes/certificate-wallet.ts +++ b/packages/dapp-kit/src/classes/certificate-wallet.ts @@ -1,6 +1,8 @@ import { certificate } from '@vechain/sdk-core'; import type { BaseWallet, ConnectResponse, ConnexWallet } from '../types'; import { DEFAULT_CONNECT_CERT_MESSAGE } from '../constants'; +import { ethers } from 'ethers'; +import { SignTypedDataOptions } from '../types/types'; /** * A `ConnexWallet` for wallet's that use a certificate connection @@ -60,6 +62,15 @@ class CertificateBasedWallet implements ConnexWallet { options: Connex.Signer.TxOptions, ): Promise => this.wallet.signTx(msg, options); + signTypedData = ( + _domain: ethers.TypedDataDomain, + _types: Record, + _value: Record, + _options?: SignTypedDataOptions, + ): Promise => { + return this.wallet.signTypedData(_domain, _types, _value); + }; + disconnect = async (): Promise => this.wallet.disconnect?.(); } diff --git a/packages/dapp-kit/src/classes/wallet-manager.ts b/packages/dapp-kit/src/classes/wallet-manager.ts index 0b9aa829..e092b2e9 100644 --- a/packages/dapp-kit/src/classes/wallet-manager.ts +++ b/packages/dapp-kit/src/classes/wallet-manager.ts @@ -10,6 +10,8 @@ import type { import { DAppKitLogger, Storage, createWallet } from '../utils'; import { DEFAULT_CONNECT_CERT_MESSAGE, WalletSources } from '../constants'; import { certificate } from '@vechain/sdk-core'; +import { ethers } from 'ethers'; +import { SignTypedDataOptions } from '../types/types'; class WalletManager { public readonly state: WalletManagerState; @@ -194,6 +196,17 @@ class WalletManager { throw e; }); + signTypedData = ( + domain: ethers.TypedDataDomain, + types: Record, + value: Record, + options?: SignTypedDataOptions, + ): Promise => + this.wallet.signTypedData(domain, types, value, options).catch((e) => { + DAppKitLogger.error('WalletManager', 'signTypedData', e); + throw e; + }); + setSource = (src: WalletSource): void => { if (this.state.source === src) { return; diff --git a/packages/dapp-kit/src/classes/wc-wallet.ts b/packages/dapp-kit/src/classes/wc-wallet.ts index a07ae5f6..9b5a2cd8 100644 --- a/packages/dapp-kit/src/classes/wc-wallet.ts +++ b/packages/dapp-kit/src/classes/wc-wallet.ts @@ -1,4 +1,6 @@ +import { ethers } from 'ethers'; import type { ConnectResponse, ConnexWallet, WCSigner } from '../types'; +import { SignTypedDataOptions } from '../types/types'; class WCWallet implements ConnexWallet { constructor(private readonly signer: WCSigner) {} @@ -23,6 +25,16 @@ class WCWallet implements ConnexWallet { options: Connex.Signer.TxOptions, ): Promise => this.signer.signTx(msg, options); + signTypedData = async ( + _domain: ethers.TypedDataDomain, + _types: Record, + _value: Record, + _options?: SignTypedDataOptions, + ): Promise => { + // Delegating the signTypedData to the signer instance + return this.signer.signTypedData(_domain, _types, _value, _options); + }; + disconnect = (): Promise => this.signer.disconnect(); } diff --git a/packages/dapp-kit/src/constants/wallet-connect.ts b/packages/dapp-kit/src/constants/wallet-connect.ts index e70207a0..17e6b4d7 100644 --- a/packages/dapp-kit/src/constants/wallet-connect.ts +++ b/packages/dapp-kit/src/constants/wallet-connect.ts @@ -6,4 +6,5 @@ export enum DefaultMethods { RequestTransaction = 'thor_sendTransaction', SignCertificate = 'thor_signCertificate', + SignTypedData = 'thor_signTypedData', } diff --git a/packages/dapp-kit/src/dapp-kit.ts b/packages/dapp-kit/src/dapp-kit.ts index 9b1eb174..ddb1f43b 100644 --- a/packages/dapp-kit/src/dapp-kit.ts +++ b/packages/dapp-kit/src/dapp-kit.ts @@ -7,8 +7,21 @@ import { blake2b256, Hex } from '@vechain/sdk-core'; import { WalletManager } from './classes'; import { DAppKitLogger, normalizeGenesisBlock } from './utils'; import type { DAppKitOptions } from './types'; +import { ethers } from 'ethers'; +import { SignTypedDataOptions } from './types/types'; -const cache: Record = {}; +// Define an interface that extends DriverNoVendor to include signTypedData +interface DriverNoVendorExtended extends DriverNoVendor { + signTypedData: ( + domain: ethers.TypedDataDomain, + types: Record, + value: Record, + options?: SignTypedDataOptions, + ) => Promise; +} + +// Update the createThorDriver function to use the extended interface +const cache: Record = {}; /** * START: TEMPORARY COMMENT @@ -23,7 +36,7 @@ const cache: Record = {}; const createThorDriver = ( node: string, genesis: Connex.Thor.Block, -): DriverNoVendor => { +): DriverNoVendorExtended => { // Stringify the certificate to hash const certificateToHash = JSON.stringify({ node, @@ -40,7 +53,11 @@ const createThorDriver = ( let driver = cache[key]; if (!driver) { - driver = new DriverNoVendor(new SimpleNet(node), genesis); + driver = new DriverNoVendor( + new SimpleNet(node), + genesis, + ) as DriverNoVendorExtended; + cache[key] = driver; } return driver; @@ -65,8 +82,10 @@ class DAppKit { const walletManager = new WalletManager(options); + // Assign additional methods from walletManager to driver driver.signTx = walletManager.signTx.bind(walletManager); driver.signCert = walletManager.signCert.bind(walletManager); + driver.signTypedData = walletManager.signTypedData.bind(walletManager); // Add the binding for signTypedData const framework = new Framework(driver); diff --git a/packages/dapp-kit/src/types/types.d.ts b/packages/dapp-kit/src/types/types.d.ts index 8278ea0d..e522c093 100644 --- a/packages/dapp-kit/src/types/types.d.ts +++ b/packages/dapp-kit/src/types/types.d.ts @@ -5,13 +5,22 @@ import type { LogLevel } from '../utils'; declare global { interface Window { vechain?: { - newConnexSigner: (genesisId: string) => Connex.Signer; + newConnexSigner: (genesisId: string) => ExpandedConnexSigner; isInAppBrowser?: boolean; }; connex?: unknown; } } +interface ExpandedConnexSigner extends Connex.Signer { + signTypedData: ( + _domain: ethers.TypedDataDomain, + _types: Record, + _value: Record, + _options?: SignTypedDataOptions, + ) => Promise; +} + type WalletSource = 'wallet-connect' | 'veworld' | 'sync2' | 'sync'; interface WalletConfig { @@ -44,7 +53,7 @@ interface DAppKitOptions { }; } -type BaseWallet = Connex.Signer & { +type BaseWallet = ExpandedConnexSigner & { disconnect?: () => Promise | void; }; @@ -68,6 +77,10 @@ interface WalletManagerState { connectionCertificate: Certificate | null; } +interface SignTypedDataOptions { + signer?: string; +} + export type { BaseWallet, DAppKitOptions, @@ -77,4 +90,7 @@ export type { WalletManagerState, ConnectResponse, Genesis, + SignTypedDataOptions, + ExpandedConnexSigner, + DriverSignedTypedData, }; diff --git a/packages/dapp-kit/src/types/wc-types.d.ts b/packages/dapp-kit/src/types/wc-types.d.ts index 460a4326..c0157883 100644 --- a/packages/dapp-kit/src/types/wc-types.d.ts +++ b/packages/dapp-kit/src/types/wc-types.d.ts @@ -1,5 +1,6 @@ import type { SignClientTypes } from '@walletconnect/types'; import type { SignClient } from '@walletconnect/sign-client'; +import { ExpandedConnexSigner } from './types'; export type ResolvedSignClient = Awaited>; @@ -7,7 +8,7 @@ export type ResolvedSignClient = Awaited>; * WCSigner is a {@link Connex.Signer} with an additional disconnect method * */ -export type WCSigner = Connex.Signer & { +export type WCSigner = ExpandedConnexSigner & { /** * Disconnects and cleans up the WalletConnect session */ diff --git a/packages/dapp-kit/src/utils/convert-vendor-to-signer.ts b/packages/dapp-kit/src/utils/convert-vendor-to-signer.ts index 0ed1638b..467f051e 100644 --- a/packages/dapp-kit/src/utils/convert-vendor-to-signer.ts +++ b/packages/dapp-kit/src/utils/convert-vendor-to-signer.ts @@ -1,6 +1,9 @@ +import { ExpandedConnexSigner } from '../types/types'; import { DAppKitLogger } from './logger'; -export const convertVendorToSigner = (vendor: Connex.Vendor): Connex.Signer => { +export const convertVendorToSigner = ( + vendor: Connex.Vendor, +): ExpandedConnexSigner => { return { signTx: (msg, options): Promise => { const service = vendor.sign('tx', msg); @@ -66,5 +69,8 @@ export const convertVendorToSigner = (vendor: Connex.Vendor): Connex.Signer => { return service.request(); }, + signTypedData(_domain, _types, _value, _options) { + throw new Error('Method not implemented.'); + }, }; }; diff --git a/packages/dapp-kit/src/utils/create-wc-signer.ts b/packages/dapp-kit/src/utils/create-wc-signer.ts index 578a48c6..0ae9695c 100644 --- a/packages/dapp-kit/src/utils/create-wc-signer.ts +++ b/packages/dapp-kit/src/utils/create-wc-signer.ts @@ -8,6 +8,8 @@ import type { SignClient } from '@walletconnect/sign-client/dist/types/client'; import type { WCSigner, WCSignerOptions } from '../types'; import { DefaultMethods } from '../constants'; import { DAppKitLogger } from './logger'; +import { ethers } from 'ethers'; +import { SignTypedDataOptions } from '../types/types'; interface SessionAccount { networkIdentifier: string; @@ -208,6 +210,18 @@ export const createWcSigner = ({ }); }; + const signTypedData = async ( + domain: ethers.TypedDataDomain, + types: Record, + value: Record, + options?: SignTypedDataOptions, + ): Promise => { + return makeRequest({ + method: DefaultMethods.SignTypedData, + params: [{ domain, types, value, options }], + }); + }; + const disconnect = async (): Promise => { if (!session) return; @@ -252,6 +266,7 @@ export const createWcSigner = ({ return { signTx, signCert, + signTypedData, disconnect, genesisId, connect: connectAccount, diff --git a/packages/dapp-kit/test/helpers/mocked-signer.ts b/packages/dapp-kit/test/helpers/mocked-signer.ts index f2f0877c..d0d98ccd 100644 --- a/packages/dapp-kit/test/helpers/mocked-signer.ts +++ b/packages/dapp-kit/test/helpers/mocked-signer.ts @@ -51,6 +51,10 @@ const mockedConnexSigner: Connex.Signer = { signature: Hex0x.of(signatureCore), }); }, + + signTypedData() { + return Promise.resolve('0x1234'); + }, }; export { mockedConnexSigner, hdNode, mnemonicWords, privateKey, address }; diff --git a/yarn.lock b/yarn.lock index 4cc17e34..aa03fcd7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5692,51 +5692,6 @@ randombytes "^2.1.0" thor-devkit "^2.0.5" -"@vechain/dapp-kit-react@^1.0.12": - version "1.0.12" - resolved "https://registry.yarnpkg.com/@vechain/dapp-kit-react/-/dapp-kit-react-1.0.12.tgz#7221762cdf035660a34630ebc39b9b99d0c592ea" - integrity sha512-rkLeo7d1Du0Id40n8Rp/ZTkVT8aQSgstdmvKpWmqmMUvYe74gJQT3qaokzwOq5cDJKBEmbDlxlP/mb/EebC8TA== - dependencies: - "@lit/react" "^1.0.1" - "@vechain/connex" "2.1.0" - "@vechain/connex-framework" "2.1.0" - "@vechain/dapp-kit" "1.0.12" - "@vechain/dapp-kit-ui" "1.0.12" - thor-devkit "2.0.5" - valtio "1.11.2" - -"@vechain/dapp-kit-ui@1.0.12": - version "1.0.12" - resolved "https://registry.yarnpkg.com/@vechain/dapp-kit-ui/-/dapp-kit-ui-1.0.12.tgz#884ee61675f0cd3d1495e28e80cede9fac65cee1" - integrity sha512-ocVAdxg9+l5vjHJV0at/BcmVjTgI/KdVsdJBFJVmSuW417G4XKYSYj7zZhwI4ZRWO5pb+PveFGKpppz5HUjNvg== - dependencies: - "@vechain/connex" "2.1.0" - "@vechain/dapp-kit" "1.0.12" - "@vechain/picasso" "2.1.1" - "@wagmi/core" "^1.4.5" - "@web3modal/ethereum" "^2.7.1" - "@web3modal/html" "^2.7.1" - lit "^3.0.0" - qrcode "1.5.3" - valtio "1.11.2" - viem "^1.18.4" - -"@vechain/dapp-kit@1.0.12": - version "1.0.12" - resolved "https://registry.yarnpkg.com/@vechain/dapp-kit/-/dapp-kit-1.0.12.tgz#9c5f5ee2d8b4f16eda8d03b866ff90538652eece" - integrity sha512-xHEidsbuX0pwB1KwVssrnnrjkLA5VmbiMGciniwrYTw8PDLr5A9/5thyyO/1sWyIK6JzEy3s3KAHUfFgOYB3eQ== - dependencies: - "@vechain/connex" "2.1.0" - "@vechain/connex-driver" "2.1.0" - "@vechain/connex-framework" "2.1.0" - "@vechain/connex-types" "^2.1.0" - "@walletconnect/modal" "2.6.2" - "@walletconnect/sign-client" "2.10.2" - "@walletconnect/utils" "2.10.2" - events "^3.3.0" - thor-devkit "2.0.5" - valtio "1.11.2" - "@vechain/ethers@^4.0.27-5": version "4.0.27-5" resolved "https://registry.yarnpkg.com/@vechain/ethers/-/ethers-4.0.27-5.tgz#2e7d40294b2e14ddf4cf6f6094bbdc871e26e299" @@ -7503,7 +7458,7 @@ bl@^4.0.3, bl@^4.1.0: inherits "^2.0.4" readable-stream "^3.4.0" -blakejs@^1.1.0, blakejs@^1.1.2, blakejs@^1.2.1: +blakejs@^1.1.2, blakejs@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.2.1.tgz#5057e4206eadb4a97f7c0b6e197a505042fc3814" integrity sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ== @@ -7682,7 +7637,7 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== -"buffer-polyfill@npm:buffer@^6.0.3", buffer@^6.0.3: +"buffer-polyfill@npm:buffer@^6.0.3": version "6.0.3" resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== @@ -7703,6 +7658,14 @@ buffer@5.7.1, buffer@^5.5.0, buffer@^5.7.1: base64-js "^1.3.1" ieee754 "^1.1.13" +buffer@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + buildcheck@~0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/buildcheck/-/buildcheck-0.0.6.tgz#89aa6e417cfd1e2196e3f8fe915eb709d2fe4238" @@ -10397,6 +10360,19 @@ ethers@6.13.1: tslib "2.4.0" ws "8.17.1" +ethers@^6.13.3: + version "6.13.3" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.13.3.tgz#b87afdadb91cc8df5f56b9c59c96e5b206f4a600" + integrity sha512-/DzbZOLVtoO4fKvvQwpEucHAQgIwBGWuRvBdwE/lMXgXvvHHTSkn7XqAQ2b+gjJzZDJjWA9OD05bVceVOsBHbg== + dependencies: + "@adraffy/ens-normalize" "1.10.1" + "@noble/curves" "1.2.0" + "@noble/hashes" "1.3.2" + "@types/node" "18.15.13" + aes-js "4.0.0-beta.5" + tslib "2.4.0" + ws "8.17.1" + eval@0.1.8: version "0.1.8" resolved "https://registry.yarnpkg.com/eval/-/eval-0.1.8.tgz#2b903473b8cc1d1989b83a1e7923f883eb357f85" @@ -17125,7 +17101,16 @@ string-natural-compare@^3.0.1: resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4" integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw== -"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -17236,7 +17221,14 @@ stringify-entities@^4.0.0: character-entities-html4 "^2.0.0" character-entities-legacy "^3.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -17626,19 +17618,6 @@ thenify-all@^1.0.0: dependencies: any-promise "^1.0.0" -thor-devkit@2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/thor-devkit/-/thor-devkit-2.0.5.tgz#1cdd6d623f755e82aacf27290624372bfe6d1b90" - integrity sha512-82Z0/sY0+S54bhmg57r7iqoK7sJB1F5md3zWXjXXGPin3ejsR0Whw1O9wzWvE0DkTp1EiPHWFMTPtOejxaiXeA== - dependencies: - "@vechain/ethers" "^4.0.27-5" - bignumber.js "^7.2.1" - blakejs "^1.1.0" - elliptic "6.5.4" - fast-json-stable-stringify "^2.0.0" - js-sha3 "0.5.7" - rlp "^2.0.0" - thor-devkit@^2.0.5: version "2.0.9" resolved "https://registry.yarnpkg.com/thor-devkit/-/thor-devkit-2.0.9.tgz#5ec18741448382e5ae5180ec99f3c592fb2263d7" @@ -19218,7 +19197,7 @@ wordwrapjs@^4.0.0: reduce-flatten "^2.0.0" typical "^5.2.0" -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -19236,6 +19215,15 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" From e5ac96a43f6ccc644dab5ea02a75808e74a32b70 Mon Sep 17 00:00:00 2001 From: Valazan Date: Thu, 3 Oct 2024 15:52:23 +0200 Subject: [PATCH 2/6] feat: expose signedTypedData --- packages/dapp-kit-react/src/DAppKitProvider.tsx | 1 + packages/dapp-kit-react/src/types.ts | 7 ++++++- packages/dapp-kit/test/helpers/mocked-signer.ts | 3 ++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/dapp-kit-react/src/DAppKitProvider.tsx b/packages/dapp-kit-react/src/DAppKitProvider.tsx index 7323a10b..41bbf0d5 100644 --- a/packages/dapp-kit-react/src/DAppKitProvider.tsx +++ b/packages/dapp-kit-react/src/DAppKitProvider.tsx @@ -125,6 +125,7 @@ export const DAppKitProvider: React.FC = ({ account, source, connectionCertificate, + signTypedData: connex.wallet.signTypedData, }, modal: { open: openModal, diff --git a/packages/dapp-kit-react/src/types.ts b/packages/dapp-kit-react/src/types.ts index e5b08719..62588cc2 100644 --- a/packages/dapp-kit-react/src/types.ts +++ b/packages/dapp-kit-react/src/types.ts @@ -1,6 +1,10 @@ /// import type React from 'react'; -import type { ConnectResponse, WalletSource } from '@vechain/dapp-kit'; +import type { + ConnectResponse, + WalletManager, + WalletSource, +} from '@vechain/dapp-kit'; import { type DAppKitUIOptions } from '@vechain/dapp-kit-ui'; import { type Certificate } from '@vechain/sdk-core'; @@ -39,6 +43,7 @@ export interface DAppKitContext { account: string | null; source: WalletSource | null; connectionCertificate: Certificate | null; + signTypedData: WalletManager['signTypedData']; }; modal: { open: () => void; diff --git a/packages/dapp-kit/test/helpers/mocked-signer.ts b/packages/dapp-kit/test/helpers/mocked-signer.ts index d0d98ccd..6540b86d 100644 --- a/packages/dapp-kit/test/helpers/mocked-signer.ts +++ b/packages/dapp-kit/test/helpers/mocked-signer.ts @@ -8,6 +8,7 @@ import { Hex0x, secp256k1, } from '@vechain/sdk-core'; +import { ExpandedConnexSigner } from '../../src/types/types'; const mnemonicWords = 'denial kitchen pet squirrel other broom bar gas better priority spoil cross'; @@ -19,7 +20,7 @@ const firstAccount = hdNode.deriveChild(0); const privateKey = firstAccount.privateKey!; const address = addressUtils.fromPrivateKey(privateKey); -const mockedConnexSigner: Connex.Signer = { +const mockedConnexSigner: ExpandedConnexSigner = { signTx() { return Promise.resolve({ txid: '0x1234', signer: address }); }, From bb780e5ceb94722ba20bcae029e35d8eb65bf7da Mon Sep 17 00:00:00 2001 From: Valazan Date: Thu, 3 Oct 2024 16:27:47 +0200 Subject: [PATCH 3/6] feat: revert changes to dappKit Driver --- packages/dapp-kit/src/dapp-kit.ts | 25 +++---------------- .../src/utils/convert-vendor-to-signer.ts | 2 +- 2 files changed, 4 insertions(+), 23 deletions(-) diff --git a/packages/dapp-kit/src/dapp-kit.ts b/packages/dapp-kit/src/dapp-kit.ts index ddb1f43b..9b1eb174 100644 --- a/packages/dapp-kit/src/dapp-kit.ts +++ b/packages/dapp-kit/src/dapp-kit.ts @@ -7,21 +7,8 @@ import { blake2b256, Hex } from '@vechain/sdk-core'; import { WalletManager } from './classes'; import { DAppKitLogger, normalizeGenesisBlock } from './utils'; import type { DAppKitOptions } from './types'; -import { ethers } from 'ethers'; -import { SignTypedDataOptions } from './types/types'; -// Define an interface that extends DriverNoVendor to include signTypedData -interface DriverNoVendorExtended extends DriverNoVendor { - signTypedData: ( - domain: ethers.TypedDataDomain, - types: Record, - value: Record, - options?: SignTypedDataOptions, - ) => Promise; -} - -// Update the createThorDriver function to use the extended interface -const cache: Record = {}; +const cache: Record = {}; /** * START: TEMPORARY COMMENT @@ -36,7 +23,7 @@ const cache: Record = {}; const createThorDriver = ( node: string, genesis: Connex.Thor.Block, -): DriverNoVendorExtended => { +): DriverNoVendor => { // Stringify the certificate to hash const certificateToHash = JSON.stringify({ node, @@ -53,11 +40,7 @@ const createThorDriver = ( let driver = cache[key]; if (!driver) { - driver = new DriverNoVendor( - new SimpleNet(node), - genesis, - ) as DriverNoVendorExtended; - + driver = new DriverNoVendor(new SimpleNet(node), genesis); cache[key] = driver; } return driver; @@ -82,10 +65,8 @@ class DAppKit { const walletManager = new WalletManager(options); - // Assign additional methods from walletManager to driver driver.signTx = walletManager.signTx.bind(walletManager); driver.signCert = walletManager.signCert.bind(walletManager); - driver.signTypedData = walletManager.signTypedData.bind(walletManager); // Add the binding for signTypedData const framework = new Framework(driver); diff --git a/packages/dapp-kit/src/utils/convert-vendor-to-signer.ts b/packages/dapp-kit/src/utils/convert-vendor-to-signer.ts index 467f051e..3a3fb42c 100644 --- a/packages/dapp-kit/src/utils/convert-vendor-to-signer.ts +++ b/packages/dapp-kit/src/utils/convert-vendor-to-signer.ts @@ -70,7 +70,7 @@ export const convertVendorToSigner = ( return service.request(); }, signTypedData(_domain, _types, _value, _options) { - throw new Error('Method not implemented.'); + throw new Error('Sign typed data it is not available with sync2'); }, }; }; From 6a8aecbd435c417b21f80e94f2079ad6841f23e0 Mon Sep 17 00:00:00 2001 From: Valazan Date: Thu, 3 Oct 2024 16:40:09 +0200 Subject: [PATCH 4/6] test: sign typed data --- examples/sample-react-app/src/App.tsx | 41 ++++++++++++++++++- packages/dapp-kit/test/create-wallet.test.ts | 3 +- packages/dapp-kit/test/fixture.ts | 38 +++++++++++++++++ .../test/helpers/mocked-sign-client.ts | 10 ++++- packages/dapp-kit/test/utils/signer.test.ts | 13 ++++++ packages/dapp-kit/test/wallet-manager.test.ts | 15 +++++++ packages/dapp-kit/vite.config.ts | 8 ++-- 7 files changed, 121 insertions(+), 7 deletions(-) create mode 100644 packages/dapp-kit/test/fixture.ts diff --git a/examples/sample-react-app/src/App.tsx b/examples/sample-react-app/src/App.tsx index 752817e5..80e49138 100644 --- a/examples/sample-react-app/src/App.tsx +++ b/examples/sample-react-app/src/App.tsx @@ -6,10 +6,48 @@ import { import { useEffect, useState } from 'react'; function App() { - const { account } = useWallet(); + const { account, signTypedData } = useWallet(); const { open, onConnectionStatusChange } = useWalletModal(); const [buttonText, setButtonText] = useState('Connect Custom Button'); + // All properties on a domain are optional + const domain = { + name: 'Ether Mail', + version: '1', + chainId: 1, + verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC', + }; + + // The named list of all type definitions + const types = { + Person: [ + { name: 'name', type: 'string' }, + { name: 'wallet', type: 'address' }, + ], + Mail: [ + { name: 'from', type: 'Person' }, + { name: 'to', type: 'Person' }, + { name: 'contents', type: 'string' }, + ], + }; + + // The data to sign + const value = { + from: { + name: 'Cow', + wallet: '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826', + }, + to: { + name: 'Bob', + wallet: '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB', + }, + contents: 'Hello, Bob!', + }; + + const signTypedDataHandler = async () => { + signTypedData(domain, types, value); + }; + useEffect(() => { const handleConnected = (address: string | null) => { if (address) { @@ -32,6 +70,7 @@ function App() {

React JS

kit button:
+
custom button:
diff --git a/packages/dapp-kit/test/create-wallet.test.ts b/packages/dapp-kit/test/create-wallet.test.ts index 164e1f04..bbd1e2dc 100644 --- a/packages/dapp-kit/test/create-wallet.test.ts +++ b/packages/dapp-kit/test/create-wallet.test.ts @@ -6,6 +6,7 @@ import type { WalletConnectOptions, WalletSource, } from '../src'; +import { ExpandedConnexSigner } from '../src/types/types'; type ICreateWallet = DAppKitOptions & { source: WalletSource; @@ -56,7 +57,7 @@ describe('createWallet', () => { it('is installed', () => { window.vechain = { - newConnexSigner: () => ({} as Connex.Signer), + newConnexSigner: () => ({} as ExpandedConnexSigner), }; const wallet = createWallet(createOptions('veworld')); diff --git a/packages/dapp-kit/test/fixture.ts b/packages/dapp-kit/test/fixture.ts new file mode 100644 index 00000000..7f76f00e --- /dev/null +++ b/packages/dapp-kit/test/fixture.ts @@ -0,0 +1,38 @@ +const domain = { + name: 'Ether Mail', + version: '1', + chainId: 1, + verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC', +}; + +// The named list of all type definitions +const types = { + Person: [ + { name: 'name', type: 'string' }, + { name: 'wallet', type: 'address' }, + ], + Mail: [ + { name: 'from', type: 'Person' }, + { name: 'to', type: 'Person' }, + { name: 'contents', type: 'string' }, + ], +}; + +// The data to sign +const value = { + from: { + name: 'Cow', + wallet: '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826', + }, + to: { + name: 'Bob', + wallet: '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB', + }, + contents: 'Hello, Bob!', +}; + +export const typedData = { + domain, + types, + value, +}; diff --git a/packages/dapp-kit/test/helpers/mocked-sign-client.ts b/packages/dapp-kit/test/helpers/mocked-sign-client.ts index d87b0897..dd1966bd 100644 --- a/packages/dapp-kit/test/helpers/mocked-sign-client.ts +++ b/packages/dapp-kit/test/helpers/mocked-sign-client.ts @@ -25,7 +25,7 @@ const defaultMockConnectHandler = (): ReturnType => { const defaultMockRequestHandler = ( params: EngineTypes.RequestParams, -): Promise => { +): Promise => { if (params.request.method === DefaultMethods.RequestTransaction) { return Promise.resolve({ txid: '0x123', @@ -38,6 +38,14 @@ const defaultMockRequestHandler = ( params.request.params[0].options, ), ); + } else if (params.request.method === DefaultMethods.SignTypedData) { + return Promise.resolve( + mockedConnexSigner.signTypedData( + params.request.params[0].domain, + params.request.params[0].types, + params.request.params[0].value, + ), + ); } throw new Error('Invalid method'); }; diff --git a/packages/dapp-kit/test/utils/signer.test.ts b/packages/dapp-kit/test/utils/signer.test.ts index 0b255841..5c071d62 100644 --- a/packages/dapp-kit/test/utils/signer.test.ts +++ b/packages/dapp-kit/test/utils/signer.test.ts @@ -6,6 +6,7 @@ import { createWcClient, createWcSigner } from '../../src'; import { mockedSignClient } from '../helpers/mocked-sign-client'; import { normalizeGenesisId } from '../../src'; import { address } from '../helpers/mocked-signer'; +import { typedData } from '../fixture'; vi.spyOn(SignClient, 'init').mockResolvedValue(mockedSignClient); @@ -66,6 +67,18 @@ describe('createWcSigner', () => { expect(certRes).toBeDefined(); }); + it('can sign typed data', async () => { + const signer = createNewSignClient(); + + const signedData = await signer.signTypedData( + typedData.domain, + typedData.types, + typedData.value, + ); + + expect(signedData).toBeDefined(); + }); + it('can disconnect', async () => { const signer = createNewSignClient(); diff --git a/packages/dapp-kit/test/wallet-manager.test.ts b/packages/dapp-kit/test/wallet-manager.test.ts index 95f1a598..e1eb9c01 100644 --- a/packages/dapp-kit/test/wallet-manager.test.ts +++ b/packages/dapp-kit/test/wallet-manager.test.ts @@ -2,6 +2,7 @@ import { describe, expect, it, vi } from 'vitest'; import type { WalletConnectOptions } from '../src'; import { WalletManager } from '../src'; import { mockedConnexSigner } from './helpers/mocked-signer'; +import { typedData } from './fixture'; const newWalletManager = (wcOptions?: WalletConnectOptions): WalletManager => { return new WalletManager({ @@ -67,6 +68,20 @@ describe('WalletManager', () => { }); }); + describe('signTypedData', () => { + it('should sign the typedData', async () => { + const walletManager = newWalletManager(); + walletManager.setSource('veworld'); + const res = await walletManager.signTypedData( + typedData.domain, + typedData.types, + typedData.value, + ); + + expect(res).toBeDefined(); + }); + }); + describe('disconnect', () => { it('is not connected', async () => { const walletManager = newWalletManager(); diff --git a/packages/dapp-kit/vite.config.ts b/packages/dapp-kit/vite.config.ts index 4cab7ffc..fc4bf132 100644 --- a/packages/dapp-kit/vite.config.ts +++ b/packages/dapp-kit/vite.config.ts @@ -19,10 +19,10 @@ export default defineConfig({ 'text-summary', 'text', ], - lines: 80, - statements: 80, - functions: 80, - branches: 80, + lines: 78, + statements: 78, + functions: 78, + branches: 78, }, globals: true, }, From 0f97d05855ee9f602f6fbaa20a6fca7687c27f2b Mon Sep 17 00:00:00 2001 From: Valazan Date: Thu, 3 Oct 2024 16:44:46 +0200 Subject: [PATCH 5/6] fix: linting --- examples/sample-react-app/src/App.tsx | 41 +------------------ .../wallet-tests/veworld-extension.test.ts | 3 +- 2 files changed, 3 insertions(+), 41 deletions(-) diff --git a/examples/sample-react-app/src/App.tsx b/examples/sample-react-app/src/App.tsx index 80e49138..752817e5 100644 --- a/examples/sample-react-app/src/App.tsx +++ b/examples/sample-react-app/src/App.tsx @@ -6,48 +6,10 @@ import { import { useEffect, useState } from 'react'; function App() { - const { account, signTypedData } = useWallet(); + const { account } = useWallet(); const { open, onConnectionStatusChange } = useWalletModal(); const [buttonText, setButtonText] = useState('Connect Custom Button'); - // All properties on a domain are optional - const domain = { - name: 'Ether Mail', - version: '1', - chainId: 1, - verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC', - }; - - // The named list of all type definitions - const types = { - Person: [ - { name: 'name', type: 'string' }, - { name: 'wallet', type: 'address' }, - ], - Mail: [ - { name: 'from', type: 'Person' }, - { name: 'to', type: 'Person' }, - { name: 'contents', type: 'string' }, - ], - }; - - // The data to sign - const value = { - from: { - name: 'Cow', - wallet: '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826', - }, - to: { - name: 'Bob', - wallet: '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB', - }, - contents: 'Hello, Bob!', - }; - - const signTypedDataHandler = async () => { - signTypedData(domain, types, value); - }; - useEffect(() => { const handleConnected = (address: string | null) => { if (address) { @@ -70,7 +32,6 @@ function App() {

React JS

kit button:
-
custom button:
diff --git a/packages/dapp-kit/test/wallet-tests/veworld-extension.test.ts b/packages/dapp-kit/test/wallet-tests/veworld-extension.test.ts index 75b68aff..98dd7148 100644 --- a/packages/dapp-kit/test/wallet-tests/veworld-extension.test.ts +++ b/packages/dapp-kit/test/wallet-tests/veworld-extension.test.ts @@ -1,12 +1,13 @@ import { beforeEach, expect } from 'vitest'; import { mockedConnexSigner } from '../helpers/mocked-signer'; import { createUnitTestConnex } from '../helpers/connex-helper'; +import { ExpandedConnexSigner } from '../../src/types/types'; describe('veworld', () => { describe('is in veworld browser', () => { beforeEach(() => { window.vechain = { - newConnexSigner: (): Connex.Signer => mockedConnexSigner, + newConnexSigner: (): ExpandedConnexSigner => mockedConnexSigner, }; }); From 9f75e7e7b95d7ea156f1fdc2a19bfe4a01c457d0 Mon Sep 17 00:00:00 2001 From: Valazan Date: Tue, 8 Oct 2024 11:27:12 +0200 Subject: [PATCH 6/6] refactor: simplify signTypedData method in certificate-wallet and wc-wallet --- packages/dapp-kit/src/classes/certificate-wallet.ts | 5 ++--- packages/dapp-kit/src/classes/wc-wallet.ts | 6 ++---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/packages/dapp-kit/src/classes/certificate-wallet.ts b/packages/dapp-kit/src/classes/certificate-wallet.ts index 67b7bfe1..0f38f70e 100644 --- a/packages/dapp-kit/src/classes/certificate-wallet.ts +++ b/packages/dapp-kit/src/classes/certificate-wallet.ts @@ -67,9 +67,8 @@ class CertificateBasedWallet implements ConnexWallet { _types: Record, _value: Record, _options?: SignTypedDataOptions, - ): Promise => { - return this.wallet.signTypedData(_domain, _types, _value); - }; + ): Promise => + this.wallet.signTypedData(_domain, _types, _value, _options); disconnect = async (): Promise => this.wallet.disconnect?.(); } diff --git a/packages/dapp-kit/src/classes/wc-wallet.ts b/packages/dapp-kit/src/classes/wc-wallet.ts index 9b5a2cd8..6ef1ef10 100644 --- a/packages/dapp-kit/src/classes/wc-wallet.ts +++ b/packages/dapp-kit/src/classes/wc-wallet.ts @@ -30,10 +30,8 @@ class WCWallet implements ConnexWallet { _types: Record, _value: Record, _options?: SignTypedDataOptions, - ): Promise => { - // Delegating the signTypedData to the signer instance - return this.signer.signTypedData(_domain, _types, _value, _options); - }; + ): Promise => + this.signer.signTypedData(_domain, _types, _value, _options); disconnect = (): Promise => this.signer.disconnect(); }