diff --git a/pocketbook/address_book.py b/pocketbook/address_book.py
index f39fd4c..aba8bbb 100644
--- a/pocketbook/address_book.py
+++ b/pocketbook/address_book.py
@@ -20,6 +20,24 @@ class AddressBook:
         self._address_book[name] = str(address)
         self._save()
 
+    def rename(self, old: str, new: str) -> bool:
+        """
+        Rename an address from one name to another
+
+        :param old: The old name for the address
+        :param new: The new name for the address
+        :return: True if successful, otherwise False
+        """
+        success = False
+
+        if old in self._address_book and new not in self._address_book:
+            self._address_book[new] = self._address_book[old]
+            del self._address_book[old]
+            self._save()
+            success = True
+
+        return success
+
     def keys(self):
         return self._address_book.keys()
 
diff --git a/pocketbook/cli.py b/pocketbook/cli.py
index c12b74c..bf1de02 100644
--- a/pocketbook/cli.py
+++ b/pocketbook/cli.py
@@ -9,6 +9,7 @@ from .commands.create import run_create
 from .commands.display import run_display
 from .commands.list import run_list
 from .commands.transfer import run_transfer
+from .commands.rename import run_rename
 from .disclaimer import display_disclaimer
 from .utils import NetworkUnavailableError
 
@@ -43,6 +44,11 @@ def parse_commandline():
     parser_transfer.add_argument('signers', nargs='+', help='The series of key names needed to sign the transaction')
     parser_transfer.set_defaults(handler=run_transfer)
 
+    parser_rename = subparsers.add_parser('rename', aliases=['mv'])
+    parser_rename.add_argument('old', help='The name of the old account name')
+    parser_rename.add_argument('new', help='The new name of the account')
+    parser_rename.set_defaults(handler=run_rename)
+
     return parser, parser.parse_args()
 
 
@@ -65,9 +71,13 @@ def main():
         try:
 
             # execute the handler
-            handler(args)
+            ret = handler(args)
 
-            exit_code = 0
+            # update the exit code based on the return value
+            if isinstance(ret, int):
+                exit_code = ret
+            else:
+                exit_code = 0
 
         except NetworkUnavailableError:
             print('The network appears to be unavailable at the moment. Please try again later')
diff --git a/pocketbook/commands/rename.py b/pocketbook/commands/rename.py
new file mode 100644
index 0000000..e395c0f
--- /dev/null
+++ b/pocketbook/commands/rename.py
@@ -0,0 +1,33 @@
+def run_rename(args):
+    from pocketbook.address_book import AddressBook
+    from pocketbook.key_store import KeyStore
+
+    address_book = AddressBook()
+    key_store = KeyStore()
+
+    # make sure that the new name is not present either as a key, or as an address
+    new_present = args.new in address_book.keys() or args.new in key_store.list_keys()
+    if new_present:
+        print('{} is already present, please choose a different destination name'.format(args.new))
+        return 1
+
+    # check the old address or key name
+    old_is_address = args.old in address_book.keys()
+    old_is_key = args.old in key_store.list_keys()
+
+    success = False
+    if old_is_address and old_is_key:
+        raise RuntimeError('Data store corrupting, key looks like an address + key')
+    elif old_is_address:
+        success = address_book.rename(args.old, args.new)
+    elif old_is_key:
+        success = key_store.rename_key(args.old, args.new)
+    else:
+        print('{} doesn\'t appear to be a valid key or address name, please check and try again'.format(args.old))
+        return 1
+
+    if not success:
+        print('Failed to rename {} to {}'.format(args.old, args.new))
+        return 1
+
+    return 0
diff --git a/pocketbook/key_store.py b/pocketbook/key_store.py
index 7f8b942..88cec46 100644
--- a/pocketbook/key_store.py
+++ b/pocketbook/key_store.py
@@ -1,4 +1,5 @@
 import os
+from copy import deepcopy
 
 import toml
 from fetchai.ledger.crypto import Entity, Address
@@ -87,6 +88,35 @@ class KeyStore:
         })
         self._flush_index()
 
+    def rename_key(self, old_name: str, new_name: str) -> bool:
+
+        # do some basic checks
+        if old_name not in self.list_keys():
+            return False
+        if new_name in self.list_keys():
+            return False
+
+        # extract the old key metadata
+        old_metadata = self._lookup_meta_data(old_name)
+        if old_metadata is None:
+            return False
+
+        # move the key file
+        old_key_file_path = self._format_key_path(old_name)
+        new_key_file_path = self._format_key_path(new_name)
+        os.rename(old_key_file_path, new_key_file_path)
+
+        # remove the old entries to the metadata
+        self._index['key'] = list(filter(lambda x: x['name'] != old_name, self._index['key']))
+
+        # update and insert the new meta data into place
+        new_metadata = deepcopy(old_metadata)
+        new_metadata['name'] = new_name
+        self._index['key'].append(new_metadata)
+        self._flush_index()
+
+        return True
+
     def _format_key_path(self, name: str):
         return os.path.join(self._root, '{}.key'.format(name))
 

Add feature to rename account
Take this example:
```
Wallet2                  key  9.0            0.0   
Wallet 3                 key  15.0           0.0   
```
... opps. Wallet 3 was meant to be Wallet3. Propose:

```
pocketbook rename "Wallet 3" "Wallet3"
```

diff --git a/tests/commands/test_rename.py b/tests/commands/test_rename.py
new file mode 100644
index 0000000..f9515c7
--- /dev/null
+++ b/tests/commands/test_rename.py
@@ -0,0 +1,147 @@
+import unittest
+from unittest.mock import patch, Mock
+
+from pocketbook.address_book import AddressBook
+from pocketbook.key_store import KeyStore
+
+
+class RenameCommandTests(unittest.TestCase):
+
+    @patch('pocketbook.address_book.AddressBook', spec=AddressBook)
+    @patch('pocketbook.key_store.KeyStore', spec=KeyStore)
+    def test_normal_key_rename(self, MockKeyStore, MockAddressBook):
+        key_store = MockKeyStore()
+        key_store.list_keys.return_value = ['old-name']
+
+        address_book = MockAddressBook()
+        address_book.keys.return_value = []
+
+        args = Mock()
+        args.old = 'old-name'
+        args.new = 'new-name'
+
+        from pocketbook.commands.rename import run_rename
+        self.assertEqual(run_rename(args), 0)
+
+        key_store.rename_key.assert_called_once_with('old-name', 'new-name')
+
+    @patch('pocketbook.address_book.AddressBook', spec=AddressBook)
+    @patch('pocketbook.key_store.KeyStore', spec=KeyStore)
+    def test_normal_address_rename(self, MockKeyStore, MockAddressBook):
+        key_store = MockKeyStore()
+        key_store.list_keys.return_value = []
+
+        address_book = MockAddressBook()
+        address_book.keys.return_value = ['old-name']
+
+        args = Mock()
+        args.old = 'old-name'
+        args.new = 'new-name'
+
+        from pocketbook.commands.rename import run_rename
+        self.assertEqual(run_rename(args), 0)
+
+        address_book.rename.assert_called_once_with('old-name', 'new-name')
+
+    @patch('pocketbook.address_book.AddressBook', spec=AddressBook)
+    @patch('pocketbook.key_store.KeyStore', spec=KeyStore)
+    def test_doesnt_exits(self, MockKeyStore, MockAddressBook):
+        key_store = MockKeyStore()
+        key_store.list_keys.return_value = []
+
+        address_book = MockAddressBook()
+        address_book.keys.return_value = []
+
+        args = Mock()
+        args.old = 'old-name'
+        args.new = 'new-name'
+
+        from pocketbook.commands.rename import run_rename
+        self.assertEqual(run_rename(args), 1)
+
+    @patch('pocketbook.address_book.AddressBook', spec=AddressBook)
+    @patch('pocketbook.key_store.KeyStore', spec=KeyStore)
+    def test_duplicate_destination_key_name(self, MockKeyStore, MockAddressBook):
+        key_store = MockKeyStore()
+        key_store.list_keys.return_value = ['new-name']
+
+        address_book = MockAddressBook()
+        address_book.keys.return_value = []
+
+        args = Mock()
+        args.old = 'old-name'
+        args.new = 'new-name'
+
+        from pocketbook.commands.rename import run_rename
+        self.assertEqual(run_rename(args), 1)
+
+    @patch('pocketbook.address_book.AddressBook', spec=AddressBook)
+    @patch('pocketbook.key_store.KeyStore', spec=KeyStore)
+    def test_duplicate_destination_address_name(self, MockKeyStore, MockAddressBook):
+        key_store = MockKeyStore()
+        key_store.list_keys.return_value = []
+
+        address_book = MockAddressBook()
+        address_book.keys.return_value = ['new-name']
+
+        args = Mock()
+        args.old = 'old-name'
+        args.new = 'new-name'
+
+        from pocketbook.commands.rename import run_rename
+        self.assertEqual(run_rename(args), 1)
+
+    @patch('pocketbook.address_book.AddressBook', spec=AddressBook)
+    @patch('pocketbook.key_store.KeyStore', spec=KeyStore)
+    def test_corrupted_db_duplicate_address_key(self, MockKeyStore, MockAddressBook):
+        key_store = MockKeyStore()
+        key_store.list_keys.return_value = ['old-name']
+
+        address_book = MockAddressBook()
+        address_book.keys.return_value = ['old-name']
+
+        args = Mock()
+        args.old = 'old-name'
+        args.new = 'new-name'
+
+        from pocketbook.commands.rename import run_rename
+        with self.assertRaises(RuntimeError):
+            run_rename(args)
+
+    @patch('pocketbook.address_book.AddressBook', spec=AddressBook)
+    @patch('pocketbook.key_store.KeyStore', spec=KeyStore)
+    def test_handle_address_rename_failure(self, MockKeyStore, MockAddressBook):
+        key_store = MockKeyStore()
+        key_store.list_keys.return_value = []
+
+        address_book = MockAddressBook()
+        address_book.keys.return_value = ['old-name']
+        address_book.rename.return_value = False
+
+        args = Mock()
+        args.old = 'old-name'
+        args.new = 'new-name'
+
+        from pocketbook.commands.rename import run_rename
+        self.assertEqual(run_rename(args), 1)
+
+        address_book.rename.assert_called_once_with('old-name', 'new-name')
+
+    @patch('pocketbook.address_book.AddressBook', spec=AddressBook)
+    @patch('pocketbook.key_store.KeyStore', spec=KeyStore)
+    def test_handle_key_rename_failure(self, MockKeyStore, MockAddressBook):
+        key_store = MockKeyStore()
+        key_store.list_keys.return_value = ['old-name']
+        key_store.rename_key.return_value = False
+
+        address_book = MockAddressBook()
+        address_book.keys.return_value = []
+
+        args = Mock()
+        args.old = 'old-name'
+        args.new = 'new-name'
+
+        from pocketbook.commands.rename import run_rename
+        self.assertEqual(run_rename(args), 1)
+
+        key_store.rename_key.assert_called_once_with('old-name', 'new-name')
diff --git a/tests/test_address_book.py b/tests/test_address_book.py
index 10e616f..694db76 100644
--- a/tests/test_address_book.py
+++ b/tests/test_address_book.py
@@ -77,3 +77,24 @@ class AddressBookTests(unittest.TestCase):
 
             address_book2 = AddressBook(root=ctx.root)
             self.assertEqual(set(address_book1.items()), set(address_book2.items()))
+
+    def test_rename(self):
+        with TemporaryPocketBookRoot() as ctx:
+            address_book1 = AddressBook(root=ctx.root)
+            address_book1.add('sample', SAMPLE_ADDRESS)
+            self.assertTrue(address_book1.rename('sample', 'sample2'))
+            self.assertIn('sample2', address_book1.keys())
+            self.assertNotIn('sample', address_book1.keys())
+
+    def test_rename_occurs_on_disk(self):
+        with TemporaryPocketBookRoot() as ctx:
+            address_book1 = AddressBook(root=ctx.root)
+            address_book1.add('sample', SAMPLE_ADDRESS)
+
+            self.assertTrue(address_book1.rename('sample', 'sample2'))
+            self.assertAddressIsPresentOnDisk('sample2', SAMPLE_ADDRESS, ctx)
+
+    def test_rename_fails(self):
+        with TemporaryPocketBookRoot() as ctx:
+            address_book1 = AddressBook(root=ctx.root)
+            self.assertFalse(address_book1.rename('sample', 'sample2'))
diff --git a/tests/test_key_store.py b/tests/test_key_store.py
index a3b035e..b0ed8b7 100644
--- a/tests/test_key_store.py
+++ b/tests/test_key_store.py
@@ -1,5 +1,6 @@
 import os
 import unittest
+from unittest.mock import patch
 
 import toml
 from fetchai.ledger.crypto import Entity, Address
@@ -8,8 +9,7 @@ from pocketbook.key_store import KeyStore, DuplicateKeyNameError, KeyNotFoundErr
 from .utils import TemporaryPocketBookRoot, SUPER_SECURE_PASSWORD
 
 
-
-class AddressBookTests(unittest.TestCase):
+class KeyStoreTests(unittest.TestCase):
     def assertFlushedToDisk(self, name: str, password: str, entity: Entity, ctx: TemporaryPocketBookRoot):
         self.assertTrue(os.path.isdir(ctx.root))
 
@@ -126,4 +126,49 @@ class AddressBookTests(unittest.TestCase):
 
     def test_unable_to_decode_key_error_message(self):
         error = UnableToDecodeKeyError()
-        self.assertEqual(str(error), 'Unable to decode key from key store')
\ No newline at end of file
+        self.assertEqual(str(error), 'Unable to decode key from key store')
+
+    def test_rename(self):
+        with TemporaryPocketBookRoot() as ctx:
+            entity = Entity()
+            key_store = KeyStore(root=ctx.root)
+            key_store.add_key('sample', SUPER_SECURE_PASSWORD, entity)
+
+            self.assertTrue(key_store.rename_key('sample', 'sample2'))
+            self.assertIn('sample2', key_store.list_keys())
+            self.assertNotIn('sample', key_store.list_keys())
+
+    def test_rename_is_persisted_to_disk(self):
+        with TemporaryPocketBookRoot() as ctx:
+            entity = Entity()
+            key_store = KeyStore(root=ctx.root)
+            key_store.add_key('sample', SUPER_SECURE_PASSWORD, entity)
+
+            self.assertTrue(key_store.rename_key('sample', 'sample2'))
+            self.assertFlushedToDisk('sample2', SUPER_SECURE_PASSWORD, entity, ctx)
+
+    def test_rename_failure_doesnt_exist(self):
+        with TemporaryPocketBookRoot() as ctx:
+            key_store = KeyStore(root=ctx.root)
+            self.assertFalse(key_store.rename_key('sample', 'sample2'))
+
+    def test_rename_failure_already_exists(self):
+        with TemporaryPocketBookRoot() as ctx:
+            entity1 = Entity()
+            entity2 = Entity()
+
+            key_store = KeyStore(root=ctx.root)
+            key_store.add_key('sample1', SUPER_SECURE_PASSWORD, entity1)
+            key_store.add_key('sample2', SUPER_SECURE_PASSWORD, entity2)
+
+            self.assertFalse(key_store.rename_key('sample1', 'sample2'))
+
+    def test_corrupted_metadata(self):
+        with TemporaryPocketBookRoot() as ctx:
+            entity1 = Entity()
+
+            key_store = KeyStore(root=ctx.root)
+            key_store.add_key('sample1', SUPER_SECURE_PASSWORD, entity1)
+
+            with patch.object(key_store, '_lookup_meta_data', return_value=None):
+                self.assertFalse(key_store.rename_key('sample1', 'sample2'))
\ No newline at end of file

pip install -e .[dev]
parse_log_pytest
requirements.txt
pytest
apt-get update apt-get install -y gcc
3.7
requirements/base.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
base58==2.1.1
build==1.1.1
certifi @ file:///croot/certifi_1671487769961/work/certifi
charset-normalizer==3.4.1
check-manifest==0.50
colored==1.4.4
ecdsa==0.19.1
exceptiongroup==1.2.2
fetchai-ledger-api==1.0.0rc1
idna==3.10
importlib-metadata==6.7.0
iniconfig==2.0.0
lark-parser==0.12.0
msgpack==1.0.5
packaging==24.0
pluggy==1.2.0
-e git+https://github.com/fetchai/tools-pocketbook.git@2bc500686da01f7561e22613247edbed46233f3d#egg=pocketbook
pyaes==1.6.1
pyproject_hooks==1.2.0
pytest==7.4.4
requests==2.31.0
semver==3.0.4
six==1.17.0
toml==0.10.2
tomli==2.0.1
typing_extensions==4.7.1
urllib3==2.0.7
zipp==3.15.0

name: tools-pocketbook
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2022.12.7=py37h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=22.3.1=py37h06a4308_0
  - python=3.7.16=h7a1cb2a_0
  - readline=8.2=h5eee18b_0
  - setuptools=65.6.3=py37h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.38.4=py37h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - base58==2.1.1
      - build==1.1.1
      - charset-normalizer==3.4.1
      - check-manifest==0.50
      - colored==1.4.4
      - ecdsa==0.19.1
      - exceptiongroup==1.2.2
      - fetchai-ledger-api==1.0.0rc1
      - idna==3.10
      - importlib-metadata==6.7.0
      - iniconfig==2.0.0
      - lark-parser==0.12.0
      - msgpack==1.0.5
      - packaging==24.0
      - pluggy==1.2.0
      - pyaes==1.6.1
      - pyproject-hooks==1.2.0
      - pytest==7.4.4
      - requests==2.31.0
      - semver==3.0.4
      - six==1.17.0
      - toml==0.10.2
      - tomli==2.0.1
      - typing-extensions==4.7.1
      - urllib3==2.0.7
      - zipp==3.15.0
prefix: /opt/conda/envs/tools-pocketbook

tests/commands/test_rename.py::RenameCommandTests::test_corrupted_db_duplicate_address_key tests/commands/test_rename.py::RenameCommandTests::test_doesnt_exits tests/commands/test_rename.py::RenameCommandTests::test_duplicate_destination_address_name tests/commands/test_rename.py::RenameCommandTests::test_duplicate_destination_key_name tests/commands/test_rename.py::RenameCommandTests::test_handle_address_rename_failure tests/commands/test_rename.py::RenameCommandTests::test_handle_key_rename_failure tests/commands/test_rename.py::RenameCommandTests::test_normal_address_rename tests/commands/test_rename.py::RenameCommandTests::test_normal_key_rename tests/test_address_book.py::AddressBookTests::test_rename tests/test_address_book.py::AddressBookTests::test_rename_fails tests/test_address_book.py::AddressBookTests::test_rename_occurs_on_disk tests/test_key_store.py::KeyStoreTests::test_corrupted_metadata tests/test_key_store.py::KeyStoreTests::test_rename tests/test_key_store.py::KeyStoreTests::test_rename_failure_already_exists tests/test_key_store.py::KeyStoreTests::test_rename_failure_doesnt_exist tests/test_key_store.py::KeyStoreTests::test_rename_is_persisted_to_disk
tests/test_address_book.py::AddressBookTests::test_check_keys_exists tests/test_address_book.py::AddressBookTests::test_get_items tests/test_address_book.py::AddressBookTests::test_invalid_lookup_of_address tests/test_address_book.py::AddressBookTests::test_load_of_addresses tests/test_address_book.py::AddressBookTests::test_lookup_of_address tests/test_address_book.py::AddressBookTests::test_no_writing_on_empty_folder tests/test_address_book.py::AddressBookTests::test_simple_write tests/test_address_book.py::AddressBookTests::test_throw_on_duplicate_add tests/test_key_store.py::KeyStoreTests::test_add_key tests/test_key_store.py::KeyStoreTests::test_added_key_is_flushed_to_disk tests/test_key_store.py::KeyStoreTests::test_address_lookup tests/test_key_store.py::KeyStoreTests::test_duplicate_adds tests/test_key_store.py::KeyStoreTests::test_duplicate_key_error_message tests/test_key_store.py::KeyStoreTests::test_init tests/test_key_store.py::KeyStoreTests::test_key_not_found_error_message tests/test_key_store.py::KeyStoreTests::test_loading_invalid_key tests/test_key_store.py::KeyStoreTests::test_loading_of_multiple_stores tests/test_key_store.py::KeyStoreTests::test_loading_saved_key tests/test_key_store.py::KeyStoreTests::test_unable_to_decode_key_error_message tests/test_key_store.py::KeyStoreTests::test_unable_to_load_saved_key
pocketbook/commands/rename.py pocketbook/key_store.py pocketbook/cli.py pocketbook/address_book.py
pocketbook/commands/rename.py pocketbook/key_store.py pocketbook/cli.py pocketbook/address_book.py