Calling mint() on the following contract throws when making use of the default parameters:
@private
@constant
def _checkForERC777TokensInterface_Recipient(
_to: address,
_amount: uint256,
_data: bytes[256]="",
):
assert True
@public
def mint(
_to: address,
_amount: uint256,
_data: bytes[256]="",
):
# NOTE: fails (stack underflow) because of default _data parameter
self._checkForERC777TokensInterface_Recipient(
_to,
_amount,
_data,
)
web3js:This throws: Error: Returned error: VM Exception while processing transaction: stack underflow
await erc777Token.mint(
wallet.address,
100,
);
This works:
await erc777Token.mint(
wallet.address,
100,
'0x0000000000000000000000000000000000000001',
);
I posted a solution further down (use data: bytes[256]="0x0" in mint()).
Maybe everything is fine and one should just not use default parameters in internal functions...
In that case this issue can be closed.
The above probably fails because vyper creates two methods:
def mint(_to: address, _amount: uint256)
# and
def mint(_to: address, _amount: uint256, _data: bytes[256])
And in the first method the _data parameter does not exist and therefore can't be passed to the _checkForERC777TokensInterface_Recipient call.
Does Vyper create two internal methods for _checkForERC777TokensInterface_Recipient as well?
Maybe something like this could be done:
@private
@constant
def _checkForERC777TokensInterface_Recipient(
_to: address,
_amount: uint256,
_data: bytes[256]="",
):
assert True
@public
def mint(
_to: address,
_amount: uint256,
_data: bytes[256]="",
):
self._checkForERC777TokensInterface_Recipient(
_to,
_amount,
_data="", # <-------------------------- CHANGE HERE
)
This fails as well:
await erc777Token.mint(
wallet.address,
100,
' 0x0000000000000000000000000000000000000000',
);
(same stack underflow error)
This works:
@private
@constant
def _checkForERC777TokensInterface_Recipient(
_to: address,
_amount: uint256,
_data: bytes[256], # <------------------------------ CHANGE IS HERE
):
assert True
@public
def mint(
_to: address,
_amount: uint256,
_data: bytes[256]="0x0", # <------------------------------ CHANGE IS HERE
):
self._checkForERC777TokensInterface_Recipient(
_to,
_amount,
_data,
)
await erc777Token.mint(
wallet.address,
100,
);
@ssteiger am I understanding correctly that the exception only occurs if the default parameter is an empty bytearray, on a private function?
@jacqueswww
Yes, it seems like the exception only occurs if the default parameter is an empty bytearray
And only if the function is private (functions with @public are working)
I exclude all accompanying parameter type changes (with same default value) in @private _checkForERC777TokensInterface_Recipient.
# works
def mint(
_to: address,
_amount: uint256,
_data: address=0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24
):
# works
def mint(
_to: address,
_amount: uint256,
_data: string[4]="test"
):
```python
def mint(
_to: address,
_amount: uint256,
_data: string[4]=""
):
### boolean
```python
# fails
# Throws: Default parameter values have to be literals.
def mint(
_to: address,
_amount: uint256,
_data: bool=False
):
# works
def mint(
_to: address,
_amount: uint256,
_data: bytes32=0x0000000000000000000000000000000000000000000000000000000000000000
):
```python
def mint(
_to: address,
_amount: uint256,
_data: bytes32=""
):
### int128
```python
# works
def mint(
_to: address,
_amount: uint256,
_data: int128=100
):
# works
def mint(
_to: address,
_amount: uint256,
_data: uint256=0
):
# works
def mint(
_to: address,
_amount: uint256,
_data: decimal=1.01
):
Looks like calling methods of other contracts (also if they are @public) produces the same error:
# CONTRACT 1
@public
def _checkForERC777TokensInterface_Recipient(
_to: address,
_amount: uint256,
_data: bytes[256]="",
):
assert True
@public
def mint(
_to: address,
_amount: uint256,
_data: bytes[256]="",
):
self._checkForERC777TokensInterface_Recipient(
_to,
_amount,
_data,
)
# CONTRACT 1
@public
def _checkForERC777TokensInterface_Recipient(
_to: address,
_amount: uint256,
_data: bytes[256]="",
):
ERC777TokensSender(_from).tokensToSend(_operator, _from, _to, _amount, _data, _operatorData)
@public
def mint(
_to: address,
_amount: uint256,
_data: bytes[256]="",
):
self._checkForERC777TokensInterface_Recipient(
_to,
_amount,
_data,
)
# CONTRACT 2 (ERC777TokensSender)
ERC777Received: event({
_operator: indexed(address),
_from: indexed(address),
_to: indexed(address),
_amount: uint256,
_data: bytes[256],
_operatorData: bytes[256]
})
# This fails (revert)
@public
def tokensReceived(
_operator: address,
_from: address,
_to: address,
_amount: uint256,
_data: bytes[256]="",
_operatorData: bytes[256]=""
):
log.ERC777Received(_operator, _from, _to, _amount, _data, _operatorData)
# CONTRACT 2 (ERC777TokensSender)
@public
def tokensReceived(
_operator: address,
_from: address,
_to: address,
_amount: uint256,
_data: bytes[256]="",
_operatorData: bytes[256]=""
):
assert True
@ssteiger do you have the same issue with v0.1.0b9?
@charles-cooper I've installed v0.1.0b10 on my system using make.
Do you know of a way to easily downgrade to v0.1.0b9?
If you are installing from a local repository, you can git checkout the v0.1.0-beta.9 tag and reinstall using make.
Just tested it. Same stack underflow error with vyper-0.1.0b8 and vyper-0.1.0b9.
Also only when the methods are @private. Setting them to @public makes the error disappear.
this is the most minimal reproducible example i've found:
# stack underflow
@private
def bar(x: bytes[32]):
pass
@public
def foo(x: bytes[32] = b'\x00'):
self.bar(x)
vs
# runs without exception
@private
def bar(x: bytes[32]):
pass
@public
def foo(x: bytes[32] = b'\x01'):
self.bar(x)
(these differ in one instruction - mstore 480 0 vs mstore 480 0x100000000000000000000000000000000000000000000000000000000000000)
As far as I have traced this, it is related to the dynamic packing/unpacking of the empty byte array into the argument section for the private function.
https://github.com/ethereum/vyper/blob/master/vyper/parser/self_call.py#L113
After returning back from the function the unpacker unpacks too may stack items.
@ssteiger please test on master ;)
It's working. Thank you for the quick fix. You guys rock!