• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

safe-global / safe-eth-py / 9987214757

18 Jul 2024 07:18AM UTC coverage: 93.565% (+0.01%) from 93.555%
9987214757

push

github

web-flow
Add addresses 1.3.0 for chain ETHERLINK_MAINNET (#1226)

* Add new chain 42793

* Add new explorer client URL: https://explorer.etherlink.com/api/v1/graphql

* Add new master copy address 0x69f4D1788e39c87893C980c06EdF4b7f686e2938

* Add new master copy address 0xfb1bffC9d739B8D520DaF37dF666da4C687191EA

* Add new proxy address 0xC22834581EbC8527d974F8a1c97E1bEA4EF910BC

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

1 of 1 new or added line in 1 file covered. (100.0%)

11 existing lines in 6 files now uncovered.

8142 of 8702 relevant lines covered (93.56%)

3.74 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

84.85
/gnosis/safe/safe_creator.py
1
import logging
4✔
2
from typing import List, NamedTuple, Optional, Sequence
4✔
3

4
from eth_account import Account
4✔
5
from eth_account.signers.local import LocalAccount
4✔
6
from eth_typing import ChecksumAddress
4✔
7
from hexbytes import HexBytes
4✔
8

9
from gnosis.eth import EthereumClient, EthereumTxSent
4✔
10
from gnosis.eth.constants import NULL_ADDRESS
4✔
11
from gnosis.eth.contracts import (
4✔
12
    get_compatibility_fallback_handler_contract,
13
    get_delegate_constructor_proxy_contract,
14
    get_safe_contract,
15
    get_simulate_tx_accessor_V1_4_1_contract,
16
)
17
from gnosis.eth.utils import get_empty_tx_params
4✔
18

19
from .exceptions import InvalidERC20Token, InvalidPaymentToken
4✔
20
from .proxy_factory import ProxyFactory
4✔
21
from .safe_create2_tx import SafeCreate2Tx, SafeCreate2TxBuilder
4✔
22

23
logger = logging.getLogger(__name__)
4✔
24

25

26
class SafeCreationEstimate(NamedTuple):
4✔
27
    gas: int
4✔
28
    gas_price: int
4✔
29
    payment: int
4✔
30
    payment_token: Optional[str]
4✔
31

32

33
class SafeCreator:
4✔
34
    """
35
    Deploy required Safe contracts and Safes
36
    """
37

38
    @staticmethod
4✔
39
    def create(
4✔
40
        ethereum_client: EthereumClient,
41
        deployer_account: LocalAccount,
42
        master_copy_address: ChecksumAddress,
43
        owners: List[ChecksumAddress],
44
        threshold: int,
45
        fallback_handler: Optional[ChecksumAddress] = NULL_ADDRESS,
46
        proxy_factory_address: Optional[ChecksumAddress] = None,
47
        payment_token: Optional[ChecksumAddress] = NULL_ADDRESS,
48
        payment: int = 0,
49
        payment_receiver: Optional[ChecksumAddress] = NULL_ADDRESS,
50
    ) -> EthereumTxSent:
51
        """
52
        Deploy new Safe proxy pointing to the specified `master_copy` address and configured
53
        with the provided `owners` and `threshold`. By default, payment for the deployer of the tx will be `0`.
54
        If `proxy_factory_address` is set deployment will be done using the proxy factory instead of calling
55
        the `constructor` of a new `DelegatedProxy`
56
        Using `proxy_factory_address` is recommended
57

58
        :param ethereum_client:
59
        :param deployer_account:
60
        :param master_copy_address:
61
        :param owners:
62
        :param threshold:
63
        :param fallback_handler:
64
        :param proxy_factory_address:
65
        :param payment_token:
66
        :param payment:
67
        :param payment_receiver:
68
        :return:
69
        """
70

71
        assert owners, "At least one owner must be set"
4✔
72
        assert 1 <= threshold <= len(owners), "Threshold=%d must be <= %d" % (
4✔
73
            threshold,
74
            len(owners),
75
        )
76

77
        initializer = (
4✔
78
            get_safe_contract(ethereum_client.w3, NULL_ADDRESS)
79
            .functions.setup(
80
                owners,
81
                threshold,
82
                NULL_ADDRESS,  # Contract address for optional delegate call
83
                b"",  # Data payload for optional delegate call
84
                fallback_handler,  # Handler for fallback calls to this contract,
85
                payment_token,
86
                payment,
87
                payment_receiver,
88
            )
89
            .build_transaction(get_empty_tx_params())["data"]
90
        )
91

92
        if proxy_factory_address:
4✔
93
            proxy_factory = ProxyFactory(proxy_factory_address, ethereum_client)
4✔
94
            return proxy_factory.deploy_proxy_contract_with_nonce(
4✔
95
                deployer_account, master_copy_address, initializer=HexBytes(initializer)
96
            )
97

98
        proxy_contract = get_delegate_constructor_proxy_contract(ethereum_client.w3)
×
UNCOV
99
        tx = proxy_contract.constructor(
×
100
            master_copy_address, initializer
101
        ).build_transaction({"from": deployer_account.address})
102
        tx_hash = ethereum_client.send_unsigned_transaction(
×
103
            tx, private_key=deployer_account.key
104
        )
105
        tx_receipt = ethereum_client.get_transaction_receipt(tx_hash, timeout=60)
×
106
        assert tx_receipt
×
UNCOV
107
        assert tx_receipt["status"]
×
108

UNCOV
109
        contract_address = tx_receipt["contractAddress"]
×
UNCOV
110
        return EthereumTxSent(tx_hash, tx, contract_address)
×
111

112
    @staticmethod
4✔
113
    def deploy_compatibility_fallback_handler(
4✔
114
        ethereum_client: EthereumClient, deployer_account: LocalAccount
115
    ) -> EthereumTxSent:
116
        """
117
        Deploy Last compatibility Fallback handler
118

119
        :param ethereum_client:
120
        :param deployer_account: Ethereum account
121
        :return: ``EthereumTxSent`` with the deployed contract address
122
        """
123

124
        contract = get_compatibility_fallback_handler_contract(ethereum_client.w3)
4✔
125
        constructor_data = contract.constructor().build_transaction(
4✔
126
            get_empty_tx_params()
127
        )["data"]
128
        ethereum_tx_sent = ethereum_client.deploy_and_initialize_contract(
4✔
129
            deployer_account, constructor_data
130
        )
131
        logger.info(
4✔
132
            "Deployed and initialized Compatibility Fallback Handler version=%s on address %s by %s",
133
            "1.4.1",
134
            ethereum_tx_sent.contract_address,
135
            deployer_account.address,
136
        )
137
        return ethereum_tx_sent
4✔
138

139
    @staticmethod
4✔
140
    def deploy_simulate_tx_accessor(
4✔
141
        ethereum_client: EthereumClient, deployer_account: LocalAccount
142
    ) -> EthereumTxSent:
143
        """
144
        Deploy Last compatibility Fallback handler
145

146
        :param ethereum_client:
147
        :param deployer_account: Ethereum account
148
        :return: ``EthereumTxSent`` with the deployed contract address
149
        """
150

151
        contract = get_simulate_tx_accessor_V1_4_1_contract(ethereum_client.w3)
4✔
152
        constructor_data = contract.constructor().build_transaction(
4✔
153
            get_empty_tx_params()
154
        )["data"]
155
        ethereum_tx_sent = ethereum_client.deploy_and_initialize_contract(
4✔
156
            deployer_account, constructor_data
157
        )
158
        logger.info(
4✔
159
            "Deployed and initialized Simulate Tx Accessor contract version=%s on address %s by %s",
160
            "1.4.1",
161
            ethereum_tx_sent.contract_address,
162
            deployer_account.address,
163
        )
164
        return ethereum_tx_sent
4✔
165

166
    @staticmethod
4✔
167
    def estimate_safe_creation_2(
4✔
168
        ethereum_client: EthereumClient,
169
        master_copy_address: ChecksumAddress,
170
        proxy_factory_address: ChecksumAddress,
171
        number_owners: int,
172
        gas_price: int,
173
        payment_token: Optional[ChecksumAddress],
174
        payment_receiver: ChecksumAddress = NULL_ADDRESS,
175
        fallback_handler: Optional[ChecksumAddress] = None,
176
        payment_token_eth_value: float = 1.0,
177
        fixed_creation_cost: Optional[int] = None,
178
    ) -> SafeCreationEstimate:
179
        """
180
        :param ethereum_client:
181
        :param master_copy_address:
182
        :param proxy_factory_address:
183
        :param number_owners:
184
        :param gas_price:
185
        :param payment_token:
186
        :param payment_receiver:
187
        :param fallback_handler:
188
        :param payment_token_eth_value:
189
        :param fixed_creation_cost:
190
        :return: An estimation for creating a Safe with the provided parameters
191
        """
192
        salt_nonce = 15
4✔
193
        owners = [Account.create().address for _ in range(number_owners)]
4✔
194
        threshold = number_owners
4✔
195
        if not fallback_handler:
4✔
196
            fallback_handler = (
4✔
197
                Account.create().address
198
            )  # Better estimate it, it's required for new Safes
199
        safe_creation_tx = SafeCreate2TxBuilder(
4✔
200
            w3=ethereum_client.w3,
201
            master_copy_address=master_copy_address,
202
            proxy_factory_address=proxy_factory_address,
203
        ).build(
204
            owners=owners,
205
            threshold=threshold,
206
            fallback_handler=fallback_handler,
207
            salt_nonce=salt_nonce,
208
            gas_price=gas_price,
209
            payment_receiver=payment_receiver,
210
            payment_token=payment_token,
211
            payment_token_eth_value=payment_token_eth_value,
212
            fixed_creation_cost=fixed_creation_cost,
213
        )
214
        return SafeCreationEstimate(
4✔
215
            safe_creation_tx.gas,
216
            safe_creation_tx.gas_price,
217
            safe_creation_tx.payment,
218
            safe_creation_tx.payment_token,
219
        )
220

221
    @staticmethod
4✔
222
    def build_safe_create2_tx(
4✔
223
        ethereum_client: EthereumClient,
224
        master_copy_address: ChecksumAddress,
225
        proxy_factory_address: ChecksumAddress,
226
        salt_nonce: int,
227
        owners: Sequence[ChecksumAddress],
228
        threshold: int,
229
        gas_price: int,
230
        payment_token: Optional[ChecksumAddress],
231
        payment_receiver: Optional[
232
            ChecksumAddress
233
        ] = None,  # If none, it will be `tx.origin`
234
        fallback_handler: Optional[ChecksumAddress] = NULL_ADDRESS,
235
        payment_token_eth_value: float = 1.0,
236
        fixed_creation_cost: Optional[int] = None,
237
    ) -> SafeCreate2Tx:
238
        """
239
        Prepare safe proxy deployment for being relayed. It calculates and sets the costs of deployment to be returned
240
        to the sender of the tx. If you are an advanced user you may prefer to use `create` function
241
        """
242
        try:
4✔
243
            safe_creation_tx = SafeCreate2TxBuilder(
4✔
244
                w3=ethereum_client.w3,
245
                master_copy_address=master_copy_address,
246
                proxy_factory_address=proxy_factory_address,
247
            ).build(
248
                owners=owners,
249
                threshold=threshold,
250
                fallback_handler=fallback_handler,
251
                salt_nonce=salt_nonce,
252
                gas_price=gas_price,
253
                payment_receiver=payment_receiver,
254
                payment_token=payment_token,
255
                payment_token_eth_value=payment_token_eth_value,
256
                fixed_creation_cost=fixed_creation_cost,
257
            )
UNCOV
258
        except InvalidERC20Token as exc:
×
UNCOV
259
            raise InvalidPaymentToken(
×
260
                "Invalid payment token %s" % payment_token
261
            ) from exc
262

263
        return safe_creation_tx
4✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc