From 1dc5d78ced4dfd1162d2dc12a2aa8c811f4193e3 Mon Sep 17 00:00:00 2001 From: CDDelta Date: Thu, 18 Nov 2021 03:43:55 +0000 Subject: [PATCH 01/10] stash --- package.json | 2 + src/stores/reducers/wallets.ts | 6 ++- src/utils/ledger.ts | 21 ++++++++ src/views/Welcome/App.tsx | 28 +++++++++-- yarn.lock | 88 +++++++++++++++++++++++++++++++++- 5 files changed, 137 insertions(+), 8 deletions(-) create mode 100644 src/utils/ledger.ts diff --git a/package.json b/package.json index a9669cb31..dcb9c7234 100644 --- a/package.json +++ b/package.json @@ -14,10 +14,12 @@ }, "dependencies": { "@geist-ui/react": "^2.2.0", + "@ledgerhq/hw-transport-webusb": "^6.11.2", "@limestonefi/api": "^0.1.3", "@primer/octicons-react": "^16.0.0", "@verto/js": "^0.0.0-alpha.19", "@verto/lib": "^0.10.4", + "@zondax/ledger-arweave": "^0.2.0", "ar-gql": "^0.0.6", "arverify": "^0.0.11", "arweave": "^1.10.18", diff --git a/src/stores/reducers/wallets.ts b/src/stores/reducers/wallets.ts index 9b11c080d..63c88ad87 100644 --- a/src/stores/reducers/wallets.ts +++ b/src/stores/reducers/wallets.ts @@ -1,5 +1,9 @@ export interface Wallet { - keyfile: string; // encrypted string + /** + * The keyfile of the wallet stored in encrypted form. + * `undefined` if imported from a Ledger. + */ + keyfile?: string; address: string; name: string; } diff --git a/src/utils/ledger.ts b/src/utils/ledger.ts new file mode 100644 index 000000000..cb4b0fb04 --- /dev/null +++ b/src/utils/ledger.ts @@ -0,0 +1,21 @@ +import TransportWebUSB from "@ledgerhq/hw-transport-webusb"; +import ArweaveApp, { ResponseBase } from "@zondax/ledger-arweave"; + +export async function getWalletAddress(): Promise { + return interactWithLedger((ledger) => ledger.getAddress()).then( + (res) => res.address + ); +} + +async function interactWithLedger( + handler: (ledger: ArweaveApp) => Promise +): Promise { + const transport = await TransportWebUSB.create(); + const ledger = new ArweaveApp(transport); + + try { + return await handler(ledger); + } finally { + transport.close(); + } +} diff --git a/src/views/Welcome/App.tsx b/src/views/Welcome/App.tsx index 14cd3be50..58ed16a52 100644 --- a/src/views/Welcome/App.tsx +++ b/src/views/Welcome/App.tsx @@ -21,6 +21,7 @@ import { Wallet } from "../../stores/reducers/wallets"; import { setWallets, switchProfile } from "../../stores/actions"; import { RootState } from "../../stores/reducers"; import { checkPassword as checkPw, setPassword } from "../../utils/auth"; +import * as ledger from "../../utils/ledger"; import { browser } from "webextension-polyfill-ts"; import CryptoES from "crypto-es"; import Arweave from "arweave"; @@ -40,7 +41,7 @@ export default function App() { }[] >([]), [loading, setLoading] = useState(false), - dispath = useDispatch(), + dispatch = useDispatch(), walletsStore = useSelector((state: RootState) => state.wallets), seedModal = useModal(false), [seedKeyfile, setSeedKeyfile] = useState<{ @@ -145,8 +146,8 @@ export default function App() { wallets.push({ address, keyfile, name }); } - dispath(setWallets([...walletsStore, ...wallets])); - if (walletsStoreEmpty) dispath(switchProfile(wallets[0].address)); + dispatch(setWallets([...walletsStore, ...wallets])); + if (walletsStoreEmpty) dispatch(switchProfile(wallets[0].address)); setLoading(false); loadWalletsModal.setVisible(false); setToast({ text: "Loaded wallets", type: "success" }); @@ -169,7 +170,7 @@ export default function App() { setSeed(mnemonic); setSeedKeyfile({ address, keyfile }); seedModal.setVisible(true); - dispath( + dispatch( setWallets([ ...walletsStore, { @@ -179,7 +180,23 @@ export default function App() { } ]) ); - dispath(switchProfile(address)); + dispatch(switchProfile(address)); + setLoading(false); + } + + async function connectLedger() { + const address = await ledger.getWalletAddress(); + + dispatch( + setWallets([ + ...walletsStore, + { + address, + name: `Account ${walletsStore.length + 1}` + } + ]) + ); + dispatch(switchProfile(address)); setLoading(false); } @@ -322,6 +339,7 @@ export default function App() { + )) || ( <> diff --git a/yarn.lock b/yarn.lock index c7940eec0..c2a74da7a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1164,6 +1164,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.12.13": + version "7.16.3" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.3.tgz#b86f0db02a04187a3c17caa77de69840165d42d5" + integrity sha512-WBwekcqacdY2e9AF/Q7WLFUWmdJGJTkbjqTjoMDgXkVZ3ZRUvOPsLb5KdwISoQVsbP+DQzVZW4Zhci0DvpbNTQ== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@^7.10.4", "@babel/template@^7.15.4", "@babel/template@^7.3.3": version "7.15.4" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.15.4.tgz#51898d35dcf3faa670c4ee6afcfd517ee139f194" @@ -2021,6 +2028,74 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" +"@ledgerhq/devices@^5.51.1": + version "5.51.1" + resolved "https://registry.yarnpkg.com/@ledgerhq/devices/-/devices-5.51.1.tgz#d741a4a5d8f17c2f9d282fd27147e6fe1999edb7" + integrity sha512-4w+P0VkbjzEXC7kv8T1GJ/9AVaP9I6uasMZ/JcdwZBS3qwvKo5A5z9uGhP5c7TvItzcmPb44b5Mw2kT+WjUuAA== + dependencies: + "@ledgerhq/errors" "^5.50.0" + "@ledgerhq/logs" "^5.50.0" + rxjs "6" + semver "^7.3.5" + +"@ledgerhq/devices@^6.11.2": + version "6.11.2" + resolved "https://registry.yarnpkg.com/@ledgerhq/devices/-/devices-6.11.2.tgz#a0413b087f7c07adf2a91d23f84a4580cdbb36fe" + integrity sha512-j2SoG0Ifm7khsPKC2UFhJIoi1oCDCvJemmO+pz7Oc9jx8JRhIGtCfqEIUhL+ZIl0TrIYuApPLaSqJjiFi7VGCQ== + dependencies: + "@ledgerhq/errors" "^6.10.0" + "@ledgerhq/logs" "^6.10.0" + rxjs "6" + semver "^7.3.5" + +"@ledgerhq/errors@^5.50.0": + version "5.50.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/errors/-/errors-5.50.0.tgz#e3a6834cb8c19346efca214c1af84ed28e69dad9" + integrity sha512-gu6aJ/BHuRlpU7kgVpy2vcYk6atjB4iauP2ymF7Gk0ez0Y/6VSMVSJvubeEQN+IV60+OBK0JgeIZG7OiHaw8ow== + +"@ledgerhq/errors@^6.10.0": + version "6.10.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/errors/-/errors-6.10.0.tgz#dda9127b65f653fbb2f74a55e8f0e550d69de6e4" + integrity sha512-fQFnl2VIXh9Yd41lGjReCeK+Q2hwxQJvLZfqHnKqWapTz68NHOv5QcI0OHuZVNEbv0xhgdLhi5b65kgYeQSUVg== + +"@ledgerhq/hw-transport-webusb@^6.11.2": + version "6.11.2" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-webusb/-/hw-transport-webusb-6.11.2.tgz#d190709881e50f8c271efe956705b05e46b4649d" + integrity sha512-ReiYoypXybLQi1ekA+YQqsvbSj0wwTFH7Lg690eUkqZydjDd4lMjpZk7lDczsXp3wxTjE/mdAQ0bpNwZWmPTJw== + dependencies: + "@ledgerhq/devices" "^6.11.2" + "@ledgerhq/errors" "^6.10.0" + "@ledgerhq/hw-transport" "^6.11.2" + "@ledgerhq/logs" "^6.10.0" + +"@ledgerhq/hw-transport@^5.43.0": + version "5.51.1" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport/-/hw-transport-5.51.1.tgz#8dd14a8e58cbee4df0c29eaeef983a79f5f22578" + integrity sha512-6wDYdbWrw9VwHIcoDnqWBaDFyviyjZWv6H9vz9Vyhe4Qd7TIFmbTl/eWs6hZvtZBza9K8y7zD8ChHwRI4s9tSw== + dependencies: + "@ledgerhq/devices" "^5.51.1" + "@ledgerhq/errors" "^5.50.0" + events "^3.3.0" + +"@ledgerhq/hw-transport@^6.11.2": + version "6.11.2" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport/-/hw-transport-6.11.2.tgz#247d0297f96db9d255672a453a1e621ade92096f" + integrity sha512-VZbGfyQ8iFl2W6TUkVmQ3rz8kyOXMbOdFiht3VNlglMe3KBGJWOjW9s/5LhOR348NJs797B1gl4V0Zqmn11kUg== + dependencies: + "@ledgerhq/devices" "^6.11.2" + "@ledgerhq/errors" "^6.10.0" + events "^3.3.0" + +"@ledgerhq/logs@^5.50.0": + version "5.50.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/logs/-/logs-5.50.0.tgz#29c6419e8379d496ab6d0426eadf3c4d100cd186" + integrity sha512-swKHYCOZUGyVt4ge0u8a7AwNcA//h4nx5wIi0sruGye1IJ5Cva0GyK9L2/WdX+kWVTKp92ZiEo1df31lrWGPgA== + +"@ledgerhq/logs@^6.10.0": + version "6.10.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/logs/-/logs-6.10.0.tgz#c012c1ecc1a0e53d50e6af381618dca5268461c1" + integrity sha512-lLseUPEhSFUXYTKj6q7s2O3s2vW2ebgA11vMAlKodXGf5AFw4zUoEbTz9CoFOC9jS6xY4Qr8BmRnxP/odT4Uuw== + "@limestonefi/api@^0.1.3": version "0.1.3" resolved "https://registry.yarnpkg.com/@limestonefi/api/-/api-0.1.3.tgz#404bb050f1042a2458646068e7ab7da8d839a902" @@ -2968,6 +3043,15 @@ resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== +"@zondax/ledger-arweave@^0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@zondax/ledger-arweave/-/ledger-arweave-0.2.0.tgz#f01df6712c107f49e52f2ec02e0e435200413074" + integrity sha512-oK/q7pR/Ly5bwfM8IDCR8YGuMpp3Cqd3x303YKpNQ0BtLjAb0TC1/kkDPVy24TmBvtW926npf3UWMfmjdVFJpw== + dependencies: + "@babel/runtime" "^7.12.13" + "@ledgerhq/hw-transport" "^5.43.0" + arweave "^1.10.6" + abab@^2.0.3, abab@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" @@ -3353,7 +3437,7 @@ arweave-multihost@^0.1.0: arweave "^1.10.15" axios "^0.21.1" -arweave@^1.10.0, arweave@^1.10.11, arweave@^1.10.13, arweave@^1.10.15, arweave@^1.10.16, arweave@^1.10.18, arweave@^1.9.1: +arweave@^1.10.0, arweave@^1.10.11, arweave@^1.10.13, arweave@^1.10.15, arweave@^1.10.16, arweave@^1.10.18, arweave@^1.10.6, arweave@^1.9.1: version "1.10.18" resolved "https://registry.yarnpkg.com/arweave/-/arweave-1.10.18.tgz#3042edd1afa52b7c3caeacf88271758232c7b866" integrity sha512-i3pKBkLtU1Jl9RtJ9A6zgz8QqF5FZy7YA+qGQ9i2Zug171p29FTDZhN+KlRcVBg8sEgd1DKyecCCYJZ3K6Jdfg== @@ -12754,7 +12838,7 @@ run-queue@^1.0.0, run-queue@^1.0.3: dependencies: aproba "^1.1.1" -rxjs@^6.4.0: +rxjs@6, rxjs@^6.4.0: version "6.6.7" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== From 5df5d1a066c77945ddc9e0c54e67c6c68dae0588 Mon Sep 17 00:00:00 2001 From: CDDelta Date: Thu, 18 Nov 2021 17:50:53 +1100 Subject: [PATCH 02/10] add basic Ledger integration --- src/background/api/address.ts | 15 ++-- src/background/api/transaction.ts | 75 ++++++++++--------- src/components/WalletManager.tsx | 10 ++- src/stores/reducers/wallets.ts | 6 +- .../components/WalletManager.module.sass | 3 + src/utils/ledger.ts | 27 ++++++- src/views/Welcome/App.tsx | 10 ++- 7 files changed, 94 insertions(+), 52 deletions(-) diff --git a/src/background/api/address.ts b/src/background/api/address.ts index d303e131b..18a6e91ad 100644 --- a/src/background/api/address.ts +++ b/src/background/api/address.ts @@ -1,6 +1,7 @@ import { JWKInterface } from "arweave/node/lib/wallet"; import { getStoreData } from "../../utils/background"; import { MessageFormat } from "../../utils/messenger"; +import * as ledger from "../../utils/ledger"; /** * APIs for getting the user's address / addresses @@ -33,17 +34,19 @@ export const publicKey = () => const wallets = (await getStoreData())?.["wallets"]; if (wallets) { - const keyfileToDecrypt = wallets.find( - (wallet) => wallet.address === address - )?.keyfile; - - if (keyfileToDecrypt) { - const keyfile: JWKInterface = JSON.parse(atob(keyfileToDecrypt)); + const wallet = wallets.find((wallet) => wallet.address === address); + if (wallet?.type === "local") { + const keyfile: JWKInterface = JSON.parse(atob(wallet.keyfile!)); resolve({ res: true, publicKey: keyfile.n }); + } else if (wallet?.type === "ledger") { + resolve({ + res: true, + publicKey: await ledger.getWalletOwner() + }); } else { resolve({ res: false, diff --git a/src/background/api/transaction.ts b/src/background/api/transaction.ts index d3b650474..17ad66b95 100644 --- a/src/background/api/transaction.ts +++ b/src/background/api/transaction.ts @@ -12,6 +12,7 @@ import { MessageFormat, validateMessage } from "../../utils/messenger"; import { getRealURL } from "../../utils/url"; import { browser } from "webextension-polyfill-ts"; import Transaction, { Tag } from "arweave/web/lib/transaction"; +import * as ledger from "../../utils/ledger"; import Arweave from "arweave"; import manifest from "../../../public/manifest.json"; import axios from "axios"; @@ -54,15 +55,11 @@ export const signTransaction = ( const sign = async () => { const storedKeyfiles = storeData?.["wallets"] ?? [], storedAddress = storeData?.["profile"], - keyfileToDecrypt = storedKeyfiles.find( + wallet = storedKeyfiles.find( (item) => item.address === storedAddress - )?.keyfile; + ); - if ( - storedKeyfiles.length === 0 || - !storedAddress || - !keyfileToDecrypt - ) { + if (storedKeyfiles.length === 0 || !storedAddress || !wallet) { browser.tabs.create({ url: browser.runtime.getURL("/welcome.html") }); return { res: false, @@ -70,11 +67,27 @@ export const signTransaction = ( }; } - const keyfile: JWKInterface = JSON.parse(atob(keyfileToDecrypt)), - decodeTransaction = arweave.transactions.fromRaw({ - ...transaction, - owner: keyfile.n - }); + const signer = + wallet.type === "local" + ? (() => { + const keyfile: JWKInterface = JSON.parse(atob(wallet.keyfile!)); + return { + owner: keyfile.n, + signTx: ( + tx: Transaction, + signatureOptions?: SignatureOptions + ) => arweave.transactions.sign(tx, keyfile, signatureOptions) + }; + })() + : { + owner: await ledger.getWalletOwner(), + signTx: (tx: Transaction) => ledger.signTransaction(tx) + }; + + const userTx = arweave.transactions.fromRaw({ + ...transaction, + owner: signer.owner + }); const arConnectTags = [ { name: "Signing-Client", value: "ArConnect" }, { name: "Signing-Client-Version", value: manifest.version } @@ -83,48 +96,38 @@ export const signTransaction = ( // add some ArConnect tags so the tx can be // identified later for debugging, etc. for (const arcTag of arConnectTags) { - decodeTransaction.addTag(arcTag.name, arcTag.value); + userTx.addTag(arcTag.name, arcTag.value); } // fee multiplication if (feeMultiplier > 1) { - decodeTransaction.reward = ( - +decodeTransaction.reward * feeMultiplier - ).toFixed(0); - price = - parseFloat(transaction.quantity) + - parseFloat(decodeTransaction.reward); + userTx.reward = (+userTx.reward * feeMultiplier).toFixed(0); + price = parseFloat(transaction.quantity) + parseFloat(userTx.reward); } - await arweave.transactions.sign( - decodeTransaction, - keyfile, - signatureOptions - ); + await signer.signTx(userTx, signatureOptions); const feeTarget = await selectVRTHolder(); if (feeTarget) { - const feeTx = await arweave.createTransaction( - { - target: feeTarget, - quantity: await getFeeAmount(storedAddress, arweave), - data: Math.random().toString().slice(-4) - }, - keyfile - ); + const feeTx = await arweave.createTransaction({ + owner: signer.owner, + target: feeTarget, + quantity: await getFeeAmount(storedAddress, arweave), + data: Math.random().toString().slice(-4) + }); feeTx.addTag("App-Name", "ArConnect"); feeTx.addTag("App-Version", manifest.version); feeTx.addTag("Type", "Fee-Transaction"); - feeTx.addTag("Linked-Transaction", decodeTransaction.id); + feeTx.addTag("Linked-Transaction", userTx.id); // fee multiplication if (feeMultiplier > 1) { feeTx.reward = (+feeTx.reward * feeMultiplier).toFixed(0); } - await arweave.transactions.sign(feeTx, keyfile); + await signer.signTx(feeTx); await arweave.transactions.post(feeTx); } @@ -148,10 +151,10 @@ export const signTransaction = ( // transaction signer creates a valid signature // using those as well const returnTransaction = { - ...decodeTransaction, + ...userTx, data: undefined, // only return the arconnect tags - tags: decodeTransaction + tags: userTx .get("tags") // @ts-expect-error .filter( diff --git a/src/components/WalletManager.tsx b/src/components/WalletManager.tsx index 5d381efa3..2829e9f51 100644 --- a/src/components/WalletManager.tsx +++ b/src/components/WalletManager.tsx @@ -1,6 +1,7 @@ import React, { useEffect, useState } from "react"; import { useDispatch, useSelector } from "react-redux"; import { RootState } from "../stores/reducers"; +import { PlugIcon, FileIcon } from "@primer/octicons-react"; import { removeWallet, renameWallet, @@ -275,7 +276,14 @@ export default function WalletManager() { )} -

{formatAddress(wallet.address)}

+

+ {wallet.type === "local" ? ( + + ) : ( + + )} + {formatAddress(wallet.address)} +

{ return interactWithLedger((ledger) => ledger.getAddress()).then( @@ -7,7 +9,26 @@ export async function getWalletAddress(): Promise { ); } -async function interactWithLedger( +export async function getWalletOwner(): Promise { + return interactWithLedger((ledger) => ledger.getAddress()).then( + (res) => res.owner + ); +} + +export async function signTransaction(transaction: Transaction): Promise { + return interactWithLedger(async (ledger) => { + const response = await ledger.sign(transaction); + const txId = await Arweave.crypto.hash(response.signature); + + transaction.setSignature({ + owner: transaction.owner, + signature: Arweave.utils.bufferTob64Url(response.signature), + id: Arweave.utils.bufferTob64Url(txId) + }); + }); +} + +async function interactWithLedger( handler: (ledger: ArweaveApp) => Promise ): Promise { const transport = await TransportWebUSB.create(); @@ -16,6 +37,6 @@ async function interactWithLedger( try { return await handler(ledger); } finally { - transport.close(); + await transport.close(); } } diff --git a/src/views/Welcome/App.tsx b/src/views/Welcome/App.tsx index 58ed16a52..77e195d18 100644 --- a/src/views/Welcome/App.tsx +++ b/src/views/Welcome/App.tsx @@ -143,7 +143,7 @@ export default function App() { keyfile = btoa(JSON.stringify(keyfilesToLoad[i])), name = `Account ${i + 1 + walletsStore.length}`; - wallets.push({ address, keyfile, name }); + wallets.push({ name, address, type: "local", keyfile }); } dispatch(setWallets([...walletsStore, ...wallets])); @@ -174,9 +174,10 @@ export default function App() { setWallets([ ...walletsStore, { - keyfile: encryptedKeyfile, + name: `Account ${walletsStore.length + 1}`, address, - name: `Account ${walletsStore.length + 1}` + type: "local", + keyfile: encryptedKeyfile } ]) ); @@ -191,8 +192,9 @@ export default function App() { setWallets([ ...walletsStore, { + name: "Ledger", address, - name: `Account ${walletsStore.length + 1}` + type: "ledger" } ]) ); From b315a3a90d11adff0b80e9d4cce07eb0742bf4f0 Mon Sep 17 00:00:00 2001 From: CDDelta Date: Thu, 18 Nov 2021 23:27:41 +1100 Subject: [PATCH 03/10] resolve with error when using crypto APIs with Ledger --- src/background/api/encryption.ts | 218 ++++++++++++------------------- 1 file changed, 85 insertions(+), 133 deletions(-) diff --git a/src/background/api/encryption.ts b/src/background/api/encryption.ts index caa90172f..198020827 100644 --- a/src/background/api/encryption.ts +++ b/src/background/api/encryption.ts @@ -1,118 +1,94 @@ import { MessageFormat } from "../../utils/messenger"; -import { - authenticateUser, - getArweaveConfig, - getStoreData -} from "../../utils/background"; +import { authenticateUser, getStoreData } from "../../utils/background"; import { JWKInterface } from "arweave/node/lib/wallet"; import Arweave from "arweave"; +import { Wallet } from "../../stores/reducers/wallets"; // encrypt data using the user's keyfile -export const encrypt = (message: MessageFormat, tabURL: string) => - new Promise>(async (resolve, _) => { - if (!message.data) - return resolve({ - res: false, - message: "No data submitted" - }); - - if (!message.options) - return resolve({ - res: false, - message: "No options submitted" - }); - - try { - await authenticateUser(message.type, tabURL); - - resolve({ - res: true, - data: await doEncrypt(message), - message: "Success" - }); - } catch { - resolve({ - res: false, - message: "Error encrypting data" - }); - } +export async function encrypt(message: MessageFormat, tabURL: string) { + return handleMessage(message, tabURL, async (message, wallet) => { + const keyfile: JWKInterface = JSON.parse(atob(wallet.keyfile!)); + return { + res: true, + data: await doEncrypt(message, keyfile), + message: "Success" + }; }); +} -export const decrypt = (message: MessageFormat, tabURL: string) => - new Promise>(async (resolve, _) => { - if (!message.data) - return resolve({ - res: false, - message: "No data submitted" - }); - - if (!message.options) - return resolve({ - res: false, - message: "No options submitted" - }); - - try { - await authenticateUser(message.type, tabURL); - - resolve({ - res: true, - data: await doDecrypt(message), - message: "Success" - }); - } catch { - resolve({ - res: false, - message: "Error decrypting data" - }); - } +export async function decrypt(message: MessageFormat, tabURL: string) { + return handleMessage(message, tabURL, async (message, wallet) => { + const keyfile: JWKInterface = JSON.parse(atob(wallet.keyfile!)); + return { + res: true, + data: await doDecrypt(message, keyfile), + message: "Success" + }; }); +} -export const signature = (message: MessageFormat, tabURL: string) => - new Promise>(async (resolve, _) => { - if (!message.data) - return resolve({ - res: false, - message: "No data submitted" - }); +export async function signature(message: MessageFormat, tabURL: string) { + return handleMessage(message, tabURL, async (message, wallet) => { + const keyfile: JWKInterface = JSON.parse(atob(wallet.keyfile!)); + return { + res: true, + data: await doSignature(message, keyfile), + message: "Success" + }; + }); +} - if (!message.options) - return resolve({ +async function handleMessage( + message: MessageFormat, + tabURL: string, + handler: ( + message: MessageFormat, + wallet: Wallet + ) => Promise> +): Promise> { + if (!message.data) + return { + res: false, + message: "No data submitted" + }; + + if (!message.options) + return { + res: false, + message: "No options submitted" + }; + + try { + await authenticateUser(message.type, tabURL); + + const storeData = await getStoreData(), + wallets = storeData?.["wallets"], + storedAddress = storeData?.["profile"], + wallet = wallets?.find((item) => item.address === storedAddress); + + if (!wallets || !storedAddress || !wallet) + throw new Error("No wallets added"); + + if (wallet.type === "local") { + return handler(message, wallet); + } else if (wallet.type === "ledger") { + return { res: false, - message: "No options submitted" - }); - - try { - await authenticateUser(message.type, tabURL); - - resolve({ - res: true, - data: await doSignature(message), - message: "Success" - }); - } catch (e: any) { - resolve({ - res: false, - message: e.message || "Error signing data" - }); + message: "Action not supported" + }; + } else { + throw new Error("Unknown wallet type"); } - }); + } catch (e: any) { + return { + res: false, + message: e.message || "Error signing data" + }; + } +} -async function doEncrypt(message: MessageFormat) { - const arweave = new Arweave(await getArweaveConfig()), - storeData = await getStoreData(), - storedKeyfiles = storeData?.["wallets"], - storedAddress = storeData?.["profile"], - keyfileToDecrypt = storedKeyfiles?.find( - (item) => item.address === storedAddress - )?.keyfile; - - // this should not happen, we already check for wallets in background.ts - if (!storedKeyfiles || !storedAddress || !keyfileToDecrypt) - throw new Error("No wallets added"); - - const keyfile: JWKInterface = JSON.parse(atob(keyfileToDecrypt)), - obj = { +async function doEncrypt(message: MessageFormat, keyfile: JWKInterface) { + const obj = { kty: "RSA", e: "AQAB", n: keyfile.n, @@ -141,30 +117,18 @@ async function doEncrypt(message: MessageFormat) { const keyBuf = array; - const encryptedData = await arweave.crypto.encrypt(dataBuf, keyBuf), + const encryptedData = await Arweave.crypto.encrypt(dataBuf, keyBuf), encryptedKey = await crypto.subtle.encrypt( { name: message.options.algorithm }, key, keyBuf ); - return arweave.utils.concatBuffers([encryptedKey, encryptedData]); + return Arweave.utils.concatBuffers([encryptedKey, encryptedData]); } -async function doDecrypt(message: MessageFormat) { - const arweave = new Arweave(await getArweaveConfig()), - storeData = await getStoreData(), - storedKeyfiles = storeData?.["wallets"], - storedAddress = storeData?.["profile"], - keyfileToDecrypt = storedKeyfiles?.find( - (item) => item.address === storedAddress - )?.keyfile; - - if (!storedKeyfiles || !storedAddress || !keyfileToDecrypt) - throw new Error("No wallets added"); - - const keyfile: JWKInterface = JSON.parse(atob(keyfileToDecrypt)), - obj = { +async function doDecrypt(message: MessageFormat, keyfile: JWKInterface) { + const obj = { ...keyfile, alg: "RSA-OAEP-256", ext: true @@ -195,27 +159,15 @@ async function doDecrypt(message: MessageFormat) { encryptedKey ); - const res = await arweave.crypto.decrypt( + const res = await Arweave.crypto.decrypt( encryptedData, new Uint8Array(symmetricKey) ); - return arweave.utils.bufferToString(res).split(message.options.salt)[0]; + return Arweave.utils.bufferToString(res).split(message.options.salt)[0]; } -async function doSignature(message: MessageFormat) { - const storeData = await getStoreData(), - storedKeyfiles = storeData?.["wallets"], - storedAddress = storeData?.["profile"], - keyfileToDecrypt = storedKeyfiles?.find( - (item) => item.address === storedAddress - )?.keyfile; - - if (!storedKeyfiles || !storedAddress || !keyfileToDecrypt) - throw new Error("No wallets added"); - - const keyfile: JWKInterface = JSON.parse(atob(keyfileToDecrypt)); - +async function doSignature(message: MessageFormat, keyfile: JWKInterface) { const cryptoKey = await crypto.subtle.importKey( "jwk", keyfile, From 73472cbdda338832fc7749c0d3dc3c6ab4f4f79c Mon Sep 17 00:00:00 2001 From: CDDelta Date: Fri, 19 Nov 2021 00:04:26 +1100 Subject: [PATCH 04/10] prevent user from connecting multiple Ledger wallets --- src/components/WalletManager.tsx | 1 + src/stores/reducers/wallets.ts | 21 +++++++++++++++++-- .../components/WalletManager.module.sass | 2 +- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/components/WalletManager.tsx b/src/components/WalletManager.tsx index 2829e9f51..ce9b40979 100644 --- a/src/components/WalletManager.tsx +++ b/src/components/WalletManager.tsx @@ -261,6 +261,7 @@ export default function WalletManager() { style={{ width: `${walletNameSizes[wallet.address] ?? 0}px` }} + disabled={wallet.type === "ledger"} /> {verifiedAddresses.includes(wallet.address) && ( address === action.payload.wallet?.address) ) break; - return [...state, action.payload.wallet]; + const wallet = action.payload.wallet; + + if (wallet.type === "local") { + return [...state, action.payload.wallet]; + } else if (wallet.type === "ledger") { + // Replace existing Ledger wallet. + return [ + ...state.filter((wallet) => wallet.type === "local"), + action.payload.wallet + ]; + } else { + throw Error("Unknown wallet type"); + } case "REMOVE_WALLET": if (!action.payload.address) break; return state.filter(({ address }) => address !== action.payload.address); case "RENAME_WALLET": - if (!action.payload.address || action.payload.name === undefined) break; + if ( + !action.payload.address || + action.payload.name === undefined || + action.payload.wallet?.type === "ledger" + ) + break; return state.map((wallet) => wallet.address === action.payload.address ? { ...wallet, name: action.payload.name ?? "" } diff --git a/src/styles/components/WalletManager.module.sass b/src/styles/components/WalletManager.module.sass index 968f93f03..32f2e1195 100644 --- a/src/styles/components/WalletManager.module.sass +++ b/src/styles/components/WalletManager.module.sass @@ -157,7 +157,7 @@ @media (prefers-color-scheme: dark) color: #fff - &:hover + &:hover:not([disabled]) border-bottom-color: rgba(#000, .5) @media (prefers-color-scheme: dark) From f723ab733e8465061ff86417984ce513f887e8e9 Mon Sep 17 00:00:00 2001 From: CDDelta Date: Fri, 19 Nov 2021 17:49:30 +1100 Subject: [PATCH 05/10] handle Ledger device errors --- src/utils/ledger.ts | 23 +++++++++++++++++++---- src/views/Welcome/App.tsx | 35 +++++++++++++++++++++-------------- 2 files changed, 40 insertions(+), 18 deletions(-) diff --git a/src/utils/ledger.ts b/src/utils/ledger.ts index 8b4d509fb..20098b5b2 100644 --- a/src/utils/ledger.ts +++ b/src/utils/ledger.ts @@ -1,8 +1,16 @@ import TransportWebUSB from "@ledgerhq/hw-transport-webusb"; -import ArweaveApp from "@zondax/ledger-arweave"; +import ArweaveApp, { ResponseBase } from "@zondax/ledger-arweave"; import Transaction from "arweave/web/lib/transaction"; import Arweave from "arweave/web/common"; +export function isSupported(): boolean { + const nav = navigator as any; + const webUSBSupported = + nav && nav.usb && typeof nav.usb.getDevices === "function"; + + return webUSBSupported; +} + export async function getWalletAddress(): Promise { return interactWithLedger((ledger) => ledger.getAddress()).then( (res) => res.address @@ -16,7 +24,7 @@ export async function getWalletOwner(): Promise { } export async function signTransaction(transaction: Transaction): Promise { - return interactWithLedger(async (ledger) => { + await interactWithLedger(async (ledger) => { const response = await ledger.sign(transaction); const txId = await Arweave.crypto.hash(response.signature); @@ -25,17 +33,24 @@ export async function signTransaction(transaction: Transaction): Promise { signature: Arweave.utils.bufferTob64Url(response.signature), id: Arweave.utils.bufferTob64Url(txId) }); + + return response; }); } -async function interactWithLedger( +async function interactWithLedger( handler: (ledger: ArweaveApp) => Promise ): Promise { const transport = await TransportWebUSB.create(); const ledger = new ArweaveApp(transport); try { - return await handler(ledger); + const response = await handler(ledger); + if (response.returnCode === ArweaveApp.ErrorCode.NoError) { + return response; + } else { + throw Error(`Error [${response.returnCode}] ${response.errorMessage}`); + } } finally { await transport.close(); } diff --git a/src/views/Welcome/App.tsx b/src/views/Welcome/App.tsx index 23225338d..0d0c9554a 100644 --- a/src/views/Welcome/App.tsx +++ b/src/views/Welcome/App.tsx @@ -187,20 +187,25 @@ export default function App() { } async function connectLedger() { - const address = await ledger.getWalletAddress(); + try { + const address = await ledger.getWalletAddress(); + + dispatch( + setWallets([ + ...walletsStore, + { + name: "Ledger", + address, + type: "ledger" + } + ]) + ); + dispatch(switchProfile(address)); - dispatch( - setWallets([ - ...walletsStore, - { - name: "Ledger", - address, - type: "ledger" - } - ]) - ); - dispatch(switchProfile(address)); - setLoading(false); + setToast({ text: "Ledger connected", type: "success" }); + } catch { + setToast({ text: "Could not connect Ledger", type: "error" }); + } } function downloadSeedWallet() { @@ -342,7 +347,9 @@ export default function App() { - + {ledger.isSupported() && ( + + )}
)) || ( <> From f91796aab572916584eea4e6216115e02cd81f8c Mon Sep 17 00:00:00 2001 From: CDDelta Date: Fri, 19 Nov 2021 18:09:40 +1100 Subject: [PATCH 06/10] filter Ledger wallet from config export --- src/utils/background.ts | 45 +++++++++++++++++++---------- src/views/Popup/routes/Settings.tsx | 13 +++++---- 2 files changed, 37 insertions(+), 21 deletions(-) diff --git a/src/utils/background.ts b/src/utils/background.ts index 710aa59d6..eae975d3a 100644 --- a/src/utils/background.ts +++ b/src/utils/background.ts @@ -76,6 +76,32 @@ export async function getPermissions(url: string): Promise { export type StoreData = Partial; +export async function parseStoreData( + encodedStoreData: string +): Promise { + const parseRoot: StoreData = JSON.parse(encodedStoreData ?? "{}"); + const parsedData: StoreData = {}; + + for (const key in parseRoot) { + // @ts-ignore + parsedData[key] = JSON.parse(parseRoot[key]); + } + + return parsedData; +} + +export async function encodeStoreData(storeData: StoreData): Promise { + // store data, but with stringified values + const encodedData: { [key: string]: string } = {}; + + for (const reducer in storeData) { + // @ts-ignore + encodedData[reducer] = JSON.stringify(storeData[reducer]); + } + + return JSON.stringify(encodedData); +} + /** * Get store data * @@ -83,15 +109,10 @@ export type StoreData = Partial; */ export async function getStoreData(): Promise { const data = (await browser.storage.local.get("persist:root"))?.[ - "persist:root" - ], - parseRoot: StoreData = JSON.parse(data ?? "{}"); - - let parsedData: StoreData = {}; - // @ts-ignore - for (const key in parseRoot) parsedData[key] = JSON.parse(parseRoot[key]); + "persist:root" + ]; - return parsedData; + return parseStoreData(data); } /** @@ -101,13 +122,7 @@ export async function getStoreData(): Promise { */ export async function setStoreData(updatedData: StoreData) { const data = { ...(await getStoreData()), ...updatedData }; - // store data, but with stringified values - let encodedData: { [key: string]: string } = {}; - - for (const reducer in data) { - // @ts-ignore - encodedData[reducer] = JSON.stringify(data[reducer]); - } + const encodedData = await encodeStoreData(data); await browser.storage.local.set({ "persist:root": JSON.stringify(encodedData) diff --git a/src/views/Popup/routes/Settings.tsx b/src/views/Popup/routes/Settings.tsx index 69c1494c6..670b70d64 100644 --- a/src/views/Popup/routes/Settings.tsx +++ b/src/views/Popup/routes/Settings.tsx @@ -51,6 +51,7 @@ import Arweave from "arweave"; import manifest from "../../../../public/manifest.json"; import SubPageTopStyles from "../../../styles/components/SubPageTop.module.sass"; import styles from "../../../styles/views/Popup/settings.module.sass"; +import { encodeStoreData, getStoreData } from "../../../utils/background"; export default function Settings() { const [setting, setCurrSetting] = useState< @@ -221,14 +222,14 @@ export default function Settings() { async function generateConfigFile() { const password = configPasswordInput.state; - const storedData = (await browser.storage.local.get("persist:root"))?.[ - "persist:root" - ]; + const storeData = await getStoreData(); - if (!storedData || storedData === "") - return setToast({ text: "Could not get stored data", type: "error" }); + storeData.wallets = storeData.wallets?.filter( + (wallet) => wallet.type === "local" + ); - const encrypted = CryptoES.AES.encrypt(storedData, password); + const encodedStoreData = await encodeStoreData(storeData); + const encrypted = CryptoES.AES.encrypt(encodedStoreData, password); // create element that downloads the virtual file const el = document.createElement("a"); From 45a2035c6a5c4453ec47580acaabe59b34c87287 Mon Sep 17 00:00:00 2001 From: CDDelta Date: Fri, 19 Nov 2021 18:54:19 +1100 Subject: [PATCH 07/10] fix store data double encoding --- src/utils/background.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/background.ts b/src/utils/background.ts index eae975d3a..9d0e9fc2b 100644 --- a/src/utils/background.ts +++ b/src/utils/background.ts @@ -125,7 +125,7 @@ export async function setStoreData(updatedData: StoreData) { const encodedData = await encodeStoreData(data); await browser.storage.local.set({ - "persist:root": JSON.stringify(encodedData) + "persist:root": encodedData }); } From 721a65f33277d45771cb133a3fb99ce648b3dac2 Mon Sep 17 00:00:00 2001 From: CDDelta Date: Wed, 24 Nov 2021 11:15:44 +1100 Subject: [PATCH 08/10] allow multiple Ledger wallet connections --- src/background/api/address.ts | 9 +++++- src/background/api/transaction.ts | 9 +++++- src/components/WalletManager.tsx | 1 - src/stores/reducers/wallets.ts | 28 ++++--------------- .../components/WalletManager.module.sass | 2 +- src/utils/ledger.ts | 17 +++++++---- src/views/Welcome/App.tsx | 13 +++++++-- 7 files changed, 44 insertions(+), 35 deletions(-) diff --git a/src/background/api/address.ts b/src/background/api/address.ts index 18a6e91ad..56b004e6a 100644 --- a/src/background/api/address.ts +++ b/src/background/api/address.ts @@ -43,9 +43,16 @@ export const publicKey = () => publicKey: keyfile.n }); } else if (wallet?.type === "ledger") { + const { address: ledgerAddress, owner } = + await ledger.getWalletInfo(); + + if (ledgerAddress !== address) { + throw new Error("Ledger address mismatch"); + } + resolve({ res: true, - publicKey: await ledger.getWalletOwner() + publicKey: owner }); } else { resolve({ diff --git a/src/background/api/transaction.ts b/src/background/api/transaction.ts index 17ad66b95..fb2a0496b 100644 --- a/src/background/api/transaction.ts +++ b/src/background/api/transaction.ts @@ -81,7 +81,14 @@ export const signTransaction = ( })() : { owner: await ledger.getWalletOwner(), - signTx: (tx: Transaction) => ledger.signTransaction(tx) + signTx: async (tx: Transaction) => { + const ledgerAddress = await ledger.getWalletAddress(); + if (ledgerAddress !== wallet.address) { + throw new Error("Ledger address mismatch"); + } + + await ledger.signTransaction(tx); + } }; const userTx = arweave.transactions.fromRaw({ diff --git a/src/components/WalletManager.tsx b/src/components/WalletManager.tsx index ce9b40979..2829e9f51 100644 --- a/src/components/WalletManager.tsx +++ b/src/components/WalletManager.tsx @@ -261,7 +261,6 @@ export default function WalletManager() { style={{ width: `${walletNameSizes[wallet.address] ?? 0}px` }} - disabled={wallet.type === "ledger"} /> {verifiedAddresses.includes(wallet.address) && ( address === action.payload.wallet?.address) - ) - break; - const wallet = action.payload.wallet; - if (wallet.type === "local") { - return [...state, action.payload.wallet]; - } else if (wallet.type === "ledger") { - // Replace existing Ledger wallet. - return [ - ...state.filter((wallet) => wallet.type === "local"), - action.payload.wallet - ]; - } else { - throw Error("Unknown wallet type"); - } + if (!wallet || state.find(({ address }) => address === wallet.address)) + break; + + return [...state, wallet]; case "REMOVE_WALLET": if (!action.payload.address) break; return state.filter(({ address }) => address !== action.payload.address); case "RENAME_WALLET": - if ( - !action.payload.address || - action.payload.name === undefined || - action.payload.wallet?.type === "ledger" - ) - break; + if (!action.payload.address || action.payload.name === undefined) break; return state.map((wallet) => wallet.address === action.payload.address ? { ...wallet, name: action.payload.name ?? "" } diff --git a/src/styles/components/WalletManager.module.sass b/src/styles/components/WalletManager.module.sass index 32f2e1195..968f93f03 100644 --- a/src/styles/components/WalletManager.module.sass +++ b/src/styles/components/WalletManager.module.sass @@ -157,7 +157,7 @@ @media (prefers-color-scheme: dark) color: #fff - &:hover:not([disabled]) + &:hover border-bottom-color: rgba(#000, .5) @media (prefers-color-scheme: dark) diff --git a/src/utils/ledger.ts b/src/utils/ledger.ts index 20098b5b2..ade961dd0 100644 --- a/src/utils/ledger.ts +++ b/src/utils/ledger.ts @@ -3,6 +3,11 @@ import ArweaveApp, { ResponseBase } from "@zondax/ledger-arweave"; import Transaction from "arweave/web/lib/transaction"; import Arweave from "arweave/web/common"; +export interface LedgerWalletInfo { + owner: string; + address: string; +} + export function isSupported(): boolean { const nav = navigator as any; const webUSBSupported = @@ -11,16 +16,16 @@ export function isSupported(): boolean { return webUSBSupported; } +export async function getWalletInfo(): Promise { + return interactWithLedger((ledger) => ledger.getAddress()); +} + export async function getWalletAddress(): Promise { - return interactWithLedger((ledger) => ledger.getAddress()).then( - (res) => res.address - ); + return getWalletInfo().then((res) => res.address); } export async function getWalletOwner(): Promise { - return interactWithLedger((ledger) => ledger.getAddress()).then( - (res) => res.owner - ); + return getWalletInfo().then((res) => res.owner); } export async function signTransaction(transaction: Transaction): Promise { diff --git a/src/views/Welcome/App.tsx b/src/views/Welcome/App.tsx index 0d0c9554a..802ce9858 100644 --- a/src/views/Welcome/App.tsx +++ b/src/views/Welcome/App.tsx @@ -171,11 +171,16 @@ export default function App() { setSeed(mnemonic); setSeedKeyfile({ address, keyfile }); seedModal.setVisible(true); + + const localWalletCount = walletsStore.reduce((count, wallet) => { + return wallet.type === "local" ? count + 1 : count; + }, 0); + dispatch( setWallets([ ...walletsStore, { - name: `Account ${walletsStore.length + 1}`, + name: `Account ${localWalletCount + 1}`, address, type: "local", keyfile: encryptedKeyfile @@ -190,11 +195,15 @@ export default function App() { try { const address = await ledger.getWalletAddress(); + const ledgerWalletCount = walletsStore.reduce((count, wallet) => { + return wallet.type === "ledger" ? count + 1 : count; + }, 0); + dispatch( setWallets([ ...walletsStore, { - name: "Ledger", + name: `Ledger ${ledgerWalletCount + 1}`, address, type: "ledger" } From 166961e256a9065473da19ea189a683be9770f27 Mon Sep 17 00:00:00 2001 From: CDDelta Date: Wed, 24 Nov 2021 12:19:07 +1100 Subject: [PATCH 09/10] throw error when trying to sign tx with options via Ledger --- src/background/api/transaction.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/background/api/transaction.ts b/src/background/api/transaction.ts index fb2a0496b..77d3f4f59 100644 --- a/src/background/api/transaction.ts +++ b/src/background/api/transaction.ts @@ -81,7 +81,16 @@ export const signTransaction = ( })() : { owner: await ledger.getWalletOwner(), - signTx: async (tx: Transaction) => { + signTx: async ( + tx: Transaction, + signatureOptions?: SignatureOptions + ) => { + if (signatureOptions) { + throw new Error( + "Ledger does not support signature options" + ); + } + const ledgerAddress = await ledger.getWalletAddress(); if (ledgerAddress !== wallet.address) { throw new Error("Ledger address mismatch"); From b091fcff2c61d6065dcb9d008578dde81474271e Mon Sep 17 00:00:00 2001 From: CDDelta Date: Mon, 6 Dec 2021 16:24:57 +1100 Subject: [PATCH 10/10] update Ledger SDK dependency --- package.json | 2 +- src/background/api/address.ts | 22 ++++++++------ src/background/api/encryption.ts | 4 +-- src/utils/background.ts | 6 ++++ yarn.lock | 49 +++++++------------------------- 5 files changed, 32 insertions(+), 51 deletions(-) diff --git a/package.json b/package.json index 7ab40fb58..a10e786a1 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "@primer/octicons-react": "^16.0.0", "@verto/js": "^0.0.0-alpha.19", "@verto/lib": "^0.10.4", - "@zondax/ledger-arweave": "^0.2.0", + "@zondax/ledger-arweave": "^1.0.0", "ar-gql": "^0.0.6", "arverify": "^0.0.11", "arweave": "^1.10.18", diff --git a/src/background/api/address.ts b/src/background/api/address.ts index 56b004e6a..1afd2aad6 100644 --- a/src/background/api/address.ts +++ b/src/background/api/address.ts @@ -30,19 +30,28 @@ export const activeAddress = () => export const publicKey = () => new Promise>(async (resolve, _) => { try { - const address = (await getStoreData())["profile"]; - const wallets = (await getStoreData())?.["wallets"]; + const store = await getStoreData(); + const address = store["profile"]; + const wallets = store["wallets"]; if (wallets) { const wallet = wallets.find((wallet) => wallet.address === address); - if (wallet?.type === "local") { + if (!wallet) { + resolve({ + res: false, + message: "No wallets added" + }); + return; + } + + if (wallet.type === "local") { const keyfile: JWKInterface = JSON.parse(atob(wallet.keyfile!)); resolve({ res: true, publicKey: keyfile.n }); - } else if (wallet?.type === "ledger") { + } else if (wallet.type === "ledger") { const { address: ledgerAddress, owner } = await ledger.getWalletInfo(); @@ -54,11 +63,6 @@ export const publicKey = () => res: true, publicKey: owner }); - } else { - resolve({ - res: false, - message: "No wallets added" - }); } } else { resolve({ diff --git a/src/background/api/encryption.ts b/src/background/api/encryption.ts index 198020827..6dcee8f29 100644 --- a/src/background/api/encryption.ts +++ b/src/background/api/encryption.ts @@ -62,8 +62,8 @@ async function handleMessage( await authenticateUser(message.type, tabURL); const storeData = await getStoreData(), - wallets = storeData?.["wallets"], - storedAddress = storeData?.["profile"], + wallets = storeData.wallets, + storedAddress = storeData.profile, wallet = wallets?.find((item) => item.address === storedAddress); if (!wallets || !storedAddress || !wallet) diff --git a/src/utils/background.ts b/src/utils/background.ts index 9d0e9fc2b..ba8629722 100644 --- a/src/utils/background.ts +++ b/src/utils/background.ts @@ -87,6 +87,12 @@ export async function parseStoreData( parsedData[key] = JSON.parse(parseRoot[key]); } + // Hack to default previously added wallets to being of the `local` type. + // Ideally we would have a data store that can perform schema migrations but this will do for now. + for (const wallet of parsedData.wallets ?? []) { + wallet.type ??= "local"; + } + return parsedData; } diff --git a/yarn.lock b/yarn.lock index 1900abaf5..a676267e1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1164,7 +1164,7 @@ dependencies: regenerator-runtime "^0.13.4" -"@babel/runtime@^7.12.13": +"@babel/runtime@^7.15.4": version "7.16.3" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.3.tgz#b86f0db02a04187a3c17caa77de69840165d42d5" integrity sha512-WBwekcqacdY2e9AF/Q7WLFUWmdJGJTkbjqTjoMDgXkVZ3ZRUvOPsLb5KdwISoQVsbP+DQzVZW4Zhci0DvpbNTQ== @@ -2028,16 +2028,6 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" -"@ledgerhq/devices@^5.51.1": - version "5.51.1" - resolved "https://registry.yarnpkg.com/@ledgerhq/devices/-/devices-5.51.1.tgz#d741a4a5d8f17c2f9d282fd27147e6fe1999edb7" - integrity sha512-4w+P0VkbjzEXC7kv8T1GJ/9AVaP9I6uasMZ/JcdwZBS3qwvKo5A5z9uGhP5c7TvItzcmPb44b5Mw2kT+WjUuAA== - dependencies: - "@ledgerhq/errors" "^5.50.0" - "@ledgerhq/logs" "^5.50.0" - rxjs "6" - semver "^7.3.5" - "@ledgerhq/devices@^6.11.2": version "6.11.2" resolved "https://registry.yarnpkg.com/@ledgerhq/devices/-/devices-6.11.2.tgz#a0413b087f7c07adf2a91d23f84a4580cdbb36fe" @@ -2048,11 +2038,6 @@ rxjs "6" semver "^7.3.5" -"@ledgerhq/errors@^5.50.0": - version "5.50.0" - resolved "https://registry.yarnpkg.com/@ledgerhq/errors/-/errors-5.50.0.tgz#e3a6834cb8c19346efca214c1af84ed28e69dad9" - integrity sha512-gu6aJ/BHuRlpU7kgVpy2vcYk6atjB4iauP2ymF7Gk0ez0Y/6VSMVSJvubeEQN+IV60+OBK0JgeIZG7OiHaw8ow== - "@ledgerhq/errors@^6.10.0": version "6.10.0" resolved "https://registry.yarnpkg.com/@ledgerhq/errors/-/errors-6.10.0.tgz#dda9127b65f653fbb2f74a55e8f0e550d69de6e4" @@ -2068,16 +2053,7 @@ "@ledgerhq/hw-transport" "^6.11.2" "@ledgerhq/logs" "^6.10.0" -"@ledgerhq/hw-transport@^5.43.0": - version "5.51.1" - resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport/-/hw-transport-5.51.1.tgz#8dd14a8e58cbee4df0c29eaeef983a79f5f22578" - integrity sha512-6wDYdbWrw9VwHIcoDnqWBaDFyviyjZWv6H9vz9Vyhe4Qd7TIFmbTl/eWs6hZvtZBza9K8y7zD8ChHwRI4s9tSw== - dependencies: - "@ledgerhq/devices" "^5.51.1" - "@ledgerhq/errors" "^5.50.0" - events "^3.3.0" - -"@ledgerhq/hw-transport@^6.11.2": +"@ledgerhq/hw-transport@^6.11.2", "@ledgerhq/hw-transport@^6.7.0": version "6.11.2" resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport/-/hw-transport-6.11.2.tgz#247d0297f96db9d255672a453a1e621ade92096f" integrity sha512-VZbGfyQ8iFl2W6TUkVmQ3rz8kyOXMbOdFiht3VNlglMe3KBGJWOjW9s/5LhOR348NJs797B1gl4V0Zqmn11kUg== @@ -2086,11 +2062,6 @@ "@ledgerhq/errors" "^6.10.0" events "^3.3.0" -"@ledgerhq/logs@^5.50.0": - version "5.50.0" - resolved "https://registry.yarnpkg.com/@ledgerhq/logs/-/logs-5.50.0.tgz#29c6419e8379d496ab6d0426eadf3c4d100cd186" - integrity sha512-swKHYCOZUGyVt4ge0u8a7AwNcA//h4nx5wIi0sruGye1IJ5Cva0GyK9L2/WdX+kWVTKp92ZiEo1df31lrWGPgA== - "@ledgerhq/logs@^6.10.0": version "6.10.0" resolved "https://registry.yarnpkg.com/@ledgerhq/logs/-/logs-6.10.0.tgz#c012c1ecc1a0e53d50e6af381618dca5268461c1" @@ -3043,14 +3014,14 @@ resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== -"@zondax/ledger-arweave@^0.2.0": - version "0.2.0" - resolved "https://registry.yarnpkg.com/@zondax/ledger-arweave/-/ledger-arweave-0.2.0.tgz#f01df6712c107f49e52f2ec02e0e435200413074" - integrity sha512-oK/q7pR/Ly5bwfM8IDCR8YGuMpp3Cqd3x303YKpNQ0BtLjAb0TC1/kkDPVy24TmBvtW926npf3UWMfmjdVFJpw== +"@zondax/ledger-arweave@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@zondax/ledger-arweave/-/ledger-arweave-1.0.0.tgz#530da1a658d10d20b398ed89eeb74ae821e9c47c" + integrity sha512-U1vVYfwtszNCHXZHwJpLqHbhesGJ/LhPjDQwcmmv1l8XBumkKW2k/urkz7ShM8YWkLJbRuv/Nu4dTIcq1k2aSQ== dependencies: - "@babel/runtime" "^7.12.13" - "@ledgerhq/hw-transport" "^5.43.0" - arweave "^1.10.6" + "@babel/runtime" "^7.15.4" + "@ledgerhq/hw-transport" "^6.7.0" + arweave "^1.10.17" abab@^2.0.3, abab@^2.0.5: version "2.0.5" @@ -3437,7 +3408,7 @@ arweave-multihost@^0.1.0: arweave "^1.10.15" axios "^0.21.1" -arweave@^1.10.0, arweave@^1.10.11, arweave@^1.10.13, arweave@^1.10.15, arweave@^1.10.16, arweave@^1.10.18, arweave@^1.10.6, arweave@^1.9.1: +arweave@^1.10.0, arweave@^1.10.11, arweave@^1.10.13, arweave@^1.10.15, arweave@^1.10.16, arweave@^1.10.17, arweave@^1.10.18, arweave@^1.9.1: version "1.10.18" resolved "https://registry.yarnpkg.com/arweave/-/arweave-1.10.18.tgz#3042edd1afa52b7c3caeacf88271758232c7b866" integrity sha512-i3pKBkLtU1Jl9RtJ9A6zgz8QqF5FZy7YA+qGQ9i2Zug171p29FTDZhN+KlRcVBg8sEgd1DKyecCCYJZ3K6Jdfg==