irongit
defi_property/contracts/HomeTransaction.sol
206 lines6.0 KB
1// SPDX-License-Identifier: MIT
2pragma solidity ^0.8.20;
3
4contract HomeTransaction {
5 // Constants
6 uint constant FINALIZATION_WINDOW = 5 minutes;
7 uint constant MIN_DEPOSIT_PERCENTAGE = 10;
8 uint constant MAX_DEPOSIT_PERCENTAGE = 100;
9
10 enum ContractState {
11 WaitingSellerSignature,
12 WaitingBuyerSignature,
13 WaitingRealtorReview,
14 WaitingFinalization,
15 Finalized,
16 Rejected
17 }
18 ContractState public contractState = ContractState.WaitingSellerSignature;
19
20 // Roles acting on contract
21 address payable public realtor;
22 address payable public seller;
23 address payable public buyer;
24
25 // Contract details
26 string public homeAddress;
27 string public zip;
28 string public city;
29 uint public realtorFee;
30 uint public price;
31
32 // Set when buyer signs and pays deposit
33 uint public deposit;
34 uint public finalizeDeadline;
35
36 // Set when realtor reviews closing conditions
37 enum ClosingConditionsReview { Pending, Accepted, Rejected }
38 ClosingConditionsReview public closingConditionsReview = ClosingConditionsReview.Pending;
39
40 // Events
41 event SellerSigned(address indexed seller);
42 event BuyerSignedAndDeposited(address indexed buyer, uint deposit);
43 event RealtorReviewed(address indexed realtor, bool accepted);
44 event TransactionFinalized(address indexed buyer, uint totalPrice);
45 event TransactionWithdrawn(address indexed initiator);
46
47 constructor(
48 string memory _address,
49 string memory _zip,
50 string memory _city,
51 uint _realtorFee,
52 uint _price,
53 address payable _realtor,
54 address payable _seller,
55 address payable _buyer
56 ) {
57 require(_price >= _realtorFee, "Price must be >= realtor fee");
58 require(_realtor != address(0), "Invalid realtor address");
59 require(_seller != address(0), "Invalid seller address");
60 require(_buyer != address(0), "Invalid buyer address");
61
62 realtor = _realtor;
63 seller = _seller;
64 buyer = _buyer;
65 homeAddress = _address;
66 zip = _zip;
67 city = _city;
68 price = _price;
69 realtorFee = _realtorFee;
70 }
71
72 modifier onlySeller() {
73 require(msg.sender == seller, "Only seller can call");
74 _;
75 }
76
77 modifier onlyBuyer() {
78 require(msg.sender == buyer, "Only buyer can call");
79 _;
80 }
81
82 modifier onlyRealtor() {
83 require(msg.sender == realtor, "Only realtor can call");
84 _;
85 }
86
87 modifier inState(ContractState _state) {
88 require(contractState == _state, "Invalid contract state");
89 _;
90 }
91
92 function sellerSignContract()
93 external
94 onlySeller
95 inState(ContractState.WaitingSellerSignature)
96 {
97 contractState = ContractState.WaitingBuyerSignature;
98 emit SellerSigned(msg.sender);
99 }
100
101 function buyerSignContractAndPayDeposit()
102 external
103 payable
104 onlyBuyer
105 inState(ContractState.WaitingBuyerSignature)
106 {
107 uint minDeposit = (price * MIN_DEPOSIT_PERCENTAGE) / 100;
108 uint maxDeposit = (price * MAX_DEPOSIT_PERCENTAGE) / 100;
109
110 require(
111 msg.value >= minDeposit && msg.value <= maxDeposit,
112 "Deposit must be 10-100% of price"
113 );
114
115 deposit = msg.value;
116 finalizeDeadline = block.timestamp + FINALIZATION_WINDOW;
117 contractState = ContractState.WaitingRealtorReview;
118
119 emit BuyerSignedAndDeposited(msg.sender, msg.value);
120 }
121
122 function realtorReviewClosingConditions(bool accepted)
123 external
124 onlyRealtor
125 inState(ContractState.WaitingRealtorReview)
126 {
127 if (accepted) {
128 closingConditionsReview = ClosingConditionsReview.Accepted;
129 contractState = ContractState.WaitingFinalization;
130 } else {
131 closingConditionsReview = ClosingConditionsReview.Rejected;
132 contractState = ContractState.Rejected;
133
134 // Refund buyer's deposit
135 (bool success, ) = buyer.call{value: deposit}("");
136 require(success, "Refund failed");
137 }
138
139 emit RealtorReviewed(msg.sender, accepted);
140 }
141
142 function buyerFinalizeTransaction()
143 external
144 payable
145 onlyBuyer
146 inState(ContractState.WaitingFinalization)
147 {
148 require(
149 msg.value + deposit == price,
150 "Must pay remaining balance"
151 );
152
153 contractState = ContractState.Finalized;
154
155 // Pay seller (price minus realtor fee)
156 (bool sellerPaid, ) = seller.call{value: price - realtorFee}("");
157 require(sellerPaid, "Seller payment failed");
158
159 // Pay realtor
160 (bool realtorPaid, ) = realtor.call{value: realtorFee}("");
161 require(realtorPaid, "Realtor payment failed");
162
163 emit TransactionFinalized(msg.sender, price);
164 }
165
166 function withdrawFromTransaction()
167 external
168 inState(ContractState.WaitingFinalization)
169 {
170 require(
171 msg.sender == buyer || block.timestamp >= finalizeDeadline,
172 "Only buyer can withdraw before deadline"
173 );
174
175 contractState = ContractState.Rejected;
176
177 // Seller gets deposit minus realtor fee
178 (bool sellerPaid, ) = seller.call{value: deposit - realtorFee}("");
179 require(sellerPaid, "Seller payment failed");
180
181 // Realtor still gets fee
182 (bool realtorPaid, ) = realtor.call{value: realtorFee}("");
183 require(realtorPaid, "Realtor payment failed");
184
185 emit TransactionWithdrawn(msg.sender);
186 }
187
188 // View functions
189 function getContractDetails() external view returns (
190 string memory _homeAddress,
191 string memory _zip,
192 string memory _city,
193 uint _price,
194 uint _realtorFee,
195 ContractState _state
196 ) {
197 return (homeAddress, zip, city, price, realtorFee, contractState);
198 }
199
200 function getRemainingBalance() external view returns (uint) {
201 if (contractState != ContractState.WaitingFinalization) {
202 return 0;
203 }
204 return price - deposit;
205 }
206}