Reading about smart contracts is one thing.
Writing one yourself is where the idea really starts to make sense.
In the previous LearnTheCrypt guide, we looked at Solidity, the programming language used to write many Ethereum smart contracts. We covered variables, functions, addresses, mappings, events and gas.
Now we’re going to put some of that knowledge together.
We’re going to build a very small smart contract, compile it, deploy it locally, and interact with it.
And don’t worryโyou won’t need to buy ETH or deploy anything to the Ethereum mainnet for this tutorial. We’ll use Remix VM, a simulated blockchain environment built into Remix. Remix’s documentation recommends this workflow for creating, compiling, deploying and interacting with a basic contract.
The contract we’ll build isn’t going to revolutionize finance.
It’s going to store a message.
That’s intentional.
When you’re learning blockchain development, understanding a tiny contract completely is much more useful than copying 500 lines of code you don’t understand.
What we’re going to build

Our contract will allow us to:
- Store a message on the blockchain
- Read the current message
- Change the message
- Keep track of who deployed the contract
The final contract will look like this:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract MessageBox {
string public message;
address public owner;
constructor(string memory initialMessage) {
message = initialMessage;
owner = msg.sender;
}
function setMessage(string memory newMessage) public {
message = newMessage;
}
}
It isn’t a complicated contract, but it contains several important Solidity concepts.
Let’s build it from scratch.
Step 1: Open Remix
For this tutorial, we’ll use Remix, a browser-based development environment for Ethereum smart contracts.
Remix lets you create Solidity files, compile contracts and deploy them without having to set up a full development environment on your computer.
Create a new Solidity file and call it:
MessageBox.sol
Remix uses .sol as the standard extension for Solidity files.
If you’re following along in the browser, make sure you’re working in Remix itself rather than downloading random Solidity files or connecting your wallet to an unfamiliar website.
For this tutorial, we don’t need a real wallet or real cryptocurrency.
Step 2: Add the license and compiler version
Start the file with:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
The first line identifies the license for the source code.
The second tells Solidity which compiler version range the contract is intended to work with.
Then we’ll create the contract:
contract MessageBox {
}
Everything between those curly brackets belongs to our contract.
So we now have:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract MessageBox {
}
Not much yet.
But this is the skeleton of our smart contract.
Step 3: Create a message variable
We want our contract to remember a message.
Add:
string public message;
So the contract becomes:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract MessageBox {
string public message;
}
Let’s break that line down.
string means we’re storing text.
message is the name of the variable.
public means the variable can be read externally.
Solidity also automatically creates a getter for a public state variable.
That means we don’t need to write a separate function just to retrieve the message.
Pretty convenient.
Step 4: Add an owner
Let’s add another variable:
address public owner;
Now we have:
contract MessageBox {
string public message;
address public owner;
}
An Ethereum address is represented by Solidity’s address type.
We’re going to use this variable to remember the address that deployed the contract.
Step 5: Add a constructor
Now things get a little more interesting.
We want whoever deploys the contract to provide the initial message.
That’s what the constructor will handle.
Add:
constructor(string memory initialMessage) {
message = initialMessage;
owner = msg.sender;
}
Our contract is now:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract MessageBox {
string public message;
address public owner;
constructor(string memory initialMessage) {
message = initialMessage;
owner = msg.sender;
}
}
The constructor runs when the contract is deployed.
It isn’t something you repeatedly call later.
In our example, it does two things.
First:
message = initialMessage;
This stores the message provided during deployment.
Second:
owner = msg.sender;
This stores the address that deployed the contract.
What is msg.sender?
You’ll see msg.sender constantly when working with Solidity.
It represents the address responsible for the current call.
In our constructor, it means the address deploying the contract.
Later, if someone calls a function, msg.sender represents the address that made that call.
This is incredibly useful for things such as access control.
For example, a contract could say:
require(msg.sender == owner, "Not the owner");
That would allow the contract to check whether the person calling the function is the owner.
We’re not going to add that restriction yet because we want to keep this first contract simple.
Step 6: Add a function to change the message
We can already store and read the message.
But what if we want to change it?
Add this function:
function setMessage(string memory newMessage) public {
message = newMessage;
}
The complete contract is now:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract MessageBox {
string public message;
address public owner;
constructor(string memory initialMessage) {
message = initialMessage;
owner = msg.sender;
}
function setMessage(string memory newMessage) public {
message = newMessage;
}
}
That’s it.
You’ve just written your first basic Ethereum smart contract.
But writing it is only half the process.
Now we need to compile it.
Step 7: Compile the contract
In Remix, open the Solidity Compiler section.
Select a compiler version compatible with the version specified in your pragma.
Then compile MessageBox.sol.
Remix supports compiling directly through its Solidity Compiler plugin, and successful compilation produces the artifacts needed for deployment.
If everything is correct, you should see a successful compilation message.
If you get an error, don’t immediately assume your entire contract is broken.
Compiler errors are normal when learning.
Read the error carefully.
Usually, Solidity will give you a line number and some indication of what it doesn’t understand.
Step 8: Deploy it using Remix VM
Now open Deploy & Run Transactions.
You’ll see an environment selector.
Choose:
Remix VM
This gives you a simulated blockchain environment with test accounts.
The important part is that these aren’t your real funds.
You can experiment without paying actual Ethereum network fees.
Remix’s documentation describes the basic workflow as compiling a contract and deploying it to the local simulated blockchain provided by Remix VM.
Make sure MessageBox is selected as the contract.
You’ll also notice an input field for the constructor parameter.
That’s because our constructor expects:
constructor(string memory initialMessage)
Enter something like:
Hello from LearnTheCrypt
Then click Deploy.
Step 9: Find your deployed contract
After deployment, look under the Deployed Contracts section.
You should see your MessageBox contract.
Expand it.
You’ll see functions and variables that you can interact with.
You should see something similar to:
message
owner
setMessage
This is where the contract becomes interactive.
Step 10: Read the message
Click the message button.
Because message was declared as:
string public message;
Solidity automatically created a getter for it.
You should see:
Hello from LearnTheCrypt
or whatever message you entered during deployment.
Notice something important here.
You didn’t send a transaction just to read the value.
You’re simply retrieving information already stored on the blockchain.
Step 11: Check the owner
Now click:
owner
You should get an Ethereum address.
That’s the address associated with the account that deployed the contract.
Remember this line?
owner = msg.sender;
That’s where the value came from.
Remix’s simulated environment provides accounts you can use to test transactions and contract interactions.
Step 12: Change the message
Now find:
setMessage
You’ll see an input box.
Enter:
Learning Solidity is getting interesting.
Then click the button.
Unlike simply reading message, this operation changes the contract’s stored state.
That means a transaction is created.
In Remix VM, the simulated transaction is processed immediately.
On an actual blockchain network, the transaction would need to be submitted and included in a block.
Now click message again.
You should see your new message.
Congratulations.
You’ve just changed blockchain state using a smart contract.
What actually happened?
It can be tempting to think that Remix simply changed a value on a webpage.
It didn’t.
Your Solidity contract was compiled into executable EVM code.
You deployed that contract to the simulated blockchain.
When you called setMessage, the function modified the contract’s stored state.
The simplified flow looked like this:
Solidity code
โ
Compiler
โ
Contract bytecode
โ
Deployment
โ
Smart contract on the blockchain
โ
Transaction
โ
Contract state changes
That’s the basic lifecycle you’ll encounter again and again in Ethereum development.
Why does setMessage cost gas?
Look at our function:
function setMessage(string memory newMessage) public {
message = newMessage;
}
It changes the value stored by the contract.
Changing blockchain state requires network resources.
On Ethereum, those resources are measured using gas.
Because Remix VM is simulated, you don’t have to pay real ETH while experimenting.
But if you eventually deploy a contract to a public blockchain, transactions that modify state will involve network fees.
This is why smart contract developers care so much about gas efficiency.
A contract that performs unnecessary computation can become expensive to use.
Why doesn’t reading message cost gas?
When you click the automatically generated message getter, you’re reading information rather than changing it.
There’s no state change.
That’s why you can retrieve the value without submitting a normal blockchain transaction.
This distinction is one of the first practical concepts every Solidity developer should understand:
Reading data is different from changing data.
Let’s add an access restriction
Our contract currently has a problem.
Anyone can call:
setMessage()
The owner can change it.
A random address can also change it.
That’s not necessarily a bugโit depends on what the contract is supposed to do.
But suppose we want only the owner to change the message.
We can add a check:
require(msg.sender == owner, "Only the owner can change the message");
Our function becomes:
function setMessage(string memory newMessage) public {
require(msg.sender == owner, "Only the owner can change the message");
message = newMessage;
}
Now the contract checks who is calling the function.
If the caller isn’t the owner, the transaction fails.
This introduces one of the most important ideas in smart contract development:
Access control.
Real-world contracts use access-control mechanisms extensively to determine who can perform sensitive actions.
Why should you test this?
This is where Remix VM becomes particularly useful.
Remix gives you multiple simulated accounts.
Deploy the contract using one account.
Then switch to another account and try calling setMessage.
The transaction should fail because the second account isn’t the owner.
Switch back to the original account and try again.
It should work.
You’ve now tested a simple permission system.
This kind of testing is extremely important before deploying anything to a real blockchain.
Our improved contract
After adding the ownership restriction, the contract looks like this:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract MessageBox {
string public message;
address public owner;
constructor(string memory initialMessage) {
message = initialMessage;
owner = msg.sender;
}
function setMessage(string memory newMessage) public {
require(
msg.sender == owner,
"Only the owner can change the message"
);
message = newMessage;
}
}
It’s still small.
That’s exactly what we want.
You can now understand almost every line in the contract.
What you just learned
In one small project, you’ve already encountered several important Solidity concepts:
State variables
string public message;
These store information in the contract’s state.
Addresses
address public owner;
These represent blockchain addresses.
Constructors
constructor(...)
These run during contract deployment.
msg.sender
This identifies the address associated with the current call.
Functions
function setMessage(...)
These define actions users can perform.
require
This allows the contract to enforce conditions.
State changes
Changing stored blockchain data requires a transaction.
That’s a lot for one tiny contract.
And that’s why starting small matters.
Don’t deploy this to mainnet yet
It might be tempting to think:
โGreat. I understand Solidity now. Time to deploy.โ
Not quite.
What you’ve built is a learning exercise, not production-ready software.
Real smart contracts need much more thorough testing.
You need to think about unexpected inputs, permissions, edge cases, gas usage, contract interactions and security vulnerabilities.
The fact that a contract compiles doesn’t mean it’s safe.
And the fact that it works once doesn’t mean it will work correctly in every situation.
That’s one of the biggest differences between learning Solidity and becoming a smart contract developer.
What’s next?
You’ve now gone from reading about smart contracts to actually writing and deploying one.
The next step is to make the contract a little more useful.
Instead of storing one simple message, we’ll start looking at tokens.
You’ll learn how token balances are represented, how transfers work, why mappings are so important, and how standards such as ERC-20 allow different Ethereum applications to understand and interact with tokens in a consistent way.
That takes us from:
โI built a smart contract.โ
to:
โI understand how real blockchain applications begin to work.โ
And that’s where Solidity starts getting much more interesting.





Leave a Reply