Welcome to the Car Rent DApp! This decentralized application (DApp) is designed for car rental ventures, providing a streamlined process for users to hire cars. Car owners can set their own hire rates, and the admin plays a crucial role in approving suitable cars for hire, ensuring the company's image is protected.
- User-Friendly Interface
- Car Listing: Car owners can list their vehicles for hire as well end user
- Deployed contract wallet address which is the admin approve and reject cars that are not suitable fo the company
- Transaction History Users can view their previous transactions with address they have transact with
- Real-time Availability-Instant updates on car availability, users can check if a particular car is currently on hire
- Payment Handling: Secure payment processing, users can only hire a car if the previous user has completed the payment.
CarRent is a project that utilizes the Toastify, RainbowKit, and Smart Contract technologies with Next.js.
To run the project locally, follow these steps:
- Clone the repository:
git clone https://github.com/your-username/CarRent.git
- Install dependencies:
npm install
- Start the development server:
npm run dev
Once the project is running, you can access it in your browser at http://localhost:3000
. Here are some usage examples:
- Connect your wallet
- Navigate to Hire cars on your navbar
- Rent a car of your choice
- Make payment and View rental history
If you would like to contribute to CarRent, please follow these guidelines:
- Fork the repository
- Create a new branch:
git checkout -b my-feature
- Make your changes and commit them:
git commit -m 'Add new feature'
- Push to the branch:
git push origin my-feature
- Submit a pull request
Explaining the smart contract
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
// Importing AccessControl for role management.
import "@openzeppelin/contracts/access/AccessControl.sol";
// Interface for interacting with an ERC-20 token.
interface IERC20Token {
function transfer(address, uint256) external returns (bool);
function approve(address, uint256) external returns (bool);
function transferFrom(address, address, uint256) external returns (bool);
function totalSupply() external view returns (uint256);
function balanceOf(address) external view returns (uint256);
function allowance(address, address) external view returns (uint256);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
// CarBooking contract managing car rentals and approvals.
contract CarBooking is AccessControl {
// Address of the Celo Dollar token.
address internal cUsdTokenAddress = 0x874069Fa1Eb16D44d622F2e0Ca25eeA172369bC1;
// Address of the administrator.
address admin;
// Number of cars and rents for tracking.
uint256 public carLength;
uint256 public rentLength;
// Enum defining car status (not accepted, accepted, out of service).
enum CarStatus {
NOTACCEPT,
ACCEPTED,
OUT_OF_SERVICE
}
// Enum defining order status (open, in progress, cancelled, completed).
enum OrderStatus {
OPEN,
INPROGRESS,
CANCELLED,
COMPLETED
}
Explanation of Imports and Contract Structure:
- The contract imports
AccessControl
for role management. - An interface
IERC20Token
is defined for interacting with an ERC-20 token. CarBooking
manages car rentals and approvals.
// Struct representing a car rental.
struct Rent {
uint256 carID;
address carAddress;
address BookingAcount;
string name;
string destination;
uint256 amount;
bool paid;
}
// Struct representing a car.
struct Car {
address payable owner;
address admin;
string model;
string image;
string plateNumber;
uint256 bookingPrice;
uint256 rentCar;
CarStatus carStatus;
OrderStatus orderStatus;
Rent[] carRent; // Store the indices of rents for this car
}
Explanation of Structs:
- Structs
Rent
andCar
define the structure of car rentals and cars.
// Constructor initializes the contract and grants admin role to the deployer.
constructor() {
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
admin = msg.sender;
}
// Modifier ensuring only the admin can call a function.
modifier onlyAdmin() {
require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "Only admin can call this function");
_;
}
// Mapping associating car ID with car details.
mapping (uint256 => Car) public cars;
Explanation of Constructor, Modifier, and Mapping:
- The constructor initializes the contract and grants the admin role.
- Modifier
onlyAdmin
restricts certain functions to the admin. - The
cars
mapping associates a unique ID with each car, storing details about each car.
// Function to add a new car with specified details.
function addCar(string memory _model, string memory _imageCar, string memory _plateNumber, uint256 _bookingPrice) public {
Car storage newCar = cars[carLength];
newCar.owner = payable(msg.sender);
newCar.admin = admin;
newCar.model = _model;
newCar.image = _imageCar;
newCar.plateNumber = _plateNumber;
newCar.bookingPrice = _bookingPrice;
carLength++;
}
Explanation of addCar
Function:
- The
addCar
function allows the addition of a new car with specified details. - It creates a new car instance with the provided details and increments
carLength
.
// Function to approve a car for usage.
function carApprove(uint256 _carId) public onlyAdmin {
Car storage car = cars[_carId];
require(car.carStatus == CarStatus.NOTACCEPT, "Car did not meet the service standards or already accepted");
car.carStatus = CarStatus.ACCEPTED;
}
Explanation of carApprove
Function:
- The
carApprove
function approves a car for usage. - It checks if the car is not already accepted and meets service standards.
- If conditions are met, it updates the car status to "ACCEPTED."
// Function to reject a previously accepted car.
function rejectCar(uint256 _carId) public onlyAdmin {
Car storage car = cars[_carId];
require(car.carStatus == CarStatus.ACCEPTED, "Car not accepted");
car.carStatus = CarStatus.NOTACCEPT;
}
Explanation of rejectCar
Function:
- The
rejectCar
function rejects a previously accepted car. - It checks if the car is already accepted before updating the status to "NOTACCEPT."
// Function to mark an accepted car as out of service.
function outOfServiceCar(uint256 _carId) public onlyAdmin {
Car storage car = cars[_carId];
require(car.carStatus == CarStatus.ACCEPTED, "Car is not approved");
car.carStatus = CarStatus.OUT_OF_SERVICE;
}
Explanation of outOfServiceCar
Function:
- The
outOfServiceCar
function marks an accepted car as out of service. - It checks if the car is approved before updating the status to "OUT_OF_SERVICE."
// Function to add a new car rental.
function addRent(uint256 _carID, string memory _name, string memory _destination) public {
require(_carID < carLength, "Invalid Car index");
Car storage car = cars[_carID];
require(car.carStatus == CarStatus.ACCEPTED, "Car is not approved for usage");
require(car.orderStatus == OrderStatus.OPEN, "Car is already on hire");
Rent memory newRent = Rent({
carID: _carID,
carAddress: car.owner,
BookingAcount: msg.sender,
name: _name,
destination: _destination,
amount: car.bookingPrice,
paid: false
});
rentLength++;
cars[_carID].orderStatus = OrderStatus.INPROGRESS;
cars[_carID].carRent.push(newRent);
}
Explanation of addRent
Function:
- The
addRent
function enables a user to rent a car. - It checks if the car is approved for usage and not already on hire.
- If conditions are met, a new rent instance is created, and the car's order status is updated to "INPROGRESS."
// Function to get details of a specific car.
function getCars(uint256 _index) public view returns (
address, address, string memory, string memory,
string memory, uint256,
uint256,
CarStatus,
OrderStatus
) {
Car storage car = cars[_index];
return (
car.owner,
car.admin,
car.model,
car.image,
car.plateNumber,
car.bookingPrice,
car.rentCar,
car.carStatus,
car.orderStatus
);
}
Explanation of getCars
Function:
- The
getCars
function retrieves details of a specific car using its index. - It returns relevant information such as owner, model, image, etc.
// Function to get details of a specific car rental.
function getRent(uint256 _index) public view returns (
uint256,
address,
address,
string memory,
string memory,
uint256,
bool
) {
Car storage car = cars[_index];
return (
car.carRent[_index].carID,
car.carRent[_index].carAddress,
car.carRent[_index].BookingAcount,
car.carRent[_index].name,
car.carRent[_index].destination,
car.carRent[_index].amount,
car.carRent[_index].paid
);
}
Explanation of getRent
Function:
- The
getRent
function retrieves details of a specific car rental using its index. - It returns information such as car ID, customer address, rental amount, etc.
// Function to get the total number of cars.
function getCarLength() public view returns(uint256) {
return carLength;
}
Explanation of getCarLength
Function:
- The
getCarLength
function returns the total number of cars.
// Function to get the total number of car rentals.
function getRentLength() public view returns(uint256) {
return rentLength;
}
Explanation of getRentLength
Function:
- The
getRentLength
function returns the total number of car rentals.
// Function for processing car rental payment.
function carRentPayment(uint256 _index) external payable {
Car storage car = cars[_index];
require(msg.sender == car.carRent[_index].BookingAcount, "Must be the owner");
require(IERC20Token(cUsdTokenAddress).transferFrom(
msg.sender,
cars[_index].owner,
car.bookingPrice
), "Transfer failed");
car.carRent[_index].paid = true;
cars[_index].orderStatus = OrderStatus.OPEN;
}
}
Explanation of carRentPayment
Function:
- The
carRentPayment
function processes car rental payment. - It ensures that the caller is the owner of the rental.
- Transfers the rental amount using the Celo Dollar token and updates the payment status and order status.