Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(wallet connect): sign payload using wc in test-dapp #3074

Merged
merged 2 commits into from
Nov 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions apps/taquito-test-dapp/src/lib/TestContainer.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,14 @@
title: "Confirmations through observable",
body: result.confirmationObsOutput,
};
} else if (test.id === "show-public-key") {
testResult = {
id: test.id,
title: "Public Key",
body: {
output: result.output
}
}
}
} else {
error = result.error;
Expand Down
12 changes: 7 additions & 5 deletions apps/taquito-test-dapp/src/lib/Wallet.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,18 @@
});
wallet.signClient.on("session_ping", ({ id, topic }) => {
console.log("session_ping in test dapp", id, topic);
store.addEvent("session_ping");
});
wallet.signClient.on("session_delete", ({ topic }) => {
console.log("EVEN: session_delete", topic);
console.log("EVENT: session_delete", topic);
store.addEvent("session_delete");
if (!wallet.isActiveSession()) {
resetApp();
}
});
wallet.signClient.on("session_update", async ({ topic }) => {
console.log("EVEN: session_update", topic);
console.log("EVENT: session_update", topic);
store.addEvent("session_update");
const allAccounts = wallet.getAccounts();
await updateStore(wallet, allAccounts);
});
Expand All @@ -75,10 +78,9 @@
permissionScope: {
networks: [$store.networkType as NetworkTypeWc2],
events: [],
methods: [PermissionScopeMethods.TEZOS_SEND, PermissionScopeMethods.TEZOS_SIGN],
methods: [PermissionScopeMethods.TEZOS_SEND, PermissionScopeMethods.TEZOS_SIGN, PermissionScopeMethods.TEZOS_GET_ACCOUNTS],
},
pairingTopic,
registryUrl: "https://www.tezos.help/wcdata/"
pairingTopic
});
const allAccounts = wallet.getAccounts();
await updateStore(wallet, allAccounts);
Expand Down
99 changes: 72 additions & 27 deletions apps/taquito-test-dapp/src/tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
UnitValue
} from "@taquito/taquito";
import type { ContractProvider } from "@taquito/taquito";
import type { BeaconWallet } from "@taquito/beacon-wallet";
import { BeaconWallet } from "@taquito/beacon-wallet";
import { stringToBytes, verifySignature } from "@taquito/utils";
import { SigningType, type RequestSignPayloadInput } from "@airgap/beacon-types";
import { get } from "svelte/store";
Expand Down Expand Up @@ -56,6 +56,16 @@ const sendTezToEtherlink = async (
}
};

const showPublicKey = async (Tezos: TezosToolkit): Promise<TestResult> => {
try {
const publicKey = await Tezos.wallet.pk();
return { success: true, opHash: "", output: publicKey };
} catch (error) {
console.log(error);
return { success: false, opHash: "", output: JSON.stringify(error) };
}
}

const sendTez = async (Tezos: TezosToolkit): Promise<TestResult> => {
let opHash = "";
try {
Expand Down Expand Up @@ -348,49 +358,73 @@ const batchApiContractCallsTest = async (

const signPayload = async (
input: string,
wallet: BeaconWallet
wallet: BeaconWallet | WalletConnect2
): Promise<TestResult> => {
const userAddress = await wallet.getPKH();
const { payload, formattedInput } = preparePayloadToSign(input, userAddress);
try {
const signedPayload = await wallet.client.requestSignPayload(payload);
return {
success: true,
opHash: "",
output: signedPayload.signature,
sigDetails: { input, formattedInput, bytes: payload.payload }
};
} catch (error) {
if (wallet instanceof BeaconWallet) {
const signedPayload = await wallet.client.requestSignPayload(payload);
return {
success: true,
opHash: "",
output: signedPayload.signature,
sigDetails: { input, formattedInput, bytes: payload.payload }
};
} else {
const signature = await wallet.sign(payload.payload);
return {
success: true,
opHash: "",
output: signature,
sigDetails: { input, formattedInput, bytes: payload.payload }
};
}
}
catch (error) {
console.log(error);
return { success: false, opHash: "", output: JSON.stringify(error) };
}
};

const signPayloadAndSend = async (
input: string,
wallet: BeaconWallet,
contract: ContractAbstraction<Wallet> | ContractAbstraction<ContractProvider>
wallet: BeaconWallet | WalletConnect2,
contract: ContractAbstraction<Wallet>
): Promise<TestResult> => {
if (!input) throw "No input provided";

const userAddress = await wallet.getPKH();
const { payload, formattedInput } = preparePayloadToSign(input, userAddress);
try {
const signedPayload = await wallet.client.requestSignPayload(payload);
// gets user's public key
const activeAccount = await wallet.client.getActiveAccount();
const publicKey = activeAccount.publicKey;
// sends transaction to contract
const op = await contract.methodsObject
.check_signature({ 0: publicKey, 1: signedPayload.signature, 2: payload.payload })
.send();
await op.confirmation();
return {
success: true,
opHash: op.hasOwnProperty("opHash") ? op["opHash"] : op["hash"],
output: signedPayload.signature,
sigDetails: { input, formattedInput, bytes: payload.payload }
};
if (wallet instanceof BeaconWallet) {
const signedPayload = await wallet.client.requestSignPayload(payload);
// gets user's public key
const activeAccount = await wallet.client.getActiveAccount();
const publicKey = activeAccount.publicKey;
// sends transaction to contract
const op = await contract.methodsObject
.check_signature({ 0: publicKey, 1: signedPayload.signature, 2: payload.payload })
.send();
await op.confirmation();
return {
success: true,
opHash: op.opHash,
output: signedPayload.signature,
sigDetails: { input, formattedInput, bytes: payload.payload }
};
} else {
const signature = await wallet.sign(payload.payload);
const publicKey = await wallet.getPK();
const op = await contract.methodsObject.check_signature({ 0: publicKey, 1: signature, 2: payload.payload }).send();
await op.confirmation();
return {
success: true,
opHash: op.opHash,
output: signature,
sigDetails: { input, formattedInput, bytes: payload.payload }
}
}
} catch (error) {
return { success: false, opHash: "", output: JSON.stringify(error) };
}
Expand Down Expand Up @@ -656,6 +690,17 @@ export const init = (
contract: ContractAbstraction<Wallet> | ContractAbstraction<ContractProvider>,
wallet: BeaconWallet | undefined
): TestSettings[] => [
{
id: "show-public-key",
name: "Show public key",
description: "This test shows your public key.",
documentation: '',
keyword: 'publicKey',
run: () => showPublicKey(Tezos),
showExecutionTime: false,
inputRequired: false,
lastResult: { option: "none", val: false }
},
{
id: "send-tez",
name: "Send tez",
Expand Down
14 changes: 7 additions & 7 deletions docs/wallet_connect_2.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ The first step is to instantiate `WalletConnect2` by passing your dapp details a
import { WalletConnect2 } from "@taquito/wallet-connect-2";

const walletConnect = await WalletConnect2.init({
projectId: "YOUR_PROJECT_ID", // Your Project ID gives you access to WalletConnect Cloud
projectId: "YOUR_PROJECT_ID", // Your Project ID gives you access to Reown Cloud
metadata: {
name: "YOUR_DAPP_NAME",
description: "YOUR_DAPP_DESCRIPTION",
Expand All @@ -22,7 +22,7 @@ const walletConnect = await WalletConnect2.init({
},
});
```
`YOUR_PROJECT_ID` can be obtained from [WalletConnect Cloud](https://cloud.walletconnect.com/sign-in)
`YOUR_PROJECT_ID` can be obtained from [Reown Cloud](https://cloud.reown.com)

The second step is to establish a connection to a wallet using the `requestPermissions` method:

Expand All @@ -33,10 +33,11 @@ await walletConnect.requestPermissions({
permissionScope: {
networks: [NetworkType.GHOSTNET],
methods: [
PermissionScopeMethods.TEZOS_SEND, PermissionScopeMethods.TEZOS_SIGN
PermissionScopeMethods.TEZOS_SEND,
PermissionScopeMethods.TEZOS_SIGN,
PermissionScopeMethods.TEZOS_GET_ACCOUNTS
],
},
// registryUrl: "https://www.tezos.help/wcdata/"
}
});
```

Expand Down Expand Up @@ -80,7 +81,6 @@ WalletConnect2.init({
networks: [NetworkType.GHOSTNET],
methods: [PermissionScopeMethods.TEZOS_SEND],
},
// registryUrl: 'https://www.tezos.help/wcdata/',
})
.then(() => {
Tezos.setWalletProvider(walletConnect);
Expand All @@ -99,7 +99,7 @@ WalletConnect2.init({

## Sign payload with `WalletConnect2`

The `signPayload` method of `WalletConnect2` can be called to sign a payload. The response will be a string representing the signature. The permission `PermissionScopeMethods.TEZOS_SIGN` must have been granted, or the error `MissingRequiredScope` will be thrown.
The `sign` method of `WalletConnect2` can be called to sign a payload. The response will be a string representing the signature. The permission `PermissionScopeMethods.TEZOS_SIGN` must have been granted, or the error `MissingRequiredScope` will be thrown.

## Events handling

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
"^@taquito/tzip12$": "<rootDir>/packages/taquito-tzip12/src/taquito-tzip12.ts",
"^@taquito/tzip16$": "<rootDir>/packages/taquito-tzip16/src/taquito-tzip16.ts",
"^@taquito/utils$": "<rootDir>/packages/taquito-utils/src/taquito-utils.ts",
"^@taquito/wallet-connect-2$": "<rootDir>/packages/taquito-wallet-connect-2/src/wallet-connect-2.ts"
"^@taquito/wallet-connect-2$": "<rootDir>/packages/taquito-wallet-connect-2/src/taquito-wallet-connect-2.ts"
},
"coveragePathIgnorePatterns": [
"/node_modules/",
Expand Down
58 changes: 58 additions & 0 deletions packages/taquito-wallet-connect-2/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Taquito Wallet Connect 2 / Reown package

_Documentation can be found [here](https://taquito.io/docs/wallet_connect_2)_

## General Information

`@taquito/wallet-connect-2` is an npm package that provides developers a way to connect a dapp built with Taquito to a wallet giving the freedom to the users of the dapp to choose the wallet via the WalletConnect/Reown protocol. The `WalletConnect2` class implements the `WalletProvider` interface, providing an alternative to `BeaconWallet`.
Note: Currently, a QR code is displayed to establish a connection with a wallet. As more Tezos wallets integrate with WalletConnect, we plan showing a list of available wallets alongside the QR code.

## Install

Install the package as follows

```
npm install @taquito/wallet-connect-2
```

## Usage

Create a wallet instance with defined option parameters and set the wallet provider using `setWalletProvider` to the `TezosToolkit` instance

```ts
import { TezosToolkit } from '@taquito/taquito';
import { WalletConnect2 } from '@taquito/wallet-connect-2';

const wallet = await WalletConnect2.init({
projectId: "861613623da99d7285aaad8279a87ee9", // Your Project ID gives you access to WalletConnect Cloud.
metadata: {
name: "Taquito Test Dapp",
description: "Test Taquito with WalletConnect2",
icons: [],
url: "",
},
});

await wallet.requestPermissions({
permissionScope: {
networks: [NetworkType.GHOSTNET],
events: [],
methods: [
PermissionScopeMethods.TEZOS_SEND,
PermissionScopeMethods.TEZOS_SIGN,
PermissionScopeMethods.TEZOS_GET_ACCOUNTS
],
}
});

const Tezos = new TezosToolkit('https://YOUR_PREFERRED_RPC_URL');
Tezos.setWalletProvider(wallet);
```

## Additional Info

See the top-level [https://github.com/ecadlabs/taquito](https://github.com/ecadlabs/taquito) file for details on reporting issues, contributing and versioning.

## Disclaimer

THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
8 changes: 8 additions & 0 deletions packages/taquito-wallet-connect-2/src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,3 +147,11 @@ export class InvalidSession extends Error {
super(message);
}
}

export class PublicKeyRetrievalError extends Error {
name = 'PublicKeyRetrievalError';

constructor() {
super(`Unable to retrieve public key`);
}
}
Loading
Loading