DEV Community

Cover image for How I have Made web3 AIRBNB Dapp Tutorial 2023
sourav maji
sourav maji

Posted on

How I have Made web3 AIRBNB Dapp Tutorial 2023

Basically the story begins like this I got an message from my friend that he has one client who wants an airbnb application which needs to be in web3 also NFTs connected with it so that buyers can use those NFTs to enter in their room

As NFTs will act like an Key in this case

In case , you don’t have knowledge about Airbnb then let me explain you

Airbnb is a popular online marketplace that allows people to rent out their homes, apartments, or other types of accommodations to travelers around the world. The platform was founded in 2008 and has since grown into one of the largest and most well-known companies in the sharing economy.

Through Airbnb, hosts can list their accommodations, set their own prices, and communicate with guests directly through the platform. Guests can search for and book accommodations based on location, price, and other criteria. Airbnb charges a fee for its services, which typically ranges from 5% to 15% of the total booking cost.

Before starting the Tutorial make sure you check out my Youtube Channel for more Tutorials related to blockchain, NFTs , web3 etc

Youtube Channel Name : Sourav MAJI

Okay , now let’s start the tutorial

The tutorial is divided into 4 Parts :

1.Writing the Smart Contract
2.Deploying the smart contract
3.Explaining the Frontend
4.Running the dapp in the Local host

First Part : Writing the Smart contract for the dapp
This is a Solidity smart contract that implements a decentralized version of Airbnb, called DecentralAirbnb. The contract allows users to create and book rentals on the blockchain.

The contract has the following variables:

erc721: an instance of the ERC721 contract, which is used to manage the rental tokens.
admin: the address of the contract's administrator.
listingFee: the fee required to list a new rental on the platform.
_rentalIds: a private counter used to generate new rental IDs.

The contract defines two data structures: RentalInfo and Booking. RentalInfo represents a rental listing and contains information such as the rental ID, owner address, name, location, description, and price per day. Booking represents a booking made by a renter and contains information such as the renter's address and the start and end dates of the booking.

The contract has several functions:

  • addRental: allows a user to create a new rental listing by providing information such as the rental name, location, and price per day. The user must also pay the listing fee to create the listing.
  • bookDates: allows a renter to book a rental for a given period of time by providing the rental ID and the start and end dates of the booking. The renter must also pay the rental fee to book the rental. If the rental is available for the given dates, a new booking is created and the rental fee is transferred to the rental owner.
  • checkIfBooked: a helper function used to check if a rental is already booked for a given period of time.
  • onlyAdmin: a modifier that restricts access to certain functions to the contract's administrator.
  • isRental: a modifier that checks if a given rental ID is valid.
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.9;

import "./PriceConverter.sol";
import "./ERC721.sol";

contract DecentralAirbnb is PriceConverter {
    //--------------------------------------------------------------------
    // VARIABLES

    ERC721 public erc721;

    address public admin;

    uint256 public listingFee;
    uint256 private _rentalIds;

    struct RentalInfo {
        uint256 id;
        address owner;
        string name;
        string city;
        string latitude;
        string longitude;
        string description;
        string imgUrl;
        uint256 maxNumberOfGuests;
        uint256 pricePerDay;
    }

    struct Booking {
        address renter;
        uint256 fromTimestamp;
        uint256 toTimestamp;
    }

    RentalInfo[] public rentals;

    mapping(uint256 => Booking[]) rentalBookings;

    //--------------------------------------------------------------------
    // EVENTS

    event NewRentalCreated(
        uint256 id,
        address owner,
        string name,
        string city,
        string latitude,
        string longitude,
        string description,
        string imgUrl,
        uint256 maxGuests,
        uint256 pricePerDay,
        uint256 timestamp
    );

    event NewBookAdded(
        uint256 rentalId,
        address renter,
        uint256 bookDateStart,
        uint256 bookDateEnd,
        uint256 timestamp
    );

    //--------------------------------------------------------------------
    // ERRORS

    error DecentralAirbnb__OnlyAdmin();
    error DecentralAirbnb__InvalidFee();
    error DecentralAirbnb__InvalidRentalId();
    error DecentralAirbnb__InvalidBookingPeriod();
    error DecentralAirbnb__AlreadyBooked();
    error DecentralAirbnb__InsufficientAmount();
    error DecentralAirbnb__TransferFailed();

    //--------------------------------------------------------------------
    // MODIFIERS

    modifier onlyAdmin() {
        if(msg.sender != admin) revert DecentralAirbnb__OnlyAdmin();
        _;
    }

    modifier isRental(uint _id) {
        if(_id >= _rentalIds) revert DecentralAirbnb__InvalidRentalId();
        _;
    }

    //--------------------------------------------------------------------
    // CONSTRUCTOR

    constructor(uint256 _listingFee, address _priceFeedAddress, address erc721address) {
        admin = msg.sender;
        listingFee = _listingFee;
        priceFeedAddress = _priceFeedAddress;
        erc721 = ERC721(erc721address);
    }

    //--------------------------------------------------------------------
    // FUNCTIONS

    function addRental(
        string memory _name,
        string memory _city,
        string memory _latitude,
        string memory _longitude,
        string memory _description,
        string memory _imgUrl,
        uint256 _maxGuests,
        uint256 _pricePerDay
    ) external payable {
        if(msg.value != listingFee) revert DecentralAirbnb__InvalidFee();
        uint256 _rentalId = _rentalIds;

        RentalInfo memory _rental = RentalInfo(
            _rentalId,
            msg.sender,
            _name,
            _city,
            _latitude,
            _longitude,
            _description,
            _imgUrl,
            _maxGuests,
            _pricePerDay
        );

        rentals.push(_rental);
        _rentalIds++;

        emit NewRentalCreated(
            _rentalId,
            msg.sender,
            _name,
            _city,
            _latitude,
            _longitude,
            _description,
            _imgUrl,
            _maxGuests,
            _pricePerDay,
            block.timestamp
        );
    }

    function bookDates(
        uint256 _id,
        uint256 _fromDateTimestamp,
        uint256 _toDateTimestamp
    ) external payable isRental(_id) {

        RentalInfo memory _rental = rentals[_id];

        uint256 bookingPeriod = (_toDateTimestamp - _fromDateTimestamp) /
            1 days;
        // can't book less than 1 day
        if(bookingPeriod < 1) revert DecentralAirbnb__InvalidBookingPeriod();

        uint256 _amount = convertFromUSD(_rental.pricePerDay) * bookingPeriod;

        if(msg.value != _amount) revert DecentralAirbnb__InsufficientAmount();
        if(checkIfBooked(_id, _fromDateTimestamp, _toDateTimestamp)) revert DecentralAirbnb__AlreadyBooked();

        rentalBookings[_id].push(
            Booking(msg.sender, _fromDateTimestamp, _toDateTimestamp)
        );

        (bool success,) = payable(_rental.owner).call{value: msg.value}("");
        if (!success) revert DecentralAirbnb__TransferFailed();

        erc721.mintUsingSequentialTokenId();

        emit NewBookAdded(
            _id,
            msg.sender,
            _fromDateTimestamp,
            _toDateTimestamp,
            block.timestamp
        );
    }

    function checkIfBooked(
        uint256 _id,
        uint256 _fromDateTimestamp,
        uint256 _toDateTimestamp
    ) internal view returns (bool) {

        Booking[] memory _rentalBookings = rentalBookings[_id];

        // Make sure the rental is available for the booking dates
        for (uint256 i = 0; i < _rentalBookings.length;) {
            if (
                ((_fromDateTimestamp >= _rentalBookings[i].fromTimestamp) &&
                    (_fromDateTimestamp <= _rentalBookings[i].toTimestamp)) ||
                ((_toDateTimestamp >= _rentalBookings[i].fromTimestamp) &&
                    (_toDateTimestamp <= _rentalBookings[i].toTimestamp))
            ) {
                return true;
            }
            unchecked {
                ++i;
            }
        }
        return false;
    }

    function getRentals() external view returns (RentalInfo[] memory) {
        return rentals;
    }

    // Return the list of booking for a given rental
    function getRentalBookings(uint256 _id)
        external
        view
        isRental(_id)
        returns (Booking[] memory)
    {
        return rentalBookings[_id];
    }

    function getRentalInfo(uint256 _id)
        external
        view
        isRental(_id)
        returns (RentalInfo memory)
    {
        return rentals[_id];
    }

    // ADMIN FUNCTIONS

    function changeListingFee(uint256 _newFee) external onlyAdmin {
        listingFee = _newFee;
    }

    function withdrawBalance() external onlyAdmin {
        (bool success,) = payable(admin).call{value: address(this).balance}("");
        if (!success) revert DecentralAirbnb__TransferFailed();
    }
}
Enter fullscreen mode Exit fullscreen mode
  • changeListingfee: this function is also accessible only by the admin where he can change the listing fees of adding rooms
  • withdrawBalance: this function is only accessible by the admin where he can withdraw the listing fees into his wallet

Now , let me explain you about the second smart contract

`// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";

contract PriceConverter {
address public priceFeedAddress;

function getPrice() public view returns (uint256, uint256) {
    AggregatorV3Interface priceFeed = AggregatorV3Interface(
        priceFeedAddress
    );
    (, int256 price, , , ) = priceFeed.latestRoundData();
    uint256 decimals = priceFeed.decimals();
    return (uint256(price), decimals);
}

function convertFromUSD(uint256 amountInUSD) public view returns (uint256) {
    (uint256 price, uint256 decimals) = getPrice();
    uint256 convertedPrice = (amountInUSD * 10**decimals) / price;
    return convertedPrice;
}
Enter fullscreen mode Exit fullscreen mode

}`

This smart contract that provides a function to convert a given amount of USD to an equivalent amount of cryptocurrency tokens. The contract uses Chainlink’s price feed oracle to get the current price of the cryptocurrency in USD, and then performs a simple conversion calculation.

The contract starts by importing the “AggregatorV3Interface.sol” file from the “@chainlink/contracts/src/v0.8/interfaces” directory. This file contains the interface for the Chainlink price feed oracle contract that provides the current price of a cryptocurrency in USD.

The contract has a state variable “priceFeedAddress” that stores the address of the Chainlink price feed oracle contract for the cryptocurrency that we want to convert to. This variable is set by calling the contract’s constructor function when deploying the contract.

The function “getPrice” retrieves the latest round data from the Chainlink price feed oracle contract for the specified cryptocurrency, and returns the current price and number of decimals of the cryptocurrency. It does this by creating an instance of the AggregatorV3Interface contract using the “priceFeedAddress” state variable, and then calling the “latestRoundData” function of this instance. The “latestRoundData” function returns five values: round ID, price, timestamp, answer, and round data. We only need the “price” and “decimals” values, so we ignore the other values by leaving them blank (using the “_” symbol).

The function “convertFromUSD” takes an input parameter “amountInUSD”, which is the amount of USD we want to convert to cryptocurrency. The function first calls the “getPrice” function to get the current price and number of decimals of the cryptocurrency, and then calculates the equivalent amount of cryptocurrency tokens by multiplying the “amountInUSD” with 10 to the power of “decimals”, and then dividing by the current price. The result is returned as a uint256 value.

The contract also has a fallback function, which is invoked when the contract receives Ether without any function call. This function does nothing and simply reverts the transaction, preventing the contract from accidentally accepting Ether.

And this is the last Smart Contract which is for the NFT user will get but for tutorial purpose I have only made it accessible for the admin

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.9;

import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/utils/Context.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import "@openzeppelin/contracts/utils/introspection/ERC165.sol";





/**
 * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
 * the Metadata extension, but not including the Enumerable extension, which is available separately as
 * {ERC721Enumerable}.
 */
contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
    using Address for address;
    using Strings for uint256;

    address payable private _contractCreator;

    // Token name
    string private _name;

    // Token symbol
    string private _symbol;

    uint256 private _tokenIdIndex;

    bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
    bytes4 private constant _INTERFACE_ID_ERC721_METADATA = 0x5b5e139f;
    bytes4 private constant _INTERFACE_ID_OFFICIAL_ERC721 = 0x80ac58cd;

    // Mapping from token ID to owner address
    mapping (uint256 => address) private _owners;

    // Mapping owner address to token count
    mapping (address => uint256) private _balances;

    // Mapping from token ID to approved address
    mapping (uint256 => address) private _tokenApprovals;

    // Mapping from owner to operator approvals
    mapping (address => mapping (address => bool)) private _operatorApprovals;

    event GenerateTokenId(address indexed addr, uint256 indexed id);
    event GenerateCommitment(address indexed addr, uint256 indexed id);
    event Burn(address burner, uint256 tokenId);

    string private constant _baseURL = "https://ipfs.io/ipfs/QmeSjSinHpPnmXmspMjwiXyN6zS4E9zccariGR3jxcaWtq/";

    /**
     * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
     */
    constructor () {
        _name = "Door Tokens";
        _symbol = "TKN";
        _contractCreator = payable(msg.sender);
        _tokenIdIndex = 1;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public pure virtual override(ERC165, IERC165) returns (bool) {
        return (interfaceId == _INTERFACE_ID_ERC165 || interfaceId == _INTERFACE_ID_ERC721_METADATA || interfaceId == _INTERFACE_ID_OFFICIAL_ERC721);
    }

    /**
     * @dev See {IERC721-balanceOf}.
     */
    function balanceOf(address owner) public view virtual override returns (uint256) {
        require(owner != address(0), "ERC721: balance query for the zero address");
        return _balances[owner];
    }

    /**
     * @dev See {IERC721-ownerOf}.
     */
    function ownerOf(uint256 tokenId) public view virtual override returns (address) {
        address owner = _owners[tokenId];
        require(owner != address(0), "ERC721: owner query for nonexistent token");
        return owner;
    }

    /**
     * @dev See {IERC721Metadata-name}.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev See {IERC721Metadata-symbol}.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev See {IERC721Metadata-tokenURI}.
     */
    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
        return string(abi.encodePacked(_baseURL, uint2str(tokenId)));
    }

    function uint2str(uint _i) internal pure returns (string memory _uintAsString) {
        if (_i == 0) {
            return "0";
        }
        uint j = _i;
        uint len;
        while (j != 0) {
            len++;
            j /= 10;
        }
        bytes memory bstr = new bytes(len);
        uint k = len;
        while (_i != 0) {
            k = k-1;
            uint8 temp = (48 + uint8(_i - _i / 10 * 10));
            bytes1 b1 = bytes1(temp);
            bstr[k] = b1;
            _i /= 10;
        }
        return string(bstr);
    }

    function mintUsingSequentialTokenId() public returns (uint256 tokenId) {
         tokenId = _tokenIdIndex;
        _mint(_contractCreator, tokenId);
        emit GenerateTokenId(_contractCreator, tokenId);
        _tokenIdIndex++;
    }

    function mintUsingSequentialTokenId(address to) public returns (uint256 tokenId) {
        require(_contractCreator == msg.sender, "Minting not permitted");  //uncomment to restrict minting
        tokenId = _tokenIdIndex;
        _mint(to, tokenId);
        emit GenerateTokenId(to, tokenId);
        _tokenIdIndex++;
    }

    function burn(uint256 tokenId) public {
        require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: Burn caller is not owner nor approved");
        _burn(tokenId);
        emit Burn(msg.sender, tokenId);
    }

    /**
     * @dev See {IERC721-approve}.
     */
    function approve(address to, uint256 tokenId) public virtual override {
        address owner = ERC721.ownerOf(tokenId);
        require(to != owner, "ERC721: approval to current owner");

        require(_msgSender() == owner || isApprovedForAll(owner, _msgSender()),
            "ERC721: approve caller is not owner nor approved for all"
        );

        _approve(to, tokenId);
    }

    /**
     * @dev See {IERC721-getApproved}.
     */
    function getApproved(uint256 tokenId) public view virtual override returns (address) {
        require(_exists(tokenId), "ERC721: approved query for nonexistent token");

        return _tokenApprovals[tokenId];
    }

    /**
     * @dev See {IERC721-setApprovalForAll}.
     */
    function setApprovalForAll(address operator, bool approved) public virtual override {
        require(operator != _msgSender(), "ERC721: approve to caller");

        _operatorApprovals[_msgSender()][operator] = approved;
        emit ApprovalForAll(_msgSender(), operator, approved);
    }

    /**
     * @dev See {IERC721-isApprovedForAll}.
     */
    function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
        return _operatorApprovals[owner][operator];
    }

    /**
     * @dev See {IERC721-transferFrom}.
     */
    function transferFrom(address from, address to, uint256 tokenId) public virtual override {
        //solhint-disable-next-line max-line-length
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");

        _transfer(from, to, tokenId);
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override {
        safeTransferFrom(from, to, tokenId, "");
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public virtual override {
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
        _safeTransfer(from, to, tokenId, _data);
    }

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * `_data` is additional data, it has no specified format and it is sent in call to `to`.
     *
     * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
     * implement alternative mechanisms to perform token transfer, such as signature-based.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeTransfer(address from, address to, uint256 tokenId, bytes memory _data) internal virtual {
        _transfer(from, to, tokenId);
        require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
    }

    /**
     * @dev Returns whether `tokenId` exists.
     *
     * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
     *
     * Tokens start existing when they are minted (`_mint`),
     * and stop existing when they are burned (`_burn`).
     */
    function _exists(uint256 tokenId) internal view virtual returns (bool) {
        return _owners[tokenId] != address(0);
    }

    /**
     * @dev Returns whether `spender` is allowed to manage `tokenId`.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
        require(_exists(tokenId), "ERC721: operator query for nonexistent token");
        address owner = ERC721.ownerOf(tokenId);
        return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender));
    }

    /**
     * @dev Safely mints `tokenId` and transfers it to `to`.
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeMint(address to, uint256 tokenId) internal virtual {
        _safeMint(to, tokenId, "");
    }

    /**
     * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
     * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
     */
    function _safeMint(address to, uint256 tokenId, bytes memory _data) internal virtual {
        _mint(to, tokenId);
        require(_checkOnERC721Received(address(0), to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
    }

    /**
     * @dev Mints `tokenId` and transfers it to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - `to` cannot be the zero address.
     *
     * Emits a {Transfer} event.
     */
    function _mint(address to, uint256 tokenId) internal virtual {
        require(to != address(0), "ERC721: mint to the zero address");
        require(!_exists(tokenId), "ERC721: token already minted");

        _beforeTokenTransfer(address(0), to, tokenId);

        _balances[to] += 1;
        _owners[tokenId] = to;

        emit Transfer(address(0), to, tokenId);
    }

    /**
     * @dev Destroys `tokenId`.
     * The approval is cleared when the token is burned.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     *
     * Emits a {Transfer} event.
     */
    function _burn(uint256 tokenId) internal virtual {
        address owner = ERC721.ownerOf(tokenId);

        // Clear approvals
        _approve(address(0), tokenId);

        _balances[owner] -= 1;
        delete _owners[tokenId];

        emit Transfer(owner, address(0), tokenId);
    }

    /**
     * @dev Transfers `tokenId` from `from` to `to`.
     *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     *
     * Emits a {Transfer} event.
     */
    function _transfer(address from, address to, uint256 tokenId) internal virtual {
        require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer of token that is not own");
        require(to != address(0), "ERC721: transfer to the zero address");

        _beforeTokenTransfer(from, to, tokenId);

        // Clear approvals from the previous owner
        _approve(address(0), tokenId);

        _balances[from] -= 1;
        _balances[to] += 1;
        _owners[tokenId] = to;

        emit Transfer(from, to, tokenId);
    }

    /**
     * @dev Approve `to` to operate on `tokenId`
     *
     * Emits a {Approval} event.
     */
    function _approve(address to, uint256 tokenId) internal virtual {
        _tokenApprovals[tokenId] = to;
        emit Approval(ERC721.ownerOf(tokenId), to, tokenId);
    }

    /**
     * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
     * The call is not executed if the target address is not a contract.
     *
     * @param from address representing the previous owner of the given token ID
     * @param to target address that will receive the tokens
     * @param tokenId uint256 ID of the token to be transferred
     * @param _data bytes optional data to send along with the call
     * @return bool whether the call correctly returned the expected magic value
     */
    function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data)
        private returns (bool)
    {
        if (to.isContract()) {
            try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, _data) returns (bytes4 retval) {
                return retval == IERC721Receiver(to).onERC721Received.selector;
            } catch (bytes memory reason) {
                if (reason.length == 0) {
                    revert("ERC721: transfer to non ERC721Receiver implementer");
                } else {
                    // solhint-disable-next-line no-inline-assembly
                    assembly {
                        revert(add(32, reason), mload(reason))
                    }
                }
            }
        } else {
            return true;
        }
    }

    function endContract() public payable
    {
        if (msg.sender == _contractCreator)
        {
            selfdestruct(_contractCreator);
        }
        else revert();
    }

    /**
     * @dev Hook that is called before any token transfer. This includes minting
     * and burning.
     *
     * Calling conditions:
     *
     * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be
     * transferred to `to`.
     * - When `from` is zero, `tokenId` will be minted for `to`.
     * - When `to` is zero, ``from``'s `tokenId` will be burned.
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal virtual { }
}
Enter fullscreen mode Exit fullscreen mode

Second Part : Deploying the Smart contract

You need to create a hardhat project and then you need to add all this 3 contracts as well as need to write the scripts for deploying this smart contract in your chosen blockchain network

Below is the source code of the script for deploying the smart contract

const hre = require("hardhat");
const fs = require('fs');
const { verify } = require('../utils/verify')

const LOCAL_NETWORKS = ["localhost", "ganache", "mumbai","goerli"]
const fse = require('fs-extra');


async function deployMock() {
  const DECIMALS = "8"
  const INITIAL_PRICE = "200000000000"

  const Mock = await hre.ethers.getContractFactory("MockV3Aggregator")

  console.log("Deploying price feed mock");
  const mockContract = await Mock.deploy(DECIMALS, INITIAL_PRICE)

  await mockContract.deployed();
  console.log("Price feed mock deployed to:", mockContract.address);

  return mockContract.address;
}

 async function deployERC721() {
  const ERC721 = await hre.ethers.getContractFactory("ERC721")
  console.log("Deploying NFT mint contract");
  const erc721Contract = await ERC721.deploy()
  await erc721Contract.deployed();
  console.log("NFT mint contract deployed to:", erc721Contract.address);
  return erc721Contract.address;
 }


async function main() {
  /* these two lines deploy the contract to the network */
  let listingFee = hre.ethers.utils.parseEther("0.001", "ether");
  var priceFeedAddress, erc721Address;
  if (LOCAL_NETWORKS.includes(hre.network.name)) {
    priceFeedAddress = await deployMock()
    erc721Address = await deployERC721()
  }
  // For deploying to polygon mainnet or testnet
  // const priceFeedAddress = ""

  const DecentralAirbnb = await hre.ethers.getContractFactory("DecentralAirbnb")
  const airbnbContract = await DecentralAirbnb.deploy(listingFee, priceFeedAddress , erc721Address )
  await airbnbContract.deployed();
  console.log("Decentral Airbnb deployed to:", airbnbContract.address);
  console.log("Network deployed to :", hre.network.name);

  /* transfer contracts addresses & ABIs to the front-end */
  if (fs.existsSync("../src")) {
    fse.copySync("./artifacts/contracts", "../src/artifacts")
    fs.writeFileSync("../src/utils/contracts-config.js", `
      export const contractAddress = "${airbnbContract.address}"
      export const ownerAddress = "${airbnbContract.signer.address}"
      export const networkDeployedTo = "${hre.network.config.chainId}"
    `)
  }

  if (!LOCAL_NETWORKS.includes(hre.network.name) && hre.config.etherscan.apiKey !== "") {
    await airbnbContract.deployTransaction.wait(6)
    await verify(airbnbContract.address, [listingFee, priceFeedAddress])
  }
}

main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error(error);
    process.exit(1);
  });
Enter fullscreen mode Exit fullscreen mode

This script will automatically add the contract address inside the config file of the frontend so you don’t have to manually add it

Below is the hardhat config .js file

require("dotenv").config();
require("@nomiclabs/hardhat-etherscan");
require("@nomicfoundation/hardhat-chai-matchers");
require("hardhat-gas-reporter");

// This is a sample Hardhat task. To learn how to create your own go to
// https://hardhat.org/guides/create-task.html
task("accounts", "Prints the list of accounts", async (taskArgs, hre) => {
  const accounts = await hre.ethers.getSigners();

  for (const account of accounts) {
    console.log(account.address);
  }
});

// You need to export an object to set up your config
// Go to https://hardhat.org/config/ to learn more

/**
 * @type import('hardhat/config').HardhatUserConfig
 */

const POLYGONSCAN_API_KEY = process.env.POLYGONSCAN_API_KEY;
const POLYGON_RPC_URL = process.env.POLYGON_RPC_URL;
const MUMBAI_RPC_URL = process.env.MUMBAI_RPC_URL;
const GOERLI_RPC_URL = process.env.GOERLI_RPC_URL;

module.exports = {
  solidity: {
    compilers: [
      {
        version: "0.8.9",
        settings: {
          optimizer: {
            enabled: true,
            runs: 200,
          },
        },
      },
    ],
  },
  networks: {
    hardhat: {
      chainId: 31337,
    },
    ganache: {
      chainId: 1337,
      url: "http://127.0.0.1:7545",
      accounts: [process.env.PRIVATE_KEY],
      allowUnlimitedContractSize: true,
    },
     mumbai: {
       url: MUMBAI_RPC_URL,
       accounts: [process.env.PRIVATE_KEY],
       chainId: 80001,
     },
     goerli: {
      url: GOERLI_RPC_URL,
      accounts: [process.env.PRIVATE_KEY],
      chainId: 5,
     },
    // polygon: {
    //   url: POLYGON_RPC_URL,
    //   accounts: [process.env.PRIVATE_KEY],
    //   chainId: 137,
    // }
  },
  paths: {
    artifacts: "./artifacts",
  },
  gasReporter: {
    enabled: true,
  },
  etherscan: {
    apiKey: POLYGONSCAN_API_KEY,
  },
};
Enter fullscreen mode Exit fullscreen mode

so , you need to create a .env file for adding your chosen chain API key as well as rpc url and your wallet private key for paying the gas fees for deploying the smart contract

After that if your script file name is deploy.js then the command for deploying your all this 3 smart contract will be this :

npx hardhat run scripts/deploy.js --network mumbai

In my case I am deploying the smart contract in mumbai testnetwork so I have written mumbai at the end of this command

Third Part : Explaining the frontend
…..Coming Soon…..

Top comments (2)

Collapse
 
parth51199 profile image
parth51199

amazing, Thanks for sharing

Collapse
 
souravmaji1 profile image
sourav maji

Your welcome