From 303376c7759ee168f35b14bddeb70a64c69cbfb0 Mon Sep 17 00:00:00 2001 From: huianyang Date: Fri, 25 Oct 2024 14:42:41 -0700 Subject: [PATCH] feat: testdapp with walletconnect option --- README.md | 2 + apps/taquito-test-dapp/package.json | 2 + apps/taquito-test-dapp/src/App.svelte | 53 +- apps/taquito-test-dapp/src/config.ts | 29 +- .../src/lib/ModalActivePairing.svelte | 79 +++ .../src/lib/TestContainer.svelte | 36 +- apps/taquito-test-dapp/src/lib/Wallet.svelte | 338 +++++++------ apps/taquito-test-dapp/src/store.ts | 25 +- apps/taquito-test-dapp/src/tests.ts | 43 +- apps/taquito-test-dapp/src/types.ts | 10 +- apps/taquito-test-dapp/vite.config.ts | 3 +- package-lock.json | 457 +++++++++++++++++- package.json | 3 +- .../taquito-wallet-connect-2/package.json | 2 +- .../src/taquito-wallet-connect-2.ts | 5 +- website/package-lock.json | 61 +-- 16 files changed, 852 insertions(+), 296 deletions(-) create mode 100644 apps/taquito-test-dapp/src/lib/ModalActivePairing.svelte diff --git a/README.md b/README.md index 0446d3bac9..7efbb26282 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,8 @@ Taquito is organized as a [monorepo](https://en.wikipedia.org/wiki/Monorepo), an | [@taquito/contracts-library](packages/taquito-contracts-library) | Provides functionality specify static data related to contracts | | [@taquito/ledger-signer](packages/taquito-ledger-signer) | Provides functionality for ledger signer provider | | [@taquito/timelock](packages/taquito-timelock) | Provides functionality to create and open timelocks | +| [@taquito/wallet-connect](packages/taquito-wallet-connect) | WalletConnect2 class can be injected into the `TezosToolkit` to work with the wallet API. | + ## API Documentation diff --git a/apps/taquito-test-dapp/package.json b/apps/taquito-test-dapp/package.json index a06b83bec3..78b3dbb161 100644 --- a/apps/taquito-test-dapp/package.json +++ b/apps/taquito-test-dapp/package.json @@ -30,7 +30,9 @@ "@taquito/core": "^20.1.0", "@taquito/taquito": "^20.1.0", "@taquito/utils": "^20.1.0", + "@taquito/wallet-connect-2": "^20.1.0", "buffer": "^6.0.3", + "svelte-modals": "^2.0.0-beta.2", "svelte-select": "^5.8.3", "vite-compatible-readable-stream": "^3.6.1" }, diff --git a/apps/taquito-test-dapp/src/App.svelte b/apps/taquito-test-dapp/src/App.svelte index c3447e11a2..e1e5dd40f8 100644 --- a/apps/taquito-test-dapp/src/App.svelte +++ b/apps/taquito-test-dapp/src/App.svelte @@ -4,7 +4,7 @@ import { NetworkType } from "@airgap/beacon-types"; import Select from "svelte-select"; import { getRpcUrl } from "./config"; - import store from "./store"; + import store, { SDK } from "./store"; import Layout from "./Layout.svelte"; import TestContainer from "./lib/TestContainer.svelte"; @@ -13,7 +13,6 @@ let browser = ""; let availableNetworks = [ { value: "ghostnet", label: "Ghostnet", group: "current testnets" }, - { value: "oxfordnet", label: "Oxfordnet", group: "current testnets" }, { value: "parisnet", label: "Parisnet", group: "current testnets" }, { value: "mainnet", label: "Mainnet", group: "mainnet" }, { value: "dailynet", label: "Dailynet", group: "other testnets" }, @@ -40,9 +39,6 @@ case "ghostnet": store.updateNetworkType(NetworkType.GHOSTNET); break; - case "oxfordnet": - store.updateNetworkType(NetworkType.OXFORDNET); - break; case "parisnet": store.updateNetworkType(NetworkType.PARISNET); break; @@ -129,6 +125,18 @@ margin: 10px 0px; } + .sdk { + border: 1px solid; + border-radius: 10px; + display: flex; + flex-direction: column; + align-items: flex-start; + padding: 20px; + grid-row-gap: 10px; + align-items: center; + width: 100%; + } + button { width: 100%; justify-content: center; @@ -175,15 +183,32 @@
(use Chrome for a better experience)
{/if}
- +
+ Connect using beacon sdk + +
+
+ Connect using Wallet Connect 2 + +
+ {/each} +

or

+ +
+ + +{/if} \ No newline at end of file diff --git a/apps/taquito-test-dapp/src/lib/TestContainer.svelte b/apps/taquito-test-dapp/src/lib/TestContainer.svelte index 89c466b26f..69d46d805c 100644 --- a/apps/taquito-test-dapp/src/lib/TestContainer.svelte +++ b/apps/taquito-test-dapp/src/lib/TestContainer.svelte @@ -5,14 +5,26 @@ import { shortenHash } from "../utils"; import { NetworkType } from "@airgap/beacon-types"; import { getTzKtUrl } from "../config"; + import { BeaconWallet } from "@taquito/beacon-wallet"; let test: TestSettings | undefined; let executionTime = 0; let loading = false; let success: boolean | undefined; let opHash = ""; - let input = { text: "", fee: 400, storageLimit: 400, gasLimit: 1320, amount: 0, address: "", delegate: "", stake: 0, unstake: 0 }; + let input = { + text: "", + fee: 400, + storageLimit: 400, + gasLimit: 1320, + amount: 0, + address: "", + delegate: "", + stake: 0, + unstake: 0, + }; let testResult: { id: string; title: string; body: any }; + let error: Error | undefined; const run = async () => { success = undefined; @@ -70,7 +82,8 @@ }; } } else { - throw "Error"; + error = result.error; + throw result.error; } } catch (error) { console.log(error); @@ -82,7 +95,9 @@ }; const switchAccount = async () => { - await $store.wallet.clearActiveAccount(); + if ($store.wallet instanceof BeaconWallet) { + await $store.wallet.clearActiveAccount(); + } store.updateUserAddress(undefined); store.updateUserBalance(undefined); store.updateWallet(undefined); @@ -306,21 +321,21 @@ bind:value={input.text} /> - {:else if test.inputRequired && test.inputType === "delegate"} + {:else if test.inputRequired && test.inputType === "delegate"}
- {:else if test.inputRequired && test.inputType === "stake"} + {:else if test.inputRequired && test.inputType === "stake"}
- {:else if test.inputRequired && test.inputType === "unstake"} + {:else if test.inputRequired && test.inputType === "unstake"}
{/if}
diff --git a/apps/taquito-test-dapp/src/lib/Wallet.svelte b/apps/taquito-test-dapp/src/lib/Wallet.svelte index e14fb992f2..48983e1803 100644 --- a/apps/taquito-test-dapp/src/lib/Wallet.svelte +++ b/apps/taquito-test-dapp/src/lib/Wallet.svelte @@ -3,105 +3,213 @@ import { fly } from "svelte/transition"; import { TezosToolkit } from "@taquito/taquito"; import { BeaconWallet } from "@taquito/beacon-wallet"; - import { BeaconEvent, type DAppClientOptions } from "@airgap/beacon-sdk"; - import store from "../store"; + import store, { SDK } from "../store"; import { formatTokenAmount, shortenHash } from "../utils"; - import { - defaultMatrixNode, - getRpcUrl, - defaultNetworkType, - type SupportedNetworks, - } from "../config"; + import { defaultMatrixNode, getRpcUrl } from "../config"; import type { TezosAccountAddress } from "../types"; + import { WalletConnect2, PermissionScopeMethods, NetworkType as NetworkTypeWc2 } from "@taquito/wallet-connect-2"; + import { Modals, closeModal, openModal } from "svelte-modals"; + import ModalActivePairing from "./ModalActivePairing.svelte"; + import type { NetworkType as NetworkTypeBeacon } from "@airgap/beacon-sdk"; let showDialog = false; let connectedWallet = ""; - const createNewWallet = (config: { networkType: SupportedNetworks }) => { - const wallet = new BeaconWallet({ + const selectExistingPairing = (wallet: WalletConnect2, existingPairing: any[]) => { + openModal( + ModalActivePairing, + { + title: "Select available pairing", + options: existingPairing, + }, + { + on: { + select: async (event) => { + closeModal(); + const topic = event.detail === "new_pairing" ? undefined : event.detail.topic; + await requestPermissionWc2(wallet, topic); + }, + }, + } + ); + }; + + const createNewBeaconWallet = () => { + return new BeaconWallet({ name: "Taquito Test Dapp", matrixNodes: [defaultMatrixNode] as any, - network: { - type: config.networkType, - rpcUrl: getRpcUrl(config.networkType), - }, - walletConnectOptions: { - projectId: "ba97fd7d1e89eae02f7c330e14ce1f36", + preferredNetwork: $store.networkType as NetworkTypeBeacon, + }); + }; + + const createNewWalletConnect2 = async () => { + const wallet = await WalletConnect2.init({ + logger: "debug", + projectId: "861613623da99d7285aaad8279a87ee9", // Your Project ID gives you access to WalletConnect Cloud. + metadata: { + name: "Taquito Test Dapp", + description: "Test Taquito with WalletConnect2", + icons: [], + url: "", }, - enableMetrics: $store.enableMetrics, }); - wallet.client.subscribeToEvent(BeaconEvent.ACTIVE_ACCOUNT_SET, () => {}); + wallet.signClient.on("session_ping", ({ id, topic }) => { + console.log("session_ping in test dapp", id, topic); + }); + wallet.signClient.on("session_delete", ({ topic }) => { + console.log("EVEN: session_delete", topic); + if (!wallet.isActiveSession()) { + resetApp(); + } + }); + wallet.signClient.on("session_update", async ({ topic }) => { + console.log("EVEN: session_update", topic); + const allAccounts = wallet.getAccounts(); + await updateStore(wallet, allAccounts); + }); return wallet; }; - const connectWallet = async () => { - const wallet = await setWallet({ - networkType: $store.networkType, + const requestPermissionWc2 = async (wallet: WalletConnect2, pairingTopic?: string) => { + await wallet.requestPermissions({ + permissionScope: { + networks: [$store.networkType as NetworkTypeWc2], + events: [], + methods: [PermissionScopeMethods.TEZOS_SEND, PermissionScopeMethods.TEZOS_SIGN], + }, + pairingTopic, + registryUrl: "https://www.tezos.help/wcdata/" }); + const allAccounts = wallet.getAccounts(); + await updateStore(wallet, allAccounts); + }; - await subscribeToAllEvents(wallet); - - try { - await wallet.requestPermissions(); + const connectWalletWithExistingSession = async (sessionId: string) => { + const newWallet = await createNewWalletConnect2(); + newWallet.configureWithExistingSessionKey(sessionId); + const allAccounts = newWallet.getAccounts(); + await updateStore(newWallet, allAccounts); + }; - const userAddress = (await wallet.getPKH()) as TezosAccountAddress; - store.updateUserAddress(userAddress); - const url = getRpcUrl($store.networkType); - const Tezos = new TezosToolkit(url); - Tezos.setWalletProvider(wallet); - store.updateTezos(Tezos); + const connectWallet = async () => { + if (!$store.wallet) { + if ($store.sdk === SDK.BEACON) { + const newWallet = createNewBeaconWallet(); + await newWallet.requestPermissions({ + network: { + type: $store.networkType as NetworkTypeBeacon, + rpcUrl: getRpcUrl($store.networkType), + }, + }); - const balance = await Tezos.tz.getBalance(userAddress); - if (balance) { - store.updateUserBalance(balance.toNumber()); + const peers = await newWallet.client.getPeers(); + connectedWallet = peers[0].name; + await updateStore(newWallet); + } else if ($store.sdk === SDK.WC2) { + const newWallet = await createNewWalletConnect2(); + const existingPairing = newWallet.getAvailablePairing(); + if (existingPairing.length > 0) { + selectExistingPairing(newWallet, existingPairing); + } else { + await requestPermissionWc2(newWallet); + } } + } else { + return $store.wallet; + } + }; + const updateUserBalance = async (userAddress: string) => { + const balance = await $store.Tezos!.tz.getBalance(userAddress); + if (balance) { + store.updateUserBalance(balance.toNumber()); + } + }; + + const updateStore = async (wallet: BeaconWallet | WalletConnect2, allAccounts?: string[]) => { + try { store.updateWallet(wallet); + let userAddress: TezosAccountAddress; + if (allAccounts) { + if (allAccounts.length > 1) { + userAddress = allAccounts.shift() as TezosAccountAddress; + store.updateAvailableAccounts(allAccounts); + } else { + store.updateAvailableAccounts([]); + userAddress = allAccounts[0] as TezosAccountAddress; + } + } else { + userAddress = (await wallet.getPKH()) as TezosAccountAddress; + } + store.updateUserAddress(userAddress); + if (wallet instanceof WalletConnect2) { + wallet.setActiveAccount(userAddress); + wallet.setActiveNetwork($store.networkType as any); + } + + const Tezos = new TezosToolkit(getRpcUrl($store.networkType)); + Tezos.setWalletProvider(wallet); + store.updateTezos(Tezos); - const peers = await wallet.client.getPeers(); - connectedWallet = peers[0].name; + await updateUserBalance(userAddress); } catch (err) { console.error(err); } }; - const disconnectWallet = async () => { - await $store.wallet?.clearActiveAccount(); + const resetApp = async () => { store.updateUserAddress(undefined); store.updateUserBalance(undefined); store.updateWallet(undefined); store.updateSelectedTest(undefined); + store.updateTests([]); + store.updateAvailableAccounts([]); }; - export const setWallet = async (config: { networkType: SupportedNetworks }) => { - store.updateNetworkType(config.networkType); - - const wallet = createNewWallet(config); - store.updateWallet(wallet); - const url = getRpcUrl(config.networkType); - const Tezos = new TezosToolkit(url); - Tezos.setWalletProvider(wallet); - store.updateTezos(Tezos); - - // const activeAccount = await wallet.client.getActiveAccount(); - // if (activeAccount) { - // const userAddress = (await wallet.getPKH()) as TezosAccountAddress; - // store.updateUserAddress(userAddress); + const disconnectWallet = async () => { + if ($store.wallet instanceof BeaconWallet) { + await $store.wallet.clearActiveAccount(); + } else if ($store.wallet instanceof WalletConnect2) { + await $store.wallet.disconnect(); + } + resetApp(); + }; - // const balance = await Tezos.tz.getBalance(userAddress); - // if (balance) { - // store.updateUserBalance(balance.toNumber()); - // } - // } - return wallet; + const switchActiveAccount = (newActiveAccount: string) => { + const currentPkh = $store.userAddress; + const availablePkh = $store.availableAccounts; + const index = availablePkh!.indexOf(newActiveAccount, 0); + if (index > -1) { + availablePkh!.splice(index, 1); + } + availablePkh!.push(currentPkh!); + store.updateAvailableAccounts(availablePkh!); + store.updateUserAddress(newActiveAccount); + if ($store.wallet instanceof WalletConnect2) { + $store.wallet.setActiveAccount(newActiveAccount); + } + updateUserBalance(newActiveAccount); }; onMount(async () => { - store.updateNetworkType(defaultNetworkType); + console.log("onmount wallet", $store); + if ( + window && + window.localStorage && + window.localStorage["wc@2:client:0.3//session"] && + window.localStorage["wc@2:client:0.3//session"] !== "[]" + ) { + const sessions = JSON.parse(window.localStorage["wc@2:client:0.3//session"]); + const lastSession = sessions[sessions.length - 1].topic; + store.updateSdk(SDK.WC2); + await connectWalletWithExistingSession(lastSession); + } else { + await connectWallet(); + } }); afterUpdate(async () => { - if ($store.wallet) { + if ($store.wallet instanceof BeaconWallet) { const activeAccount = await $store.wallet.client.getActiveAccount(); if (activeAccount) { const peers = await $store.wallet.client.getPeers(); @@ -109,92 +217,10 @@ connectedWallet = peers[0].name; } } + } else if ($store.wallet instanceof WalletConnect2) { + connectedWallet = $store.wallet.getPeerMetadata().name; } }); - - const saveLog = (data: unknown, eventType: BeaconEvent) => { - const log = JSON.stringify({ eventType, data }); - store.addEvent(log); - }; - - async function subscribeToAllEvents(wallet: BeaconWallet) { - await wallet.client.subscribeToEvent(BeaconEvent.PERMISSION_REQUEST_SENT, (data) => - saveLog(data, BeaconEvent.PERMISSION_REQUEST_SENT), - ); - await wallet.client.subscribeToEvent(BeaconEvent.PERMISSION_REQUEST_SUCCESS, (data) => - saveLog(data, BeaconEvent.PERMISSION_REQUEST_SUCCESS), - ); - await wallet.client.subscribeToEvent(BeaconEvent.PERMISSION_REQUEST_ERROR, (data) => - saveLog(data, BeaconEvent.PERMISSION_REQUEST_ERROR), - ); - await wallet.client.subscribeToEvent(BeaconEvent.OPERATION_REQUEST_SENT, (data) => - saveLog(data, BeaconEvent.OPERATION_REQUEST_SENT), - ); - await wallet.client.subscribeToEvent(BeaconEvent.OPERATION_REQUEST_SUCCESS, (data) => - saveLog(data, BeaconEvent.OPERATION_REQUEST_SUCCESS), - ); - await wallet.client.subscribeToEvent(BeaconEvent.OPERATION_REQUEST_ERROR, (data) => - saveLog(data, BeaconEvent.OPERATION_REQUEST_ERROR), - ); - await wallet.client.subscribeToEvent(BeaconEvent.SIGN_REQUEST_SENT, (data) => - saveLog(data, BeaconEvent.SIGN_REQUEST_SENT), - ); - await wallet.client.subscribeToEvent(BeaconEvent.SIGN_REQUEST_SUCCESS, (data) => - saveLog(data, BeaconEvent.SIGN_REQUEST_SUCCESS), - ); - await wallet.client.subscribeToEvent(BeaconEvent.SIGN_REQUEST_ERROR, (data) => - saveLog(data, BeaconEvent.SIGN_REQUEST_ERROR), - ); - await wallet.client.subscribeToEvent(BeaconEvent.BROADCAST_REQUEST_SENT, (data) => - saveLog(data, BeaconEvent.BROADCAST_REQUEST_SENT), - ); - await wallet.client.subscribeToEvent(BeaconEvent.BROADCAST_REQUEST_SUCCESS, (data) => - saveLog(data, BeaconEvent.BROADCAST_REQUEST_SUCCESS), - ); - await wallet.client.subscribeToEvent(BeaconEvent.BROADCAST_REQUEST_ERROR, (data) => - saveLog(data, BeaconEvent.BROADCAST_REQUEST_ERROR), - ); - await wallet.client.subscribeToEvent(BeaconEvent.ACKNOWLEDGE_RECEIVED, (data) => - saveLog(data, BeaconEvent.ACKNOWLEDGE_RECEIVED), - ); - await wallet.client.subscribeToEvent(BeaconEvent.LOCAL_RATE_LIMIT_REACHED, (data) => - saveLog(data, BeaconEvent.LOCAL_RATE_LIMIT_REACHED), - ); - await wallet.client.subscribeToEvent(BeaconEvent.NO_PERMISSIONS, (data) => - saveLog(data, BeaconEvent.NO_PERMISSIONS), - ); - - await wallet.client.subscribeToEvent(BeaconEvent.ACTIVE_ACCOUNT_SET, (data) => { - saveLog(data, BeaconEvent.ACTIVE_ACCOUNT_SET); - store.updateUserAddress(data.address); - store.updateNetworkType(data.network.type as SupportedNetworks); - }); - - await wallet.client.subscribeToEvent(BeaconEvent.ACTIVE_TRANSPORT_SET, (data) => - saveLog(data, BeaconEvent.ACTIVE_TRANSPORT_SET), - ); - await wallet.client.subscribeToEvent(BeaconEvent.SHOW_PREPARE, (data) => - saveLog(data, BeaconEvent.SHOW_PREPARE), - ); - await wallet.client.subscribeToEvent(BeaconEvent.HIDE_UI, (data) => - saveLog(data, BeaconEvent.HIDE_UI), - ); - await wallet.client.subscribeToEvent(BeaconEvent.PAIR_INIT, (data) => - saveLog(data, BeaconEvent.PAIR_INIT), - ); - await wallet.client.subscribeToEvent(BeaconEvent.PAIR_SUCCESS, (data) => - saveLog(data, BeaconEvent.PAIR_SUCCESS), - ); - await wallet.client.subscribeToEvent(BeaconEvent.CHANNEL_CLOSED, (data) => - saveLog(data, BeaconEvent.CHANNEL_CLOSED), - ); - await wallet.client.subscribeToEvent(BeaconEvent.INTERNAL_ERROR, (data) => - saveLog(data, BeaconEvent.INTERNAL_ERROR), - ); - await wallet.client.subscribeToEvent(BeaconEvent.UNKNOWN, (data) => - saveLog(data, BeaconEvent.UNKNOWN), - ); - }