Skip to content

Instantly share code, notes, and snippets.

@EtDu
Last active March 22, 2020 07:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save EtDu/4e0edb9c598cacec9f02a8a92abee21b to your computer and use it in GitHub Desktop.
Save EtDu/4e0edb9c598cacec9f02a8a92abee21b to your computer and use it in GitHub Desktop.
pragma solidity ^0.5.17;
import "./SafeMath.sol";
import "./provableAPI.sol";
contract LeaseGenerator is usingProvable {
using SafeMath for uint;
address payable landlordAddress;
address payable tenantAddress;
uint ETHUSD;
uint tenantPayment;
uint leaseBalanceWei;
enum State {
payingLeaseDeposit,
payingLease,
collectingLeaseDeposit,
reclaimingLeaseDeposit,
idle
}
State workingState;
struct Lease {
uint8 numberOfMonths;
uint8 monthsPaid;
uint16 monthlyAmountUsd;
uint16 leaseDepositUsd;
uint32 leasePaymentWindowSeconds;
uint64 leasePaymentWindowEnd;
uint64 depositPaymentWindowEnd;
bool leaseDepositPaid;
bool leaseFullyPaid;
}
mapping (bytes32 => bool) validIds;
mapping (address => Lease) tenantLease;
modifier onlyLandlord() {
require(msg.sender == landlordAddress, "Must be the landlord to create a lease");
_;
}
event leaseCreated(
uint8 numberOfMonths,
uint8 monthsPaid,
uint16 monthlyAmountUsd,
uint16 leaseDepositUsd,
uint32 leasePaymentWindowSeconds,
bool leaseDepositPaid,
bool leaseFullyPaid
);
event leaseDepositPaid(
address tenantAddress,
uint amountSentUsd
);
event leasePaymentPaid(
address tenantAddress,
uint amountSentUsd
);
event leaseDepositCollected(
address tenantAddress,
uint amountCollected
);
event leaseDepositReclaimed(
address tenantAddress,
uint amountReclaimed
);
event leaseFullyPaid(
address tenantAddress,
uint numberOfmonths,
uint monthsPaid
);
event fundsWithdrawn(
uint transferAmount,
uint leaseBalanceWei
);
constructor () public payable {
landlordAddress = msg.sender;
provable_setCustomGasPrice(100000000000);
OAR = OracleAddrResolverI(0xB7D2d92e74447535088A32AD65d459E97f692222);
}
function fetchUsdRate() internal {
require(provable_getPrice("URL") < address(this).balance, "Not enough Ether in contract, please add more");
bytes32 queryId = provable_query("URL", "json(https://api.pro.coinbase.com/products/ETH-USD/ticker).price");
validIds[queryId] = true;
}
function __callback(bytes32 myId, string memory result) public {
require(validIds[myId], "Provable query IDs do not match, no valid call was made to provable_query()");
require(msg.sender == provable_cbAddress(), "Calling address does match usingProvable contract address ");
validIds[myId] = false;
ETHUSD = parseInt(result);
if (workingState == State.payingLeaseDeposit) {
_payLeaseDeposit();
} else if (workingState == State.payingLease) {
_payLease();
} else if (workingState == State.collectingLeaseDeposit) {
_collectLeaseDeposit();
} else if (workingState == State.reclaimingLeaseDeposit) {
_reclaimLeaseDeposit();
}
}
function createNewLease(
uint8 numberOfMonths,
uint16 monthlyAmountUsd,
uint16 leaseDepositUsd,
uint32 leasePaymentWindowSeconds,
uint32 depositPaymentWindowSeconds,
address payable tenantAddr
) public onlyLandlord {
uint64 depositPaymentWindowEnd = uint64(now.add(depositPaymentWindowSeconds));
tenantLease[tenantAddr] = Lease(
numberOfMonths,
0,
monthlyAmountUsd,
leaseDepositUsd,
leasePaymentWindowSeconds,
0,
depositPaymentWindowEnd,
false,
false,
false
);
emit leaseCreated(
numberOfMonths,
0,
monthlyAmountUsd,
leaseDepositUsd,
leasePaymentWindowSeconds,
false,
false,
false
);
}
function payLeaseDeposit() public payable {
Lease storage lease = tenantLease[msg.sender];
require(!lease.leaseDepositPaid, "Lease deposit is already paid.");
require(lease.depositPaymentWindowEnd >= now, "Lease deposit payment must fit into payment window");
tenantAddress = msg.sender;
tenantPayment = msg.value;
workingState = State.payingLeaseDeposit;
fetchUsdRate();
}
function _payLeaseDeposit() internal {
workingState = State.idle;
Lease storage lease = tenantLease[tenantAddress];
uint amountSentUsd = tenantPayment.mul(ETHUSD).div(1e18);
require(
amountSentUsd >= lease.leaseDepositUsd - 5 &&
amountSentUsd <= lease.leaseDepositUsd + 5,
"Deposit payment must equal to the deposit amount with a maximum offset of $5");
lease.leaseDepositPaid = true;
lease.depositPaymentWindowEnd = 0;
lease.leasePaymentWindowEnd = uint64(now + lease.leasePaymentWindowSeconds);
emit leaseDepositPaid(
tenantAddress,
amountSentUsd
);
}
function payLease() public payable {
Lease storage lease = tenantLease[msg.sender];
require(lease.leaseDepositPaid, "Lease deposit must be paid before making lease payments");
require(!lease.leaseFullyPaid, "Lease has already been fully paid");
require(lease.leasePaymentWindowEnd >= now, "Lease payment must fit into payment window");
tenantAddress = msg.sender;
tenantPayment = msg.value;
workingState = State.payingLease;
fetchUsdRate();
}
function _payLease() internal {
workingState = State.idle;
Lease storage lease = tenantLease[tenantAddress];
uint amountSentUsd = tenantPayment.mul(ETHUSD).div(1e18);
require(
amountSentUsd >= lease.monthlyAmountUsd - 5,
"Lease payment must be greater than or equal to the monthly amount with a maximum offset of $5");
uint monthsPaid = uint256(lease.monthsPaid).add(amountSentUsd.add(10).div(uint256(lease.monthlyAmountUsd)));
lease.monthsPaid = uint8(monthsPaid);
leaseBalanceWei = leaseBalanceWei.add(tenantPayment);
if (monthsPaid == lease.numberOfMonths) {
lease.leaseFullyPaid = true;
lease.leasePaymentWindowEnd = 0;
emit leaseFullyPaid(
tenantAddress,
lease.numberOfMonths,
monthsPaid
);
} else {
lease.leasePaymentWindowEnd = lease.leasePaymentWindowEnd + lease.leasePaymentWindowSeconds;
emit leasePaymentPaid(
tenantAddress,
amountSentUsd
);
}
}
function collectLeaseDeposit(address payable tenantAddr) public onlyLandlord {
Lease storage lease = tenantLease[tenantAddr];
require(!lease.leaseFullyPaid, "Cannot collect lease deposit if lease is already paid");
require(lease.leasePaymentWindowEnd <= now, "Lease payment must be overdue past payment window to collect lease deposit");
require(lease.leaseDepositUsd > 0, "Lease deposit has already been removed");
tenantAddress = tenantAddr;
workingState = State.collectingLeaseDeposit;
fetchUsdRate();
}
function _collectLeaseDeposit() internal {
workingState = State.idle;
Lease storage lease = tenantLease[tenantAddress];
uint leaseDeposit = lease.leaseDepositUsd;
lease.leaseDepositUsd = 0;
landlordAddress.transfer(leaseDeposit.div(ETHUSD).mul(1e18));
emit leaseDepositCollected(
tenantAddress,
leaseDeposit
);
}
function reclaimLeaseDeposit() public {
Lease storage lease = tenantLease[msg.sender];
require(lease.leaseFullyPaid, "Lease must be fully paid to take back lease deposit");
require(lease.leaseDepositUsd > 0, "Lease deposit has already been removed");
tenantAddress = msg.sender;
workingState = State.reclaimingLeaseDeposit;
fetchUsdRate();
}
function _reclaimLeaseDeposit() internal {
workingState = State.idle;
Lease storage lease = tenantLease[tenantAddress];
uint leaseDeposit = lease.leaseDepositUsd;
lease.leaseDepositUsd = 0;
tenantAddress.transfer(leaseDeposit.div(ETHUSD).mul(1e18));
emit leaseDepositReclaimed(
tenantAddress,
leaseDeposit
);
}
function withdrawFunds() public onlyLandlord {
require(leaseBalanceWei > 0, "Lease balance must be greater than 0");
uint transferAmount = leaseBalanceWei;
leaseBalanceWei = 0;
landlordAddress.transfer(transferAmount);
emit fundsWithdrawn(
transferAmount,
leaseBalanceWei
);
}
function getLease(address tenant) public view returns (
uint8,
uint8,
uint16,
uint16,
uint32,
uint64,
uint64,
bool,
bool,
bool) {
Lease memory lease = tenantLease[tenant];
return (
lease.numberOfMonths,
lease.monthsPaid,
lease.monthlyAmountUsd,
lease.leaseDepositUsd,
lease.leasePaymentWindowSeconds,
lease.leasePaymentWindowEnd,
lease.depositPaymentWindowEnd,
lease.leaseDepositPaid,
lease.leaseFullyPaid,
}
function getRate() public view returns (uint) {
return ETHUSD;
}
function getContractBalance() public view returns (uint) {
return uint(address(this).balance);
}
function() external payable {}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment