From 0cb3228c311ac11c3bd6b8b26dbec58d1a6f07de Mon Sep 17 00:00:00 2001 From: abefernan <44572727+abefernan@users.noreply.github.com> Date: Wed, 19 Jun 2024 16:58:04 +0200 Subject: [PATCH 1/5] Tweak types --- types/{index.ts => db.ts} | 16 ---------------- 1 file changed, 16 deletions(-) rename types/{index.ts => db.ts} (71%) diff --git a/types/index.ts b/types/db.ts similarity index 71% rename from types/index.ts rename to types/db.ts index 4c28749..64cfd39 100644 --- a/types/index.ts +++ b/types/db.ts @@ -1,12 +1,5 @@ import { StdFee } from "@cosmjs/amino"; import { EncodeObject } from "@cosmjs/proto-signing"; -import { Keplr } from "@keplr-wallet/types"; - -declare global { - interface Window { - keplr: Keplr; - } -} export interface DbSignature { bodyBytes: string; @@ -42,12 +35,3 @@ export type DbNonce = { readonly address: string; readonly nonce: number; }; - -export interface WalletAccount { - address?: Uint8Array; - pubKey: Uint8Array; - algo: string; - bech32Address: string; - isNanoLedger?: boolean; - name?: string; -} From b3e82a0c9dfa8745a120ce25c08e2ae5c84b6cce Mon Sep 17 00:00:00 2001 From: abefernan <44572727+abefernan@users.noreply.github.com> Date: Wed, 19 Jun 2024 17:00:27 +0200 Subject: [PATCH 2/5] Adapt types imports --- components/dataViews/ListMultisigTxs.tsx | 2 +- components/dataViews/ListUserMultisigs.tsx | 2 +- components/dataViews/ThresholdInfo.tsx | 2 +- components/dataViews/TransactionInfo/index.tsx | 4 ++-- components/forms/CreateTxForm/index.tsx | 4 ++-- components/forms/TransactionSigning.tsx | 1 + lib/graphqlHelpers.ts | 2 +- lib/txMsgHelpers.ts | 4 ++-- pages/[chainName]/[address]/transaction/[transactionID].tsx | 4 ++-- 9 files changed, 13 insertions(+), 12 deletions(-) diff --git a/components/dataViews/ListMultisigTxs.tsx b/components/dataViews/ListMultisigTxs.tsx index 7f14506..9105cf1 100644 --- a/components/dataViews/ListMultisigTxs.tsx +++ b/components/dataViews/ListMultisigTxs.tsx @@ -4,7 +4,7 @@ import { getConnectError } from "@/lib/errorHelpers"; import { requestJson } from "@/lib/request"; import { msgTypeCountsFromJson } from "@/lib/txMsgHelpers"; import { cn, toastError } from "@/lib/utils"; -import { DbNonce, DbTransaction } from "@/types"; +import { DbNonce, DbTransaction } from "@/types/db"; import { WalletInfo } from "@/types/signing"; import { toBase64 } from "@cosmjs/encoding"; import { StargateClient } from "@cosmjs/stargate"; diff --git a/components/dataViews/ListUserMultisigs.tsx b/components/dataViews/ListUserMultisigs.tsx index 54ea7be..6d5073e 100644 --- a/components/dataViews/ListUserMultisigs.tsx +++ b/components/dataViews/ListUserMultisigs.tsx @@ -3,7 +3,7 @@ import { getConnectError } from "@/lib/errorHelpers"; import { MultisigFromQuery } from "@/lib/graphqlHelpers"; import { requestJson } from "@/lib/request"; import { toastError } from "@/lib/utils"; -import { DbNonce } from "@/types"; +import { DbNonce } from "@/types/db"; import { WalletInfo } from "@/types/signing"; import { MultisigThresholdPubkey } from "@cosmjs/amino"; import { toBase64 } from "@cosmjs/encoding"; diff --git a/components/dataViews/ThresholdInfo.tsx b/components/dataViews/ThresholdInfo.tsx index 7623750..8b093fa 100644 --- a/components/dataViews/ThresholdInfo.tsx +++ b/components/dataViews/ThresholdInfo.tsx @@ -1,6 +1,6 @@ +import { DbSignature } from "@/types/db"; import { MultisigThresholdPubkey } from "@cosmjs/amino"; import { useEffect, useState } from "react"; -import { DbSignature } from "../../types"; import StackableContainer from "../layout/StackableContainer"; import CopyAndPaste from "./CopyAndPaste"; diff --git a/components/dataViews/TransactionInfo/index.tsx b/components/dataViews/TransactionInfo/index.tsx index 863e15b..e9c1857 100644 --- a/components/dataViews/TransactionInfo/index.tsx +++ b/components/dataViews/TransactionInfo/index.tsx @@ -1,8 +1,8 @@ +import { DbTransactionJsonObj } from "@/types/db"; +import { MsgTypeUrls } from "@/types/txMsg"; import { EncodeObject } from "@cosmjs/proto-signing"; import { useChains } from "../../../context/ChainsContext"; import { printableCoins } from "../../../lib/displayHelpers"; -import { DbTransactionJsonObj } from "../../../types"; -import { MsgTypeUrls } from "../../../types/txMsg"; import StackableContainer from "../../layout/StackableContainer"; import TxMsgBeginRedelegateDetails from "./TxMsgBeginRedelegateDetails"; import TxMsgCreateVestingAccountDetails from "./TxMsgCreateVestingAccountDetails"; diff --git a/components/forms/CreateTxForm/index.tsx b/components/forms/CreateTxForm/index.tsx index 4d0b05b..b31f5a5 100644 --- a/components/forms/CreateTxForm/index.tsx +++ b/components/forms/CreateTxForm/index.tsx @@ -1,5 +1,7 @@ import { loadValidators } from "@/context/ChainsContext/helpers"; import { toastError, toastSuccess } from "@/lib/utils"; +import { DbTransactionJsonObj } from "@/types/db"; +import { MsgTypeUrl, MsgTypeUrls } from "@/types/txMsg"; import { EncodeObject } from "@cosmjs/proto-signing"; import { Account, calculateFee } from "@cosmjs/stargate"; import { assert, sleep } from "@cosmjs/utils"; @@ -9,8 +11,6 @@ import { toast } from "sonner"; import { useChains } from "../../../context/ChainsContext"; import { requestJson } from "../../../lib/request"; import { exportMsgToJson, gasOfTx } from "../../../lib/txMsgHelpers"; -import { DbTransactionJsonObj } from "../../../types"; -import { MsgTypeUrl, MsgTypeUrls } from "../../../types/txMsg"; import Button from "../../inputs/Button"; import Input from "../../inputs/Input"; import StackableContainer from "../../layout/StackableContainer"; diff --git a/components/forms/TransactionSigning.tsx b/components/forms/TransactionSigning.tsx index 388ba3e..329cd1c 100644 --- a/components/forms/TransactionSigning.tsx +++ b/components/forms/TransactionSigning.tsx @@ -1,4 +1,5 @@ import { toastError, toastSuccess } from "@/lib/utils"; +import { DbSignature, DbTransactionJsonObj } from "@/types/db"; import { LoadingStates, SigningStatus } from "@/types/signing"; import { MultisigThresholdPubkey, makeCosmoshubPath } from "@cosmjs/amino"; import { createWasmAminoConverters, wasmTypes } from "@cosmjs/cosmwasm-stargate"; diff --git a/lib/graphqlHelpers.ts b/lib/graphqlHelpers.ts index 10d66a5..cab2736 100644 --- a/lib/graphqlHelpers.ts +++ b/lib/graphqlHelpers.ts @@ -1,4 +1,4 @@ -import { DbMultisig, DbNonce, DbSignature, DbTransaction, DbTransactionJsonObj } from "../types"; +import { DbMultisig, DbNonce, DbSignature, DbTransaction, DbTransactionJsonObj } from "../types/db"; import { requestGraphQlJson } from "./request"; /** diff --git a/lib/txMsgHelpers.ts b/lib/txMsgHelpers.ts index 03c0a7d..2b176dd 100644 --- a/lib/txMsgHelpers.ts +++ b/lib/txMsgHelpers.ts @@ -1,6 +1,6 @@ +import { DbTransactionJsonObj } from "@/types/db"; +import { MsgCodecs, MsgTypeUrl, MsgTypeUrls } from "@/types/txMsg"; import { EncodeObject } from "@cosmjs/proto-signing"; -import { DbTransactionJsonObj } from "../types"; -import { MsgCodecs, MsgTypeUrl, MsgTypeUrls } from "../types/txMsg"; const gasOfMsg = (msgType: MsgTypeUrl): number => { switch (msgType) { diff --git a/pages/[chainName]/[address]/transaction/[transactionID].tsx b/pages/[chainName]/[address]/transaction/[transactionID].tsx index c3ab6b4..b391e43 100644 --- a/pages/[chainName]/[address]/transaction/[transactionID].tsx +++ b/pages/[chainName]/[address]/transaction/[transactionID].tsx @@ -1,5 +1,6 @@ import { isChainInfoFilled } from "@/context/ChainsContext/helpers"; import { toastError, toastSuccess } from "@/lib/utils"; +import { DbSignature } from "@/types/db"; import { MultisigThresholdPubkey } from "@cosmjs/amino"; import { fromBase64 } from "@cosmjs/encoding"; import { Account, StargateClient, makeMultisignedTxBytes } from "@cosmjs/stargate"; @@ -17,10 +18,9 @@ import Page from "../../../../components/layout/Page"; import StackableContainer from "../../../../components/layout/StackableContainer"; import { useChains } from "../../../../context/ChainsContext"; import { findTransactionByID } from "../../../../lib/graphqlHelpers"; -import { isAccount, getHostedMultisig } from "../../../../lib/multisigHelpers"; +import { getHostedMultisig, isAccount } from "../../../../lib/multisigHelpers"; import { requestJson } from "../../../../lib/request"; import { dbTxFromJson } from "../../../../lib/txMsgHelpers"; -import { DbSignature } from "../../../../types"; interface Props { props: { From 4ed8512db54076cd54e0cfce844690e4799f0007 Mon Sep 17 00:00:00 2001 From: abefernan <44572727+abefernan@users.noreply.github.com> Date: Wed, 19 Jun 2024 17:00:43 +0200 Subject: [PATCH 3/5] Add keplr helpers --- lib/keplr.ts | 106 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 lib/keplr.ts diff --git a/lib/keplr.ts b/lib/keplr.ts new file mode 100644 index 0000000..89ee177 --- /dev/null +++ b/lib/keplr.ts @@ -0,0 +1,106 @@ +import { ChainInfo } from "@/context/ChainsContext/types"; +import { StdSignature, decodeSignature, pubkeyToAddress } from "@cosmjs/amino"; +import { toBase64 } from "@cosmjs/encoding"; +import { verifyADR36Amino } from "@keplr-wallet/cosmos"; +import { StdSignDoc } from "@keplr-wallet/types"; +import { useLayoutEffect } from "react"; + +const getKeplr = async (chainId: string) => { + const keplr = window.keplr; + if (!keplr) { + throw new Error("Keplr not found"); + } + + await keplr.enable(chainId); + + keplr.defaultOptions = { + sign: { preferNoSetFee: true, preferNoSetMemo: true, disableBalanceCheck: true }, + }; + + return keplr; +}; + +export const getKeplrKey = async (chainId: string) => { + const keplr = await getKeplr(chainId); + const keplrKey = await keplr.getKey(chainId); + + return keplrKey; +}; + +export const getKeplrAminoSigner = async (chainId: string) => { + const keplr = await getKeplr(chainId); + const aminoSigner = keplr.getOfflineSignerOnlyAmino(chainId); + + return aminoSigner; +}; + +export const getKeplrVerifySignature = async (signer: string, chain: ChainInfo, nonce: number) => { + const keplr = await getKeplr(chain.chainId); + + const { signature } = await keplr.signAmino( + chain.chainId, + signer, + getKeplrVerifyMsg(signer, chain.chainDisplayName, nonce), + ); + + return signature; +}; + +const getKeplrVerifyMsg = ( + signer: string, + chainDisplayName: string, + nonce: number, +): StdSignDoc => ({ + chain_id: "", + account_number: "0", + sequence: "0", + fee: { gas: "0", amount: [] }, + memo: "", + msgs: [ + { + type: "sign/MsgSignData", + value: { + signer, + data: toBase64(new Uint8Array(Buffer.from(getKeplrVerifyData(chainDisplayName, nonce)))), + }, + }, + ], +}); + +const getKeplrVerifyData = (chainDisplayName: string, nonce: number) => + JSON.stringify({ + title: `Keplr Login to ${chainDisplayName}`, + description: "Sign this no fee transaction to login with your Keplr wallet", + nonce, + }); + +export const verifyKeplrSignature = (signature: StdSignature, chain: ChainInfo, nonce: number) => { + const signer = pubkeyToAddress(signature.pub_key, chain.addressPrefix); + const data = getKeplrVerifyData(chain.chainDisplayName, nonce); + const { pubkey: decodedPubKey, signature: decodedSignature } = decodeSignature(signature); + + const verified = verifyADR36Amino( + chain.addressPrefix, + signer, + data, + decodedPubKey, + decodedSignature, + ); + + return verified; +}; + +const accountChangeKey = "keplr_keystorechange"; +export const useKeplrReconnect = (condition: boolean, connectWallet: () => Promise) => { + useLayoutEffect(() => { + if (!condition) { + return; + } + + window.addEventListener(accountChangeKey, connectWallet); + + return () => { + window.removeEventListener(accountChangeKey, connectWallet); + }; + }, [condition, connectWallet]); +}; From 8912968600bac86dfc473b9c045d7a0f6ab662d3 Mon Sep 17 00:00:00 2001 From: abefernan <44572727+abefernan@users.noreply.github.com> Date: Wed, 19 Jun 2024 17:01:16 +0200 Subject: [PATCH 4/5] Use keplr helpers on app --- .../AccountView/ButtonConnectWallet.tsx | 27 ++------- components/dataViews/ListMultisigTxs.tsx | 59 ++----------------- components/dataViews/ListUserMultisigs.tsx | 57 ++---------------- components/dataViews/MultisigView/index.tsx | 8 +-- components/forms/CreateMultisigForm/index.tsx | 8 +-- components/forms/TransactionSigning.tsx | 35 ++++------- 6 files changed, 31 insertions(+), 163 deletions(-) diff --git a/components/dataViews/AccountView/ButtonConnectWallet.tsx b/components/dataViews/AccountView/ButtonConnectWallet.tsx index 9aad8f8..a1703b4 100644 --- a/components/dataViews/AccountView/ButtonConnectWallet.tsx +++ b/components/dataViews/AccountView/ButtonConnectWallet.tsx @@ -1,3 +1,4 @@ +import { getKeplrKey, useKeplrReconnect } from "@/lib/keplr"; import { cn, toastError } from "@/lib/utils"; import { LoadingStates, WalletInfo, WalletType } from "@/types/signing"; import { makeCosmoshubPath } from "@cosmjs/amino"; @@ -6,7 +7,7 @@ import { LedgerSigner } from "@cosmjs/ledger-amino"; import TransportWebUSB from "@ledgerhq/hw-transport-webusb"; import { Loader2, Unplug } from "lucide-react"; import Image from "next/image"; -import { Dispatch, SetStateAction, useCallback, useLayoutEffect, useState } from "react"; +import { Dispatch, SetStateAction, useCallback, useState } from "react"; import { useChains } from "../../../context/ChainsContext"; import { getConnectError } from "../../../lib/errorHelpers"; import { Button } from "../../ui/button"; @@ -30,16 +31,8 @@ export default function ButtonConnectWallet({ try { setLoading((oldLoading) => ({ ...oldLoading, keplr: true })); - await window.keplr.enable(chain.chainId); - window.keplr.defaultOptions = { - sign: { preferNoSetFee: true, preferNoSetMemo: true, disableBalanceCheck: true }, - }; - - const { bech32Address: address, pubKey: pubKeyArray } = await window.keplr.getKey( - chain.chainId, - ); + const { bech32Address: address, pubKey: pubKeyArray } = await getKeplrKey(chain.chainId); const pubKey = toBase64(pubKeyArray); - setWalletInfo({ type: "Keplr", address, pubKey }); } catch (e) { const connectError = getConnectError(e); @@ -53,19 +46,7 @@ export default function ButtonConnectWallet({ } }, [chain.chainId, setWalletInfo]); - useLayoutEffect(() => { - if (!walletInfo?.address) { - return; - } - - const accountChangeKey = "keplr_keystorechange"; - - if (walletInfo.type === "Keplr") { - window.addEventListener(accountChangeKey, connectKeplr); - } else { - window.removeEventListener(accountChangeKey, connectKeplr); - } - }, [connectKeplr, walletInfo]); + useKeplrReconnect(!!walletInfo?.address, connectKeplr); const connectLedger = async () => { try { diff --git a/components/dataViews/ListMultisigTxs.tsx b/components/dataViews/ListMultisigTxs.tsx index 9105cf1..a3397d9 100644 --- a/components/dataViews/ListMultisigTxs.tsx +++ b/components/dataViews/ListMultisigTxs.tsx @@ -1,6 +1,7 @@ import { useChains } from "@/context/ChainsContext"; import { ellideMiddle } from "@/lib/displayHelpers"; import { getConnectError } from "@/lib/errorHelpers"; +import { getKeplrKey, getKeplrVerifySignature, useKeplrReconnect } from "@/lib/keplr"; import { requestJson } from "@/lib/request"; import { msgTypeCountsFromJson } from "@/lib/txMsgHelpers"; import { cn, toastError } from "@/lib/utils"; @@ -11,7 +12,7 @@ import { StargateClient } from "@cosmjs/stargate"; import { Loader2, MoveRightIcon } from "lucide-react"; import Image from "next/image"; import Link from "next/link"; -import { useCallback, useLayoutEffect, useState } from "react"; +import { useCallback, useState } from "react"; import { Badge } from "../ui/badge"; import { Button } from "../ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../ui/card"; @@ -19,8 +20,6 @@ import { Label } from "../ui/label"; import { Switch } from "../ui/switch"; import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip"; -// Show pending default. Toggle con show already broadcasted too - interface ListMultisigTxsProps { readonly multisigAddress: string; readonly multisigThreshold: number; @@ -52,36 +51,10 @@ export default function ListMultisigTxs({ `/api/chain/${chain.chainId}/nonce/${accountOnChain.address}`, ); - const { signature } = await window.keplr.signAmino(chain.chainId, accountOnChain.address, { - chain_id: "", - account_number: "0", - sequence: "0", - fee: { gas: "0", amount: [] }, - msgs: [ - { - type: "sign/MsgSignData", - value: { - signer: accountOnChain.address, - data: toBase64( - new Uint8Array( - Buffer.from( - JSON.stringify({ - title: `Keplr Login to ${chain.chainDisplayName}`, - description: "Sign this no fee transaction to login with your Keplr wallet", - nonce, - }), - ), - ), - ), - }, - }, - ], - memo: "", - }); - + const signature = await getKeplrVerifySignature(accountOnChain.address, chain, nonce); return signature; }, - [chain.chainDisplayName, chain.chainId, chain.nodeAddress], + [chain], ); const fetchTransactions = useCallback( @@ -112,17 +85,8 @@ export default function ListMultisigTxs({ try { setLoading(true); - await window.keplr.enable(chain.chainId); - window.keplr.defaultOptions = { - sign: { preferNoSetFee: true, preferNoSetMemo: true, disableBalanceCheck: true }, - }; - - const { bech32Address: address, pubKey: pubKeyArray } = await window.keplr.getKey( - chain.chainId, - ); - + const { bech32Address: address, pubKey: pubKeyArray } = await getKeplrKey(chain.chainId); const pubKey = toBase64(pubKeyArray); - setWalletInfo({ address, pubKey }); await fetchTransactions(address); @@ -138,18 +102,7 @@ export default function ListMultisigTxs({ } }, [chain.chainId, fetchTransactions]); - useLayoutEffect(() => { - if (!walletInfo?.address) { - return; - } - - const accountChangeKey = "keplr_keystorechange"; - window.addEventListener(accountChangeKey, connectWallet); - - return () => { - window.removeEventListener(accountChangeKey, connectWallet); - }; - }, [connectWallet, walletInfo?.address]); + useKeplrReconnect(!!walletInfo?.address, connectWallet); return ( diff --git a/components/dataViews/ListUserMultisigs.tsx b/components/dataViews/ListUserMultisigs.tsx index 6d5073e..96267c2 100644 --- a/components/dataViews/ListUserMultisigs.tsx +++ b/components/dataViews/ListUserMultisigs.tsx @@ -1,6 +1,7 @@ import { useChains } from "@/context/ChainsContext"; import { getConnectError } from "@/lib/errorHelpers"; import { MultisigFromQuery } from "@/lib/graphqlHelpers"; +import { getKeplrKey, getKeplrVerifySignature, useKeplrReconnect } from "@/lib/keplr"; import { requestJson } from "@/lib/request"; import { toastError } from "@/lib/utils"; import { DbNonce } from "@/types/db"; @@ -11,7 +12,7 @@ import { StargateClient } from "@cosmjs/stargate"; import { Loader2, MoveRightIcon } from "lucide-react"; import Image from "next/image"; import Link from "next/link"; -import { useCallback, useLayoutEffect, useState } from "react"; +import { useCallback, useState } from "react"; import { Badge } from "../ui/badge"; import { Button } from "../ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../ui/card"; @@ -44,36 +45,10 @@ export default function ListUserMultisigs() { `/api/chain/${chain.chainId}/nonce/${accountOnChain.address}`, ); - const { signature } = await window.keplr.signAmino(chain.chainId, accountOnChain.address, { - chain_id: "", - account_number: "0", - sequence: "0", - fee: { gas: "0", amount: [] }, - msgs: [ - { - type: "sign/MsgSignData", - value: { - signer: accountOnChain.address, - data: toBase64( - new Uint8Array( - Buffer.from( - JSON.stringify({ - title: `Keplr Login to ${chain.chainDisplayName}`, - description: "Sign this no fee transaction to login with your Keplr wallet", - nonce, - }), - ), - ), - ), - }, - }, - ], - memo: "", - }); - + const signature = await getKeplrVerifySignature(accountOnChain.address, chain, nonce); return signature; }, - [chain.chainDisplayName, chain.chainId, chain.nodeAddress], + [chain], ); const fetchMultisigs = useCallback( @@ -104,17 +79,8 @@ export default function ListUserMultisigs() { try { setLoading(true); - await window.keplr.enable(chain.chainId); - window.keplr.defaultOptions = { - sign: { preferNoSetFee: true, preferNoSetMemo: true, disableBalanceCheck: true }, - }; - - const { bech32Address: address, pubKey: pubKeyArray } = await window.keplr.getKey( - chain.chainId, - ); - + const { bech32Address: address, pubKey: pubKeyArray } = await getKeplrKey(chain.chainId); const pubKey = toBase64(pubKeyArray); - setWalletInfo({ address, pubKey }); await fetchMultisigs(address); @@ -130,18 +96,7 @@ export default function ListUserMultisigs() { } }, [chain.chainId, fetchMultisigs]); - useLayoutEffect(() => { - if (!walletInfo?.address) { - return; - } - - const accountChangeKey = "keplr_keystorechange"; - window.addEventListener(accountChangeKey, connectWallet); - - return () => { - window.removeEventListener(accountChangeKey, connectWallet); - }; - }, [connectWallet, walletInfo?.address]); + useKeplrReconnect(!!walletInfo?.address, connectWallet); return ( diff --git a/components/dataViews/MultisigView/index.tsx b/components/dataViews/MultisigView/index.tsx index 2bb2695..7434a74 100644 --- a/components/dataViews/MultisigView/index.tsx +++ b/components/dataViews/MultisigView/index.tsx @@ -2,6 +2,7 @@ import { Alert, AlertDescription } from "@/components/ui/alert"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { isChainInfoFilled } from "@/context/ChainsContext/helpers"; import { checkAddress } from "@/lib/displayHelpers"; +import { getKeplrKey } from "@/lib/keplr"; import { HostedMultisig, createMultisigFromCompressedSecp256k1Pubkeys, @@ -44,12 +45,7 @@ export default function MultisigView() { "Pubkey on chain is not of type MultisigThreshold", ); - await window.keplr.enable(chain.chainId); - window.keplr.defaultOptions = { - sign: { preferNoSetFee: true, preferNoSetMemo: true, disableBalanceCheck: true }, - }; - - const { bech32Address: address } = await window.keplr.getKey(chain.chainId); + const { bech32Address: address } = await getKeplrKey(chain.chainId); await createMultisigFromCompressedSecp256k1Pubkeys( newHostedMultisig.accountOnChain.pubkey.value.pubkeys.map((p) => p.value), diff --git a/components/forms/CreateMultisigForm/index.tsx b/components/forms/CreateMultisigForm/index.tsx index 475e39d..44e3918 100644 --- a/components/forms/CreateMultisigForm/index.tsx +++ b/components/forms/CreateMultisigForm/index.tsx @@ -9,6 +9,7 @@ import { FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; +import { getKeplrKey } from "@/lib/keplr"; import { toastError } from "@/lib/utils"; import { StargateClient } from "@cosmjs/stargate"; import { zodResolver } from "@hookform/resolvers/zod"; @@ -91,12 +92,7 @@ export default function CreateMultisigForm() { ); try { - await window.keplr.enable(chain.chainId); - window.keplr.defaultOptions = { - sign: { preferNoSetFee: true, preferNoSetMemo: true, disableBalanceCheck: true }, - }; - - const { bech32Address: address } = await window.keplr.getKey(chain.chainId); + const { bech32Address: address } = await getKeplrKey(chain.chainId); const multisigAddress = await createMultisigFromCompressedSecp256k1Pubkeys( pubkeys, diff --git a/components/forms/TransactionSigning.tsx b/components/forms/TransactionSigning.tsx index 329cd1c..1f61bd2 100644 --- a/components/forms/TransactionSigning.tsx +++ b/components/forms/TransactionSigning.tsx @@ -1,3 +1,4 @@ +import { getKeplrAminoSigner, getKeplrKey, useKeplrReconnect } from "@/lib/keplr"; import { toastError, toastSuccess } from "@/lib/utils"; import { DbSignature, DbTransactionJsonObj } from "@/types/db"; import { LoadingStates, SigningStatus } from "@/types/signing"; @@ -13,13 +14,13 @@ import { defaultRegistryTypes, } from "@cosmjs/stargate"; import { assert } from "@cosmjs/utils"; +import { Key } from "@keplr-wallet/types"; import TransportWebUSB from "@ledgerhq/hw-transport-webusb"; -import { useCallback, useLayoutEffect, useState } from "react"; +import { useCallback, useState } from "react"; import { toast } from "sonner"; import { useChains } from "../../context/ChainsContext"; import { getConnectError } from "../../lib/errorHelpers"; import { requestJson } from "../../lib/request"; -import { DbSignature, DbTransactionJsonObj, WalletAccount } from "../../types"; import HashView from "../dataViews/HashView"; import Button from "../inputs/Button"; import StackableContainer from "../layout/StackableContainer"; @@ -36,7 +37,7 @@ const TransactionSigning = (props: TransactionSigningProps) => { const memberPubkeys = props.pubkey.value.pubkeys.map(({ value }) => value); const { chain } = useChains(); - const [walletAccount, setWalletAccount] = useState(); + const [walletAccount, setWalletAccount] = useState>(); const [signing, setSigning] = useState("not_signed"); const [walletType, setWalletType] = useState<"Keplr" | "Ledger">(); const [ledgerSigner, setLedgerSigner] = useState(null); @@ -46,17 +47,13 @@ const TransactionSigning = (props: TransactionSigningProps) => { try { setLoading((oldLoading) => ({ ...oldLoading, keplr: true })); - await window.keplr.enable(chain.chainId); - window.keplr.defaultOptions = { - sign: { preferNoSetFee: true, preferNoSetMemo: true, disableBalanceCheck: true }, - }; - const tempWalletAccount = await window.keplr.getKey(chain.chainId); - setWalletAccount(tempWalletAccount); + const newWalletAccount = await getKeplrKey(chain.chainId); + setWalletAccount(newWalletAccount); - const pubkey = toBase64(tempWalletAccount.pubKey); + const pubkey = toBase64(newWalletAccount.pubKey); const isMember = memberPubkeys.includes(pubkey); const hasSigned = isMember - ? props.signatures.some((sig) => sig.address === tempWalletAccount.bech32Address) + ? props.signatures.some((sig) => sig.address === newWalletAccount.bech32Address) : false; if (!isMember) { setSigning("not_a_member"); @@ -81,15 +78,7 @@ const TransactionSigning = (props: TransactionSigningProps) => { } }, [chain.chainId, memberPubkeys, props.signatures]); - useLayoutEffect(() => { - const accountChangeKey = "keplr_keystorechange"; - - if (walletType === "Keplr") { - window.addEventListener(accountChangeKey, connectKeplr); - } else { - window.removeEventListener(accountChangeKey, connectKeplr); - } - }, [connectKeplr, walletType]); + useKeplrReconnect(!!walletAccount?.address, connectKeplr); const connectLedger = async () => { try { @@ -104,7 +93,7 @@ const TransactionSigning = (props: TransactionSigningProps) => { prefix: chain.addressPrefix, }); const accounts = await offlineSigner.getAccounts(); - const tempWalletAccount: WalletAccount = { + const tempWalletAccount = { bech32Address: accounts[0].address, pubKey: accounts[0].pubkey, algo: accounts[0].algo, @@ -147,9 +136,7 @@ const TransactionSigning = (props: TransactionSigningProps) => { setLoading((newLoading) => ({ ...newLoading, signing: true })); const offlineSigner = - walletType === "Keplr" - ? window.keplr.getOfflineSignerOnlyAmino(chain.chainId) - : ledgerSigner; + walletType === "Keplr" ? await getKeplrAminoSigner(chain.chainId) : ledgerSigner; if (!offlineSigner) { throw new Error("Offline signer not found"); From b88916eaa3d42f9093b1b9f1467e04a5ab7fae97 Mon Sep 17 00:00:00 2001 From: abefernan <44572727+abefernan@users.noreply.github.com> Date: Wed, 19 Jun 2024 17:01:26 +0200 Subject: [PATCH 5/5] Use keplr helpers on vercel fns --- .../chain/[chainId]/multisig/list/index.ts | 22 +++++-------------- pages/api/transaction/list/index.ts | 19 +++------------- 2 files changed, 9 insertions(+), 32 deletions(-) diff --git a/pages/api/chain/[chainId]/multisig/list/index.ts b/pages/api/chain/[chainId]/multisig/list/index.ts index 11376c6..b69e433 100644 --- a/pages/api/chain/[chainId]/multisig/list/index.ts +++ b/pages/api/chain/[chainId]/multisig/list/index.ts @@ -5,10 +5,10 @@ import { getNonce, updateNonce, } from "@/lib/graphqlHelpers"; +import { verifyKeplrSignature } from "@/lib/keplr"; import { decodeSignature, pubkeyToAddress } from "@cosmjs/amino"; import { toBase64 } from "@cosmjs/encoding"; import { StargateClient } from "@cosmjs/stargate"; -import { verifyADR36Amino } from "@keplr-wallet/cosmos"; import { StdSignature } from "@keplr-wallet/types"; import type { NextApiRequest, NextApiResponse } from "next"; @@ -38,27 +38,17 @@ export default async function multisigsApi(req: NextApiRequest, res: NextApiResp throw new Error(`Nonce not found on ${chainId} for ${address}`); } - const { pubkey: decodedPubKey, signature: decodedSignature } = decodeSignature(signature); - const data = JSON.stringify({ - title: `Keplr Login to ${chain.chainDisplayName}`, - description: "Sign this no fee transaction to login with your Keplr wallet", - nonce: dbNonce.nonce, - }); - await updateNonce(chainId, address, dbNonce.nonce + 1); - - const verified = verifyADR36Amino( - chain.addressPrefix, - address, - data, - decodedPubKey, - decodedSignature, - ); + const verified = verifyKeplrSignature(signature, chain, dbNonce.nonce); if (verified) { console.log("Function `getMultisigs` invoked", chainId, address); + const created = await getCreatedMultisigs(chainId, address); + + const { pubkey: decodedPubKey } = decodeSignature(signature); const belonged = await getBelongedMultisigs(chainId, toBase64(decodedPubKey)); + console.log("success", { created, belonged }); res.status(200).send({ created, belonged }); return; diff --git a/pages/api/transaction/list/index.ts b/pages/api/transaction/list/index.ts index 6a77013..f989101 100644 --- a/pages/api/transaction/list/index.ts +++ b/pages/api/transaction/list/index.ts @@ -6,10 +6,10 @@ import { getTransactions, updateNonce, } from "@/lib/graphqlHelpers"; +import { verifyKeplrSignature } from "@/lib/keplr"; import { decodeSignature, pubkeyToAddress } from "@cosmjs/amino"; import { toBase64 } from "@cosmjs/encoding"; import { StargateClient } from "@cosmjs/stargate"; -import { verifyADR36Amino } from "@keplr-wallet/cosmos"; import { StdSignature } from "@keplr-wallet/types"; import type { NextApiRequest, NextApiResponse } from "next"; @@ -28,7 +28,7 @@ export default async function transactionsApi(req: NextApiRequest, res: NextApiR throw new Error("Multisig not found"); } - const { pubkey: decodedPubKey, signature: decodedSignature } = decodeSignature(signature); + const { pubkey: decodedPubKey } = decodeSignature(signature); if (!multisig.pubkeyJSON.includes(toBase64(decodedPubKey))) { throw new Error("You don't belong to the multisig"); @@ -49,21 +49,8 @@ export default async function transactionsApi(req: NextApiRequest, res: NextApiR throw new Error(`Nonce not found on ${chain.chainId} for ${address}`); } - const data = JSON.stringify({ - title: `Keplr Login to ${chain.chainDisplayName}`, - description: "Sign this no fee transaction to login with your Keplr wallet", - nonce: dbNonce.nonce, - }); - await updateNonce(chain.chainId, address, dbNonce.nonce + 1); - - const verified = verifyADR36Amino( - chain.addressPrefix, - address, - data, - decodedPubKey, - decodedSignature, - ); + const verified = verifyKeplrSignature(signature, chain, dbNonce.nonce); if (verified) { const multisigId = await getMultisigId(multisigAddress, chain.chainId);