Skip to content

Instantly share code, notes, and snippets.

@ikizoglu
Created August 16, 2023 18:53
Show Gist options
  • Save ikizoglu/6cb8e0123dd0f97ab658eec53e63971f to your computer and use it in GitHub Desktop.
Save ikizoglu/6cb8e0123dd0f97ab658eec53e63971f to your computer and use it in GitHub Desktop.
RentalCarApp
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "../../node_modules/@openzeppelin/contracts/utils/Counters.sol";
import "../../node_modules/@openzeppelin/contracts/security/ReentrancyGuard.sol";
contract CarRentalPlatform is ReentrancyGuard{
//Data
//Counter
using Counters for Counters.Counter;
Counters.Counter private _counter;
//Owner
address private owner;
//TotalPayments
uint private totalPayments;
//user struct
struct User{
address walletAddress;
string name;
string lastname;
uint rentedCarId;
uint balance;
uint debt;
uint start;
}
//car struct
struct Car{
uint id;
string name;
string imgUrl;
Status status;
uint rentFee;
uint salesFee;
}
//enum to indicate the status of the car
enum Status {
Retired,
InUse,
Available
}
//events
event CarAdded(uint indexed id, string name, string imgUrl, uint rentFee, uint salesFee);
event CarMetadataEdited(uint indexed id, string name, string imgUrl, uint rentFee, uint salesFee);
event CarStatusEdited (uint indexed id, Status status);
event UserAdded(address indexed walletAddress, string name, string lastname);
event Deposit(address indexed walletAddress, uint amount);
event CheckOut(address indexed walletAddress, uint indexed carId);
event CheckIn (address indexed walletAddress, uint indexed carId);
event PaymentMade(address indexed walletAddress, uint amount);
event BalanceWithdrawn(address indexed walletAddress, uint amount);
//user mapping
mapping(address => User) private users;
//car mapping
mapping(address => Car) private cars;
//constructor
constructor(){
owner = msg.sender;
totalPayments = 0;
}
//Modifiers
//onlyOwner
modifier onlyOwner(){
require(msg.sender == owner, "Only the owner can call this function");
_;
}
//FUNCTIONS
//Execute Functions
//setOwner #onlyOwner
function setOwner(address _newOwner) external onlyOwner{
owner = _newOwner;
}
//addUser #nonExisting
function addUser(string calldata name, string calldata lastname) external {
require(!isUser(msg.sender), "User already exists");
users[msg.sender] = User(msg.sender, name, lastname, 0,0,0,0);
emit UserAdded(msg.sender, users[msg.sender].name, users[msg.sender].lastname);
}
//addCar #onlyOwner #nonExistingCar
function addCar(string calldata name, string calldata url, uint rent, uint sale) external onlyOwner {
_counter.increment();
uint counter = _counter.current();
cars[counter] = Car(counter, name, url, Status.Available, rent, sale);
emit CarAdded(counter, cars[counter].name, cars[counter].imgUrl, cars[counter].rentFee, cars[counter].salesFee);
}
//editCarMetadata #onlyOwner #existingCar
function editCarMetadata(uint id, string calldata name, string calldata imgUrl,uint rentFee, uint salesFee) external onlyOwner {
require(cars[id].id !=0, "Car with given ID does not exist");
Car storage car = cars[id];
if (bytes(name).length != 0){
car.name = name;
}
if (rentFee > 0){
car.rentFee = rentFee;
}
if (salesFee > 0){
car.salesFee = salesFee;
}
emit CarMetadataEdited( id, car.name, car.imgUrl, car.rentFee, car.salesFee);
}
//editCarStatus #onlyOwner #existingCar
function editCarStatus(uint id, Status status) external onlyOwner {
require(cars[id].id != 0, "Car with given ID does not exist");
cars[id].status = status;
}
//checkOut #ExistingUser #isCarAvailable #userHasNotRentedACar #userHasNoDebt
function checkOut(uint id) external {
require(isUser(msg.sender), "User does not exist!");
require(cars[id].status == Status.Available, "Car is not available for use");
require(users[msg.sender].rentedCarId == 0, "User has alredy rented a car");
require(users[msg.sender].debt == 0, "User has an outstanding debt!");
users[msg.sender].start = block.timestamp;
users[msg.sender].rentedCarId = id;
cars[id].status = Status.InUse;
emit CheckOut(msg.sender, id);
}
//checkIn #existingUser #userHasRentedACar
function checkIn() external {
require(isUser(msg.sender), "User does not exist!");
uint rentedCarId = users[msg.sender].rentedCarId;
require(rentedCarId != 0, "User has not rented a car");
uint usedSeconds = block.timestamp - users[msg.sender].start;
uint rentFee = cars[rentedCarId].rentFee;
users[msg.sender].debt += calculateDebt(usedSeconds, rentFee);
users[msg.sender].rentedCarId = 0;
users[msg.sender].start = 0;
users[msg.sender].status = Status.Available;
emit CheckIn(msg.sender, rentedCarId);
}
//deposit #existingUser
function deposit() external payable{
require(isUser(msg.sender), "User does not exist");
users[msg.sender].balance += msg.value;
emit Deposit(msg.sender, msg.value);
}
//makePayment #existingUser #existingDebt #suffiecientBalance
function makePayment() external {
require(isUser(msg.sender), "User does not exist");
uint debt = users[msg.sender].debt;
uint balance = users[msg.sender].balance;
require(debt > 0, "User has no debt to pay");
require(balance >= debt, "User has insufficient balance");
unchecked {
users[msg.sender].balance -= debt;
}
totalPayments += debt;
users[msg.sender].debt = 0;
emit PaymentMade(msg.sender, debt);
}
//withdrawBalance #existingUser
function withdrawBalance(uint amount) external nonReentrant {
require(isUser(msg.sender), "User does not exist");
uint balance = users[msg.sender].balance;
require(balance >= amount, "Insufficient balance to withdraw");
unchecked {
users[msg.sender].balance -= amount;
}
(bool success, ) =msg.sender.call{value: amount}("");
require(success, "Transfer failed");
emit BalanceWithdrawn(msg.sender, amount);
}
//withdrawOwnerBalance # onlyOwner
function withdrawOwnerBalance(uint amount) external onlyOwner{
require(totalPayments >= amount, "Unsufficient contract balance to withdraw");
(bool success, ) = owner.call{value: amount}("");
require(success, "Transfer failed");
unchecked {
totalPayments -=amount;
}
}
//Query Functions
//getOwner
function getOwner() external view returns(address){
return owner;
}
//isUser
function isUser(address walletAddress) private view returns(bool) {
return users[walletAddress].walletAdress != address(0);
}
//getUser #existingUser
function getUser(address walletAddress) external view returns(User memory) {
require(isUser(walletAddress), "User does not exist");
return users[walletAddress];
}
//getCar #existingCar
function getCar(uint id) external view returns (Car memory) {
require(cars[id].id != 0, "Car does not exist");
return cars[id];
}
//getCarByStatus
function getCarByStatus(Status _status) external view returns(Car[] memory){
uint count = 0;
uint length = _counter.current();
for (uint i = 1; i <= length; i++) {
if (cars[i].status == _status) {
count++;
}
}
Car[] memory carsWithStatus = new Car[](count);
count = 0;
for (uint i = 1; i <= length; i++) {
if(cars[i].status == _status) {
count++;
}
}
return carsWithStatus;
}
//calculateDebt
function calculateDebt(uint usedSeconds, uint rentFee) private pure returns(uint) {
uint usedMinutes = usedSeconds / 60;
return usedMinutes * rentFee;
}
//getCurrentCount
function getCurrentCount() external view returns(uint) {
return _counter.current();
}
//getContractBalance #onlyOwner
function getContractBalance() external view onlyOwner returns(uint) {
return address(this).balance;
}
//getTotalPayment #onlyOwner
function getTotalPayments() external view onlyOwner returns(uint) {
return totalPayments;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment