From 0f66b10949eef207d1ac8977da6bb25809cb9762 Mon Sep 17 00:00:00 2001 From: Archit Date: Fri, 20 Sep 2024 19:19:09 +0530 Subject: [PATCH 1/3] allow wallet connect to connect with solana chains --- demo/vue-app-new/package-lock.json | 53 ++++++++++- demo/vue-app-new/package.json | 2 + .../src/components/AppDashboard.vue | 4 +- .../src/components/AppSettings.vue | 1 + demo/vue-app-new/src/config.ts | 11 ++- demo/vue-app-new/src/services/solHandlers.ts | 5 +- package-lock.json | 7 +- .../wallet-connect-v2-adapter/package.json | 7 +- .../src/WalletConnectV2Provider.ts | 49 +++++++--- .../wallet-connect-v2-adapter/src/config.ts | 18 +++- .../src/walletConnectV2Utils.ts | 95 ++++++++++++++++--- .../src/walletConnectV2adapter.ts | 73 ++++++++++++-- .../wallet-connect-v2-adapter/torus.config.js | 4 +- .../providers/solana-provider/src/index.ts | 1 + .../base/baseInjectedProvider.ts | 3 +- .../base/providerHandlers.ts | 2 +- .../torus/providerHandlers.ts | 2 +- .../walletStandardProvider.ts | 2 +- .../solanaPrivateKeyProvider.ts | 10 +- .../solanaPrivateKeyUtils.ts | 2 +- .../solana-provider/src/rpc/index.ts | 3 + .../solana-provider/src/rpc/interfaces.ts | 33 +++++++ .../src/rpc/solanaRpcMiddlewares.ts | 29 +----- 23 files changed, 327 insertions(+), 89 deletions(-) create mode 100644 packages/providers/solana-provider/src/rpc/index.ts create mode 100644 packages/providers/solana-provider/src/rpc/interfaces.ts diff --git a/demo/vue-app-new/package-lock.json b/demo/vue-app-new/package-lock.json index 89b11039f..cbbff4ef1 100644 --- a/demo/vue-app-new/package-lock.json +++ b/demo/vue-app-new/package-lock.json @@ -32,6 +32,7 @@ "@web3auth/torus-solana-adapter": "file:../../packages/adapters/torus-solana-adapter", "@web3auth/wallet-connect-v2-adapter": "file:../../packages/adapters/wallet-connect-v2-adapter", "@web3auth/wallet-services-plugin": "file:../../packages/plugins/wallet-services-plugin", + "bs58": "^5.0.0", "ethers": "^6.13.2", "vue": "^3.4.31", "vue-i18n": "^9.13.1", @@ -39,6 +40,7 @@ }, "devDependencies": { "@toruslabs/eslint-config-vue": "^3.3.1", + "@types/bs58": "^4.0.4", "@vitejs/plugin-vue": "^5.0.5", "autoprefixer": "^10.4.19", "eslint": "^8.54.0", @@ -209,16 +211,21 @@ "version": "9.0.2", "license": "ISC", "dependencies": { + "@toruslabs/base-controllers": "^6.1.2", "@walletconnect/sign-client": "^2.16.1", "@walletconnect/types": "^2.16.1", "@walletconnect/utils": "^2.16.1", "@web3auth/auth": "^9.3.3", "@web3auth/base": "^9.0.2", - "@web3auth/base-evm-adapter": "^9.0.2", "@web3auth/base-provider": "^9.0.2", "@web3auth/ethereum-provider": "^9.0.2", + "@web3auth/solana-provider": "^9.0.2", + "bs58": "^5.0.0", "deepmerge": "^4.3.1" }, + "devDependencies": { + "@types/bs58": "^4.0.4" + }, "engines": { "node": ">=18.x", "npm": ">=9.x" @@ -1685,6 +1692,15 @@ "superstruct": "^2.0.2" } }, + "node_modules/@solana/web3.js/node_modules/bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", + "license": "MIT", + "dependencies": { + "base-x": "^3.0.2" + } + }, "node_modules/@swc/helpers": { "version": "0.5.13", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.13.tgz", @@ -1977,6 +1993,17 @@ "vue": "^3.x" } }, + "node_modules/@types/bs58": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/bs58/-/bs58-4.0.4.tgz", + "integrity": "sha512-0IEpMFXXQi2zXaXl9GJ3sRwQo0uEkD+yFOv+FnAU5lkPtcu6h61xb7jc2CFPEZ5BUOaiP13ThuGc9HD4R8lR5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "base-x": "^3.0.6" + } + }, "node_modules/@types/connect": { "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", @@ -3349,6 +3376,15 @@ "text-encoding-utf-8": "^1.0.2" } }, + "node_modules/borsh/node_modules/bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", + "license": "MIT", + "dependencies": { + "base-x": "^3.0.2" + } + }, "node_modules/bowser": { "version": "2.11.0", "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", @@ -3412,13 +3448,20 @@ } }, "node_modules/bs58": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", - "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-5.0.0.tgz", + "integrity": "sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==", + "license": "MIT", "dependencies": { - "base-x": "^3.0.2" + "base-x": "^4.0.0" } }, + "node_modules/bs58/node_modules/base-x": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-4.0.0.tgz", + "integrity": "sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw==", + "license": "MIT" + }, "node_modules/buffer": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", diff --git a/demo/vue-app-new/package.json b/demo/vue-app-new/package.json index 84f660e30..3ca152ddd 100644 --- a/demo/vue-app-new/package.json +++ b/demo/vue-app-new/package.json @@ -34,6 +34,7 @@ "@web3auth/torus-solana-adapter": "file:../../packages/adapters/torus-solana-adapter", "@web3auth/wallet-connect-v2-adapter": "file:../../packages/adapters/wallet-connect-v2-adapter", "@web3auth/wallet-services-plugin": "file:../../packages/plugins/wallet-services-plugin", + "bs58": "^5.0.0", "ethers": "^6.13.2", "vue": "^3.4.31", "vue-i18n": "^9.13.1", @@ -41,6 +42,7 @@ }, "devDependencies": { "@toruslabs/eslint-config-vue": "^3.3.1", + "@types/bs58": "^4.0.4", "@vitejs/plugin-vue": "^5.0.5", "autoprefixer": "^10.4.19", "eslint": "^8.54.0", diff --git a/demo/vue-app-new/src/components/AppDashboard.vue b/demo/vue-app-new/src/components/AppDashboard.vue index 9cd45266d..4099ad102 100644 --- a/demo/vue-app-new/src/components/AppDashboard.vue +++ b/demo/vue-app-new/src/components/AppDashboard.vue @@ -8,8 +8,8 @@ import { recoverAddress, TypedDataEncoder, verifyMessage } from "ethers"; import { useI18n } from "vue-i18n"; import { getV4TypedData } from "../config"; -import { getAccounts, getBalance, getChainId, sendEth, signEthMessage, signTransaction } from "../services/ethHandlers"; -import { signAllTransactions, signAndSendTransaction, signMessage } from "../services/solHandlers"; +import { getAccounts, getBalance, getChainId, sendEth, signEthMessage } from "../services/ethHandlers"; +import { signAllTransactions, signAndSendTransaction, signMessage, signTransaction } from "../services/solHandlers"; import { formDataStore } from "../store/form"; const { t } = useI18n({ useScope: "global" }); diff --git a/demo/vue-app-new/src/components/AppSettings.vue b/demo/vue-app-new/src/components/AppSettings.vue index c42a0fd7d..7d535527f 100644 --- a/demo/vue-app-new/src/components/AppSettings.vue +++ b/demo/vue-app-new/src/components/AppSettings.vue @@ -29,6 +29,7 @@ const adapterOptions = computed(() => ] : [ { name: "torus-solana-adapter", value: "torus-solana" }, + { name: "wallet-connect-v2-adapter", value: "wallet-connect-v2" }, { name: "injected-adapters", value: "injected-solana" }, ] ); diff --git a/demo/vue-app-new/src/config.ts b/demo/vue-app-new/src/config.ts index 5141866a0..d1199e5a4 100644 --- a/demo/vue-app-new/src/config.ts +++ b/demo/vue-app-new/src/config.ts @@ -48,7 +48,16 @@ export const chainConfigs: Record = { logo: "https://cryptologos.cc/logos/solana-sol-logo.png", chainId: "0x3", ticker: "SOL", - tickerName: "Solana", + tickerName: "Solana Devnet", + }, + { + chainNamespace: CHAIN_NAMESPACES.SOLANA, + rpcTarget: "https://api.devnet.solana.com", + blockExplorerUrl: "https://explorer.solana.com", + logo: "https://cryptologos.cc/logos/solana-sol-logo.png", + chainId: "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp", + ticker: "SOL", + tickerName: "Solana Mainnet", }, ], [CHAIN_NAMESPACES.CASPER]: [], diff --git a/demo/vue-app-new/src/services/solHandlers.ts b/demo/vue-app-new/src/services/solHandlers.ts index 8aeee8db6..fc76d7a01 100644 --- a/demo/vue-app-new/src/services/solHandlers.ts +++ b/demo/vue-app-new/src/services/solHandlers.ts @@ -2,6 +2,7 @@ import { Connection, LAMPORTS_PER_SOL, PublicKey, SystemProgram, Transaction } from "@solana/web3.js"; import { CustomChainConfig, IProvider, log } from "@web3auth/base"; import { SolanaWallet } from "@web3auth/solana-provider"; +import base58 from "bs58"; const getConnection = async (provider: IProvider): Promise => { const solanaWallet = new SolanaWallet(provider); @@ -77,6 +78,7 @@ export const signTransaction = async (provider: IProvider, uiConsole: any) => { const conn = await getConnection(provider); const solWeb3 = new SolanaWallet(provider); const pubKey = await solWeb3.requestAccounts(); + log.info("pubKey", pubKey); const block = await conn.getLatestBlockhash("finalized"); const transactionInstruction = SystemProgram.transfer({ @@ -108,7 +110,8 @@ export const signMessage = async (provider: IProvider, uiConsole: any) => { const solWeb3 = new SolanaWallet(provider); const msg = Buffer.from("Test Signing Message ", "utf8"); const res = await solWeb3.signMessage(new Uint8Array(msg)); - uiConsole(res); + const parsedResult = base58.encode(res); + uiConsole(parsedResult); } catch (error) { log.error("Error", error); uiConsole("error", error); diff --git a/package-lock.json b/package-lock.json index b989a9589..768dd814f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26423,16 +26423,21 @@ "version": "9.0.2", "license": "ISC", "dependencies": { + "@toruslabs/base-controllers": "^6.1.2", "@walletconnect/sign-client": "^2.16.1", "@walletconnect/types": "^2.16.1", "@walletconnect/utils": "^2.16.1", "@web3auth/auth": "^9.3.3", "@web3auth/base": "^9.0.2", - "@web3auth/base-evm-adapter": "^9.0.2", "@web3auth/base-provider": "^9.0.2", "@web3auth/ethereum-provider": "^9.0.2", + "@web3auth/solana-provider": "^9.0.2", + "bs58": "^5.0.0", "deepmerge": "^4.3.1" }, + "devDependencies": { + "@types/bs58": "^4.0.4" + }, "engines": { "node": ">=18.x", "npm": ">=9.x" diff --git a/packages/adapters/wallet-connect-v2-adapter/package.json b/packages/adapters/wallet-connect-v2-adapter/package.json index 071d344a2..b9b6c0682 100644 --- a/packages/adapters/wallet-connect-v2-adapter/package.json +++ b/packages/adapters/wallet-connect-v2-adapter/package.json @@ -35,16 +35,21 @@ "@babel/runtime": "^7.x" }, "dependencies": { + "@toruslabs/base-controllers": "^6.1.2", "@walletconnect/sign-client": "^2.16.1", "@walletconnect/types": "^2.16.1", "@walletconnect/utils": "^2.16.1", "@web3auth/auth": "^9.3.3", "@web3auth/base": "^9.0.2", - "@web3auth/base-evm-adapter": "^9.0.2", "@web3auth/base-provider": "^9.0.2", "@web3auth/ethereum-provider": "^9.0.2", + "@web3auth/solana-provider": "^9.0.2", + "bs58": "^5.0.0", "deepmerge": "^4.3.1" }, + "devDependencies": { + "@types/bs58": "^4.0.4" + }, "lint-staged": { "!(*d).ts": [ "eslint --cache --fix", diff --git a/packages/adapters/wallet-connect-v2-adapter/src/WalletConnectV2Provider.ts b/packages/adapters/wallet-connect-v2-adapter/src/WalletConnectV2Provider.ts index 15631f9a7..1745bcdfe 100644 --- a/packages/adapters/wallet-connect-v2-adapter/src/WalletConnectV2Provider.ts +++ b/packages/adapters/wallet-connect-v2-adapter/src/WalletConnectV2Provider.ts @@ -1,17 +1,18 @@ import type { ISignClient, SignClientTypes } from "@walletconnect/types"; import { getAccountsFromNamespaces, parseAccountId } from "@walletconnect/utils"; import { JRPCEngine, JRPCMiddleware, providerErrors, providerFromEngine } from "@web3auth/auth"; -import { CHAIN_NAMESPACES, CustomChainConfig, getChainConfig, log, WalletInitializationError, WalletLoginError } from "@web3auth/base"; +import { CHAIN_NAMESPACES, CustomChainConfig, getChainConfig, log, WalletLoginError } from "@web3auth/base"; import { BaseProvider, BaseProviderConfig, BaseProviderState } from "@web3auth/base-provider"; import { AddEthereumChainParameter, createChainSwitchMiddleware, createEthMiddleware, - createJsonRpcClient, - IChainSwitchHandlers, + createJsonRpcClient as createEthJsonRpcClient, + IChainSwitchHandlers as IEthChainSwitchHandlers, } from "@web3auth/ethereum-provider"; +import { createJsonRpcClient as createSolJsonRpcClient, createSolanaMiddleware } from "@web3auth/solana-provider"; -import { addChain, getAccounts, getProviderHandlers, switchChain } from "./walletConnectV2Utils"; +import { addChain, getAccounts, getEthProviderHandlers, getSolProviderHandlers, switchChain } from "./walletConnectV2Utils"; export interface WalletConnectV2ProviderConfig extends BaseProviderConfig { chainConfig: CustomChainConfig; @@ -22,8 +23,6 @@ export interface WalletConnectV2ProviderState extends BaseProviderState { } export class WalletConnectV2Provider extends BaseProvider { - readonly PROVIDER_CHAIN_NAMESPACE = CHAIN_NAMESPACES.EIP155; - private connector: ISignClient | null = null; constructor({ config, state, connector }: { config: WalletConnectV2ProviderConfig; state?: BaseProviderState; connector?: ISignClient }) { @@ -52,8 +51,6 @@ export class WalletConnectV2Provider extends BaseProvider { - const { chainNamespace } = this.config.chainConfig; - if (chainNamespace !== this.PROVIDER_CHAIN_NAMESPACE) throw WalletInitializationError.incompatibleChainNameSpace("Invalid chain namespace"); this.onConnectorStateUpdate(connector); await this.setupEngine(connector); } @@ -107,18 +104,27 @@ export class WalletConnectV2Provider extends BaseProvider { + if (this.config.chainConfig.chainNamespace === CHAIN_NAMESPACES.EIP155) { + return this.setupEthEngine(connector); + } else if (this.config.chainConfig.chainNamespace === CHAIN_NAMESPACES.SOLANA) { + return this.setupSolEngine(connector); + } + throw new Error(`Unsupported chainNamespace: ${this.config.chainConfig.chainNamespace}`); + } + + private async setupEthEngine(connector: ISignClient): Promise { const { chainId } = this.config.chainConfig; const numChainId = parseInt(chainId, 16); - const providerHandlers = getProviderHandlers({ connector, chainId: numChainId }); + const providerHandlers = getEthProviderHandlers({ connector, chainId: numChainId }); const jrpcRes = await getAccounts(connector); this.update({ accounts: jrpcRes || [], }); const ethMiddleware = createEthMiddleware(providerHandlers); - const chainSwitchMiddleware = this.getChainSwitchMiddleware(); + const chainSwitchMiddleware = this.getEthChainSwitchMiddleware(); const engine = new JRPCEngine(); - const { networkMiddleware } = createJsonRpcClient(this.config.chainConfig as CustomChainConfig); + const { networkMiddleware } = createEthJsonRpcClient(this.config.chainConfig as CustomChainConfig); engine.push(ethMiddleware); engine.push(chainSwitchMiddleware); engine.push(networkMiddleware); @@ -126,8 +132,25 @@ export class WalletConnectV2Provider extends BaseProvider { - const chainSwitchHandlers: IChainSwitchHandlers = { + private async setupSolEngine(connector: ISignClient): Promise { + const { chainId } = this.config.chainConfig; + const providerHandlers = getSolProviderHandlers({ connector, chainId }); + const jrpcRes = await getAccounts(connector); + + this.update({ + accounts: jrpcRes || [], + }); + const solMiddleware = createSolanaMiddleware(providerHandlers); + const engine = new JRPCEngine(); + const { networkMiddleware } = createSolJsonRpcClient(this.config.chainConfig as CustomChainConfig); + engine.push(solMiddleware); + engine.push(networkMiddleware); + const provider = providerFromEngine(engine); + this.updateProviderEngineProxy(provider); + } + + private getEthChainSwitchMiddleware(): JRPCMiddleware { + const chainSwitchHandlers: IEthChainSwitchHandlers = { addChain: async (params: AddEthereumChainParameter): Promise => { const { chainId, chainName, rpcUrls, blockExplorerUrls, nativeCurrency, iconUrls } = params; this.addChain({ diff --git a/packages/adapters/wallet-connect-v2-adapter/src/config.ts b/packages/adapters/wallet-connect-v2-adapter/src/config.ts index 080e6c344..e316f215f 100644 --- a/packages/adapters/wallet-connect-v2-adapter/src/config.ts +++ b/packages/adapters/wallet-connect-v2-adapter/src/config.ts @@ -15,11 +15,21 @@ export enum DEFAULT_EIP155_METHODS { SWITCH_ETHEREUM_CHAIN = "wallet_switchEthereumChain", } +export enum DEFAULT_SOLANA_METHODS { + SIGN_TRANSACTION = "solana_signTransaction", + SIGN_MESSAGE = "solana_signMessage", +} + export enum DEFAULT_EIP_155_EVENTS { ETH_CHAIN_CHANGED = "chainChanged", ETH_ACCOUNTS_CHANGED = "accountsChanged", } +export enum DEFAULT_SOLANA_EVENTS { + ETH_CHAIN_CHANGED = "chainChanged", + ETH_ACCOUNTS_CHANGED = "accountsChanged", +} + /** * Extracts a name for the site from the DOM */ @@ -107,6 +117,8 @@ export const getSupportedMethodsByNamespace = (namespace: string) => { switch (namespace) { case CHAIN_NAMESPACES.EIP155: return Object.values(DEFAULT_EIP155_METHODS); + case CHAIN_NAMESPACES.SOLANA: + return Object.values(DEFAULT_SOLANA_METHODS); default: throw new Error(`No default methods for namespace: ${namespace}`); } @@ -116,6 +128,8 @@ export const getSupportedEventsByNamespace = (namespace: string) => { switch (namespace) { case CHAIN_NAMESPACES.EIP155: return Object.values(DEFAULT_EIP_155_EVENTS); + case CHAIN_NAMESPACES.SOLANA: + return Object.values(DEFAULT_SOLANA_EVENTS); default: throw new Error(`No default events for namespace: ${namespace}`); } @@ -143,7 +157,7 @@ export const getWalletConnectV2Settings = async ( adapterSettings: IAdapterSettings; loginSettings: EngineTypes.ConnectParams; }> => { - if (namespace === CHAIN_NAMESPACES.EIP155) { + if (namespace === CHAIN_NAMESPACES.EIP155 || namespace === CHAIN_NAMESPACES.SOLANA) { const appMetadata = await getSiteMetadata(); const adapterSettings: IAdapterSettings = { walletConnectInitOptions: { @@ -159,7 +173,7 @@ export const getWalletConnectV2Settings = async ( }; const chainNamespaces = chainIds.map((chainId) => { - return `${namespace}:${parseInt(chainId, 16)}`; + return `${namespace}:${namespace === CHAIN_NAMESPACES.EIP155 ? parseInt(chainId, 16) : chainId}`; }); const loginSettings: EngineTypes.ConnectParams = { diff --git a/packages/adapters/wallet-connect-v2-adapter/src/walletConnectV2Utils.ts b/packages/adapters/wallet-connect-v2-adapter/src/walletConnectV2Utils.ts index f627a32a8..fbdd5ecfe 100644 --- a/packages/adapters/wallet-connect-v2-adapter/src/walletConnectV2Utils.ts +++ b/packages/adapters/wallet-connect-v2-adapter/src/walletConnectV2Utils.ts @@ -2,7 +2,15 @@ import type { ISignClient, SessionTypes } from "@walletconnect/types"; import { getAccountsFromNamespaces, parseAccountId } from "@walletconnect/utils"; import { type JRPCRequest, providerErrors, rpcErrors } from "@web3auth/auth"; import { WalletLoginError } from "@web3auth/base"; -import type { AddEthereumChainParameter, IProviderHandlers, MessageParams, TransactionParams, TypedMessageParams } from "@web3auth/ethereum-provider"; +import type { + AddEthereumChainParameter, + IProviderHandlers as EthProviderHandlers, + MessageParams, + TransactionParams, + TypedMessageParams, +} from "@web3auth/ethereum-provider"; +import type { IProviderHandlers as SolProviderHandlers, TransactionOrVersionedTransaction } from "@web3auth/solana-provider"; +import base58 from "bs58"; async function getLastActiveSession(signClient: ISignClient): Promise { if (signClient.session.length) { @@ -16,7 +24,11 @@ function isMobileDevice() { return /Mobi|Android|iPhone|iPad|iPod|Opera Mini|IEMobile|WPDesktop/i.test(window.navigator.userAgent); } -export async function sendJrpcRequest(signClient: ISignClient, chainId: number, method: string, params: U): Promise { +function isSolanaChain(chainId: string) { + return chainId.startsWith("solana:"); +} + +export async function sendJrpcRequest(signClient: ISignClient, chainId: string, method: string, params: U): Promise { const session = await getLastActiveSession(signClient); if (!session) { throw providerErrors.disconnected(); @@ -30,10 +42,15 @@ export async function sendJrpcRequest(signClient: ISignClient, chainId: nu return signClient.request({ topic: session.topic, - chainId: `eip155:${chainId}`, + chainId, request: { method, - params, + params: isSolanaChain(chainId) + ? { + ...params, + pubkey: session.self.publicKey, + } + : params, }, }); } @@ -56,7 +73,7 @@ export async function getAccounts(signClient: ISignClient): Promise { throw WalletLoginError.connectionError("Failed to get accounts"); } -export function getProviderHandlers({ connector, chainId }: { connector: ISignClient; chainId: number }): IProviderHandlers { +export function getEthProviderHandlers({ connector, chainId }: { connector: ISignClient; chainId: number }): EthProviderHandlers { return { getPrivateKey: async () => { throw rpcErrors.methodNotSupported(); @@ -68,28 +85,82 @@ export function getProviderHandlers({ connector, chainId }: { connector: ISignCl return getAccounts(connector); }, processTransaction: async (txParams: TransactionParams, _: JRPCRequest): Promise => { - const methodRes = await sendJrpcRequest(connector, chainId, "eth_sendTransaction", [txParams]); + const methodRes = await sendJrpcRequest(connector, `eip155:${chainId}`, "eth_sendTransaction", [txParams]); return methodRes; }, processSignTransaction: async (txParams: TransactionParams, _: JRPCRequest): Promise => { - const methodRes = await sendJrpcRequest(connector, chainId, "eth_signTransaction", [txParams]); + const methodRes = await sendJrpcRequest(connector, `eip155:${chainId}`, "eth_signTransaction", [txParams]); return methodRes; }, processEthSignMessage: async (msgParams: MessageParams, _: JRPCRequest): Promise => { - const methodRes = await sendJrpcRequest(connector, chainId, "eth_sign", [msgParams.from, msgParams.data]); + const methodRes = await sendJrpcRequest(connector, `eip155:${chainId}`, "eth_sign", [msgParams.from, msgParams.data]); return methodRes; }, processPersonalMessage: async (msgParams: MessageParams, _: JRPCRequest): Promise => { - const methodRes = await sendJrpcRequest(connector, chainId, "personal_sign", [msgParams.data, msgParams.from]); + const methodRes = await sendJrpcRequest(connector, `eip155:${chainId}`, "personal_sign", [msgParams.data, msgParams.from]); return methodRes; }, processTypedMessageV4: async (msgParams: TypedMessageParams): Promise => { - const methodRes = await sendJrpcRequest(connector, chainId, "eth_signTypedData_v4", [msgParams.from, msgParams.data]); + const methodRes = await sendJrpcRequest(connector, `eip155:${chainId}`, "eth_signTypedData_v4", [ + msgParams.from, + msgParams.data, + ]); return methodRes; }, }; } +export function getSolProviderHandlers({ connector, chainId }: { connector: ISignClient; chainId: string }): SolProviderHandlers { + return { + requestAccounts: async (_: JRPCRequest) => { + return getAccounts(connector); + }, + getPrivateKey: async () => { + throw rpcErrors.methodNotSupported(); + }, + getSecretKey: async () => { + throw rpcErrors.methodNotSupported(); + }, + getPublicKey: async () => { + throw rpcErrors.methodNotSupported(); + }, + getAccounts: async (_: JRPCRequest) => { + return getAccounts(connector); + }, + signAllTransactions: async (_: JRPCRequest) => { + throw rpcErrors.methodNotSupported(); + }, + signAndSendTransaction: async (_: JRPCRequest) => { + throw rpcErrors.methodNotSupported(); + }, + signMessage: async (req: JRPCRequest<{ message: Uint8Array }>): Promise => { + // eslint-disable-next-line no-console + console.log("signMessage", req); + const methodRes = await sendJrpcRequest<{ signature: string }, { message: string }>(connector, `solana:${chainId}`, "solana_signMessage", { + message: base58.encode(req.params.message), + }); + // eslint-disable-next-line no-console + console.log("signMessage res", methodRes); + return base58.decode(methodRes.signature); + }, + signTransaction: async (req: JRPCRequest<{ message: TransactionOrVersionedTransaction }>): Promise => { + // eslint-disable-next-line no-console + console.log("signTransaction", req); + const methodRes = await sendJrpcRequest<{ signature: string }, { transaction: string }>( + connector, + `solana:${chainId}`, + "solana_signTransaction", + { transaction: req.params.message.serialize({ requireAllSignatures: false }).toString("base64") } + ); + // TODO: add signatures here. + const finalTransaction = req.params.message; + // eslint-disable-next-line no-console + console.log("signTransaction", methodRes); + return finalTransaction; + }, + }; +} + export async function switchChain({ connector, chainId, @@ -99,7 +170,7 @@ export async function switchChain({ chainId: number; newChainId: string; }): Promise { - await sendJrpcRequest(connector, chainId, "wallet_switchEthereumChain", [{ chainId: newChainId }]); + await sendJrpcRequest(connector, `eip155:${chainId}`, "wallet_switchEthereumChain", [{ chainId: newChainId }]); } export async function addChain({ @@ -111,5 +182,5 @@ export async function addChain({ chainId: number; chainConfig: AddEthereumChainParameter; }): Promise { - await sendJrpcRequest(connector, chainId, "wallet_addEthereumChain", [chainConfig]); + await sendJrpcRequest(connector, `eip155:${chainId}`, "wallet_addEthereumChain", [chainConfig]); } diff --git a/packages/adapters/wallet-connect-v2-adapter/src/walletConnectV2adapter.ts b/packages/adapters/wallet-connect-v2-adapter/src/walletConnectV2adapter.ts index f35f82bff..2bc8306fc 100644 --- a/packages/adapters/wallet-connect-v2-adapter/src/walletConnectV2adapter.ts +++ b/packages/adapters/wallet-connect-v2-adapter/src/walletConnectV2adapter.ts @@ -1,3 +1,4 @@ +import { signChallenge, verifySignedChallenge } from "@toruslabs/base-controllers"; import Client from "@walletconnect/sign-client"; import { SessionTypes } from "@walletconnect/types"; import { getSdkError, isValidArray } from "@walletconnect/utils"; @@ -10,12 +11,17 @@ import { ADAPTER_STATUS_TYPE, AdapterInitOptions, AdapterNamespaceType, + BaseAdapter, CHAIN_NAMESPACES, ChainNamespaceType, + checkIfTokenIsExpired, CONNECTED_EVENT_DATA, CustomChainConfig, + getSavedToken, IProvider, log, + saveToken, + UserAuthInfo, UserInfo, WALLET_ADAPTERS, WalletConnectV2Data, @@ -23,19 +29,18 @@ import { WalletLoginError, Web3AuthError, } from "@web3auth/base"; -import { BaseEvmAdapter } from "@web3auth/base-evm-adapter"; import deepmerge from "deepmerge"; import { getWalletConnectV2Settings } from "./config"; import { WalletConnectV2AdapterOptions } from "./interface"; import { WalletConnectV2Provider } from "./WalletConnectV2Provider"; -class WalletConnectV2Adapter extends BaseEvmAdapter { +class WalletConnectV2Adapter extends BaseAdapter { readonly name: string = WALLET_ADAPTERS.WALLET_CONNECT_V2; - readonly adapterNamespace: AdapterNamespaceType = ADAPTER_NAMESPACES.EIP155; + readonly adapterNamespace: AdapterNamespaceType = ADAPTER_NAMESPACES.MULTICHAIN; - readonly currentChainNamespace: ChainNamespaceType = CHAIN_NAMESPACES.EIP155; + readonly currentChainNamespace: ChainNamespaceType = CHAIN_NAMESPACES.OTHER; readonly type: ADAPTER_CATEGORY_TYPE = ADAPTER_CATEGORY.EXTERNAL; @@ -75,7 +80,6 @@ class WalletConnectV2Adapter extends BaseEvmAdapter { } async init(options: AdapterInitOptions): Promise { - await super.init(); super.checkInitializationRequirements(); const projectId = this.adapterOptions.adapterSettings?.walletConnectInitOptions?.projectId; if (!projectId) { @@ -87,12 +91,15 @@ class WalletConnectV2Adapter extends BaseEvmAdapter { [this.chainConfig?.chainId as string], projectId ); + // eslint-disable-next-line no-console + console.log("wc settings", wc2Settings); if (!this.adapterOptions.loginSettings || Object.keys(this.adapterOptions.loginSettings).length === 0) { this.adapterOptions.loginSettings = wc2Settings.loginSettings; } this.adapterOptions.adapterSettings = deepmerge(wc2Settings.adapterSettings || {}, this.adapterOptions.adapterSettings || {}); - + // eslint-disable-next-line no-console + console.log("wc settings", this.adapterOptions.loginSettings); const { adapterSettings } = this.adapterOptions; this.connector = await Client.init(adapterSettings?.walletConnectInitOptions); this.wcProvider = new WalletConnectV2Provider({ @@ -190,7 +197,6 @@ class WalletConnectV2Adapter extends BaseEvmAdapter { } async disconnect(options: { cleanup: boolean } = { cleanup: false }): Promise { - await super.disconnectSession(); const { cleanup } = options; if (!this.connector || !this.connected || !this.activeSession?.topic) throw WalletLoginError.notConnectedError("Not connected with wallet"); await this.connector.disconnect({ topic: this.activeSession?.topic, reason: getSdkError("USER_DISCONNECTED") }); @@ -204,7 +210,57 @@ class WalletConnectV2Adapter extends BaseEvmAdapter { this.status = ADAPTER_STATUS.READY; } this.activeSession = null; - await super.disconnect(); + this.emit(ADAPTER_EVENTS.DISCONNECTED); + } + + async authenticateUser(): Promise { + if (!this.provider || this.status !== ADAPTER_STATUS.CONNECTED) throw WalletLoginError.notConnectedError(); + const { chainNamespace, chainId } = this.chainConfig; + const accounts = await this.provider.request({ + method: "eth_accounts", + }); + if (accounts && accounts.length > 0) { + const existingToken = getSavedToken(accounts[0] as string, this.name); + if (existingToken) { + const isExpired = checkIfTokenIsExpired(existingToken); + if (!isExpired) { + return { idToken: existingToken }; + } + } + + const payload = { + domain: window.location.origin, + uri: window.location.href, + address: accounts[0], + chainId: parseInt(chainId, 16), + version: "1", + nonce: Math.random().toString(36).slice(2), + issuedAt: new Date().toISOString(), + }; + + const challenge = await signChallenge(payload, chainNamespace); + const hexChallenge = `0x${Buffer.from(challenge, "utf8").toString("hex")}`; + + const signedMessage = await this.provider.request<[string, string], string>({ + method: "personal_sign", + params: [hexChallenge, accounts[0]], + }); + + const idToken = await verifySignedChallenge( + chainNamespace, + signedMessage as string, + challenge, + this.name, + this.sessionTime, + this.clientId, + this.web3AuthNetwork + ); + saveToken(accounts[0] as string, this.name, idToken); + return { + idToken, + }; + } + throw WalletLoginError.notConnectedError("Not connected with wallet, Please login/connect first"); } public async enableMFA(): Promise { @@ -246,7 +302,6 @@ class WalletConnectV2Adapter extends BaseEvmAdapter { await this.connector.disconnect({ topic: this.activeSession?.topic, reason: getSdkError("USER_DISCONNECTED") }); } - log.debug("creating new session for web3auth wallet connect"); const { uri, approval } = await this.connector.connect(this.adapterOptions.loginSettings); const qrcodeModal = this.adapterOptions?.adapterSettings?.qrcodeModal; diff --git a/packages/adapters/wallet-connect-v2-adapter/torus.config.js b/packages/adapters/wallet-connect-v2-adapter/torus.config.js index 6df6a4932..440a224ca 100644 --- a/packages/adapters/wallet-connect-v2-adapter/torus.config.js +++ b/packages/adapters/wallet-connect-v2-adapter/torus.config.js @@ -1 +1,3 @@ -module.exports = require("../../../torus.config"); +module.exports = { + esm: true, +} diff --git a/packages/providers/solana-provider/src/index.ts b/packages/providers/solana-provider/src/index.ts index eb3d71de4..d1e438257 100644 --- a/packages/providers/solana-provider/src/index.ts +++ b/packages/providers/solana-provider/src/index.ts @@ -1,3 +1,4 @@ export * from "./interface"; export * from "./providers"; +export * from "./rpc"; export * from "./solanaWallet"; diff --git a/packages/providers/solana-provider/src/providers/injectedProviders/base/baseInjectedProvider.ts b/packages/providers/solana-provider/src/providers/injectedProviders/base/baseInjectedProvider.ts index 720ce94f1..0ab112f96 100644 --- a/packages/providers/solana-provider/src/providers/injectedProviders/base/baseInjectedProvider.ts +++ b/packages/providers/solana-provider/src/providers/injectedProviders/base/baseInjectedProvider.ts @@ -2,8 +2,9 @@ import { JRPCEngine, JRPCMiddleware, providerFromEngine } from "@web3auth/auth"; import { CustomChainConfig, WalletLoginError } from "@web3auth/base"; import { BaseProvider, BaseProviderConfig, BaseProviderState } from "@web3auth/base-provider"; +import { IProviderHandlers } from "../../../rpc"; import { createConfigMiddleware } from "../../../rpc/JrpcClient"; -import { createSolanaMiddleware, IProviderHandlers } from "../../../rpc/solanaRpcMiddlewares"; +import { createSolanaMiddleware } from "../../../rpc/solanaRpcMiddlewares"; export abstract class BaseInjectedProvider

extends BaseProvider { constructor({ config, state }: { config: BaseProviderConfig; state?: BaseProviderState }) { diff --git a/packages/providers/solana-provider/src/providers/injectedProviders/base/providerHandlers.ts b/packages/providers/solana-provider/src/providers/injectedProviders/base/providerHandlers.ts index 73b3ffc9e..e250a52fe 100644 --- a/packages/providers/solana-provider/src/providers/injectedProviders/base/providerHandlers.ts +++ b/packages/providers/solana-provider/src/providers/injectedProviders/base/providerHandlers.ts @@ -2,7 +2,7 @@ import { JRPCRequest, rpcErrors } from "@web3auth/auth"; import bs58 from "bs58"; import { IBaseWalletProvider, TransactionOrVersionedTransaction } from "../../../interface"; -import { IProviderHandlers } from "../../../rpc/solanaRpcMiddlewares"; +import { IProviderHandlers } from "../../../rpc"; export const getBaseProviderHandlers = (injectedProvider: IBaseWalletProvider): IProviderHandlers => { const providerHandlers: IProviderHandlers = { diff --git a/packages/providers/solana-provider/src/providers/injectedProviders/torus/providerHandlers.ts b/packages/providers/solana-provider/src/providers/injectedProviders/torus/providerHandlers.ts index cd0edf24f..c9efa87da 100644 --- a/packages/providers/solana-provider/src/providers/injectedProviders/torus/providerHandlers.ts +++ b/packages/providers/solana-provider/src/providers/injectedProviders/torus/providerHandlers.ts @@ -1,7 +1,7 @@ import { JRPCRequest, rpcErrors } from "@web3auth/auth"; import { ITorusWalletProvider, TransactionOrVersionedTransaction } from "../../../interface"; -import { IProviderHandlers } from "../../../rpc/solanaRpcMiddlewares"; +import { IProviderHandlers } from "../../../rpc"; export const getTorusHandlers = (injectedProvider: ITorusWalletProvider): IProviderHandlers => { const providerHandlers: IProviderHandlers = { diff --git a/packages/providers/solana-provider/src/providers/injectedProviders/walletStandardProvider.ts b/packages/providers/solana-provider/src/providers/injectedProviders/walletStandardProvider.ts index ba46fa454..c24870c64 100644 --- a/packages/providers/solana-provider/src/providers/injectedProviders/walletStandardProvider.ts +++ b/packages/providers/solana-provider/src/providers/injectedProviders/walletStandardProvider.ts @@ -1,5 +1,5 @@ import { IWalletStandardProviderHandler } from "../../interface"; -import { IProviderHandlers } from "../../rpc/solanaRpcMiddlewares"; +import { IProviderHandlers } from "../../rpc"; import { BaseInjectedProvider } from "./base/baseInjectedProvider"; import { getBaseProviderHandlers } from "./base/providerHandlers"; diff --git a/packages/providers/solana-provider/src/providers/privateKeyProvider/solanaPrivateKeyProvider.ts b/packages/providers/solana-provider/src/providers/privateKeyProvider/solanaPrivateKeyProvider.ts index 773075fa0..09671649e 100644 --- a/packages/providers/solana-provider/src/providers/privateKeyProvider/solanaPrivateKeyProvider.ts +++ b/packages/providers/solana-provider/src/providers/privateKeyProvider/solanaPrivateKeyProvider.ts @@ -2,15 +2,9 @@ import { getED25519Key, JRPCEngine, JRPCMiddleware, JRPCRequest, providerErrors, import { CHAIN_NAMESPACES, CustomChainConfig, WalletInitializationError } from "@web3auth/base"; import { BaseProvider, BaseProviderConfig, BaseProviderState } from "@web3auth/base-provider"; +import { AddSolanaChainParameter, IChainSwitchHandlers } from "../../rpc"; import { createJsonRpcClient } from "../../rpc/JrpcClient"; -import { - AddSolanaChainParameter, - createAccountMiddleware, - createChainSwitchMiddleware, - createSolanaMiddleware, - IAccountHandlers, - IChainSwitchHandlers, -} from "../../rpc/solanaRpcMiddlewares"; +import { createAccountMiddleware, createChainSwitchMiddleware, createSolanaMiddleware, IAccountHandlers } from "../../rpc/solanaRpcMiddlewares"; import { getProviderHandlers } from "./solanaPrivateKeyUtils"; export interface SolanaPrivKeyProviderConfig extends BaseProviderConfig { diff --git a/packages/providers/solana-provider/src/providers/privateKeyProvider/solanaPrivateKeyUtils.ts b/packages/providers/solana-provider/src/providers/privateKeyProvider/solanaPrivateKeyUtils.ts index ae507854f..9cff0a049 100644 --- a/packages/providers/solana-provider/src/providers/privateKeyProvider/solanaPrivateKeyUtils.ts +++ b/packages/providers/solana-provider/src/providers/privateKeyProvider/solanaPrivateKeyUtils.ts @@ -5,7 +5,7 @@ import { SafeEventEmitterProvider, WalletInitializationError } from "@web3auth/b import bs58 from "bs58"; import { TransactionOrVersionedTransaction } from "../../interface"; -import { IProviderHandlers } from "../../rpc/solanaRpcMiddlewares"; +import { IProviderHandlers } from "../../rpc"; export async function getProviderHandlers({ privKey, diff --git a/packages/providers/solana-provider/src/rpc/index.ts b/packages/providers/solana-provider/src/rpc/index.ts new file mode 100644 index 000000000..532554f34 --- /dev/null +++ b/packages/providers/solana-provider/src/rpc/index.ts @@ -0,0 +1,3 @@ +export * from "./interfaces"; +export * from "./JrpcClient"; +export * from "./solanaRpcMiddlewares"; diff --git a/packages/providers/solana-provider/src/rpc/interfaces.ts b/packages/providers/solana-provider/src/rpc/interfaces.ts new file mode 100644 index 000000000..100af4806 --- /dev/null +++ b/packages/providers/solana-provider/src/rpc/interfaces.ts @@ -0,0 +1,33 @@ +import { JRPCRequest } from "@web3auth/auth"; + +import { TransactionOrVersionedTransaction } from "../interface"; + +export interface AddSolanaChainParameter { + chainId: string; // A 0x-prefixed hexadecimal string + chainName: string; + nativeCurrency: { + name: string; + symbol: string; // 2-6 characters long + decimals: 18; + }; + rpcUrls: string[]; + blockExplorerUrls?: string[]; + iconUrls?: string[]; +} + +export interface IChainSwitchHandlers { + addNewChainConfig: (req: JRPCRequest) => Promise; + switchSolanaChain: (req: JRPCRequest<{ chainId: string }>) => Promise; +} + +export interface IProviderHandlers { + requestAccounts: (req: JRPCRequest) => Promise; + getAccounts: (req: JRPCRequest) => Promise; + getPublicKey: (req: JRPCRequest) => Promise; + getPrivateKey: (req: JRPCRequest) => Promise; + signTransaction: (req: JRPCRequest<{ message: TransactionOrVersionedTransaction }>) => Promise; + signAllTransactions: (req: JRPCRequest<{ message: TransactionOrVersionedTransaction[] }>) => Promise; + signAndSendTransaction: (req: JRPCRequest<{ message: TransactionOrVersionedTransaction }>) => Promise<{ signature: string }>; + getSecretKey: (req: JRPCRequest) => Promise; + signMessage: (req: JRPCRequest<{ message: Uint8Array; display?: string }>) => Promise; +} diff --git a/packages/providers/solana-provider/src/rpc/solanaRpcMiddlewares.ts b/packages/providers/solana-provider/src/rpc/solanaRpcMiddlewares.ts index b920667ad..2a8e9e0b5 100644 --- a/packages/providers/solana-provider/src/rpc/solanaRpcMiddlewares.ts +++ b/packages/providers/solana-provider/src/rpc/solanaRpcMiddlewares.ts @@ -1,18 +1,7 @@ import { createAsyncMiddleware, JRPCMiddleware, JRPCRequest, mergeMiddleware } from "@web3auth/auth"; import { TransactionOrVersionedTransaction } from "../interface"; - -export interface IProviderHandlers { - requestAccounts: (req: JRPCRequest) => Promise; - getAccounts: (req: JRPCRequest) => Promise; - getPublicKey: (req: JRPCRequest) => Promise; - getPrivateKey: (req: JRPCRequest) => Promise; - signTransaction: (req: JRPCRequest<{ message: TransactionOrVersionedTransaction }>) => Promise; - signAllTransactions: (req: JRPCRequest<{ message: TransactionOrVersionedTransaction[] }>) => Promise; - signAndSendTransaction: (req: JRPCRequest<{ message: TransactionOrVersionedTransaction }>) => Promise<{ signature: string }>; - getSecretKey: (req: JRPCRequest) => Promise; - signMessage: (req: JRPCRequest<{ message: Uint8Array; display?: string }>) => Promise; -} +import { AddSolanaChainParameter, IChainSwitchHandlers, IProviderHandlers } from "./interfaces"; export function createGetAccountsMiddleware({ getAccounts }: { getAccounts: IProviderHandlers["getAccounts"] }): JRPCMiddleware { return createAsyncMiddleware(async (request, response, next) => { @@ -110,23 +99,7 @@ export function createSolanaMiddleware(providerHandlers: IProviderHandlers): JRP createGenericJRPCMiddleware("solanaSecretKey", getSecretKey) as JRPCMiddleware, ]); } -export interface AddSolanaChainParameter { - chainId: string; // A 0x-prefixed hexadecimal string - chainName: string; - nativeCurrency: { - name: string; - symbol: string; // 2-6 characters long - decimals: 18; - }; - rpcUrls: string[]; - blockExplorerUrls?: string[]; - iconUrls?: string[]; -} -export interface IChainSwitchHandlers { - addNewChainConfig: (req: JRPCRequest) => Promise; - switchSolanaChain: (req: JRPCRequest<{ chainId: string }>) => Promise; -} export function createChainSwitchMiddleware({ addNewChainConfig, switchSolanaChain }: IChainSwitchHandlers): JRPCMiddleware { return mergeMiddleware([ createGenericJRPCMiddleware("addSolanaChain", addNewChainConfig) as JRPCMiddleware, From bef2be3945474de8af1fc58abede5895d1037c08 Mon Sep 17 00:00:00 2001 From: Archit Date: Mon, 23 Sep 2024 19:30:13 +0530 Subject: [PATCH 2/3] fixes sol signature + get id token + fixes disconnect session --- .../src/components/AppDashboard.vue | 25 +++++++++++----- demo/vue-app-new/src/services/solHandlers.ts | 5 ++-- package-lock.json | 1 + .../wallet-connect-v2-adapter/package.json | 1 + .../src/walletConnectV2Utils.ts | 14 ++++----- .../src/walletConnectV2adapter.ts | 29 ++++++++++++------- 6 files changed, 45 insertions(+), 30 deletions(-) diff --git a/demo/vue-app-new/src/components/AppDashboard.vue b/demo/vue-app-new/src/components/AppDashboard.vue index 4099ad102..ba1b242e3 100644 --- a/demo/vue-app-new/src/components/AppDashboard.vue +++ b/demo/vue-app-new/src/components/AppDashboard.vue @@ -1,7 +1,7 @@