Repository: eth-educators/ethstaker-deposit-cli

Issue:
Add support for EIP 7251 in preparation for Pectra
[EIP-7251: Increase the MAX_EFFECTIVE_BALANCE](https://eips.ethereum.org/EIPS/eip-7251) adds a new withdrawal credentials `0x02` for a compounding validator with 2048 ETH as its max effective balance. We should add support for this in the `new-mnemonic`, `existing-mnemonic` and `partial-deposit` commands.

EIP-7251 is expected to land with the Pectra fork.

Discussion:
@remyroy: I started some work on this in https://github.com/remyroy/ethstaker-deposit-cli/tree/compounding-creds

Solution patch:
diff --git a/docs/src/existing_mnemonic.md b/docs/src/existing_mnemonic.md
index 84ebfbb5..ddf2222f 100644
--- a/docs/src/existing_mnemonic.md
+++ b/docs/src/existing_mnemonic.md
@@ -23,6 +23,10 @@ Uses an existing BIP-39 mnemonic phrase for key generation.
 
 - **`--withdrawal_address`**: The Ethereum address that will be used in withdrawal. It typically starts with '0x' followed by 40 hexadecimal characters. Please make sure you have full control over the address you choose here. Once you set a withdrawal address on chain, it cannot be changed.
 
+- **`--compounding / --regular-withdrawal`**: Generates compounding validators with 0x02 withdrawal credentials for a 2048 ETH maximum effective balance or generate regular validators with 0x01 withdrawal credentials for a 32 ETH maximum effective balance. Use of this option requires a withdrawal address. This feature is only supported on networks that have undergone the Pectra fork. Defaults to regular withdrawal.
+
+- **`--amount`**: The amount to deposit to these validators in ether denomination. Must be at least 1 ether and can not have greater precision than 1 gwei. Use of this option requires compounding validators. Defaults to 32 ether.
+
 - **`--pbkdf2`**: Will use pbkdf2 key derivation instead of scrypt for generated keystore files as defined in [EIP-2335](https://eips.ethereum.org/EIPS/eip-2335#decryption-key). This can be a good alternative if you intend to work with a large number of keys, as it can improve performance however it is less secure. You should only use this option if you understand the associated risks and have familiarity with encryption.
 
 - **`--folder`**: The folder where keystore and deposit data files will be saved.
diff --git a/docs/src/new_mnemonic.md b/docs/src/new_mnemonic.md
index 1dd05903..702be708 100644
--- a/docs/src/new_mnemonic.md
+++ b/docs/src/new_mnemonic.md
@@ -17,6 +17,10 @@ Generates a new BIP-39 mnemonic along with validator keystore and deposit files
 
 - **`--withdrawal_address`**: The Ethereum address that will be used in withdrawal. It typically starts with '0x' followed by 40 hexadecimal characters. Please make sure you have full control over the address you choose here. Once you set a withdrawal address on chain, it cannot be changed.
 
+- **`--compounding / --regular-withdrawal`**: Generates compounding validators with 0x02 withdrawal credentials for a 2048 ETH maximum effective balance or generate regular validators with 0x01 withdrawal credentials for a 32 ETH maximum effective balance. Use of this option requires a withdrawal address. This feature is only supported on networks that have undergone the Pectra fork. Defaults to regular withdrawal.
+
+- **`--amount`**: The amount to deposit to these validators in ether denomination. Must be at least 1 ether and can not have greater precision than 1 gwei. Use of this option requires compounding validators. Defaults to 32 ether.
+
 - **`--pbkdf2`**: Will use pbkdf2 key encryption instead of scrypt for generated keystore files as defined in [EIP-2335](https://eips.ethereum.org/EIPS/eip-2335#decryption-key). This can be a good alternative if you intend to work with a large number of keys, as it can improve performance. pbkdf2 encryption is, however, less secure than scrypt. You should only use this option if you understand the associated risks and have familiarity with encryption.
 
 - **`--folder`**: The folder where keystore and deposit data files will be saved.
diff --git a/docs/src/partial_deposit.md b/docs/src/partial_deposit.md
index 0741b4d5..67797a98 100644
--- a/docs/src/partial_deposit.md
+++ b/docs/src/partial_deposit.md
@@ -18,6 +18,8 @@ If you wish to create a validator with 0x00 credentials, you must use the **[new
 
 - **`--withdrawal_address`**: The withdrawal address of the existing validator or the desired withdrawal address.
 
+- **`--compounding / --regular-withdrawal`**: Generates compounding validators with 0x02 withdrawal credentials for a 2048 ETH maximum effective balance or generate regular validators with 0x01 withdrawal credentials for a 32 ETH maximum effective balance. Use of this option requires a withdrawal address. This feature is only supported on networks that have undergone the Pectra fork. Defaults to regular withdrawal.
+
 - **`--output_folder`**: The folder path for the `deposit-*` JSON file.
 
 - **`--devnet_chain_setting`**: The custom chain setting of a devnet or testnet. Note that it will override your `--chain` choice. This should be a JSON string containing an object with the following keys: network_name, genesis_fork_version, exit_fork_version and genesis_validator_root.
diff --git a/ethstaker_deposit/cli/existing_mnemonic.py b/ethstaker_deposit/cli/existing_mnemonic.py
index 8c3b53ed..49f108ab 100644
--- a/ethstaker_deposit/cli/existing_mnemonic.py
+++ b/ethstaker_deposit/cli/existing_mnemonic.py
@@ -19,6 +19,7 @@
     captive_prompt_callback,
     choice_prompt_func,
     jit_option,
+    prompt_if_none,
 )
 from ethstaker_deposit.utils.intl import fuzzy_reverse_dict_lookup, get_first_options, load_text
 from ethstaker_deposit.utils.validation import validate_int_range
@@ -39,7 +40,7 @@ def load_mnemonic_arguments_decorator(function: Callable[..., Any]) -> Callable[
                 captive_prompt_callback(
                     lambda mnemonic: validate_mnemonic(mnemonic=mnemonic, language=c.params.get('mnemonic_language')),
                     prompt=lambda: load_text(['arg_mnemonic', 'prompt'], func='existing_mnemonic'),
-                    prompt_if_none=True,
+                    prompt_if=prompt_if_none,
                 )(c, _, mnemonic),
             help=lambda: load_text(['arg_mnemonic', 'help'], func='existing_mnemonic'),
             param_decls='--mnemonic',
@@ -105,7 +106,7 @@ def validate_mnemonic_language(ctx: click.Context, param: Any, language: str) ->
         lambda num: validate_int_range(num, 0, 2**32),
         lambda: load_text(['arg_validator_start_index', 'prompt'], func='existing_mnemonic'),
         lambda: load_text(['arg_validator_start_index', 'confirm'], func='existing_mnemonic'),
-        prompt_if_none=True,
+        prompt_if=prompt_if_none,
     ),
     default=0,
     help=lambda: load_text(['arg_validator_start_index', 'help'], func='existing_mnemonic'),
diff --git a/ethstaker_deposit/cli/exit_transaction_keystore.py b/ethstaker_deposit/cli/exit_transaction_keystore.py
index faa4b730..0de07381 100644
--- a/ethstaker_deposit/cli/exit_transaction_keystore.py
+++ b/ethstaker_deposit/cli/exit_transaction_keystore.py
@@ -18,6 +18,8 @@
     captive_prompt_callback,
     choice_prompt_func,
     jit_option,
+    prompt_if_none,
+    prompt_if_other_is_none,
 )
 from ethstaker_deposit.utils.constants import DEFAULT_EXIT_TRANSACTION_FOLDER_NAME
 from ethstaker_deposit.utils.intl import (
@@ -45,7 +47,7 @@
             lambda: load_text(['arg_exit_transaction_keystore_chain', 'prompt'], func=FUNC_NAME),
             ALL_CHAIN_KEYS
         ),
-        prompt_if_other_is_none='devnet_chain_setting',
+        prompt_if=prompt_if_other_is_none('devnet_chain_setting'),
         default=MAINNET,
     ),
     default=MAINNET,
@@ -57,7 +59,7 @@
     callback=captive_prompt_callback(
         lambda file: validate_keystore_file(file),
         lambda: load_text(['arg_exit_transaction_keystore_keystore', 'prompt'], func=FUNC_NAME),
-        prompt_if_none=True,
+        prompt_if=prompt_if_none,
     ),
     help=lambda: load_text(['arg_exit_transaction_keystore_keystore', 'help'], func=FUNC_NAME),
     param_decls='--keystore',
diff --git a/ethstaker_deposit/cli/exit_transaction_mnemonic.py b/ethstaker_deposit/cli/exit_transaction_mnemonic.py
index b8325435..65fdbb09 100644
--- a/ethstaker_deposit/cli/exit_transaction_mnemonic.py
+++ b/ethstaker_deposit/cli/exit_transaction_mnemonic.py
@@ -18,6 +18,7 @@
     captive_prompt_callback,
     choice_prompt_func,
     jit_option,
+    prompt_if_other_is_none,
 )
 from ethstaker_deposit.utils.constants import DEFAULT_EXIT_TRANSACTION_FOLDER_NAME
 from ethstaker_deposit.utils.intl import (
@@ -61,7 +62,7 @@ def _exit_verifier(kwargs: Dict[str, Any]) -> bool:
             lambda: load_text(['arg_exit_transaction_mnemonic_chain', 'prompt'], func=FUNC_NAME),
             ALL_CHAIN_KEYS
         ),
-        prompt_if_other_is_none='devnet_chain_setting',
+        prompt_if=prompt_if_other_is_none('devnet_chain_setting'),
         default=MAINNET,
     ),
     default=MAINNET,
@@ -140,6 +141,7 @@ def exit_transaction_mnemonic(
             'amount': 0,
             'chain_setting': chain_setting,
             'hex_withdrawal_address': None,
+            'compounding': False,
         } for index in key_indices]
 
         with concurrent.futures.ProcessPoolExecutor() as executor:
diff --git a/ethstaker_deposit/cli/generate_bls_to_execution_change.py b/ethstaker_deposit/cli/generate_bls_to_execution_change.py
index 6487a79f..a74ce5c8 100644
--- a/ethstaker_deposit/cli/generate_bls_to_execution_change.py
+++ b/ethstaker_deposit/cli/generate_bls_to_execution_change.py
@@ -32,6 +32,8 @@
     captive_prompt_callback,
     choice_prompt_func,
     jit_option,
+    prompt_if_none,
+    prompt_if_other_is_none,
 )
 from ethstaker_deposit.exceptions import ValidationError
 from ethstaker_deposit.utils.intl import (
@@ -81,7 +83,7 @@ def _validate_credentials_match(kwargs: Dict[str, Any]) -> Optional[ValidationEr
             lambda: load_text(['arg_chain', 'prompt'], func=FUNC_NAME),
             ALL_CHAIN_KEYS
         ),
-        prompt_if_other_is_none='devnet_chain_setting',
+        prompt_if=prompt_if_other_is_none('devnet_chain_setting'),
         default=MAINNET,
     ),
     default=MAINNET,
@@ -114,7 +116,7 @@ def _validate_credentials_match(kwargs: Dict[str, Any]) -> Optional[ValidationEr
         lambda bls_withdrawal_credentials_list:
             validate_bls_withdrawal_credentials_list(bls_withdrawal_credentials_list),
         lambda: load_text(['arg_bls_withdrawal_credentials_list', 'prompt'], func=FUNC_NAME),
-        prompt_if_none=True,
+        prompt_if=prompt_if_none,
     ),
     help=lambda: load_text(['arg_bls_withdrawal_credentials_list', 'help'], func=FUNC_NAME),
     param_decls='--bls_withdrawal_credentials_list',
@@ -126,7 +128,7 @@ def _validate_credentials_match(kwargs: Dict[str, Any]) -> Optional[ValidationEr
         lambda: load_text(['arg_withdrawal_address', 'prompt'], func=FUNC_NAME),
         lambda: load_text(['arg_withdrawal_address', 'confirm'], func=FUNC_NAME),
         lambda: load_text(['arg_withdrawal_address', 'mismatch'], func=FUNC_NAME),
-        prompt_if_none=True,
+        prompt_if=prompt_if_none,
     ),
     help=lambda: load_text(['arg_withdrawal_address', 'help'], func=FUNC_NAME),
     param_decls=['--withdrawal_address'],
@@ -180,6 +182,7 @@ def generate_bls_to_execution_change(
         chain_setting=chain_setting,
         start_index=validator_start_index,
         hex_withdrawal_address=withdrawal_address,
+        compounding=False,
     )
 
     # Check if the given old bls_withdrawal_credentials is as same as the mnemonic generated
diff --git a/ethstaker_deposit/cli/generate_bls_to_execution_change_keystore.py b/ethstaker_deposit/cli/generate_bls_to_execution_change_keystore.py
index f65b0a74..0ab45029 100644
--- a/ethstaker_deposit/cli/generate_bls_to_execution_change_keystore.py
+++ b/ethstaker_deposit/cli/generate_bls_to_execution_change_keystore.py
@@ -27,6 +27,8 @@
     captive_prompt_callback,
     choice_prompt_func,
     jit_option,
+    prompt_if_none,
+    prompt_if_other_is_none,
 )
 from ethstaker_deposit.utils.intl import (
     closest_match,
@@ -53,7 +55,7 @@
             lambda: load_text(['arg_chain', 'prompt'], func=FUNC_NAME),
             ALL_CHAIN_KEYS
         ),
-        prompt_if_other_is_none='devnet_chain_setting',
+        prompt_if=prompt_if_other_is_none('devnet_chain_setting'),
         default=MAINNET,
     ),
     default=MAINNET,
@@ -65,7 +67,7 @@
     callback=captive_prompt_callback(
         lambda file: validate_keystore_file(file),
         lambda: load_text(['arg_bls_to_execution_changes_keystore_keystore', 'prompt'], func=FUNC_NAME),
-        prompt_if_none=True,
+        prompt_if=prompt_if_none,
     ),
     help=lambda: load_text(['arg_bls_to_execution_changes_keystore_keystore', 'help'], func=FUNC_NAME),
     param_decls='--keystore',
@@ -99,7 +101,7 @@
         lambda: load_text(['arg_withdrawal_address', 'prompt'], func=FUNC_NAME),
         lambda: load_text(['arg_withdrawal_address', 'confirm'], func=FUNC_NAME),
         lambda: load_text(['arg_withdrawal_address', 'mismatch'], func=FUNC_NAME),
-        prompt_if_none=True,
+        prompt_if=prompt_if_none,
     ),
     help=lambda: load_text(['arg_withdrawal_address', 'help'], func=FUNC_NAME),
     param_decls=['--withdrawal_address'],
diff --git a/ethstaker_deposit/cli/generate_keys.py b/ethstaker_deposit/cli/generate_keys.py
index 55916973..343dd351 100644
--- a/ethstaker_deposit/cli/generate_keys.py
+++ b/ethstaker_deposit/cli/generate_keys.py
@@ -18,17 +18,24 @@
     validate_int_range,
     validate_password_strength,
     validate_withdrawal_address,
+    validate_yesno,
+    validate_deposit_amount,
     validate_devnet_chain_setting,
 )
 from ethstaker_deposit.utils.constants import (
     DEFAULT_VALIDATOR_KEYS_FOLDER_NAME,
     MIN_ACTIVATION_AMOUNT,
+    ETH2GWEI,
 )
 from ethstaker_deposit.utils.ascii_art import RHINO_0
 from ethstaker_deposit.utils.click import (
     captive_prompt_callback,
     choice_prompt_func,
     jit_option,
+    prompt_if_none,
+    prompt_if_other_is_none,
+    prompt_if_other_exists,
+    prompt_if_other_value,
 )
 from ethstaker_deposit.utils.intl import (
     closest_match,
@@ -43,6 +50,9 @@
 )
 
 
+min_activation_amount_eth = MIN_ACTIVATION_AMOUNT // ETH2GWEI
+
+
 def generate_keys_arguments_decorator(function: Callable[..., Any]) -> Callable[..., Any]:
     '''
     This is a decorator that, when applied to a parent-command, implements the
@@ -71,7 +81,7 @@ def generate_keys_arguments_decorator(function: Callable[..., Any]) -> Callable[
                     lambda: load_text(['chain', 'prompt'], func='generate_keys_arguments_decorator'),
                     ALL_CHAIN_KEYS
                 ),
-                prompt_if_other_is_none='devnet_chain_setting',
+                prompt_if=prompt_if_other_is_none('devnet_chain_setting'),
                 default=MAINNET,
             ),
             default=MAINNET,
@@ -86,7 +96,7 @@ def generate_keys_arguments_decorator(function: Callable[..., Any]) -> Callable[
                 lambda: load_text(['keystore_password', 'confirm'], func='generate_keys_arguments_decorator'),
                 lambda: load_text(['keystore_password', 'mismatch'], func='generate_keys_arguments_decorator'),
                 True,
-                prompt_if_none=True,
+                prompt_if=prompt_if_none,
             ),
             help=lambda: load_text(['keystore_password', 'help'], func='generate_keys_arguments_decorator'),
             hide_input=True,
@@ -100,13 +110,40 @@ def generate_keys_arguments_decorator(function: Callable[..., Any]) -> Callable[
                 lambda: load_text(['arg_withdrawal_address', 'confirm'], func='generate_keys_arguments_decorator'),
                 lambda: load_text(['arg_withdrawal_address', 'mismatch'], func='generate_keys_arguments_decorator'),
                 default="",
-                prompt_if_none=True,
+                prompt_if=prompt_if_none,
             ),
             default="",
             help=lambda: load_text(['arg_withdrawal_address', 'help'], func='generate_keys_arguments_decorator'),
             param_decls=['--withdrawal_address', '--execution_address', '--eth1_withdrawal_address'],
             prompt=False,  # the callback handles the prompt
         ),
+        jit_option(
+            callback=captive_prompt_callback(
+                lambda value: validate_yesno(None, None, value),
+                lambda: load_text(['arg_compounding', 'prompt'], func='generate_keys_arguments_decorator'),
+                default="False",
+                prompt_if=prompt_if_other_exists('withdrawal_address'),
+            ),
+            default=False,
+            help=lambda: load_text(['arg_compounding', 'help'], func='generate_keys_arguments_decorator'),
+            param_decls='--compounding/--regular-withdrawal',
+            prompt=False,  # the callback handles the prompt
+            type=bool,
+            show_default=True,
+        ),
+        jit_option(
+            callback=captive_prompt_callback(
+                lambda amount: validate_deposit_amount(amount),
+                lambda: load_text(['arg_amount', 'prompt'], func='generate_keys_arguments_decorator'),
+                default=str(min_activation_amount_eth),
+                prompt_if=prompt_if_other_value('compounding', True),
+            ),
+            default=str(min_activation_amount_eth),
+            help=lambda: load_text(['arg_amount', 'help'], func='generate_keys_arguments_decorator'),
+            param_decls='--amount',
+            prompt=False,  # the callback handles the prompt
+            show_default=True,
+        ),
         jit_option(
             default=False,
             is_flag=True,
@@ -130,11 +167,13 @@ def generate_keys_arguments_decorator(function: Callable[..., Any]) -> Callable[
 @click.pass_context
 def generate_keys(ctx: click.Context, validator_start_index: int,
                   num_validators: int, folder: str, chain: str, keystore_password: str,
-                  withdrawal_address: HexAddress, pbkdf2: bool,
+                  withdrawal_address: HexAddress, compounding: bool, amount: int, pbkdf2: bool,
                   devnet_chain_setting: Optional[BaseChainSetting], **kwargs: Any) -> None:
     mnemonic = ctx.obj['mnemonic']
     mnemonic_password = ctx.obj['mnemonic_password']
-    amounts = [MIN_ACTIVATION_AMOUNT] * num_validators
+    if withdrawal_address is None or not compounding:
+        amount = MIN_ACTIVATION_AMOUNT
+    amounts = [amount] * num_validators
     folder = os.path.join(folder, DEFAULT_VALIDATOR_KEYS_FOLDER_NAME)
 
     # Get chain setting
@@ -153,6 +192,7 @@ def generate_keys(ctx: click.Context, validator_start_index: int,
         chain_setting=chain_setting,
         start_index=validator_start_index,
         hex_withdrawal_address=withdrawal_address,
+        compounding=compounding,
         use_pbkdf2=pbkdf2
     )
 
diff --git a/ethstaker_deposit/cli/partial_deposit.py b/ethstaker_deposit/cli/partial_deposit.py
index 7efe1446..a83157ac 100644
--- a/ethstaker_deposit/cli/partial_deposit.py
+++ b/ethstaker_deposit/cli/partial_deposit.py
@@ -22,8 +22,15 @@
     captive_prompt_callback,
     choice_prompt_func,
     jit_option,
+    prompt_if_none,
+    prompt_if_other_is_none,
+    prompt_if_other_exists,
+)
+from ethstaker_deposit.utils.constants import (
+    DEFAULT_PARTIAL_DEPOSIT_FOLDER_NAME,
+    EXECUTION_ADDRESS_WITHDRAWAL_PREFIX,
+    COMPOUNDING_WITHDRAWAL_PREFIX,
 )
-from ethstaker_deposit.utils.constants import DEFAULT_PARTIAL_DEPOSIT_FOLDER_NAME, EXECUTION_ADDRESS_WITHDRAWAL_PREFIX
 from ethstaker_deposit.utils.deposit import export_deposit_data_json
 from ethstaker_deposit.utils.intl import (
     closest_match,
@@ -38,8 +45,9 @@
 from ethstaker_deposit.utils.validation import (
     validate_deposit,
     validate_keystore_file,
-    validate_partial_deposit_amount,
+    validate_deposit_amount,
     validate_withdrawal_address,
+    validate_yesno,
     validate_devnet_chain_setting,
 )
 
@@ -57,7 +65,7 @@
             lambda: load_text(['arg_partial_deposit_chain', 'prompt'], func=FUNC_NAME),
             ALL_CHAIN_KEYS
         ),
-        prompt_if_other_is_none='devnet_chain_setting',
+        prompt_if=prompt_if_other_is_none('devnet_chain_setting'),
         default=MAINNET,
     ),
     default=MAINNET,
@@ -69,7 +77,7 @@
     callback=captive_prompt_callback(
         lambda file: validate_keystore_file(file),
         lambda: load_text(['arg_partial_deposit_keystore', 'prompt'], func=FUNC_NAME),
-        prompt_if_none=True,
+        prompt_if=prompt_if_none,
     ),
     help=lambda: load_text(['arg_partial_deposit_keystore', 'help'], func=FUNC_NAME),
     param_decls='--keystore',
@@ -90,10 +98,10 @@
 )
 @jit_option(
     callback=captive_prompt_callback(
-        lambda amount: validate_partial_deposit_amount(amount),
+        lambda amount: validate_deposit_amount(amount),
         lambda: load_text(['arg_partial_deposit_amount', 'prompt'], func=FUNC_NAME),
         default="32",
-        prompt_if_none=True,
+        prompt_if=prompt_if_none,
     ),
     default="32",
     help=lambda: load_text(['arg_partial_deposit_amount', 'help'], func=FUNC_NAME),
@@ -106,12 +114,26 @@
         lambda: load_text(['arg_withdrawal_address', 'prompt'], func=FUNC_NAME),
         lambda: load_text(['arg_withdrawal_address', 'confirm'], func=FUNC_NAME),
         lambda: load_text(['arg_withdrawal_address', 'mismatch'], func=FUNC_NAME),
-        prompt_if_none=True,
+        prompt_if=prompt_if_none,
     ),
     help=lambda: load_text(['arg_withdrawal_address', 'help'], func=FUNC_NAME),
     param_decls=['--withdrawal_address', '--execution_address', '--eth1_withdrawal_credentials'],
     prompt=False,  # the callback handles the prompt
 )
+@jit_option(
+    callback=captive_prompt_callback(
+        lambda value: validate_yesno(None, None, value),
+        lambda: load_text(['arg_compounding', 'prompt'], func=FUNC_NAME),
+        default="False",
+        prompt_if=prompt_if_other_exists('withdrawal_address'),
+    ),
+    default=False,
+    help=lambda: load_text(['arg_compounding', 'help'], func=FUNC_NAME),
+    param_decls='--compounding/--regular-withdrawal',
+    prompt=False,  # the callback handles the prompt
+    type=bool,
+    show_default=True,
+)
 @jit_option(
     default=os.getcwd(),
     help=lambda: load_text(['arg_partial_deposit_output_folder', 'help'], func=FUNC_NAME),
@@ -133,6 +155,7 @@ def partial_deposit(
         keystore_password: str,
         amount: int,
         withdrawal_address: HexAddress,
+        compounding: bool,
         output_folder: str,
         devnet_chain_setting: Optional[BaseChainSetting],
         **kwargs: Any) -> None:
@@ -147,7 +170,11 @@ def partial_deposit(
     # Get chain setting
     chain_setting = devnet_chain_setting if devnet_chain_setting is not None else get_chain_setting(chain)
 
-    withdrawal_credentials = EXECUTION_ADDRESS_WITHDRAWAL_PREFIX
+    if compounding:
+        withdrawal_credentials = COMPOUNDING_WITHDRAWAL_PREFIX
+    else:
+        withdrawal_credentials = EXECUTION_ADDRESS_WITHDRAWAL_PREFIX
+
     withdrawal_credentials += b'\x00' * 11
     withdrawal_credentials += to_canonical_address(withdrawal_address)
 
diff --git a/ethstaker_deposit/credentials.py b/ethstaker_deposit/credentials.py
index 26969f33..f61ab1de 100644
--- a/ethstaker_deposit/credentials.py
+++ b/ethstaker_deposit/credentials.py
@@ -26,6 +26,7 @@
 from ethstaker_deposit.utils.constants import (
     BLS_WITHDRAWAL_PREFIX,
     EXECUTION_ADDRESS_WITHDRAWAL_PREFIX,
+    COMPOUNDING_WITHDRAWAL_PREFIX,
     ETH2GWEI,
     MAX_DEPOSIT_AMOUNT,
     MIN_DEPOSIT_AMOUNT,
@@ -50,6 +51,7 @@
 class WithdrawalType(Enum):
     BLS_WITHDRAWAL = 0
     EXECUTION_ADDRESS_WITHDRAWAL = 1
+    COMPOUNDING_WITHDRAWAL = 2
 
 
 class Credential:
@@ -60,6 +62,7 @@ class Credential:
     def __init__(self, *, mnemonic: str, mnemonic_password: str,
                  index: int, amount: int, chain_setting: BaseChainSetting,
                  hex_withdrawal_address: Optional[HexAddress],
+                 compounding: Optional[bool] = False,
                  use_pbkdf2: Optional[bool] = False):
         # Set path as EIP-2334 format
         # https://eips.ethereum.org/EIPS/eip-2334
@@ -76,6 +79,7 @@ def __init__(self, *, mnemonic: str, mnemonic_password: str,
         self.amount = amount
         self.chain_setting = chain_setting
         self.hex_withdrawal_address = hex_withdrawal_address
+        self.compounding = compounding
         self.use_pbkdf2 = use_pbkdf2
 
     @property
@@ -95,7 +99,10 @@ def withdrawal_address(self) -> Optional[Address]:
     @property
     def withdrawal_prefix(self) -> bytes:
         if self.withdrawal_address is not None:
-            return EXECUTION_ADDRESS_WITHDRAWAL_PREFIX
+            if self.compounding:
+                return COMPOUNDING_WITHDRAWAL_PREFIX
+            else:
+                return EXECUTION_ADDRESS_WITHDRAWAL_PREFIX
         else:
             return BLS_WITHDRAWAL_PREFIX
 
@@ -105,6 +112,8 @@ def withdrawal_type(self) -> WithdrawalType:
             return WithdrawalType.BLS_WITHDRAWAL
         elif self.withdrawal_prefix == EXECUTION_ADDRESS_WITHDRAWAL_PREFIX:
             return WithdrawalType.EXECUTION_ADDRESS_WITHDRAWAL
+        elif self.withdrawal_prefix == COMPOUNDING_WITHDRAWAL_PREFIX:
+            return WithdrawalType.COMPOUNDING_WITHDRAWAL
         else:
             raise ValueError(f"Invalid withdrawal_prefix {self.withdrawal_prefix.hex()}")
 
@@ -120,6 +129,13 @@ def withdrawal_credentials(self) -> bytes:
             withdrawal_credentials = EXECUTION_ADDRESS_WITHDRAWAL_PREFIX
             withdrawal_credentials += b'\x00' * 11
             withdrawal_credentials += self.withdrawal_address
+        elif (
+            self.withdrawal_type == WithdrawalType.COMPOUNDING_WITHDRAWAL
+            and self.withdrawal_address is not None
+        ):
+            withdrawal_credentials = COMPOUNDING_WITHDRAWAL_PREFIX
+            withdrawal_credentials += b'\x00' * 11
+            withdrawal_credentials += self.withdrawal_address
         else:
             raise ValueError(f"Invalid withdrawal_type {self.withdrawal_type}")
         return withdrawal_credentials
@@ -279,6 +295,7 @@ def from_mnemonic(cls,
                       chain_setting: BaseChainSetting,
                       start_index: int,
                       hex_withdrawal_address: Optional[HexAddress],
+                      compounding: Optional[bool] = False,
                       use_pbkdf2: Optional[bool] = False) -> 'CredentialList':
         if len(amounts) != num_keys:
             raise ValueError(
@@ -296,6 +313,7 @@ def from_mnemonic(cls,
                 'amount': amounts[index - start_index],
                 'chain_setting': chain_setting,
                 'hex_withdrawal_address': hex_withdrawal_address,
+                'compounding': compounding,
                 'use_pbkdf2': use_pbkdf2,
             } for index in key_indices]
 
diff --git a/ethstaker_deposit/intl/en/cli/generate_keys.json b/ethstaker_deposit/intl/en/cli/generate_keys.json
index 45663f12..fe9f448e 100644
--- a/ethstaker_deposit/intl/en/cli/generate_keys.json
+++ b/ethstaker_deposit/intl/en/cli/generate_keys.json
@@ -23,6 +23,14 @@
             "confirm": "Repeat your withdrawal address for confirmation.",
             "mismatch": "Error: the two entered values do not match. Please type again."
         },
+        "arg_compounding": {
+            "help": "Generates compounding validators with 0x02 withdrawal credentials for a 2048 ETH maximum effective balance or generate regular validators with 0x01 withdrawal credentials for a 32 ETH maximum effective balance. Use of this option requires a withdrawal address. This feature is only supported on networks that have undergone the Pectra fork.",
+            "prompt": "Please enter yes if you want to generate compounding validators with 0x02 withdrawal credentials for a 2048 ETH maximum effective balance. Compounding validators and 0x02 withdrawal credentials are only supported on networks that have undergone the Pectra fork. Please type no or nothing if you want regular validators with 0x01 withdrawal credentials for a 32 ETH maximum effective balance."
+        },
+        "arg_amount": {
+            "help": "The amount to deposit to these validators in ether denomination. Must be at least 1 ether and can not have greater precision than 1 gwei. Use of this option requires compounding validators.",
+            "prompt": "Please enter the amount you wish to deposit to these validators. Must be at least 1 ether and can not have greater precision than 1 gwei. 32 is required to activate a new validator"
+        },
         "arg_pbkdf2": {
             "help": "Uses the pbkdf2 hashing function instead of scrypt for generated keystore files. "
         },
diff --git a/ethstaker_deposit/intl/en/cli/partial_deposit.json b/ethstaker_deposit/intl/en/cli/partial_deposit.json
index 187cf5f6..93f408d9 100644
--- a/ethstaker_deposit/intl/en/cli/partial_deposit.json
+++ b/ethstaker_deposit/intl/en/cli/partial_deposit.json
@@ -28,6 +28,10 @@
           "confirm": "Repeat the withdrawal address for confirmation.",
           "prompt": "Please enter the withdrawal address. If you wish to create a validator with 0x00 credentials use the new-mnemonic or existing-mnemonic command."
       },
+      "arg_compounding": {
+          "help": "Generates compounding validators with 0x02 withdrawal credentials for a 2048 ETH maximum effective balance or generate regular validators with 0x01 withdrawal credentials for a 32 ETH maximum effective balance. This feature is only supported on networks that have undergone the Pectra fork.",
+          "prompt": "Please enter yes if you want to generate compounding validators with 0x02 withdrawal credentials for a 2048 ETH maximum effective balance. Compounding validators and 0x02 withdrawal credentials are only supported on networks that have undergone the Pectra fork. Please type no or nothing if you want regular validators with 0x01 withdrawal credentials for a 32 ETH maximum effective balance."
+      },
       "arg_devnet_chain_setting": {
           "help": "[DEVNET ONLY] Set specific GENESIS_FORK_VERSION value. This should be a JSON string containing an object with the following keys: network_name, genesis_fork_version, exit_fork_version and genesis_validator_root. It should be similar to what you can find in settings.py. This will override any selected chain."
       },
diff --git a/ethstaker_deposit/intl/en/utils/validation.json b/ethstaker_deposit/intl/en/utils/validation.json
index 143a9085..03c895ad 100644
--- a/ethstaker_deposit/intl/en/utils/validation.json
+++ b/ethstaker_deposit/intl/en/utils/validation.json
@@ -19,11 +19,14 @@
         "err_missing_address": "An address must be provided",
         "msg_ECDSA_hex_addr_withdrawal": "**[Warning] you are setting a withdrawal address. Please ensure that you have full control over this address.**"
     },
-    "validate_partial_deposit_amount": {
+    "validate_yesno": {
+        "err_invalid_bool_value": "This is an invalid value for a yes/no question."
+    },
+    "validate_deposit_amount": {
         "err_invalid_amount": "Invalid amount provided. Please provide a value of at least 1 ether of how much you wish to deposit.",
         "err_not_gwei_denomination": "Invalid amount provided. Please provide a value can not have precision beyond 1 gwei.",
         "err_min_deposit": "Invalid amount provided. Please provide a value of at least 1 ether.",
-        "err_max_deposit": "Invalid amount provided. The max deposit amount is 2048 ether. Please provide a lesser amount."
+        "err_max_deposit": "Invalid amount provided. The max deposit amount is 2048 ether. Please provide a lesser or equal amount."
     },
     "validate_bls_withdrawal_credentials": {
         "err_is_already_01_form": "The given withdrawal credentials is already in 0x01 form. Have you already set the withdrawal address?",
diff --git a/ethstaker_deposit/utils/click.py b/ethstaker_deposit/utils/click.py
index d9f257dd..9266a485 100644
--- a/ethstaker_deposit/utils/click.py
+++ b/ethstaker_deposit/utils/click.py
@@ -84,6 +84,48 @@ def decorator(f: Callable[[Any], Any]) -> Callable[[Any], Any]:
     return decorator
 
 
+def prompt_if_none(ctx: click.Context, param: Any, user_input: str) -> bool:
+    '''
+    Returns true if the param prompt is none. To be used with the prompt_if argument from captive_prompt_callback.
+    '''
+
+    return param.prompt is None
+
+
+def prompt_if_other_is_none(other: str) -> Callable[[click.Context, Any, str], bool]:
+    '''
+    Returns true if the other param is none. To be used with the prompt_if argument from captive_prompt_callback.
+    '''
+
+    def callback(ctx: click.Context, param: Any, user_input: str) -> bool:
+        return ctx.params.get(other, None) is None
+
+    return callback
+
+
+def prompt_if_other_exists(other: str) -> Callable[[click.Context, Any, str], bool]:
+    '''
+    Returns true if the other param exists. To be used with the prompt_if argument from captive_prompt_callback.
+    '''
+
+    def callback(ctx: click.Context, param: Any, user_input: str) -> bool:
+        return ctx.params.get(other, None) is not None
+
+    return callback
+
+
+def prompt_if_other_value(other: str, value: Any) -> Callable[[click.Context, Any, str], bool]:
+    '''
+    Returns true if the other param's value equal the passed value. To be used with the prompt_if argument
+    from captive_prompt_callback.
+    '''
+
+    def callback(ctx: click.Context, param: Any, user_input: str) -> bool:
+        return ctx.params.get(other, None) == value
+
+    return callback
+
+
 def captive_prompt_callback(
     processing_func: Callable[[str], Any],
     prompt: Callable[[], str],
@@ -91,8 +133,7 @@ def captive_prompt_callback(
     confirmation_mismatch_msg: Callable[[], str] = lambda: '',
     hide_input: bool = False,
     default: Optional[Union[Callable[[], str], str]] = None,
-    prompt_if_none: bool = False,
-    prompt_if_other_is_none: Optional[str] = None,
+    prompt_if: Optional[Callable[[click.Context, Any, str], bool]] = None,
 ) -> Callable[[click.Context, str, str], Any]:
     '''
     Traps the user in a prompt until the value chosen is acceptable
@@ -104,21 +145,17 @@ def captive_prompt_callback(
     :param hide_input: bool, hides the input as the user types
     :param default: the optional callable that returns a str or a str to be used as the default value if nothing is
     entered by the user
-    :param prompt_if_none: bool, prompt if the source of the parameter is from the default value and it's none
-    :param prompt_if_other_is_none: the optional str of the other parameter to check. prompt if the source of the
-    parameter is from the default value and the value of that other parameter is none
+    :param prompt_if: the optional callable, prompt if the source of the parameter is from the default value and this
+    call returns true
     '''
     def callback(ctx: click.Context, param: Any, user_input: str) -> Any:
         # the callback is called twice, once for the option prompt and once to verify the input
         # To avoid showing confirmation prompt twice, we introduce a flag to prompt inside
         # the callback
         # See https://github.com/pallets/click/discussions/2673
-        if (prompt_if_none and param.prompt is None
-                and ctx.get_parameter_source(param.name) == click.core.ParameterSource.DEFAULT):
-            user_input = click.prompt(prompt(), hide_input=hide_input, default=_value_of(default))
-        elif (prompt_if_other_is_none is not None
-                and ctx.params.get(prompt_if_other_is_none, None) is None
-                and ctx.get_parameter_source(param.name) == click.core.ParameterSource.DEFAULT):
+        if (prompt_if is not None
+                and ctx.get_parameter_source(param.name) == click.core.ParameterSource.DEFAULT
+                and prompt_if(ctx, param, user_input)):
             user_input = click.prompt(prompt(), hide_input=hide_input, default=_value_of(default))
         if config.non_interactive:
             return processing_func(user_input)
diff --git a/ethstaker_deposit/utils/constants.py b/ethstaker_deposit/utils/constants.py
index 99427ecf..312a2e0e 100644
--- a/ethstaker_deposit/utils/constants.py
+++ b/ethstaker_deposit/utils/constants.py
@@ -16,7 +16,6 @@
 COMPOUNDING_WITHDRAWAL_PREFIX = bytes.fromhex('02')
 
 ETH2GWEI = 10 ** 9
-# TODO: check if user will can deposit a new validator with more than 32 and 0x02 credentials or 0x00 and 0x01 only
 MIN_DEPOSIT_AMOUNT = 2 ** 0 * ETH2GWEI
 MIN_ACTIVATION_AMOUNT = 2 ** 5 * ETH2GWEI
 MAX_DEPOSIT_AMOUNT = 2 ** 11 * ETH2GWEI
diff --git a/ethstaker_deposit/utils/validation.py b/ethstaker_deposit/utils/validation.py
index f251f6cd..504b8ef6 100644
--- a/ethstaker_deposit/utils/validation.py
+++ b/ethstaker_deposit/utils/validation.py
@@ -6,6 +6,9 @@
 import concurrent.futures
 from typing import Any, Dict, Sequence, Optional
 
+from click.types import BoolParamType
+from click.exceptions import BadParameter
+
 from eth_typing import (
     BLSPubkey,
     BLSSignature,
@@ -183,7 +186,19 @@ def validate_withdrawal_address(cts: click.Context, param: Any, address: str, re
     return normalized_address
 
 
-def validate_partial_deposit_amount(amount: str) -> int:
+def validate_yesno(ctx: click.Context, param: Any, value: str) -> bool:
+    '''
+    Verifies that a value is part of the bool values accepted by click. The string values “1”, “true”,
+    “t”, “yes”, “y”, and “on” convert to True. “0”, “false”, “f”, “no”, “n”, and “off” convert to False.
+    '''
+    try:
+        param = BoolParamType()
+        return param.convert(value, param, ctx)
+    except BadParameter:
+        raise ValidationError(load_text(['err_invalid_bool_value']))
+
+
+def validate_deposit_amount(amount: str) -> int:
     '''
     Verifies that `amount` is a valid gwei denomination and 1 ether <= amount <= MAX_DEPOSIT_AMOUNT gwei
     Amount is expected to be in ether and the returned value will be converted to gwei and represented as an int


Test patch:
diff --git a/ethstaker_deposit/cli/test_keystore.py b/ethstaker_deposit/cli/test_keystore.py
index f7a64269..90286c37 100644
--- a/ethstaker_deposit/cli/test_keystore.py
+++ b/ethstaker_deposit/cli/test_keystore.py
@@ -7,6 +7,7 @@
 from ethstaker_deposit.utils.click import (
     captive_prompt_callback,
     jit_option,
+    prompt_if_none,
 )
 from ethstaker_deposit.utils.intl import (
     load_text,
@@ -24,7 +25,7 @@
     callback=captive_prompt_callback(
         lambda file: validate_keystore_file(file),
         lambda: load_text(['arg_test_keystore_keystore', 'prompt'], func=FUNC_NAME),
-        prompt_if_none=True,
+        prompt_if=prompt_if_none,
     ),
     help=lambda: load_text(['arg_test_keystore_keystore', 'help'], func=FUNC_NAME),
     param_decls='--keystore',
diff --git a/tests/test_cli/test_existing_mnemonic.py b/tests/test_cli/test_existing_mnemonic.py
index 7252af90..ec5b771d 100644
--- a/tests/test_cli/test_existing_mnemonic.py
+++ b/tests/test_cli/test_existing_mnemonic.py
@@ -8,7 +8,13 @@
 from eth_utils import decode_hex
 
 from ethstaker_deposit.deposit import cli
-from ethstaker_deposit.utils.constants import DEFAULT_VALIDATOR_KEYS_FOLDER_NAME, EXECUTION_ADDRESS_WITHDRAWAL_PREFIX
+from ethstaker_deposit.utils.constants import (
+    DEFAULT_VALIDATOR_KEYS_FOLDER_NAME,
+    EXECUTION_ADDRESS_WITHDRAWAL_PREFIX,
+    COMPOUNDING_WITHDRAWAL_PREFIX,
+    MIN_ACTIVATION_AMOUNT,
+    ETH2GWEI,
+)
 from .helpers import clean_key_folder, get_permissions, get_uuid
 
 
@@ -68,7 +74,7 @@ def test_existing_mnemonic_withdrawal_address() -> None:
     inputs = [
         'TREZOR',
         'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about',
-        '2', '2', '5', 'mainnet', 'MyPasswordIs', 'MyPasswordIs', withdrawal_address, withdrawal_address, '']
+        '2', '2', '5', 'mainnet', 'MyPasswordIs', 'MyPasswordIs', withdrawal_address, withdrawal_address, '', '']
     data = '\n'.join(inputs)
     arguments = [
         '--language', 'english',
@@ -109,6 +115,178 @@ def test_existing_mnemonic_withdrawal_address() -> None:
     clean_key_folder(my_folder_path)
 
 
+def test_existing_mnemonic_compounding_validators() -> None:
+    # Prepare folder
+    my_folder_path = os.path.join(os.getcwd(), 'TESTING_TEMP_FOLDER')
+    clean_key_folder(my_folder_path)
+    if not os.path.exists(my_folder_path):
+        os.mkdir(my_folder_path)
+
+    runner = CliRunner()
+    withdrawal_address = '0x00000000219ab540356cBB839Cbe05303d7705Fa'
+    inputs = [
+        'TREZOR',
+        'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about',
+        '2', '2', '5', 'mainnet', 'MyPasswordIs', 'MyPasswordIs', withdrawal_address, withdrawal_address, 'yes', '', '']
+    data = '\n'.join(inputs)
+    arguments = [
+        '--language', 'english',
+        '--ignore_connectivity',
+        'existing-mnemonic',
+        '--folder', my_folder_path,
+        '--mnemonic_password', 'TREZOR',
+    ]
+    result = runner.invoke(cli, arguments, input=data)
+
+    assert result.exit_code == 0
+
+    # Check files
+    validator_keys_folder_path = os.path.join(my_folder_path, DEFAULT_VALIDATOR_KEYS_FOLDER_NAME)
+    _, _, key_files = next(os.walk(validator_keys_folder_path))
+
+    deposit_file = [key_file for key_file in key_files if key_file.startswith('deposit_data')][0]
+    with open(validator_keys_folder_path + '/' + deposit_file, 'r', encoding='utf-8') as f:
+        deposits_dict = json.load(f)
+    for deposit in deposits_dict:
+        withdrawal_credentials = bytes.fromhex(deposit['withdrawal_credentials'])
+        assert withdrawal_credentials == (
+            COMPOUNDING_WITHDRAWAL_PREFIX + b'\x00' * 11 + decode_hex(withdrawal_address)
+        )
+        amount = deposit['amount']
+        assert amount == MIN_ACTIVATION_AMOUNT
+
+    all_uuid = [
+        get_uuid(validator_keys_folder_path + '/' + key_file)
+        for key_file in key_files
+        if key_file.startswith('keystore')
+    ]
+    assert len(set(all_uuid)) == 5
+
+    # Verify file permissions
+    if os.name == 'posix':
+        for file_name in key_files:
+            assert get_permissions(validator_keys_folder_path, file_name) == '0o400'
+    # Clean up
+    clean_key_folder(my_folder_path)
+
+
+def test_existing_mnemonic_compounding_custom_amount() -> None:
+    # Prepare folder
+    my_folder_path = os.path.join(os.getcwd(), 'TESTING_TEMP_FOLDER')
+    clean_key_folder(my_folder_path)
+    if not os.path.exists(my_folder_path):
+        os.mkdir(my_folder_path)
+
+    custom_amount = 1050
+
+    runner = CliRunner()
+    withdrawal_address = '0x00000000219ab540356cBB839Cbe05303d7705Fa'
+    inputs = [
+        'TREZOR',
+        'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about',
+        '2', '2', '5', 'mainnet', 'MyPasswordIs', 'MyPasswordIs', withdrawal_address, withdrawal_address, 'yes', '']
+    data = '\n'.join(inputs)
+    arguments = [
+        '--language', 'english',
+        '--ignore_connectivity',
+        'existing-mnemonic',
+        '--folder', my_folder_path,
+        '--amount', str(custom_amount),
+        '--mnemonic_password', 'TREZOR',
+    ]
+    result = runner.invoke(cli, arguments, input=data)
+
+    assert result.exit_code == 0
+
+    # Check files
+    validator_keys_folder_path = os.path.join(my_folder_path, DEFAULT_VALIDATOR_KEYS_FOLDER_NAME)
+    _, _, key_files = next(os.walk(validator_keys_folder_path))
+
+    deposit_file = [key_file for key_file in key_files if key_file.startswith('deposit_data')][0]
+    with open(validator_keys_folder_path + '/' + deposit_file, 'r', encoding='utf-8') as f:
+        deposits_dict = json.load(f)
+    for deposit in deposits_dict:
+        withdrawal_credentials = bytes.fromhex(deposit['withdrawal_credentials'])
+        assert withdrawal_credentials == (
+            COMPOUNDING_WITHDRAWAL_PREFIX + b'\x00' * 11 + decode_hex(withdrawal_address)
+        )
+        amount = deposit['amount']
+        assert amount == (custom_amount * ETH2GWEI)
+
+    all_uuid = [
+        get_uuid(validator_keys_folder_path + '/' + key_file)
+        for key_file in key_files
+        if key_file.startswith('keystore')
+    ]
+    assert len(set(all_uuid)) == 5
+
+    # Verify file permissions
+    if os.name == 'posix':
+        for file_name in key_files:
+            assert get_permissions(validator_keys_folder_path, file_name) == '0o400'
+    # Clean up
+    clean_key_folder(my_folder_path)
+
+
+def test_existing_mnemonic_compounding_cli_args() -> None:
+    # Prepare folder
+    my_folder_path = os.path.join(os.getcwd(), 'TESTING_TEMP_FOLDER')
+    clean_key_folder(my_folder_path)
+    if not os.path.exists(my_folder_path):
+        os.mkdir(my_folder_path)
+
+    custom_amount = 247
+
+    runner = CliRunner()
+    withdrawal_address = '0x00000000219ab540356cBB839Cbe05303d7705Fa'
+    inputs = [
+        'TREZOR',
+        'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about',
+        '2', '2', '5', 'mainnet', 'MyPasswordIs', 'MyPasswordIs', withdrawal_address, withdrawal_address, '']
+    data = '\n'.join(inputs)
+    arguments = [
+        '--language', 'english',
+        '--ignore_connectivity',
+        'existing-mnemonic',
+        '--folder', my_folder_path,
+        '--compounding',
+        '--amount', str(custom_amount),
+        '--mnemonic_password', 'TREZOR',
+    ]
+    result = runner.invoke(cli, arguments, input=data)
+
+    assert result.exit_code == 0
+
+    # Check files
+    validator_keys_folder_path = os.path.join(my_folder_path, DEFAULT_VALIDATOR_KEYS_FOLDER_NAME)
+    _, _, key_files = next(os.walk(validator_keys_folder_path))
+
+    deposit_file = [key_file for key_file in key_files if key_file.startswith('deposit_data')][0]
+    with open(validator_keys_folder_path + '/' + deposit_file, 'r', encoding='utf-8') as f:
+        deposits_dict = json.load(f)
+    for deposit in deposits_dict:
+        withdrawal_credentials = bytes.fromhex(deposit['withdrawal_credentials'])
+        assert withdrawal_credentials == (
+            COMPOUNDING_WITHDRAWAL_PREFIX + b'\x00' * 11 + decode_hex(withdrawal_address)
+        )
+        amount = deposit['amount']
+        assert amount == (custom_amount * ETH2GWEI)
+
+    all_uuid = [
+        get_uuid(validator_keys_folder_path + '/' + key_file)
+        for key_file in key_files
+        if key_file.startswith('keystore')
+    ]
+    assert len(set(all_uuid)) == 5
+
+    # Verify file permissions
+    if os.name == 'posix':
+        for file_name in key_files:
+            assert get_permissions(validator_keys_folder_path, file_name) == '0o400'
+    # Clean up
+    clean_key_folder(my_folder_path)
+
+
 def test_existing_mnemonic_withdrawal_address_bad_checksum() -> None:
     # Prepare folder
     my_folder_path = os.path.join(os.getcwd(), 'TESTING_TEMP_FOLDER')
@@ -126,7 +304,7 @@ def test_existing_mnemonic_withdrawal_address_bad_checksum() -> None:
         'TREZOR',
         'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about',
         '2', '2', '5', 'mainnet', 'MyPasswordIs', 'MyPasswordIs',
-        wrong_withdrawal_address, correct_withdrawal_address, correct_withdrawal_address, ''
+        wrong_withdrawal_address, correct_withdrawal_address, correct_withdrawal_address, '', ''
     ]
     data = '\n'.join(inputs)
     arguments = [
diff --git a/tests/test_cli/test_exit_transaction_keystore.py b/tests/test_cli/test_exit_transaction_keystore.py
index ed27b940..f17c3777 100644
--- a/tests/test_cli/test_exit_transaction_keystore.py
+++ b/tests/test_cli/test_exit_transaction_keystore.py
@@ -39,7 +39,8 @@ def test_exit_transaction_keystore() -> None:
         index=0,
         amount=0,
         chain_setting=get_chain_setting(chain),
-        hex_withdrawal_address=None
+        hex_withdrawal_address=None,
+        compounding=False,
     )
 
     # Save keystore file
@@ -112,6 +113,7 @@ def test_exit_transaction_with_pbkdf2() -> None:
         amount=0,
         chain_setting=get_chain_setting(chain),
         hex_withdrawal_address=None,
+        compounding=False,
         use_pbkdf2=True,
     )
     scrypt_credential = Credential(
@@ -121,6 +123,7 @@ def test_exit_transaction_with_pbkdf2() -> None:
         amount=0,
         chain_setting=get_chain_setting(chain),
         hex_withdrawal_address=None,
+        compounding=False,
         use_pbkdf2=False,
     )
 
@@ -260,7 +263,8 @@ def test_invalid_keystore_password() -> None:
         index=0,
         amount=0,
         chain_setting=get_chain_setting(chain),
-        hex_withdrawal_address=None
+        hex_withdrawal_address=None,
+        compounding=False,
     )
 
     # Save keystore file
@@ -320,7 +324,8 @@ def test_exit_transaction_keystore_custom_testnet() -> None:
         index=0,
         amount=0,
         chain_setting=get_devnet_chain_setting(**devnet_chain),
-        hex_withdrawal_address=None
+        hex_withdrawal_address=None,
+        compounding=False,
     )
 
     # Save keystore file
diff --git a/tests/test_cli/test_generate_bls_to_execution_change_keystore.py b/tests/test_cli/test_generate_bls_to_execution_change_keystore.py
index 9859f971..0ad5ead1 100644
--- a/tests/test_cli/test_generate_bls_to_execution_change_keystore.py
+++ b/tests/test_cli/test_generate_bls_to_execution_change_keystore.py
@@ -37,7 +37,8 @@ def test_bls_change_keystore() -> None:
         index=0,
         amount=0,
         chain_setting=get_chain_setting(chain),
-        hex_withdrawal_address=None
+        hex_withdrawal_address=None,
+        compounding=False,
     )
 
     keystore_filepath = credential.save_signing_keystore(keystore_password, changes_folder_path, time.time())
@@ -103,6 +104,7 @@ def test_bls_change_keystore_with_pbkdf2() -> None:
         amount=0,
         chain_setting=get_chain_setting(chain),
         hex_withdrawal_address=None,
+        compounding=False,
         use_pbkdf2=True,
     )
     scrypt_credential = Credential(
@@ -112,6 +114,7 @@ def test_bls_change_keystore_with_pbkdf2() -> None:
         amount=0,
         chain_setting=get_chain_setting(chain),
         hex_withdrawal_address=None,
+        compounding=False,
         use_pbkdf2=False,
     )
 
@@ -247,7 +250,8 @@ def test_invalid_keystore_password() -> None:
         index=0,
         amount=0,
         chain_setting=get_chain_setting(chain),
-        hex_withdrawal_address=None
+        hex_withdrawal_address=None,
+        compounding=False,
     )
 
     keystore_filepath = credential.save_signing_keystore(keystore_password, bls_change_folder_path, time.time())
@@ -307,7 +311,8 @@ def test_bls_change_keystore_custom_testnet() -> None:
         index=0,
         amount=0,
         chain_setting=get_devnet_chain_setting(**devnet_chain),
-        hex_withdrawal_address=None
+        hex_withdrawal_address=None,
+        compounding=False,
     )
 
     keystore_filepath = credential.save_signing_keystore(keystore_password, changes_folder_path, time.time())
@@ -376,7 +381,8 @@ def test_invalid_custom_testnet() -> None:
         index=0,
         amount=0,
         chain_setting=get_devnet_chain_setting(**correct_devnet_chain),
-        hex_withdrawal_address=None
+        hex_withdrawal_address=None,
+        compounding=False,
     )
 
     keystore_filepath = credential.save_signing_keystore(keystore_password, changes_folder_path, time.time())
diff --git a/tests/test_cli/test_new_mnemonic.py b/tests/test_cli/test_new_mnemonic.py
index 091e4869..6c533e1f 100644
--- a/tests/test_cli/test_new_mnemonic.py
+++ b/tests/test_cli/test_new_mnemonic.py
@@ -5,6 +5,7 @@
 
 import pytest
 import inspect
+from decimal import Decimal
 from click.testing import CliRunner
 
 from eth_utils import decode_hex
@@ -16,6 +17,9 @@
     BLS_WITHDRAWAL_PREFIX,
     DEFAULT_VALIDATOR_KEYS_FOLDER_NAME,
     EXECUTION_ADDRESS_WITHDRAWAL_PREFIX,
+    COMPOUNDING_WITHDRAWAL_PREFIX,
+    MIN_ACTIVATION_AMOUNT,
+    ETH2GWEI,
 )
 from ethstaker_deposit.utils.intl import load_text
 from .helpers import clean_key_folder, get_permissions, get_uuid
@@ -90,7 +94,7 @@ def mock_get_mnemonic(language, words_path, entropy=None) -> str:
 
     runner = CliRunner()
     withdrawal_address = '0x00000000219ab540356cBB839Cbe05303d7705Fa'
-    inputs = ['english', '1', 'mainnet', 'MyPasswordIs', 'MyPasswordIs', withdrawal_address, withdrawal_address,
+    inputs = ['english', '1', 'mainnet', 'MyPasswordIs', 'MyPasswordIs', withdrawal_address, withdrawal_address, '',
               'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about', '']
     data = '\n'.join(inputs)
     arguments = [
@@ -114,6 +118,189 @@ def mock_get_mnemonic(language, words_path, entropy=None) -> str:
         assert withdrawal_credentials == (
             EXECUTION_ADDRESS_WITHDRAWAL_PREFIX + b'\x00' * 11 + decode_hex(withdrawal_address)
         )
+        amount = deposit['amount']
+        assert amount == MIN_ACTIVATION_AMOUNT
+
+    all_uuid = [
+        get_uuid(validator_keys_folder_path + '/' + key_file)
+        for key_file in key_files
+        if key_file.startswith('keystore')
+    ]
+    assert len(set(all_uuid)) == 1
+
+    # Verify file permissions
+    if os.name == 'posix':
+        for file_name in key_files:
+            assert get_permissions(validator_keys_folder_path, file_name) == '0o400'
+
+    # Clean up
+    clean_key_folder(my_folder_path)
+
+
+def test_new_mnemonic_compounding_validators(monkeypatch) -> None:
+    # monkeypatch get_mnemonic
+    def mock_get_mnemonic(language, words_path, entropy=None) -> str:
+        return "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
+
+    monkeypatch.setattr(new_mnemonic, "get_mnemonic", mock_get_mnemonic)
+
+    # Prepare folder
+    my_folder_path = os.path.join(os.getcwd(), 'TESTING_TEMP_FOLDER')
+    clean_key_folder(my_folder_path)
+    if not os.path.exists(my_folder_path):
+        os.mkdir(my_folder_path)
+
+    runner = CliRunner()
+    withdrawal_address = '0x00000000219ab540356cBB839Cbe05303d7705Fa'
+    inputs = ['english', '1', 'mainnet', 'MyPasswordIs', 'MyPasswordIs', withdrawal_address, withdrawal_address,
+              'yes', '',
+              'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about', '']
+    data = '\n'.join(inputs)
+    arguments = [
+        '--language', 'english',
+        '--ignore_connectivity',
+        'new-mnemonic',
+        '--folder', my_folder_path,
+    ]
+    result = runner.invoke(cli, arguments, input=data)
+    assert result.exit_code == 0
+
+    # Check files
+    validator_keys_folder_path = os.path.join(my_folder_path, DEFAULT_VALIDATOR_KEYS_FOLDER_NAME)
+    _, _, key_files = next(os.walk(validator_keys_folder_path))
+
+    deposit_file = [key_file for key_file in key_files if key_file.startswith('deposit_data')][0]
+    with open(validator_keys_folder_path + '/' + deposit_file, 'r', encoding='utf-8') as f:
+        deposits_dict = json.load(f)
+    for deposit in deposits_dict:
+        withdrawal_credentials = bytes.fromhex(deposit['withdrawal_credentials'])
+        assert withdrawal_credentials == (
+            COMPOUNDING_WITHDRAWAL_PREFIX + b'\x00' * 11 + decode_hex(withdrawal_address)
+        )
+        amount = deposit['amount']
+        assert amount == MIN_ACTIVATION_AMOUNT
+
+    all_uuid = [
+        get_uuid(validator_keys_folder_path + '/' + key_file)
+        for key_file in key_files
+        if key_file.startswith('keystore')
+    ]
+    assert len(set(all_uuid)) == 1
+
+    # Verify file permissions
+    if os.name == 'posix':
+        for file_name in key_files:
+            assert get_permissions(validator_keys_folder_path, file_name) == '0o400'
+
+    # Clean up
+    clean_key_folder(my_folder_path)
+
+
+def test_new_mnemonic_compounding_custom_amount(monkeypatch) -> None:
+    # monkeypatch get_mnemonic
+    def mock_get_mnemonic(language, words_path, entropy=None) -> str:
+        return "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
+
+    monkeypatch.setattr(new_mnemonic, "get_mnemonic", mock_get_mnemonic)
+
+    # Prepare folder
+    my_folder_path = os.path.join(os.getcwd(), 'TESTING_TEMP_FOLDER')
+    clean_key_folder(my_folder_path)
+    if not os.path.exists(my_folder_path):
+        os.mkdir(my_folder_path)
+
+    custom_amount = 1050
+
+    runner = CliRunner()
+    withdrawal_address = '0x00000000219ab540356cBB839Cbe05303d7705Fa'
+    inputs = ['english', '1', 'mainnet', 'MyPasswordIs', 'MyPasswordIs', withdrawal_address, withdrawal_address,
+              'yes', str(custom_amount),
+              'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about', '']
+    data = '\n'.join(inputs)
+    arguments = [
+        '--language', 'english',
+        '--ignore_connectivity',
+        'new-mnemonic',
+        '--folder', my_folder_path,
+    ]
+    result = runner.invoke(cli, arguments, input=data)
+    assert result.exit_code == 0
+
+    # Check files
+    validator_keys_folder_path = os.path.join(my_folder_path, DEFAULT_VALIDATOR_KEYS_FOLDER_NAME)
+    _, _, key_files = next(os.walk(validator_keys_folder_path))
+
+    deposit_file = [key_file for key_file in key_files if key_file.startswith('deposit_data')][0]
+    with open(validator_keys_folder_path + '/' + deposit_file, 'r', encoding='utf-8') as f:
+        deposits_dict = json.load(f)
+    for deposit in deposits_dict:
+        withdrawal_credentials = bytes.fromhex(deposit['withdrawal_credentials'])
+        assert withdrawal_credentials == (
+            COMPOUNDING_WITHDRAWAL_PREFIX + b'\x00' * 11 + decode_hex(withdrawal_address)
+        )
+        amount = deposit['amount']
+        assert amount == (custom_amount * ETH2GWEI)
+
+    all_uuid = [
+        get_uuid(validator_keys_folder_path + '/' + key_file)
+        for key_file in key_files
+        if key_file.startswith('keystore')
+    ]
+    assert len(set(all_uuid)) == 1
+
+    # Verify file permissions
+    if os.name == 'posix':
+        for file_name in key_files:
+            assert get_permissions(validator_keys_folder_path, file_name) == '0o400'
+
+    # Clean up
+    clean_key_folder(my_folder_path)
+
+
+def test_new_mnemonic_compounding_custom_amount_decimal(monkeypatch) -> None:
+    # monkeypatch get_mnemonic
+    def mock_get_mnemonic(language, words_path, entropy=None) -> str:
+        return "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
+
+    monkeypatch.setattr(new_mnemonic, "get_mnemonic", mock_get_mnemonic)
+
+    # Prepare folder
+    my_folder_path = os.path.join(os.getcwd(), 'TESTING_TEMP_FOLDER')
+    clean_key_folder(my_folder_path)
+    if not os.path.exists(my_folder_path):
+        os.mkdir(my_folder_path)
+
+    custom_amount = Decimal('1120.25')
+
+    runner = CliRunner()
+    withdrawal_address = '0x00000000219ab540356cBB839Cbe05303d7705Fa'
+    inputs = ['english', '1', 'mainnet', 'MyPasswordIs', 'MyPasswordIs', withdrawal_address, withdrawal_address,
+              'yes', str(custom_amount),
+              'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about', '']
+    data = '\n'.join(inputs)
+    arguments = [
+        '--language', 'english',
+        '--ignore_connectivity',
+        'new-mnemonic',
+        '--folder', my_folder_path,
+    ]
+    result = runner.invoke(cli, arguments, input=data)
+    assert result.exit_code == 0
+
+    # Check files
+    validator_keys_folder_path = os.path.join(my_folder_path, DEFAULT_VALIDATOR_KEYS_FOLDER_NAME)
+    _, _, key_files = next(os.walk(validator_keys_folder_path))
+
+    deposit_file = [key_file for key_file in key_files if key_file.startswith('deposit_data')][0]
+    with open(validator_keys_folder_path + '/' + deposit_file, 'r', encoding='utf-8') as f:
+        deposits_dict = json.load(f)
+    for deposit in deposits_dict:
+        withdrawal_credentials = bytes.fromhex(deposit['withdrawal_credentials'])
+        assert withdrawal_credentials == (
+            COMPOUNDING_WITHDRAWAL_PREFIX + b'\x00' * 11 + decode_hex(withdrawal_address)
+        )
+        amount = deposit['amount']
+        assert amount == int(custom_amount * ETH2GWEI)
 
     all_uuid = [
         get_uuid(validator_keys_folder_path + '/' + key_file)
@@ -151,7 +338,7 @@ def mock_get_mnemonic(language, words_path, entropy=None) -> str:
     correct_withdrawal_address = '0x00000000219ab540356cBB839Cbe05303d7705Fa'
 
     inputs = ['english', '1', 'mainnet', 'MyPasswordIs', 'MyPasswordIs',
-              wrong_withdrawal_address, correct_withdrawal_address, correct_withdrawal_address,
+              wrong_withdrawal_address, correct_withdrawal_address, correct_withdrawal_address, '',
               'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about', '']
     data = '\n'.join(inputs)
     arguments = [
@@ -207,7 +394,7 @@ def mock_get_mnemonic(language, words_path, entropy=None) -> str:
 
     runner = CliRunner()
     withdrawal_address = '0x00000000219ab540356cBB839Cbe05303d7705Fa'
-    inputs = [withdrawal_address, 'english', '1', 'mainnet', 'MyPasswordIs', 'MyPasswordIs',
+    inputs = [withdrawal_address, 'english', '1', 'mainnet', 'MyPasswordIs', 'MyPasswordIs', '',
               'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about', '']
     data = '\n'.join(inputs)
     arguments = [
@@ -265,7 +452,7 @@ def mock_get_mnemonic(language, words_path, entropy=None) -> str:
 
     runner = CliRunner()
     withdrawal_address = '0x00000000219ab540356cBB839Cbe05303d7705Fa'
-    inputs = [withdrawal_address, 'english', '1', 'mainnet', 'MyPasswordIs', 'MyPasswordIs',
+    inputs = [withdrawal_address, 'english', '1', 'mainnet', 'MyPasswordIs', 'MyPasswordIs', '',
               'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about', '']
     data = '\n'.join(inputs)
     arguments = [
@@ -323,7 +510,7 @@ def mock_get_mnemonic(language, words_path, entropy=None) -> str:
 
     runner = CliRunner()
     withdrawal_address = '0x00000000219ab540356cBB839Cbe05303d7705Fa'
-    inputs = [withdrawal_address, 'english', '1', 'mainnet', 'MyPasswordIs', 'MyPasswordIs',
+    inputs = [withdrawal_address, 'english', '1', 'mainnet', 'MyPasswordIs', 'MyPasswordIs', '',
               'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about', '']
     data = '\n'.join(inputs)
     arguments = [
diff --git a/tests/test_cli/test_partial_deposit.py b/tests/test_cli/test_partial_deposit.py
index 2fa84c20..685a317e 100644
--- a/tests/test_cli/test_partial_deposit.py
+++ b/tests/test_cli/test_partial_deposit.py
@@ -3,9 +3,10 @@
 import pytest
 import time
 
+from decimal import Decimal
 from click.testing import CliRunner
 
-from eth_utils import to_normalized_address
+from eth_utils import to_normalized_address, decode_hex
 
 from ethstaker_deposit.credentials import Credential
 from ethstaker_deposit.deposit import cli
@@ -13,6 +14,10 @@
 from ethstaker_deposit.utils.constants import (
     DEFAULT_PARTIAL_DEPOSIT_FOLDER_NAME,
     DEFAULT_VALIDATOR_KEYS_FOLDER_NAME,
+    EXECUTION_ADDRESS_WITHDRAWAL_PREFIX,
+    COMPOUNDING_WITHDRAWAL_PREFIX,
+    MIN_ACTIVATION_AMOUNT,
+    ETH2GWEI,
 )
 from .helpers import clean_folder, clean_key_folder, clean_partial_deposit_folder, get_permissions
 
@@ -45,12 +50,13 @@ def test_partial_deposit(amount: str) -> None:
                             index=0,
                             amount=32000000000,
                             chain_setting=chain_settings,
-                            hex_withdrawal_address=to_normalized_address(withdrawal_address))
+                            hex_withdrawal_address=to_normalized_address(withdrawal_address),
+                            compounding=False)
 
     keystore_file_folder = credential.save_signing_keystore(password, partial_deposit_folder, time.time())
 
     runner = CliRunner()
-    inputs = ['english', 'mainnet', password, amount, withdrawal_address, withdrawal_address]
+    inputs = ['english', 'mainnet', password, amount, withdrawal_address, withdrawal_address, '']
     data = '\n'.join(inputs)
     arguments = [
         '--ignore_connectivity',
@@ -67,6 +73,76 @@ def test_partial_deposit(amount: str) -> None:
 
     assert len(deposit_files) == 1
 
+    deposit_file = deposit_files[0]
+    with open(partial_deposit_folder + '/' + deposit_file, 'r', encoding='utf-8') as f:
+        deposits_dict = json.load(f)
+    for deposit in deposits_dict:
+        withdrawal_credentials = bytes.fromhex(deposit['withdrawal_credentials'])
+        assert withdrawal_credentials == (
+            EXECUTION_ADDRESS_WITHDRAWAL_PREFIX + b'\x00' * 11 + decode_hex(withdrawal_address)
+        )
+        result_amount = deposit['amount']
+        assert result_amount == int(Decimal(amount) * ETH2GWEI)
+
+    if os.name == 'posix':
+        assert get_permissions(partial_deposit_folder, deposit_files[0]) == '0o400'
+
+    clean_partial_deposit_folder(my_folder_path)
+
+
+def test_partial_deposit_compounding() -> None:
+    my_folder_path = os.path.join(os.getcwd(), 'TESTING_TEMP_FOLDER')
+    partial_deposit_folder = os.path.join(my_folder_path, DEFAULT_PARTIAL_DEPOSIT_FOLDER_NAME)
+    clean_partial_deposit_folder(my_folder_path)
+    if not os.path.exists(my_folder_path):
+        os.mkdir(my_folder_path)
+    if not os.path.exists(partial_deposit_folder):
+        os.mkdir(partial_deposit_folder)
+
+    chain_settings = get_chain_setting()
+    password = "MyPasswordIs"
+    withdrawal_address = "0xcd60A5f152724480c3a95E4Ff4dacEEf4074854d"
+    mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
+
+    credential = Credential(mnemonic=mnemonic,
+                            mnemonic_password="",
+                            index=0,
+                            amount=32000000000,
+                            chain_setting=chain_settings,
+                            hex_withdrawal_address=to_normalized_address(withdrawal_address),
+                            compounding=False)
+
+    keystore_file_folder = credential.save_signing_keystore(password, partial_deposit_folder, time.time())
+
+    runner = CliRunner()
+    inputs = ['english', 'mainnet', password, '32', withdrawal_address, withdrawal_address, 'yes']
+    data = '\n'.join(inputs)
+    arguments = [
+        '--ignore_connectivity',
+        'partial-deposit',
+        '--keystore', keystore_file_folder,
+        '--output_folder', my_folder_path,
+    ]
+    result = runner.invoke(cli, arguments, input=data)
+    assert result.exit_code == 0
+
+    _, _, folder_files = next(os.walk(partial_deposit_folder))
+
+    deposit_files = [deposit_file for deposit_file in folder_files if deposit_file.startswith('deposit')]
+
+    assert len(deposit_files) == 1
+
+    deposit_file = deposit_files[0]
+    with open(partial_deposit_folder + '/' + deposit_file, 'r', encoding='utf-8') as f:
+        deposits_dict = json.load(f)
+    for deposit in deposits_dict:
+        withdrawal_credentials = bytes.fromhex(deposit['withdrawal_credentials'])
+        assert withdrawal_credentials == (
+            COMPOUNDING_WITHDRAWAL_PREFIX + b'\x00' * 11 + decode_hex(withdrawal_address)
+        )
+        amount = deposit['amount']
+        assert amount == MIN_ACTIVATION_AMOUNT
+
     if os.name == 'posix':
         assert get_permissions(partial_deposit_folder, deposit_files[0]) == '0o400'
 
@@ -111,7 +187,7 @@ def test_partial_deposit_matches_existing_mnemonic_deposit() -> None:
     assert len(deposit_files) == 1
     key_file_location = os.path.join(validator_key_folder, key_files[0])
 
-    inputs = ['english', 'mainnet', password, "32", withdrawal_address, withdrawal_address]
+    inputs = ['english', 'mainnet', password, "32", withdrawal_address, withdrawal_address, '']
     data = '\n'.join(inputs)
     arguments = [
         '--ignore_connectivity',
@@ -181,7 +257,7 @@ def test_partial_deposit_does_not_match_if_amount_differs() -> None:
     assert len(deposit_files) == 1
     key_file_location = os.path.join(validator_key_folder, key_files[0])
 
-    inputs = ['english', 'mainnet', password, "33", withdrawal_address, withdrawal_address]
+    inputs = ['english', 'mainnet', password, "33", withdrawal_address, withdrawal_address, '']
     data = '\n'.join(inputs)
     arguments = [
         '--ignore_connectivity',
@@ -257,12 +333,13 @@ def test_partial_deposit_custom_network(amount: str) -> None:
                             index=0,
                             amount=32000000000,
                             chain_setting=get_devnet_chain_setting(**devnet_chain),
-                            hex_withdrawal_address=to_normalized_address(withdrawal_address))
+                            hex_withdrawal_address=to_normalized_address(withdrawal_address),
+                            compounding=False)
 
     keystore_file_folder = credential.save_signing_keystore(password, partial_deposit_folder, time.time())
 
     runner = CliRunner()
-    inputs = ['english', password, amount, withdrawal_address, withdrawal_address]
+    inputs = ['english', password, amount, withdrawal_address, withdrawal_address, '']
     data = '\n'.join(inputs)
     arguments = [
         '--ignore_connectivity',
@@ -312,12 +389,13 @@ def test_invalid_custom_network_json() -> None:
                             index=0,
                             amount=32000000000,
                             chain_setting=get_devnet_chain_setting(**devnet_chain),
-                            hex_withdrawal_address=to_normalized_address(withdrawal_address))
+                            hex_withdrawal_address=to_normalized_address(withdrawal_address),
+                            compounding=False)
 
     keystore_file_folder = credential.save_signing_keystore(password, partial_deposit_folder, time.time())
 
     runner = CliRunner()
-    inputs = ['english', password, '32', withdrawal_address, withdrawal_address]
+    inputs = ['english', password, '32', withdrawal_address, withdrawal_address, '']
     data = '\n'.join(inputs)
     arguments = [
         '--ignore_connectivity',
diff --git a/tests/test_cli/test_test_keystore.py b/tests/test_cli/test_test_keystore.py
index e24727ba..4dc1a347 100644
--- a/tests/test_cli/test_test_keystore.py
+++ b/tests/test_cli/test_test_keystore.py
@@ -30,7 +30,8 @@ def test_exit_transaction_keystore() -> None:
         index=0,
         amount=0,
         chain_setting=get_chain_setting('mainnet'),
-        hex_withdrawal_address=None
+        hex_withdrawal_address=None,
+        compounding=False,
     )
 
     # Save keystore file
diff --git a/tests/test_credentials.py b/tests/test_credentials.py
index ccbcf767..eab2b9be 100644
--- a/tests/test_credentials.py
+++ b/tests/test_credentials.py
@@ -14,4 +14,5 @@ def test_from_mnemonic() -> None:
             chain_setting=MainnetSetting,
             start_index=1,
             hex_withdrawal_address=None,
+            compounding=False,
         )
diff --git a/tests/test_utils/test_validation.py b/tests/test_utils/test_validation.py
index 1350fa09..78406581 100644
--- a/tests/test_utils/test_validation.py
+++ b/tests/test_utils/test_validation.py
@@ -10,7 +10,7 @@
 from ethstaker_deposit.utils.validation import (
     normalize_input_list,
     validate_int_range,
-    validate_partial_deposit_amount,
+    validate_deposit_amount,
     validate_password_strength,
     validate_signed_exit,
     validate_devnet_chain_setting_json,
@@ -100,12 +100,12 @@ def test_validate_int_range(num: Any, low: int, high: int, valid: bool) -> None:
         (' ', False)
     ]
 )
-def test_validate_partial_deposit_amount(amount: str, valid: bool) -> None:
+def test_validate_deposit_amount(amount: str, valid: bool) -> None:
     if valid:
-        validate_partial_deposit_amount(amount)
+        validate_deposit_amount(amount)
     else:
         with pytest.raises(ValidationError):
-            validate_partial_deposit_amount(amount)
+            validate_deposit_amount(amount)
 
 
 @pytest.mark.parametrize(