Deploying Smart Contracts and Managing Transactions on Ethereum
To build a decentralized application on Ethereum, you need to know how to send a transaction, sign it, and broadcast it to the network. Transactions enable you to transfer ether, deploy your own contracts, and interact with other smart contracts.
To build a decentralized application on Ethereum, you need to know how to send a transaction, sign it, and broadcast it to the network. Transactions enable you to transfer ether, deploy your own contracts, and interact with other smart contracts. In this tutorial, we’ll walk you through how to confidently execute these actions, as well as some relevant libraries and commands to make your development workflow easier.
Creating an Ethereum Transaction
Transactions are state-changing actions on a blockchain. Examples of transactions are sending ether, tokens, and creating or utilizing functions in smart contracts. If you’re new to transactions in Ethereum, check out this helpful introduction to accounts, contracts, and types of Ethereum transactions. Additionally, in this repo, you’ll find a collection of scripts that show you how to interact with Ethereum using the two most popular Javascript libraries: web3.js and ethers.js. For more on web3.js vs. ethers.js, see our technical guide where we breakdown their similarities and differences.
The first step in the process is creating a transaction payload. This allows you to express what type of action you want to perform and how much are you willing to pay for performing that action (the gas fee). Let’s take a look at these properties:
from: DATA, 20 Bytes
- The address the transaction is sent from.
to: DATA, 20 Bytes
- (optional when creating new contract): The address the transaction is directed to.
value: QUANTITY
- (optional): Integer of the value sent with this transaction.
gas: QUANTITY
- (optional, default: 90000): Integer of the gas provided for the transaction execution. It will return unused gas.
gasPrice: QUANTITY
- (optional, default: To-Be-Determined): Integer of the amount (in gwei) you’re willing to pay per gas for the transaction to execute.
data: DATA
- For contract deployments: The compiled code of a contract.
- For contract interactions: the hash of the invoked method signature and encoded parameters. For details, see Ethereum Contract ABI.
- For simple ether transfers:  the “data” will be empty.
nonce: QUANTITY
- (optional): Integer of a nonce. This maintains the order in which the transactions are processed and allows you to overwrite your own pending transactions that use the same nonce.
As a Dapp developer, you can rely on your Web3 utility library to fill in most of these details for you. Below, you will find a more detailed description for how to craft these transactions, depending on the type of action you want to perform.
Signing Your Transaction
Once you have generated a transaction, you need to sign it before it can get accepted by the Ethereum network. The signature serves two purposes: 1) it authenticates the transaction as originating from your account, and 2) it authorizes the network to deduct the transaction gas fee from your account.
There are multiple ways to sign your Ethereum transactions, including:
- Use the signing functionality of your preferred Web3 library with a locally managed private key
- Use a separate signing service (EthSigner)
- Delegate the signing process to a user wallet (e.g. Metamask or Gnosis, Argent via WalletConnect)
- Use the signing functionality built into your Ethereum node
Here are some examples of different approaches:
- How to send a transaction in MetaMask
- How to create, sign and send raw transactions between accounts using NodeJs
In most cases, your Web3 utility library (e.g. ethers.js or web3.js) will typically take the resulting signed transaction, serialize it and then send it to the node via an eth_sendRawTransaction
RPC call.
If you choose to use the built in signing functionality of an Ethereum node (last scenario), the libraries will use a different RPC call, eth_sendTransaction
, which transmits the plain-text, unsigned transaction details to the node. Infura does not support this interaction pattern, so if you want to use this approach you currently need to host your own node. It is important to note that because this feature requires your node to store a private key and expose an RPC to a network, it is considered a bad security practice (unauthorized access to the node can compromise your private keys and allow attackers to sign transactions on your behalf). If you are running your own node, Hyperledger Besu has a very helpful proxy service, EthSigner that helps you sign transactions and securely store your keys in a separate file or keystore and implements the eth_sendTransaction
method. Ethsigner can be used with any Ethereum client, and also supports Infura. You need to configure Ethsigner with a single signer or multiple signers before you can make a transaction.
Sending an Ethereum Transaction Using Infura
After a transaction is signed, it needs to be broadcasted to the Ethereum network so other nodes can see it and miners can include it in the blockchain.
Infura provides the standard eth_sendRawTransaction
RPC method that libraries will typically use behind the scenes to broadcast transactions, so you don’t need to be concerned with calling the RPC method manually.
At some point, an Ethereum miner will get a copy of the transaction, complete a proof-of-work for a new block containing it, and broadcast that block with its proof-of-work to the rest of the network. Every node that receives this block will re-execute the transactions within it, updating the state of the Ethereum Virtual Machine (EVM)*.
*Note: There is considerable complexity around which miners receive which transactions, how they are chosen by the miner, and how blocks get propagated to the network once the miner has completed the proof-of-work. For the sake of the simplicity of this tutorial, we won’t be digging into those topics here.
Ether transfer
An ether transfer is the simplest form of transaction, because ether transfers are native EVM instructions that do not require smart contracts. We simply specify the destination address, the gas price, and the value of ether we are sending (denominated in wei, which is 1e-18 ether.). We then sign the transaction, which will assign it a nonce
value and a signature.
Example: Sign and send ether with a simple transaction
Prerequisites: Make sure to have Node.js 12+ installed and an Infura Project ID available. Clone this repository and run:
cd demo-eth-tx/
npm install
# Add your Infura Project ID below
echo 'INFURA_PROJECT_ID=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' > .env
Now, here is a script for sending a small amount of ether between two accounts using the ethers.js library. Run it and wait for the transaction to be mined:
node ethers/send.js
The same script, using the web3.js library. Run it with:
node web3/send.js
Smart Contract Compilation and Deployment
Contract deployment is another native EVM operation, and looks nearly the same as an ether transfer, except in this scenario, we’ll provide a value for the data
parameter instead of the to
parameter.
The argument we will pass through for data
will be comprised of the bytecode of the contract we are deploying. The bytecode of your contract can be derived using the solc compiler (if using Solidity (preferred)).
Note: When you start building your own smart contracts, you should probably use a development suite such as Truffle, Buidler, or Remix. These tools will make your life easier by freeing you from having to manually compile your smart contract code đź‘Ť
Example: Working with Smart Contracts
Now we'll look into the steps required to write, deploy, and interact with a smart contract.
Let's start with a simple contract (Demo.sol
):
contract Demo {
event Echo(string message);
function echo(string calldata message) external {
emit Echo(message);
}
}
This contract has a single method (called echo
) that can be called by anyone with a message
and emits an event that echoes the input message
.
Contract compilation
Before deploying the contract on the network, we need to compile it. There's a simple compile.js script included here to serve this purpose for now:
node compile.js
As soon as the contract is compiled, a Demo.json file will show up in the main directory. This file includes the contract bytecode (required for deployment) and the Application Binary Interface (ABI) required for contract interactions.
Contract deployment
Here are deployment scripts for both ethers.js and web3.js. Run any of these to deploy your contract:
node ethers/deploy.js
# or
node web3/deploy.js
Once your contract is deployed, you’ll receive your transaction hash. You can examine it using a block explorer or using getTransaction()
and eth.getTransactionReceipt()
. As soon as the deployment transaction is mined, the script will output the address of the new contract.
Contract interaction
Now that the contract is deployed, you can interact with it. You’ll need to craft a transaction by passing through the contract’s address to the to
parameter and some data informing the contract how to execute to the data
parameter.
The Web3 utility libraries provide you with a higher-level interface for issuing the contract and take care of generating the data
parameter for you.
Here’s a short explanation of what these libraries do behind the scenes:  The first part of the data
field is the function selector associated with the contract method being invoked. We can compute the function selector by taking the first 4 bytes of the hash of a function name and its parenthesized argument types, with all whitespace excised. For example, the string transfer(address,uint256) can be hashed into a 4-byte function signature 0xa9059cbb
. The function signature is concatenated with RLP-encoded function arguments (the address and the amount of tokens in this case) to make up the transaction data
field. For more on contract ABI specification and argument encoding works, see the Solidity ABI specification.
Now, here are contract interaction scripts for both ethers.js and web3.js. The scripts are configured to interact with an older, existing contract, but feel free to edit this line of ethers/call.js or this line of web3/call.js and replace it with the address of your newly deployed contract.
Now you can run:
node ethers/call.js
# or
node web3/call.js
Congratulations! You deployed and interacted with an Ethereum smart contract. It's time for you to go build something awesome! 🚀
Infura offers developers fast, reliable access to Ethereum and IPFS networks. Our Core service is free and provides everything developers need to build decentralized applications. If you need more request volume and direct support, check out Infura+ to pick the right plan for your project.
Many thanks to Lucian Boca for his extensive contributions to this guide. For more Web3 tutorials, check out the Infura Blog or subscribe to our newsletter. 🧡