Building a Vehicle Rental Smart Contract Using Solidity
A practical introduction to structs, mappings, modifiers, and booking management in Ethereum smart contracts.
Introduction
Imagine renting a vehicle without depending entirely on a centralized rental management system. Vehicle information, customer registration, and booking records could be managed through a smart contract, with the rules enforced by code.
This is one practical application of blockchain technology.
In this tutorial, we'll explore how to build a basic Vehicle Rental Smart Contract using Solidity. The project focuses on managing vehicle records, registering users, creating rental bookings, and maintaining booking statuses.
This is an educational prototype developed and tested using Remix IDE. It is not deployed to a blockchain network, and its payment logic calculates rental amounts without transferring cryptocurrency.
By the end, you'll understand how several important Solidity concepts work together in a practical application.
- Understanding the project architecture
Before writing Solidity code, let's identify what our application needs to manage.
The contract has three primary components:
- Vehicle management: Stores vehicle details, rental prices, availability, seating capacity, and ownership.
- User management: Stores registered customer information.
- Booking management: Maintains booking records, rental duration, calculated amounts, and booking status.
The relationship looks like this:
Vehicle Rental Contract
|
---------------------
| | |
Vehicle User Booking
Records Records Records
| | |
Vehicle Customer Rental
Details Details Details
This structure helps separate the responsibilities of different parts of the contract.
- Setting up the development environment
For this project, I used "Remix IDE" (https://remix.ethereum.org/), a browser-based development environment for Solidity smart contracts.
You don't need to install a complete local blockchain development environment to follow the basic implementation.
Steps:
- Open Remix IDE.
- Create a new Solidity file named "VehiclRental.sol".
- Add the Solidity version declaration.
- Open the Solidity Compiler tab.
- Select a compatible compiler version.
- Compile the contract and review any errors.
The project uses Solidity version "0.8.13".
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract VehicleRental {
}
The SPDX identifier specifies the source-code license, while the pragma declaration specifies the compatible Solidity compiler version.
- Defining the data structures
One of the most useful Solidity features is the "struct" keyword.
A struct allows us to group related information into a single custom data type.
Our application requires three primary structures.
Vehicle structure
A vehicle needs information such as its registration number, name, rental price, availability, seating capacity, and owner.
struct Vehicle {
string veh_no;
string name;
uint price;
bool availability;
uint seating_cap;
address owner;
}
Each field serves a particular purpose:
- "veh_no": Vehicle registration identifier.
- "name": Vehicle name.
- "price": Rental price.
- "availability": Whether the vehicle is available.
- "seating_cap": Maximum seating capacity.
- "owner": Blockchain address of the vehicle owner.
User structure
We also need to maintain customer information.
struct User {
address renter;
string name;
uint age;
string phone_no;
}
The "renter" field identifies the customer's blockchain address.
Booking structure
The booking structure connects the customer with the rented vehicle.
enum BookingStatus {
Pending,
Booked,
Completed,
Cancelled
}
struct Booking {
uint book_id;
uint rent_time;
uint return_time;
uint total_amt;
string veh_no;
address renter;
BookingStatus status;
}
Using an enum instead of arbitrary status strings makes the booking states easier to manage.
For example, a booking can move through different stages:
Pending → Booked → Completed
|
└──────────→ Cancelled
This is a simplified conceptual flow; the contract must enforce valid state transitions in its functions.
- Using mappings to store information
Now that our data structures are ready, we need a way to access their records.
Solidity provides mappings for key-value storage.
Our contract uses three mappings:
mapping(string => Vehicle) vehicles;
mapping(uint => Booking) bookings;
mapping(address => User) users;
Here's what each mapping does:
Mapping| Purpose
"vehicles"| Retrieves vehicle records using a vehicle identifier
"bookings"| Retrieves booking records using a booking ID
"users"| Retrieves user records using their wallet address
Mappings are particularly useful when an application needs to retrieve records using unique identifiers.
An important detail: Solidity mappings do not provide a built-in way to enumerate all their keys. If we need to display every vehicle or booking, we must maintain additional lists or use an appropriate off-chain indexing solution.
- Implementing access control with modifiers
Smart contracts need rules that determine who can execute particular functions.
For example:
- Only registered customers should be able to perform customer-specific operations.
- Only vehicle owners should be allowed to manage their vehicles.
- A booking should only be created for an existing vehicle.
Solidity modifiers help implement these restrictions.
A simplified example:
modifier onlyRegisteredUser() {
require(
users[msg.sender].renter == msg.sender,
"User not registered"
);
_;
}
The "require()" statement checks a condition before the function continues.
The underscore represents the location where the modified function's code executes.
Other modifiers in this project include:
- "onlyOwner": Restricts an operation to the vehicle owner.
- "vehicleExist": Checks whether a vehicle exists before processing an operation.
These checks help prevent unauthorized operations, although modifiers alone do not guarantee complete contract security.
- Calculating rental amounts
One of the important parts of a vehicle rental application is calculating the total rental cost.
The basic calculation is:
Total rental amount = Rental price × Rental duration
For example:
Suppose a vehicle costs 500 units per rental period and is booked for three periods.
Rental price = 500
Duration = 3
Total amount = 500 × 3
= 1500
The actual implementation needs to define its time unit clearly and validate the rental and return times.
For example, if timestamps are used, the contract should reject a return time that is not later than the rental time.
An illustrative validation is:
require(
returnTime > rentTime,
"Invalid rental duration"
);
The project calculates the booking amount and stores it in the booking record. It does not currently execute an ETH transfer.
This distinction is important: calculating a payment amount is not the same as processing a blockchain payment.
- Managing booking status
A rental application needs to track the current state of every booking.
Our "BookingStatus" enum provides four possible states:
- "Pending": The booking has been created but is not yet confirmed.
- "Booked": The booking has been confirmed.
- "Completed": The rental process has been completed.
- "Cancelled": The booking has been cancelled.
These states make the booking lifecycle easier to understand and manage.
A production implementation should validate every status transition and define what happens to vehicle availability when a booking is confirmed, completed, or cancelled.
- Compiling and testing with Remix IDE
After implementing the contract, the next step is to compile and test it.
In Remix:
- Open the Solidity Compiler.
- Select a compatible Solidity compiler version.
- Compile the contract.
- Open the Deploy & Run Transactions panel.
- Select the appropriate Remix execution environment.
- Deploy the contract to that environment.
- Interact with the available functions and inspect their outputs.
For an educational prototype, test scenarios should include:
- Registering a customer.
- Registering a vehicle.
- Attempting to register a duplicate vehicle identifier.
- Creating a booking for an existing vehicle.
- Attempting to book a nonexistent vehicle.
- Checking the calculated rental amount.
- Testing unauthorized access.
- Checking booking status updates.
These are suggested test cases, not a claim that every scenario has already passed.
- Limitations and future improvements
Building this project helped me understand how Solidity data structures, mappings, modifiers, and enums work together.
However, the current implementation is an educational prototype rather than a production-ready rental platform.
Some important improvements would include:
- On-chain payments
Implement payable functions and a secure payment/refund mechanism, with appropriate handling of failed transactions.
- Stronger booking validation
Prevent overlapping bookings and ensure vehicle availability is updated consistently.
- Security testing
Test access-control restrictions, input validation, reentrancy risks where applicable, and other potential vulnerabilities.
- Deployment
Deploy to an Ethereum testnet and verify contract behaviour in a blockchain environment.
- Frontend integration
Build a web interface that allows customers and vehicle owners to interact with the contract through a wallet.
Conclusion
A Vehicle Rental Smart Contract is a useful beginner project for understanding how blockchain applications manage structured data and enforce rules through code.
Through this implementation, I explored Solidity structs, enums, mappings, modifiers, booking records, and rental amount calculations.
The most important lesson was that writing a smart contract is not simply about translating application logic into Solidity. It also requires thinking about access control, data consistency, transaction behaviour, and security.
This project is an initial step in my journey towards developing more practical blockchain applications.
Next step: Extending the prototype with secure payment handling, comprehensive tests, and testnet deployment.
Project source code: "Vehicle Rental Smart Contract – GitHub" (https://github.com/gauravjadkar/Vehicle-Rental-Smart-Contract-solidity-)
Technologies: Solidity 0.8.13 | Remix IDE
Top comments (1)
Check the github repo and suggest any improvement