From 7aada2d00c02870572728c9da94abf1f2518baf0 Mon Sep 17 00:00:00 2001 From: techdigger <72038289+techdigger@users.noreply.github.com> Date: Thu, 12 Nov 2026 15:49:10 +0000 Subject: [PATCH 1/2] Add Safe v1.5.0 canonical addresses safe_addresses.py lists contracts up to v1.4.1, so on a chain where only v1.5.0 is deployed the interactive mode fails to find a Safe singleton, fallback handler, proxy factory, MultiSend or SignMessageLib. Add the v1.5.0 canonical addresses (safe-deployments v1.5.0) first in each list. --- src/safe_cli/safe_addresses.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/safe_cli/safe_addresses.py b/src/safe_cli/safe_addresses.py index be138c13..3917f35d 100644 --- a/src/safe_cli/safe_addresses.py +++ b/src/safe_cli/safe_addresses.py @@ -30,6 +30,7 @@ def get_safe_contract_address(ethereum_client: EthereumClient) -> ChecksumAddres return _get_valid_contract( ethereum_client, [ + "0xFf51A5898e281Db6DfC7855790607438dF2ca44b", # v1.5.0 "0x41675C099F32341bf84BFc5382aF534df5C7461a", # v1.4.1 "0xd9Db270c1B5E3Bd161E8c8503c55cEABeE709552", # v1.3.0 "0x69f4D1788e39c87893C980c06EdF4b7f686e2938", # v1.3.0 @@ -41,6 +42,7 @@ def get_safe_l2_contract_address(ethereum_client: EthereumClient) -> ChecksumAdd return _get_valid_contract( ethereum_client, [ + "0xEdd160fEBBD92E350D4D398fb636302fccd67C7e", # v1.5.0 "0x29fcB43b46531BcA003ddC8FCB67FFE91900C762", # v1.4.1 "0x610fcA2e0279Fa1F8C00c8c2F71dF522AD469380", # v1.4.1 zkSync "0x3E5c63644E683549055b9Be8653de26E0B4CD36E", # v1.3.0 @@ -56,6 +58,7 @@ def get_default_fallback_handler_address( return _get_valid_contract( ethereum_client, [ + "0x3EfCBb83A4A7AfcB4F68D501E2c2203a38be77f4", # v1.5.0 "0xfd0732Dc9E303f09fCEf3a7388Ad10A83459Ec99", # v1.4.1 "0x9301E98DD367135f21bdF66f342A249c9D5F9069", # v1.4.1 zkSync "0xf48f2B2d2a534e402487b3ee7C18c33Aec0Fe5e4", # v1.3.0 @@ -69,6 +72,7 @@ def get_proxy_factory_address(ethereum_client: EthereumClient) -> ChecksumAddres return _get_valid_contract( ethereum_client, [ + "0x14F2982D601c9458F93bd70B218933A6f8165e7b", # v1.5.0 "0x4e1DCf7AD4e460CfD30791CCC4F9c8a4f820ec67", # v1.4.1 "0xc329D02fd8CB2fc13aa919005aF46320794a8629", # v1.4.1 zkSync "0xa6B71E26C5e0845f74c812102Ca7114b6a896AB2", # v1.3.0 @@ -82,6 +86,7 @@ def get_last_multisend_address(ethereum_client: EthereumClient) -> ChecksumAddre return _get_valid_contract( ethereum_client, [ + "0x218543288004CD07832472D464648173c77D7eB7", # v1.5.0 "0x38869bf66a61cF6bDB996A6aE40D5853Fd43B526", # v1.4.1 "0x309D0B190FeCCa8e1D5D8309a16F7e3CB133E885", # v1.4.1 zkSync "0xA238CBeb142c10Ef7Ad8442C6D1f9E89e07e7761", # v1.3.0 @@ -97,6 +102,7 @@ def get_last_multisend_call_only_address( return _get_valid_contract( ethereum_client, [ + "0xA83c336B20401Af773B6219BA5027174338D1836", # v1.5.0 "0x40A2aCCbd92BCA938b02010E17A5b8929b49130D", # v1.4.1 "0x0408EF011960d02349d50286D20531229BCef773", # v1.4.1 zkSync "0x40A2aCCbd92BCA938b02010E17A5b8929b49130D", # v1.3.0 @@ -112,6 +118,7 @@ def get_last_sign_message_lib_address( return _get_valid_contract( ethereum_client, [ + "0x4FfeF8222648872B3dE295Ba1e49110E61f5b5aa", # v1.5.0 "0xd53cd0aB83D845Ac265BE939c57F53AD838012c9", # v1.4.1 "0xAca1ec0a1A575CDCCF1DC3d5d296202Eb6061888", # v1.4.1 zkSync "0xA65387F16B013cf2Af4605Ad8aA5ec25a2cbA3a2", # v1.3.0 From 3ead7b1f70aa0110ed28491c021e13032d781928 Mon Sep 17 00:00:00 2001 From: techdigger <72038289+techdigger@users.noreply.github.com> Date: Thu, 12 Nov 2026 16:58:19 +0000 Subject: [PATCH 2/2] Use v1.5.0 only where no earlier version is deployed Listing v1.5.0 first made it the default on every network that has it: safe-creator would create v1.5.0 Safes, update would move old Safes straight to v1.5.0, and v1.4.1 Safes would be reported as not updated. List it last instead, so networks with an earlier version behave as before and networks with only v1.5.0 work. Add a test for both cases, for all seven lists. --- src/safe_cli/safe_addresses.py | 18 +++++++------ tests/test_safe_addresses.py | 47 ++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 8 deletions(-) diff --git a/src/safe_cli/safe_addresses.py b/src/safe_cli/safe_addresses.py index 3917f35d..3d3576bb 100644 --- a/src/safe_cli/safe_addresses.py +++ b/src/safe_cli/safe_addresses.py @@ -1,6 +1,8 @@ """ Get the correct addresses for the contracts by testing the deployment addresses using the RPC -Currently using Safe v1.4.1 when available, and 1.3.0 as fallback as they are compatible +Currently using Safe v1.4.1 when available, and 1.3.0 as fallback as they are compatible. +v1.5.0 is used only on networks where no earlier version is deployed, so nothing changes elsewhere. +https://github.com/safe-global/safe-deployments/tree/main/src/assets/v1.5.0 https://github.com/gnosis/safe-deployments/tree/main/src/assets/v1.4.1 https://github.com/gnosis/safe-deployments/tree/main/src/assets/v1.3.0 """ @@ -30,10 +32,10 @@ def get_safe_contract_address(ethereum_client: EthereumClient) -> ChecksumAddres return _get_valid_contract( ethereum_client, [ - "0xFf51A5898e281Db6DfC7855790607438dF2ca44b", # v1.5.0 "0x41675C099F32341bf84BFc5382aF534df5C7461a", # v1.4.1 "0xd9Db270c1B5E3Bd161E8c8503c55cEABeE709552", # v1.3.0 "0x69f4D1788e39c87893C980c06EdF4b7f686e2938", # v1.3.0 + "0xFf51A5898e281Db6DfC7855790607438dF2ca44b", # v1.5.0, where no earlier version is deployed ], ) @@ -42,12 +44,12 @@ def get_safe_l2_contract_address(ethereum_client: EthereumClient) -> ChecksumAdd return _get_valid_contract( ethereum_client, [ - "0xEdd160fEBBD92E350D4D398fb636302fccd67C7e", # v1.5.0 "0x29fcB43b46531BcA003ddC8FCB67FFE91900C762", # v1.4.1 "0x610fcA2e0279Fa1F8C00c8c2F71dF522AD469380", # v1.4.1 zkSync "0x3E5c63644E683549055b9Be8653de26E0B4CD36E", # v1.3.0 "0xfb1bffC9d739B8D520DaF37dF666da4C687191EA", # v1.3.0 "0x1727c2c531cf966f902E5927b98490fDFb3b2b70", # v1.3.0 zkSync + "0xEdd160fEBBD92E350D4D398fb636302fccd67C7e", # v1.5.0, where no earlier version is deployed ], ) @@ -58,12 +60,12 @@ def get_default_fallback_handler_address( return _get_valid_contract( ethereum_client, [ - "0x3EfCBb83A4A7AfcB4F68D501E2c2203a38be77f4", # v1.5.0 "0xfd0732Dc9E303f09fCEf3a7388Ad10A83459Ec99", # v1.4.1 "0x9301E98DD367135f21bdF66f342A249c9D5F9069", # v1.4.1 zkSync "0xf48f2B2d2a534e402487b3ee7C18c33Aec0Fe5e4", # v1.3.0 "0x017062a1dE2FE6b99BE3d9d37841FeD19F573804", # v1.3.0 "0x2f870a80647BbC554F3a0EBD093f11B4d2a7492A", # v1.3.0 zkSync + "0x3EfCBb83A4A7AfcB4F68D501E2c2203a38be77f4", # v1.5.0, where no earlier version is deployed ], ) @@ -72,12 +74,12 @@ def get_proxy_factory_address(ethereum_client: EthereumClient) -> ChecksumAddres return _get_valid_contract( ethereum_client, [ - "0x14F2982D601c9458F93bd70B218933A6f8165e7b", # v1.5.0 "0x4e1DCf7AD4e460CfD30791CCC4F9c8a4f820ec67", # v1.4.1 "0xc329D02fd8CB2fc13aa919005aF46320794a8629", # v1.4.1 zkSync "0xa6B71E26C5e0845f74c812102Ca7114b6a896AB2", # v1.3.0 "0xC22834581EbC8527d974F8a1c97E1bEA4EF910BC", # v1.3.0 "0xDAec33641865E4651fB43181C6DB6f7232Ee91c2", # v1.3.0 zkSync + "0x14F2982D601c9458F93bd70B218933A6f8165e7b", # v1.5.0, where no earlier version is deployed ], ) @@ -86,12 +88,12 @@ def get_last_multisend_address(ethereum_client: EthereumClient) -> ChecksumAddre return _get_valid_contract( ethereum_client, [ - "0x218543288004CD07832472D464648173c77D7eB7", # v1.5.0 "0x38869bf66a61cF6bDB996A6aE40D5853Fd43B526", # v1.4.1 "0x309D0B190FeCCa8e1D5D8309a16F7e3CB133E885", # v1.4.1 zkSync "0xA238CBeb142c10Ef7Ad8442C6D1f9E89e07e7761", # v1.3.0 "0x998739BFdAAdde7C933B942a68053933098f9EDa", # v1.3.0 "0x0dFcccB95225ffB03c6FBB2559B530C2B7C8A912", # v1.3.0 zkSync + "0x218543288004CD07832472D464648173c77D7eB7", # v1.5.0, where no earlier version is deployed ], ) @@ -102,12 +104,12 @@ def get_last_multisend_call_only_address( return _get_valid_contract( ethereum_client, [ - "0xA83c336B20401Af773B6219BA5027174338D1836", # v1.5.0 "0x40A2aCCbd92BCA938b02010E17A5b8929b49130D", # v1.4.1 "0x0408EF011960d02349d50286D20531229BCef773", # v1.4.1 zkSync "0x40A2aCCbd92BCA938b02010E17A5b8929b49130D", # v1.3.0 "0xA1dabEF33b3B82c7814B6D82A79e50F4AC44102B", # v1.3.0 "0xf220D3b4DFb23C4ade8C88E526C1353AbAcbC38F", # v1.3.0 zkSync + "0xA83c336B20401Af773B6219BA5027174338D1836", # v1.5.0, where no earlier version is deployed ], ) @@ -118,11 +120,11 @@ def get_last_sign_message_lib_address( return _get_valid_contract( ethereum_client, [ - "0x4FfeF8222648872B3dE295Ba1e49110E61f5b5aa", # v1.5.0 "0xd53cd0aB83D845Ac265BE939c57F53AD838012c9", # v1.4.1 "0xAca1ec0a1A575CDCCF1DC3d5d296202Eb6061888", # v1.4.1 zkSync "0xA65387F16B013cf2Af4605Ad8aA5ec25a2cbA3a2", # v1.3.0 "0x98FFBBF51bb33A056B08ddf711f289936AafF717", # v1.3.0 "0x357147caf9C0cCa67DfA0CF5369318d8193c8407", # v1.3.0 zkSync + "0x4FfeF8222648872B3dE295Ba1e49110E61f5b5aa", # v1.5.0, where no earlier version is deployed ], ) diff --git a/tests/test_safe_addresses.py b/tests/test_safe_addresses.py index 0b7bb8e5..726b1b37 100644 --- a/tests/test_safe_addresses.py +++ b/tests/test_safe_addresses.py @@ -1,4 +1,5 @@ import unittest +from unittest import mock from eth_account import Account from safe_eth.eth import EthereumClient @@ -9,6 +10,7 @@ get_default_fallback_handler_address, get_last_multisend_address, get_last_multisend_call_only_address, + get_last_sign_message_lib_address, get_proxy_factory_address, get_safe_contract_address, get_safe_l2_contract_address, @@ -65,6 +67,51 @@ def test_get_addresses(self): "0x40A2aCCbd92BCA938b02010E17A5b8929b49130D", ) + def test_v1_5_0_only_where_nothing_earlier(self): + # function -> (v1.4.1 address, v1.5.0 address), from safe-deployments + expected = { + get_safe_contract_address: ( + "0x41675C099F32341bf84BFc5382aF534df5C7461a", + "0xFf51A5898e281Db6DfC7855790607438dF2ca44b", + ), + get_safe_l2_contract_address: ( + "0x29fcB43b46531BcA003ddC8FCB67FFE91900C762", + "0xEdd160fEBBD92E350D4D398fb636302fccd67C7e", + ), + get_default_fallback_handler_address: ( + "0xfd0732Dc9E303f09fCEf3a7388Ad10A83459Ec99", + "0x3EfCBb83A4A7AfcB4F68D501E2c2203a38be77f4", + ), + get_proxy_factory_address: ( + "0x4e1DCf7AD4e460CfD30791CCC4F9c8a4f820ec67", + "0x14F2982D601c9458F93bd70B218933A6f8165e7b", + ), + get_last_multisend_address: ( + "0x38869bf66a61cF6bDB996A6aE40D5853Fd43B526", + "0x218543288004CD07832472D464648173c77D7eB7", + ), + get_last_multisend_call_only_address: ( + "0x40A2aCCbd92BCA938b02010E17A5b8929b49130D", + "0xA83c336B20401Af773B6219BA5027174338D1836", + ), + get_last_sign_message_lib_address: ( + "0xd53cd0aB83D845Ac265BE939c57F53AD838012c9", + "0x4FfeF8222648872B3dE295Ba1e49110E61f5b5aa", + ), + } + v1_5_0_addresses = {v1_5_0 for _, v1_5_0 in expected.values()} + ethereum_client = mock.Mock() + for function, (v1_4_1, v1_5_0) in expected.items(): + with self.subTest(function=function.__name__): + # A network with only v1.5.0 deployed + ethereum_client.is_contract.side_effect = lambda address: ( + address in v1_5_0_addresses + ) + self.assertEqual(function(ethereum_client), v1_5_0) + # A network with every version deployed keeps using v1.4.1 + ethereum_client.is_contract.side_effect = lambda address: True + self.assertEqual(function(ethereum_client), v1_4_1) + if __name__ == "__main__": unittest.main()