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 |
|:--------------------------------------------------:|
|  |
### 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>
| 結果 |
|:--------------------------------------------------:|
| |
- **Step 2: 租戶支付押金**
- 租戶支付押金:切換到租戶的帳戶。
- 在Value 輸入金額 4 ,並調用 payDeposit 函數,發送等於押金金額(4)的以太幣。
- 支付押金完成後再調用 getLeaseActive() 函數,確認租約正式開通(注意此時Balance金額有變化)。
</br>
| 結果 |
|:--------------------------------------------------:|
|  |
- **Step 3: 租戶支付租金**
- 在租金支付截止日之前,租戶在Value 輸入金額 2 , 應調用 payRent()函數,以支付正確的租金金額。
- 支付租金完成後再調用 event RentPaid(0)函數,確認租金已經支付。(RentPaid=true, arg: 第一個月=0; 注意此時Balance金額有變化)。
</br>
| 結果 |
|:--------------------------------------------------:|
| |
- **Step 4: 房東提取租金**
- 房東可以在確認租金已支付後,通過調用 withdrawRent 函數來提取租金(注意此時Balance金額有變化)。
</br>
| 結果 |
|:--------------------------------------------------:|
||
- **Step 5: 租約終止**
- 租約可以由租戶或房東通過調用 terminateLease 函數來終止(本次由房東中止)。
- 這將改變合約的狀態,使得租約不再有效(getLeaseActive=false)。
</br>
| 結果 |
|:--------------------------------------------------:|
||
- **Step 6: 房東退還押金**
- 一旦租約終止,房東需要調用 refundDeposit 函數退還押金給租戶(注意此時Balance金額有變化)。
</br>
| 結果 |
|:--------------------------------------------------:|
||