I've come to understanding that in order to write a solidity function in the contract that allows me to send money from the contract to some address I have to use something like this:

function transfer(address payable _to, uint _amount) public {
    (bool success, ) = _to.call{value: _amount}("");
    require(success, "Failed to send Ether");

But what is this? First thing: is .call{...}("") even a function at all? Why do I have to put an empty string there? And what's the deal with the curly brackets before the parameter list of the function call?

I've also found a different way to do it, like this:

addr.call{value: msg.value, gas: 5000}(abi.encodeWithSignature("foo(string,uint256)", "call foo", 123));

So, do I need to specify the gas value for the transaction or not? And what's inside the parameter list (that abi.encodeWithSignature(...))?

Also why the definition of the variable success is in brackets? is it the same as doing the following in Python?

# ignoring value_2
value_1, _ = my_function()

Sorry for asking so many question, but I've had a hard time trying to find/understand the solidity documentation.

2 Answers 2

  1. {...} in .call{...} is a function modifier. It modifies the future call by adding metadata to it, like an ETH value to attach or max gas to send along.

  2. If you do not modify the metadata, the compiler will anyway put 0 ETH and the gas that makes sense to it.

  3. The call is effective on (...). What you put in there is the data sent along the call.

  4. If you send no data, then you write ("").

  5. A function signature, or better called a function selector, is the 4 bytes that is prepended to the call data to indicate which function to call. It is calculated as the hash of the function signature. The called smart contract has a table with the selectors it implements with where to jump in the code. If none is known, it calls the fallback function(s). The selector for transferFrom on an ERC20 is byte4(keccak256("transferFrom(address,address,uint256)")). Notice the absence of spaces and the canonical uint256 instead of the shorter uint. It can also be accessed with ERC20.transferFrom.selector.

  6. abi.encodeWithSignature takes the parameters and, depending on their types, pads them to 32 bytes and packs them together. Then prepends the lot with the 4-byte selector.

  7. _to.call is a bit low-level. In fact, you usually do, for instance ERC20(_to).transferFrom(...) instead of:

    _to.call(abi.encodeWithSignature("transferFrom(address,address,uint256)", me, you, 123))
  8. .call returns 2 variables. The success, and IIRC, where to find the return value in memory. If you don't care about the return value, you can omit it. But it still needs the right type, hence the (success, ) =.

  • Thank you! So ERC20 can be used to transfer coin from an address to another, right? Doesn't it come into conflict with the transferFrom function from ERC 721, right?
    – Armando
    Feb 14, 2022 at 16:39
  • ERC20 and ERC721 are 2 different standards. They are implemented in 2 different smart contract instances. You either call one or the other. So they don't come into conflict. Look up both contract codes to see what they do, ERC20 is more than "transfer coin from an address to another". Feb 14, 2022 at 19:47

To add to Xavier's answer, there is documentation here that briefly discusses the use of the f{}() notation. The curly braces accept any "special options".

When calling functions of other contracts, you can specify the amount of Wei or gas sent with the call with the special options {value: 10, gas: 10000}. Note that it is discouraged to specify gas values explicitly, since the gas costs of opcodes can change in the future. Any Wei you send to the contract is added to the total balance of that contract:


Be careful that feed.info{value: 10, gas: 800} only locally sets the value and amount of gas sent with the function call, and the parentheses at the end perform the actual call. So feed.info{value: 10, gas: 800} does not call the function and the value and gas settings are lost, only feed.info{value: 10, gas: 800}() performs the function call.

That being said, I would caution against the use of the word "function modifier", since that term is reserved for a decorator-like feature in Solidity (see here).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.