-
Notifications
You must be signed in to change notification settings - Fork 6
/
HarbergerAds.sol
145 lines (122 loc) · 4.53 KB
/
HarbergerAds.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
pragma solidity ^0.4.24;
import 'zeppelin-solidity/contracts/ownership/Ownable.sol';
import 'zeppelin-solidity/contracts/token/ERC20/ERC20.sol';
contract HarbergerAds is Ownable {
// Per day tax rate
uint256 public taxNumerator;
uint256 public taxDenominator;
ERC20 public erc20;
struct Property {
address taxRecipient;
address owner;
uint256 price;
uint256 paidThru;
}
Property[] public properties;
constructor(
ERC20 erc20Address,
uint256 _taxNumerator,
uint256 _taxDenominator
) public {
taxNumerator = _taxNumerator;
taxDenominator = _taxDenominator;
erc20 = erc20Address;
}
function taxesDue(uint256 id) public view returns (uint256) {
Property storage p = properties[id];
return p.price * (now - p.paidThru) * taxNumerator
/ taxDenominator / 1 days;
}
function balanceOf(address user) public constant returns (uint256) {
uint256 balance = erc20.balanceOf(user);
uint256 allowance = erc20.allowance(user, this);
if (balance > allowance) {
return allowance;
} else {
return balance;
}
}
event Change(uint256 indexed id, address indexed to, address indexed from, uint256 price);
event ChangeRecipient(uint256 indexed id, address indexed to, address indexed from);
event TaxesPaid(uint256 indexed id, address owner, uint256 paid);
function addProperty(uint256 price) public {
Property memory p;
p.owner = msg.sender;
p.taxRecipient = msg.sender;
p.price = price;
uint256 id = properties.push(p) - 1;
emit Change(id, msg.sender, 0, price);
}
// Possibly foreclose on property[id]
function forecloseIfPossible(uint256 id) public {
Property storage p = properties[id];
// Owner must be broke and behind on taxes to foreclose
if (p.owner != p.taxRecipient && balanceOf(p.owner) == 0 && p.paidThru < now && p.price > 0) {
emit Change(id, 0, p.owner, 0);
delete(properties[id]);
}
}
// Collect taxes due from account.
// Return true if taxes fully paid, false otherwise
function collectTaxes(uint256 id) public returns (bool) {
Property storage p = properties[id];
if (p.owner == p.taxRecipient) {
p.paidThru = uint256(now);
return true;
}
uint256 balance = balanceOf(p.owner);
uint256 taxes = taxesDue(id);
if (taxes <= balance) {
p.paidThru = uint256(now);
if (taxes > 0) {
require(erc20.transferFrom(p.owner, p.taxRecipient, taxes), 'transferFrom failed');
}
emit TaxesPaid(id, p.owner, taxes);
return true;
} else {
// Adjust paidThru proportionally (overflow check unnecessary)
p.paidThru += uint256((now - p.paidThru) * balance / taxes);
// Collect entire balance for partially-paid taxes
if (balance > 0) {
require(erc20.transferFrom(p.owner, p.taxRecipient, balance), 'transferFrom failed');
}
emit TaxesPaid(id, p.owner, balance);
return false;
}
}
// Try to buy property for no more than 'max'
function buy(
uint256 id,
uint256 max,
uint256 price
)
public
{
Property storage p = properties[id];
// Collect taxes from property's owner and possibly foreclose on property[id].
collectTaxes(id);
// Foreclosure may change price and seller.
forecloseIfPossible(id);
address seller = p.owner;
if (seller != msg.sender) {
require(max >= p.price, "price is too high");
require(balanceOf(msg.sender) >= p.price,
"insufficient funds");
// Transfer purchase price
if (p.price > 0) {
require(erc20.transferFrom(p.owner, seller, p.price), 'transferFrom failed');
}
p.owner = msg.sender;
}
p.price = price;
emit Change(id, msg.sender, seller, price);
}
function propertyCount() public view returns (uint256) {
return properties.length;
}
function changeRecipient(uint256 id, address newRecipient) public {
require(msg.sender == properties[id].taxRecipient, "must be previous taxRecipient");
properties[id].taxRecipient = newRecipient;
emit ChangeRecipient(id, newRecipient, msg.sender);
}
}