Blockchain Developers Guide
  • Elena R
    author-profile

    Elena R right arrow

    Author

    Elena is an expert in technical analysis and risk management in cryptocurrency market. She has 10+year experience in writing - accordingly she is avid journalists with a passion towards researching new insights coming into crypto erena.

    • author twitter

Smart Contracts 101: How to Write, Test, and Deploy Your First Contract

Introduction

Blockchain is a huge decentralized distributed database. It is designed in such a way that it is ideal for industries where transparency, immutability, tamper-proofing, and decentralization are priorities. Blockchain has expanded its domain in various fields such as Finance, cryptocurrencies, Supply chain, and Healthcare.

Introduction to Smart Contracts

What Are Smart Contracts?

Smart contracts are self-executing programs stored on the blockchain network. They contain the code for transactions, agreements, and contract terms. Due to their immutable and transparent nature, smart contracts are ideal for decentralized applications (dApps). Essentially, they enforce contract terms automatically when predefined conditions are met.

Why should you Write and Deploy Smart Contracts for dApps?

Decentralization as the name refers means no centralized authority and intermediaries. Smart contracts are automated without needing any third-party interference. Smart contracts allow deals without trust cut down on cheating risk, and make many steps work better. Hence smart contracts are a good choice for dApps.Getting good at writing and deploying smart contracts is key for developers who want to build strong and safe dApps.

In this article, we will walk you through a step-by-step guide on writing and deploying smart contracts. Get ready! We are now going to dive into the practical aspects of writing, deploying, and integrating smart contracts within decentralized applications (DApps), exploring the tools, best practices, and real-world use cases that make these technologies transformative.

A Comprehensive Guide to Smart Contracts

Setting Up Your Development Environment

Before you begin coding, it’s essential to establish a solid development environment and choose the right tools.

Integrated Development Environments (IDEs):

Several IDEs are popular among developers for smart contract development and testing:

  • Remix: Remix is an online IDE tailored specifically for Solidity smart contracts. It offers an interactive interface and powerful debugging tools, providing a user-friendly experience with no setup required. Simply visit the site and start coding.
  • VS Code: Visual Studio Code (VS Code) is a widely-used IDE that supports multiple programming languages and includes extensions for Solidity. It provides a versatile development environment and integrates seamlessly with other tools and plugins.

Setting Up VS Code

  • Download it from the official site
  • Follow the on screen instructions
  • Install all the Solidity  extensions needed to write smart contracts 

Frameworks and Libraries:

Frameworks and libraries ease the development process, making it more structured and streamlined. Let’s take a look at the ones supporting smart contracts.

  • Truffle: It is a development framework that provides a set of tools for the development, compilation, linking, and deployment of smart contracts.
  • Hardhat: Hardhat is a popular choice among developers due to its features like flexibility and extensibility. Also, it has an integrated task runner, and network management ability, and can extend its functionalities through plugins.
  • Brownie: Brownie is a Python-based framework for Ethereum smart contract development.
  • Setting up the frameworks is just writing these commands in your terminal and you are ready to go:
Install Node.js and setup npm
Install Truffle: npm
install -g truffleInstall Hardhat:  install –save-dev hardhat
Install Ganache
pip install eth-brownie

Life Cycle of a Smart Contract:

Let’s have a look at the Lifecycle of the Smart contract

Writing Smart Contracts

Now, let’s get to the fun part—writing your first smart contract! 

Contract Structure:

Before we jump to the technical part let us understand the core concepts and structure of a Smart Contract. A smart contract is a small program that runs on the blockchain—every element has a specific purpose in making this program both functional and secure.

 A smart contract is comprised of the following components:

  • State Variables: They are used to store the contract data.
  • Functions: Functions are the operations we need to perform on the contract data
  • Events: These are the log activities that can be observed externally
  • Modifiers: These control the access to functions.

Here is the practically written structure of the Smart Contract:

pragma solidity ^0.8.0;
contract SimpleStorage {    uint256 public storedData; //state variable
    
event DataStored(uint256 data); //event
    
function set(uint256 x) public { //function        
storedData = x;        
emit DataStored(x);   
 }
    function get() public view returns (uint256) {        
return storedData;    
}
}

Common Design Patterns:

When developing decentralized applications, using design patterns is a smart way to write efficient, maintainable contracts. Patterns are used to enhance the scalability of the smart contract. Here are the two most widely used patterns: 

Factory Pattern: The Factory pattern influences the creation of many instances of a contract. This comes in handy to create new contracts on the fly, like setting up new versions of a contract for different users. It is helpful in a multi-user scenario.

Example code snippet

pragma solidity ^0.8.0;
contract Token {    string public name;
    
constructor(string memory _name) {        
name = _name;    
}
}
contract TokenFactory {    
Token[] public deployedTokens;
    
function createToken(string memory _name) public {        
Token newToken = new Token(_name); // Create a new Token contract
instance        

deployedTokens.push(newToken); // Store the instance in an array    
}
}

The function TokenFactory() is used to store the values of contract tokens dynamically.

Proxy Pattern: This pattern enables the contract upgradeability by delegating calls to an implementation contract. Fundamentally smart contracts are immutable but a Proxy pattern provides a way for users to change the logic and upgrade the contract over time.

Example code snippet

pragma solidity ^0.8.0;

contract Proxy {    
address public implementation; // Address of the current implementation
    
function upgrade(address _newImplementation) public {        
implementation = _newImplementation; // Upgrade the logic    
}
    fallback() external payable {        
address _impl = implementation;        
require(_impl != address(0));        
(bool success, ) = _impl.delegatecall(msg.data); // Delegate calls to the implementation        require(success);    
}
}

Advanced Features:

These features make smart contracts more modular, reusable and powerful.

  • Inheritance: Inheritance allows smart contracts to inherit properties and methods from other contracts, similar to object-oriented programming. This feature establishes a parent-child relationship between contracts, enabling the creation of modular and reusable code.
  • Libraries: Libraries are the universal reusable code snippets that enhance the readability and modularity of the code by reducing redundancy.
  • Interfaces and Abstract Contracts: Interfaces and abstract contracts define the structure of contracts without implementing functions. This promotes modularity by allowing developers to design contract blueprints that can be implemented in various ways.

Smart Contract Security

Common Vulnerabilities:

Security is paramount in Blockchain hence implementing best security practices is a must. Smart contracts hold real value and a little ignorance can lead to big impacts. The DAO hack of 2016 is a prime example of how vulnerable contracts can be if security best practices aren’t followed. Some common vulnerabilities in this domain are Reentrancy and Integer Overflow/underflow.

  • Reentrancy: This is the phenomenon where attackers call a contract repeatedly before the previous execution is completed.
  • Integer Overflow/Underflow: These are errors occurring when the calculations exceed the maximum or minimum values. 
  • Front Running: Attackers Premetibvely execute the transaction.

 Best Security Practices:

  • Use the latest Complier version
  • Follow the checks-effects-interaction
  • Limit the amount of code in fallback functions
  • Use the SafeMath library to prevent over and underflows.

Audit and Testing:

Once you are done with the writing part having regular audits and testing your smart contracts is the heart of the development life cycle.

MythX

MythX is a security analysis tool that performs security scans in your smart contracts.

 Steps to integrate MythX into the development workflow:

  • Sign up for MythX,
  • configure your project 
  • run scans to identify
  • fix security issues.                     

Slither

Slither looks for bugs in Solidity contracts. It’s a tool that doesn’t run the code but checks it for problems. Slither checks your code to find common safety issues. It also gives ideas to make your code better.

Performing security audits on smart contracts:

  • Review the code for vulnerabilities
  • Use automated tools
  • Address the identified issues before the final deployment.

Example:

const { expect } = require(“chai”);
describe(“SimpleStorage”, function () {    
it(“Should store and retrieve the correct value”, async function () {        
const SimpleStorage = await
ethers.getContractFactory(“SimpleStorage”);        
const simpleStorage = await SimpleStorage.deploy();        
await simpleStorage.set(42);
        expect(await simpleStorage.get()).to.equal(42);    
});
});

Smart Contract Deployment

The next step after you are sure and content with the test and audit results of your contracts is Deployment. For Ethereum-based projects, tools like Truffle or Hardhat simplify the process.

Deployment Strategies:

Getting Smart Contracts Ready for Launch:

Code Optimization: Clean up and improve code to use less gas, which affects how much it costs to launch. Use tools like solc and hardhat-gas-reporter to check how much gas your code uses.

Gas Management: Keep a close eye on gas limits and prices to avoid failed transactions and high costs. Make sure your contract functions work well and don’t try to do too many complex things in one go.

Deployment Process Using Truffle, Hardhat, or Brownie:

(bash)
truffle migrate –network <network_name>

(hardhat)
async function main() {    
const SimpleStorage = await
ethers.getContractFactory(“SimpleStorage”);    
const simpleStorage = await SimpleStorage.deploy();    
await simpleStorage.deployed();    
console.log(“SimpleStorage deployed to:”, simpleStorage.address);
}

(brownie)
def main():    
SimpleStorage.deploy({‘from’: accounts[0]})

Deployment Scripts: Create scripts to automate the deployment process. These scripts handle contract deployment set up configurations, and start the system.

async function main() {    
const [deployer] = await ethers.getSigners();    
console.log(“Deploying contracts with the account:”, deployer.address);
    
const SimpleStorage = await
ethers.getContractFactory(“SimpleStorage”);    
const simpleStorage = await SimpleStorage.deploy();    
console.log(“SimpleStorage address:”, simpleStorage.address);
}
main()  
.then(() => process.exit(0))  
.catch((error) => {    
console.error(error);    
process.exit(1);  
});
//Hardhat configuration
module.exports = { 

 networks: {    
ropsten: {      
url: “https://ropsten.infura.io/v3/YOUR-PROJECT-ID”,      
accounts: [`0x${YOUR_PRIVATE_KEY}`]    
},    
mainnet: {      
url: “https://mainnet.infura.io/v3/YOUR-PROJECT-ID”,      
accounts: [`0x${YOUR_PRIVATE_KEY}`]    
}  
}
};

Verification and Validation:

Use services like Etherscan to verify and publish your contract’s source code.

npx hardhat verify –network mainnet DEPLOYED_CONTRACT_ADDRESS “Constructor argument 1”

Interacting with Deployed Contracts

Once your contract is deployed, you’ll need to interact with it using a front-end interface. Libraries like Web3.js or ethers.js allow you to communicate with smart contracts from JavaScript.

Web3 Integration:

  • We use Web3.py, and ether.js for interaction with the deployed contacts and front-end integration.
  • Here are some example code snippets:
from web3 import Web3

# Connect to the Ethereum networkweb3 = Web3(Web3.HTTPProvider(‘https://mainnet.infura.io/v3/YOUR_INFURA_PROJECT_ID’))

# Define the contract ABI and address
abi =
‘[{“constant”:false,”inputs”:[{“name”:”x”,”type”:”uint256″}],”name”:”set”,
“outputs”:[],”payable”:false,”stateMutability”:”nonpayable”,”type”:”function”},{“constant”:true,”inputs”:[],”name”:”get”,”outputs”:[{“name”:””,”type”:”uint256″}],”payable”:false,”stateMutability”:”view”,”type”:”function”}]’

contract_address = ‘0xYourContractAddress’
# Create a contract instance
contract = web3.eth.contract(address=contract_address, abi=abi)

# Call the contract’s functions
stored_data = contract.functions.get().call()
print(f’Stored Data: {stored_data}’)

# Send a transaction to modify the contract’s state
tx_hash = contract.functions.set(42).transact({‘from’: web3.eth.accounts[0]})
web3.eth.waitForTransactionReceipt(tx_hash)

Following is the code snippet for the frontend integration using Web3,js

<!DOCTYPE html>
<html>
<head> 
 <title>Simple Storage</title>  
<script src=”https://cdn.jsdelivr.net/npm/web3@latest/dist/web3.min.js”></script></head><body>  
<h1>Simple Storage</h1>  
<p id=”storedData”></p>  
<button onclick=”setData()”>Set Data</button>
  
<script>    
const web3 = new Web3(Web3.givenProvider || ‘http://127.0.0.1:8545’);    
const contractAddress = ‘0xYourContractAddress’;    
const abi = [ /* ABI from the contract */ ];    
const contract = new web3.eth.Contract(abi, contractAddress);
    
async function setData() {
const accounts = await web3.eth.requestAccounts();
await contract.methods.set(42).send({ from: accounts[0] })
getData();

   
    getData();
  </script>
</body>
</html>

Transaction Management:

Sending transactions involves invoking functions on your smart contract, which typically modify the blockchain state. Here’s how you can do it using different libraries:

web3.js
const Web3 = require(‘web3’);
const web3 = new Web3(‘https://mainnet.infura.io/v3/YOUR-PROJECT-ID’);
const contract = new web3.eth.Contract(abi, contractAddress);

const sendTransaction = async () => {  
const receipt = await contract.methods.set(42).send({ from: userAddress });  
console.log(“Transaction receipt:”, receipt);
};
ether.js
const { ethers } = require(‘ethers’);
const provider = new ethers.providers.InfuraProvider(‘mainnet’,
‘YOUR-PROJECT-ID’);
const wallet = new ethers.Wallet(‘YOUR-PRIVATE-KEY’, provider);
const contract = new ethers.Contract(contractAddress, abi, wallet);

const sendTransaction = async () => {  
const tx = await contract.set(42);  
const receipt = await tx.wait();  
console.log(“Transaction receipt:”, receipt);};

web3.py 

from web3 import Web3web3 =
Web3(Web3.HTTPProvider(‘https://mainnet.infura.io/v3/YOUR-PROJECT-ID’))contract = web3.eth.contract(address=contract_address, abi=abi)

tx = contract.functions.set(42).buildTransaction({    
‘from’: user_address,    
‘nonce’: web3.eth.getTransactionCount(user_address),    
‘gas’: 2000000,    ‘
gasPrice’: web3.toWei(’50’, ‘gwei’)
})

signed_tx = web3.eth.account.sign_transaction(tx, private_key)tx_hash = web3.eth.sendRawTransaction(signed_tx.rawTransaction)receipt = web3.eth.waitForTransactionReceipt(tx_hash)print(“Transaction receipt:”, receipt)

Smart contracts can emit events that can be listened to by your application. Here’s how you can handle events:

ether.js
contract.events.DataStored()  
.on(‘data’, (event) => {    
console.log(event.returnValues);  
})  
.on(‘error’, console.error);
web3.py
event_filter = contract.events.DataStored.createFilter(fromBlock=’latest’)while True:    
for event in event_filter.get_new_entries():        
print(event[‘args’])

Error Handling and Retries in Transaction Processing

Blockchain transactions can fail for many reasons. These include running out of gas or network congestion. To build a reliable app, you need to handle errors well and try again when things go wrong.

const sendTransaction = async () => {  
try {    
const receipt = await contract.methods.set(42).send({ from:
userAddress });    
console.log(“Transaction successful:”, receipt);  } catch (error) {    
console.error(“Transaction failed:”, error);    
// Implement retry logic    
if (shouldRetry(error)) {      
setTimeout(sendTransaction, 1000); // Retry after 1 second    
}  
}
};
const shouldRetry = (error) => {  
// Define retry logic based on the error type  
return error.message.includes(‘network congestion’) ||
error.message.includes(‘out of gas’);
};

Upgrading and Maintaining Smart Contracts

Upgradeability Patterns:

Implementing proxy patterns for contract upgradeability:

Proxy patterns give you the ability to upgrade smart contracts by keeping the logic separate from the storage. The proxy sends calls to the logic contract while keeping the same storage layout.

Solidity:
contract Proxy {    
address implementation;
    
function upgrade(address newImplementation) public {        
implementation = newImplementation;    }
    
fallback() external payable {        
address _impl = implementation;        
require(_impl != address(0), “Implementation contract not set”);        
assembly {            
let ptr := mload(0x40)            
calldatacopy(ptr, 0, calldatasize())            
let result := delegatecall(gas(), _impl, ptr, calldatasize(),
0, 0)            
let size := returndatasize()            
returndatacopy(ptr, 0, size)            
switch result            
case 0 { revert(ptr, size) }            
default { return(ptr, size) }        
}    
}
}

Handling state migrations and contract storage: When you upgrade contracts, you need to make sure the state stays intact and moves over. Write code to shift data from the old contract to the new one.

contract NewContract {    
uint256 public data;
    
function setData(uint256 _data) public {        
data = _data;    
}
}
contract Migration {    
function migrate(address oldContract, address newContract) public {        
uint256 data = OldContract(oldContract).data();        
NewContract(newContract).setData(data);    
}
}

Maintaining Contracts:

Best Practices to Maintain and Update Contracts Over Time:

  • Regular Audits: Conduct security audits at set intervals to spot and correct weak points.
  •  Monitoring: Keep an eye on contract performance and security with specialized tools and services.
  •  Documentation: Keep thorough records to make updates and maintenance easier.

Monitoring Deployed Contracts for Performance and Security:

Use tools like Etherscan, Tenderly, and custom scripts to track contract activity, performance, and possible security threats. Check logs and transaction histories often to spot anything unusual.

When you stick to these practices, you make sure your smart contracts stay secure work well, and can adapt to future changes and upgrades.

Real-World Applications and Case Studies

We can take the example of Uniswap: Decentralized exchange.

Overview of the project: 

Uniswap is a decentralized exchange (DEX) built on the Ethereum blockchain. Unlike traditional exchanges that rely on order books, Uniswap uses an automated market maker (AMM) model to facilitate ERC-20 token trading through liquidity pools. Users can trade tokens directly from their wallets, providing liquidity to pools and earning fees in return.

Key Parts: 

  • Liquidity Pools: In this phenomenon, users deposit equal values for two tokens in a pool to allow others to trade.
  • AMM System: This system determines the price of tokens based on the ratio of tokens in the pool, eliminating the need for an order book.
  • Main Contracts: The core contracts manage the creation of new trading pairs, execution of trades, and liquidity management.
  • Contract for creating new pairs: Allows users to create new trading pairs for any ERC-20 tokens.

Good Practices:

  • Have Regular Safety Checks
  • Gas Optimization
  • Community decision making

Project Development:

To develop an entire project from scratch you need to have a framework in mind that has all the necessary steps. This can be a checklist and help you develop the application efficiently. Take a quick look at the Flow chart:

Define the Project Scope: problem identification and research
Choose the right blockchain platform
Design the Smart contract: Check the architecture, security and Optimization
Develop and Test
Deploy and maintain

Key considerations for project planning and execution.

  • Clear Objectives
  • Stake Holder Engagement
  • Resource allocation
  • User Experience(UX)
  • Timeline and Milestones
  • Risk Management
  • Community engagement
  • Regulatory Compliance
  • Quality Assurance
  • Sustainability
  • Technology and Tools
  • Monitoring and evaluation
  • Change Management

Conclusion

Smart contracts are the main elements of blockchain development that enable trustless, decentralized, and automated interactions. From writing secure contracts to deploying and interacting with them, you’ve now gained a solid foundation to build your own dApps. This is just the stepping stone of the entire journey ahead. Keep exploring the advanced features and update yourself to excel in every part of life.

Stay curious and keep up with changes in rules and industry guidelines. Change how you build things to use new tools and systems making sure your smart contracts stay useful and work well as the blockchain world grows and changes. Happy coding!!

Let’s talk about new tech and trends, like layer 2 solutions and cross-chain interoperability. These could shake things up in smart contract development. This game plan helps developers learn step-by-step how to write, deploy, and keep smart contracts running. It’s key for building decentralized apps you can count on.

Also Check Out: How to Build Your First Blockchain with Plutus: A Step-by-Step Tutorial

completed lesson

Well Done! You have now completed the Lesson.

Complete the Quiz and Get Certified! All The Best!
Show More

Disclaimer and Risk Warning

The information provided in this content by Coinpedia Academy is for general knowledge and educational purpose only. It is not financial, professional or legal advice, and does not endorse any specific product or service. The organization is not responsible for any losses you may experience. And, Creators own the copyright for images and videos used. If you find any of the contents published inappropriate, please feel free to inform us.

Table of Contents
Back to top button