diff --git a/packages/assets-controllers/src/TokenDetectionController.ts b/packages/assets-controllers/src/TokenDetectionController.ts index 8e483d8f37b..f134a913e03 100644 --- a/packages/assets-controllers/src/TokenDetectionController.ts +++ b/packages/assets-controllers/src/TokenDetectionController.ts @@ -169,6 +169,7 @@ export class TokenDetectionController extends StaticIntervalPollingController< #isDetectionEnabledForNetwork: boolean; + // OOOH interesting, this is from another controller! readonly #getBalancesInSingleCall: AssetsContractController['getBalancesInSingleCall']; readonly #trackMetaMetricsEvent: (options: { @@ -203,6 +204,7 @@ export class TokenDetectionController extends StaticIntervalPollingController< messenger, }: { interval?: number; + // NOTE - CHECK HOW THIS WORKS disabled?: boolean; getBalancesInSingleCall: AssetsContractController['getBalancesInSingleCall']; trackMetaMetricsEvent: (options: { @@ -467,6 +469,7 @@ export class TokenDetectionController extends StaticIntervalPollingController< this.setIntervalLength(DEFAULT_INTERVAL); } + // THIS IS THE MAIN LOGIC /** * For each token in the token list provided by the TokenListController, checks the token's balance for the selected account address on the active network. * On mainnet, if token detection is disabled in preferences, ERC20 token auto detection will be triggered for each contract address in the legacy token list from the @metamask/contract-metadata repo. @@ -527,6 +530,7 @@ export class TokenDetectionController extends StaticIntervalPollingController< await Promise.all(tokenDetectionPromises); } + // Batches of 1000 tokens arr. #getSlicesOfTokensToDetect({ chainId, selectedAddress, @@ -572,6 +576,7 @@ export class TokenDetectionController extends StaticIntervalPollingController< return slicesOfTokensToDetect; } + // CALLS ARE MADE HERE async #addDetectedTokens({ tokensSlice, selectedAddress, @@ -584,12 +589,19 @@ export class TokenDetectionController extends StaticIntervalPollingController< chainId: Hex; }): Promise { await safelyExecute(async () => { + // OK NICE + // Lets see if we can add impl inside this `getBalancesInSingleCall` & retain interface? + // Or we can do branching logic? + // Interface is `BalanceMap`, either keep same interface or some add some logic - lets see after inspecting API + // NOTE - this is from a different controller 👀 const balances = await this.#getBalancesInSingleCall( selectedAddress, tokensSlice, networkClientId, ); + // Mapping to `Token` shape. + // We can decide how to handle interface mapping const tokensWithBalance: Token[] = []; const eventTokensDetails: string[] = []; for (const nonZeroTokenAddress of Object.keys(balances)) { diff --git a/packages/profile-sync-controller/src/shared/encryption/cache.ts b/packages/profile-sync-controller/src/shared/encryption/cache.ts index 3b14925b131..6ac21fcba23 100644 --- a/packages/profile-sync-controller/src/shared/encryption/cache.ts +++ b/packages/profile-sync-controller/src/shared/encryption/cache.ts @@ -103,3 +103,10 @@ export function setCachedKey( cache.set(base64Salt, key); } + +/** + * foo + */ +export function clearCache() { + inMemCachedKDF = {}; +} diff --git a/packages/profile-sync-controller/src/shared/encryption/encryption.ts b/packages/profile-sync-controller/src/shared/encryption/encryption.ts index a490ca2459b..7f6d3ca0792 100644 --- a/packages/profile-sync-controller/src/shared/encryption/encryption.ts +++ b/packages/profile-sync-controller/src/shared/encryption/encryption.ts @@ -5,7 +5,12 @@ import { sha256 } from '@noble/hashes/sha256'; import { utf8ToBytes, concatBytes, bytesToHex } from '@noble/hashes/utils'; import type { NativeScrypt } from '../types/encryption'; -import { getAnyCachedKey, getCachedKeyBySalt, setCachedKey } from './cache'; +import { + getAnyCachedKey, + getCachedKeyBySalt, + setCachedKey, + clearCache, +} from './cache'; import { base64ToByteArray, byteArrayToBase64, bytesToUtf8 } from './utils'; export type EncryptedPayload = { @@ -247,3 +252,104 @@ export function createSHA256Hash(data: string): string { const hashedData = sha256(data); return bytesToHex(hashedData); } + +const getRandomEthereumAddress = () => { + const chars = '0123456789abcdef'; + let address = '0x'; + for (let i = 0; i < 40; i++) { + address += chars[Math.floor(Math.random() * chars.length)]; + } + return address; +}; + +const getRandomName = () => { + const names = [ + 'Quick', + 'Silent', + 'Brave', + 'Clever', + 'Mighty', + 'Swift', + 'Wise', + 'Bold', + 'Fierce', + 'Noble', + ]; + return names[Math.floor(Math.random() * names.length)]; +}; + +const createMockData = () => ({ + v: 1, + a: getRandomEthereumAddress(), + id: '3f29b2e4-8c3b-4d6a-9f1c-1a2b3c4d5e6f', + n: getRandomName(), + nlu: Date.now(), +}); + +// const mockData = JSON.stringify(createMockData()); +const mockDataArray = Array.from({ length: 1000 }, createMockData).map((d) => + JSON.stringify(d), +); + +/** + * runEncryptionPerfTestWithoutCache + * Will attempt encrypting 1, 10, 100, 1000 accounts. + * It will not use the cache for the first entry, and then will use the cached keys + * @param nativeScript - native scrypt + */ +export async function runEncryptionPerfTestWithoutCache( + nativeScript?: NativeScrypt, +) { + const password = 'test-password'; + + const testCases = [1, 10, 100, 1000]; + + for (const count of testCases) { + clearCache(); + const start = performance.now(); + for (let i = 0; i < count; i++) { + const plaintext = mockDataArray[i % mockDataArray.length]; + await encryption.encryptString(plaintext, password, nativeScript); + } + const end = performance.now(); + console.log( + `runEncryptionPerfTestWithoutCache: Time taken for ${count} accounts to encrypt: ${ + end - start + } ms`, + ); + } +} + +/** + * runEncryptionPerfTestWithCache + * Will attempt encrypting 1, 10, 100, 1000 accounts. + * This will use the cached keys if it exists + * @param nativeScript - native scrypt + */ +export async function runEncryptionPerfTestWithCache( + nativeScript?: NativeScrypt, +) { + const password = 'test-password'; + + const testCases = [1, 10, 100, 1000]; + + // Something to populate cache + await encryption.encryptString('warm up cache', password); + + for (const count of testCases) { + const start = performance.now(); + for (let i = 0; i < count; i++) { + const plaintext = mockDataArray[i % mockDataArray.length]; + await encryption.encryptString(plaintext, password, nativeScript); + } + const end = performance.now(); + console.log( + `runEncryptionPerfTestWithCache: Time taken for ${count} accounts to encrypt: ${ + end - start + } ms`, + ); + } +} + +// runEncryptionPerfTestWithoutCache(); +// runEncryptionPerfTestWithCache();