From be7c89090c6f844b00e4f44b37de92f4a4bbb2ce Mon Sep 17 00:00:00 2001 From: Justin <328965+justinbarry@users.noreply.github.com> Date: Tue, 16 Apr 2024 20:06:02 -0700 Subject: [PATCH] * Add new api file which verifies signatures * Add return type to getAccounts method * Update test for SignArbSecp256k1HdWallet with new import and signature verification * Add signature and grant verification in API * Add example of how to verify signature in demo --- .changeset/slimy-deers-hammer.md | 5 + .changeset/tall-games-live.md | 5 + apps/demo-app/.eslintrc.js | 4 + apps/demo-app/next-env.d.ts | 1 + apps/demo-app/package.json | 8 +- apps/demo-app/pages/api/check-signature.ts | 205 ++++++++++++++++++ apps/demo-app/src/app/page.tsx | 38 +--- .../src/components/blocking-button.tsx | 45 ++++ apps/demo-app/src/components/sign-arb.tsx | 190 ++++++++++++++++ apps/demo-app/tsconfig.json | 20 +- .../src/SignArbSecp256k1HdWallet.ts | 18 +- .../tests/SignArbSecp256k1HdWallet.test.ts | 78 +++++-- pnpm-lock.yaml | 175 +++++++++++++-- 13 files changed, 700 insertions(+), 92 deletions(-) create mode 100644 .changeset/slimy-deers-hammer.md create mode 100644 .changeset/tall-games-live.md create mode 100644 apps/demo-app/pages/api/check-signature.ts create mode 100644 apps/demo-app/src/components/blocking-button.tsx create mode 100644 apps/demo-app/src/components/sign-arb.tsx diff --git a/.changeset/slimy-deers-hammer.md b/.changeset/slimy-deers-hammer.md new file mode 100644 index 00000000..7fc485b4 --- /dev/null +++ b/.changeset/slimy-deers-hammer.md @@ -0,0 +1,5 @@ +--- +"demo-app": minor +--- + +Add an example signature verification api and an accompanying UI element to execute it diff --git a/.changeset/tall-games-live.md b/.changeset/tall-games-live.md new file mode 100644 index 00000000..ea936937 --- /dev/null +++ b/.changeset/tall-games-live.md @@ -0,0 +1,5 @@ +--- +"@burnt-labs/abstraxion-core": patch +--- + +Add a return type to SignArbSecp256k1HdWallet's `getAccounts` method diff --git a/apps/demo-app/.eslintrc.js b/apps/demo-app/.eslintrc.js index d25be95d..277de130 100644 --- a/apps/demo-app/.eslintrc.js +++ b/apps/demo-app/.eslintrc.js @@ -1,4 +1,8 @@ module.exports = { root: true, extends: ["@burnt-labs/eslint-config-custom/next"], + rules: { + "no-console": ["error", { allow: ["warn", "error"] }], + "no-alert": "off", + }, }; diff --git a/apps/demo-app/next-env.d.ts b/apps/demo-app/next-env.d.ts index 4f11a03d..fd36f949 100644 --- a/apps/demo-app/next-env.d.ts +++ b/apps/demo-app/next-env.d.ts @@ -1,5 +1,6 @@ /// /// +/// // NOTE: This file should not be edited // see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/apps/demo-app/package.json b/apps/demo-app/package.json index 21de56df..d51d6178 100644 --- a/apps/demo-app/package.json +++ b/apps/demo-app/package.json @@ -10,9 +10,15 @@ }, "dependencies": { "@burnt-labs/abstraxion": "workspace:*", + "@burnt-labs/abstraxion-core": "workspace:*", + "@burnt-labs/constants": "workspace:*", "@burnt-labs/signers": "workspace:*", "@burnt-labs/ui": "workspace:*", + "@cosmjs/amino": "^0.32.3", "@cosmjs/cosmwasm-stargate": "^0.32.2", + "@heroicons/react": "^2.1.4", + "@keplr-wallet/cosmos": "^0.12.80", + "cosmjs-types": "^0.9.0", "next": "^14.0.3", "react": "^18.2.0", "react-dom": "^18.2.0" @@ -31,4 +37,4 @@ "tailwindcss": "^3.2.4", "typescript": "^5.2.2" } -} \ No newline at end of file +} diff --git a/apps/demo-app/pages/api/check-signature.ts b/apps/demo-app/pages/api/check-signature.ts new file mode 100644 index 00000000..571bbc5e --- /dev/null +++ b/apps/demo-app/pages/api/check-signature.ts @@ -0,0 +1,205 @@ +import type { NextApiRequest, NextApiResponse } from "next"; +import { verifyADR36Amino } from "@keplr-wallet/cosmos"; + +// This import will need to change based on the chain you are confirming against. +import { testnetChainInfo } from "@burnt-labs/constants"; +import { QueryGrantsResponse } from "cosmjs-types/cosmos/authz/v1beta1/query"; + +function isString(test: unknown): test is string { + return typeof test === "string"; +} + +/** + * Verify that the given XION signature corresponds to the given message and address. + * + * @param address - The address that is supposed to have signed the message. + * @param pubKey - The public key associated with the session address base64 encoded. + * @param messageString - The message that is supposed to have been signed. + * @param signature - The signature to verify against the message and address. + * @returns True if the signature is valid, false otherwise. + */ +export function verifyXionSignature( + address: string, + pubKey: string, + messageString: string, + signature: string, +): boolean { + const signatureBuffer = Buffer.from(signature, "base64"); + const uint8Signature = new Uint8Array(signatureBuffer); // Convert the buffer to an Uint8Array + const pubKeyValueBuffer = Buffer.from(pubKey, "base64"); // Decode the base64 encoded value + const pubKeyUint8Array = new Uint8Array(pubKeyValueBuffer); // Convert the buffer to an Uint8Array + + return verifyADR36Amino( + "xion", + address, + messageString, + pubKeyUint8Array, + uint8Signature, + ); +} + +/** + * Verifies the Xion signature and grants for a given address, session address, public key, + * message string, and signature. + * + * @param address - The address to verify the grants for. + * @param sessionAddress - The session address to verify the grants for. + * @param pubKey - The public key associated with the session address base64 encoded. + * @param messageString - The message string to verify the signature against. + * @param signature - The signature to verify. + * + * @returns Promise - A promise that resolves to true if the Xion signature and grants are valid, + * or false otherwise. + */ +export async function verifyXionSignatureAndGrants( + address: string, + sessionAddress: string, + pubKey: string, + messageString: string, + signature: string, +): Promise { + const isValid = verifyXionSignature( + sessionAddress, + pubKey, + messageString, + signature, + ); + if (!isValid) { + return false; + } + + return verifyGrants(address, sessionAddress); +} + +/** + * Verifies if grants have been given by a granter to a grantee. + * + * @param grantee - The address of the granter. + * @param granter - The address of the grantee. + * + * @returns Promise - A promise that resolves to a boolean indicating whether ANY grants have been given. + */ +export async function verifyGrants( + granter: string, + grantee: string, +): Promise { + const baseUrl = `${testnetChainInfo.rest}/cosmos/authz/v1beta1/grants`; + const url = new URL(baseUrl); + const params = new URLSearchParams({ + grantee, + granter, + }); + url.search = params.toString(); + const data = await fetch(url, { + cache: "no-store", + }) + .then((response): Promise => response.json()) + .catch((err) => { + console.error("Could not fetch grants info", err); + }); + + return Boolean(data && data.grants.length > 0); +} + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse, +) { + const { query, method } = req; + + if (method === "GET") { + const { + userSessionAddress, + userSessionPubKey, + metaAccountAddress, + message, + signature, + } = query; + + const errors: string[] = []; + if (!userSessionAddress) { + errors.push("userSessionAddress is required"); + } + + if (!userSessionPubKey) { + errors.push("userSessionPubKey is required"); + } + + if (!metaAccountAddress && typeof metaAccountAddress === "string") { + errors.push("itemId is required"); + } + + if (!message && typeof message === "string") { + errors.push("message is required"); + } + + if (!signature && typeof signature === "string") { + errors.push("signature is required"); + } + + if (errors.length > 0) { + res.status(400).json({ errors }); + return; + } + + // These aid TS type inference. You can pull in a more complex validation package to handle this like io-ts + if (!isString(userSessionAddress)) { + res + .status(400) + .json({ errors: ["userSessionAddress is required to be a string"] }); + return; + } + + if (!isString(userSessionPubKey)) { + res + .status(400) + .json({ errors: ["userSessionPubKey is required to be a string"] }); + return; + } + + if (!isString(metaAccountAddress)) { + res + .status(400) + .json({ errors: ["metaAccountAddress is required to be a string"] }); + return; + } + + if (!isString(message)) { + res.status(400).json({ errors: ["message is required to be a string"] }); + return; + } + + if (!isString(signature)) { + res + .status(400) + .json({ errors: ["signature is required to be a string"] }); + return; + } + + /* + * Confirming account "ownership" is a three-step process + * 1. Confirm the signature passed is valid for the temporary userSessionAddress + * 2. Pull any grant bestowed to the userSessionAddress on chain + * 3. Check that AT LEAST one grant exists of any type exists between the userSessionAddress and the metaAccountAddress + * + * NOTE: If the userSessionAccount has not submitted a transaction to the chain, the PubKey will be unknown. + * Therefore it must be passed as a api query parameter (In this example base64 encoded). + * + **/ + + const isValid = await verifyXionSignatureAndGrants( + metaAccountAddress, + userSessionAddress, + userSessionPubKey, + message, + signature, + ); + + res.status(200).json({ + valid: isValid, + }); + } else { + res.setHeader("Allow", ["GET"]); + res.status(405).end(`Method ${method} Not Allowed`); + } +} diff --git a/apps/demo-app/src/app/page.tsx b/apps/demo-app/src/app/page.tsx index 0826f42f..c2eafddd 100644 --- a/apps/demo-app/src/app/page.tsx +++ b/apps/demo-app/src/app/page.tsx @@ -7,14 +7,16 @@ import { useAbstraxionSigningClient, useModal, } from "@burnt-labs/abstraxion"; -import { Button, Input } from "@burnt-labs/ui"; +import { Button } from "@burnt-labs/ui"; import "@burnt-labs/ui/dist/index.css"; import type { ExecuteResult } from "@cosmjs/cosmwasm-stargate"; +import { SignArb } from "../components/sign-arb.tsx"; const seatContractAddress = "xion1z70cvc08qv5764zeg3dykcyymj5z6nu4sqr7x8vl4zjef2gyp69s9mmdka"; type ExecuteResultOrUndefined = ExecuteResult | undefined; + export default function Page(): JSX.Element { // Abstraxion hooks const { data: account } = useAbstraxionAccount(); @@ -28,7 +30,6 @@ export default function Page(): JSX.Element { const [loading, setLoading] = useState(false); const [executeResult, setExecuteResult] = useState(undefined); - const [arbitraryMessage, setArbitraryMessage] = useState(""); const blockExplorerUrl = `https://explorer.burnt.com/xion-testnet-1/tx/${executeResult?.transactionHash}`; @@ -43,14 +44,6 @@ export default function Page(): JSX.Element { const oneYearFromNow = new Date(); oneYearFromNow.setFullYear(oneYearFromNow.getFullYear() + 1); - async function handleSign(): Promise { - if (client?.granteeAddress) { - const response = await signArb?.(client.granteeAddress, arbitraryMessage); - // eslint-disable-next-line no-console -- We log this for testing purposes. - console.log(response); - } - } - async function claimSeat(): Promise { setLoading(true); const msg = { @@ -128,30 +121,7 @@ export default function Page(): JSX.Element { LOGOUT ) : null} - {signArb ? ( -
-

- SIGN ARBITRARY MESSAGE -

- { - setArbitraryMessage(e.target.value); - }} - placeholder="Message..." - value={arbitraryMessage} - /> - -
- ) : null} + {signArb ? : null} ) : null} ; +} + +export function BlockingButton({ text, onExecute }: BlockingButtonProps) { + const [loading, setLoading] = useState(false); + + const handleClick = async () => { + try { + setLoading(true); + // Execute the supplied function + await onExecute(); + } catch (err) { + console.error(err); + } finally { + setLoading(false); + } + }; + + return ( + + ); +} diff --git a/apps/demo-app/src/components/sign-arb.tsx b/apps/demo-app/src/components/sign-arb.tsx new file mode 100644 index 00000000..0a69ef7c --- /dev/null +++ b/apps/demo-app/src/components/sign-arb.tsx @@ -0,0 +1,190 @@ +import { Button, Input } from "@burnt-labs/ui"; +import { useState } from "react"; +import { + useAbstraxionAccount, + useAbstraxionSigningClient, +} from "@burnt-labs/abstraxion"; +import type { GranteeSignerClient } from "@burnt-labs/abstraxion-core"; +import { BlockingButton } from "./blocking-button.tsx"; + +const copyToClipboard = (textToCopy: string) => async () => { + await window.navigator.clipboard.writeText(textToCopy); +}; + +// This component is a wizard of sorts showcasing the ability to sign and verify arbitrary ADR-036 signatures +export function SignArb(): React.ReactNode { + const [signArbResult, setSignArbResult] = useState(); + const [arbitraryMessage, setArbitraryMessage] = useState(""); + + if (signArbResult) { + return ( + { + setArbitraryMessage(""); + setSignArbResult(undefined); + + // Added to make ESLint happy + return Promise.resolve(); + }} + message={arbitraryMessage} + signature={signArbResult} + /> + ); + } + + return ( + + ); +} + +interface SignArbSignProps { + setResult: (signature: string) => void; + arbitraryMessage: string; + setArbitraryMessage: (signature: string) => void; +} + +function SignArbSign({ + setResult, + arbitraryMessage, + setArbitraryMessage, +}: SignArbSignProps) { + const { client, signArb } = useAbstraxionSigningClient(); + + async function handleSign(): Promise { + if (client?.granteeAddress) { + const response = await signArb?.(client.granteeAddress, arbitraryMessage); + + if (response) setResult(response); + } + } + + return ( +
+

+ SIGN ARBITRARY MESSAGE +

+ { + setArbitraryMessage(e.target.value); + }} + placeholder="Message..." + value={arbitraryMessage} + /> + +
+ ); +} + +interface SignArbVerifyProps { + message: string; + signature: string; + clearSigFn: () => Promise; +} + +const verifySignatureWithApi = async ( + client: GranteeSignerClient, + metaAccountAddress: string, + message: string, + signature: string, +) => { + const granteeAccountData = await client.getGranteeAccountData(); + + if (!granteeAccountData) return false; + + const userSessionAddress = granteeAccountData.address; + const userSessionPubKey = Buffer.from(granteeAccountData.pubkey).toString( + "base64", + ); + + const baseUrl = `${window.location.origin}/api/check-signature`; + const url = new URL(baseUrl); + const params = new URLSearchParams({ + userSessionAddress, + userSessionPubKey, + metaAccountAddress, + message, + signature, + }); + url.search = params.toString(); + const data = await fetch(url, { + cache: "no-store", + }) + .then( + ( + response, + ): Promise<{ + valid: boolean; + }> => response.json(), + ) + .catch((err) => { + console.error("Could not fetch grants info", err); + }); + + if (data && data.valid) { + return true; + } + + return false; +}; + +function SignArbVerify({ message, signature, clearSigFn }: SignArbVerifyProps) { + const { client } = useAbstraxionSigningClient(); + const { data: account } = useAbstraxionAccount(); + + if (!client) return
; + + return ( +
+

+ Signature +

+

{signature}

+

+ User Session Address +

+

+ {client.granteeAddress} +

+

+ Meta Account Address +

+

+ {account.bech32Address} +

+ +
+ { + const result = await verifySignatureWithApi( + client, + account.bech32Address, + message, + signature, + ); + + // eslint-disable-next-line no-alert --- No need for more complex user notification scheme + alert(`You message is ${result ? "valid" : "invalid"}!!!!`); + }} + text="Verify Signature" + /> + + +
+
+ ); +} diff --git a/apps/demo-app/tsconfig.json b/apps/demo-app/tsconfig.json index 10ba1595..e4d07053 100644 --- a/apps/demo-app/tsconfig.json +++ b/apps/demo-app/tsconfig.json @@ -1,9 +1,21 @@ { "extends": "@burnt-labs/tsconfig/nextjs.json", "compilerOptions": { - "plugins": [{ "name": "next" }], - "moduleResolution": "Bundler" + "plugins": [ + { + "name": "next" + } + ], + "moduleResolution": "Bundler", + "strictNullChecks": true }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], - "exclude": ["node_modules"] + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts" + ], + "exclude": [ + "node_modules" + ] } diff --git a/packages/abstraxion-core/src/SignArbSecp256k1HdWallet.ts b/packages/abstraxion-core/src/SignArbSecp256k1HdWallet.ts index 88ec91c6..ae2c3b5c 100644 --- a/packages/abstraxion-core/src/SignArbSecp256k1HdWallet.ts +++ b/packages/abstraxion-core/src/SignArbSecp256k1HdWallet.ts @@ -1,5 +1,5 @@ import { Buffer } from "buffer"; -import { makeSignBytes, type AccountData } from "@cosmjs/proto-signing"; +import { type AccountData, makeSignBytes } from "@cosmjs/proto-signing"; import type { KdfConfiguration } from "@cosmjs/amino"; import { encodeSecp256k1Signature, @@ -10,17 +10,17 @@ import { assert, isNonNullObject } from "@cosmjs/utils"; import { Hash, PrivKeySecp256k1 } from "@keplr-wallet/crypto"; import type { HdPath, Secp256k1Keypair } from "@cosmjs/crypto"; import { - Secp256k1, - Slip10, - Slip10Curve, - stringToPath, - EnglishMnemonic, - Bip39, - Random, Argon2id, + Bip39, + EnglishMnemonic, isArgon2idOptions, pathToString, + Random, + Secp256k1, sha256, + Slip10, + Slip10Curve, + stringToPath, } from "@cosmjs/crypto"; import { fromBase64, @@ -315,7 +315,7 @@ export class SignArbSecp256k1HdWallet { return JSON.stringify(out); } - async getAccounts() { + async getAccounts(): Promise { const accountsWithPrivkeys = await this.getAccountsWithPrivkeys(); return accountsWithPrivkeys.map(({ algo, pubkey, address }) => ({ algo: algo, diff --git a/packages/abstraxion-core/tests/SignArbSecp256k1HdWallet.test.ts b/packages/abstraxion-core/tests/SignArbSecp256k1HdWallet.test.ts index 8b1bdd09..5bc0baf3 100644 --- a/packages/abstraxion-core/tests/SignArbSecp256k1HdWallet.test.ts +++ b/packages/abstraxion-core/tests/SignArbSecp256k1HdWallet.test.ts @@ -1,5 +1,7 @@ -import { TextEncoder, TextDecoder } from "node:util"; -import { SignArbSecp256k1HdWallet } from "@/SignArbSecp256k1HdWallet"; +import { TextDecoder, TextEncoder } from "node:util"; +import { verifyADR36Amino } from "@keplr-wallet/cosmos"; +import { SignArbSecp256k1HdWallet } from "../src/SignArbSecp256k1HdWallet"; +import { AccountData } from "@cosmjs/proto-signing"; global.TextEncoder = TextEncoder; // @ts-expect-error: TextDecoder is not available in testing environment by default. @@ -7,35 +9,67 @@ global.TextDecoder = TextDecoder; describe("SignArbSecp256k1HdWallet", () => { let wallet: SignArbSecp256k1HdWallet; + let account: AccountData; beforeEach(async () => { - // DO NOT USE WALLET IN PRODUCTION - const serialization = JSON.stringify({ - type: "directsecp256k1hdwallet-v1", - kdf: { - algorithm: "argon2id", - params: { - outputLength: 32, - opsLimit: 24, - memLimitKib: 12288, - }, - }, - encryption: { - algorithm: "xchacha20poly1305-ietf", - }, - data: "8AV9HAqwKThQOZ/jW9HCkd89LNUo//W/+Rg+s1pzNp0TuFk3uut6pi9OgIRM2HRnLS68CjOCiZltc09EYmJBBBj5l0oVnPcAyJjcs1nlAPoppKiKqr1TWCYfNx/YhOmdFrghX9tWE9SWaAx5jwQFOvSbVZaWhv2shEShSvhZ/aUcZJDScN+TZFzwyvVFqE0TMpma8ZACDXmr1Mw+rWfy4KkiGV1+shiVsM9owpZfhrCKNzowpIYZJBn5xE/tMA==", + wallet = await SignArbSecp256k1HdWallet.generate(12, { + prefix: "xion", }); - wallet = await SignArbSecp256k1HdWallet.deserialize( - serialization, - "abstraxion", - ); + + [account] = await wallet.getAccounts(); }); test("signArb returns a signature for a valid signer address and message", async () => { - const signerAddress = "xion1cgvua2mkvux6xaw20w4ltjcrs9u3kagfpqd3al"; // Empty test account + const signerAddress = account.address; // Empty test account const message = "test"; const signature = await wallet.signArb(signerAddress, message); expect(signature).toBeDefined(); expect(typeof signature).toBe("string"); }); + + test("example of how to confirm signArb result", async () => { + async function verifyXionSignature( + address: string, + pubKey: string, + messageString: string, + signature: string, + ): Promise { + const signatureBuffer = Buffer.from(signature, "base64"); + const uint8Signature = new Uint8Array(signatureBuffer); // Convert the buffer to an Uint8Array + const pubKeyValueBuffer = Buffer.from(pubKey, "base64"); // Decode the base64 encoded value + const pubKeyUint8Array = new Uint8Array(pubKeyValueBuffer); // Convert the buffer to an Uint8Array + + return verifyADR36Amino( + "xion", + address, + messageString, + pubKeyUint8Array, + uint8Signature, + ); + } + + const granterAddress = + "xion15wvfkv5wkp7dvxquxm3nkhrfy98nahjqpp3a2r5h9tcj29r9wxnq6j5eeh"; + + console.log(account.algo); + const { pubkey, address: granteeAddress } = account; + + const exampleMessage = "Test message"; + + // Passing in as a string since this is likely how be encoded when sent to an api + const base64PubKey = Buffer.from(pubkey).toString("base64"); + const signature = await wallet.signArb(granteeAddress, exampleMessage); + + const result = await verifyXionSignature( + granteeAddress, + base64PubKey, + exampleMessage, + signature, + ); + + expect(result).toBeTruthy(); + + // After the signature is verified, we must check to see if ANY grant that exists between the granter and the grantee. + // Please see `packages/abstraxion-core/src/AbstraxionAuth.ts` or the docs for an example of how to do this. + }); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cc75a869..6795bc42 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -28,7 +28,7 @@ importers: version: 0.5.3(prettier@3.0.3) turbo: specifier: latest - version: 1.11.2 + version: 1.13.3 apps/abstraxion-dashboard: dependencies: @@ -241,15 +241,33 @@ importers: '@burnt-labs/abstraxion': specifier: workspace:* version: link:../../packages/abstraxion + '@burnt-labs/abstraxion-core': + specifier: workspace:* + version: link:../../packages/abstraxion-core + '@burnt-labs/constants': + specifier: workspace:* + version: link:../../packages/constants '@burnt-labs/signers': specifier: workspace:* version: link:../../packages/signers '@burnt-labs/ui': specifier: workspace:* version: link:../../packages/ui + '@cosmjs/amino': + specifier: ^0.32.3 + version: 0.32.3 '@cosmjs/cosmwasm-stargate': specifier: ^0.32.2 version: 0.32.2 + '@heroicons/react': + specifier: ^2.1.4 + version: 2.1.4(react@18.2.0) + '@keplr-wallet/cosmos': + specifier: ^0.12.80 + version: 0.12.80 + cosmjs-types: + specifier: ^0.9.0 + version: 0.9.0 next: specifier: ^14.0.3 version: 14.0.3(@babel/core@7.24.5)(react-dom@18.2.0)(react@18.2.0) @@ -2316,6 +2334,15 @@ packages: '@cosmjs/utils': 0.32.2 dev: false + /@cosmjs/amino@0.32.3: + resolution: {integrity: sha512-G4zXl+dJbqrz1sSJ56H/25l5NJEk/pAPIr8piAHgbXYw88OdAOlpA26PQvk2IbSN/rRgVbvlLTNgX2tzz1dyUA==} + dependencies: + '@cosmjs/crypto': 0.32.3 + '@cosmjs/encoding': 0.32.3 + '@cosmjs/math': 0.32.3 + '@cosmjs/utils': 0.32.3 + dev: false + /@cosmjs/cosmwasm-stargate@0.31.3: resolution: {integrity: sha512-Uv9TmCn3650gdFeZm7SEfUZF3uX3lfJfFhXOk6I2ZLr/FrKximnlb+vwAfZaZnWYvlA7qrKtHIjeRNHvT23zcw==} dependencies: @@ -2339,7 +2366,7 @@ packages: /@cosmjs/cosmwasm-stargate@0.32.2: resolution: {integrity: sha512-OwJHzIx2CoJS6AULxOpNR6m+CI0GXxy8z9svHA1ZawzNM3ZGlL0GvHdhmF0WkpX4E7UdrYlJSLpKcgg5Fo6i7Q==} dependencies: - '@cosmjs/amino': 0.32.2 + '@cosmjs/amino': 0.32.3 '@cosmjs/crypto': 0.32.2 '@cosmjs/encoding': 0.32.2 '@cosmjs/math': 0.32.2 @@ -2394,6 +2421,18 @@ packages: libsodium-wrappers-sumo: 0.7.13 dev: false + /@cosmjs/crypto@0.32.3: + resolution: {integrity: sha512-niQOWJHUtlJm2GG4F00yGT7sGPKxfUwz+2qQ30uO/E3p58gOusTcH2qjiJNVxb8vScYJhFYFqpm/OA/mVqoUGQ==} + dependencies: + '@cosmjs/encoding': 0.32.3 + '@cosmjs/math': 0.32.3 + '@cosmjs/utils': 0.32.3 + '@noble/hashes': 1.3.3 + bn.js: 5.2.1 + elliptic: 6.5.4 + libsodium-wrappers-sumo: 0.7.13 + dev: false + /@cosmjs/encoding@0.27.1: resolution: {integrity: sha512-rayLsA0ojHeniaRfWWcqSsrE/T1rl1gl0OXVNtXlPwLJifKBeLEefGbOUiAQaT0wgJ8VNGBazVtAZBpJidfDhw==} dependencies: @@ -2418,6 +2457,14 @@ packages: readonly-date: 1.0.0 dev: false + /@cosmjs/encoding@0.32.3: + resolution: {integrity: sha512-p4KF7hhv8jBQX3MkB3Defuhz/W0l3PwWVYU2vkVuBJ13bJcXyhU9nJjiMkaIv+XP+W2QgRceqNNgFUC5chNR7w==} + dependencies: + base64-js: 1.5.1 + bech32: 1.1.4 + readonly-date: 1.0.0 + dev: false + /@cosmjs/json-rpc@0.31.3: resolution: {integrity: sha512-7LVYerXjnm69qqYR3uA6LGCrBW2EO5/F7lfJxAmY+iII2C7xO3a0vAjMSt5zBBh29PXrJVS6c2qRP22W1Le2Wg==} dependencies: @@ -2464,6 +2511,12 @@ packages: bn.js: 5.2.1 dev: false + /@cosmjs/math@0.32.3: + resolution: {integrity: sha512-amumUtZs8hCCnV+lSBaJIiZkGabQm22QGg/IotYrhcmoOEOjt82n7hMNlNXRs7V6WLMidGrGYcswB5zcmp0Meg==} + dependencies: + bn.js: 5.2.1 + dev: false + /@cosmjs/proto-signing@0.31.3: resolution: {integrity: sha512-24+10/cGl6lLS4VCrGTCJeDRPQTn1K5JfknzXzDIHOx8THR31JxA7/HV5eWGHqWgAbudA7ccdSvEK08lEHHtLA==} dependencies: @@ -2613,6 +2666,10 @@ packages: resolution: {integrity: sha512-Gg5t+eR7vPJMAmhkFt6CZrzPd0EKpAslWwk5rFVYZpJsM8JG5KT9XQ99hgNM3Ov6ScNoIWbXkpX27F6A9cXR4Q==} dev: false + /@cosmjs/utils@0.32.3: + resolution: {integrity: sha512-WCZK4yksj2hBDz4w7xFZQTRZQ/RJhBX26uFHmmQFIcNUUVAihrLO+RerqJgk0dZqC42wstM9pEUQGtPmLcIYvg==} + dev: false + /@cspotcode/source-map-support@0.8.1: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} @@ -3170,6 +3227,14 @@ packages: graphql: 16.8.0 dev: false + /@heroicons/react@2.1.4(react@18.2.0): + resolution: {integrity: sha512-ju0wj0wwrUTMQ2Yceyrma7TKuI3BpSjp+qKqV81K9KGcUHdvTMdiwfRc2cwXBp3uXtKuDZkh0v03nWOQnJFv2Q==} + peerDependencies: + react: '>= 16' + dependencies: + react: 18.2.0 + dev: false + /@humanwhocodes/config-array@0.11.13: resolution: {integrity: sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==} engines: {node: '>=10.10.0'} @@ -3183,6 +3248,7 @@ packages: /@humanwhocodes/config-array@0.11.14: resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} engines: {node: '>=10.10.0'} + deprecated: Use @eslint/config-array instead dependencies: '@humanwhocodes/object-schema': 2.0.3 debug: 4.3.4(supports-color@5.5.0) @@ -3200,6 +3266,7 @@ packages: /@humanwhocodes/object-schema@2.0.3: resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} + deprecated: Use @eslint/object-schema instead dev: true /@ioredis/commands@1.2.0: @@ -3498,6 +3565,16 @@ packages: mobx: 6.12.0 dev: false + /@keplr-wallet/common@0.12.80: + resolution: {integrity: sha512-HndiWKqNtPq6uGt7aY5FQEwegSG0SPN21W2M8XgEOwryzTKAMnFm8sjfvAAIjZW0Wmvkx1fX1e/MWQV27IfxJg==} + dependencies: + '@keplr-wallet/crypto': 0.12.80 + '@keplr-wallet/types': 0.12.80 + buffer: 6.0.3 + delay: 4.4.1 + mobx: 6.12.0 + dev: false + /@keplr-wallet/cosmos@0.12.67: resolution: {integrity: sha512-XOwiY+85/ACkJ/1iaj/40ITd2iowuJExN8uz7OTYZ5b7y6XeaG22XifukZTZuSL0jQI2YbzansYAcoMCiBHbNQ==} dependencies: @@ -3514,6 +3591,22 @@ packages: protobufjs: 6.11.4 dev: false + /@keplr-wallet/cosmos@0.12.80: + resolution: {integrity: sha512-MS3BGdy9Y0R8Rujr9CHkzoWpC8PmSDZOBBMRhljelChVAAfc1Dvz7nsJ66Sk9vpsiNUwCctcK+sxedtT7Mia9A==} + dependencies: + '@ethersproject/address': 5.7.0 + '@keplr-wallet/common': 0.12.80 + '@keplr-wallet/crypto': 0.12.80 + '@keplr-wallet/proto-types': 0.12.80 + '@keplr-wallet/simple-fetch': 0.12.80 + '@keplr-wallet/types': 0.12.80 + '@keplr-wallet/unit': 0.12.80 + bech32: 1.1.4 + buffer: 6.0.3 + long: 4.0.0 + protobufjs: 6.11.4 + dev: false + /@keplr-wallet/crypto@0.12.67: resolution: {integrity: sha512-ETLeRay4fUyJhN+emGz1wjA4OICct55wDTeKR4qfyYMn7WNbIKK+CSM71gaApZZAWZmXimglEYq5xAF8KSpPsg==} dependencies: @@ -3540,6 +3633,19 @@ packages: sha.js: 2.4.11 dev: false + /@keplr-wallet/crypto@0.12.80: + resolution: {integrity: sha512-iJLxy3OJV1/ZDZ3y0ZKHHN46rwtSgyvVxVp0IiDVtZQ77sYw2yiQArux6cYwi8i2d2eZPJc2I//hvZagBdeoGA==} + dependencies: + '@ethersproject/keccak256': 5.7.0 + bip32: 2.0.6 + bip39: 3.1.0 + bs58check: 2.1.2 + buffer: 6.0.3 + crypto-js: 4.2.0 + elliptic: 6.5.4 + sha.js: 2.4.11 + dev: false + /@keplr-wallet/proto-types@0.12.67: resolution: {integrity: sha512-rfsOBKH+UrkmA39mqiXYy6lz+YctWMtjaZ5Tqc1+E3qbt10z5gu220+pI+hc0XxIj481j0v5vjFhAL9yGIZMVg==} dependencies: @@ -3547,16 +3653,33 @@ packages: protobufjs: 6.11.4 dev: false + /@keplr-wallet/proto-types@0.12.80: + resolution: {integrity: sha512-ZX0/O+P2zkjr6AwrppckeJfvDnsJnaoNBMxLQ5XZldLAzKb0ECEQnC8IgFkTWYqy9jgFbu2kJwqtrPnKSqKwMQ==} + dependencies: + long: 4.0.0 + protobufjs: 6.11.4 + dev: false + /@keplr-wallet/simple-fetch@0.12.67: resolution: {integrity: sha512-xfu4S9HHqEH0AskG0eyLEAW10FaXD9C/tfSbpITMZU1FM9EnegLoHty+hXtMlIGDZbNZ8rsSpABMFLTEr2XB0g==} dev: false + /@keplr-wallet/simple-fetch@0.12.80: + resolution: {integrity: sha512-GBOONbo5npz+t47cnjRnpO1Up+XGAPxjKLRtwYopZeV99KZa7oLb73RQBXT0XooQwq73bwy/Xv0qPCSYhWKUgA==} + dev: false + /@keplr-wallet/types@0.12.67: resolution: {integrity: sha512-HDEYOezWq//ug/R0lkOQWsaOYh3b9ECA8LN+tAA+baWMo81X9m9L63ux7NYw0QmoLxWqWrRO/T9F28+q2dw6+A==} dependencies: long: 4.0.0 dev: false + /@keplr-wallet/types@0.12.80: + resolution: {integrity: sha512-GRMyOioocq2LaLAoaGegCOqgjKq4uXOPQ9QwoLpx0ZpsbccSr2wKN8PhFs/B9fwuhiJu1aroX7hXC0jlsE8XLg==} + dependencies: + long: 4.0.0 + dev: false + /@keplr-wallet/unit@0.12.67: resolution: {integrity: sha512-Mzlrh0j65i6+qoTNg1/Um4Hywpu9mmaZn8HRiX01HdPyaEcLREFkf18eJMklWuHx4nERg+EzvU5E/9J2fuRPdA==} dependencies: @@ -3565,6 +3688,14 @@ packages: utility-types: 3.10.0 dev: false + /@keplr-wallet/unit@0.12.80: + resolution: {integrity: sha512-XpZe2i0iY8jtbA1GTsK4DV4KN5C2nXnSFdBn36F0TS/Ow1DO5KSqIyYsAU86pTzZfbkUzn5OPPmJUHGAg00GJg==} + dependencies: + '@keplr-wallet/types': 0.12.80 + big-integer: 1.6.51 + utility-types: 3.10.0 + dev: false + /@lit-labs/ssr-dom-shim@1.1.2: resolution: {integrity: sha512-jnOD+/+dSrfTWYfSXBXlo5l5f0q1UuJo3tkbMDCYA2lKUYq79jaxqtGEvnRoh049nt1vdo1+45RinipU6FGY2g==} dev: false @@ -13133,64 +13264,64 @@ packages: yargs: 17.7.2 dev: false - /turbo-darwin-64@1.11.2: - resolution: {integrity: sha512-toFmRG/adriZY3hOps7nYCfqHAS+Ci6xqgX3fbo82kkLpC6OBzcXnleSwuPqjHVAaRNhVoB83L5njcE9Qwi2og==} + /turbo-darwin-64@1.13.3: + resolution: {integrity: sha512-glup8Qx1qEFB5jerAnXbS8WrL92OKyMmg5Hnd4PleLljAeYmx+cmmnsmLT7tpaVZIN58EAAwu8wHC6kIIqhbWA==} cpu: [x64] os: [darwin] requiresBuild: true dev: false optional: true - /turbo-darwin-arm64@1.11.2: - resolution: {integrity: sha512-FCsEDZ8BUSFYEOSC3rrARQrj7x2VOrmVcfrMUIhexTxproRh4QyMxLfr6LALk4ymx6jbDCxWa6Szal8ckldFbA==} + /turbo-darwin-arm64@1.13.3: + resolution: {integrity: sha512-/np2xD+f/+9qY8BVtuOQXRq5f9LehCFxamiQnwdqWm5iZmdjygC5T3uVSYuagVFsZKMvX3ycySwh8dylGTl6lg==} cpu: [arm64] os: [darwin] requiresBuild: true dev: false optional: true - /turbo-linux-64@1.11.2: - resolution: {integrity: sha512-Vzda/o/QyEske5CxLf0wcu7UUS+7zB90GgHZV4tyN+WZtoouTvbwuvZ3V6b5Wgd3OJ/JwWR0CXDK7Sf4VEMr7A==} + /turbo-linux-64@1.13.3: + resolution: {integrity: sha512-G+HGrau54iAnbXLfl+N/PynqpDwi/uDzb6iM9hXEDG+yJnSJxaHMShhOkXYJPk9offm9prH33Khx2scXrYVW1g==} cpu: [x64] os: [linux] requiresBuild: true dev: false optional: true - /turbo-linux-arm64@1.11.2: - resolution: {integrity: sha512-bRLwovQRz0yxDZrM4tQEAYV0fBHEaTzUF0JZ8RG1UmZt/CqtpnUrJpYb1VK8hj1z46z9YehARpYCwQ2K0qU4yw==} + /turbo-linux-arm64@1.13.3: + resolution: {integrity: sha512-qWwEl5VR02NqRyl68/3pwp3c/olZuSp+vwlwrunuoNTm6JXGLG5pTeme4zoHNnk0qn4cCX7DFrOboArlYxv0wQ==} cpu: [arm64] os: [linux] requiresBuild: true dev: false optional: true - /turbo-windows-64@1.11.2: - resolution: {integrity: sha512-LgTWqkHAKgyVuLYcEPxZVGPInTjjeCnN5KQMdJ4uQZ+xMDROvMFS2rM93iQl4ieDJgidwHCxxCxaU9u8c3d/Kg==} + /turbo-windows-64@1.13.3: + resolution: {integrity: sha512-Nudr4bRChfJzBPzEmpVV85VwUYRCGKecwkBFpbp2a4NtrJ3+UP1VZES653ckqCu2FRyRuS0n03v9euMbAvzH+Q==} cpu: [x64] os: [win32] requiresBuild: true dev: false optional: true - /turbo-windows-arm64@1.11.2: - resolution: {integrity: sha512-829aVBU7IX0c/B4G7g1VI8KniAGutHhIupkYMgF6xPkYVev2G3MYe6DMS/vsLt9GGM9ulDtdWxWrH5P2ngK8IQ==} + /turbo-windows-arm64@1.13.3: + resolution: {integrity: sha512-ouJCgsVLd3icjRLmRvHQDDZnmGzT64GBupM1Y+TjtYn2LVaEBoV6hicFy8x5DUpnqdLy+YpCzRMkWlwhmkX7sQ==} cpu: [arm64] os: [win32] requiresBuild: true dev: false optional: true - /turbo@1.11.2: - resolution: {integrity: sha512-jPC7LVQJzebs5gWf8FmEvsvXGNyKbN+O9qpvv98xpNaM59aS0/Irhd0H0KbcqnXfsz7ETlzOC3R+xFWthC4Z8A==} + /turbo@1.13.3: + resolution: {integrity: sha512-n17HJv4F4CpsYTvKzUJhLbyewbXjq1oLCi90i5tW1TiWDz16ML1eDG7wi5dHaKxzh5efIM56SITnuVbMq5dk4g==} hasBin: true optionalDependencies: - turbo-darwin-64: 1.11.2 - turbo-darwin-arm64: 1.11.2 - turbo-linux-64: 1.11.2 - turbo-linux-arm64: 1.11.2 - turbo-windows-64: 1.11.2 - turbo-windows-arm64: 1.11.2 + turbo-darwin-64: 1.13.3 + turbo-darwin-arm64: 1.13.3 + turbo-linux-64: 1.13.3 + turbo-linux-arm64: 1.13.3 + turbo-windows-64: 1.13.3 + turbo-windows-arm64: 1.13.3 dev: false /type-check@0.4.0: