14 |
15 |
16 |
43 |
44 |
73 |
--------------------------------------------------------------------------------
/setup.md:
--------------------------------------------------------------------------------
1 | ## Creating a Decentralized Airbnb DApp
2 | ### Intro
3 | In this workshop, we'll explore how to create decentralized Airbnb. We will use the pre-written Ethereum smart contract and deploy it locally on ganache using remix, we can deploy contract on any Testnet or Mainnet .
4 |
5 | This dApp uses [Nuxt](https://nuxtjs.org/), but is out of scope for this workshop, so don't worry, we will only be focusing on the pieces that helps us build. In practice, any JS framework can be used.
6 |
7 |
8 | # Installation
9 | ## Check your environment
10 | Prior to all, you should have the following prerequisites installed on your machine:
11 | #### NodeJS 8.10+
12 | ```
13 | node version
14 | > 8.10+
15 | ```
16 | If you need to update Node, please [install `nvm`](https://github.com/creationix/nvm#installation) and install/use the LTS version. macOS/Linux commands provided for you below for convenience:
17 | ```
18 | curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | bash
19 | nvm install --lts
20 | nvm use lts
21 | ```
22 | #### MetaMask
23 |
24 | [Download MetaMask chrome extension](https://metamask.io/)
25 |
26 |
27 | #### Install ganache-cli
28 | ```
29 | npm install -g ganache-cli
30 | ```
31 |
32 | # Template clone and explore
33 | Now we have Setup installed properly, let’s grab the dApp template that we will use as the skeleton of our dApp. This template is a website built using Nuxt.js (don't worry if you don't know Nuxt.js, we are not focusing on this part).
34 |
35 | ## We will...
36 | We will use existing solidity contracts developed in previous session, and then use these functions inside the website.
37 | [Airbnb.sol](./contracts/Airbnb.sol)
38 |
39 | ## Let's go
40 |
41 | Clone the template into your folder:
42 | ```
43 | git clone https://github.com/maticnetwork/ethindia-workshop.git
44 |
45 | cd ethindia-workshop
46 |
47 | npm i
48 | ```
49 |
50 | ## Start TestRPC for development using [ganache-cli](https://github.com/trufflesuite/ganache-cli)
51 |
52 | ```
53 | npm run test:ethereum
54 | ```
55 |
56 | ## Deploy contract using Remix Etherium IDE
57 |
58 | - Open [Remix IDE](https://remix.ethereum.org)
59 |
60 | - Enable `Solidity Compiler` and `Deploy & Run Transactions` on remix IDE
61 |
62 | - Create new file [Airbnb.sol](./contracts/Airbnb.sol) from plus icon on left panel
63 |
64 | - Select appropriate network on metamask, as we are going to deploy contract using ganache-cli we will select `Locahost 8545`
65 |
66 | - Select appropriate compiler on remix IDE, in this example we are going to use `0.5.7` above compiler
67 |
68 | - Go to DEPLOY & RUN TRANSACTIONS and click deploy.
69 |
70 | - On sucessfull deploy copy `contract address`
71 |
72 |
73 | [NEXT - Complete DApps UI ](./dapps.md)
74 |
--------------------------------------------------------------------------------
/contracts/Airbnb.sol:
--------------------------------------------------------------------------------
1 | pragma solidity ^0.5.7;
2 |
3 | contract Airbnb {
4 | // INSERT struct Property
5 |
6 | // Unique and sequential propertyId for every new property
7 | uint256 public propertyId;
8 |
9 | // mapping of propertyId to Property object
10 | mapping(uint256 => Property) public properties;
11 |
12 | // INSERT struct Booking
13 |
14 | uint256 public bookingId;
15 |
16 | // mapping of bookingId to Booking object
17 | mapping(uint256 => Booking) public bookings;
18 |
19 | // This event is emitted when a new property is put up for sale
20 | event NewProperty (
21 | uint256 indexed propertyId
22 | );
23 |
24 | // This event is emitted when a NewBooking is made
25 | event NewBooking (
26 | uint256 indexed propertyId,
27 | uint256 indexed bookingId
28 | );
29 |
30 | /**
31 | * @dev Put up your funky space on the market
32 | * @param name Name of the property
33 | * @param description Short description of your property
34 | * @param price Price per day in wei (1 ether = 10^18 wei)
35 | */
36 | function rentOutproperty(string memory name, string memory description, uint256 price) public {
37 | // create a property object
38 |
39 | // Persist `property` object to the "permanent" storage
40 |
41 | // emit an event to notify the clients
42 | }
43 |
44 | /**
45 | * @dev Make an Airbnb booking
46 | * @param _propertyId id of the property to rent out
47 | * @param checkInDate Check-in date
48 | * @param checkoutDate Check-out date
49 | */
50 | function rentProperty(uint256 _propertyId, uint256 checkInDate, uint256 checkoutDate) public payable {
51 | // Retrieve `property` object from the storage
52 |
53 | // Assert that property is active
54 |
55 | // Assert that property is available for the dates
56 |
57 | // Check the customer has sent an amount equal to (pricePerDay * numberOfDays)
58 |
59 | // send funds to the owner of the property
60 | _sendFunds(property.owner, msg.value);
61 |
62 | // conditions for a booking are satisfied, so make the booking
63 | _createBooking(_propertyId, checkInDate, checkoutDate);
64 | }
65 |
66 | function _sendFunds (address beneficiary, uint256 value) internal {
67 | // address(uint160()) is a weird solidity quirk
68 | // Read more here: https://solidity.readthedocs.io/en/v0.5.10/050-breaking-changes.html?highlight=address%20payable#explicitness-requirements
69 | address(uint160(beneficiary)).transfer(value);
70 | }
71 |
72 | function _createBooking(uint256 _propertyId, uint256 checkInDate, uint256 checkoutDate) internal {
73 | // Create a new booking object
74 |
75 | // persist to storage
76 |
77 | // Retrieve `property` object from the storage
78 |
79 | // Mark the property booked on the requested dates
80 |
81 | // Emit an event to notify clients
82 | }
83 |
84 | /**
85 | * @dev Take down the property from the market
86 | * @param _propertyId Property ID
87 | */
88 | function markPropertyAsInactive(uint256 _propertyId) public {
89 | require(
90 | properties[_propertyId].owner == msg.sender,
91 | "THIS IS NOT YOUR PROPERTY"
92 | );
93 | properties[_propertyId].isActive = false;
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/instructions.md:
--------------------------------------------------------------------------------
1 | ### Let's begin by writing our data structures
2 | 1. `struct Property` represents a property that you may want to rent out
3 | ```
4 | struct Property {
5 | string name;
6 | string description;
7 | bool isActive; // is property active
8 | uint256 price; // per day price in wei (1 ether = 10^18 wei)
9 | address owner; // Owner of the property
10 |
11 | // Is the property booked on a particular day,
12 | // For the sake of simplicity, we assign 0 to Jan 1, 1 to Jan 2 and so on
13 | // so isBooked[31] will denote whether the property is booked for Feb 1
14 | bool[] isBooked;
15 | }
16 | ```
17 |
18 | 2. `struct Booking` represents the details of a particular booking
19 | ```
20 | struct Booking {
21 | uint256 propertyId;
22 | uint256 checkInDate;
23 | uint256 checkoutDate;
24 | address user;
25 | }
26 | ```
27 |
28 | ### Functions
29 | 1. Put up your funky space on the market
30 | ```
31 | function rentOutproperty(string memory name, string memory description, uint256 price) public {
32 | Property memory property = Property({
33 | name: name,
34 | description: description,
35 | isActive: true,
36 | price: price,
37 | owner: msg.sender,
38 | isBooked: new bool[](365)
39 | });
40 |
41 | // Persist `property` object to the "permanent" storage
42 | properties[propertyId] = property;
43 |
44 | // emit an event to notify the clients
45 | emit NewProperty(propertyId++);
46 | }
47 | ```
48 |
49 | 2. Rent/Book a property for your vacation!
50 | ```
51 | function rentProperty(uint256 _propertyId, uint256 checkInDate, uint256 checkoutDate) public payable {
52 | // Retrieve `property` object from the storage
53 | Property storage property = properties[_propertyId];
54 |
55 | // Assert that property is active
56 | require(
57 | property.isActive == true,
58 | "property with this ID is not active"
59 | );
60 |
61 | // Assert that property is available for the dates
62 | for (uint256 i = checkInDate; i < checkoutDate; i++) {
63 | if (property.isBooked[i] == true) {
64 | // if property is booked on a day, revert the transaction
65 | revert("property is not available for the selected dates");
66 | }
67 | }
68 |
69 | // Check the customer has sent an amount equal to (pricePerDay * numberOfDays)
70 | require(
71 | msg.value == property.price * (checkoutDate - checkInDate),
72 | "Sent insufficient funds"
73 | );
74 | }
75 | ```
76 |
77 | 3. Helper function to create a booking
78 | ```
79 | function _createBooking(uint256 _propertyId, uint256 checkInDate, uint256 checkoutDate) internal {
80 | // Create a new booking object
81 | Booking memory booking = Booking({
82 | propertyId: _propertyId,
83 | checkInDate: checkInDate,
84 | checkoutDate: checkoutDate,
85 | user: msg.sender
86 | });
87 |
88 | // persist to storage
89 | bookings[bookingId] = booking;
90 |
91 | // Retrieve `property` object from the storage
92 | Property storage property = properties[_propertyId];
93 |
94 | // Mark the property booked on the requested dates
95 | for (uint256 i = checkInDate; i < checkoutDate; i++) {
96 | property.isBooked[i] = true;
97 | }
98 |
99 | // Emit an event to notify clients
100 | emit NewBooking(_propertyId, bookingId++);
101 | }
102 | ```
103 |
104 |
--------------------------------------------------------------------------------
/dapp-ui/components/detailsModal.vue:
--------------------------------------------------------------------------------
1 |
2 |