Skip to content
This repository was archived by the owner on Mar 2, 2026. It is now read-only.
Open
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,10 @@ jobs:
create_env_file "apps/extension/.env.dev"
fi

- name: Run tests
run: pnpm -F flow-wallet-extension test:run
- name: Run tests for all packages
run: |
echo "Running tests for all packages..."
pnpm test:run

- name: Build
run: |
Expand Down
6 changes: 1 addition & 5 deletions apps/extension/_raw/manifest/manifest.dev.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,7 @@
"host_permissions": ["https://api.mixpanel.com/*"],
"web_accessible_resources": [
{
"resources": [
"node_modules/@trustwallet/wallet-core/dist/lib/wallet-core.wasm",
"pageProvider.js",
"user-media-permission.html"
],
"resources": ["pageProvider.js", "user-media-permission.html"],
"matches": ["<all_urls>"]
},
{
Expand Down
6 changes: 1 addition & 5 deletions apps/extension/_raw/manifest/manifest.pro.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,7 @@
"host_permissions": ["https://api.mixpanel.com/*"],
"web_accessible_resources": [
{
"resources": [
"node_modules/@trustwallet/wallet-core/dist/lib/wallet-core.wasm",
"pageProvider.js",
"user-media-permission.html"
],
"resources": ["pageProvider.js", "user-media-permission.html"],
"matches": ["<all_urls>"]
},
{
Expand Down
13 changes: 1 addition & 12 deletions apps/extension/build/webpack.common.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,6 @@ const config = (env: { config: 'dev' | 'pro' | 'none' }): webpack.Configuration
},
experiments: {
topLevelAwait: true,
asyncWebAssembly: true,
syncWebAssembly: true,
},
module: {
rules: [
Expand Down Expand Up @@ -103,11 +101,6 @@ const config = (env: { config: 'dev' | 'pro' | 'none' }): webpack.Configuration
filename: 'images/[name][ext]',
},
},
{
test: /\.wasm$/,
type: 'webassembly/async',
include: /node_modules/,
},
{
test: /\.md$/,
use: 'raw-loader',
Expand All @@ -124,11 +117,7 @@ const config = (env: { config: 'dev' | 'pro' | 'none' }): webpack.Configuration
plugins: [
new CopyPlugin({
patterns: [
{
from: '../../node_modules/@trustwallet/wallet-core/dist/lib/wallet-core.wasm',
to: 'wallet-core.wasm',
},
// Add this pattern to copy the manifest.json from _raw to dist
// Copy locales
{
from: '_raw/_locales',
to: '_locales',
Expand Down
13 changes: 8 additions & 5 deletions apps/extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"pre-commit": "pnpm lint-staged",
"analyze:imports": "pnpx tsx build/analyze-imports.ts",
"analyze:dependencies": "pnpx tsx build/analyze-dependencies.ts",
"benchmark:crypto": "pnpx tsx build/performance-tests/run-benchmark.ts",
"analyze:fetch": "./build/fetch-project-data.sh",
"analyze:project": "pnpx tsx build/analyze-project.ts",
"analyze:priority": "pnpm analyze:fetch && pnpm analyze:project",
Expand All @@ -50,15 +51,14 @@
"@mui/icons-material": "^7.2.0",
"@mui/material": "^7.2.0",
"@mui/system": "^7.2.0",
"@noble/secp256k1": "^1.7.2",
"@noble/curves": "^1.9.3",
"@onflow/fcl": "^1.19.0",
"@onflow/flow-wallet-core": "workspace:*",
"@onflow/flow-wallet-data-model": "workspace:*",
"@onflow/flow-wallet-extension-shared": "workspace:*",
"@onflow/flow-wallet-reducers": "workspace:*",
"@onflow/flow-wallet-shared": "workspace:*",
"@onflow/flow-wallet-extension-shared": "workspace:*",
"@onflow/flow-wallet-data-model": "workspace:*",
"@onflow/flow-wallet-core": "workspace:*",
"@reown/walletkit": "^1.2.8",
"@trustwallet/wallet-core": "^4.3.6",
"@tsparticles/engine": "^3.8.1",
"@tsparticles/react": "^3.0.0",
"@walletconnect/core": "^2.21.4",
Expand Down Expand Up @@ -122,6 +122,8 @@
"@eslint/js": "^9.30.1",
"@metamask/eth-sig-util": "^8.2.0",
"@playwright/test": "^1.53.2",
"@scure/bip32": "^1.7.0",
"@scure/bip39": "^1.6.0",
"@storybook/addon-docs": "^9.0.16",
"@storybook/addon-onboarding": "^9.0.16",
"@storybook/addon-themes": "^9.0.16",
Expand Down Expand Up @@ -184,6 +186,7 @@
"typescript-eslint": "^8.36.0",
"typescript-transform-paths": "^3.5.5",
"url-loader": "^4.1.1",
"viem": "^2.32.0",
"vitest": "^3.2.4",
"webpack": "^5.99.9",
"webpack-cli": "^4.10.0",
Expand Down
18 changes: 11 additions & 7 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,21 +62,25 @@
}
},
"dependencies": {
"@onflow/flow-wallet-shared": "workspace:*",
"@onflow/flow-wallet-extension-shared": "workspace:*",
"@noble/ciphers": "^1.3.0",
"@noble/curves": "^1.9.3",
"@onflow/flow-wallet-data-model": "workspace:*",
"@onflow/flow-wallet-extension-shared": "workspace:*",
"@onflow/flow-wallet-shared": "workspace:*",
"@scure/bip32": "^1.7.0",
"@scure/bip39": "^1.6.0",
"bignumber.js": "^9.1.2"
},
"devDependencies": {
"@types/node": "^22.10.2",
"@vitest/coverage-v8": "3.2.4",
"tsup": "^8.3.5",
"typescript": "^5.7.2",
"vitest": "^3.2.4",
"@types/react": "^18.3.23",
"@types/react-dom": "^18.3.7",
"@vitest/coverage-v8": "3.2.4",
"react": "^18.3.1",
"react-dom": "^18.3.1"
"react-dom": "^18.3.1",
"tsup": "^8.3.5",
"typescript": "^5.7.2",
"vitest": "^3.2.4"
},
"peerDependencies": {
"ethers": "^6.13.4",
Expand Down
42 changes: 34 additions & 8 deletions packages/core/src/service/userWallet.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import * as secp from '@noble/secp256k1';
import { secp256k1 } from '@noble/curves/secp256k1';
import { sha256 } from '@noble/hashes/sha2';
import { keccak_256 } from '@noble/hashes/sha3';
import * as fcl from '@onflow/fcl';
import type { Account as FclAccount } from '@onflow/typedefs';
import * as ethUtil from 'ethereumjs-util';
import { getApp } from 'firebase/app';
import { getAuth, signInAnonymously } from 'firebase/auth/web-extension';

Expand Down Expand Up @@ -1097,10 +1098,9 @@ class UserWallet {
Buffer.from(value.padEnd(pad * 2, 0), 'hex').toString('hex');
const USER_DOMAIN_TAG = rightPaddedHexBuffer(Buffer.from('FLOW-V0.0-user').toString('hex'), 32);

const hex = secp.utils.bytesToHex;
const message = USER_DOMAIN_TAG + Buffer.from(idToken, 'utf8').toString('hex');

const messageHash = await secp.utils.sha256(Buffer.from(message, 'hex'));
const messageHash = await sha256(Buffer.from(message, 'hex'));

// Get the private key tuple
const publicPrivateKeyTuple = await seed2PublicPrivateKey(mnemonic);
Expand All @@ -1111,10 +1111,12 @@ class UserWallet {
// TODO: Look into the logic for this
// We want to us a secp256k1 public key in this logic
// We should be able to use the public key from the account key request...
const publicKey = hex(secp.getPublicKey(privateKey).slice(1));
const publicKey = Buffer.from(secp256k1.getPublicKey(privateKey, false).slice(1)).toString(
'hex'
);
if (accountKey.public_key === publicKey) {
const signature = await secp.sign(messageHash, privateKey);
const realSignature = secp.Signature.fromHex(signature).toCompactHex();
const signature = secp256k1.sign(messageHash, privateKey);
const realSignature = signature.toCompactHex();
return openapiService.loginV3(accountKey, deviceInfo, realSignature, replaceUser);
} else {
return false;
Expand Down Expand Up @@ -1545,6 +1547,30 @@ const childAccountMapToWalletAccounts = (
return childAccounts;
};

/**
* Convert an address to ERC-55 checksum format using Noble
* @param address - The address to convert (with or without 0x prefix)
* @returns The checksummed address with 0x prefix
*/
const toChecksumAddress = (address: string): string => {
// Remove 0x prefix if present and convert to lowercase
const addr = address.toLowerCase().replace(/^0x/i, '');

// Get the keccak hash of the lowercase address
const hash = keccak_256(addr);
const hashHex = Buffer.from(hash).toString('hex');

// Build the checksummed address
let checksummed = '0x';
for (let i = 0; i < addr.length; i++) {
const char = addr[i];
const hashByte = parseInt(hashHex[i], 16);
checksummed += hashByte >= 8 ? char.toUpperCase() : char;
}

return checksummed;
};

/**
* Convert an EVM address to a wallet account
* @param network - The network to load the accounts for
Expand All @@ -1566,7 +1592,7 @@ const evmAddressToWalletAccount = (network: string, evmAddress?: EvmAddress): Wa

// This is the COA address we get straight from the script
// This is where we encode the address in ERC-55 format
const checksummedAddress = ethUtil.toChecksumAddress(ensureEvmAddressPrefix(evmAddress));
const checksummedAddress = toChecksumAddress(ensureEvmAddressPrefix(evmAddress));

// The index of the evm wallet - always 0 as we only support one evm wallet
const index = 0;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { ctr } from '@noble/ciphers/aes';
import { scrypt } from '@noble/hashes/scrypt';
import { keccak_256 } from '@noble/hashes/sha3';
import { concatBytes, randomBytes } from '@noble/hashes/utils';

/**
* Generate a test keystore for testing
*/
export function generateTestKeystore(privateKey: Uint8Array, password: string) {
const salt = randomBytes(32);
const iv = randomBytes(16);
const dklen = 32;

// Derive key using scrypt
const derivedKey = scrypt(new TextEncoder().encode(password), salt, {
N: 8192,
r: 8,
p: 1,
dkLen: dklen,
});

// Encrypt private key
const aes = ctr(derivedKey.slice(0, 16), iv);
const ciphertext = aes.encrypt(privateKey);

// Generate MAC
const macData = concatBytes(derivedKey.slice(16, 32), ciphertext);
const mac = keccak_256(macData);

return {
version: 3,
id: 'test-id',
crypto: {
ciphertext: Buffer.from(ciphertext).toString('hex'),
cipherparams: {
iv: Buffer.from(iv).toString('hex'),
},
cipher: 'aes-128-ctr',
kdf: 'scrypt',
kdfparams: {
dklen: 32,
salt: Buffer.from(salt).toString('hex'),
n: 8192,
r: 8,
p: 1,
},
mac: Buffer.from(mac).toString('hex'),
},
};
}
Loading
Loading