Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into metamask-snaps-inte…
Browse files Browse the repository at this point in the history
…gration
  • Loading branch information
gomesalexandre committed Sep 25, 2023
2 parents 590cda1 + fcab4e7 commit 1c7f34b
Show file tree
Hide file tree
Showing 11 changed files with 1,928 additions and 558 deletions.
3 changes: 3 additions & 0 deletions examples/sandbox/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ <h4>Select</h4>
<button id="tallyHo">Pair Tally Ho</button>
<button id="coinbase">Pair Coinbase</button>
<button id="walletConnect">Pair WalletConnect</button>
<button id="walletConnectV2">Pair WalletConnect V2</button>

<select id="keyring" style="height: 100px" size="4"></select>
</div>
Expand All @@ -145,6 +146,8 @@ <h4>Manage</h4>
<button class="button button-outline" id="doPing">Ping</button>
<button class="button button-outline" id="doWipe">Wipe</button>
<button class="button button-outline" id="doLoadDevice">Load</button>
<button class="button button-outline" id="doClearSession">Clear Session</button>
<button class="button button-outline" id="doDisconnect">Disconnect</button>
<input type="text" id="manageResults" />
</div>
<div class="container">
Expand Down
58 changes: 58 additions & 0 deletions examples/sandbox/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ import * as tallyHo from "@shapeshiftoss/hdwallet-tallyho";
import * as trezorConnect from "@shapeshiftoss/hdwallet-trezor-connect";
import { WalletConnectProviderConfig } from "@shapeshiftoss/hdwallet-walletconnect";
import * as walletConnect from "@shapeshiftoss/hdwallet-walletconnect";
import * as walletConnectv2 from "@shapeshiftoss/hdwallet-walletconnectv2";
import * as xdefi from "@shapeshiftoss/hdwallet-xdefi";
import { EthereumProviderOptions } from "@walletconnect/ethereum-provider/dist/types/EthereumProvider";
import $ from "jquery";
import Web3 from "web3";

Expand Down Expand Up @@ -77,6 +79,21 @@ const walletConnectOptions: WalletConnectProviderConfig = {
1: "https://mainnet.infura.io/v3/d734c7eebcdf400185d7eb67322a7e57",
},
};
const walletConnectV2Options: EthereumProviderOptions = {
projectId: "5abef0455c768644c2bc866f1520374f",
chains: [1],
optionalChains: [100],
optionalMethods: [
"eth_signTypedData",
"eth_signTypedData_v4",
"eth_sign",
"ethVerifyMessage",
"eth_accounts",
"eth_sendTransaction",
"eth_signTransaction",
],
showQrModal: true,
};

const coinbaseOptions: CoinbaseProviderConfig = {
appName: "ShapeShift Sandbox",
Expand Down Expand Up @@ -109,6 +126,7 @@ const portisAdapter = portis.PortisAdapter.useKeyring(keyring, { portisAppId });
const metaMaskAdapter = metaMask.MetaMaskAdapter.useKeyring(keyring);
const tallyHoAdapter = tallyHo.TallyHoAdapter.useKeyring(keyring);
const walletConnectAdapter = walletConnect.WalletConnectAdapter.useKeyring(keyring, walletConnectOptions);
const walletConnectV2Adapter = walletConnectv2.WalletConnectV2Adapter.useKeyring(keyring, walletConnectV2Options);
const xdefiAdapter = xdefi.XDEFIAdapter.useKeyring(keyring);
const keplrAdapter = keplr.KeplrAdapter.useKeyring(keyring);
const nativeAdapter = native.NativeAdapter.useKeyring(keyring);
Expand Down Expand Up @@ -141,6 +159,7 @@ const $metaMask = $("#metaMask");
const $coinbase = $("#coinbase");
const $tallyHo = $("#tallyHo");
const $walletConnect = $("#walletConnect");
const $walletConnectV2 = $("#walletConnectV2");
const $xdefi = $("#xdefi");
const $keplr = $("#keplr");
const $keyring = $("#keyring");
Expand Down Expand Up @@ -283,6 +302,19 @@ $walletConnect.on("click", async (e) => {
}
});

$walletConnectV2.on("click", async (e) => {
e.preventDefault();
try {
wallet = await walletConnectV2Adapter.pairDevice();
window["wallet"] = wallet;
let deviceID = "nothing";
deviceID = await wallet.getDeviceID();
$("#keyring select").val(deviceID);
} catch (error) {
console.error(error);
}
});

$xdefi.on("click", async (e) => {
e.preventDefault();
wallet = await xdefiAdapter.pairDevice();
Expand Down Expand Up @@ -401,6 +433,12 @@ async function deviceConnected(deviceId) {
console.error("Could not initialize WalletConnectAdapter", e);
}

try {
await walletConnectV2Adapter.initialize();
} catch (e) {
console.error("Could not initialize WalletConnectV2Adapter", e);
}

for (const deviceID of Object.keys(keyring.wallets)) {
await deviceConnected(deviceID);
}
Expand Down Expand Up @@ -518,6 +556,8 @@ const $getXpubs = $("#getXpubs");
const $doPing = $("#doPing");
const $doWipe = $("#doWipe");
const $doLoadDevice = $("#doLoadDevice");
const $doDisconnect = $("#doDisconnect");
const $doClearSession = $("#doClearSession");
const $manageResults = $("#manageResults");

$getVendor.on("click", async (e) => {
Expand Down Expand Up @@ -651,6 +691,24 @@ $doLoadDevice.on("click", (e) => {
});
});

$doDisconnect.on("click", (e) => {
e.preventDefault();
if (!wallet) {
$manageResults.val("No wallet?");
return;
}
wallet.disconnect().then(() => $manageResults.val("Disconnected"));
});

$doClearSession.on("click", (e) => {
e.preventDefault();
if (!wallet) {
$manageResults.val("No wallet?");
return;
}
wallet.clearSession().then(() => $manageResults.val("Session Cleared"));
});

const $openApp = $("#openApp");
const $ledgerAppToOpen = $("#ledgerAppToOpen");
const $validateApp = $("#validateApp");
Expand Down
5 changes: 5 additions & 0 deletions examples/sandbox/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"compilerOptions": {
"resolveJsonModule": true,
}
}
10 changes: 5 additions & 5 deletions packages/hdwallet-walletconnect/src/adapter.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as core from "@shapeshiftoss/hdwallet-core";
import { Events, Keyring } from "@shapeshiftoss/hdwallet-core";
import WalletConnectProvider from "@walletconnect/web3-provider";

import { WalletConnectHDWallet } from "./walletconnect";
Expand All @@ -10,15 +10,15 @@ export type WalletConnectProviderConfig =
| { rpc: { [key: number]: string } };

export class WalletConnectAdapter {
keyring: core.Keyring;
keyring: Keyring;
private providerConfig: WalletConnectProviderConfig;

private constructor(keyring: core.Keyring, config: WalletConnectProviderConfig) {
private constructor(keyring: Keyring, config: WalletConnectProviderConfig) {
this.keyring = keyring;
this.providerConfig = config;
}

public static useKeyring(keyring: core.Keyring, config: WalletConnectProviderConfig) {
public static useKeyring(keyring: Keyring, config: WalletConnectProviderConfig) {
return new WalletConnectAdapter(keyring, config);
}

Expand All @@ -38,7 +38,7 @@ export class WalletConnectAdapter {
await wallet.initialize();
const deviceID = await wallet.getDeviceID();
this.keyring.add(wallet, deviceID);
this.keyring.emit(["WalletConnect", deviceID, core.Events.CONNECT], deviceID);
this.keyring.emit(["WalletConnect", deviceID, Events.CONNECT], deviceID);
return wallet;
} catch (error) {
console.error("Could not pair WalletConnect");
Expand Down
27 changes: 27 additions & 0 deletions packages/hdwallet-walletconnectV2/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"name": "@shapeshiftoss/hdwallet-walletconnectv2",
"version": "1.50.6",
"license": "MIT",
"publishConfig": {
"access": "public"
},
"main": "dist/index.js",
"source": "src/index.ts",
"types": "dist/index.d.ts",
"scripts": {
"build": "tsc --build",
"clean": "rm -rf dist tsconfig.tsbuildinfo",
"dev": "yarn tsc --build --watch",
"prepublishOnly": "yarn clean && yarn build"
},
"dependencies": {
"@shapeshiftoss/hdwallet-core": "^1.50.6",
"@walletconnect/ethereum-provider": "^2.10.1",
"@walletconnect/modal": "^2.6.2",
"ethers": "^5.6.5"
},
"devDependencies": {
"typescript": "^4.3.2"
},
"gitHead": "64bf68d0087821d82d2c714a82991cee8dbdc4b3"
}
44 changes: 44 additions & 0 deletions packages/hdwallet-walletconnectV2/src/adapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { Events, Keyring } from "@shapeshiftoss/hdwallet-core";
import { EthereumProvider } from "@walletconnect/ethereum-provider";
import { EthereumProviderOptions } from "@walletconnect/ethereum-provider/dist/types/EthereumProvider";

import { WalletConnectV2HDWallet } from "./walletconnectV2";

export class WalletConnectV2Adapter {
keyring: Keyring;
private readonly providerConfig: EthereumProviderOptions;

private constructor(keyring: Keyring, config: EthereumProviderOptions) {
this.keyring = keyring;
this.providerConfig = config;
}

public static useKeyring(keyring: Keyring, config: EthereumProviderOptions) {
return new WalletConnectV2Adapter(keyring, config);
}

public async initialize(): Promise<number> {
return Object.keys(this.keyring.wallets).length;
}

public async pairDevice(): Promise<WalletConnectV2HDWallet> {
try {
if (!this.providerConfig) {
throw new Error("WalletConnectV2 provider configuration not set.");
}

const provider = await EthereumProvider.init(this.providerConfig);
const wallet = new WalletConnectV2HDWallet(provider);

// Enable session (triggers QR Code modal)
await wallet.initialize();
const deviceID = await wallet.getDeviceID();
this.keyring.add(wallet, deviceID);
this.keyring.emit(["WalletConnectV2", deviceID, Events.CONNECT], deviceID);
return wallet;
} catch (error) {
console.error("Could not pair WalletConnectV2");
throw error;
}
}
}
115 changes: 115 additions & 0 deletions packages/hdwallet-walletconnectV2/src/ethereum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import type {
BIP32Path,
ETHSignedMessage,
ETHSignedTx,
ETHSignTx,
ETHTxHash,
ETHVerifyMessage,
PathDescription,
} from "@shapeshiftoss/hdwallet-core";
import { addressNListToBIP32, slip44ByCoin } from "@shapeshiftoss/hdwallet-core";
import EthereumProvider from "@walletconnect/ethereum-provider";
import type { Bytes } from "ethers";
import { arrayify, isBytes } from "ethers/lib/utils";

const getUnsignedTxFromMessage = (msg: ETHSignTx & { from: string }) => {
const utxBase = {
from: msg.from,
to: msg.to,
value: msg.value,
data: msg.data,
chainId: msg.chainId,
nonce: msg.nonce,
gasLimit: msg.gasLimit,
};

return msg.maxFeePerGas
? {
...utxBase,
maxFeePerGas: msg.maxFeePerGas,
maxPriorityFeePerGas: msg.maxPriorityFeePerGas,
}
: { ...utxBase, gasPrice: msg.gasPrice };
};

export function describeETHPath(path: BIP32Path): PathDescription {
const pathStr = addressNListToBIP32(path);
const unknown: PathDescription = {
verbose: pathStr,
coin: "Ethereum",
isKnown: false,
};

if (path.length !== 5) return unknown;

if (path[0] !== 0x80000000 + 44) return unknown;

if (path[1] !== 0x80000000 + slip44ByCoin("Ethereum")) return unknown;

if ((path[2] & 0x80000000) >>> 0 !== 0x80000000) return unknown;

if (path[3] !== 0) return unknown;

if (path[4] !== 0) return unknown;

const index = path[2] & 0x7fffffff;
return {
verbose: `Ethereum Account #${index}`,
accountIdx: index,
wholeAccount: true,
coin: "Ethereum",
isKnown: true,
};
}

export async function ethSignTx(
args: ETHSignTx & { from: string },
provider: EthereumProvider
): Promise<ETHSignedTx | null> {
const utx = getUnsignedTxFromMessage(args);
return await provider.request({ method: "eth_signTransaction", params: [utx] });
}

export async function ethSendTx(
msg: ETHSignTx & { from: string },
provider: EthereumProvider
): Promise<ETHTxHash | null> {
const utx = getUnsignedTxFromMessage(msg);
const txHash: string = await provider.request({ method: "eth_sendTransaction", params: [utx] });
return txHash
? {
hash: txHash,
}
: null;
}

export async function ethSignMessage(
args: { data: string | Bytes; fromAddress: string },
provider: EthereumProvider
): Promise<ETHSignedMessage | null> {
const buffer = isBytes(args.data) ? Buffer.from(arrayify(args.data)) : Buffer.from(args.data);

return await provider.request({
method: "eth_sign",
params: [args.fromAddress, buffer],
});
}

export async function ethGetAddress(provider: EthereumProvider): Promise<string | null> {
try {
if (!(provider && provider.connected)) {
throw new Error("No WalletConnectV2 provider available.");
}
const ethAccounts: string[] = await provider.request({
method: "eth_accounts",
});
return ethAccounts[0];
} catch (error) {
console.error(error);
return null;
}
}

export async function ethVerifyMessage(provider: EthereumProvider, args: ETHVerifyMessage): Promise<boolean> {
return await provider.request({ method: "ethVerifyMessage", params: [args.message, args.signature] });
}
2 changes: 2 additions & 0 deletions packages/hdwallet-walletconnectV2/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./adapter";
export * from "./walletconnectV2";
Loading

0 comments on commit 1c7f34b

Please sign in to comment.