DEV Community

Cover image for Payable Functions in Solidity
Shlok Kumar
Shlok Kumar

Posted on

Payable Functions in Solidity

A payable function in Solidity is a function that can receive Ether and respond to an Ether deposit for record-keeping purposes.

When you write a smart contract, you have to make sure that money goes into and out of the contract. Payable takes care of this for you. Any Solidity function with a modifier Payable makes sure that the function can send and receive Ether. It can handle transactions where the Ether value is not zero, but it can't handle transactions where the Ether value is zero.

Also, if you want a function to process transactions but don't include the payable keyword in them, the transaction will be automatically rejected. As an example, let's say you have a receive() function with the payable modifier. This means that this function can receive money in the contract. Now, let's say you have a send() function without the payable modifier. When you try to send money out of the contract, the transaction will be rejected.

In Solidity, fallback payable functions are also a big help. If someone sends money to your contract through a function that doesn't have the payable modifier, you can define that function to have a fallback payable function that makes sure the transaction will still go through. Because of this, it is a good idea to use some version of a function with the noname and payable modifiers. In this case, the name of the function is "noname," not "payable." "Payable" is the word that describes the function.

Function declaration with no name

function () public payable {} 
Enter fullscreen mode Exit fullscreen mode

You can define a payable function using the following syntax:

function receive() payable {}
function send() payable {}
Enter fullscreen mode Exit fullscreen mode

As you can see, the keyword payable is not a function; instead, it is a modifier. People sometimes mistake it for a function, which changes the meaning of the whole function and makes the code not work. As we said before about the "noname" function, if someone tries to call another function without the payable modifier, it acts as a fallback and sends the ether being sent to this "noname" function.

Solidity supports several methods of transferring ether between the contracts.

  1. address.send(amount)

  2. address.transfer(amount)

  3. address.call.value(amount)()

address.send(amount)

Send was the first way that ether could be sent from contract to another (). There are two things you should think about while using send.

The first and most important feature is that there is a 2300 gas limit for a contract's fallback function of receiving ether. Also, this amount won't be enough to make more than one event.

The second important thing to keep in mind is that if send() fails (for example, because you ran out of gas), it returns false but does not throw an exception. So, every time send() is used, it should be inside of require. If you don't pay for gas, you won't be able to submit a transaction to the blockchain. Instead, all the changes will be rolled back.

address.transfer(amount)

The transfer method has the same limit of 2300 gas. During the process of making these features, however, the developers talked about adding a.gas() modifier, which changes the limit of the given gas.

Second, unlike the send() method, the transfer() method throws an exception when it doesn't work. So, you find out right when the transaction is tried to be executed that it failed.

address.call.value(amount)()

The last and most personalized way to send ether is by using the call function. If there is an error, the given function will still return false, so keep in mind how to use require().

Its main difference from the two functions that came before it is that you can set the gas limit with the .gas(gasLimit) modifier. It is necessary if the function that pays out the ether in the contract has a complicated logic that needs a lot of gas

Example 1:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Payable {
    // Payable address can receive Ether
    address payable public owner;
    // Payable constructor can receive Ether
    constructor() payable {
        owner = payable(msg.sender);
    }

    // Function to deposit Ether into this contract.
    // Call this function along with some Ether.
    // The balance of this contract will be automatically updated.
    function deposit() public payable {}
    // Call this function along with some Ether.
    // The function will throw an error since this function is not payable.
    function notPayable() public {}

    // Function to withdraw all Ether from this contract.
    function withdraw() public {
        // get the amount of Ether stored in this contract
        uint amount = address(this).balance;
        // send all Ether to owner
        // Owner can receive Ether since the address of owner is payable
        (bool success, ) = owner.call{value: amount}("");
        require(success, "Failed to send Ether");
    }

    function getBalance() public view returns(uint){
        return address(this).balance;
    }

    // Function to transfer Ether from this contract to address from input
    function deposit_transfer(address payable to, uint amount) public payable{
          // Note that "to" is declared as payable
        to.transfer(amount);//2300 gas
    }

    function deposit_send(address payable to, uint amount) public payable{
          // Note that "to" is declared as payable
        bool sent=_to.send(_amount);//2300 gas
        require(sent,"failure to send ether");
    }

    function deposit_call(address payable to, uint amount) public payable{
          // Note that "to" is declared as payable
        (bool sent,bytes memory data)=_to.call{gas: 25000, value:_amount}("");//set max gas limit as 25000 gas
        require(sent,"failure to send ether");
    }
}
Enter fullscreen mode Exit fullscreen mode

Example 2:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract ReceiveEther {
    /*
    Which function is called, fallback() or receive()?
           send Ether
               |
         msg.data is empty?
              / \
            yes  no
            /     \
receive() exists?  fallback()
         /   \
        yes   no
        /      \
    receive()   fallback()
    */

    // Function to receive Ether. msg.data must be empty
    receive() external payable {}
    // Fallback function is called when msg.data is not empty
    fallback() external payable {}
    function getBalance() public view returns (uint) {
        return address(this).balance;
    }
}
contract SendEther {
    function sendViaTransfer(address payable to) public payable {
        // This function is no longer recommended for sending Ether.
        to.transfer(msg.value);
    }

    function sendViaSend(address payable to) public payable {
        // Send returns a boolean value indicating success or failure.
        // This function is not recommended for sending Ether.
        bool sent = to.send(msg.value);
        require(sent, "Failed to send Ether");
    }

    function sendViaCall(address payable to) public payable {
        // Call returns a boolean value indicating success or failure.
        // This is the current recommended method to use.
        (bool sent, bytes memory data) = to.call{value: msg.value}("");
        require(sent, "Failed to send Ether");
    }
} 
Enter fullscreen mode Exit fullscreen mode

For more content, follow me at - https://linktr.ee/shlokkumar2303

Top comments (0)