From 4b87ef61e9e209c5726ab2e222d28bc107fe39b8 Mon Sep 17 00:00:00 2001 From: Pedro Rezende Date: Tue, 7 Jan 2025 16:55:52 -0300 Subject: [PATCH 1/4] feat(namadillo): querying next rpc if the previous one fails (#1479) --- .../src/App/Common/QueryProvider.tsx | 16 +--------- .../src/App/Common/StorageProvider.tsx | 24 ++++++++++++++ .../namadillo/src/atoms/integrations/atoms.ts | 8 ++--- .../src/atoms/integrations/functions.ts | 11 +++++-- .../src/atoms/integrations/services.ts | 32 +++++++++---------- apps/namadillo/src/index.tsx | 21 ++++++------ apps/namadillo/src/integrations/utils.ts | 2 +- apps/namadillo/src/types.ts | 5 +++ 8 files changed, 71 insertions(+), 48 deletions(-) create mode 100644 apps/namadillo/src/App/Common/StorageProvider.tsx diff --git a/apps/namadillo/src/App/Common/QueryProvider.tsx b/apps/namadillo/src/App/Common/QueryProvider.tsx index b3e0200077..afa5428447 100644 --- a/apps/namadillo/src/App/Common/QueryProvider.tsx +++ b/apps/namadillo/src/App/Common/QueryProvider.tsx @@ -1,8 +1,4 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; -import { getDefaultStore } from "jotai"; -import { queryClientAtom } from "jotai-tanstack-query"; -import { Provider as JotaiProvider } from "jotai/react"; -import { useHydrateAtoms } from "jotai/utils"; type ErrorWithResponse = Error & { response?: { @@ -11,7 +7,6 @@ type ErrorWithResponse = Error & { }; const MAX_RETRIES = 3; - export const queryClient = new QueryClient({ defaultOptions: { queries: { @@ -31,21 +26,12 @@ export const queryClient = new QueryClient({ }, }); -const HydrateAtoms = (props: { children: JSX.Element }): JSX.Element => { - useHydrateAtoms([[queryClientAtom, queryClient]]); - return props.children; -}; - export const QueryProvider = ({ children, }: { children: JSX.Element; }): JSX.Element => { return ( - - - {children} - - + {children} ); }; diff --git a/apps/namadillo/src/App/Common/StorageProvider.tsx b/apps/namadillo/src/App/Common/StorageProvider.tsx new file mode 100644 index 0000000000..5a9e941090 --- /dev/null +++ b/apps/namadillo/src/App/Common/StorageProvider.tsx @@ -0,0 +1,24 @@ +import { queryClient } from "App/Common/QueryProvider"; +import { getDefaultStore } from "jotai"; +import { queryClientAtom } from "jotai-tanstack-query"; +import { Provider as JotaiProvider } from "jotai/react"; +import { useHydrateAtoms } from "jotai/utils"; + +type StorageProviderProps = { children: JSX.Element }; + +const HydrateAtoms = (props: { children: JSX.Element }): JSX.Element => { + useHydrateAtoms([[queryClientAtom, queryClient]]); + return props.children; +}; + +export const defaultStore = getDefaultStore(); + +export const StorageProvider = ({ + children, +}: StorageProviderProps): JSX.Element => { + return ( + + {children} + + ); +}; diff --git a/apps/namadillo/src/atoms/integrations/atoms.ts b/apps/namadillo/src/atoms/integrations/atoms.ts index d4326cc0df..3d414e062a 100644 --- a/apps/namadillo/src/atoms/integrations/atoms.ts +++ b/apps/namadillo/src/atoms/integrations/atoms.ts @@ -18,6 +18,7 @@ import { BuildTxAtomParams, ChainId, ChainRegistryEntry, + RpcStorage, TransferStep, TransferTransactionData, } from "types"; @@ -64,10 +65,9 @@ export const selectedIBCChainAtom = atomWithStorage( undefined ); -export const workingRpcsAtom = atomWithStorage>( - "namadillo:rpcs", - {} -); +export const rpcByChainAtom = atomWithStorage< + Record | undefined +>("namadillo:rpc:active", undefined); export const ibcTransferAtom = atomWithMutation(() => { return { diff --git a/apps/namadillo/src/atoms/integrations/functions.ts b/apps/namadillo/src/atoms/integrations/functions.ts index 43ee5a455e..b5e3408f2b 100644 --- a/apps/namadillo/src/atoms/integrations/functions.ts +++ b/apps/namadillo/src/atoms/integrations/functions.ts @@ -26,6 +26,7 @@ import { Coin, GasConfig, LocalnetToml, + RpcStorage, } from "types"; import { toDisplayAmount } from "utils"; import { unknownAsset } from "utils/assets"; @@ -299,13 +300,17 @@ export const mapCoinsToAssets = async ( ); }; -export const getRpcByIndex = (chain: Chain, index = 0): string => { +export const getRpcByIndex = (chain: Chain, index = 0): RpcStorage => { const availableRpcs = chain.apis?.rpc; if (!availableRpcs) { throw new Error("There are no available RPCs for " + chain.chain_name); } - const randomRpc = availableRpcs[Math.min(index, availableRpcs.length - 1)]; - return randomRpc.address; + + const rpc = availableRpcs[Math.min(index, availableRpcs.length - 1)]; + return { + address: rpc.address, + index, + }; }; export const getRestApiAddressByIndex = (chain: Chain, index = 0): string => { diff --git a/apps/namadillo/src/atoms/integrations/services.ts b/apps/namadillo/src/atoms/integrations/services.ts index 39a26673f6..31676f2e51 100644 --- a/apps/namadillo/src/atoms/integrations/services.ts +++ b/apps/namadillo/src/atoms/integrations/services.ts @@ -24,7 +24,7 @@ import { } from "types"; import { toBaseAmount } from "utils"; import { getSdkInstance } from "utils/sdk"; -import { workingRpcsAtom } from "./atoms"; +import { rpcByChainAtom } from "./atoms"; import { getRpcByIndex } from "./functions"; type CommonParams = { @@ -142,28 +142,28 @@ export const submitIbcTransfer = async ( export const queryAndStoreRpc = async ( chain: Chain, - queryFn: QueryFn, - rpcIndex = 0 + queryFn: QueryFn ): Promise => { const { get, set } = getDefaultStore(); - const workingRpcs = get(workingRpcsAtom); - const rpcAddress = - chain.chain_id in workingRpcs ? - workingRpcs[chain.chain_id] - : getRpcByIndex(chain, rpcIndex); + const lastRpc = get(rpcByChainAtom) || {}; + const rpc = + chain.chain_id in lastRpc ? + lastRpc[chain.chain_id] + : getRpcByIndex(chain, 0); try { - const output = await queryFn(rpcAddress); - set(workingRpcsAtom, { - ...workingRpcs, - [chain.chain_id]: rpcAddress, + const output = await queryFn(rpc.address); + set(rpcByChainAtom, { + ...lastRpc, + [chain.chain_id]: { ...rpc }, }); return output; } catch (err) { - if (chain.chain_id in workingRpcs) { - delete workingRpcs[chain.chain_id]; - set(workingRpcsAtom, { ...workingRpcs }); - } + // On error, saves the next available rpc + set(rpcByChainAtom, { + ...lastRpc, + [chain.chain_id]: getRpcByIndex(chain, rpc.index + 1), + }); throw err; } }; diff --git a/apps/namadillo/src/index.tsx b/apps/namadillo/src/index.tsx index 73ebfd6dd0..4e1553e745 100644 --- a/apps/namadillo/src/index.tsx +++ b/apps/namadillo/src/index.tsx @@ -8,6 +8,7 @@ import { getRouter } from "./App/AppRoutes"; import "@namada/components/src/base.css"; import "@namada/utils/bigint-to-json-polyfill"; +import { StorageProvider } from "App/Common/StorageProvider"; import { ExtensionLoader } from "App/Setup/ExtensionLoader"; import { IndexerLoader } from "App/Setup/IndexerLoader"; import { TomlConfigLoader } from "App/Setup/TomlConfigLoader"; @@ -23,15 +24,17 @@ if (container) { root.render( - - - - - - - - - + + + + + + + + + + + ); diff --git a/apps/namadillo/src/integrations/utils.ts b/apps/namadillo/src/integrations/utils.ts index b616f858d7..63eeb26899 100644 --- a/apps/namadillo/src/integrations/utils.ts +++ b/apps/namadillo/src/integrations/utils.ts @@ -154,7 +154,7 @@ export const basicConvertToKeplrChain = ( return { chainId: chain.chain_id, chainName: chain.chain_name, - rpc, + rpc: rpc.address, rest, bip44: { coinType: chain.slip44 }, bech32Config: generateBech32Config(chain.bech32_prefix), diff --git a/apps/namadillo/src/types.ts b/apps/namadillo/src/types.ts index 6387b47b5d..49deac9efb 100644 --- a/apps/namadillo/src/types.ts +++ b/apps/namadillo/src/types.ts @@ -81,6 +81,11 @@ export type SettingsStorage = { enableTestnets?: boolean; }; +export type RpcStorage = { + address: string; + index: number; +}; + export type Validator = Unique & { alias?: string; address: Address; From 741fbeca3cb6cdef1834462b72a9145c51aaf91b Mon Sep 17 00:00:00 2001 From: Pedro Rezende Date: Tue, 7 Jan 2025 18:23:38 -0300 Subject: [PATCH 2/4] Namadillo: Top Navigation improvements (#1475) * feat(namadillo): top navigation modal components * feat(namadillo): adding copy to clipboard feature to tx hash --- .../src/App/Common/AssetsModalCard.tsx | 30 ++++++++++++ .../src/App/Common/ModalContainer.tsx | 4 +- .../src/App/Common/SelectOptionModal.tsx | 46 +++++++++++++++++++ .../src/App/Common/ShieldAssetsModal.tsx | 45 ++++++++++++++++++ .../src/App/Common/UnshieldAssetsModal.tsx | 46 +++++++++++++++++++ .../src/App/Layout/TopNavigation.tsx | 41 +++++++++++++---- .../TransferTransactionTimeline.tsx | 8 ++-- .../src/integrations/assets/keplr.svg | 2 +- packages/components/src/Modal.tsx | 4 +- 9 files changed, 209 insertions(+), 17 deletions(-) create mode 100644 apps/namadillo/src/App/Common/AssetsModalCard.tsx create mode 100644 apps/namadillo/src/App/Common/SelectOptionModal.tsx create mode 100644 apps/namadillo/src/App/Common/ShieldAssetsModal.tsx create mode 100644 apps/namadillo/src/App/Common/UnshieldAssetsModal.tsx diff --git a/apps/namadillo/src/App/Common/AssetsModalCard.tsx b/apps/namadillo/src/App/Common/AssetsModalCard.tsx new file mode 100644 index 0000000000..1121098215 --- /dev/null +++ b/apps/namadillo/src/App/Common/AssetsModalCard.tsx @@ -0,0 +1,30 @@ +import { Stack } from "@namada/components"; +import clsx from "clsx"; + +type AssetsModalCard = { + title: string; + icon: React.ReactNode; + onClick: () => void; +} & React.PropsWithChildren; + +export const AssetsModalCard = ({ + title, + icon, + children, + onClick, +}: AssetsModalCard): JSX.Element => { + return ( + +

{title}

+ +
{children}
+
+ ); +}; diff --git a/apps/namadillo/src/App/Common/ModalContainer.tsx b/apps/namadillo/src/App/Common/ModalContainer.tsx index cabac0e541..ebf1a9f17d 100644 --- a/apps/namadillo/src/App/Common/ModalContainer.tsx +++ b/apps/namadillo/src/App/Common/ModalContainer.tsx @@ -39,11 +39,11 @@ export const ModalContainer = ({ > -
+
{header}
{children} diff --git a/apps/namadillo/src/App/Common/SelectOptionModal.tsx b/apps/namadillo/src/App/Common/SelectOptionModal.tsx new file mode 100644 index 0000000000..98a5fa1868 --- /dev/null +++ b/apps/namadillo/src/App/Common/SelectOptionModal.tsx @@ -0,0 +1,46 @@ +import { Modal } from "@namada/components"; +import { AssetsModalCard } from "./AssetsModalCard"; +import { ModalContainer } from "./ModalContainer"; + +type Entry = { + title: string; + onClick: () => void; + icon: React.ReactNode; + children: React.ReactNode; +}; + +type SelectOptionModalProps = { + title: string; + onClose: () => void; + items: Entry[]; +}; + +export const SelectOptionModal = ({ + title, + onClose, + items, +}: SelectOptionModalProps): JSX.Element => { + return ( + + +
    + {items.map((item, index) => ( + <> +
  • + {item.children} +
  • + {index + 1 < items.length && ( +
  • + )} + + ))} +
+
+
+ ); +}; diff --git a/apps/namadillo/src/App/Common/ShieldAssetsModal.tsx b/apps/namadillo/src/App/Common/ShieldAssetsModal.tsx new file mode 100644 index 0000000000..380ee64dff --- /dev/null +++ b/apps/namadillo/src/App/Common/ShieldAssetsModal.tsx @@ -0,0 +1,45 @@ +import { routes } from "App/routes"; +import { wallets } from "integrations"; +import { getAssetImageUrl } from "integrations/utils"; +import { useNavigate } from "react-router-dom"; +import { namadaAsset } from "utils"; +import { SelectOptionModal } from "./SelectOptionModal"; + +type ShieldAssetsModal = { + onClose: () => void; +}; + +export const ShieldAssetsModal = ({ + onClose, +}: ShieldAssetsModal): JSX.Element => { + const navigate = useNavigate(); + const goTo = (url: string): void => { + navigate(url); + onClose(); + }; + + return ( + , + onClick: () => goTo(routes.ibc), + children: "Shield external assets over IBC to Namada", + }, + { + title: "Internal Shield", + icon: ( + + + + ), + onClick: () => goTo(routes.maspShield), + children: "Shield Assets from your Namada transparent account", + }, + ]} + /> + ); +}; diff --git a/apps/namadillo/src/App/Common/UnshieldAssetsModal.tsx b/apps/namadillo/src/App/Common/UnshieldAssetsModal.tsx new file mode 100644 index 0000000000..bb51e1fa37 --- /dev/null +++ b/apps/namadillo/src/App/Common/UnshieldAssetsModal.tsx @@ -0,0 +1,46 @@ +import { routes } from "App/routes"; +import { wallets } from "integrations"; +import { getAssetImageUrl } from "integrations/utils"; +import { useNavigate } from "react-router-dom"; +import { namadaAsset } from "utils"; +import { SelectOptionModal } from "./SelectOptionModal"; + +type UnshieldAssetsModal = { + onClose: () => void; +}; + +export const UnshieldAssetsModal = ({ + onClose, +}: UnshieldAssetsModal): JSX.Element => { + const navigate = useNavigate(); + const goTo = (url: string): void => { + navigate(url); + onClose(); + }; + + return ( + , + onClick: () => goTo(routes.ibcWithdraw), + children: "Unshield assets over IBC to an IBC chain", + }, + { + title: "Internal Unshield", + icon: ( + + + + ), + onClick: () => goTo(routes.maspUnshield), + children: + "Unshield assets from your Namada shielded to transparent account", + }, + ]} + /> + ); +}; diff --git a/apps/namadillo/src/App/Layout/TopNavigation.tsx b/apps/namadillo/src/App/Layout/TopNavigation.tsx index 73d265866a..9761c3c9ff 100644 --- a/apps/namadillo/src/App/Layout/TopNavigation.tsx +++ b/apps/namadillo/src/App/Layout/TopNavigation.tsx @@ -1,7 +1,8 @@ import { ActionButton } from "@namada/components"; import { ConnectExtensionButton } from "App/Common/ConnectExtensionButton"; +import { ShieldAssetsModal } from "App/Common/ShieldAssetsModal"; import { TransactionInProgressSpinner } from "App/Common/TransactionInProgressSpinner"; -import { IbcLogo } from "App/Ibc/assets/IbcLogo"; +import { UnshieldAssetsModal } from "App/Common/UnshieldAssetsModal"; import { routes } from "App/routes"; import { applicationFeaturesAtom, @@ -9,6 +10,7 @@ import { } from "atoms/settings"; import { useUserHasAccount } from "hooks/useIsAuthenticated"; import { useAtomValue } from "jotai"; +import { useState } from "react"; import { AiOutlineMessage } from "react-icons/ai"; import { IoSettingsOutline } from "react-icons/io5"; import { useLocation, useNavigate } from "react-router-dom"; @@ -17,6 +19,9 @@ import { NamadaAccount } from "./NamadaAccount"; import { SyncIndicator } from "./SyncIndicator"; export const TopNavigation = (): JSX.Element => { + const [shieldingModalOpen, setShieldingModalOpen] = useState(false); + const [unshieldingModalOpen, setUnshieldingModalOpen] = useState(false); + const userHasAccount = useUserHasAccount(); const signArbitraryEnabled = useAtomValue(signArbitraryEnabledAtom); const { maspEnabled, namTransfersEnabled } = useAtomValue( @@ -35,20 +40,32 @@ export const TopNavigation = (): JSX.Element => { return (
-
+
+ {maspEnabled && ( + setShieldingModalOpen(true)} + > + Shield Assets + + )} {maspEnabled && ( - -
- Shield Assets over -
+ setUnshieldingModalOpen(true)} + > + Unshield )} {(maspEnabled || namTransfersEnabled) && ( Transfer @@ -87,6 +104,14 @@ export const TopNavigation = (): JSX.Element => {
+ + {shieldingModalOpen && ( + setShieldingModalOpen(false)} /> + )} + + {unshieldingModalOpen && ( + setUnshieldingModalOpen(false)} /> + )}
); }; diff --git a/apps/namadillo/src/App/Transactions/TransferTransactionTimeline.tsx b/apps/namadillo/src/App/Transactions/TransferTransactionTimeline.tsx index 141520d123..e73f84fe83 100644 --- a/apps/namadillo/src/App/Transactions/TransferTransactionTimeline.tsx +++ b/apps/namadillo/src/App/Transactions/TransferTransactionTimeline.tsx @@ -1,5 +1,5 @@ import { Asset } from "@chain-registry/types"; -import { Tooltip } from "@namada/components"; +import { CopyToClipboardControl } from "@namada/components"; import { shortenAddress } from "@namada/utils"; import { Timeline, TransactionStep } from "App/Common/Timeline"; import { AssetImage } from "App/Transfer/AssetImage"; @@ -155,11 +155,11 @@ export const TransferTransactionTimeline = ({ {stepDescription[transaction.currentStep || "sign"]} {transaction.hash && ( - + Transaction hash:{" "} - + {shortenAddress(transaction.hash, 8, 8)} - {transaction.hash} + )} diff --git a/apps/namadillo/src/integrations/assets/keplr.svg b/apps/namadillo/src/integrations/assets/keplr.svg index e56c5257c3..1aae30d444 100644 --- a/apps/namadillo/src/integrations/assets/keplr.svg +++ b/apps/namadillo/src/integrations/assets/keplr.svg @@ -1,4 +1,4 @@ - + diff --git a/packages/components/src/Modal.tsx b/packages/components/src/Modal.tsx index 23dc28954a..031e5ddee5 100644 --- a/packages/components/src/Modal.tsx +++ b/packages/components/src/Modal.tsx @@ -41,14 +41,14 @@ export const Modal = ({ transition={{ duration: 0 }} exit={{ opacity: 0 }} onClick={onClose} - className="fixed top-0 left-0 w-full h-full cursor-pointer backdrop-blur-lg z-[1000] bg-rblack/50" + className="fixed top-0 left-0 w-full h-full cursor-pointer backdrop-blur-lg z-[9998] bg-rblack/50" />
Date: Tue, 7 Jan 2025 20:51:08 -0300 Subject: [PATCH 3/4] fix(namadillo): fixing nam balance container height overflow (#1480) --- apps/namadillo/src/App/AccountOverview/NamBalanceContainer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/namadillo/src/App/AccountOverview/NamBalanceContainer.tsx b/apps/namadillo/src/App/AccountOverview/NamBalanceContainer.tsx index 35c3e5853b..ba0ee37500 100644 --- a/apps/namadillo/src/App/AccountOverview/NamBalanceContainer.tsx +++ b/apps/namadillo/src/App/AccountOverview/NamBalanceContainer.tsx @@ -102,7 +102,7 @@ export const NamBalanceContainer = (): JSX.Element => { NAM Balance )} -
+
Date: Wed, 8 Jan 2025 10:34:01 +0000 Subject: [PATCH 4/4] docs/1476: Keychain: Update Firefox docs (#1477) * docs: update docs, remove problematic flag from Dockerfile * fix: add dependency to fix wasm-opt error during docker build * fix: don't export wasm-bindgen mnemonic size enum * fix: fix tests * fix: revert minification setting --- apps/extension/FIREFOX_README.md | 46 +++++++++++++++++++- apps/extension/webpack.config.js | 1 - docker/extension/Dockerfile | 4 +- packages/crypto/lib/src/crypto/bip39.rs | 26 +++++------ packages/crypto/src/__tests__/crypto.test.ts | 9 ++-- packages/sdk/src/mnemonic.ts | 6 ++- packages/sdk/src/tests/mnemonic.test.ts | 6 +-- 7 files changed, 69 insertions(+), 29 deletions(-) diff --git a/apps/extension/FIREFOX_README.md b/apps/extension/FIREFOX_README.md index c1950630ad..55aaedfcda 100644 --- a/apps/extension/FIREFOX_README.md +++ b/apps/extension/FIREFOX_README.md @@ -14,7 +14,7 @@ exactly as they are described below. **NOTE** The add-on submission was built in the following environment: -- Ubuntu Server 24.04 LTS **x86_64** - Please ensure your environment matches this to produce an identical build! +- Ubuntu Desktop 24.04 LTS **ARM64** - Please ensure your environment matches this to produce an identical build! - Docker version 27.x Follow these instructions to build with Docker: @@ -47,6 +47,50 @@ docker run --rm -v ./apps/extension/build:/shared namada-keychain-firefox cp -r If Docker is not currently installed in your environment, please refer to the [instructions of the official Docker documentation](https://docs.docker.com/engine/install/ubuntu/). +The steps we took to install Docker on Ubuntu Desktop 24.04 ARM64 are as follows: + +1. Remove any pre-existing Docker-related packages: + +```bash +for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done +``` + +2. Setup Docker repository and install keyring + +```bash +# Add Docker's official GPG key: +sudo apt-get update +sudo apt-get install ca-certificates curl +sudo install -m 0755 -d /etc/apt/keyrings +sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc +sudo chmod a+r /etc/apt/keyrings/docker.asc + +# Add the repository to Apt sources: +echo \ + "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \ + $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ + sudo tee /etc/apt/sources.list.d/docker.list > /dev/null +sudo apt-get update +``` + +3. Install Docker packages: + +```bash +sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin +``` + +4. Post-install - Add `docker` group to user: + +```bash +# If docker group doesn't currently exist: +sudo groupadd docker + +# Add current user to docker group: +sudo usermod -aG docker $USER +``` + +5. Log out, then log back in for group to take effect! This is to ensure that you don't need to run our Docker commands as root via `sudo`. + [ [Table of Contents](#table-of-contents) ] ### Source code diff --git a/apps/extension/webpack.config.js b/apps/extension/webpack.config.js index d7f982e734..6411a9b8cf 100644 --- a/apps/extension/webpack.config.js +++ b/apps/extension/webpack.config.js @@ -234,7 +234,6 @@ module.exports = { warningsFilter: [/dependency between chunks.+wasm-bindgen-rayon/], }, optimization: { - minimize: false, moduleIds: "deterministic", // Ensures consistent module IDs chunkIds: "deterministic", // Ensures consistent chunk IDs }, diff --git a/docker/extension/Dockerfile b/docker/extension/Dockerfile index 7fa5495d1d..f53d8addb7 100644 --- a/docker/extension/Dockerfile +++ b/docker/extension/Dockerfile @@ -1,9 +1,9 @@ -FROM --platform=linux/amd64 rust:1.79 AS builder +FROM --platform=linux/arm64 rust:1.79 AS builder WORKDIR /app # Installing required packages -RUN apt update && apt install -y nodejs npm clang pkg-config libssl-dev protobuf-compiler curl +RUN apt update && apt install -y nodejs npm clang pkg-config libssl-dev protobuf-compiler curl binaryen RUN npm install -g yarn RUN rustup target add wasm32-unknown-unknown RUN curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh -s -- -y diff --git a/packages/crypto/lib/src/crypto/bip39.rs b/packages/crypto/lib/src/crypto/bip39.rs index 168738c92b..14b1deb59d 100644 --- a/packages/crypto/lib/src/crypto/bip39.rs +++ b/packages/crypto/lib/src/crypto/bip39.rs @@ -10,13 +10,8 @@ use zeroize::Zeroize; pub enum Bip39Error { #[error("Invalid phrase")] InvalidPhrase, -} - -#[wasm_bindgen] -#[derive(Copy, Clone)] -pub enum PhraseSize { - N12 = 12, - N24 = 24, + #[error("Invalid phrase size! Must be 12 or 24!")] + InvalidPhraseSize, } #[wasm_bindgen] @@ -27,15 +22,16 @@ pub struct Mnemonic { #[wasm_bindgen] impl Mnemonic { #[wasm_bindgen(constructor)] - pub fn new(size: PhraseSize) -> Mnemonic { + pub fn new(size: u8) -> Result { let mnemonic_type = match size { - PhraseSize::N12 => MnemonicType::Words12, - PhraseSize::N24 => MnemonicType::Words24, + 12 => MnemonicType::Words12, + 24 => MnemonicType::Words24, + _ => return Err(Bip39Error::InvalidPhraseSize.to_string()), }; let mnemonic = M::new(mnemonic_type, Language::English); - Mnemonic { mnemonic } + Ok(Mnemonic { mnemonic }) } pub fn validate(phrase: &str) -> bool { @@ -88,13 +84,13 @@ mod tests { #[wasm_bindgen_test] fn can_generate_mnemonic_from_size() { - let mnemonic = Mnemonic::new(PhraseSize::N12); + let mnemonic = Mnemonic::new(12).unwrap(); let phrase = mnemonic.phrase(); let words: Vec<&str> = phrase.split(' ').collect(); assert_eq!(words.iter().len(), 12); - let mnemonic = Mnemonic::new(PhraseSize::N24); + let mnemonic = Mnemonic::new(24).unwrap(); let phrase = mnemonic.phrase(); let words: Vec<&str> = phrase.split(' ').collect(); @@ -141,14 +137,14 @@ mod tests { #[wasm_bindgen_test] fn can_generate_word_list_from_mnemonic() { - let mnemonic = Mnemonic::new(PhraseSize::N12); + let mnemonic = Mnemonic::new(12).unwrap(); let words = mnemonic .to_words() .expect("Should return a VecStringPointer containing the words"); assert_eq!(words.strings.len(), 12); - let mnemonic = Mnemonic::new(PhraseSize::N24); + let mnemonic = Mnemonic::new(24).unwrap(); let words = mnemonic .to_words() .expect("Should return a VecStringPointer containing the words"); diff --git a/packages/crypto/src/__tests__/crypto.test.ts b/packages/crypto/src/__tests__/crypto.test.ts index 94a8beb8b0..305e114899 100644 --- a/packages/crypto/src/__tests__/crypto.test.ts +++ b/packages/crypto/src/__tests__/crypto.test.ts @@ -5,7 +5,6 @@ import { ByteSize, HDWallet, Mnemonic, - PhraseSize, Rng, Salt, ShieldedHDWallet, @@ -41,20 +40,20 @@ const MNEMONIC_WORDS = [ describe("Mnemonic", () => { test("It should return the correct number of words", () => { - let mnemonic = new Mnemonic(PhraseSize.N12); + let mnemonic = new Mnemonic(12); let words = readVecStringPointer(mnemonic?.to_words(), memory); expect(words.length).toBe(12); - mnemonic = new Mnemonic(PhraseSize.N24); + mnemonic = new Mnemonic(24); words = readVecStringPointer(mnemonic.to_words(), memory); expect(words.length).toBe(24); }); test("It should return a seed with a valid length", () => { - const mnemonic12 = new Mnemonic(PhraseSize.N12); + const mnemonic12 = new Mnemonic(12); const seed = mnemonic12.to_seed(); - const mnemonic24 = new Mnemonic(PhraseSize.N24); + const mnemonic24 = new Mnemonic(24); const seed2 = mnemonic24.to_seed(); expect(seed.length).toBe(SEED_LENGTH); diff --git a/packages/sdk/src/mnemonic.ts b/packages/sdk/src/mnemonic.ts index c875d02ff2..108653bfc1 100644 --- a/packages/sdk/src/mnemonic.ts +++ b/packages/sdk/src/mnemonic.ts @@ -1,12 +1,14 @@ import { Mnemonic as MnemonicWasm, - PhraseSize, StringPointer, readVecStringPointer, readVecU8Pointer, } from "@namada/crypto"; -export { PhraseSize } from "@namada/crypto"; +export enum PhraseSize { + N12 = 12, + N24 = 24, +} /** * Class for accessing mnemonic functionality from wasm diff --git a/packages/sdk/src/tests/mnemonic.test.ts b/packages/sdk/src/tests/mnemonic.test.ts index 531f6cf7a2..0ad63e05bc 100644 --- a/packages/sdk/src/tests/mnemonic.test.ts +++ b/packages/sdk/src/tests/mnemonic.test.ts @@ -1,17 +1,17 @@ -import { PhraseSize } from "@namada/crypto"; +import { PhraseSize } from "mnemonic"; import { MNEMONIC_1 as mnemonic1 } from "./data"; import { initSdk } from "./initSdk"; describe("mnemonic", () => { it("should generate a 12 word mnemonic phrase", async () => { const { mnemonic } = initSdk(); - const words = await mnemonic.generate(); + const words = mnemonic.generate(); expect(words.length).toEqual(12); }); it("should generate a 24 word mnemonic phrase", async () => { const { mnemonic } = initSdk(); - const words = await mnemonic.generate(PhraseSize.N24); + const words = mnemonic.generate(PhraseSize.N24); expect(words.length).toEqual(24); });