How to send ETH from a web3 app to a smart contract
Published
Table of contents
Sending ETH from a user's account to a smart contract is one of the most commong things you'll have to deal with when creating web 3 apps so in this article I'm going to explain how to do it.
In summary, to send ETH to a smart contract you need:
- A smart contract with a payable function
- A web app to interact with the contract
- A wallet with some ETH to send 🤑
Smart contract that receives ETH
In order for a smart contract function to be able to receive ETH, we need to use the identify that function as payable
. Payable functions can receive an additional object parameter in which we can include multiple parameters like value
, gasPrice
, gasLimit
and other transaction related options.
The example below is a contract with a payable function named playGame
which receives as a number as a parameter.
pragma solidity ^0.8.4;
contract GameContract {
uint256 secretNumber = 33;
uint256 ticketPrice = 0.01 ether;
function playGame(uint256 _number) public payable returns ( boolean memory){
console.log("Current balance is ", address(this).balance);
// Validate value received is at least the ticket price
require(msg.value >= ticketPrice, "Minimum value is 0.01 ETH!");
// if number sent is correct, send money and reset number
if (_number == secretNumber) {
// Winner
// send 80% of contract's balance to the player
(bool success, ) = (msg.sender).call{
value: (address(this).balance * 80) / 100
}("");
console.log("Balance after win is ", address(this).balance);
return true;
}
// User lost
// received ETH is added to the contract balance
console.log("Updated balance is ", address(this).balance);
return false;
}
}
This is a very basic game in which, if the user guess the secret number, he'll receive 80% of the ETH accumulated in the contract. If the player fails to guess the number, the ETH received is added to the contract's balance.
Sending ETH from a web app
From the web application point of view, I'll use Ethers.js to interact with the contract. Using Ethers, we can include an additional parameter when calling the smart contract function. This additional parameter contains transaction overrides and can contain value
or gasPrice
.
import { Contract, ethers } from 'ethers';
import GameContract from '@/artifacts/solidity/contracts/GameContract.sol/GameContract.json';
const provider = new ethers.providers.Web3Provider(window.ethereum);
const signer = provider.getSigner();
// address of the smart contract
const contractAddress = '0x4ed7c70C26B79c776995fC64377f0d4aB3B0e222';
// as the operation we're going to do is a transaction,
// we pass the signer instead of the provider
const contract = new ethers.Contract(contractAddress, GameContract.abi, signer);
// guessed number hardcoded here for this example
const guessNumber = 3;
try {
const txOverrides = {
// same as ticketPrice in the smart contract
value: ethers.utils.parseEther('0.01'),
};
// call smart contract function passing the number and the overrides including the ETH value
const transaction = await contract.playGame(guessNumber, txOverrides);
console.log('transaction :>> ', transaction);
// wait for the transaction to actually settle in the blockchain
await transaction.wait();
} catch (err) {
console.error(error);
}
Apart from the contract address (you can find how to deploy smart contracts in this article), we just need to get a signer
to actually sign the transaction, as we'll be sending ETH from the user's wallet.
Notice that, even though the playGame
function in our contract only receives a single parameter, I'm also passing the transaction overrides object that includes the ETH value. Once we call this function, the Metamask prompt to sign the transaction will include the amount of ETH that we indicated plus some fees.
You can get some ETH for the Rinkeby test net from the faucets mentioned here
TAGS