From 8bf2e2d349e4f2abe9d89cfb646bc109c7c55045 Mon Sep 17 00:00:00 2001 From: antazoey Date: Fri, 31 Jan 2025 13:50:57 -0600 Subject: [PATCH] feat: allow setting `sign=False` on transactions (good for titanoboa) (#2486) --- src/ape/api/accounts.py | 28 +++++++++++++++------- tests/functional/test_accounts.py | 17 +++++++++++++ tests/functional/test_contract_instance.py | 6 +++++ 3 files changed, 43 insertions(+), 8 deletions(-) diff --git a/src/ape/api/accounts.py b/src/ape/api/accounts.py index 223e9c3aaf..c908faf26d 100644 --- a/src/ape/api/accounts.py +++ b/src/ape/api/accounts.py @@ -124,6 +124,7 @@ def call( txn: TransactionAPI, send_everything: bool = False, private: bool = False, + sign: bool = True, **signer_options, ) -> ReceiptAPI: """ @@ -143,6 +144,8 @@ def call( Defaults to ``False``. private (bool): ``True`` will use the :meth:`~ape.api.providers.ProviderAPI.send_private_transaction` method. + sign (bool): ``False`` to not sign the transaction (useful for providers like Titanoboa + which still use a sender but don't need to sign). **signer_options: Additional kwargs given to the signer to modify the signing operation. Returns: @@ -177,17 +180,21 @@ def call( else: txn.value = amount_to_send - signed_txn = self.sign_transaction(txn, **signer_options) - if not signed_txn: - raise SignatureError("The transaction was not signed.") + if sign: + prepared_txn = self.sign_transaction(txn, **signer_options) + if not prepared_txn: + raise SignatureError("The transaction was not signed.") - if not txn.sender: - txn.sender = self.address + else: + prepared_txn = txn + + if not prepared_txn.sender: + prepared_txn.sender = self.address return ( - self.provider.send_private_transaction(signed_txn) + self.provider.send_private_transaction(prepared_txn) if private - else self.provider.send_transaction(signed_txn) + else self.provider.send_transaction(prepared_txn) ) def transfer( @@ -683,7 +690,12 @@ def sign_transaction(self, txn: TransactionAPI, **signer_options) -> Optional[Tr return txn def call( - self, txn: TransactionAPI, send_everything: bool = False, private: bool = False, **kwargs + self, + txn: TransactionAPI, + send_everything: bool = False, + private: bool = False, + sign: bool = True, + **kwargs, ) -> ReceiptAPI: txn = self.prepare_transaction(txn) txn.sender = txn.sender or self.raw_address diff --git a/tests/functional/test_accounts.py b/tests/functional/test_accounts.py index a66c8a02ae..f0e02472bc 100644 --- a/tests/functional/test_accounts.py +++ b/tests/functional/test_accounts.py @@ -261,6 +261,11 @@ def test_transfer_mixed_up_sender_and_value(sender, receiver): sender.transfer("123 wei", receiver) +def test_transfer_sign_is_false(sender, receiver): + with pytest.raises(SignatureError): + sender.transfer(receiver, "1 gwei", sign=False) + + def test_deploy(owner, contract_container, clean_contract_caches): contract = owner.deploy(contract_container, 0) assert contract.address @@ -978,3 +983,15 @@ def test_repr(account_manager): """ actual = repr(account_manager) assert actual == "" + + +def test_call(owner, vyper_contract_instance): + tx = vyper_contract_instance.setNumber.as_transaction(5991) + receipt = owner.call(tx) + assert not receipt.failed + + +def test_call_sign_false(owner, vyper_contract_instance): + tx = vyper_contract_instance.setNumber.as_transaction(5991) + with pytest.raises(SignatureError): + owner.call(tx, sign=False) diff --git a/tests/functional/test_contract_instance.py b/tests/functional/test_contract_instance.py index e3f5deb562..8a502cb057 100644 --- a/tests/functional/test_contract_instance.py +++ b/tests/functional/test_contract_instance.py @@ -17,6 +17,7 @@ ContractLogicError, CustomError, MethodNonPayableError, + SignatureError, ) from ape.types.address import AddressType from ape_ethereum.transactions import TransactionStatusEnum, TransactionType @@ -74,6 +75,11 @@ def test_contract_transactions(owner, contract_instance): assert contract_instance.myNumber() == 2 +def test_contract_transaction_when_sign_false(owner, contract_instance): + with pytest.raises(SignatureError): + contract_instance.setNumber(2, sender=owner, sign=False) + + def test_wrong_number_of_arguments(owner, contract_instance): if "sol" in contract_instance.contract_type.source_id.lower(): second = r"\n\t.*setNumber\(uint256 num, address _address\).*"