From bb008bcf24c75bc418bd68b682463794f68d9dbc Mon Sep 17 00:00:00 2001
From: Nur Fikri <codingki@gmail.com>
Date: Mon, 22 Apr 2024 22:45:02 +0700
Subject: [PATCH] feat: add metamask snap cosmos wallet integration

---
 docs/docs/change-log.md                       |   8 +
 docs/docs/index.md                            |   2 +-
 docs/docs/types/walletType.md                 |   2 +
 example/next/ui/connect-button.tsx            |   3 +
 example/starter/src/utils/graz.ts             |   4 +
 packages/graz/package.json                    |  10 +-
 .../wallet/cosmos-metamask-snap/index.ts      |  84 +++++++++++
 packages/graz/src/actions/wallet/index.ts     |   6 +-
 .../index.ts                                  |  17 +--
 .../leap.ts                                   |   0
 .../types.ts                                  |   0
 packages/graz/src/cli.mjs                     |  19 ++-
 packages/graz/src/index.ts                    |   2 +-
 packages/graz/src/types/wallet.ts             |   7 +-
 packages/graz/src/utils/cli/clack.ts          |  26 ----
 packages/graz/src/utils/cli/prompt-opt.ts     |  75 ---------
 pnpm-lock.yaml                                | 142 +++++-------------
 17 files changed, 175 insertions(+), 232 deletions(-)
 create mode 100644 packages/graz/src/actions/wallet/cosmos-metamask-snap/index.ts
 rename packages/graz/src/actions/wallet/{metamask-snap => leap-metamask-snap}/index.ts (94%)
 rename packages/graz/src/actions/wallet/{metamask-snap => leap-metamask-snap}/leap.ts (100%)
 rename packages/graz/src/actions/wallet/{metamask-snap => leap-metamask-snap}/types.ts (100%)
 delete mode 100644 packages/graz/src/utils/cli/clack.ts
 delete mode 100644 packages/graz/src/utils/cli/prompt-opt.ts

diff --git a/docs/docs/change-log.md b/docs/docs/change-log.md
index 8b0a307f..f2b6ee39 100644
--- a/docs/docs/change-log.md
+++ b/docs/docs/change-log.md
@@ -4,6 +4,14 @@ sidebar_position: 8
 
 # Changelog
 
+## Version 0.1.8
+
+- Added [Metamask Snap Cosmos](https://github.com/cosmos/snap) integration
+
+## Version 0.1.7
+
+- Fix Metamask Snap Leap when signing
+
 ## Version 0.1.6
 
 - Added capsule wallet integration
diff --git a/docs/docs/index.md b/docs/docs/index.md
index b7327a6c..f08591ad 100644
--- a/docs/docs/index.md
+++ b/docs/docs/index.md
@@ -9,7 +9,7 @@ sidebar_position: 1
 ## Features
 
 - 🪝 20+ hooks for interfacing with wallets, clients, signers, etc. (connecting, view balances, send tokens, etc.)
-- 💳 Multiple wallet supports (Keplr, Leap, Cosmostation, Vectis, Station, XDefi, Metamask Snap, WalletConnect)
+- 💳 Multiple wallet supports (Keplr, Leap, Cosmostation, Vectis, Station, XDefi, Metamask Snap, WalletConnect, Capsule)
 - ⚙️ Generate mainnet & testnet `ChainInfo`
 - 📚 Built-in caching, request deduplication, and all the good stuff from [`@tanstack/react-query`](https://tanstack.com/query) and [`zustand`](https://github.com/pmndrs/zustand)
 - 🔄 Auto refresh on wallet and network change
diff --git a/docs/docs/types/walletType.md b/docs/docs/types/walletType.md
index 9bdeb9fc..fd6e6440 100644
--- a/docs/docs/types/walletType.md
+++ b/docs/docs/types/walletType.md
@@ -45,7 +45,9 @@ enum WalletType {
   WC_LEAP_MOBILE = "wc_leap_mobile",
   WC_COSMOSTATION_MOBILE = "wc_cosmostation_mobile",
   METAMASK_SNAP_LEAP = "metamask_snap_leap",
+  METAMASK_SNAP_COSMOS = "metamask_snap_cosmos",
   STATION = "station",
   XDEFI = "xdefi",
+  CAPSULE = "capsule",
 }
 ```
diff --git a/example/next/ui/connect-button.tsx b/example/next/ui/connect-button.tsx
index 54f11fc7..fb35dc8d 100644
--- a/example/next/ui/connect-button.tsx
+++ b/example/next/ui/connect-button.tsx
@@ -81,6 +81,9 @@ export const ConnectButton: FC = () => {
             {wallets.metamask_snap_leap ? (
               <Button onClick={() => handleConnect(WalletType.METAMASK_SNAP_LEAP)}>Metamask Snap Leap</Button>
             ) : null}
+            {wallets.metamask_snap_cosmos ? (
+              <Button onClick={() => handleConnect(WalletType.METAMASK_SNAP_COSMOS)}>Metamask Snap Cosmos</Button>
+            ) : null}
             {wallets.capsule ? <Button onClick={() => handleConnect(WalletType.CAPSULE)}>Capsule</Button> : null}
           </Stack>
         </ModalContent>
diff --git a/example/starter/src/utils/graz.ts b/example/starter/src/utils/graz.ts
index 9e993b12..0a8d527b 100644
--- a/example/starter/src/utils/graz.ts
+++ b/example/starter/src/utils/graz.ts
@@ -38,6 +38,10 @@ export const listedWallets = {
     name: "Metamask Snap Leap",
     imgSrc: "/assets/wallet-icon-metamask.png",
   },
+  [WalletType.METAMASK_SNAP_COSMOS]: {
+    name: "Metamask Snap Cosmos",
+    imgSrc: "/assets/wallet-icon-metamask.png",
+  },
   [WalletType.WALLETCONNECT]: {
     name: "WalletConnect",
     imgSrc: "/assets/wallet-icon-walletconnect.png",
diff --git a/packages/graz/package.json b/packages/graz/package.json
index 2cefddfc..7e99c8b1 100644
--- a/packages/graz/package.json
+++ b/packages/graz/package.json
@@ -49,16 +49,10 @@
     "@cosmjs/tendermint-rpc": "*",
     "@leapwallet/cosmos-social-login-capsule-provider": "^0.0.30",
     "@leapwallet/cosmos-social-login-capsule-provider-ui": "^0.0.47",
-    "long": "^4",
     "react": ">=17"
   },
-  "peerDependenciesMeta": {
-    "long": {
-      "optional": true
-    }
-  },
   "dependencies": {
-    "@clack/prompts": "^0.7.0",
+    "@cosmsnap/snapper": "^0.1.29",
     "@keplr-wallet/cosmos": "^0.12.20",
     "@keplr-wallet/types": "^0.12.23",
     "@metamask/providers": "^12.0.0",
@@ -71,7 +65,7 @@
     "@web3modal/standalone": "^2.4.3",
     "cac": "^6.7.14",
     "cosmos-directory-client": "0.0.6",
-    "wadesta": "^0.0.5",
+    "long": "4",
     "zustand": "^4.5.2"
   },
   "devDependencies": {
diff --git a/packages/graz/src/actions/wallet/cosmos-metamask-snap/index.ts b/packages/graz/src/actions/wallet/cosmos-metamask-snap/index.ts
new file mode 100644
index 00000000..23099517
--- /dev/null
+++ b/packages/graz/src/actions/wallet/cosmos-metamask-snap/index.ts
@@ -0,0 +1,84 @@
+import { CosmosSnap, installSnap, isSnapInstalled } from "@cosmsnap/snapper";
+
+import { useGrazInternalStore } from "../../../store";
+import type { KnownKeys, Wallet } from "../../../types/wallet";
+import type { ChainId } from "../../../utils/multi-chain";
+
+const metamaskSnapCosmosKeysMap: KnownKeys = {};
+
+export const getMetamaskSnapCosmos = (): Wallet => {
+  const ethereum = window.ethereum;
+  let cosmos = window.cosmos;
+  if (ethereum) {
+    const init = async () => {
+      const clientVersion = await ethereum.request({
+        method: "web3_clientVersion",
+      });
+
+      const isMetamask = (clientVersion as string).includes("MetaMask");
+
+      if (!isMetamask) throw new Error("Metamask is not installed");
+
+      if (typeof window.okxwallet !== "undefined") {
+        // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+        // @ts-expect-error
+        if (window.okxwallet.isOkxWallet) {
+          throw new Error("You have OKX Wallet installed. Please disable and reload the page to use Metamask Snap.");
+        }
+      }
+      const version = (clientVersion as string).split("MetaMask/v")[1]?.split(".")[0];
+      const isSupportMMSnap = Number(version) >= 11;
+      if (!isSupportMMSnap) throw new Error("Metamask Snap is not supported in this version");
+
+      const installedSnap = await isSnapInstalled();
+      if (!installedSnap) await installSnap();
+      window.cosmos = new CosmosSnap();
+      cosmos = window.cosmos;
+      return true;
+    };
+
+    const enable = async (chainId: ChainId) => {
+      const installedSnap = await isSnapInstalled();
+      if (!installedSnap) await installSnap();
+    };
+
+    const getOfflineSignerAuto = async (chainId: string) => {
+      const key = await cosmos.getKey(chainId);
+      if (key.isNanoLedger) return cosmos.getOfflineSignerOnlyAmino(chainId);
+      return cosmos.getOfflineSigner(chainId);
+    };
+
+    return {
+      init,
+      enable,
+      getOfflineSigner: (chainId: string) => cosmos.getOfflineSigner(chainId),
+      experimentalSuggestChain: async (chainInfo) => {
+        await cosmos.experimentalSuggestChain(chainInfo);
+      },
+      signAmino: async (chainId, signer, signDoc) => {
+        return cosmos.signAmino(chainId, signer, signDoc);
+      },
+      getKey: async (chainId) => {
+        if (typeof metamaskSnapCosmosKeysMap[chainId] !== "undefined") return metamaskSnapCosmosKeysMap[chainId]!;
+
+        return cosmos.getKey(chainId);
+      },
+      getOfflineSignerAuto,
+      getOfflineSignerOnlyAmino: (chainId) => {
+        return cosmos.getOfflineSignerOnlyAmino(chainId);
+      },
+      signDirect: async (chainId, signer, signDoc) => {
+        // @ts-expect-error - signDoc is not the same as SignDoc
+        return cosmos.signDirect(chainId, signer, signDoc);
+      },
+      signArbitrary: async (chainId, signer, data) => {
+        return cosmos.signArbitrary(chainId, signer, data);
+      },
+      disable: async (chainId) => {
+        chainId && (await cosmos.deleteChain(chainId));
+      },
+    };
+  }
+  useGrazInternalStore.getState()._notFoundFn();
+  throw new Error("window.ethereum is not defined");
+};
diff --git a/packages/graz/src/actions/wallet/index.ts b/packages/graz/src/actions/wallet/index.ts
index 7f67395f..9421b675 100644
--- a/packages/graz/src/actions/wallet/index.ts
+++ b/packages/graz/src/actions/wallet/index.ts
@@ -3,10 +3,11 @@ import { grazSessionDefaultValues, useGrazInternalStore, useGrazSessionStore } f
 import type { Wallet } from "../../types/wallet";
 import { WALLET_TYPES, WalletType } from "../../types/wallet";
 import { getCapsule } from "./capsule";
+import { getMetamaskSnapCosmos } from "./cosmos-metamask-snap";
 import { getCosmostation } from "./cosmostation";
 import { getKeplr } from "./keplr";
 import { getLeap } from "./leap";
-import { getMetamaskSnapLeap } from "./metamask-snap/leap";
+import { getMetamaskSnapLeap } from "./leap-metamask-snap/leap";
 import { getStation } from "./station";
 import { getVectis } from "./vectis";
 import { getWalletConnect } from "./wallet-connect";
@@ -80,6 +81,9 @@ export const getWallet = (type: WalletType = useGrazInternalStore.getState().wal
       case WalletType.METAMASK_SNAP_LEAP: {
         return getMetamaskSnapLeap();
       }
+      case WalletType.METAMASK_SNAP_COSMOS: {
+        return getMetamaskSnapCosmos();
+      }
       case WalletType.STATION: {
         return getStation();
       }
diff --git a/packages/graz/src/actions/wallet/metamask-snap/index.ts b/packages/graz/src/actions/wallet/leap-metamask-snap/index.ts
similarity index 94%
rename from packages/graz/src/actions/wallet/metamask-snap/index.ts
rename to packages/graz/src/actions/wallet/leap-metamask-snap/index.ts
index 3aed639d..83394179 100644
--- a/packages/graz/src/actions/wallet/metamask-snap/index.ts
+++ b/packages/graz/src/actions/wallet/leap-metamask-snap/index.ts
@@ -11,7 +11,7 @@ import type {
 import Long from "long";
 
 import { useGrazInternalStore } from "../../../store";
-import type { SignAminoParams, SignDirectParams, Wallet } from "../../../types/wallet";
+import type { KnownKeys, SignAminoParams, SignDirectParams, Wallet } from "../../../types/wallet";
 import type { ChainId } from "../../../utils/multi-chain";
 import type { GetSnapsResponse, Snap } from "./types";
 
@@ -20,11 +20,7 @@ export interface GetMetamaskSnap {
   params?: Record<string, unknown>;
 }
 
-export interface KnownKeys {
-  [key: string]: Key
-}
-
-const knownKeysMap: KnownKeys = {};
+const metamaskSnapLeapKeysMap: KnownKeys = {};
 
 /**
  * Function to return {@link Wallet} object and throws and error if it does not exist on `window`.
@@ -161,8 +157,9 @@ export const getMetamaskSnap = (params?: GetMetamaskSnap): Wallet => {
     };
 
     // getKey from @leapwallet/cosmos-snap-provider return type is wrong
-    const getKey = async (chainId: string): Promise<Key> => {      
-      if (typeof knownKeysMap[chainId] !== 'undefined') return knownKeysMap[chainId] as Key;
+    const getKey = async (chainId: string): Promise<Key> => {
+      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+      if (typeof metamaskSnapLeapKeysMap[chainId] !== "undefined") return metamaskSnapLeapKeysMap[chainId]!;
 
       // eslint-disable-next-line @typescript-eslint/no-explicit-any
       const res: any = await ethereum.request({
@@ -187,8 +184,8 @@ export const getMetamaskSnap = (params?: GetMetamaskSnap): Wallet => {
       delete res.pubkey;
 
       // Cache the key for future use
-      knownKeysMap[chainId] = res;
-      return knownKeysMap[chainId] as Key;
+      metamaskSnapLeapKeysMap[chainId] = res;
+      return metamaskSnapLeapKeysMap[chainId]!;
     };
 
     const getAccount = async (chainId: string): Promise<AccountData> => {
diff --git a/packages/graz/src/actions/wallet/metamask-snap/leap.ts b/packages/graz/src/actions/wallet/leap-metamask-snap/leap.ts
similarity index 100%
rename from packages/graz/src/actions/wallet/metamask-snap/leap.ts
rename to packages/graz/src/actions/wallet/leap-metamask-snap/leap.ts
diff --git a/packages/graz/src/actions/wallet/metamask-snap/types.ts b/packages/graz/src/actions/wallet/leap-metamask-snap/types.ts
similarity index 100%
rename from packages/graz/src/actions/wallet/metamask-snap/types.ts
rename to packages/graz/src/actions/wallet/leap-metamask-snap/types.ts
diff --git a/packages/graz/src/cli.mjs b/packages/graz/src/cli.mjs
index b9bcec0e..d19d394b 100755
--- a/packages/graz/src/cli.mjs
+++ b/packages/graz/src/cli.mjs
@@ -188,6 +188,23 @@ const makeRecord = async (client, { filter = "" } = {}) => {
         coinGeckoId: mainAsset.coingecko_id,
       };
 
+      const feeCurrencies = chain.fees?.fee_tokens.map((token) => ({
+        coinDenom: chain.assets?.find((asset) => asset.denom === token.denom)?.denom_units.at(-1)?.denom || token.denom,
+        coinMinimalDenom:
+          chain.assets?.find((asset) => asset.denom === token.denom)?.denom_units[0]?.denom || token.denom,
+        coinDecimals: Number(chain.assets?.find((asset) => asset.denom === token.denom)?.decimals),
+        coinGeckoId: chain.assets?.find((asset) => asset.denom === token.denom)?.coingecko_id || "",
+        gasPriceStep: {
+          low: Number(token.low_gas_price),
+          average: Number(token.average_gas_price),
+          high: Number(token.high_gas_price),
+        },
+      }));
+
+      if (!feeCurrencies) {
+        throw new Error(`⚠️\t${chain.name} has no fee currencies, skipping codegen...`);
+      }
+
       record[chain.path] = {
         chainId: chain.chain_id,
         currencies: chain.assets.map((asset) => ({
@@ -200,7 +217,7 @@ const makeRecord = async (client, { filter = "" } = {}) => {
         rpc: apis.rpc[0].address || "",
         bech32Config: Bech32Address.defaultBech32Config(chain.bech32_prefix),
         chainName: chain.chain_name,
-        feeCurrencies: [nativeCurrency],
+        feeCurrencies,
         stakeCurrency: nativeCurrency,
         bip44: {
           coinType: chain.slip44 ?? 0,
diff --git a/packages/graz/src/index.ts b/packages/graz/src/index.ts
index 054b7100..ef65fb24 100644
--- a/packages/graz/src/index.ts
+++ b/packages/graz/src/index.ts
@@ -6,7 +6,7 @@ export * from "./actions/wallet";
 export * from "./actions/wallet/cosmostation";
 export * from "./actions/wallet/keplr";
 export * from "./actions/wallet/leap";
-export * from "./actions/wallet/metamask-snap/leap";
+export * from "./actions/wallet/leap-metamask-snap/leap";
 export * from "./actions/wallet/vectis";
 export * from "./actions/wallet/wallet-connect";
 export * from "./actions/wallet/wallet-connect/cosmostation";
diff --git a/packages/graz/src/types/wallet.ts b/packages/graz/src/types/wallet.ts
index 6ac63304..5fce7ca1 100644
--- a/packages/graz/src/types/wallet.ts
+++ b/packages/graz/src/types/wallet.ts
@@ -1,4 +1,4 @@
-import type { Keplr, KeplrIntereactionOptions } from "@keplr-wallet/types";
+import type { Keplr, KeplrIntereactionOptions, Key } from "@keplr-wallet/types";
 
 export enum WalletType {
   KEPLR = "keplr",
@@ -14,6 +14,8 @@ export enum WalletType {
   WC_COSMOSTATION_MOBILE = "wc_cosmostation_mobile",
   // eslint-disable-next-line @typescript-eslint/naming-convention
   METAMASK_SNAP_LEAP = "metamask_snap_leap",
+  // eslint-disable-next-line @typescript-eslint/naming-convention
+  METAMASK_SNAP_COSMOS = "metamask_snap_cosmos",
   STATION = "station",
   XDEFI = "xdefi",
   CAPSULE = "capsule",
@@ -32,6 +34,7 @@ export const WALLET_TYPES = [
   WalletType.STATION,
   WalletType.XDEFI,
   WalletType.CAPSULE,
+  WalletType.METAMASK_SNAP_COSMOS,
 ];
 
 export type Wallet = Pick<
@@ -55,3 +58,5 @@ export type Wallet = Pick<
 
 export type SignDirectParams = Parameters<Wallet["signDirect"]>;
 export type SignAminoParams = Parameters<Wallet["signAmino"]>;
+
+export type KnownKeys = Record<string, Key>;
diff --git a/packages/graz/src/utils/cli/clack.ts b/packages/graz/src/utils/cli/clack.ts
deleted file mode 100644
index 12940c54..00000000
--- a/packages/graz/src/utils/cli/clack.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import * as p from "@clack/prompts";
-
-// https://github.com/natemoo-re/clack/blob/90f8e3d762e96fde614fdf8da0529866649fafe2/packages/prompts/src/index.ts#L785-L799
-export interface Task {
-  title: string;
-  task: (message: (string: string) => void) => string | Promise<string> | Promise<void>;
-  enabled?: boolean;
-}
-
-// https://github.com/natemoo-re/clack/blob/90f8e3d762e96fde614fdf8da0529866649fafe2/packages/prompts/src/index.ts#L804-L813
-export const tasks = async ($tasks: Task[]) => {
-  for await (const task of $tasks) {
-    if (task.enabled === false) continue;
-    const s = p.spinner();
-    s.start(task.title);
-    const result = await task.task(s.message);
-    s.stop(result || task.title);
-  }
-};
-
-export const withSpinner = <T>(fn: (s: ReturnType<typeof p.spinner>) => T) => {
-  const s = p.spinner();
-  return fn(s);
-};
-
-export * from "@clack/prompts";
diff --git a/packages/graz/src/utils/cli/prompt-opt.ts b/packages/graz/src/utils/cli/prompt-opt.ts
deleted file mode 100644
index a4f7fe77..00000000
--- a/packages/graz/src/utils/cli/prompt-opt.ts
+++ /dev/null
@@ -1,75 +0,0 @@
-import * as fs from "node:fs";
-
-import type { GenerateOptions, ParsedGenerateOptions } from "wadesta";
-
-import * as p from "./clack";
-
-export const DEFAULT_OUT_DIR = "./generated/chains";
-export const DEFAULT_REGISTRY_SRC = "github:cosmos/chain-registry";
-export const DEFAULT_REGISTRY_TMP_PREFIX = "wadesta-registry";
-
-type ClackGenerateOptions = {
-  [K in keyof GenerateOptions]: (NonNullable<GenerateOptions[K]> extends string ? string : GenerateOptions[K]) | symbol;
-};
-
-export const promptParsedOptions = async (
-  args: ParsedGenerateOptions,
-  overrides: GenerateOptions = {},
-): Promise<GenerateOptions> => {
-  if (!args.isActuallyInteractive) return args;
-  if (overrides.interactive === false || args.interactive === false) return args;
-  return p.group<ClackGenerateOptions>(
-    {
-      registry: async () => {
-        if ("registry" in overrides) return overrides.registry;
-        if (args.registrySrc) return "local";
-        if (args.registry) return args.registry;
-        return p.text({
-          message: "Enter a chain registry source",
-          placeholder: DEFAULT_REGISTRY_SRC,
-          defaultValue: DEFAULT_REGISTRY_SRC,
-        });
-      },
-      registrySrc: async ({ results }) => {
-        if ("registrySrc" in overrides) return overrides.registrySrc;
-        if (args.registrySrc) return args.registrySrc;
-        if (results.registry !== "local") return;
-        return p.text({
-          message: "Enter local registry path",
-          placeholder: "./path/to/registry",
-          validate: (val) => {
-            try {
-              fs.lstatSync(val);
-            } catch {
-              return "Enter a valid path";
-            }
-          },
-        });
-      },
-      outDir: async () => {
-        if ("outDir" in overrides) return overrides.outDir;
-        if (args.outDir) return args.outDir;
-        return p.text({
-          message: "Enter generated client output directory",
-          placeholder: DEFAULT_OUT_DIR,
-          defaultValue: DEFAULT_OUT_DIR,
-        });
-      },
-      merged: async () => {
-        if ("merged" in overrides) return overrides.merged;
-        if (args.merged) return args.merged;
-        return p.confirm({
-          message: "Merge variables? (true: `chainIds`, false: `mainnetChainIds`, `testnetChainIds`, and `chainIds`)",
-          active: "merge",
-          inactive: "do not merge",
-        });
-      },
-    },
-    {
-      onCancel: () => {
-        p.cancel("Operation cancelled.");
-        process.exit(1);
-      },
-    },
-  );
-};
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 4072675c..cc3c4b5f 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -128,7 +128,7 @@ importers:
         version: 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.21)(react@18.2.0)
       '@graz-sh/types':
         specifier: ^0.0.4
-        version: 0.0.4(long@5.2.3)
+        version: 0.0.4(long@4.0.0)
       bignumber.js:
         specifier: ^9.1.2
         version: 9.1.2
@@ -212,9 +212,6 @@ importers:
 
   packages/graz:
     dependencies:
-      '@clack/prompts':
-        specifier: ^0.7.0
-        version: 0.7.0
       '@cosmjs/cosmwasm-stargate':
         specifier: '*'
         version: 0.31.0
@@ -230,6 +227,9 @@ importers:
       '@cosmjs/tendermint-rpc':
         specifier: '*'
         version: 0.31.0
+      '@cosmsnap/snapper':
+        specifier: ^0.1.29
+        version: 0.1.29
       '@keplr-wallet/cosmos':
         specifier: ^0.12.20
         version: 0.12.20
@@ -273,11 +273,8 @@ importers:
         specifier: 0.0.6
         version: 0.0.6
       long:
-        specifier: ^4
+        specifier: '4'
         version: 4.0.0
-      wadesta:
-        specifier: ^0.0.5
-        version: 0.0.5(long@4.0.0)
       zustand:
         specifier: ^4.5.2
         version: 4.5.2(@types/react@18.2.21)(react@18.2.0)
@@ -4385,23 +4382,6 @@ packages:
       react: 18.2.0
     dev: false
 
-  /@clack/core@0.3.3:
-    resolution: {integrity: sha512-5ZGyb75BUBjlll6eOa1m/IZBxwk91dooBWhPSL67sWcLS0zt9SnswRL0l26TVdBhb0wnWORRxUn//uH6n4z7+A==}
-    dependencies:
-      picocolors: 1.0.0
-      sisteransi: 1.0.5
-    dev: false
-
-  /@clack/prompts@0.7.0:
-    resolution: {integrity: sha512-0MhX9/B4iL6Re04jPrttDm+BsP8y6mS7byuv0BvXgdXhbV5PdlsHt55dvNsuBCPZ7xq1oTAOOuotR9NFbQyMSA==}
-    dependencies:
-      '@clack/core': 0.3.3
-      picocolors: 1.0.0
-      sisteransi: 1.0.5
-    dev: false
-    bundledDependencies:
-      - is-unicode-supported
-
   /@cmfcmf/docusaurus-search-local@1.1.0(@docusaurus/core@2.4.1)(search-insights@2.8.2):
     resolution: {integrity: sha512-0IVb/aA0IK8ZlktuxmgXmluXfcSpo6Vdd2nG21y1aOH9nVYnPP231Dn0H8Ng9Qf9ronQQCDWHnuWpYOr9rUrEQ==}
     peerDependencies:
@@ -4462,7 +4442,7 @@ packages:
   /@confio/ics23@0.6.8:
     resolution: {integrity: sha512-wB6uo+3A50m0sW/EWcU64xpV/8wShZ6bMTa7pF8eYsTrSkQA7oLUIJcs/wb8g4y2Oyq701BaGiO6n/ak5WXO1w==}
     dependencies:
-      '@noble/hashes': 1.3.0
+      '@noble/hashes': 1.4.0
       protobufjs: 6.11.3
     dev: false
 
@@ -4609,7 +4589,7 @@ packages:
       '@cosmjs/encoding': 0.31.3
       '@cosmjs/math': 0.31.3
       '@cosmjs/utils': 0.31.3
-      '@noble/hashes': 1.3.2
+      '@noble/hashes': 1.4.0
       bn.js: 5.2.1
       elliptic: 6.5.4
       libsodium-wrappers-sumo: 0.7.11
@@ -5057,6 +5037,15 @@ packages:
     resolution: {integrity: sha512-VBhAgzrrYdIe0O5IbKRqwszbQa7ZyQLx9nEQuHQ3HUplQW7P44COG/ye2n6AzCudtqxmwdX7nyX8ta1J07GoqA==}
     dev: false
 
+  /@cosmsnap/snapper@0.1.29:
+    resolution: {integrity: sha512-pnCdpIJzezKSeRMZzIA9A/LZxpwvXjN8XZK3J8rlX5xrVPrYvETL0hC7KeAxnRle9LcwKqv+AwebrHrHQwpvEQ==}
+    engines: {node: '>=16.0.0'}
+    dependencies:
+      '@keplr-wallet/proto-types': 0.12.12
+      '@keplr-wallet/types': 0.12.12
+      ses: 0.18.4
+    dev: false
+
   /@discoveryjs/json-ext@0.5.7:
     resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==}
     engines: {node: '>=10.0.0'}
@@ -6830,14 +6819,6 @@ packages:
       long: 4.0.0
     dev: false
 
-  /@graz-sh/types@0.0.4(long@5.2.3):
-    resolution: {integrity: sha512-9kj2MzQs5qIzr7eQUPeZv6/5N+To5hauPd7rQ/zmXpXPOhhZ4qDiU0Rceknm4q2ZsG3ed4uEYogd8JzyJI5dQA==}
-    peerDependencies:
-      long: '*'
-    dependencies:
-      long: 5.2.3
-    dev: false
-
   /@hapi/hoek@9.3.0:
     resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==}
 
@@ -7006,6 +6987,13 @@ packages:
       sha.js: 2.4.11
     dev: false
 
+  /@keplr-wallet/proto-types@0.12.12:
+    resolution: {integrity: sha512-iAqqNlJpxu/8j+SwOXEH2ymM4W0anfxn+eNeWuqz2c/0JxGTWeLURioxQmCtewtllfHdDHHcoQ7/S+NmXiaEgQ==}
+    dependencies:
+      long: 4.0.0
+      protobufjs: 6.11.3
+    dev: false
+
   /@keplr-wallet/proto-types@0.12.20:
     resolution: {integrity: sha512-PZxifNq5SkGD8GSzAUJ57A/qK+TI+mqvYPO7X2PHjf8nGZyjLlTzFXj/oLEOrUYGuMWKOYgVY35nBhtkY9TlDA==}
     dependencies:
@@ -7017,6 +7005,12 @@ packages:
     resolution: {integrity: sha512-WOxQJrHz/lqN/XudUbZY+H41hhtgrdGubJn8VnTJ4OomfEdfBX4GR+zuTkN0r+Vn64/xKFeeOXWdWMmJK0eOnw==}
     dev: false
 
+  /@keplr-wallet/types@0.12.12:
+    resolution: {integrity: sha512-fo6b8j9EXnJukGvZorifJWEm1BPIrvaTLuu5PqaU5k1ANDasm/FL1NaUuaTBVvhRjINtvVXqYpW/rVUinA9MBA==}
+    dependencies:
+      long: 4.0.0
+    dev: false
+
   /@keplr-wallet/types@0.12.20:
     resolution: {integrity: sha512-uCVu1WYv908eeK0Dlrltthf6cL9ThHdmmShhVXHx/ZzXUbRn1bfsEKwo83nggd5985XqdSiwXFmprskTHGO/pQ==}
     dependencies:
@@ -11081,7 +11075,7 @@ packages:
   /axios@0.21.4:
     resolution: {integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==}
     dependencies:
-      follow-redirects: 1.15.2
+      follow-redirects: 1.15.6
     transitivePeerDependencies:
       - debug
     dev: false
@@ -12081,10 +12075,6 @@ packages:
     resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==}
     dev: false
 
-  /colorette@1.2.1:
-    resolution: {integrity: sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==}
-    dev: false
-
   /colorette@2.0.20:
     resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==}
     dev: false
@@ -14583,16 +14573,6 @@ packages:
       tslib: 2.6.2
     dev: false
 
-  /follow-redirects@1.15.2:
-    resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==}
-    engines: {node: '>=4.0'}
-    peerDependencies:
-      debug: '*'
-    peerDependenciesMeta:
-      debug:
-        optional: true
-    dev: false
-
   /follow-redirects@1.15.6:
     resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==}
     engines: {node: '>=4.0'}
@@ -14776,10 +14756,6 @@ packages:
     resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
     dev: true
 
-  /fuzzysearch@1.0.3:
-    resolution: {integrity: sha512-s+kNWQuI3mo9OALw0HJ6YGmMbLqEufCh2nX/zzV5CrICQ/y4AwPxM+6TIiF9ItFCHXFCyM/BfCCmN57NTIJuPg==}
-    dev: false
-
   /gauge@4.0.4:
     resolution: {integrity: sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==}
     engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
@@ -15071,10 +15047,6 @@ packages:
     dependencies:
       define-properties: 1.2.0
 
-  /globalyzer@0.1.0:
-    resolution: {integrity: sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==}
-    dev: false
-
   /globby@11.1.0:
     resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==}
     engines: {node: '>=10'}
@@ -15097,10 +15069,6 @@ packages:
       slash: 4.0.0
     dev: false
 
-  /globrex@0.1.2:
-    resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==}
-    dev: false
-
   /google-protobuf@3.21.2:
     resolution: {integrity: sha512-3MSOYFO5U9mPGikIYCzK0SaThypfGgS6bHqrUGXG3DPHCrb+txNqeEcns1W0lkGfk0rCyNXm7xB9rMxnCiZOoA==}
     dev: false
@@ -17411,11 +17379,6 @@ packages:
       '@motionone/vue': 10.16.2
     dev: false
 
-  /mri@1.1.6:
-    resolution: {integrity: sha512-oi1b3MfbyGa7FJMP9GmLTttni5JoICpYBRlq+x5V16fZbLsnL9N3wFqqIm/nIG43FjUFkFh9Epzp/kzUGUnJxQ==}
-    engines: {node: '>=4'}
-    dev: false
-
   /mri@1.2.0:
     resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
     engines: {node: '>=4'}
@@ -20775,6 +20738,10 @@ packages:
       - supports-color
     dev: false
 
+  /ses@0.18.4:
+    resolution: {integrity: sha512-Ph0PC38Q7uutHmMM9XPqA7rp/2taiRwW6pIZJwTr4gz90DtrBvy/x7AmNPH2uqNPhKriZpYKvPi1xKWjM9xJuQ==}
+    dev: false
+
   /set-blocking@2.0.0:
     resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
     dev: false
@@ -21614,24 +21581,6 @@ packages:
     resolution: {integrity: sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==}
     dev: false
 
-  /tiged@2.12.5:
-    resolution: {integrity: sha512-divl34KaEXBUO8N2m5UcMdQZjqLZRzEq+ychP5bNYSGF0JQq1j6m4uF7WuQOCRomlBeu5vJfjkXtyYumijX+bA==}
-    engines: {node: '>=8.0.0'}
-    hasBin: true
-    dependencies:
-      colorette: 1.2.1
-      enquirer: 2.3.6
-      fs-extra: 10.1.0
-      fuzzysearch: 1.0.3
-      https-proxy-agent: 5.0.0
-      mri: 1.1.6
-      rimraf: 3.0.2
-      tar: 6.2.0
-      tiny-glob: 0.2.8
-    transitivePeerDependencies:
-      - supports-color
-    dev: false
-
   /timed-out@4.0.1:
     resolution: {integrity: sha512-G7r3AhovYtr5YKOWQkta8RKAPb+J9IsO4uVmzjl8AZwfhs8UcUwTiD6gcJYSgOtzyjvQKrKYn41syHbUWMkafA==}
     engines: {node: '>=0.10.0'}
@@ -21644,13 +21593,6 @@ packages:
       setimmediate: 1.0.5
     dev: true
 
-  /tiny-glob@0.2.8:
-    resolution: {integrity: sha512-vkQP7qOslq63XRX9kMswlby99kyO5OvKptw7AMwBVMjXEI7Tb61eoI5DydyEMOseyGS5anDN1VPoVxEvH01q8w==}
-    dependencies:
-      globalyzer: 0.1.0
-      globrex: 0.1.2
-    dev: false
-
   /tiny-invariant@1.3.1:
     resolution: {integrity: sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==}
     dev: false
@@ -22755,22 +22697,6 @@ packages:
     resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==}
     dev: true
 
-  /wadesta@0.0.5(long@4.0.0):
-    resolution: {integrity: sha512-nhwUb/dDUqwI8I4f8N5/5V78aZx5YRoC0+mnu14KKG+1B/HevCa0PcRhM1tLZz4R5ZQ2J900FPlSSIcjSa2GaA==}
-    hasBin: true
-    peerDependencies:
-      long: '*'
-    peerDependenciesMeta:
-      long:
-        optional: true
-    dependencies:
-      '@graz-sh/types': 0.0.4(long@4.0.0)
-      degit: /tiged@2.12.5
-      long: 4.0.0
-    transitivePeerDependencies:
-      - supports-color
-    dev: false
-
   /wagmi@1.4.13(@types/react@18.2.21)(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2)(viem@1.5.3)(zod@3.20.6):
     resolution: {integrity: sha512-AScVYFjqNt1wMgL99Bob7MLdhoTZ3XKiOZL5HVBdy4W1sh7QodA3gQ8IsmTuUrQ7oQaTxjiXEhwg7sWNrPBvJA==}
     peerDependencies: