🌎 Hello World Smart Contract

This is a step-by-step tutorial that will teach you how to (1) create a smart contract (2) interact with the smart contract you built, and (3) publish it to Etherscan, so anyone can interact with it!

Estimated time to complete this guide: ~45 minutes

If you are new to blockchain development and don't know where to start, or if you just want to understand how to deploy and interact with smart contracts using Alchemy, Truffle, and web3, this guide is for you. We will walk through creating and deploying a simple smart contract on the ropsten test network using a Metamask account, Solidity, Truffle, and Alchemy (don't worry if you don't understand what any of this means yet, we will explain it!). We'll also go through how we can interact with our smart contract once it's deployed and how to publish it on Etherscan.

Part 1: Create your Smart Contract

Step 1: Create your Alchemy account

You will need an Alchemy account in order to deploy and make requests to your smart contract. If you don't already have one, you can sign up for free here: https://dashboard.alchemyapi.io/signup/.

Step 2: Create your app

Once you've created an Alchemy account, you can generate an API key by creating an app. This will allow us to make requests to the ropsten test network, to learn more about testnets check out this guide.

  1. Navigate to the "Create App" page in your Alchemy Dashboard by hovering over "Apps" in the nav bar and clicking "Create App"

  2. Name your app "Hello World", offer a short description, select "Staging" for the Environment, and choose "Ropsten" for your network

  3. Click "Create app" and that's it! Your app should appear in the table below

Step 3: Create a Metamask Account

Metamask is used to manage your Ethereum account address. You can download and create a Metamask account here. When you are creating an account, or if you already have an account, make sure to switch over to the "Ropsten Test Network" in the upper right.

Step 4: Add ether from a Faucet

In order to deploy our smart contract to the test network, we'll need some fake Eth. To get ether we can go to the Ropsten faucet and enter your Ropsten account address, then click "Send Ropsten Eth." It may take some time to receive your fake Eth due to network traffic. (At the time of writing this, it took around 30 minutes.) You should see Eth in your Metamask account soon afterπŸ€‘

Step 5: Check your Balance

To double check our balance is there, we can use our Alchemy API key and account address to make a call to eth_getBalance. You can do this through the command line or by using Alchemy's composer feature.

curl https://eth-ropsten.alchemyapi.io/v2/your-api-key \
-X POST \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_getBalance","params":["your-account-address", "latest"],"id":0}'

The result you get back should be:

{"jsonrpc": "2.0", "id": 0, "result": "0x2B5E3AF16B1880000"}

NOTE: This result is in wei not eth. Wei is used as the smallest denomination of ether. The conversion from wei to eth is: 1 eth = 10^18 wei. So if we convert 0x2B5E3AF16B1880000 to decimal we get 5*10^18 which equals 5 eth.

Phew! Our fake money is all thereπŸ€‘ .

Step 6: Download Truffle

​Truffle is a development environment, testing network, and asset pipeline for Ethereum that we will use to build, compile, and deploy our smart contract. To download Truffle, you can install NodeJS and paste the following command in your terminal:

npm install -g truffle

Step 7: Create a Truffle Project

Next we have to create a Truffle project to store our files.

  1. Create a new directory for your Truffle project:

mkdir Hello-World
cd Hello-World
  1. Get boilerplate files for creating and deploying smart contracts by "unboxing":

truffle unbox

Once this operation is completed, you'll now have a project structure with the following items:

Step 8: Install HDWalletProvider

​Truffle HDWallet provider is an easy way to configure network connection to ethereum through a provider like Alchemy. You can install it using the following command:

npm install @truffle/hdwallet-provider

Step 9: Write our Contract

Open up the Hello-World project in your favorite editor. Smart contracts are written in a language called Solidity which is what we will use to write our HelloWorld.sol smart contract.

  1. Navigate to the "contracts" folder and create a new file called HelloWorld.sol

  2. Below is a sample Hello World smart contract from the Ethereum Foundation which we will be using for this tutorial. Copy and paste in the contents below into your HelloWorld.sol file, and be sure to read the comments to understand what this contract does:

// Specifies the version of Solidity, using semantic versioning.
// Learn more: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma
pragma solidity ^0.5.10;
​
// Defines a contract named `HelloWorld`.
// A contract is a collection of functions and data (its state).
// Once deployed, a contract resides at a specific address on the Ethereum blockchain.
// Learn more: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html
contract HelloWorld {
​
// Declares a state variable `message` of type `string`.
// State variables are variables whose values are permanently stored in contract storage.
// The keyword `public` makes variables accessible from outside a contract
// and creates a function that other contracts or clients can call to access the value.
string public message;
​
// Similar to many class-based object-oriented languages, a constructor is
// a special function that is only executed upon contract creation.
// Constructors are used to initialize the contract's data.
// Learn more: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors
constructor(string memory initMessage) public {
// Accepts a string argument `initMessage` and sets the value
// into the contract's `message` storage variable).
message = initMessage;
}
​
// A public function that accepts a string argument
// and updates the `message` storage variable.
function update(string memory newMessage) public {
message = newMessage;
}
}

This smart contract is very simple, it stores a message that is passed in when the contract is initialized and can be updated by calling the update function.

Step 10: Configure our Project

The next step is to edit your truffle-config.js file to use HDWalletProvider and provide all the necessary configuration for deploying to ropsten.

1. First, define the HDWalletProvider object in your configuration file. Add this line at the top of your truffle-config.js file:

const HDWalletProvider = require("@truffle/hdwallet-provider");

2. Next, provide a reference to your mnemonic or seed phrase from Metamask. to get your seed reference from Metamask follow these instructions. Once you have your mnemonic, we recommend storing it safely in an environment file (we will also add our API key and eventually private key when interacting).

NOTE: We'll be using dotenv to safely store our mnemonic, API key, and private key. This separates your private keys from source code and ensures no secret information will be included if you wish to share your code publicly.

First, install the dotenv package.

npm install dotenv --save

Next add the following line to your truffle-config.js file.

require('dotenv').config()

Then create a .env file at the root directory of your application and add your mnmeonic (use these instructions to find it) and Alchemy API URL to it:

API_URL = "https://eth-ropsten.alchemyapi.io/v2/your-api-key"
MNEMONIC = "your-metamask-seed-reference"

Finally add the new environment variables to our truffle-config.js file:

const { API_URL, MNEMONIC } = process.env;

3. Add the ropsten network to the module.exports in truffle-config.js:

module.exports = {
networks: {
development: {
host: "localhost",
port: 8545,
network_id: "*", // Match any network id
gas: 5000000
},
ropsten: {
provider: function() {
return new HDWalletProvider(MNEMONIC, API_URL)
},
network_id: 3
}
​
},
compilers: {
solc: {
settings: {
optimizer: {
enabled: true, // Default: false
runs: 200 // Default: 200
},
}
}
}
};

Step 11: Compile our Smart Contract

To compile a Truffle project, navigate to the root of the directory where the project is located and then type the following command:

truffle compile

You should now see a build/contracts/ directory in your project. These artifacts are integral to the inner workings of Truffle, and are important for deploying your smart contract. This includes your contract's ABI, which is something you will encounter down the line. You should not edit these files.

Step 12: Add HelloWorld to 2_deploy_contracts.js file

Now, go into your migrations folder and open the 2_deploy_contracts.js file.

For purposes of speed, we will comment out the deployment of the other smart contracts. Since our smart contract has a constructor that takes in a parameter, we have to include the parameter in our deploy().

//const ConvertLib = artifacts.require("ConvertLib");
//const MetaCoin = artifacts.require("MetaCoin");
​
const HelloWorld = artifacts.require("HelloWorld");
const initMessage = "Hello world!";
​
module.exports = function(deployer) {
//deployer.deploy(ConvertLib);
//deployer.link(ConvertLib, MetaCoin);
//deployer.deploy(MetaCoin);
deployer.deploy(HelloWorld, initMessage);
};

Step 13: Deploy our Smart Contract

In order to deploy our smart contract to the Ethereum network, we will use truffle's migrations which are JavaScript files that help you deploy contracts to the Ethereum network.

To run your migrations, run the following command in your terminal:

truffle migrate --network ropsten

You should then see a response that looks similar to the following:

Starting migrations...
======================
> Network name: 'ropsten'
> Network id: 3
> Block gas limit: 8000000 (0x7a1200)
​
1_initial_migration.js
======================
​
Deploying 'Migrations'
----------------------
> transaction hash: 0x61a6c81aaf5be5329c8572ac8de8f9d27064d75f5184f2389f66212b91c9736e
> Blocks: 1 Seconds: 12
> contract address: 0x341662A4BD97bf8542bB0d815F99aff47dB2Fc42
> block number: 8903909
> block timestamp: 1603052580
> account: 0x610Ae88399fc1687FA7530Aac28eC2539c7d6d63
> balance: 4.98766424
> gas used: 168286 (0x2915e)
> gas price: 20 gwei
> value sent: 0 ETH
> total cost: 0.00336572 ETH
​
​
> Saving migration to chain.
> Saving artifacts
-------------------------------------
> Total cost: 0.00336572 ETH
​
2_deploy_contracts.js
=====================
​
Deploying 'HelloWorld'
----------------------
> transaction hash: 0x1312f26f70bd444a25790215c56aa4d87a56bc40d141f216df0661ddc3df42bb
> Blocks: 2 Seconds: 32
> contract address: 0x70c86b8d660eBd0adef24E9ACcb389BFb6611B2b
> block number: 8903912
> block timestamp: 1603052602
> account: 0x610Ae88399fc1687FA7530Aac28eC2539c7d6d63
> balance: 4.98206016
> gas used: 237925 (0x3a165)
> gas price: 20 gwei
> value sent: 0 ETH
> total cost: 0.0047585 ETH
​
​
> Saving migration to chain.
> Saving artifacts
-------------------------------------
> Total cost: 0.0047585 ETH
​
Summary
=======
> Total deployments: 2
> Final cost: 0.00812422 ETH

Once this is finished without errors you will have deployed the contract, check it out on https://ropsten.etherscan.io/ by searching for your transaction hash or contract address!! πŸŽ‰

If you head over to your Alchemy dashboard and click on the app details for your Hello World app you can see that there are requests flowing through. These requests are made in order to fulfill the deployment of your smart contract.

Part 2: Interact with your Smart Contract

Now that we've successfully deployed a smart contract to the ropsten network, let's test out our web3 skills and interact with it!

Step 1: Install web3

We need install a web3 package in order to interact with the Ethereum blockchain. We'll be using Alchemy web3 in this example, however, there are a handful of other web3 providers you can choose from.

In your project directory run:

npm install @alch/alchemy-web3

Step 2: Create a contract-interact.js file

In your project directory create a contract-interact.js file and add the following lines of code:

require('dotenv').config();
const API_URL = process.env.API_URL;
const { createAlchemyWeb3 } = require("@alch/alchemy-web3");
const web3 = createAlchemyWeb3(API_URL);

Step 3: Grab your contract ABI

Our contract ABI (Application Binary Interface) is the interface to interact with our smart contract. Learn more about Contract ABIs here. Truffle automatically generates an ABI for us and saves it in the HelloWorld.json file. In order to use this we'll need to parse out the contents by adding the following lines of code to our contract-interact.js file:

const contract = require("./build/contracts/HelloWorld.json");

If you want to see the ABI you can print it to your console:

console.log(JSON.stringify(contract.abi));

To run contract-interact.js, navigate to your terminal and run

node contract-interact.js

Step 4: Create an instance of your contract

In order to interact with our contract we need to create an instance of it in our code. To do so we'll need our contract address which we can get from the deployment or Etherscan by looking up the address you used to deploy the contract. In the above example our contract address is 0x70c86b8d660eBd0adef24E9ACcb389BFb6611B2b .

Next we will use the web3 contract method to create our contract using the ABI and address:

const contractAddress = "0x70c86b8d660eBd0adef24E9ACcb389BFb6611B2b";
const helloWorldContract = new web3.eth.Contract(contract.abi, contractAddress);

Step 5: Read the init message

Remember when we deployed our contract with theinitMessage = "Hello world!"? We are now going to read that message stored in our smart contract and print it to the console.

In JavaScript we use asynchronous functions to interacting with networks. Check out this article to learn more about this.

Use the below code to call the message function in our smart contract and read the init message:

async function main() {
const message = await helloWorldContract.methods.message.call();
console.log("The message is: " + message);
}
main();

After running the file using node contract-interact.js in the terminal we should see this response:

The message is: Hello world!

Congrats! You've just successfully read smart contract data from the Ethereum blockchain, way to go!

Step 6: Update the message

Now instead of just reading the message, we'll update the messaged saved in our smart contract using the update function. β€Œ

In order to do so we'll need to create a transaction, sign it, and send it inside another async function that we'll callupdateMessage(newMessage). This can be pretty confusing when you first get started so we'll split it up into multiple steps.

Step 7: Update the .env file

In order to create and send transactions to the Ethereum chain, we'll need to add a couple more things to our .env file.

  • PUBLIC_KEY: Your public ethereum account address, we'll need this to get the account nonce (will explain later)

  • PRIVATE_KEY: We need our private key from Metamask in order to sign our transaction. You can export the private key using these instructions. This ensures the transaction is coming from the account owner. Again, don't worry, this will live in our .env file!

Your .env file should now look like this:

API_URL = "https://eth-ropsten.alchemyapi.io/v2/your-api-key"
MNEMONIC = "your-metamask-seed-reference"
PUBLIC_KEY = "your-public-account-address"
PRIVATE_KEY = "your-private-account-address"

Step 8: Create the transaction

Define updateMessage(newMessage) and create our transaction, adding the following:

  1. First grab your PUBLIC_KEY and PRIVATE_KEY from the .env file.

  2. Next, we'll need to grab the 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.

  3. Next, we'll use eth_estimateGas to figure out the right amount of gas to include in order to complete our transaction. This avoids the risk of a failed transaction due to insufficient gas.

  4. Finally we'll create our transaction with the following info:

  • 'from': PUBLIC_KEY : The origin of our transaction is our public address

  • 'to': contractAddress : The contract we wish to interact with and send the transaction

  • 'nonce': nonce : The account nonce with the number of transactions send from our address

  • 'gas': estimatedGas : The estimated gas needed to complete the transaction

  • 'data': helloWorldContract.methods.update("<new message>").encodeABI() : The computation we wish to perform in this transaction (updating the contract message)

Your contract-interact.js file should look like this now:

require('dotenv').config();
const API_URL = process.env.API_URL;
const PUBLIC_KEY = process.env.PUBLIC_KEY;
const PRIVATE_KEY = process.env.PRIVATE_KEY;
​
const { createAlchemyWeb3 } = require("@alch/alchemy-web3");
const web3 = createAlchemyWeb3(API_URL);
​
const contract = require("./build/contracts/HelloWorld.json");
const contractAddress = "0x0d6261a5D3102b565B75Fc680B64093820a17612";
const helloWorldContract = new web3.eth.Contract(contract.abi, contractAddress);
​
async function updateMessage(newMessage) {
const nonce = await web3.eth.getTransactionCount(PUBLIC_KEY, 'latest'); // get latest nonce
const gasEstimate = await helloWorldContract.methods.update(newMessage).estimateGas(); // estimate gas
​
// Create the transaction
const tx = {
'from': PUBLIC_KEY,
'to': contractAddress,
'nonce': nonce,
'gas': gasEstimate,
'data': helloWorldContract.methods.update(newMessage).encodeABI()
};
}
​
​
async function main() {
const message = await helloWorldContract.methods.message.call();
console.log("The message is: " + message);
}
​
main();

Step 9: Sign the transaction

Now that we've created our transaction, we need to sign it in order to send it off. Here is where we'll use our private key.

web3.eth.sendSignedTransaction will give us the transaction hash, which we can use to make sure our transaction was mined and didn't get dropped by the network.

require('dotenv').config();
const API_URL = process.env.API_URL;
const PUBLIC_KEY = process.env.PUBLIC_KEY;
const PRIVATE_KEY = process.env.PRIVATE_KEY;
​
const { createAlchemyWeb3 } = require("@alch/alchemy-web3");
const web3 = createAlchemyWeb3(API_URL);
​
const contract = require("./build/contracts/HelloWorld.json");
const contractAddress = "0x0d6261a5D3102b565B75Fc680B64093820a17612";
const helloWorldContract = new web3.eth.Contract(contract.abi, contractAddress);
​
async function updateMessage(newMessage) {
const nonce = await web3.eth.getTransactionCount(PUBLIC_KEY, 'latest'); // get latest nonce
const gasEstimate = await helloWorldContract.methods.update(newMessage).estimateGas(); // estimate gas
​
// Create the transaction
const tx = {
'from': PUBLIC_KEY,
'to': contractAddress,
'nonce': nonce,
'gas': gasEstimate,
'data': helloWorldContract.methods.update(newMessage).encodeABI()
};
​
// Sign the transaction
const signPromise = web3.eth.accounts.signTransaction(tx, PRIVATE_KEY);
signPromise.then((signedTx) => {
web3.eth.sendSignedTransaction(signedTx.rawTransaction, function(err, hash) {
if (!err) {
console.log("The hash of your transaction is: ", hash, "\n Check Alchemy's Mempool to view the status of your transaction!"); //
} else {
console.log("Something went wrong when submitting your transaction:", err)
}
});
}).catch((err) => {
console.log("Promise failed:", err);
});
}
​
async function main() {
const message = await helloWorldContract.methods.message.call();
console.log("The message is: " + message);
}
​
main();

Step 10: Call updateMessage and run node contract-interact.js

Finally, we can call updateMessage with our new message by making an await call in main for updateMessage with your newMessage.

Then run node contract-interact.js in your terminal.

require('dotenv').config();
const API_URL = process.env.API_URL;
const PUBLIC_KEY = process.env.PUBLIC_KEY;
const PRIVATE_KEY = process.env.PRIVATE_KEY;
​
const { createAlchemyWeb3 } = require("@alch/alchemy-web3");
const web3 = createAlchemyWeb3(API_URL);
​
const contract = require("./build/contracts/HelloWorld.json");
const contractAddress = "0x0d6261a5D3102b565B75Fc680B64093820a17612";
const helloWorldContract = new web3.eth.Contract(contract.abi, contractAddress);
​
async function updateMessage(newMessage) {
const nonce = await web3.eth.getTransactionCount(PUBLIC_KEY, 'latest'); // get latest nonce
const gasEstimate = await helloWorldContract.methods.update(newMessage).estimateGas(); // estimate gas
​
// Create the transaction
const tx = {
'from': PUBLIC_KEY,
'to': contractAddress,
'nonce': nonce,
'gas': gasEstimate,
'data': helloWorldContract.methods.update(newMessage).encodeABI()
};
​
// Sign the transaction
const signPromise = web3.eth.accounts.signTransaction(tx, PRIVATE_KEY);
signPromise.then((signedTx) => {
web3.eth.sendSignedTransaction(signedTx.rawTransaction, function(err, hash) {
if (!err) {
console.log("The hash of your transaction is: ", hash, "\n Check Alchemy's Mempool to view the status of your transaction!"); //
} else {
console.log("Something went wrong when submitting your transaction:", err)
}
});
}).catch((err) => {
console.log("Promise failed:", err);
});
}
​
​
async function main() {
const message = await helloWorldContract.methods.message.call();
console.log("The message is: " + message);
await updateMessage("Hello Drupe!");
}
​
main();

You should see a response that looks like:

The message is: Hello world!
The hash of your transaction is: 0xd6b89d1e31d53b732afc461e04ed0cebc451cfe6e8470519fe06eb4295f5b504
Check Alchemy's Mempool to view the status of your transaction!

Next visit your Alchemy mempool to see the status of your transaction (whether it's pending, mined, or got dropped by the network). If your transaction got dropped, it's also helpful to check Ropsten Etherscan and search for your transaction hash.

Once your transaction gets mined, comment out the await updateMessage("Hello Drupe!"); line in main() and re-run node contract-interact.js to print out the new message.

Your main() should look like (everything else in your code should stay the same)

async function main() {
const message = await helloWorldContract.methods.message.call();
console.log("The message is: " + message);
// await updateMessage("Hello Drupe!");
}

After running node contract-interact.js you should now see the new message printed to your console:

The message is: Hello Drupe!

And that's it! You've now deployed AND interacted with an Ethereum smart contract -- now it's time to take it live! πŸŽ‰β€‹

[OPTIONAL] Part 3: Publish your Smart Contract to Etherscan, so anyone can interact with it!

You did all the hard work of bringing your smart contract to life - now it's time to share it with the world!

By verifying your smart contract on Etherscan, anyone can view your source code and interact with your smart contract. Let's get started!

Step 1: Install the truffle-plugin-verify plugin

We need the truffle-plugin-verify plugin to automatically verify your truffle smart contract's source code and ABI on Etherscan. In your project directory run:

npm install -g truffle-plugin-verify

Once installed, add the plugin to your truffle-config.js file. Your file should look similar to this.

const HDWalletProvider = require("@truffle/hdwallet-provider");
require('dotenv').config()
​
const { API_URL, MNEMONIC } = process.env;
​
module.exports = {
networks: {
development: {
host: "localhost",
port: 8545,
network_id: "*", // Match any network id
gas: 5000000
},
ropsten: {
provider: function() {
return new HDWalletProvider(MNEMONIC, API_URL)
},
network_id: 3
}
},
compilers: {
solc: {
settings: {
optimizer: {
enabled: true, // Default: false
runs: 200 // Default: 200
},
}
}
},
plugins: ['truffle-plugin-verify'] //PLUGIN ADDED HERE
};

Step 2: Generate an API Key on your Etherscan account

An Etherscan API Key is necessary to verify that you're the owner of the smart contract that you're trying to publish.

If you don't have an Etherscan account, first sign up using this link.

Once logged in, press your username on the top right, and select the "My profile" button:

​

Next, navigate to the "API-KEYs" button on the left tab bar. Then press the "Add" button, name your app whatever you wish (we chose hello-world), and then select continue.

Once you've followed the steps above, you should be able to view your new API key, which we've highlighted in red below. Copy this API key to your clipboard.

Step 3: Add your Etherscan API key to your truffle-config.jsfile.

You're almost at the finish line! πŸ˜…

Let's update your .env file to include your Etherscan API Key. Your .envfile should now look like this:

API_URL = "https://eth-ropsten.alchemyapi.io/v2/your-api-key"
MNEMONIC = "your-metamask-seed-reference"
PUBLIC_KEY = "your-public-account-address"
PRIVATE_KEY = "your-private-account-address"
ETHERSCAN_API_KEY = "your-etherscan-key"

Now update your truffle-config.js file to include your Etherscan API key. See the bottom of the code below for reference:

const HDWalletProvider = require("@truffle/hdwallet-provider");
require('dotenv').config()
​
const { API_URL, MNEMONIC } = process.env;
​
module.exports = {
networks: {
development: {
host: "localhost",
port: 8545,
network_id: "*", // Match any network id
gas: 5000000
},
ropsten: {
provider: function() {
return new HDWalletProvider(MNEMONIC, API_URL)
},
network_id: 3
}
},
compilers: {
solc: {
settings: {
optimizer: {
enabled: true, // Default: false
runs: 200 // Default: 200
},
}
}
},
plugins: ['truffle-plugin-verify'],
api_keys: {
etherscan: process.env.ETHERSCAN_API_KEY
}
​
};

Step 4: Let's publish your smart contract to the world!

Last but not least, run the following command in your terminal:

truffle run verify HelloWorld --network ropsten

If all goes well, you should see the following message in your terminal.

Verifying HelloWorld
Pass - Verified: https://ropsten.etherscan.io/address/<contract-address>#contracts
Successfully verified 1 contract(s).

When you navigate to the link provided in your terminal, you should be able to see your smart contract code and ABI published on Etherscan!

Wahooo - you did it champ! Now anyone can call or write to your smart contract! We can't wait to see what you build next!πŸŽ‰β€‹