diff --git a/app/components/Nav/Main/MainNavigator.js b/app/components/Nav/Main/MainNavigator.js
index 67a2683130e..eae118a38e2 100644
--- a/app/components/Nav/Main/MainNavigator.js
+++ b/app/components/Nav/Main/MainNavigator.js
@@ -32,6 +32,7 @@ import AppInformation from '../../Views/Settings/AppInformation';
import DeveloperOptions from '../../Views/Settings/DeveloperOptions';
import Contacts from '../../Views/Settings/Contacts';
import FeatureFlagOverride from '../../Views/FeatureFlagOverride';
+import LibsodiumPoc from '../../Views/LibsodiumPoc';
import Wallet from '../../Views/Wallet';
import SecurityTrustScreen from '../../UI/SecurityTrust/Views/SecurityTrustScreen';
import AddAsset from '../../Views/AddAsset/AddAsset';
@@ -1467,6 +1468,11 @@ const MainNavigator = () => {
options={{ headerShown: false }}
/>
)}
+
+ typeof performance !== 'undefined' && typeof performance.now === 'function'
+ ? performance.now()
+ : Date.now();
+
+/**
+ * Builds a `check(name, fn)` helper that runs `fn` `iterations` times, records
+ * pass/fail into `collected`, and reports the average execution time per run.
+ * Timing stops at the first failing iteration.
+ */
+const makeCheck =
+ (collected: CheckResult[], iterations: number) =>
+ (name: string, fn: () => string | void) => {
+ try {
+ let detail = 'ok';
+ const start = now();
+ for (let i = 0; i < iterations; i++) {
+ detail = fn() || 'ok';
+ }
+ const total = now() - start;
+ collected.push({
+ name,
+ ok: true,
+ detail,
+ iterations,
+ avgMs: total / iterations,
+ });
+ } catch (err) {
+ collected.push({
+ name,
+ ok: false,
+ detail: err instanceof Error ? err.message : String(err),
+ iterations,
+ avgMs: null,
+ });
+ }
+ };
+
+/**
+ * Formats an average duration in ms with resolution appropriate to its size.
+ */
+const formatAvg = (avgMs: number): string => {
+ if (avgMs >= 100) {
+ return `${avgMs.toFixed(0)} ms`;
+ }
+ if (avgMs >= 1) {
+ return `${avgMs.toFixed(2)} ms`;
+ }
+ return `${avgMs.toFixed(4)} ms`;
+};
+
+/**
+ * Proof-of-concept screen that exercises two libsodium bindings inside the
+ * React Native (Hermes) runtime and reports whether each cryptographic
+ * primitive round-trips correctly. The bindings are libsodium-wrappers
+ * (pure-JS / WebAssembly) and react-native-libsodium (native JSI bindings).
+ */
+const LibsodiumPoc = () => {
+ const tw = useTailwind();
+ const navigation = useNavigation();
+ const [running, setRunning] = useState(false);
+ const [results, setResults] = useState([]);
+ const [summary, setSummary] = useState(null);
+ const [engine, setEngine] = useState(null);
+ const [iterationsInput, setIterationsInput] = useState('1');
+
+ const parseIterations = useCallback(() => {
+ const parsed = parseInt(iterationsInput, 10);
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : 1;
+ }, [iterationsInput]);
+
+ /**
+ * libsodium-wrappers (pure-JS / WebAssembly) — full wrapper API, including
+ * string helpers and the secretstream construction.
+ */
+ const runJsChecks = useCallback(async () => {
+ setRunning(true);
+ setResults([]);
+ setSummary(null);
+ setEngine(null);
+
+ const iterations = parseIterations();
+ const collected: CheckResult[] = [];
+ const check = makeCheck(collected, iterations);
+
+ try {
+ await _sodium.ready;
+ const sodium = _sodium;
+
+ setEngine(
+ `libsodium-wrappers ${sodium.sodium_version_string()} · ${
+ typeof WebAssembly === 'undefined'
+ ? 'pure-JS (asm.js backup — no WebAssembly)'
+ : 'WebAssembly available'
+ }`,
+ );
+
+ check('helpers: hex round-trip', () => {
+ const bytes = sodium.from_hex('deadbeef');
+ if (sodium.to_hex(bytes) !== 'deadbeef') {
+ throw new Error('hex mismatch');
+ }
+ return 'deadbeef -> bytes -> deadbeef';
+ });
+
+ check('helpers: utf-8 string round-trip', () => {
+ const s = 'héllo libsodium 🔐';
+ if (sodium.to_string(sodium.from_string(s)) !== s) {
+ throw new Error('string mismatch');
+ }
+ return s;
+ });
+
+ check('helpers: base64 round-trip', () => {
+ const b = sodium.from_string('metamask');
+ const b64 = sodium.to_base64(b);
+ if (sodium.to_hex(sodium.from_base64(b64)) !== sodium.to_hex(b)) {
+ throw new Error('base64 mismatch');
+ }
+ return b64;
+ });
+
+ check('randombytes_buf(32)', () => {
+ const r = sodium.randombytes_buf(32);
+ if (!(r instanceof Uint8Array) || r.length !== 32) {
+ throw new Error('bad random buffer');
+ }
+ return `len=${r.length}`;
+ });
+
+ check('crypto_generichash (BLAKE2b) known vector', () => {
+ const h = sodium.crypto_generichash(
+ 32,
+ sodium.from_string('test'),
+ null,
+ );
+ if (sodium.to_hex(h) !== GENERICHASH_TEST_VECTOR) {
+ throw new Error(`got ${sodium.to_hex(h)}`);
+ }
+ return GENERICHASH_TEST_VECTOR.slice(0, 16) + '…';
+ });
+
+ check('crypto_secretbox: encrypt/decrypt round-trip', () => {
+ const key = sodium.crypto_secretbox_keygen();
+ const nonce = sodium.randombytes_buf(
+ sodium.crypto_secretbox_NONCEBYTES,
+ );
+ const ct = sodium.crypto_secretbox_easy(
+ sodium.from_string('secretbox message'),
+ nonce,
+ key,
+ );
+ const pt = sodium.crypto_secretbox_open_easy(ct, nonce, key);
+ if (sodium.to_string(pt) !== 'secretbox message') {
+ throw new Error('decrypt mismatch');
+ }
+ return 'decrypted ok';
+ });
+
+ check('crypto_secretbox: tampered ciphertext rejected', () => {
+ const key = sodium.crypto_secretbox_keygen();
+ const nonce = sodium.randombytes_buf(
+ sodium.crypto_secretbox_NONCEBYTES,
+ );
+ const ct = sodium.crypto_secretbox_easy(
+ sodium.from_string('tamper me'),
+ nonce,
+ key,
+ );
+ ct[0] ^= 0xff;
+ let threw = false;
+ try {
+ sodium.crypto_secretbox_open_easy(ct, nonce, key);
+ } catch {
+ threw = true;
+ }
+ if (!threw) {
+ throw new Error('tampered ciphertext was accepted');
+ }
+ return 'rejected as expected';
+ });
+
+ check('crypto_box (X25519): public-key encryption', () => {
+ const alice = sodium.crypto_box_keypair();
+ const bob = sodium.crypto_box_keypair();
+ const nonce = sodium.randombytes_buf(sodium.crypto_box_NONCEBYTES);
+ const ct = sodium.crypto_box_easy(
+ sodium.from_string('hello bob'),
+ nonce,
+ bob.publicKey,
+ alice.privateKey,
+ );
+ const pt = sodium.crypto_box_open_easy(
+ ct,
+ nonce,
+ alice.publicKey,
+ bob.privateKey,
+ );
+ if (sodium.to_string(pt) !== 'hello bob') {
+ throw new Error('decrypt mismatch');
+ }
+ return `keyType=${alice.keyType}`;
+ });
+
+ check('crypto_sign (Ed25519): sign/verify + reject bad', () => {
+ const kp = sodium.crypto_sign_keypair();
+ const msg = sodium.from_string('sign this');
+ const sig = sodium.crypto_sign_detached(msg, kp.privateKey);
+ if (!sodium.crypto_sign_verify_detached(sig, msg, kp.publicKey)) {
+ throw new Error('valid signature rejected');
+ }
+ sig[0] ^= 0xff;
+ if (sodium.crypto_sign_verify_detached(sig, msg, kp.publicKey)) {
+ throw new Error('invalid signature accepted');
+ }
+ return `keyType=${kp.keyType}`;
+ });
+
+ check('crypto_secretstream_xchacha20poly1305 round-trip', () => {
+ const key = sodium.crypto_secretstream_xchacha20poly1305_keygen();
+ const { state: sOut, header } =
+ sodium.crypto_secretstream_xchacha20poly1305_init_push(key);
+ const c1 = sodium.crypto_secretstream_xchacha20poly1305_push(
+ sOut,
+ sodium.from_string('message 1'),
+ null,
+ sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE,
+ );
+ const c2 = sodium.crypto_secretstream_xchacha20poly1305_push(
+ sOut,
+ sodium.from_string('message 2'),
+ null,
+ sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL,
+ );
+ const sIn = sodium.crypto_secretstream_xchacha20poly1305_init_pull(
+ header,
+ key,
+ );
+ const r1 = sodium.crypto_secretstream_xchacha20poly1305_pull(
+ sIn,
+ c1,
+ null,
+ );
+ const r2 = sodium.crypto_secretstream_xchacha20poly1305_pull(
+ sIn,
+ c2,
+ null,
+ );
+ if (r1 === false || r2 === false) {
+ throw new Error('stream pull failed (bad tag/ciphertext)');
+ }
+ if (
+ sodium.to_string(r1.message) !== 'message 1' ||
+ sodium.to_string(r2.message) !== 'message 2'
+ ) {
+ throw new Error('stream decrypt mismatch');
+ }
+ return '2 messages decrypted';
+ });
+
+ const passed = collected.filter((r) => r.ok).length;
+ setResults(collected);
+ setSummary(
+ `${passed}/${collected.length} checks passed · ${iterations}× each`,
+ );
+ } catch (err) {
+ setResults(collected);
+ setSummary(
+ `libsodium-wrappers failed to initialize: ${
+ err instanceof Error ? err.message : String(err)
+ }`,
+ );
+ } finally {
+ setRunning(false);
+ }
+ }, [parseIterations]);
+
+ /**
+ * react-native-libsodium (native JSI). Its native binding exposes a curated
+ * subset of the API — notably no string helpers (`from_string`/`from_hex`),
+ * no `sodium_version_string`, and no secretstream — but it does include the
+ * native `crypto_pwhash` (Argon2). Checks below stick to that subset and pass
+ * JS strings directly where the wrapper would normally take a `from_string`
+ * buffer.
+ */
+ const runNativeChecks = useCallback(async () => {
+ setRunning(true);
+ setResults([]);
+ setSummary(null);
+ setEngine(null);
+
+ const iterations = parseIterations();
+ const collected: CheckResult[] = [];
+ const check = makeCheck(collected, iterations);
+
+ try {
+ await _rnSodium.ready;
+ const sodium = _rnSodium;
+
+ setEngine('react-native-libsodium (native JSI) · libsodium 1.0.21');
+
+ check('helpers: base64 round-trip', () => {
+ const b = sodium.randombytes_buf(16);
+ const b64 = sodium.to_base64(b);
+ if (sodium.to_hex(sodium.from_base64(b64)) !== sodium.to_hex(b)) {
+ throw new Error('base64 mismatch');
+ }
+ return b64;
+ });
+
+ check('randombytes_buf(32)', () => {
+ const r = sodium.randombytes_buf(32);
+ if (!(r instanceof Uint8Array) || r.length !== 32) {
+ throw new Error('bad random buffer');
+ }
+ return `len=${r.length}`;
+ });
+
+ check('crypto_generichash (BLAKE2b) known vector', () => {
+ // Native accepts JS strings directly (UTF-8), matching from_string.
+ const h = sodium.crypto_generichash(32, 'test', null);
+ if (sodium.to_hex(h) !== GENERICHASH_TEST_VECTOR) {
+ throw new Error(`got ${sodium.to_hex(h)}`);
+ }
+ return GENERICHASH_TEST_VECTOR.slice(0, 16) + '…';
+ });
+
+ check('crypto_secretbox: encrypt/decrypt round-trip', () => {
+ const key = sodium.crypto_secretbox_keygen();
+ const nonce = sodium.randombytes_buf(
+ sodium.crypto_secretbox_NONCEBYTES,
+ );
+ const ct = sodium.crypto_secretbox_easy(
+ 'secretbox message',
+ nonce,
+ key,
+ );
+ const pt = sodium.crypto_secretbox_open_easy(ct, nonce, key);
+ if (sodium.to_string(pt) !== 'secretbox message') {
+ throw new Error('decrypt mismatch');
+ }
+ return 'decrypted ok';
+ });
+
+ check('crypto_secretbox: tampered ciphertext rejected', () => {
+ const key = sodium.crypto_secretbox_keygen();
+ const nonce = sodium.randombytes_buf(
+ sodium.crypto_secretbox_NONCEBYTES,
+ );
+ const ct = sodium.crypto_secretbox_easy('tamper me', nonce, key);
+ ct[0] ^= 0xff;
+ let threw = false;
+ try {
+ sodium.crypto_secretbox_open_easy(ct, nonce, key);
+ } catch {
+ threw = true;
+ }
+ if (!threw) {
+ throw new Error('tampered ciphertext was accepted');
+ }
+ return 'rejected as expected';
+ });
+
+ check('crypto_box (X25519): public-key encryption', () => {
+ const alice = sodium.crypto_box_keypair();
+ const bob = sodium.crypto_box_keypair();
+ const nonce = sodium.randombytes_buf(sodium.crypto_box_NONCEBYTES);
+ const ct = sodium.crypto_box_easy(
+ 'hello bob',
+ nonce,
+ bob.publicKey,
+ alice.privateKey,
+ );
+ const pt = sodium.crypto_box_open_easy(
+ ct,
+ nonce,
+ alice.publicKey,
+ bob.privateKey,
+ );
+ if (sodium.to_string(pt) !== 'hello bob') {
+ throw new Error('decrypt mismatch');
+ }
+ return `keyType=${alice.keyType}`;
+ });
+
+ check('crypto_sign (Ed25519): sign/verify + reject bad', () => {
+ const kp = sodium.crypto_sign_keypair();
+ const sig = sodium.crypto_sign_detached('sign this', kp.privateKey);
+ if (
+ !sodium.crypto_sign_verify_detached(sig, 'sign this', kp.publicKey)
+ ) {
+ throw new Error('valid signature rejected');
+ }
+ sig[0] ^= 0xff;
+ if (
+ sodium.crypto_sign_verify_detached(sig, 'sign this', kp.publicKey)
+ ) {
+ throw new Error('invalid signature accepted');
+ }
+ return `keyType=${kp.keyType}`;
+ });
+
+ check('crypto_pwhash (Argon2) deterministic — native only', () => {
+ const salt = sodium.randombytes_buf(sodium.crypto_pwhash_SALTBYTES);
+ const k1 = sodium.crypto_pwhash(
+ 32,
+ 'correct horse battery staple',
+ salt,
+ sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE,
+ sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE,
+ sodium.crypto_pwhash_ALG_DEFAULT,
+ );
+ const k2 = sodium.crypto_pwhash(
+ 32,
+ 'correct horse battery staple',
+ salt,
+ sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE,
+ sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE,
+ sodium.crypto_pwhash_ALG_DEFAULT,
+ );
+ if (k1.length !== 32 || sodium.to_hex(k1) !== sodium.to_hex(k2)) {
+ throw new Error('pwhash not deterministic');
+ }
+ return `derived ${k1.length}-byte key`;
+ });
+
+ const passed = collected.filter((r) => r.ok).length;
+ setResults(collected);
+ setSummary(
+ `${passed}/${collected.length} checks passed · ${iterations}× each`,
+ );
+ } catch (err) {
+ setResults(collected);
+ setSummary(
+ `react-native-libsodium failed to initialize: ${
+ err instanceof Error ? err.message : String(err)
+ }`,
+ );
+ } finally {
+ setRunning(false);
+ }
+ }, [parseIterations]);
+
+ const handleBack = useCallback(() => {
+ navigation.goBack();
+ }, [navigation]);
+
+ return (
+
+
+
+
+ Exercises two libsodium bindings inside the React Native runtime and
+ reports whether each primitive round-trips correctly.
+
+
+
+
+ Iterations per check
+
+
+ setIterationsInput(text.replace(/[^0-9]/g, ''))
+ }
+ onEndEditing={() => {
+ if (!iterationsInput || parseInt(iterationsInput, 10) < 1) {
+ setIterationsInput('1');
+ }
+ }}
+ keyboardType="number-pad"
+ editable={!running}
+ placeholder="1"
+ style={tw.style(
+ 'w-20 px-3 py-2 rounded-lg border border-border-default text-text-default text-center bg-background-default',
+ )}
+ placeholderTextColor={tw.color('text-muted')}
+ testID="libsodium-poc-iterations-input"
+ />
+
+
+
+
+
+
+ {running && !summary ? (
+
+
+
+ ) : null}
+
+ {engine ? (
+
+
+ {engine}
+
+
+ ) : null}
+
+ {summary ? (
+
+
+ {summary}
+
+
+ ) : null}
+
+ {results.map((r) => (
+
+
+
+ {r.ok ? '✓' : '✗'} {r.name}
+
+ {r.avgMs !== null ? (
+
+ {formatAvg(r.avgMs)}
+
+ ) : null}
+
+
+ {r.detail}
+ {r.avgMs !== null && r.iterations > 1
+ ? ` · avg over ${r.iterations} runs`
+ : ''}
+
+
+ ))}
+
+
+ );
+};
+
+export default LibsodiumPoc;
diff --git a/app/components/Views/Wallet/index.tsx b/app/components/Views/Wallet/index.tsx
index cb66f96c035..91de62502e5 100644
--- a/app/components/Views/Wallet/index.tsx
+++ b/app/components/Views/Wallet/index.tsx
@@ -71,6 +71,9 @@ import {
BadgeWrapper,
BadgeWrapperPosition,
BadgeWrapperPositionAnchorShape,
+ Button as MMDSButton,
+ ButtonVariant as MMDSButtonVariant,
+ ButtonSize as MMDSButtonSize,
ButtonIcon,
ButtonIconSize,
IconColor as MMDSIconColor,
@@ -1039,6 +1042,19 @@ const Wallet = ({
) : null;
+ const libsodiumPocButton =
+ process.env.METAMASK_ENVIRONMENT !== 'production' ? (
+ navigation.navigate(Routes.LIBSODIUM_POC)}
+ testID="wallet-libsodium-poc-button"
+ >
+ libsodium POC
+
+ ) : null;
+
const portfolioHeaderBase = (
{bannerContent}
@@ -1049,6 +1065,7 @@ const Wallet = ({
homeGrowthBannerContent}
{homepageDiscoveryPills}
{isMoneyAccountVisible && }
+ {libsodiumPocButton}
);
@@ -1064,6 +1081,7 @@ const Wallet = ({
homeGrowthBannerContent}
{homepageDiscoveryPills}
{isMoneyAccountVisible && }
+ {libsodiumPocButton}
);
diff --git a/app/constants/navigation/Routes.ts b/app/constants/navigation/Routes.ts
index 20eec0ff886..312da2bffb2 100644
--- a/app/constants/navigation/Routes.ts
+++ b/app/constants/navigation/Routes.ts
@@ -583,6 +583,7 @@ const Routes = {
CONFIRM: 'AgenticCliDashboardConfirmation',
},
FEATURE_FLAG_OVERRIDE: 'FeatureFlagOverride',
+ LIBSODIUM_POC: 'LibsodiumPoc',
CREATE_PRICE_ALERT: 'CreatePriceAlert',
MANAGE_PRICE_ALERTS: 'ManagePriceAlerts',
SECURITY_TRUST: 'SecurityTrust',
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 5063e98376c..b37ea582d35 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -2776,6 +2776,34 @@ PODS:
- Yoga
- react-native-launch-arguments (4.0.1):
- React
+ - react-native-libsodium (1.7.0):
+ - boost
+ - DoubleConversion
+ - fast_float
+ - fmt
+ - glog
+ - hermes-engine
+ - RCT-Folly
+ - RCT-Folly/Fabric
+ - RCTRequired
+ - RCTTypeSafety
+ - React-Core
+ - React-debug
+ - React-Fabric
+ - React-featureflags
+ - React-graphics
+ - React-ImageManager
+ - React-jsi
+ - React-NativeModulesApple
+ - React-RCTFabric
+ - React-renderercss
+ - React-rendererdebug
+ - React-utils
+ - ReactCodegen
+ - ReactCommon/turbomodule/bridging
+ - ReactCommon/turbomodule/core
+ - SocketRocket
+ - Yoga
- react-native-mmkv (3.3.3):
- boost
- DoubleConversion
@@ -4552,6 +4580,7 @@ DEPENDENCIES:
- react-native-in-app-review (from `../node_modules/react-native-in-app-review`)
- react-native-keyboard-controller (from `../node_modules/react-native-keyboard-controller`)
- react-native-launch-arguments (from `../node_modules/react-native-launch-arguments`)
+ - react-native-libsodium (from `../node_modules/react-native-libsodium`)
- react-native-mmkv (from `../node_modules/react-native-mmkv`)
- "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)"
- react-native-pager-view (from `../node_modules/react-native-pager-view`)
@@ -4872,6 +4901,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-keyboard-controller"
react-native-launch-arguments:
:path: "../node_modules/react-native-launch-arguments"
+ react-native-libsodium:
+ :path: "../node_modules/react-native-libsodium"
react-native-mmkv:
:path: "../node_modules/react-native-mmkv"
react-native-netinfo:
@@ -5160,6 +5191,7 @@ SPEC CHECKSUMS:
react-native-in-app-review: b3d1eed3d1596ebf6539804778272c4c65e4a400
react-native-keyboard-controller: dbf568b5d029cdc5b26667a7c2323b160c25dc00
react-native-launch-arguments: 7eb321ed3f3ef19b3ec4a2eca71c4f9baee76b41
+ react-native-libsodium: 9b2056828bae2ada8ca3ec52666d24e26687c357
react-native-mmkv: ac7507625cd74bac0eb5333604a7cd7b08fe9e3e
react-native-netinfo: 57447b5a45c98808f8eae292cf641f3d91d13830
react-native-pager-view: d7d2aa47f54343bf55fdcee3973503dd27c2bd37
diff --git a/metro.config.js b/metro.config.js
index 3da33e91e43..970f1898bb9 100644
--- a/metro.config.js
+++ b/metro.config.js
@@ -132,6 +132,16 @@ module.exports = function (baseConfig) {
'node:buffer': '@craftzdog/react-native-buffer',
},
resolveRequest: (context, moduleName, platform) => {
+ // libsodium.js references `node:fs` inside a Node-only runtime
+ // branch (gated on `process.versions.node`) that never executes
+ // in React Native. Metro still resolves the require statically,
+ // and `node:fs` is not shimmed, so stub it to an empty module.
+ if (
+ moduleName === 'node:fs' &&
+ context.originModulePath?.includes('libsodium')
+ ) {
+ return { type: 'empty' };
+ }
// MYXProvider is intentionally excluded from @metamask/perps-controller's
// published dist (extension-only). The dynamic import() uses webpackIgnore
// but babel's dynamicImportToRequire rewrites it to require(), causing Metro
diff --git a/package.json b/package.json
index 18055f28659..0385fc766dc 100644
--- a/package.json
+++ b/package.json
@@ -470,6 +470,7 @@
"ip-regex": "^5.0.0",
"is-url": "^1.2.4",
"js-sha3": "0.9.3",
+ "libsodium-wrappers": "^0.8.4",
"lodash": "4.18.1",
"lottie-react-native": "~7.3.1",
"luxon": "^3.5.0",
@@ -518,6 +519,7 @@
"react-native-keyboard-controller": "1.20.7",
"react-native-keychain": "^9.0.0",
"react-native-level-fs": "3.0.1",
+ "react-native-libsodium": "^1.7.0",
"react-native-linear-gradient": "^2.8.3",
"react-native-material-textfield": "0.16.1",
"react-native-mmkv": "^3.2.0",
diff --git a/yarn.lock b/yarn.lock
index a176b51a500..d95ad9d6a9a 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -34983,6 +34983,38 @@ __metadata:
languageName: node
linkType: hard
+"libsodium-sumo@npm:^0.8.0":
+ version: 0.8.4
+ resolution: "libsodium-sumo@npm:0.8.4"
+ checksum: 10/dcb605269232b1724fb3f19d1d261ec0c47406f7102e16e5fa02ef3fd101b93ab6fa00b1a3029fbdfc5ea82533539d3221ed004afba53788099e8701baeb0797
+ languageName: node
+ linkType: hard
+
+"libsodium-wrappers-sumo@npm:^0.8.2":
+ version: 0.8.4
+ resolution: "libsodium-wrappers-sumo@npm:0.8.4"
+ dependencies:
+ libsodium-sumo: "npm:^0.8.0"
+ checksum: 10/026dfca86f7497ad7572491105c1eb11a423694c5742d29f38f4cdcd8f7ca76c083fc18a7448da0dfaec2fb764086e65775157f197474416598df41f8f419c8e
+ languageName: node
+ linkType: hard
+
+"libsodium-wrappers@npm:^0.8.2, libsodium-wrappers@npm:^0.8.4":
+ version: 0.8.4
+ resolution: "libsodium-wrappers@npm:0.8.4"
+ dependencies:
+ libsodium: "npm:^0.8.0"
+ checksum: 10/186e9004482826a086e03017efca80405b7449f085cced48702adb8bb92d66e8c983f761550883cb1c078b01098ca459053d3c7c69ec3cdee7f86ceba25bd888
+ languageName: node
+ linkType: hard
+
+"libsodium@npm:^0.8.0":
+ version: 0.8.4
+ resolution: "libsodium@npm:0.8.4"
+ checksum: 10/14ce69b78350e81d92d60da78608df5d019825013b3140999d820c70fb7151f5b6e71e1d7c05ca88de4b46f8deda710a023a6ecd4a8e0b8544a4d5e129d1a769
+ languageName: node
+ linkType: hard
+
"lie@npm:~3.3.0":
version: 3.3.0
resolution: "lie@npm:3.3.0"
@@ -36272,6 +36304,7 @@ __metadata:
js-sha3: "npm:0.9.3"
js-yaml: "npm:^4.1.0"
koa: "npm:^2.14.2"
+ libsodium-wrappers: "npm:^0.8.4"
lint-staged: "npm:10.5.4"
listr2: "npm:^8.0.2"
liveline: "npm:0.0.7"
@@ -36335,6 +36368,7 @@ __metadata:
react-native-keychain: "npm:^9.0.0"
react-native-launch-arguments: "npm:^4.0.1"
react-native-level-fs: "npm:3.0.1"
+ react-native-libsodium: "npm:^1.7.0"
react-native-linear-gradient: "npm:^2.8.3"
react-native-material-textfield: "npm:0.16.1"
react-native-mmkv: "npm:^3.2.0"
@@ -40783,6 +40817,20 @@ __metadata:
languageName: node
linkType: hard
+"react-native-libsodium@npm:^1.7.0":
+ version: 1.7.0
+ resolution: "react-native-libsodium@npm:1.7.0"
+ dependencies:
+ "@noble/hashes": "npm:^1.3.2"
+ libsodium-wrappers: "npm:^0.8.2"
+ libsodium-wrappers-sumo: "npm:^0.8.2"
+ peerDependencies:
+ react: "*"
+ react-native: "*"
+ checksum: 10/e1b6991df02bed1137fcd43dc3d5eccdeca7c65b21f08346ece5e47bc23a92c0f30ed172ad7b82afb331d96a8080a0f06ddb7b5ac82568db4aa52583c5e7eaf5
+ languageName: node
+ linkType: hard
+
"react-native-linear-gradient@npm:^2.8.3":
version: 2.8.3
resolution: "react-native-linear-gradient@npm:2.8.3"