Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Contract caller #1157

Closed
wants to merge 31 commits into from
Closed

Contract caller #1157

wants to merge 31 commits into from

Conversation

kclowes
Copy link
Collaborator

@kclowes kclowes commented Dec 11, 2018

What was wrong?

Want to deprecate ConciseContract and ImplicitContract in favor of a ContractCaller. There are more details in the issue linked below.

Todo:

  • better testing
  • ENS module breakage
  • Update docs

Related to Issue #
#1025

How was it fixed?

Added a new ContractCaller class and added deprecation methods to ConciseContract and ImplicitContract.

Cute Animal Picture

Put a link to a cute animal picture inside the parenthesis-->

Copy link
Collaborator

@carver carver left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just some early thoughts, and another API improvement possibility.

web3/contract.py Outdated Show resolved Hide resolved
web3/contract.py Outdated

def __call__(self, *args, **kwargs):
# TODO: Remove this and consolidate into ContractCaller.
# I don't know why this is working but it is.
Copy link
Collaborator

@carver carver Dec 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤣 (I've been there)

web3/contract.py Outdated Show resolved Hide resolved


def test_caller_with_parens_and_transaction_dict(math_contract):
result = math_contract.caller({'from': 'notarealaddress.eth'}).add(2, 3)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another potentially interesting API would be to support sending these transaction fields in by keyword. from is problematic since it's a keyword, but we could go the pyethereum route and use sender.

Here that would mean:

math_contract.caller(sender='notarealaddress.eth').add(2, 3)

which is kind of nice.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good to me. So we would support both the sender kwarg and a transaction dict with from as a key, right?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@carver I'd like to introduce that in a subsequent PR since it would make sense to make it available globally.

web3.eth.sendTransaction(sender=..., gas=...)
contract.functions.transfer(...).transact(sender=...)
contract.caller(sender=...).add(...)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be nice to know that the from address was properly set. For this reason, maybe we should test this using a different contract that had a method similar to.

contract CallerTester {
  function returnMeta() returns (address, bytes, uint256, uint256, uint256) {
    return (msg.sender, msg.data, msg.gasPrice, msg.value, gasLeft())
  }
}

This would allow you to test at least 4 of the transaction parameters (with gasLeft() only allowing you to do a approximate test for gas allowance).

web3/contract.py Outdated


class ContractCaller:
def __init__(self, abi, web3, address, *args, **kwargs):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of *args, **kwargs I think it would be sufficient to have a single transaction_dict=None.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking that you could optionally pass in a plain old dictionary without the transaction keyword, in which case I'd need to keep *args. Here is an example: https://web3py.readthedocs.io/en/stable/contracts.html#web3.contract.ContractFunction.call. Am I reading those docs right or does a transaction dict always need the transaction keyword in front of it?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't dive in right this second to make sure it works, but what I was thinking in general is that you can use the fact that positional args can fill in keyword args, like:

def fn(kw1=None, **kwargs):
  return kw1

assert fn(1) == 1

web3/contract.py Outdated
setattr(self, func['name'], caller_method)

def __call__(self, *args, **kwargs):
return type(self)(self.abi, self.web3, self.address, *args, **kwargs)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is where the kwargs could be consolidated into transaction_dict. I'm not sure what args would be here, besides the transaction dict.

Something like this could work:

    def __call__(self, transaction_dict=None, **kwargs):
        assert transaction_dict is None or len(kwargs) == 0  # but not actually an assert
        return type(self)(self.abi, self.web3, self.address, transaction_dict=(transaction_dict or kwargs))

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC the **kwargs portion is to support keyword based transaction parameters? If so my previous comment stands about holding off on introducing this option into the API.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like you can use keyword args for the transaction parameters and block_identifier

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I'm keen on having the block_identifier be something that you set once in the caller and keep around. At the very least, it makes me less excited about the earlier idea (break up the transaction dict into keywords), because then you have a mess of transaction fields and other stuff like the block_identifier.

But... if we don't have the block_identifier in the caller, then is it just impossible to set the block identifier using the ContractCaller? (That might be okay, maybe the "long form" call is required to do that kind of request)

tests/core/contracts/test_contract_call_interface.py Outdated Show resolved Hide resolved
tests/core/contracts/test_contract_call_interface.py Outdated Show resolved Hide resolved


def test_caller_with_parens_and_transaction_dict(math_contract):
result = math_contract.caller({'from': 'notarealaddress.eth'}).add(2, 3)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be nice to know that the from address was properly set. For this reason, maybe we should test this using a different contract that had a method similar to.

contract CallerTester {
  function returnMeta() returns (address, bytes, uint256, uint256, uint256) {
    return (msg.sender, msg.data, msg.gasPrice, msg.value, gasLeft())
  }
}

This would allow you to test at least 4 of the transaction parameters (with gasLeft() only allowing you to do a approximate test for gas allowance).

tests/core/contracts/test_contract_call_interface.py Outdated Show resolved Hide resolved
tests/core/contracts/test_contract_call_interface.py Outdated Show resolved Hide resolved
tests/ens/test_setup_address.py Outdated Show resolved Hide resolved
web3/contract.py Show resolved Hide resolved
web3/contract.py Outdated Show resolved Hide resolved
@@ -527,6 +527,52 @@ def FallballFunctionContract(web3, FALLBACK_FUNCTION_CONTRACT):
return web3.eth.contract(**FALLBACK_FUNCTION_CONTRACT)


CONTRACT_RETURN_ARGS_SOURCE = """
contract CallerTester {
function returnMeta() public payable returns (address, bytes memory, uint256, uint256) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you might return block number too which would make it easy to test calls at specific block numbers.

@kclowes kclowes changed the title [WIP] Contract caller Contract caller Jan 24, 2019
@kclowes
Copy link
Collaborator Author

kclowes commented Jan 25, 2019

@pipermerriam @carver I think this is pretty close if you want to give it another once-over 🙏. The only outstanding question is: do we want to allow block_identifier to be passed in as a kwarg? I don't have a strong opinion. The functionality is there now, but I'm happy to take it out if someone has strong feelings. Also, let me know if you have any feedback on the docs. Thanks!

@carver
Copy link
Collaborator

carver commented Jan 25, 2019

Can you rebase away the merge from master so those changes don't show up in the diff?

@kclowes
Copy link
Collaborator Author

kclowes commented Jan 30, 2019

@carver Since this branch was open so long and a bunch of people were touching the same files, git history got really ugly. I couldn't figure out a graceful and somewhat quick way to rebase away the merge from master. So, I grabbed the relevant commits from here and opened a new branch off of the current master – see #1227. I'm much happier with how the history looks over there. Closing this PR in favor of #1227.

@kclowes kclowes closed this Jan 30, 2019
@pipermerriam
Copy link
Member

Lemme know if you'd like a crash course in git rebasing

@kclowes
Copy link
Collaborator Author

kclowes commented Jan 30, 2019

I'd love a crash course in git rebasing 😆 It occurred to me while I was trying to rebase this PR that I actually need to learn about what it does, rather than guessing like I've done in the past.

@kclowes kclowes deleted the contract-caller branch February 21, 2019 23:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants