How to Send ETH to a Specific Smart Contract Function Using ethers.js

·

Sending ETH to a specific function within a smart contract is a common requirement when building decentralized applications (dApps) with ethers.js, one of the most popular libraries for interacting with Ethereum. Whether you're funding a payable function, participating in a token sale, or triggering a reward mechanism, understanding how to correctly send Ether during a contract call is essential.

This guide walks you through the complete process of sending ETH to a payable smart contract function using ethers.js, including setup, code implementation, best practices, and real-world considerations.


Setting Up ethers.js

Before interacting with any smart contract, ensure that ethers.js is installed in your project:

npm install ethers

Once installed, import the library into your JavaScript or TypeScript file:

const { ethers } = require("ethers");

👉 Learn how to securely manage Ethereum transactions using modern Web3 tools.


Step-by-Step: Sending ETH to a Payable Contract Function

1. Initialize Your Wallet

To sign and send transactions, you need an Ethereum wallet controlled by a private key. In ethers.js, this is done using the Wallet class.

const privateKey = '0xYourPrivateKey'; // Replace with your actual private key
const wallet = new ethers.Wallet(privateKey);
🔐 Security Tip: Never hardcode private keys in production code. Use environment variables or secure secret management tools.

2. Connect to an Ethereum Network

Use a provider to connect to the Ethereum blockchain. You can use public providers like Alchemy, Infura, or the default provider from ethers.js.

const provider = ethers.getDefaultProvider('sepolia'); // Or 'mainnet', 'goerli', etc.

You can also connect via custom RPC endpoints for better reliability:

const provider = new ethers.JsonRpcProvider('https://ethereum-sepolia-rpc.publicnode.com');

Then connect the wallet to the provider:

const signer = wallet.connect(provider);

3. Create a Contract Instance

To interact with a smart contract, you’ll need:

The ABI defines which functions are available and how to call them.

const contractAddress = '0xYourContractAddress';
const contractABI = [
  "function payFunction() external payable"
]; // Minimal ABI for payable function

const contract = new ethers.Contract(contractAddress, contractABI, signer);

Ensure the ABI includes the function you're calling and marks it as payable if it accepts ETH.


4. Send ETH to the Payable Function

Now, invoke the payable function and include the { value } option to send Ether:

const valueToSend = ethers.parseEther("0.1"); // Sends 0.1 ETH

try {
  const tx = await contract.payFunction({ value: valueToSend });
  console.log("Transaction sent:", tx.hash);

  const receipt = await tx.wait();
  console.log("Transaction confirmed in block:", receipt.blockNumber);
} catch (error) {
  console.error("Transaction failed:", error.message);
}

Here’s what happens:

👉 Discover how to build scalable dApps with reliable Web3 infrastructure.


Understanding Payable Functions in Solidity

For this to work, the target function in your Solidity smart contract must be marked payable. Here's an example:

pragma solidity ^0.8.0;

contract PaymentReceiver {
    event Received(address sender, uint amount);

    function payFunction() external payable {
        emit Received(msg.sender, msg.value);
    }

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

In this contract:

Without the payable modifier, sending ETH will result in a transaction failure.


Common Mistakes and Best Practices

❌ Don't Forget the payable Modifier

If the Solidity function isn’t marked payable, sending ETH will revert.

❌ Avoid Hardcoding Sensitive Data

Never expose private keys in source code. Use .env files:

PRIVATE_KEY=0xabc123...

And load it safely:

require('dotenv').config();
const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider);

✅ Use Testnets First

Test your logic on Sepolia or Holesky before deploying to mainnet.

✅ Handle Errors Gracefully

Wrap transactions in try-catch blocks to handle reverts, gas issues, or network errors.


Frequently Asked Questions (FAQ)

Q: Can I send ETH to any smart contract function?
A: No — only functions marked as payable in Solidity can receive Ether. Attempting to send ETH to non-payable functions will cause the transaction to fail.

Q: What happens if I send ETH to a non-payable function?
A: The transaction will be reverted by the EVM (Ethereum Virtual Machine), and you’ll lose the gas fees.

Q: How do I check if a function is payable?
A: Review the contract’s ABI — payable functions include "payable": true. Alternatively, inspect the Solidity source code for the payable keyword.

Q: Can I send ETH without calling a function?
A: Yes — you can send ETH directly to a contract’s address if it has a receive() or fallback() function defined. Otherwise, the transfer will fail.

Q: Is there a difference between parseEther and parseUnits?
A: parseEther("1") converts 1 ETH into wei (1e18). parseUnits("1", 6) would convert 1 USDC (6 decimals) into its base unit. Use accordingly based on token decimals.

Q: Do I need gas fees in addition to the sent value?
A: Yes — gas fees are paid separately in ETH and are required to execute any transaction on Ethereum, regardless of whether you're sending additional ETH in value.


Advanced Example: Dynamic Value and Error Handling

Here’s a more robust version of the script:

async function sendEthToContract(amountEth, contractAddr, abi, privateKey, network) {
  const provider = ethers.getDefaultProvider(network);
  const wallet = new ethers.Wallet(privateKey, provider);

  const contract = new ethers.Contract(contractAddr, abi, wallet);

  const value = ethers.parseEther(amountEth.toString());

  try {
    const tx = await contract.payFunction({ value, gasLimit: 100000 });
    console.log(`Transaction hash: ${tx.hash}`);
    await tx.wait();
    console.log("Transaction confirmed!");
  } catch (err) {
    console.error("Error sending transaction:", err.message);
  }
}

This modular approach improves reusability and error transparency.


Final Thoughts

Sending ETH to a specific smart contract function using ethers.js is straightforward once you understand the core components: wallet setup, provider connection, contract instantiation, and proper use of the { value } option.

Key core keywords naturally integrated throughout this article include:

By following best practices — such as securing private keys, testing on testnets, and validating function payability — you can build reliable and secure dApps that interact seamlessly with Ethereum.

👉 Start building secure and efficient Ethereum applications today.