Quick start ----------- Just run ``pip install safe-eth-py`` or add it to your **requirements.txt** If you want django ethereum utils (models, serializers, filters...) you need to run ``pip install safe-eth-py[django]`` If you have issues building **coincurve** maybe `you are missing some libraries `_ Ethereum utils -------------- safe_eth.eth ~~~~~~~~~~ - ``class EthereumClient (ethereum_node_url: str)``: Class to connect and do operations with an ethereum node. Uses web3 and raw rpc calls for things not supported in web3. Only ``http/https`` urls are supported for the node url. ``EthereumClient`` has some utils that improve a lot performance using Ethereum nodes, like the possibility of doing ``batch_calls`` (a single request making read-only calls to multiple contracts): .. code-block:: python from safe_eth.eth import EthereumClient from safe_eth.eth.contracts import get_erc721_contract ethereum_client = EthereumClient(ETHEREUM_NODE_URL) erc721_contract = get_erc721_contract(ethereum_client.w3, token_address) name, symbol = ethereum_client.batch_call([ erc721_contract.functions.name(), erc721_contract.functions.symbol(), ]) More optimal in case you want to call the same function in multiple contracts .. code-block:: python from safe_eth.eth import EthereumClient from safe_eth.eth.contracts import get_erc20_contract ethereum_client = EthereumClient(ETHEREUM_NODE_URL) erc20_contract = get_erc20_contract(ethereum_client.w3, token_address) my_account = '0xD0E03B027A367fED4fd0E7834a82CD8A73E76B45' name, symbol = ethereum_client.batch_call_same_function( erc20_contract.functions.balanceOf(my_account), ['0x6810e776880C02933D47DB1b9fc05908e5386b96', '0x6B175474E89094C44Da98b954EedeAC495271d0F'] ) If you want to use the underlying `web3.py `_ library: .. code-block:: python from safe_eth.eth import EthereumClient ethereum_client = EthereumClient(ETHEREUM_NODE_URL) ethereum_client.w3.eth.get_block(57) ``EthereumClient`` supports `EIP1559 `_ fees: .. code-block:: python from safe_eth.eth import TxSpeed base_fee, priority_fee = ethereum_client.estimate_fee_eip1559(tx_speed=TxSpeed.NORMAL) # If you want to convert a legacy tx to a EIP1559 one eip1559_tx = ethereum_client.set_eip1559_fees(legacy_tx, tx_speed=TxSpeed.NORMAL) You can modify timeouts (in seconds) for the RPC endpoints by setting `ETHEREUM_RPC_TIMEOUT` and `ETHEREUM_RPC_SLOW_TIMEOUT` as environment variables. By default every RPC request will be retried `3` times. You can modify that by setting `ETHEREUM_RPC_RETRY_COUNT`. safe_eth.eth.clients ~~~~~~~~~~ You can modify timeouts (in seconds) for the safe_eth.eth.clients by setting the following environment variables: - ``class EnsClient``: `ENS_CLIENT_REQUEST_TIMEOUT`. - ``class EtherscanClient``: `ETHERSCAN_CLIENT_REQUEST_TIMEOUT`. - ``class SourcifyClient``: `SOURCIFY_CLIENT_REQUEST_TIMEOUT`. safe_eth.eth.constants ~~~~~~~~~~~~~~~~~~~~ - ``NULL_ADDRESS (0x000...0)``: Solidity ``address(0)``. - ``SENTINEL_ADDRESS (0x000...1)``: Used for Safe's linked lists (modules, owners...). - Maximum and minimum values for `R`, `S` and `V` in ethereum signatures. safe_eth.eth.eip712 ~~~~~~~~~~~~~~~~~~~~ .. code-block:: python from safe_eth.eth.eip712 import eip712_encode_hash types = {'EIP712Domain': [{'name': 'name', 'type': 'string'}, {'name': 'version', 'type': 'string'}, {'name': 'chainId', 'type': 'uint256'}, {'name': 'verifyingContract', 'type': 'address'}], 'Mailbox': [{'name': 'owner', 'type': 'address'}, {'name': 'messages', 'type': 'Message[]'}], 'Message': [{'name': 'sender', 'type': 'address'}, {'name': 'subject', 'type': 'string'}, {'name': 'isSpam', 'type': 'bool'}, {'name': 'body', 'type': 'string'}]} msgs = [{'sender': ADDRESS, 'subject': 'Hello World', 'body': 'The sparrow flies at midnight.', 'isSpam': False}, {'sender': ADDRESS, 'subject': 'You may have already Won! :dumb-emoji:', 'body': 'Click here for sweepstakes!', 'isSpam': True}] mailbox = {'owner': ADDRESS, 'messages': msgs} payload = {'types': types, 'primaryType': 'Mailbox', 'domain': {'name': 'MyDApp', 'version': '3.0', 'chainId': 41, 'verifyingContract': ADDRESS}, 'message': mailbox} eip712_hash = eip712_encode_hash(payload) safe_eth.eth.oracles ~~~~~~~~~~~~~~~~~~ Price oracles for Uniswap, UniswapV2, Kyber, SushiSwap, Aave, Balancer, Curve, Mooniswap, Yearn... Example: .. code-block:: python from safe_eth.eth import EthereumClient from safe_eth.eth.oracles import UniswapV2Oracle ethereum_client = EthereumClient(ETHEREUM_NODE_URL) uniswap_oracle = UniswapV2Oracle(ethereum_client) gno_token_mainnet_address = '0x6810e776880C02933D47DB1b9fc05908e5386b96' weth_token_mainnet_address = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2' price = uniswap_oracle.get_price(gno_token_mainnet_address, uniswap_oracle.weth_address) safe_eth.eth.utils ~~~~~~~~~~~~~~~~ Contains utils for ethereum operations: - ``mk_contract_address_2(from_: Union[str, bytes], salt: Union[str, bytes], init_code: [str, bytes]) -> str``: Calculates the address of a new contract created using the new CREATE2 opcode. Ethereum django (REST) utils ---------------------------- Django utils are available under ``safe_eth.eth.django``. You can find a set of helpers for working with Ethereum using Django and Django Rest framework. It includes: - **safe_eth.eth.django.filters**: EthereumAddressFilter. - **safe_eth.eth.django.models**: Model fields (Ethereum address, Ethereum big integer field). - **safe_eth.eth.django.serializers**: Serializer fields (Ethereum address field, hexadecimal field). - **safe_eth.eth.django.validators**: Ethereum related validators. - **safe_eth.safe.serializers**: Serializers for Safe (signature, transaction...). - All the tests are written using Django Test suite. Safe Products --------------- Safe ~~~~ On ``safe_eth.safe`` there're classes to work with `Safe `_ .. code-block:: python from safe_eth.eth import EthereumClient from safe_eth.safe import Safe safe_address = '' # Fill with checksummed version of a Safe address ethereum_client = EthereumClient(ETHEREUM_NODE_URL) safe = Safe(safe_address, ethereum_client) safe_info = safe.retrieve_all_info() To work with Multisig Transactions: .. code-block:: python safe_tx = safe.build_multisig_tx(to, value, data, operation, safe_tx_gas, base_gas, gas_price, gas_token, refund_receiver, signatures, safe_nonce) safe_tx.sign(owner_1_private_key) safe_tx.sign(owner_2_private_key) safe_tx.call() # Check it works safe_tx.execute(tx_sender_private_key) To interact with the Transaction Service API: .. code-block:: python from safe_eth.eth import EthereumClient from safe_eth.safe import Safe ethereum_client = EthereumClient(ETHEREUM_NODE_URL) transaction_service_api = TransactionServiceApi( network=EthereumNetwork.SEPOLIA, ethereum_client=ethereum_client ) delegates_for_safe = transaction_service_api.get_delegates(SAFE_ADDRESS) You can modify the request timeout (in seconds) by setting `SAFE_TRANSACTION_SERVICE_REQUEST_TIMEOUT` as environment variable. CowSwap ~~~~~~~~ On ``safe_eth.cowswap`` there're classes to work with `CowSwap `_ .. code-block:: python import time from safe_eth.eth import EthereumNetwork from safe_eth.cowswap import Order, OrderKind, CowSwapAPI account_address = '' # Fill with checksummed version of a CowSwap user address account_private_key = '' # Fill with private key of a user address cow_swap_api = CowSwapAPI(EthereumNetwork.SEPOLIA) print(cow_swap_api.get_trades(owner=account_address)) buy_amount = cow_swap_api.get_estimated_amount(base_token, quote_token, OrderKind.SELL, sell_amount) valid_to = int(time.time() + (24 * 60 * 60)) # Order valid for 1 day order = Order( sellToken=base_token, buyToken=buyToken, receiver=receiver, sellAmount=sell_amount, buyAmount=buy_amount, validTo=valid_to, # timestamp appData={}, # Dict with CowSwap AppData schema definition (https://github.com/cowprotocol/app-data) fee_amount=0, # If set to `0` it will be autodetected kind='sell', # `sell` or `buy` partiallyFillable=True, # `True` or `False` sellTokenBalance='erc20', # `erc20`, `external` or `internal` buyTokenBalance='erc20', # `erc20` or `internal` ) cow_swap_api.place_order(order, account_private_key)