-
Notifications
You must be signed in to change notification settings - Fork 1.8k
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
Contract caller #1157
Conversation
There was a problem hiding this 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
|
||
def __call__(self, *args, **kwargs): | ||
# TODO: Remove this and consolidate into ContractCaller. | ||
# I don't know why this is working but it is. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤣 (I've been there)
|
||
|
||
def test_caller_with_parens_and_transaction_dict(math_contract): | ||
result = math_contract.caller({'from': 'notarealaddress.eth'}).add(2, 3) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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(...)
There was a problem hiding this comment.
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): |
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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))
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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)
64e95bb
to
a25abbd
Compare
|
||
|
||
def test_caller_with_parens_and_transaction_dict(math_contract): | ||
result = math_contract.caller({'from': 'notarealaddress.eth'}).add(2, 3) |
There was a problem hiding this comment.
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/conftest.py
Outdated
@@ -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) { |
There was a problem hiding this comment.
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.
rename transaction_dict for consistency
Use shorthand syntax for
Test a contract with no_abi
369c593
to
1b3c129
Compare
@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 |
Can you rebase away the merge from master so those changes don't show up in the diff? |
@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. |
Lemme know if you'd like a crash course in git rebasing |
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. |
What was wrong?
Want to deprecate ConciseContract and ImplicitContract in favor of a ContractCaller. There are more details in the issue linked below.
Todo:
Related to Issue #
#1025
How was it fixed?
Added a new ContractCaller class and added deprecation methods to ConciseContract and ImplicitContract.
Cute Animal Picture