Sending Transactions Using viem
This is a beginner friendly guide to sending Ethereum transactions using the viem (opens in a new tab) library. There are three main steps in order to send a transaction to the Ethereum blockchain: create, sign, and broadcast. We’ll go through all three, hopefully answering any questions you might have! In this tutorial, we'll be using Alchemy (opens in a new tab) to send our transactions to the Ethereum chain. You can create a free Alchemy account here (opens in a new tab).
NOTE: This guide is for signing your transactions on the backend for your app. If you want to integrate signing your transactions on the frontend, check out the MetaMask provider API (opens in a new tab).
The Basics
Like most blockchain developers when they first start, you might have done some research on how to send a transaction (something that should be pretty simple) and ran into a plethora of guides, each saying different things and leaving you a bit overwhelmed and confused. If you’re in that boat, don’t worry; we all were at some point! So, before we start, let’s get a few things straight:
1. Alchemy does not store your private keys
- This means that Alchemy cannot sign and send transactions on your behalf. The reason for this is security purposes. Alchemy will never ask you to share your private key, and you should never share your private key with a hosted node (or anyone for that matter).
- You can read from the blockchain using Alchemy’s core API, but to write to it you’ll need to use something else to sign your transactions before sending them through Alchemy (this is the same for any other node service).
2. What is a “signer”?
- Signers will sign transactions for you using your private key. In this tutorial we’ll be using viem (opens in a new tab) to sign our transaction, but you could also use any other web3 library.
- On the frontend, a good example of a signer would be MetaMask (opens in a new tab), which will sign and send transactions on your behalf.
3. Why do I need to sign my transactions?
- Every user that wants to send a transaction on the Ethereum network must sign the transaction (using their private key), in order to validate that the origin of the transaction is who it claims to be.
- It is super important to protect this private key, since having access to it grants full control over your Ethereum account, allowing you (or anyone with access) to perform transactions on your behalf.
4. How do I protect my private key?
- There are many ways to protect your private key and to use it to send off transactions. In this tutorial we will be using a
.envfile. However, you could also use a separate provider that stores private keys, use a keystore file, or other options.
5. What is the difference between eth_sendTransaction and eth_sendRawTransaction?
eth_sendTransaction and eth_sendRawTransaction are both Ethereum API functions which broadcast a transaction to the Ethereum network so it will be added to a future block. They differ in how they handle signing of the transactions.
eth_sendTransactionis used for sending unsigned transactions, which means the node you are sending to must manage your private key so it can sign the transaction before broadcasting it to the chain. Since Alchemy doesn't hold user's private keys, they do not support this method.eth_sendRawTransaction(opens in a new tab) is used to broadcast transactions that have already been signed. This means you first have to sign the transaction with your private key (in viem, usingsignTransaction(opens in a new tab) with a local account (opens in a new tab)), then pass in the result intoeth_sendRawTransaction.
When using viem, eth_sendRawTransaction is accessed by calling the function sendRawTransaction (opens in a new tab).
This is what we will be using in this tutorial.
6. What is the web3 library?
- A web3 library is a wrapper around the standard JSON-RPC calls that is quite common to use in Ethereum development.
- There are many web3 libraries for different languages. In this tutorial we’ll be using viem (opens in a new tab), which is written in TypeScript and works in any JavaScript project. You can check out other options here like ethers.js (opens in a new tab).
Okay, now that we have a few of these questions out of the way, let’s move on to the tutorial. Feel free to ask questions anytime in the Alchemy discord (opens in a new tab)!
7. How to send secure, gas-optimized, and private transactions?
- Alchemy has a set of transaction resources (opens in a new tab). You can use these to send transactions, simulate transactions before they happen, send private transactions, and send gas-optimized transactions
- You can also use Alchemy webhooks (opens in a new tab) to be alerted when your transaction is pulled from the mempool and added to the chain
NOTE: This guide requires an Alchemy account, an Ethereum address or MetaMask wallet, NodeJs, and npm installed. If not, follow these steps:
- Create a free Alchemy account (opens in a new tab)
- Create MetaMask account (opens in a new tab) (or get an Ethereum address)
- Install Node.js and npm (opens in a new tab)
Steps to Sending your Transaction
1. Create an Alchemy app on the Sepolia testnet
Navigate to your Alchemy Dashboard (opens in a new tab) and create a new app, choosing Sepolia (or any other testnet) for your network.
2. Request ETH from the Sepolia faucet
Follow the instructions on the Alchemy Sepolia faucet (opens in a new tab) to receive ETH, or use one of the other Sepolia faucets. Faucets give out small amounts (the Alchemy faucet gives 0.1 ETH per day), which is plenty for this tutorial. Make sure to include your Sepolia Ethereum address (from MetaMask) and not another network. After following the instructions, double-check that you’ve received the ETH in your wallet.
3. Create a new project directory and cd into it
Create a new project directory from the command line (terminal for macs) and navigate into it:
mkdir sendtx-example
cd sendtx-example
4. Install viem (or any web3 library)
Run the following command in your project directory to install viem (opens in a new tab):
Note, if you'd like to use the ethers.js library, follow the instructions here (opens in a new tab).
npm init -y
npm install viem
5. Install dotenv
We’ll use a .env file to safely store our API key and private key.
npm install dotenv
6. Create the .env file
Create a .env file in your project directory and add the following (replacing “your-api-url" and "your-private-key")
- To find your Alchemy API URL, navigate to the app details page of the app you just created on your dashboard, click “View Key” in the top right corner, and grab the HTTP URL.
- To find your private key using MetaMask, check out this guide (opens in a new tab). Use an account that only holds testnet ETH.
API_URL="your-api-url"
PRIVATE_KEY="your-private-key"
.env! Please make sure never to share or expose your .env file with anyone, as you are compromising your secrets in doing so. If you are using version control, add your .env to a gitignore file.7. Create sendTx.js file
Great, now that we have our sensitive data protected in a .env file, let’s start coding. For our send transaction example, we’ll be sending 0.001 ETH to another address you control, such as a second account in MetaMask.
Create a sendTx.js file, which is where we will configure and send our example transaction, and add the following lines of code to it:
require("dotenv").config({ quiet: true })
const { createPublicClient, createWalletClient, http, parseEther } = require("viem")
const { privateKeyToAccount } = require("viem/accounts")
const { sepolia } = require("viem/chains")
async function main() {
const { API_URL, PRIVATE_KEY } = process.env
const account = privateKeyToAccount(
PRIVATE_KEY.startsWith("0x") ? PRIVATE_KEY : `0x${PRIVATE_KEY}`
)
const publicClient = createPublicClient({
chain: sepolia,
transport: http(API_URL),
})
const walletClient = createWalletClient({
account,
chain: sepolia,
transport: http(API_URL),
})
const nonce = await publicClient.getTransactionCount({
address: account.address,
blockTag: "pending",
}) // nonce starts counting from 0
const transaction = await walletClient.prepareTransactionRequest({
to: "0xYourSecondAddress", // TODO: replace with the address you want to send ETH to
value: parseEther("0.001"), // 0.001 ETH
gas: 21000n,
nonce,
// optional data field to send message or execute smart contract
})
const signedTx = await walletClient.signTransaction(transaction)
try {
const hash = await walletClient.sendRawTransaction({
serializedTransaction: signedTx,
})
console.log("🎉 The hash of your transaction is:", hash)
const receipt = await publicClient.waitForTransactionReceipt({ hash })
console.log(`Transaction ${receipt.status} in block ${receipt.blockNumber}`)
} catch (error) {
console.log(
"❗Something went wrong while submitting your transaction:",
error.details ?? error.message
)
}
}
main()
Be sure to replace 0xYourSecondAddress with the address you want to send ETH to. The script derives your own (sending) address from PRIVATE_KEY, so you don't need to enter it.
Now, before we jump into running this code, let's talk about some of the components here.
account: A local account created from your private key withprivateKeyToAccount(opens in a new tab). Signing happens on your machine, and the private key is never sent to Alchemy.publicClientandwalletClient: viem clients that send JSON-RPC requests to your Alchemy URL. The public client reads from the chain, and the wallet client prepares and signs transactions for your account.nonce: The nonce specification is used to keep track of the number of transactions sent from your address. We need this for security purposes and to prevent replay attacks. To get the number of transactions sent from your address we use getTransactionCount (opens in a new tab). viem would also fill this in for you, but we fetch it explicitly here to show where it comes from.transaction: The transaction object has a few aspects we need to specify.prepareTransactionRequest(opens in a new tab) fills in the rest for us, including thechainIdand the EIP-1559 fee fieldsmaxFeePerGasandmaxPriorityFeePerGas, based on current network conditionsto: This is the address we want to send ETH to.value: This is the amount we wish to send, specified in Wei where 10^18 Wei = 1 ETH.parseEther(opens in a new tab) converts an ETH amount to Wei for us. Your account needs a little more than this, since it also pays for gasgas: There are many ways to determine the right amount of gas to include with your transaction. Alchemy supports webhooks (opens in a new tab) that can notify you about onchain activity. For Mainnet transactions, it's good practice to check current gas conditions to determine the right amount of gas to include. A plain ETH transfer to an ordinary address always uses exactly 21000 gas, which is the minimum for any transaction, so we put 21000 here. If you leavegasout, viem estimates it for you.nonce: see above nonce definition. Nonce starts counting from zero.- [OPTIONAL] data: Used for sending additional information with your transfer, or calling a smart contract, not required for balance transfers, check out the note below.
signedTx: To sign our transaction object we will use thesignTransactionmethod, which signs it locally with the account created from ourPRIVATE_KEYsendRawTransaction: Once we have a signed transaction, we can send it off to be included in a subsequent block by usingsendRawTransaction, which callseth_sendRawTransactionwaitForTransactionReceipt: Waits until the transaction is included in a block and returns its receipt
A Note on data There are a two main types of transactions that can be sent in Ethereum.
- Balance transfer: Send ETH from one address to another. No data field required, however, if you'd like to send additional information alongside your transaction, you can include that information in HEX format in this field.
- For example, let's say we wanted to write the hash of an IPFS document to the Ethereum chain in order to give it an immutable timestamp. Our data field should then look like
data: toHex("IPFS hash"), using viem'stoHex(opens in a new tab). And now anyone can query the chain and see when that document was added.
- For example, let's say we wanted to write the hash of an IPFS document to the Ethereum chain in order to give it an immutable timestamp. Our data field should then look like
- Smart contract transaction: Execute some smart contract code on the chain. In this case, the data field should contain the smart function you wish to execute, alongside any parameters.
- For a practical example, check out the Hello World Smart Contract tutorial.
8. Run the code using node sendTx.js
Navigate back to your terminal or command line and run:
node sendTx.js
9. See your transaction on a block explorer
The script prints the transaction hash as soon as the node accepts the transaction, then waits until it is included in a block (usually within a few seconds) and prints the result. To see the details, paste the hash into a Sepolia block explorer such as Sepolia Etherscan (opens in a new tab) or Blockscout (opens in a new tab). There you can watch the transaction go from pending to included, and see the amount, fees and block it landed in.
If you see an insufficient funds error instead, your account doesn't have enough Sepolia ETH to cover the value plus gas. Go back to step 2 and request more from a faucet.
Yippieeee! You just sent your first Ethereum transaction using Alchemy 🎉
For feedback and suggestions about this guide, please message Elan on Alchemy’s Discord (opens in a new tab)!
Originally published by Alchemy.