(
+ defaultOptions: Required // defaultOptions is an object with default options
+): {
+ (getValues: (options: P) => V): {
+ // If some fields returned by getValues are overridden
+ >(overrides: O): Omit> & Omit
+ // If no fields are overridden
+ (): V
+ }
+}
+
+// If there are custom options with default values getter function
+export function createFixture(
+ getDefaultOptions: () => Required // getDefaultOptions is a function that returns an object with default options
+): {
+ (getValues: (options: P) => V): {
+ // If some fields returned by getValues are overridden
+ >(overrides: O): Omit> & Omit
+ // If no fields are overridden
+ (): V
+ }
+}
+
+export function createFixture(
+ defaultOptionsOrGetter?: Required | (() => Required
)
+) {
+ return (getValues: (options?: P) => V) => {
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
+ return | Partial>(overrides?: O) => {
+ // Get default options (if they exist)
+ const defaultOptions =
+ typeof defaultOptionsOrGetter === 'function'
+ ? defaultOptionsOrGetter()
+ : defaultOptionsOrGetter
+ // Get overrides for options
+ const optionOverrides = defaultOptions ? pick(overrides, Object.keys(defaultOptions)) : {}
+ // Get values with getValues function
+ const mergedOptions = defaultOptions ? { ...defaultOptions, ...optionOverrides } : undefined
+ const values = getValues(mergedOptions)
+ // Get overrides for values
+ const valueOverrides = overrides ? omit(overrides, Object.keys(defaultOptions || {})) : {}
+ return Array.isArray(values) ? values : { ...values, ...valueOverrides }
+ }
+ }
+}
diff --git a/packages/wallet/src/test/utils/index.ts b/packages/wallet/src/test/utils/index.ts
new file mode 100644
index 00000000000..c6872e3a265
--- /dev/null
+++ b/packages/wallet/src/test/utils/index.ts
@@ -0,0 +1,3 @@
+export * from './array'
+export * from './factory'
+export * from './random'
diff --git a/packages/wallet/src/test/utils/random.ts b/packages/wallet/src/test/utils/random.ts
new file mode 100644
index 00000000000..9f9bdeea742
--- /dev/null
+++ b/packages/wallet/src/test/utils/random.ts
@@ -0,0 +1,40 @@
+/**
+ * Returns a random value from the given enum.
+ *
+ * @param enumObj The enum object from which a random value is to be selected. This object should be a TypeScript enum
+ * where the enum values are of type string.
+ * @returns A random value from the specified enum.
+ *
+ * @example
+ * ```typescript
+ * enum Colors {
+ * Red = 'RED',
+ * Green = 'GREEN',
+ * Blue = 'BLUE'
+ * }
+ *
+ * const randomColor = randomEnumValue(Colors);
+ * console.log(randomColor); // Outputs: 'RED', 'GREEN', or 'BLUE' (randomly selected)
+ * ```
+ *
+ * @typeparam T Type of the enum object (will be automatically inferred from the provided argument).
+ */
+export const randomEnumValue = >(
+ enumObj: T
+): T[keyof T] => {
+ // If enum has different types for keys and values (keys are always strings,
+ // values can be strings or numbers), we need to filter out the keys
+ const keys = Object.keys(enumObj).filter((key) => isNaN(Number(key)))
+ const randomKey = randomChoice(keys)
+ return enumObj[randomKey] as T[keyof T]
+}
+
+/**
+ * Returns a random value from the array of choices.
+ *
+ * @returns A random value from the specified array.
+ */
+export const randomChoice = (choices: T[]): T => {
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ return choices[Math.floor(Math.random() * choices.length)]!
+}
diff --git a/packages/wallet/src/utils/balance.test.ts b/packages/wallet/src/utils/balance.test.ts
index 9fe8895d9a1..da34a0e9cbd 100644
--- a/packages/wallet/src/utils/balance.test.ts
+++ b/packages/wallet/src/utils/balance.test.ts
@@ -1,7 +1,12 @@
import { CurrencyAmount } from '@uniswap/sdk-core'
import JSBI from 'jsbi'
import { DAI } from 'wallet/src/constants/tokens'
-import { ArbitrumEth, MainnetEth, OptimismEth, PolygonMatic } from 'wallet/src/test/fixtures'
+import {
+ ARBITRUM_CURRENCY,
+ MAINNET_CURRENCY,
+ OPTIMISM_CURRENCY,
+ POLYGON_CURRENCY,
+} from 'wallet/src/test/fixtures'
import {
MIN_ARBITRUM_FOR_GAS,
MIN_ETH_FOR_GAS,
@@ -25,7 +30,7 @@ describe(maxAmountSpend, () => {
it('reserves gas for large amounts on ETH Mainnet', () => {
const amount = CurrencyAmount.fromRawAmount(
- MainnetEth,
+ MAINNET_CURRENCY,
JSBI.add(JSBI.BigInt(99), JSBI.BigInt(MIN_ETH_FOR_GAS))
)
const amount1Spend = maxAmountSpend(amount)
@@ -34,7 +39,7 @@ describe(maxAmountSpend, () => {
it('handles small amounts on ETH Mainnet', () => {
const amount = CurrencyAmount.fromRawAmount(
- MainnetEth,
+ MAINNET_CURRENCY,
JSBI.subtract(JSBI.BigInt(99), JSBI.BigInt(MIN_ETH_FOR_GAS))
)
const amount1Spend = maxAmountSpend(amount)
@@ -45,7 +50,7 @@ describe(maxAmountSpend, () => {
it('reserves gas for large amounts on Polygon', () => {
const amount = CurrencyAmount.fromRawAmount(
- PolygonMatic,
+ POLYGON_CURRENCY,
JSBI.add(JSBI.BigInt(99), JSBI.BigInt(MIN_POLYGON_FOR_GAS))
)
const amount1Spend = maxAmountSpend(amount)
@@ -54,7 +59,7 @@ describe(maxAmountSpend, () => {
it('handles small amounts on Polygon', () => {
const amount = CurrencyAmount.fromRawAmount(
- PolygonMatic,
+ POLYGON_CURRENCY,
JSBI.subtract(JSBI.BigInt(99), JSBI.BigInt(MIN_POLYGON_FOR_GAS))
)
const amount1Spend = maxAmountSpend(amount)
@@ -65,7 +70,7 @@ describe(maxAmountSpend, () => {
it('reserves gas for large amounts on Arbitrum', () => {
const amount = CurrencyAmount.fromRawAmount(
- ArbitrumEth,
+ ARBITRUM_CURRENCY,
JSBI.add(JSBI.BigInt(99), JSBI.BigInt(MIN_ARBITRUM_FOR_GAS))
)
const amount1Spend = maxAmountSpend(amount)
@@ -74,7 +79,7 @@ describe(maxAmountSpend, () => {
it('handles small amounts on Arbitrum', () => {
const amount = CurrencyAmount.fromRawAmount(
- ArbitrumEth,
+ ARBITRUM_CURRENCY,
JSBI.subtract(JSBI.BigInt(99), JSBI.BigInt(MIN_ARBITRUM_FOR_GAS))
)
const amount1Spend = maxAmountSpend(amount)
@@ -85,7 +90,7 @@ describe(maxAmountSpend, () => {
it('reserves gas for large amounts on Optimism', () => {
const amount = CurrencyAmount.fromRawAmount(
- OptimismEth,
+ OPTIMISM_CURRENCY,
JSBI.add(JSBI.BigInt(99), JSBI.BigInt(MIN_OPTIMISM_FOR_GAS))
)
const amount1Spend = maxAmountSpend(amount)
@@ -94,7 +99,7 @@ describe(maxAmountSpend, () => {
it('handles small amounts on Optimism', () => {
const amount = CurrencyAmount.fromRawAmount(
- OptimismEth,
+ OPTIMISM_CURRENCY,
JSBI.subtract(JSBI.BigInt(99), JSBI.BigInt(MIN_OPTIMISM_FOR_GAS))
)
const amount1Spend = maxAmountSpend(amount)
diff --git a/packages/wallet/src/utils/currency.test.ts b/packages/wallet/src/utils/currency.test.ts
index 1253bc86f72..c0c428ff700 100644
--- a/packages/wallet/src/utils/currency.test.ts
+++ b/packages/wallet/src/utils/currency.test.ts
@@ -1,5 +1,5 @@
import { DAI, USDC } from 'wallet/src/constants/tokens'
-import { mockLocalizedFormatter } from 'wallet/src/test/utils'
+import { mockLocalizedFormatter } from 'wallet/src/test/mocks/utils'
import { getCurrencyDisplayText, getFormattedCurrencyAmount } from './currency'
describe(getFormattedCurrencyAmount, () => {
diff --git a/packages/wallet/src/utils/duration.ts b/packages/wallet/src/utils/duration.ts
new file mode 100644
index 00000000000..a04ae999790
--- /dev/null
+++ b/packages/wallet/src/utils/duration.ts
@@ -0,0 +1,29 @@
+import { getDurationRemaining } from 'utilities/src/time/duration'
+import i18n from 'wallet/src/i18n/i18n'
+
+export function getOtpDurationString(expirationTime: number): string {
+ const timeLeft = expirationTime - Date.now()
+ if (timeLeft <= 0) {
+ return i18n.t('scantastic.code.expired')
+ }
+
+ const { seconds, minutes, hours } = getDurationRemaining(expirationTime)
+
+ if (minutes) {
+ if (hours) {
+ return i18n.t('scantastic.code.timeRemaining.shorthand.hours', {
+ seconds,
+ minutes,
+ hours,
+ })
+ }
+
+ return i18n.t('scantastic.code.timeRemaining.shorthand.minutes', {
+ seconds,
+ minutes,
+ })
+ }
+ return i18n.t('scantastic.code.timeRemaining.shorthand.seconds', {
+ seconds,
+ })
+}
diff --git a/packages/wallet/src/utils/mnemonics.test.ts b/packages/wallet/src/utils/mnemonics.test.ts
index ded5735b1ad..85a213baa2d 100644
--- a/packages/wallet/src/utils/mnemonics.test.ts
+++ b/packages/wallet/src/utils/mnemonics.test.ts
@@ -1,26 +1,26 @@
+import i18n from 'wallet/src/i18n/i18n'
import { MnemonicValidationError, translateMnemonicErrorMessage } from 'wallet/src/utils/mnemonics'
describe(translateMnemonicErrorMessage, () => {
- const t = (str: string): string => str
-
it('correct invalid phrase message', () => {
- expect(translateMnemonicErrorMessage(MnemonicValidationError.InvalidPhrase, undefined, t)).toBe(
- 'Invalid phrase'
- )
+ expect(
+ translateMnemonicErrorMessage(MnemonicValidationError.InvalidPhrase, undefined, i18n.t)
+ ).toBe('Invalid phrase')
})
it('correct invalid word message', () => {
- expect(translateMnemonicErrorMessage(MnemonicValidationError.InvalidWord, 't', t)).toBe(
- 'Invalid word: {{word}}'
- )
+ const invalidWord = 'gibberish'
+ expect(
+ translateMnemonicErrorMessage(MnemonicValidationError.InvalidWord, invalidWord, i18n.t)
+ ).toBe(`Invalid word: ${invalidWord}`)
})
it('correct incorrect number of words message', () => {
- expect(translateMnemonicErrorMessage(MnemonicValidationError.TooManyWords, undefined, t)).toBe(
- 'Recovery phrase must be 12-24 words'
- )
expect(
- translateMnemonicErrorMessage(MnemonicValidationError.NotEnoughWords, undefined, t)
+ translateMnemonicErrorMessage(MnemonicValidationError.TooManyWords, undefined, i18n.t)
+ ).toBe('Recovery phrase must be 12-24 words')
+ expect(
+ translateMnemonicErrorMessage(MnemonicValidationError.NotEnoughWords, undefined, i18n.t)
).toBe('Recovery phrase must be 12-24 words')
})
})
diff --git a/packages/wallet/src/utils/mnemonics.ts b/packages/wallet/src/utils/mnemonics.ts
index ba9a16ae49b..ce015323613 100644
--- a/packages/wallet/src/utils/mnemonics.ts
+++ b/packages/wallet/src/utils/mnemonics.ts
@@ -17,12 +17,12 @@ export function translateMnemonicErrorMessage(
): string {
switch (error) {
case MnemonicValidationError.InvalidPhrase:
- return t('Invalid phrase')
+ return t('account.seedPhrase.error.invalid')
case MnemonicValidationError.InvalidWord:
- return t('Invalid word: {{word}}', { word: invalidWord })
+ return t('account.seedPhrase.error.invalidWord', { word: invalidWord })
case MnemonicValidationError.TooManyWords:
case MnemonicValidationError.NotEnoughWords:
- return t('Recovery phrase must be 12-24 words')
+ return t('account.seedPhrase.error.phraseLength')
default:
throw new Error(`Unhandled MnemonicValidationError case: ${error}`)
}
diff --git a/packages/wallet/src/utils/password.test.ts b/packages/wallet/src/utils/password.test.ts
index 34fc14ffa25..fef2b741e59 100644
--- a/packages/wallet/src/utils/password.test.ts
+++ b/packages/wallet/src/utils/password.test.ts
@@ -5,12 +5,6 @@ import {
isPasswordStrongEnough,
} from './password'
-jest.mock('i18next', () => {
- return {
- t: (key: string): string => key,
- }
-})
-
describe(isPasswordStrongEnough, () => {
it('returns true for equal strengths', () => {
expect(
diff --git a/packages/wallet/src/utils/password.ts b/packages/wallet/src/utils/password.ts
index 76be8052a5c..4445dae4f9a 100644
--- a/packages/wallet/src/utils/password.ts
+++ b/packages/wallet/src/utils/password.ts
@@ -43,11 +43,14 @@ export function getPasswordStrengthTextAndColor(strength: PasswordStrength): {
} {
switch (strength) {
case PasswordStrength.WEAK:
- return { text: t('Weak'), color: '$statusCritical' }
+ return { text: t('common.input.password.strength.weak'), color: '$statusCritical' }
case PasswordStrength.MEDIUM:
- return { text: t('Medium'), color: '$DEP_accentWarning' }
+ return {
+ text: t('common.input.password.strength.medium'),
+ color: '$DEP_accentWarning',
+ }
case PasswordStrength.STRONG:
- return { text: t('Strong'), color: '$statusSuccess' }
+ return { text: t('common.input.password.strength.strong'), color: '$statusSuccess' }
default:
return { text: '', color: '$neutral1' }
}
diff --git a/packages/wallet/src/utils/platform/index.native.ts b/packages/wallet/src/utils/platform/index.native.ts
index b9cd675acd0..d4a5f2bcc74 100644
--- a/packages/wallet/src/utils/platform/index.native.ts
+++ b/packages/wallet/src/utils/platform/index.native.ts
@@ -4,3 +4,14 @@ export const isMobile = true
export const isIOS = Platform.OS === 'ios'
export const isAndroid = Platform.OS === 'android'
export const isNonSupportedDevice = !isIOS && !isAndroid
+
+export function getCloudProviderName(): string {
+ switch (Platform.OS) {
+ case 'android':
+ return 'Google Drive'
+ case 'ios':
+ return 'iCloud'
+ default:
+ return ''
+ }
+}
diff --git a/packages/wallet/src/utils/platform/index.ts b/packages/wallet/src/utils/platform/index.ts
index 079e52cf52d..c515fd44212 100644
--- a/packages/wallet/src/utils/platform/index.ts
+++ b/packages/wallet/src/utils/platform/index.ts
@@ -10,3 +10,14 @@ export const isIOS = platform === 'iOS'
export const isAndroid = platform === 'Android'
export const isNonSupportedDevice = !isIOS && !isAndroid && type === 'mobile'
export const isMobileSafari = isMobile && isIOS && name?.toLowerCase().includes('safari')
+
+export function getCloudProviderName(): string {
+ switch (platform) {
+ case 'Android':
+ return 'Google Drive'
+ case 'iOS':
+ return 'iCloud'
+ default:
+ return ''
+ }
+}
diff --git a/packages/wallet/tsconfig.json b/packages/wallet/tsconfig.json
index 4ad23921347..4c279287ded 100644
--- a/packages/wallet/tsconfig.json
+++ b/packages/wallet/tsconfig.json
@@ -10,6 +10,9 @@
{
"path": "../ui"
},
+ {
+ "path": "../uniswap"
+ },
{
"path": "../utilities"
}
diff --git a/yarn.lock b/yarn.lock
index 0474b6e9524..fc48a5fc684 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -12677,15 +12677,6 @@ __metadata:
languageName: node
linkType: hard
-"@types/react-helmet@npm:6.1.7":
- version: 6.1.7
- resolution: "@types/react-helmet@npm:6.1.7"
- dependencies:
- "@types/react": "*"
- checksum: d5ee8343f10f8746a022249c6401493baa73241a90b5002b59e8a6ce31650b9e1ab38d398eba5564af026f68eda107c8fcdca01fc046a163a96dc422710f6a77
- languageName: node
- linkType: hard
-
"@types/react-native@npm:0.71.3":
version: 0.71.3
resolution: "@types/react-native@npm:0.71.3"
@@ -13621,7 +13612,6 @@ __metadata:
"@types/qs": 6.9.2
"@types/react": ^18.0.15
"@types/react-dom": ^18.0.6
- "@types/react-helmet": 6.1.7
"@types/react-redux": 7.1.30
"@types/react-scroll-sync": 0.8.7
"@types/react-table": 7.7.12
@@ -13739,7 +13729,7 @@ __metadata:
react: 18.2.0
react-dom: 18.2.0
react-feather: 2.0.10
- react-helmet: 6.1.0
+ react-helmet-async: 2.0.4
react-infinite-scroll-component: 6.1.0
react-is: 18.2.0
react-markdown: 4.3.1
@@ -13771,6 +13761,7 @@ __metadata:
ts-jest: ^29.1.1
tsafe: 1.6.4
typescript: 5.3.3
+ uniswap: "workspace:^"
use-resize-observer: 9.1.0
utilities: "workspace:^"
uuid: 9.0.0
@@ -13967,6 +13958,7 @@ __metadata:
statsig-react-native: 4.11.0
typed-redux-saga: 1.5.0
typescript: 5.3.3
+ uniswap: "workspace:^"
utilities: "workspace:^"
wallet: "workspace:^"
yarn-deduplicate: 6.0.0
@@ -37736,7 +37728,7 @@ __metadata:
languageName: node
linkType: hard
-"react-fast-compare@npm:^3.0.1, react-fast-compare@npm:^3.1.1":
+"react-fast-compare@npm:^3.0.1, react-fast-compare@npm:^3.2.2":
version: 3.2.2
resolution: "react-fast-compare@npm:3.2.2"
checksum: 2071415b4f76a3e6b55c84611c4d24dcb12ffc85811a2840b5a3f1ff2d1a99be1020d9437ee7c6e024c9f4cbb84ceb35e48cf84f28fcb00265ad2dfdd3947704
@@ -37783,17 +37775,17 @@ __metadata:
languageName: node
linkType: hard
-"react-helmet@npm:6.1.0":
- version: 6.1.0
- resolution: "react-helmet@npm:6.1.0"
+"react-helmet-async@npm:2.0.4":
+ version: 2.0.4
+ resolution: "react-helmet-async@npm:2.0.4"
dependencies:
- object-assign: ^4.1.1
- prop-types: ^15.7.2
- react-fast-compare: ^3.1.1
- react-side-effect: ^2.1.0
+ invariant: ^2.2.4
+ react-fast-compare: ^3.2.2
+ shallowequal: ^1.1.0
peerDependencies:
- react: ">=16.3.0"
- checksum: a4998479dab7fc1c2799eddefb1870a9d881b5f71cfdf97979a9882e42f4bb50402d55335f308f461e735e01a06f46b16cc7b4e6bcb22c7a4a6f85a753c5c106
+ react: ^16.6.0 || ^17.0.0 || ^18.0.0
+ react-dom: ^16.6.0 || ^17.0.0 || ^18.0.0
+ checksum: 1bd16e6be6d15cf3d4b4c0853d1e122941a05d3fb2bad1fb1c5037069c5f142fcab063c342b95c58a998a81f093bdf1bd1bb00852a5a3a84d49e48790d5142bb
languageName: node
linkType: hard
@@ -38780,15 +38772,6 @@ __metadata:
languageName: node
linkType: hard
-"react-side-effect@npm:^2.1.0":
- version: 2.1.2
- resolution: "react-side-effect@npm:2.1.2"
- peerDependencies:
- react: ^16.3.0 || ^17.0.0 || ^18.0.0
- checksum: c5eb1f42b464fb093bca59aaae0f1b2060373a2aaff95275b8781493628cdbbb6acdd6014e7883782c65c361f35a30f28cc515d68a1263ddb39cbbc47110be53
- languageName: node
- linkType: hard
-
"react-spring@npm:9.7.3":
version: 9.7.3
resolution: "react-spring@npm:9.7.3"
@@ -44374,6 +44357,22 @@ __metadata:
languageName: node
linkType: hard
+"uniswap@workspace:^, uniswap@workspace:packages/uniswap":
+ version: 0.0.0-use.local
+ resolution: "uniswap@workspace:packages/uniswap"
+ dependencies:
+ "@typechain/ethers-v5": 7.2.0
+ "@uniswap/eslint-config": "workspace:^"
+ depcheck: 1.4.7
+ eslint: 8.44.0
+ ethers: 5.7.2
+ jest: 29.6.4
+ jest-presets: "workspace:^"
+ typechain: 5.2.0
+ typescript: 5.3.3
+ languageName: unknown
+ linkType: soft
+
"universal-user-agent@npm:^6.0.0":
version: 6.0.0
resolution: "universal-user-agent@npm:6.0.0"
@@ -45404,7 +45403,6 @@ __metadata:
"@testing-library/jest-native": 5.4.2
"@testing-library/react-hooks": 7.0.2
"@testing-library/react-native": 11.5.0
- "@typechain/ethers-v5": 7.2.0
"@types/react": ^18.0.15
"@types/react-window": 1.8.2
"@types/ua-parser-js": 0.7.31
@@ -45459,11 +45457,11 @@ __metadata:
redux-saga: 1.2.2
redux-saga-test-plan: 4.0.4
statsig-react-native: 4.11.0
- typechain: 5.2.0
typed-redux-saga: 1.5.0
typescript: 5.3.3
ua-parser-js: 1.0.37
ui: "workspace:^"
+ uniswap: "workspace:^"
utilities: "workspace:^"
uuid: 9.0.0
wcag-contrast: 3.0.0