diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
index 6527894333..1ef5db9fa5 100644
--- a/.idea/codeStyles/Project.xml
+++ b/.idea/codeStyles/Project.xml
@@ -9,8 +9,6 @@
-
-
@@ -200,4 +198,4 @@
-
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
index ce1b62532a..5294908fc5 100644
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -15,6 +15,9 @@
+
+
+
@@ -85,8 +88,6 @@
-
-
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/impl/di/CustomTokenRouterModule.kt b/app/src/main/java/com/tangem/tap/features/customtoken/impl/di/CustomTokenRouterModule.kt
new file mode 100644
index 0000000000..0c8dec0948
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/features/customtoken/impl/di/CustomTokenRouterModule.kt
@@ -0,0 +1,21 @@
+package com.tangem.tap.features.customtoken.impl.di
+
+import com.tangem.tap.features.customtoken.impl.presentation.routers.CustomTokenRouter
+import com.tangem.tap.features.customtoken.impl.presentation.routers.DefaultCustomTokenRouter
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.android.components.ViewModelComponent
+import dagger.hilt.android.scopes.ViewModelScoped
+
+/**
+[REDACTED_AUTHOR]
+ */
+@Module
+@InstallIn(ViewModelComponent::class)
+internal object CustomTokenRouterModule {
+
+ @Provides
+ @ViewModelScoped
+ fun provideAddCustomTokenRouter(): CustomTokenRouter = DefaultCustomTokenRouter()
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/models/AddCustomTokenViewsModels.kt b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/models/AddCustomTokenViewsModels.kt
new file mode 100644
index 0000000000..65a253714a
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/models/AddCustomTokenViewsModels.kt
@@ -0,0 +1,269 @@
+package com.tangem.tap.features.customtoken.impl.presentation.models
+
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.ui.text.input.ImeAction
+import androidx.compose.ui.text.input.KeyboardType
+import com.tangem.tap.features.details.ui.cardsettings.TextReference
+import com.tangem.wallet.R
+
+/**
+ * Toolbar model of add custom token screen
+ *
+ * @property title title
+ * @property onBackButtonClick lambda be invoked when back button is been pressed
+ */
+internal data class AddCustomTokensToolbar(val title: TextReference, val onBackButtonClick: () -> Unit)
+
+/**
+ * Model of block with fields for testing
+ *
+ * @property chooseTokenButtonText choose token button text
+ * @property clearButtonText clear button text
+ * @property resetButtonText reset button text
+ * @property onClearAddressButtonClick lambda be invoked when clear address button is been pressed
+ * @property onResetButtonClick lambda be invoked when reset form fields button is been pressed
+ */
+internal data class AddCustomTokenTestBlock(
+ val chooseTokenButtonText: String,
+ val clearButtonText: String,
+ val resetButtonText: String,
+ val onClearAddressButtonClick: () -> Unit,
+ val onResetButtonClick: () -> Unit,
+)
+
+/**
+ * Bottom sheet model for choose custom token
+ *
+ * @property categoriesBlocks tokens categories
+ * @property onTestTokenClick lambda be invoked when token is been pressed
+ */
+internal data class AddCustomTokenChooseTokenBottomSheet(
+ val categoriesBlocks: List,
+ val onTestTokenClick: (String) -> Unit,
+) {
+
+ /**
+ * Tokens category model
+ *
+ * @property name category name
+ * @property items category items
+ */
+ data class TokensCategoryBlock(val name: String, val items: List)
+
+ /**
+ * Test token model
+ *
+ * @property name token name
+ * @property address token address
+ */
+ data class TestTokenItem(val name: String, val address: String)
+}
+
+/**
+ * Form with fields model of add custom token screen
+ *
+ * @property contractAddressInputField input field to enter the contract address
+ * @property networkSelectorField selector field to select the token network
+ * @property tokenNameInputField input field to enter the token name
+ * @property tokenSymbolInputField input field to enter the token symbol
+ * @property decimalsInputField input field to enter the token decimals
+ * @property derivationPathSelectorField selector field to select the derivation path
+ */
+internal data class AddCustomTokenForm(
+ val contractAddressInputField: AddCustomTokenInputField.ContactAddress,
+ val networkSelectorField: AddCustomTokenSelectorField.Network,
+ val tokenNameInputField: AddCustomTokenInputField.TokenName,
+ val tokenSymbolInputField: AddCustomTokenInputField.TokenSymbol,
+ val decimalsInputField: AddCustomTokenInputField.Decimals,
+ val derivationPathSelectorField: AddCustomTokenSelectorField.DerivationPath?,
+)
+
+/** Base input field model of add custom token screen */
+internal sealed interface AddCustomTokenInputField {
+
+ /** Current value */
+ val value: String
+
+ /** Lambda be invoked when value is been changed */
+ val onValueChange: (String) -> Unit
+
+ /** Keyboard options */
+ val keyboardOptions: KeyboardOptions
+
+ /** Label */
+ val label: TextReference
+
+ /** Input availability */
+ val isEnabled: Boolean
+
+ /** Flag that determine if current value has error */
+ val isError: Boolean
+
+ /** Placeholder (hint) */
+ val placeholder: TextReference
+
+ /** Flag that determine the processing of current value */
+ val isLoading: Boolean
+
+ /**
+ * Input field model to enter the contract address
+ *
+ * @property value current value
+ * @property onValueChange lambda be invoked when value is been changed
+ * @property isError flag that determine if current value has error
+ * @property isLoading flag that determine the processing of current value
+ */
+ data class ContactAddress(
+ override val value: String,
+ override val onValueChange: (String) -> Unit,
+ override val isError: Boolean,
+ override val isLoading: Boolean,
+ ) : AddCustomTokenInputField {
+ override val isEnabled = true
+ override val keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next)
+ override val label = TextReference.Res(R.string.custom_token_contract_address_input_title)
+ override val placeholder = TextReference.Str(value = "0x0000000000000000000000000000000000000000")
+ }
+
+ /**
+ * Input field model to enter the token name
+ *
+ * @property value current value
+ * @property onValueChange lambda be invoked when value is been changed
+ * @property isEnabled input availability
+ * @property isError flag that determine if current value has error
+ */
+ data class TokenName(
+ override val value: String,
+ override val onValueChange: (String) -> Unit,
+ override val isEnabled: Boolean,
+ override val isError: Boolean,
+ ) : AddCustomTokenInputField {
+ override val keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next)
+ override val label = TextReference.Res(R.string.custom_token_name_input_title)
+ override val placeholder = TextReference.Res(id = R.string.custom_token_name_input_placeholder)
+ override val isLoading = false
+ }
+
+ /**
+ * Input field model to enter the token symbol
+ *
+ * @property value current value
+ * @property onValueChange lambda be invoked when value is been changed
+ * @property isEnabled input availability
+ * @property isError flag that determine if current value has error
+ */
+ data class TokenSymbol(
+ override val value: String,
+ override val onValueChange: (String) -> Unit,
+ override val isEnabled: Boolean,
+ override val isError: Boolean,
+ ) : AddCustomTokenInputField {
+ override val keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next)
+ override val label = TextReference.Res(R.string.custom_token_token_symbol_input_title)
+ override val placeholder = TextReference.Res(id = R.string.custom_token_token_symbol_input_placeholder)
+ override val isLoading = false
+ }
+
+ /**
+ * Input field model to enter the token decimals
+ *
+ * @property value current value
+ * @property onValueChange lambda be invoked when value is been changed
+ * @property isEnabled input availability
+ * @property isError flag that determine if current value has error
+ */
+ data class Decimals(
+ override val value: String,
+ override val onValueChange: (String) -> Unit,
+ override val isEnabled: Boolean,
+ override val isError: Boolean,
+ ) : AddCustomTokenInputField {
+ override val keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Next)
+ override val label = TextReference.Res(R.string.custom_token_decimals_input_title)
+ override val placeholder = TextReference.Str(value = "8")
+ override val isLoading = false
+ }
+}
+
+/** Base selector field model of add custom token screen */
+internal sealed interface AddCustomTokenSelectorField {
+
+ /** Selection availability */
+ val isEnabled: Boolean
+
+ /** Label string resource id */
+ val label: TextReference
+
+ /** Selected menu item */
+ val selectedItem: SelectorItem
+
+ /** Menu items */
+ val items: List
+
+ /** Lambda be invoked when menu item is been selected */
+ val onMenuItemClick: (Int) -> Unit
+
+ /**
+ * Network selector model
+ *
+ * @property selectedItem selected menu item
+ * @property items menu items
+ * @property onMenuItemClick lambda be invoked when menu item is been selected
+ */
+ data class Network(
+ override val selectedItem: SelectorItem.Title,
+ override val items: List,
+ override val onMenuItemClick: (Int) -> Unit,
+ ) : AddCustomTokenSelectorField {
+ override val isEnabled = true
+ override val label = TextReference.Res(R.string.custom_token_network_input_title)
+ }
+
+ /**
+ * Derivation path selector model
+ *
+ * @property isEnabled selection availability
+ * @property selectedItem selected menu item
+ * @property items menu items
+ * @property onMenuItemClick lambda be invoked when menu item is been selected
+ */
+ data class DerivationPath(
+ override val isEnabled: Boolean,
+ override val selectedItem: SelectorItem.TitleWithSubtitle,
+ override val items: List,
+ override val onMenuItemClick: (Int) -> Unit,
+ ) : AddCustomTokenSelectorField {
+ override val label = TextReference.Res(R.string.custom_token_derivation_path_input_title)
+ }
+
+ /** Base menu item model */
+ sealed interface SelectorItem {
+
+ /** Title */
+ val title: TextReference
+
+ /**
+ * Menu item with title
+ *
+ * @property title title text
+ */
+ data class Title(override val title: TextReference) : SelectorItem
+
+ /**
+ * Menu item with title ans subtitle
+ *
+ * @property title title text
+ * @property subtitle subtitle text
+ */
+ data class TitleWithSubtitle(override val title: TextReference, val subtitle: TextReference) : SelectorItem
+ }
+}
+
+/**
+ * Floating button of add custom token screen
+ *
+ * @property isEnabled button availability
+ * @property onClick lambda be invoked when button is been pressed
+ */
+internal data class AddCustomTokenFloatingButton(val isEnabled: Boolean, val onClick: () -> Unit)
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/routers/CustomTokenRouter.kt b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/routers/CustomTokenRouter.kt
new file mode 100644
index 0000000000..933bda4bc7
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/routers/CustomTokenRouter.kt
@@ -0,0 +1,12 @@
+package com.tangem.tap.features.customtoken.impl.presentation.routers
+
+/**
+ * Custom token feature router
+ *
+[REDACTED_AUTHOR]
+ */
+internal interface CustomTokenRouter {
+
+ /** Return to last screen */
+ fun popBackStack()
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/routers/DefaultCustomTokenRouter.kt b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/routers/DefaultCustomTokenRouter.kt
new file mode 100644
index 0000000000..9460879f92
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/routers/DefaultCustomTokenRouter.kt
@@ -0,0 +1,12 @@
+package com.tangem.tap.features.customtoken.impl.presentation.routers
+
+import com.tangem.tap.common.redux.navigation.NavigationAction
+import com.tangem.tap.store
+
+/** Default implementation of custom token feature router */
+internal class DefaultCustomTokenRouter : CustomTokenRouter {
+
+ override fun popBackStack() {
+ store.dispatch(NavigationAction.PopBackTo())
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/states/AddCustomTokenStateHolder.kt b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/states/AddCustomTokenStateHolder.kt
new file mode 100644
index 0000000000..17b0f92c9c
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/states/AddCustomTokenStateHolder.kt
@@ -0,0 +1,91 @@
+package com.tangem.tap.features.customtoken.impl.presentation.states
+
+import com.tangem.tap.features.customtoken.impl.presentation.models.AddCustomTokenChooseTokenBottomSheet
+import com.tangem.tap.features.customtoken.impl.presentation.models.AddCustomTokenFloatingButton
+import com.tangem.tap.features.customtoken.impl.presentation.models.AddCustomTokenForm
+import com.tangem.tap.features.customtoken.impl.presentation.models.AddCustomTokenTestBlock
+import com.tangem.tap.features.customtoken.impl.presentation.models.AddCustomTokensToolbar
+import com.tangem.tap.features.details.ui.cardsettings.TextReference
+
+/**
+ * State holder of add custom token screen
+ *
+[REDACTED_AUTHOR]
+ */
+internal sealed interface AddCustomTokenStateHolder {
+
+ /** Lambda be invoked when system back action is been called */
+ val onBackButtonClick: () -> Unit
+
+ /** Toolbar model */
+ val toolbar: AddCustomTokensToolbar
+
+ /** Form model */
+ val form: AddCustomTokenForm
+
+ /** Warnings */
+ val warnings: List
+
+ /** Floating button model */
+ val floatingButton: AddCustomTokenFloatingButton
+
+ /**
+ * Util function that allow to make a copy
+ *
+ * @param onBackButtonClick lambda be invoked when system back action is been called
+ * @param toolbar toolbar model
+ * @param form form model
+ * @param warnings warnings
+ * @param floatingButton floating button model
+ */
+ fun copySealed(
+ onBackButtonClick: () -> Unit = this.onBackButtonClick,
+ toolbar: AddCustomTokensToolbar = this.toolbar,
+ form: AddCustomTokenForm = this.form,
+ warnings: List = this.warnings,
+ floatingButton: AddCustomTokenFloatingButton = this.floatingButton,
+ ): AddCustomTokenStateHolder {
+ return when (this) {
+ is Content -> copy(onBackButtonClick, toolbar, form, warnings, floatingButton)
+ is TestContent -> copy(onBackButtonClick, toolbar, form, warnings, floatingButton)
+ }
+ }
+
+ /**
+ * Content state
+ *
+ * @property onBackButtonClick lambda be invoked when system back action is been called
+ * @property toolbar toolbar model
+ * @property form form model
+ * @property warnings warnings
+ * @property floatingButton floating button model
+ */
+ data class Content(
+ override val onBackButtonClick: () -> Unit,
+ override val toolbar: AddCustomTokensToolbar,
+ override val form: AddCustomTokenForm,
+ override val warnings: List,
+ override val floatingButton: AddCustomTokenFloatingButton,
+ ) : AddCustomTokenStateHolder
+
+ /**
+ * Content state with fields for testing
+ *
+ * @property onBackButtonClick lambda be invoked when system back action is been called
+ * @property toolbar toolbar model
+ * @property form form model
+ * @property warnings warnings
+ * @property floatingButton floating button model
+ * @property testBlock test block model
+ * @property bottomSheet bottom sheet model
+ */
+ data class TestContent(
+ override val onBackButtonClick: () -> Unit,
+ override val toolbar: AddCustomTokensToolbar,
+ override val form: AddCustomTokenForm,
+ override val warnings: List,
+ override val floatingButton: AddCustomTokenFloatingButton,
+ val testBlock: AddCustomTokenTestBlock,
+ val bottomSheet: AddCustomTokenChooseTokenBottomSheet,
+ ) : AddCustomTokenStateHolder
+}
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
index e3a622a5ef..6f02666fb4 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -6,7 +6,7 @@
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
-org.gradle.jvmargs = -Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
+org.gradle.jvmargs = -Xmx4096m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml
index 5d83697c53..b5543ac9e5 100644
--- a/gradle/dependencies.toml
+++ b/gradle/dependencies.toml
@@ -24,7 +24,7 @@ androidx-paging = "3.1.1"
compose-compiler = "1.4.3"
compose-runtime = "1.3.3"
compose-foundation = "1.3.1"
-compose-material = "1.3.1"
+compose-material = "1.4.2"
compose-constraint = "1.0.1"
compose-navigation = "2.5.3"
compose-accompanist = "0.28.0"