---
# System prepended metadata

title: 112學年 第2學期 金融科技安全 期末專題報告

---

112學年 第2學期 金融科技安全 期末專題報告
===
[TOC]	
- 班級: 資工碩一	學號 : R1243011 			姓名:賴偉晟
- :link: [HackMD_link(建議可用網站來觀看此次報告)](https://hackmd.io/@lailai1208/SksYW5WrR)
	
## 區塊鍊智能合約應用程式開發 (共1題，100分，滿分100分)


請以課程中所教授智能合約範例為基礎，構思一Ethereum 智能合約應用，並透過Remix IDE + Metamask部署於Test Network。
請於2024/6/20 23:59前透過數位學習園區繳交報告及程式碼

### A. 描述本專題的題目、動機、目的與架構。
- **題目 :** 租賃管理智能合約(以常見租房為例)
- **動機 :** 在租賃市場中，常見的問題包括租金支付遲延、押金爭議及合約條款的透明度不足。
- **目的 :** 通過智能合約，我們可以自動化租金支付，確保押金的公正處理，並提供一個不可篡改的合約條款記錄，從而提高交易的透明度與信任。
- **架構 :**
    1.	智能合約初始化 : 房東建立基本的租賃條款，包括租金金額、支付時間、租期等。
    2.	租金支付 : 租戶按月支付租金到智能合約，如果逾期未支付，合約自動產生提醒並可能加收遲延費用。
    3.	押金處理 : 租戶入住時支付押金到智能合約，租約結束時，根據物業狀態自動退回押金或部分扣除。
    4.	合約終止 : 租約到期或雙方同意提前終止，合約自動執行相關條款。

    </br>

    |             Complete Workflow Diagram              |
    |:--------------------------------------------------:|
    | ![image](https://hackmd.io/_uploads/Sk6uScbSA.png) |


### B. 解說本專題程式的開發環境、關鍵程式碼。
- 開發環境: Remix IDE，使用Solidity語言開發智能合約。
- 初始變數設置:
    ```solidity=4
    address payable private owner; // 出租方地址
    address private tenant; // 租戶地址
    uint256 private deposit; // 押金金额（單位：Wei）
    uint256 private rent; // 每月租金（單位：Wei）
    uint256 private dueDate; // 每月租金支付截止日
    bool private leaseActive; // 租约是否有效
    mapping(uint256 => bool) public rentPaid; // 每個月的租金支付狀態
    uint256 private contractStart; // 合约開始時間

    ```

- 關鍵程式碼:
    - 合約建立時，確定雙方同意的條款並寫入區塊鍊。
        ```solidity=18
        constructor(address _tenant, uint256 _deposit, uint256 _rent,
                    uint256 _dueDate) public {
            owner = msg.sender; // 將部署者設為出租方
            tenant = _tenant; // 設定租户
            deposit = _deposit; // 設定押金金额（以 Wei 為單位）
            rent = _rent; // 設定每月租金（以 Wei 為单位）
            dueDate = _dueDate; // 設定租金支付截止日
            leaseActive = false; // 初始化時租约未開通
            contractStart = block.timestamp; // 初始化合约開始
        }
        ```
    - 租金支付函數，自動檢查支付日期並記錄交易。
        ```solidity=38
        function payRent() public payable {
            require(msg.sender == tenant, "Only tenant can pay rent.");
            require(leaseActive, "Lease is not active.");
            require(msg.value == rent, "Incorrect rent amount.");
            uint256 currentMonth = getCurrentMonth();
            require(!rentPaid[currentMonth], "Rent already paid for this month.");
            require(isWithinDueDate(), "Rent payment is overdue.");

            rentPaid[currentMonth] = true;
            emit RentPaid(tenant, msg.value);
        }
        ```
    - 押金管理，包括存入與退還邏輯。
    
        1. 押金存入(由租客負責)
            ```solidity=29
            function payDeposit() public payable {
            require(msg.sender == tenant, "Only tenant can pay the deposit.");
            require(msg.value == deposit, "Incorrect deposit amount.");
            require(!leaseActive, "Lease is already active.");

            leaseActive = true;
            emit DepositReceived(tenant, msg.value);
            }
            ```
        2. 押金退還(任一方[租客 or 房東]終止合約後，由房東負責)
            ```solidity=59
            function refundDeposit() public {
            require(msg.sender == owner, "Only owner can refund the deposit.");
            require(!leaseActive, "Lease is still active.");
        
            address payable tenantPayable = address(uint160(tenant));
            tenantPayable.transfer(deposit);
            emit DepositRefunded(tenant);
            }
            ```
            
- 完整程式碼 : 

    ```solidity=
    pragma solidity >=0.4.22 <0.6.0;

    contract RentalContract {
        address payable private owner; // 出租方地址
        address private tenant; // 租户地址
        uint256 private deposit; // 押金金额（單位：Wei）
        uint256 private rent; // 每月租金（單位：Wei）
        uint256 private dueDate; // 每月租金支付截止日
        bool private leaseActive; // 租约是否有效
        mapping(uint256 => bool) public rentPaid; // 每個月的租金支付狀態
        uint256 private contractStart; // 合约開始時間

        event DepositReceived(address tenant, uint256 amount);
        event RentPaid(address tenant, uint256 amount);
        event LeaseTerminated(address requestor);
        event DepositRefunded(address tenant);

        constructor(address _tenant, uint256 _deposit, 
                    uint256 _rent, uint256 _dueDate) public {
            owner = msg.sender; // 將部署者設為出租方
            tenant = _tenant; // 設定定租户
            deposit = _deposit; // 設定押金金额（以 Wei 為單位）
            rent = _rent; // 設定每月租金（以 Wei 為單位）
            dueDate = _dueDate; // 設定租金支付截止日
            leaseActive = false; // 初始化時租约未開通
            contractStart = block.timestamp; // 初始化合约開始時間
        }

        function payDeposit() public payable {
            require(msg.sender == tenant, "Only tenant can pay the deposit.");
            require(msg.value == deposit, "Incorrect deposit amount.");
            require(!leaseActive, "Lease is already active.");

            leaseActive = true;
            emit DepositReceived(tenant, msg.value);
        }

        function payRent() public payable {
            require(msg.sender == tenant, "Only tenant can pay rent.");
            require(leaseActive, "Lease is not active.");
            require(msg.value == rent, "Incorrect rent amount.");
            uint256 currentMonth = getCurrentMonth();
            require(!rentPaid[currentMonth], "Rent already paid for this month.");
            require(isWithinDueDate(), "Rent payment is overdue.");

            rentPaid[currentMonth] = true;
            emit RentPaid(tenant, msg.value);
        }

        function withdrawRent() public {
            require(msg.sender == owner, "Only owner can withdraw rent.");
            require(leaseActive, "Lease is not active.");
            uint256 currentMonth = getCurrentMonth();
            require(rentPaid[currentMonth], "Rent not paid for this month.");

            owner.transfer(rent);
        }

        function refundDeposit() public {
            require(msg.sender == owner, "Only owner can refund the deposit.");
            require(!leaseActive, "Lease is still active.");

            address payable tenantPayable = address(uint160(tenant));
            tenantPayable.transfer(deposit);
            emit DepositRefunded(tenant);
        }

        function terminateLease() public {
            require(msg.sender == owner || msg.sender == tenant, "Only owner or tenant can terminate the lease.");
            require(leaseActive, "Lease is already terminated.");

            leaseActive = false;
            emit LeaseTerminated(msg.sender);
        }

        function getCurrentMonth() private view returns (uint256) {
            return (block.timestamp - contractStart) / 30 days;
        }

        function isWithinDueDate() private view returns (bool) {
            uint256 currentDay = (block.timestamp / 1 days) % 30;
            return currentDay <= dueDate;
        }

        // 新增的 getter 函数，常看合約條款狀態
        function getDeposit() public view returns (uint256) {
            return deposit;
        }

        function getRent() public view returns (uint256) {
            return rent;
        }

        function getDueDate() public view returns (uint256) {
            return dueDate;
        }

        function getLeaseActive() public view returns (bool) {
            return leaseActive;
        }

        function getCurrentTimestamp() public view returns (uint256) {
            return block.timestamp;
        }

    }
    ```

### C. 呈現本專題程式的執行結果。

- **Step 1: 部署合約**
    使用出租方（房東）的以太坊帳戶部署合約。部署時需要指定：
    - 租戶的以太坊地址（_tenant）。
    - 押金金額（_deposit），單位為Wei，本次設定金額為4。
    - 每月租金（_rent），單位為Wei，本次設定金額為2。
    - 租金支付截止日（_dueDate），本次設定日期為30。       
    </br>

    |                        結果                        |
    |:--------------------------------------------------:|
    | ![image](https://hackmd.io/_uploads/rJlbk2bHR.png)|

- **Step 2: 租戶支付押金**
    - 租戶支付押金：切換到租戶的帳戶。
    - 在Value 輸入金額 4 ，並調用 payDeposit 函數，發送等於押金金額(4)的以太幣。
    - 支付押金完成後再調用 getLeaseActive() 函數，確認租約正式開通(注意此時Balance金額有變化)。
    </br>

    |                        結果                        |
    |:--------------------------------------------------:|
    | ![image](https://hackmd.io/_uploads/SyWhYiZH0.png) |

- **Step 3: 租戶支付租金**
    - 在租金支付截止日之前，租戶在Value 輸入金額 2 ， 應調用 payRent()函數，以支付正確的租金金額。
    - 支付租金完成後再調用  event RentPaid(0)函數，確認租金已經支付。(RentPaid=true, arg: 第一個月=0; 注意此時Balance金額有變化)。
    </br>

    |                        結果                        |
    |:--------------------------------------------------:|
    | ![image](https://hackmd.io/_uploads/HkCtynZr0.png)|

- **Step 4: 房東提取租金**
    - 房東可以在確認租金已支付後，通過調用 withdrawRent 函數來提取租金(注意此時Balance金額有變化)。
    </br>

    |                        結果                        |
    |:--------------------------------------------------:|
    |![image](https://hackmd.io/_uploads/Sk4p13-SA.png)|

- **Step 5: 租約終止**
    - 租約可以由租戶或房東通過調用 terminateLease 函數來終止(本次由房東中止)。
    - 這將改變合約的狀態，使得租約不再有效(getLeaseActive=false)。
    </br>

    |                        結果                        |
    |:--------------------------------------------------:|
    |![image](https://hackmd.io/_uploads/B1P7ehWBC.png)|

- **Step 6: 房東退還押金**
    - 一旦租約終止，房東需要調用 refundDeposit 函數退還押金給租戶(注意此時Balance金額有變化)。
    </br>

    |                        結果                        |
    |:--------------------------------------------------:|
    |![image](https://hackmd.io/_uploads/BJVYe2ZBA.png)|
    
