pragma solidity 0.4.25; contract Taxi { struct Participant { address addr; uint accountBalance; } struct Driver { address addr; uint accountBalance; uint32 lastPaidTimestamp; } struct CarProposal{ uint32 proposedCarID; uint price; uint32 offerValidTime; } struct PurchaseProposal{ CarProposal carToBePurchasedByDealer; mapping(address=>bool) participantsVoted; uint8 yesCount; // no need to hold the approval state, it will be determined by vote counts inside sellCar() called by the dealer. } address public manager; mapping (address=>bool) public participantJoinedFlag; Participant[] public participants; Driver public driver; address public dealer; uint32 public carID; CarProposal public carProposal; PurchaseProposal purchaseProposal; uint32 public lastCarExpensePaid_timestamp; uint32 dividendLastPaidTimestamp; modifier onlyManager(){ require(msg.sender == manager, "Only manager can call this function."); _; } modifier onlyDealer(){ require(msg.sender == dealer, "Only dealer can call this function."); _; } modifier onlyDriver(){ require(msg.sender == driver.addr, "Only driver can call this function."); _; } function getTime() public view returns(uint32){ return uint32(now); } constructor() public { manager = msg.sender; } function join() public payable{ require(!participantJoinedFlag[msg.sender],"Can only join once."); require(participants.length < 101, "Maximum 100 participants."); require(msg.value == 10 ether, "Must send exactly 10 ether to join."); Participant memory newParticipant = Participant({ addr: msg.sender, accountBalance: 0 }); participantJoinedFlag[msg.sender] = true; participants.push(newParticipant); } function getContractBalance() public view returns(uint){ return address(this).balance; } function participantsNum() public view returns(uint8){ return uint8(participants.length); } function setCarDealer(address dealerAddr) public onlyManager{ dealer = dealerAddr; } /// @param validTime Number of the last block which will accept votes for this proposal function carPropose(uint32 carId, uint price, uint32 validTime) public onlyDealer{ carProposal.proposedCarID = carId; carProposal.price = price; carProposal.offerValidTime = validTime; } function purchaseCar() public onlyManager{ require(now < carProposal.offerValidTime, "Offer valid time has passed"); // send the money dealer.transfer(carProposal.price); // update carId carID = carProposal.proposedCarID; // reset proposal carProposal.offerValidTime = 0; } /// @param validTime Number of the last block which will accept votes for this proposal function purchasePropose(uint32 carId, uint price, uint32 validTime) public onlyDealer{ CarProposal memory carProposalToBePurchasedByDealer = CarProposal({ proposedCarID: carId, price: price, offerValidTime: validTime }); purchaseProposal.carToBePurchasedByDealer = carProposalToBePurchasedByDealer; purchaseProposal.yesCount = 0; } /// @notice Allows a participant to vote for the current proposal. function approveSellProposal() public{ require(participantJoinedFlag[msg.sender], "Must be a participant to vote."); require(purchaseProposal.carToBePurchasedByDealer.offerValidTime >= now, "Offer valid time has passed."); require(!purchaseProposal.participantsVoted[msg.sender], "Can only vote once."); purchaseProposal.participantsVoted[msg.sender] = true; purchaseProposal.yesCount++; } function sellCar() public payable onlyDealer{ require(msg.value == purchaseProposal.carToBePurchasedByDealer.price, "Send the car value."); require(purchaseProposal.yesCount > participants.length / 2, "Votes not enough to approve the proposal."); require(dealer.balance >= purchaseProposal.carToBePurchasedByDealer.price, "Dealer shall have enough money to buy the car."); carID = 0; delete purchaseProposal; } function setDriver(address adr) public onlyManager{ Driver memory newDriver = Driver({ addr: adr, accountBalance: 0, lastPaidTimestamp: 0 }); driver = newDriver; } function getCharge() public payable{ require(msg.value>0, "No free rides. Send something please :)"); // nothing to do, fare will be sent with message.value } function paySalary() public onlyManager{ require(now - driver.lastPaidTimestamp > 720, "Last salary paid not earlier than a month"); driver.accountBalance += 5 ether; // salary is 5 ether driver.lastPaidTimestamp = uint32(now); } function getSalary() public onlyDriver{ require(driver.accountBalance > 0, "Driver balance is zero."); uint tmpBalance = driver.accountBalance; driver.accountBalance = 0; driver.addr.transfer(tmpBalance); } function carExpenses() public onlyManager{ // check if the last time the expenses were paid was more than 6 months ago // 15778800: 6 months in seconds require(now - 4320 > lastCarExpensePaid_timestamp, "Car expenses shall be paid at least 6 months after the last one."); dealer.transfer(10 ether); lastCarExpensePaid_timestamp = uint32(now); } function payDividend() public onlyManager{ require(now - 4320 > dividendLastPaidTimestamp, "Dividend already paid in the last 6 months."); uint amountToBeShared = address(this).balance; // check if lastCarExpensePaid_timestamp is in current 6 month period. // if no, subtract before dividing if(now - lastCarExpensePaid_timestamp > 4320){ amountToBeShared -= 10 ether; } // check if driver.lastPaidTimestamp is in current month. // if no, subtract before dividing if(now - driver.lastPaidTimestamp > 720) amountToBeShared -= 5 ether; // divide uint sharePerParticipant = amountToBeShared / participants.length; for(uint8 i=0; i<participants.length; i++){ participants[i].accountBalance += sharePerParticipant; } } function getDividend() public{ require(!participantJoinedFlag[msg.sender], "Not a participant. Join first to be able to get dividend."); for(uint8 i=0; i<participants.length; i++){ if(participants[i].addr == msg.sender){ assert(address(this).balance >= participants[i].accountBalance); participants[i].accountBalance = 0; msg.sender.transfer(participants[i].accountBalance); break; } } } }
0.4.25