refactor(contracts): update the contract to latest solidity contract
2 files changed, +193 -68
+68-30contracts/Factory.sol
| @@ -1,41 +1,79 @@ | ||
| 1 | -pragma solidity >=0.4.25 <0.6.0; | |
| 1 | +// SPDX-License-Identifier: MIT | |
| 2 | +pragma solidity ^0.8.20; | |
| 2 | 3 | |
| 3 | 4 | import "./HomeTransaction.sol"; |
| 4 | 5 | |
| 5 | 6 | contract Factory { |
| 6 | - HomeTransaction[] contracts; | |
| 7 | + HomeTransaction[] public contracts; | |
| 7 | 8 | |
| 8 | - function create( | |
| 9 | + mapping(address => HomeTransaction[]) public contractsByRealtor; | |
| 10 | + mapping(address => HomeTransaction[]) public contractsBySeller; | |
| 11 | + mapping(address => HomeTransaction[]) public contractsByBuyer; | |
| 12 | + | |
| 13 | + event ContractCreated( | |
| 14 | + address indexed contractAddress, | |
| 15 | + address indexed realtor, | |
| 16 | + address indexed seller, | |
| 17 | + address buyer, | |
| 18 | + uint price | |
| 19 | + ); | |
| 20 | + | |
| 21 | + function create( | |
| 9 | 22 | string memory _address, |
| 10 | 23 | string memory _zip, |
| 11 | 24 | string memory _city, |
| 12 | 25 | uint _realtorFee, |
| 13 | 26 | uint _price, |
| 14 | 27 | address payable _seller, |
| 15 | - address payable _buyer) public returns(HomeTransaction homeTransaction) { | |
| 16 | - homeTransaction = new HomeTransaction( | |
| 17 | - _address, | |
| 18 | - _zip, | |
| 19 | - _city, | |
| 20 | - _realtorFee, | |
| 21 | - _price, | |
| 22 | - msg.sender, | |
| 23 | - _seller, | |
| 24 | - _buyer); | |
| 25 | - contracts.push(homeTransaction); | |
| 26 | - } | |
| 27 | - | |
| 28 | - function getInstance(uint index) public view returns (HomeTransaction instance) { | |
| 29 | - require(index < contracts.length, "index out of range"); | |
| 30 | - | |
| 31 | - instance = contracts[index]; | |
| 32 | - } | |
| 33 | - | |
| 34 | - function getInstances() public view returns (HomeTransaction[] memory instances) { | |
| 35 | - instances = contracts; | |
| 36 | - } | |
| 37 | - | |
| 38 | - function getInstanceCount() public view returns (uint count) { | |
| 39 | - count = contracts.length; | |
| 40 | - } | |
| 41 | -} | |
| 28 | + address payable _buyer | |
| 29 | + ) external returns (HomeTransaction homeTransaction) { | |
| 30 | + homeTransaction = new HomeTransaction( | |
| 31 | + _address, | |
| 32 | + _zip, | |
| 33 | + _city, | |
| 34 | + _realtorFee, | |
| 35 | + _price, | |
| 36 | + payable(msg.sender), // realtor is the caller | |
| 37 | + _seller, | |
| 38 | + _buyer | |
| 39 | + ); | |
| 40 | + | |
| 41 | + contracts.push(homeTransaction); | |
| 42 | + contractsByRealtor[msg.sender].push(homeTransaction); | |
| 43 | + contractsBySeller[_seller].push(homeTransaction); | |
| 44 | + contractsByBuyer[_buyer].push(homeTransaction); | |
| 45 | + | |
| 46 | + emit ContractCreated( | |
| 47 | + address(homeTransaction), | |
| 48 | + msg.sender, | |
| 49 | + _seller, | |
| 50 | + _buyer, | |
| 51 | + _price | |
| 52 | + ); | |
| 53 | + } | |
| 54 | + | |
| 55 | + function getInstance(uint index) external view returns (HomeTransaction) { | |
| 56 | + require(index < contracts.length, "Index out of range"); | |
| 57 | + return contracts[index]; | |
| 58 | + } | |
| 59 | + | |
| 60 | + function getInstances() external view returns (HomeTransaction[] memory) { | |
| 61 | + return contracts; | |
| 62 | + } | |
| 63 | + | |
| 64 | + function getInstanceCount() external view returns (uint) { | |
| 65 | + return contracts.length; | |
| 66 | + } | |
| 67 | + | |
| 68 | + function getContractsByRealtor(address realtor) external view returns (HomeTransaction[] memory) { | |
| 69 | + return contractsByRealtor[realtor]; | |
| 70 | + } | |
| 71 | + | |
| 72 | + function getContractsBySeller(address seller) external view returns (HomeTransaction[] memory) { | |
| 73 | + return contractsBySeller[seller]; | |
| 74 | + } | |
| 75 | + | |
| 76 | + function getContractsByBuyer(address buyer) external view returns (HomeTransaction[] memory) { | |
| 77 | + return contractsByBuyer[buyer]; | |
| 78 | + } | |
| 79 | +} |
+125-38contracts/HomeTransaction.sol
| @@ -1,9 +1,11 @@ | ||
| 1 | -pragma solidity >=0.4.25 <0.6.0; | |
| 1 | +// SPDX-License-Identifier: MIT | |
| 2 | +pragma solidity ^0.8.20; | |
| 2 | 3 | |
| 3 | 4 | contract HomeTransaction { |
| 4 | 5 | // Constants |
| 5 | - uint constant timeBetweenDepositAndFinalization = 5 minutes; | |
| 6 | - uint constant depositPercentage = 10; | |
| 6 | + uint constant FINALIZATION_WINDOW = 5 minutes; | |
| 7 | + uint constant MIN_DEPOSIT_PERCENTAGE = 10; | |
| 8 | + uint constant MAX_DEPOSIT_PERCENTAGE = 100; | |
| 7 | 9 | |
| 8 | 10 | enum ContractState { |
| 9 | 11 | WaitingSellerSignature, |
| @@ -11,10 +13,10 @@ contract HomeTransaction { | ||
| 11 | 13 | WaitingRealtorReview, |
| 12 | 14 | WaitingFinalization, |
| 13 | 15 | Finalized, |
| 14 | - Rejected } | |
| 16 | + Rejected | |
| 17 | + } | |
| 15 | 18 | ContractState public contractState = ContractState.WaitingSellerSignature; |
| 16 | 19 | |
| 17 | - | |
| 18 | 20 | // Roles acting on contract |
| 19 | 21 | address payable public realtor; |
| 20 | 22 | address payable public seller; |
| @@ -33,7 +35,14 @@ contract HomeTransaction { | ||
| 33 | 35 | |
| 34 | 36 | // Set when realtor reviews closing conditions |
| 35 | 37 | enum ClosingConditionsReview { Pending, Accepted, Rejected } |
| 36 | - ClosingConditionsReview closingConditionsReview = ClosingConditionsReview.Pending; | |
| 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); | |
| 37 | 46 | |
| 38 | 47 | constructor( |
| 39 | 48 | string memory _address, |
| @@ -43,8 +52,12 @@ contract HomeTransaction { | ||
| 43 | 52 | uint _price, |
| 44 | 53 | address payable _realtor, |
| 45 | 54 | address payable _seller, |
| 46 | - address payable _buyer) public { | |
| 47 | - require(_price >= _realtorFee, "Price needs to be more than realtor fee!"); | |
| 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"); | |
| 48 | 61 | |
| 49 | 62 | realtor = _realtor; |
| 50 | 63 | seller = _seller; |
| @@ -56,32 +69,61 @@ contract HomeTransaction { | ||
| 56 | 69 | realtorFee = _realtorFee; |
| 57 | 70 | } |
| 58 | 71 | |
| 59 | - function sellerSignContract() public payable { | |
| 60 | - require(seller == msg.sender, "Only seller can sign contract"); | |
| 72 | + modifier onlySeller() { | |
| 73 | + require(msg.sender == seller, "Only seller can call"); | |
| 74 | + _; | |
| 75 | + } | |
| 61 | 76 | |
| 62 | - require(contractState == ContractState.WaitingSellerSignature, "Wrong contract state"); | |
| 77 | + modifier onlyBuyer() { | |
| 78 | + require(msg.sender == buyer, "Only buyer can call"); | |
| 79 | + _; | |
| 80 | + } | |
| 63 | 81 | |
| 64 | - contractState = ContractState.WaitingBuyerSignature; | |
| 82 | + modifier onlyRealtor() { | |
| 83 | + require(msg.sender == realtor, "Only realtor can call"); | |
| 84 | + _; | |
| 65 | 85 | } |
| 66 | 86 | |
| 67 | - function buyerSignContractAndPayDeposit() public payable { | |
| 68 | - require(buyer == msg.sender, "Only buyer can sign contract"); | |
| 87 | + modifier inState(ContractState _state) { | |
| 88 | + require(contractState == _state, "Invalid contract state"); | |
| 89 | + _; | |
| 90 | + } | |
| 69 | 91 | |
| 70 | - require(contractState == ContractState.WaitingBuyerSignature, "Wrong contract state"); | |
| 92 | + function sellerSignContract() | |
| 93 | + external | |
| 94 | + onlySeller | |
| 95 | + inState(ContractState.WaitingSellerSignature) | |
| 96 | + { | |
| 97 | + contractState = ContractState.WaitingBuyerSignature; | |
| 98 | + emit SellerSigned(msg.sender); | |
| 99 | + } | |
| 71 | 100 | |
| 72 | - require(msg.value >= price*depositPercentage/100 && msg.value <= price, "Buyer needs to deposit between 10% and 100% to sign contract"); | |
| 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; | |
| 73 | 109 | |
| 74 | - contractState = ContractState.WaitingRealtorReview; | |
| 110 | + require( | |
| 111 | + msg.value >= minDeposit && msg.value <= maxDeposit, | |
| 112 | + "Deposit must be 10-100% of price" | |
| 113 | + ); | |
| 75 | 114 | |
| 76 | 115 | deposit = msg.value; |
| 77 | - finalizeDeadline = now + timeBetweenDepositAndFinalization; | |
| 78 | - } | |
| 79 | - | |
| 80 | - function realtorReviewedClosingConditions(bool accepted) public { | |
| 81 | - require(realtor == msg.sender, "Only realtor can review closing conditions"); | |
| 116 | + finalizeDeadline = block.timestamp + FINALIZATION_WINDOW; | |
| 117 | + contractState = ContractState.WaitingRealtorReview; | |
| 82 | 118 | |
| 83 | - require(contractState == ContractState.WaitingRealtorReview, "Wrong contract state"); | |
| 119 | + emit BuyerSignedAndDeposited(msg.sender, msg.value); | |
| 120 | + } | |
| 84 | 121 | |
| 122 | + function realtorReviewClosingConditions(bool accepted) | |
| 123 | + external | |
| 124 | + onlyRealtor | |
| 125 | + inState(ContractState.WaitingRealtorReview) | |
| 126 | + { | |
| 85 | 127 | if (accepted) { |
| 86 | 128 | closingConditionsReview = ClosingConditionsReview.Accepted; |
| 87 | 129 | contractState = ContractState.WaitingFinalization; |
| @@ -89,31 +131,76 @@ contract HomeTransaction { | ||
| 89 | 131 | closingConditionsReview = ClosingConditionsReview.Rejected; |
| 90 | 132 | contractState = ContractState.Rejected; |
| 91 | 133 | |
| 92 | - buyer.transfer(deposit); | |
| 134 | + // Refund buyer's deposit | |
| 135 | + (bool success, ) = buyer.call{value: deposit}(""); | |
| 136 | + require(success, "Refund failed"); | |
| 93 | 137 | } |
| 138 | + | |
| 139 | + emit RealtorReviewed(msg.sender, accepted); | |
| 94 | 140 | } |
| 95 | 141 | |
| 96 | - function buyerFinalizeTransaction() public payable { | |
| 97 | - require(buyer == msg.sender, "Only buyer can finalize transaction"); | |
| 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 | + ); | |
| 98 | 152 | |
| 99 | - require(contractState == ContractState.WaitingFinalization, "Wrong contract state"); | |
| 153 | + contractState = ContractState.Finalized; | |
| 100 | 154 | |
| 101 | - require(msg.value + deposit == price, "Buyer needs to pay the rest of the cost to finalize transaction"); | |
| 155 | + // Pay seller (price minus realtor fee) | |
| 156 | + (bool sellerPaid, ) = seller.call{value: price - realtorFee}(""); | |
| 157 | + require(sellerPaid, "Seller payment failed"); | |
| 102 | 158 | |
| 103 | - contractState = ContractState.Finalized; | |
| 159 | + // Pay realtor | |
| 160 | + (bool realtorPaid, ) = realtor.call{value: realtorFee}(""); | |
| 161 | + require(realtorPaid, "Realtor payment failed"); | |
| 104 | 162 | |
| 105 | - seller.transfer(price-realtorFee); | |
| 106 | - realtor.transfer(realtorFee); | |
| 163 | + emit TransactionFinalized(msg.sender, price); | |
| 107 | 164 | } |
| 108 | 165 | |
| 109 | - function anyWithdrawFromTransaction() public { | |
| 110 | - require(buyer == msg.sender || finalizeDeadline <= now, "Only buyer can withdraw before transaction deadline"); | |
| 111 | - | |
| 112 | - require(contractState == ContractState.WaitingFinalization, "Wrong contract state"); | |
| 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 | + ); | |
| 113 | 174 | |
| 114 | 175 | contractState = ContractState.Rejected; |
| 115 | 176 | |
| 116 | - seller.transfer(deposit-realtorFee); | |
| 117 | - realtor.transfer(realtorFee); | |
| 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; | |
| 118 | 205 | } |
| 119 | -} | |
| 206 | +} |