Преглед на файлове

Updating `@solana/wallet-adapter-walletconnect` to the latest changes

- A bunch of types got changed; updated all the `@walletconnect` dependencies
- Copied a majority of the flow from https://github.com/WalletConnect/web-examples

Not allowing parameter overrides, since the wallet-adapter needs to handle that behavior anyway

v0.0.0-alpha.0

v0.0.0-alpha.1

v0.0.0-alpha.2

Using published package

v0.0.0-alpha.3

Moving files around

Fixing more packages

Add the last active pairing to make life easier for wallets

Adding more color on pairing

Remove automatic pairing
jon wong преди 3 години
родител
ревизия
e973b51f4c

+ 32 - 10
packages/starter/example/components/ContextProvider.tsx

@@ -6,7 +6,14 @@ import { WalletAdapterNetwork } from '@solana/wallet-adapter-base';
 import { WalletDialogProvider as MaterialUIWalletDialogProvider } from '@solana/wallet-adapter-material-ui';
 import { ConnectionProvider, WalletProvider } from '@solana/wallet-adapter-react';
 import { WalletModalProvider as ReactUIWalletModalProvider } from '@solana/wallet-adapter-react-ui';
-import { FakeWalletAdapter } from '@solana/wallet-adapter-wallets';
+import { WalletConnectWalletAdapter } from '@solana/wallet-adapter-walletconnect';
+import {
+    GlowWalletAdapter,
+    PhantomWalletAdapter,
+    SlopeWalletAdapter,
+    SolflareWalletAdapter,
+    TorusWalletAdapter,
+} from '@solana/wallet-adapter-wallets';
 import { clusterApiUrl } from '@solana/web3.js';
 import { SnackbarProvider, useSnackbar } from 'notistack';
 import type { FC, ReactNode } from 'react';
@@ -59,15 +66,30 @@ const WalletContextProvider: FC<{ children: ReactNode }> = ({ children }) => {
 
     const wallets = useMemo(
         () => [
-            /**
-             * Select the wallets you wish to support, by instantiating wallet adapters here.
-             *
-             * Common adapters can be found in the npm package `@solana/wallet-adapter-wallets`.
-             * That package supports tree shaking and lazy loading -- only the wallets you import
-             * will be compiled into your application, and only the dependencies of wallets that
-             * your users connect to will be loaded.
-             */
-            new FakeWalletAdapter(),
+            new SolanaMobileWalletAdapter({
+                appIdentity: { name: 'Solana Wallet Adapter Example App' },
+                authorizationResultCache: createDefaultAuthorizationResultCache(),
+            }),
+            new PhantomWalletAdapter(),
+            new GlowWalletAdapter(),
+            new SlopeWalletAdapter(),
+            new SolflareWalletAdapter({ network }),
+            new TorusWalletAdapter(),
+
+            new WalletConnectWalletAdapter({
+                network,
+                options: {
+                    relayUrl: 'wss://relay.walletconnect.com',
+                    // example WC dapp project ID
+                    projectId: 'e899c82be21d4acca2c8aec45e893598',
+                    metadata: {
+                        name: 'Example Dapp',
+                        description: 'Example Dapp',
+                        url: 'https://github.com/solana-labs/wallet-adapter',
+                        icons: ['https://avatars.githubusercontent.com/u/35608259?s=200'],
+                    },
+                },
+            }),
         ],
         []
     );

+ 3 - 2
packages/starter/example/package.json

@@ -28,8 +28,9 @@
     },
     "dependencies": {
         "@ant-design/icons": "^4.0.0",
-        "@emotion/react": "^11.10.0",
-        "@emotion/styled": "^11.10.0",
+        "@emotion/react": "^11.9.3",
+        "@emotion/styled": "^11.9.3",
+        "@solana/wallet-adapter-walletconnect": "0.1.0",
         "@mui/icons-material": "^5.8.4",
         "@mui/material": "^5.9.3",
         "@solana/wallet-adapter-ant-design": "^0.11.9",

+ 0 - 0
wip/walletconnect/.prettierignore → packages/wallets/walletconnect/.prettierignore


+ 0 - 0
wip/walletconnect/LICENSE → packages/wallets/walletconnect/LICENSE


+ 0 - 0
wip/walletconnect/README.md → packages/wallets/walletconnect/README.md


+ 7 - 5
wip/walletconnect/package.json → packages/wallets/walletconnect/package.json

@@ -32,15 +32,17 @@
         "@solana/web3.js": "^1.50.1"
     },
     "dependencies": {
-        "@solana/wallet-adapter-base": "^0.9.10",
-        "@walletconnect/client": "^2.0.0-beta.26",
-        "@walletconnect/qrcode-modal": "2.0.0-alpha.20",
-        "@walletconnect/utils": "^2.0.0-beta.26",
+        "@solana/wallet-adapter-base": "^0.9.7",
+        "@walletconnect/qrcode-modal": "^2.0.0-alpha.20",
+        "@walletconnect/sign-client": "2.0.0-beta.102-6a30ee7.0",
+        "@walletconnect/utils": "^2.0.0-beta.102",
+        "better-sqlite3": "^7.6.2",
         "solana-wallet": "^1.0.1"
     },
     "devDependencies": {
         "@solana/web3.js": "^1.50.1",
         "@types/pino": "^6.3.11",
-        "@walletconnect/types": "^2.0.0-beta.26"
+        "@walletconnect/types": "2.0.0-beta.102-6a30ee7.0",
+        "shx": "^0.3.4"
     }
 }

+ 71 - 83
wip/walletconnect/src/adapter.ts → packages/wallets/walletconnect/src/adapter.ts

@@ -2,6 +2,7 @@ import type { WalletName } from '@solana/wallet-adapter-base';
 import {
     BaseSignerWalletAdapter,
     WalletAccountError,
+    WalletAdapterNetwork,
     WalletConnectionError,
     WalletDisconnectedError,
     WalletDisconnectionError,
@@ -14,14 +15,12 @@ import {
     WalletSignTransactionError,
     WalletWindowClosedError,
 } from '@solana/wallet-adapter-base';
-import type { Transaction } from '@solana/web3.js';
-import { PublicKey } from '@solana/web3.js';
-import WalletConnectClient, { CLIENT_EVENTS } from '@walletconnect/client';
+import { PublicKey, Transaction } from '@solana/web3.js';
+import WalletConnectClient, { SignClient } from '@walletconnect/sign-client';
 import QRCodeModal from '@walletconnect/qrcode-modal';
-import type { ClientOptions, ClientTypes, PairingTypes, SessionTypes } from '@walletconnect/types';
-import { ERROR } from '@walletconnect/utils';
+import { EngineTypes, SessionTypes, SignClientTypes } from '@walletconnect/types';
+import { getSdkError, parseAccountId } from '@walletconnect/utils';
 import base58 from 'bs58';
-import { serialiseTransaction } from 'solana-wallet';
 
 export enum WalletConnectChainID {
     Mainnet = 'solana:4sGjMW1sUnHzSxGspuhpqLDx6wiyjNtZ',
@@ -33,9 +32,30 @@ export enum WalletConnectRPCMethod {
     signMessage = 'solana_signMessage',
 }
 
+const getWalletConnectParams = (chainId: WalletConnectChainID, pairingTopic?: string): EngineTypes.ConnectParams => ({
+    requiredNamespaces: {
+        solana: {
+            chains: [chainId],
+            methods: [WalletConnectRPCMethod.signTransaction, WalletConnectRPCMethod.signMessage],
+            events: [],
+        },
+    },
+    pairingTopic,
+});
+
+const getChainId = (network: WalletAdapterNetwork): WalletConnectChainID => {
+    switch (network) {
+        case WalletAdapterNetwork.Mainnet:
+            return WalletConnectChainID.Mainnet;
+        case WalletAdapterNetwork.Devnet:
+        default:
+            return WalletConnectChainID.Devnet;
+    }
+};
+
 export interface WalletConnectWalletAdapterConfig {
-    options: ClientOptions;
-    params?: ClientTypes.ConnectParams;
+    network: WalletAdapterNetwork;
+    options: SignClientTypes.Options;
 }
 
 export const WalletConnectWalletName = 'WalletConnect' as WalletName;
@@ -48,25 +68,20 @@ export class WalletConnectWalletAdapter extends BaseSignerWalletAdapter {
 
     private _publicKey: PublicKey | null;
     private _connecting: boolean;
-    private _options: ClientOptions;
-    private _params: ClientTypes.ConnectParams;
+    private _options: SignClientTypes.Options;
     private _client: WalletConnectClient | undefined;
-    private _session: SessionTypes.Created | undefined;
+    private _session: SessionTypes.Struct | undefined;
     private _readyState: WalletReadyState =
         typeof window === 'undefined' ? WalletReadyState.Unsupported : WalletReadyState.Loadable;
+    private _network: WalletAdapterNetwork;
 
     constructor(config: WalletConnectWalletAdapterConfig) {
         super();
 
         this._publicKey = null;
         this._connecting = false;
+        this._network = config.network || WalletAdapterNetwork.Devnet;
         this._options = config.options;
-        this._params = config.params || {
-            permissions: {
-                blockchain: { chains: [WalletConnectChainID.Mainnet, WalletConnectChainID.Devnet] },
-                jsonrpc: { methods: [WalletConnectRPCMethod.signTransaction, WalletConnectRPCMethod.signMessage] },
-            },
-        };
     }
 
     get publicKey(): PublicKey | null {
@@ -89,74 +104,48 @@ export class WalletConnectWalletAdapter extends BaseSignerWalletAdapter {
             this._connecting = true;
 
             let client: WalletConnectClient;
-            let session: SessionTypes.Settled;
+            let session: SessionTypes.Struct;
             try {
                 client = await WalletConnectClient.init(this._options);
 
-                // eslint-disable-next-line no-async-promise-executor
-                session = await new Promise<SessionTypes.Settled>(async (resolve, reject) => {
-                    if (client.session.topics.length) {
-                        const _session = await client.session.get(client.session.topics[0]);
-                        if (_session) {
-                            resolve(_session);
-                            return;
-                        }
-                    }
-
-                    async function onPairingProposal(proposal: PairingTypes.Proposal) {
-                        const { uri } = proposal.signal.params;
-                        QRCodeModal.open(uri, () => {
-                            cleanup();
-                            reject(new WalletWindowClosedError());
-                        });
-                    }
-
-                    async function onSessionProposal(proposal: SessionTypes.Proposal) {
-                        console.log('onSessionProposal', proposal);
-                    }
-
-                    async function onSessionUpdated(updated: SessionTypes.Settled) {
-                        console.log('onSessionUpdated', updated);
-                        resolve(updated);
-                    }
-
-                    function cleanup() {
-                        client.off(CLIENT_EVENTS.pairing.proposal, onPairingProposal);
-                        client.off(CLIENT_EVENTS.session.proposal, onSessionProposal);
-                        client.off(CLIENT_EVENTS.session.updated, onSessionUpdated);
-                    }
-
-                    try {
-                        client.on(CLIENT_EVENTS.pairing.proposal, onPairingProposal);
-                        client.on(CLIENT_EVENTS.session.proposal, onSessionProposal);
-                        client.on(CLIENT_EVENTS.session.updated, onSessionUpdated);
-
-                        const s = await client.connect(this._params);
-                        resolve(s);
-                    } catch (error: any) {
-                        cleanup();
-                        reject(error);
-                    }
-                });
+                const pairings = client.pairing.getAll({ active: true });
+                // Prototypically, the user should be prompted to either:
+                // - Connect to a previously active pairing
+                // - Choose a new pairing
+                // There doesn't seem to be a WalletConnect-provided UI for this like there exists for the QRCode modal, though,
+                // and pushing this into user-land would be way too much
+                // If we decide to try and pair automatically, the UI will hang waiting for a pairing that might not complete
+                // const lastActivePairing = pairings.length ? pairings[pairings.length - 1].topic : undefined;
+                const lastActivePairing = undefined;
+
+                const { uri, approval } = await client.connect(
+                    getWalletConnectParams(getChainId(this._network), lastActivePairing)
+                );
+
+                if (uri) {
+                    QRCodeModal.open(uri, () => {
+                        throw new WalletWindowClosedError();
+                    });
+                }
+
+                session = await approval();
+
             } catch (error: any) {
                 if (error instanceof WalletError) throw error;
                 throw new WalletConnectionError(error?.message, error);
             }
 
-            if (!session.state.accounts.length) throw new WalletAccountError();
-
-            const match = session.state.accounts[0].match(/:([0-9a-zA-Z]+)$/);
-            if (!match) throw new WalletAccountError();
-            const account = match[1];
+            if (!session.namespaces.solana.accounts.length) throw new WalletAccountError();
 
             let publicKey: PublicKey;
             try {
-                publicKey = new PublicKey(account);
+                const { address } = parseAccountId(session.namespaces.solana.accounts[0]);
+                publicKey = new PublicKey(address);
             } catch (error: any) {
                 throw new WalletPublicKeyError(error?.message, error);
             }
 
-            client.on(CLIENT_EVENTS.session.deleted, this._disconnected);
+            client.on('session_delete', this._disconnected);
 
             this._publicKey = publicKey;
             this._client = client;
@@ -175,15 +164,14 @@ export class WalletConnectWalletAdapter extends BaseSignerWalletAdapter {
 
     async disconnect(): Promise<void> {
         const client = this._client;
-        if (client) {
+        if (client && this._session) {
             this._publicKey = null;
             this._client = undefined;
 
             try {
                 await client.disconnect({
-                    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-                    topic: this._session!.topic,
-                    reason: ERROR.USER_DISCONNECTED.format(),
+                    topic: this._session.topic,
+                    reason: getSdkError('USER_DISCONNECTED'),
                 });
             } catch (error: any) {
                 this.emit('error', new WalletDisconnectionError(error?.message, error));
@@ -200,15 +188,14 @@ export class WalletConnectWalletAdapter extends BaseSignerWalletAdapter {
             const client = this._client;
             const publicKey = this._publicKey;
             const session = this._session;
+
             if (!client || !publicKey || !session) throw new WalletNotConnectedError();
 
             try {
                 const { signature } = await client.request({
+                    chainId: getChainId(this._network),
                     topic: session.topic,
-                    request: {
-                        method: WalletConnectRPCMethod.signTransaction,
-                        params: serialiseTransaction(transaction),
-                    },
+                    request: { method: WalletConnectRPCMethod.signTransaction, params: { ...transaction } },
                 });
 
                 transaction.addSignature(publicKey, base58.decode(signature));
@@ -240,19 +227,20 @@ export class WalletConnectWalletAdapter extends BaseSignerWalletAdapter {
             const client = this._client;
             const publicKey = this._publicKey;
             const session = this._session;
+
             if (!client || !publicKey || !session) throw new WalletNotConnectedError();
 
             try {
                 const { signature } = await client.request({
+                    // This isn't strictly necessary for Solana, but is a required parameter for this function.
+                    chainId: getChainId(this._network),
                     topic: session.topic,
                     request: {
                         method: WalletConnectRPCMethod.signMessage,
-                        params: {
-                            pubkey: publicKey.toString(),
-                            message: base58.encode(message),
-                        },
+                        params: { pubkey: publicKey.toString(), message: base58.encode(message) },
                     },
                 });
+
                 return base58.decode(signature);
             } catch (error: any) {
                 throw new WalletSignMessageError(error?.message, error);
@@ -266,7 +254,7 @@ export class WalletConnectWalletAdapter extends BaseSignerWalletAdapter {
     private _disconnected = () => {
         const client = this._client;
         if (client) {
-            client.off(CLIENT_EVENTS.session.deleted, this._disconnected);
+            client.off('session_delete', this._disconnected);
 
             this._publicKey = null;
             this._client = undefined;

+ 0 - 0
wip/walletconnect/src/index.ts → packages/wallets/walletconnect/src/index.ts


+ 0 - 0
wip/walletconnect/tsconfig.cjs.json → packages/wallets/walletconnect/tsconfig.cjs.json


+ 0 - 0
wip/walletconnect/tsconfig.json → packages/wallets/walletconnect/tsconfig.json


+ 0 - 1
wip/walletconnect/.gitignore

@@ -1 +0,0 @@
-lib

Файловите разлики са ограничени, защото са твърде много
+ 678 - 5
yarn.lock


Някои файлове не бяха показани, защото твърде много файлове са промени