-
Notifications
You must be signed in to change notification settings - Fork 6
/
DAppStore.vy
261 lines (211 loc) · 9.86 KB
/
DAppStore.vy
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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
# import ApproveAndCallFallBack as ApproveAndCallFallBackInterface
# implements: ApproveAndCallFallBackInterface
struct Data:
developer: address
id: bytes32
dappBalance: int128
rate: int128
available: int128
votes_minted: int128
votes_cast: int128
effective_balance: int128
contract MiniMeTokenInterface:
# ERC20 methods
def totalSupply() -> uint256: constant
def balanceOf(_owner: address) -> uint256: constant
def allowance(_owner: address, _spender: address) -> uint256: constant
def transfer(_to: address, _value: uint256) -> bool: modifying
def transferFrom(_from: address, _to: address, _value: uint256) -> bool: modifying
def approve(_spender: address, _value: uint256) -> bool: modifying
# MiniMe methods
def approveAndCall(_spender: address, _amount: uint256, _extraData: bytes[132]) -> bool : modifying
def createCloneToken(_cloneTokenName: string[64], _cloneDecimalUnits: uint256, _cloneTokenSymbol: string[32], _snapshotBlock: uint256, _transfersEnabled: bool) -> address: modifying
def generateTokens(_owner: address, _amount: uint256) -> bool: modifying
def destroyTokens(_owner: address, _amount: uint256) -> bool: modifying
def enableTransfers(_transfersEnabled: bool): modifying
def claimTokens(_token: address): modifying
def balanceOfAt(_owner: address, _blockNumber: uint256) -> uint256 : constant
def totalSupplyAt(_blockNumber: address) -> uint256: constant
# Events
DAppCreated: event({_id: bytes32, _amount: int128})
Upvote: event({_id: bytes32, _amount: int128, _newEffectiveBalance: int128})
Downvote: event({_id: bytes32, _cost: int128, _newEffectiveBalance: int128})
Withdraw: event({_id: bytes32, _amount: int128, _newEffectiveBalance: int128})
TOTAL_SNT: constant(int128) = 3470483788
dapps: map(uint256, Data)
idToIdx: map(bytes32, uint256)
currMax: public(uint256)
total: public(int128)
ceiling: public(int128)
maxStake: public(int128)
SNT: public(MiniMeTokenInterface)
#Constant functions
@public
@constant
def upvoteEffect(_id: bytes32, _amount: int128) -> int128:
"""
@dev Used in UI to display effect on ranking of user's donation
@param _id bytes32 unique identifier
@param _amount Amount of SNT tokens to stake/"donate" to this DApp's ranking.
@return effect of donation on DApp's effective_balance
"""
dappIdx: uint256 = self.idToIdx[_id]
dapp: Data = self.dapps[dappIdx]
assert dapp.id == _id
# Should be accurate up to 10^-18
mBalance: int128 = dapp.dappBalance + _amount
# 0 < rate < 1, accurate to at least 10 places
mRate: int128 = 1 - (mBalance / self.maxStake)
# Should match accuracy of balance
mAvailable: int128 = mBalance * mRate
# > 1, should match accuracy of rate
mExponent: int128 = 1 / mRate
# Should match accuracy of rate
mVMinted: int128 = mAvailable ** mExponent
# Should match accuracy of balance
mEBalance: int128 = mBalance - ((mVMinted * mRate) * (mAvailable / mVMinted))
return (mEBalance - dapp.dappBalance)
@public
def downvoteCost(_id: bytes32) -> int128[3]:
"""
@dev For simplicity, users can only downvote by 1% at a time.
@param _id bytes32 unique identifier.
@return Array [balanceDownBy, votesRequired, cost]
"""
dappIdx: uint256 = self.idToIdx[_id]
dapp: Data = self.dapps[dappIdx]
assert dapp.id == _id
balanceDownBy: int128 = dapp.effective_balance / 100
votesRequired: int128 = (balanceDownBy * dapp.votes_minted * dapp.rate) / dapp.available
cost: int128 = (dapp.available / (dapp.votes_minted - (dapp.votes_cast + votesRequired))) * (votesRequired / 1 / 100)
return [balanceDownBy, votesRequired, cost]
#Constructor
@public
def __init__(_tokenAddr: address):
self.SNT = MiniMeTokenInterface(_tokenAddr)
self.total = TOTAL_SNT
self.ceiling = 40
self.maxStake = (self.total * self.ceiling) / 10000
#Private Functions
@private
def _createDapp(_from: address, _id: bytes32, _amount: int128):
"""
@dev private low level function for adding a dapp to the store
@param _from Address of the dapp's developer
@param _id Unique identifier for the dapp
@param _amount Amount of SNT tokens to be staked
"""
amount: uint256 = convert(_amount, uint256)
assert self.currMax < MAX_UINT256, "Reached maximum dapps limit for the DAppStore"
assert _amount > 0, "You must spend some SNT to submit a ranking in order to avoid spam"
assert _amount < self.maxStake, "You cannot stake more SNT than the ceiling dictates"
assert self.SNT.allowance(_from, self) >= convert(_amount,uint256), "Not enough SNT allowance"
assert self.SNT.transferFrom(_from, self, convert(_amount,uint256)), "Transfer failed"
self.idToIdx[_id] = self.currMax
newDapp: Data
newDapp.developer = _from
newDapp.id = _id
newDapp.dappBalance = _amount
newDapp.rate = 1 - (newDapp.dappBalance / self.maxStake)
newDapp.available = newDapp.dappBalance * newDapp.rate
exponent: int128 = 1 / newDapp.rate
newDapp.votes_minted = newDapp.available ** exponent
newDapp.votes_cast = 0
newDapp.effective_balance = _amount
self.dapps[self.currMax] = newDapp
self.currMax += 1
log.DAppCreated(_id, newDapp.effective_balance)
@private
def _upvote(_from: address, _id: bytes32, _amount: int128):
"""
@dev private low level function for upvoting a dapp by contributing SNT directly to a Dapp's balance
@param _from Address of the upvoter
@param _id Unique identifier for the dapp
@param _amount Amount of SNT tokens to stake/"donate" to this DApp's ranking
"""
assert _amount > 0, "You must send some SNT in order to upvote"
amount: uint256 = convert(_amount, uint256)
dappIdx: uint256 = self.idToIdx[_id]
dapp: Data = self.dapps[dappIdx]
assert dapp.id == _id, "Error fetching correct data"
assert dapp.dappBalance + _amount < self.maxStake, "You cannot stake more SNT than the ceiling dictates"
assert self.SNT.allowance(_from, self) >= convert(_amount,uint256), "Not enough SNT allowance"
assert self.SNT.transferFrom(_from, self, convert(_amount,uint256)), "Transfer failed"
dapp.dappBalance += _amount
dapp.rate = 1 - (dapp.dappBalance / self.maxStake)
dapp.available = dapp.dappBalance * dapp.rate
exponent: int128 = 1 / dapp.rate
dapp.votes_minted = dapp.available ** exponent
dapp.effective_balance = dapp.dappBalance - ((dapp.votes_cast * dapp.rate) * (dapp.available / dapp.votes_minted))
self.dapps[dappIdx] = dapp
log.Upvote(_id, _amount, dapp.effective_balance)
@private
def _downvote(_from: address, _id: bytes32):
"""
@dev private low level function for downvoting a dapp by contributing SNT directly to a Dapp's balance
@param _from Address of the downvoter
@param _id Unique identifier for the dapp
"""
dappIdx: uint256 = self.idToIdx[_id]
dapp: Data = self.dapps[dappIdx]
assert dapp.id == _id, "Error fetching correct data"
check: decimal = convert(dapp.votes_cast / dapp.votes_minted, decimal)
assert check < 0.99, "All valid votes have already been cast"
downvoteEffect: int128[3] = self.downvoteCost(_id)
assert self.SNT.allowance(_from, dapp.developer) >= convert(downvoteEffect[2], uint256), "Not enough SNT allowance"
assert self.SNT.transferFrom(_from, dapp.developer, convert(downvoteEffect[2], uint256)), "Transfer failed"
dapp.available -= downvoteEffect[2]
dapp.votes_cast += downvoteEffect[1]
dapp.effective_balance -= downvoteEffect[0]
self.dapps[dappIdx] = dapp
log.Downvote(_id, downvoteEffect[2], dapp.effective_balance)
# Public Functions
@public
def createDapp(_id: bytes32, _amount: int128):
"""
@dev Anyone can create a DApp (i.e an arb piece of data this contract happens to care about)
@param _id bytes32 unique identifier
@param _amount Amount of SNT tokens to stake on initial ranking
"""
self._createDapp(msg.sender, _id, _amount)
@public
def upvote(_id: bytes32, _amount: int128):
"""
@dev Sends SNT directly to the contract, not the developer. This gets added to the DApp's balance, no curve required
@param _id bytes32 unique identifier
@param _amount Amount of tokens to stake on DApp's ranking. Used for upvoting + staking more
"""
self._upvote(msg.sender, _id, _amount)
@public
def downvote(_id: bytes32):
"""
@dev Sends SNT directly to the developer and lowers the DApp's effective balance in the Store
@param _id bytes32 unique identifier.
"""
self._downvote(msg.sender, _id)
@public
def withdraw(_id: bytes32, _amount: int128):
"""
@dev Developers can withdraw an amount not more than what was available of the
SNT they originally staked minus what they have already received back in downvotes
@param _id bytes32 unique identifier
@param _amount Amount of tokens to withdraw from DApp's overall balance
"""
dappIdx: uint256 = self.idToIdx[_id]
dapp: Data = self.dapps[dappIdx]
assert dapp.id == _id, "Error fetching correct data"
assert msg.sender == dapp.developer, "Only the developer can withdraw SNT staked on this data"
assert _amount <= dapp.available, "You can only withdraw a percentage of the SNT staked, less what you have already received"
amount: uint256 = convert(_amount, uint256)
dapp.dappBalance -= _amount
dapp.rate = 1 - (dapp.dappBalance / self.maxStake)
dapp.available = dapp.dappBalance * dapp.rate
exponent: int128 = 1 / dapp.rate
dapp.votes_minted = dapp.available ** exponent
if (dapp.votes_cast > dapp.votes_minted):
dapp.votes_cast = dapp.votes_minted
dapp.effective_balance = dapp.dappBalance - ((dapp.votes_cast * dapp.rate) * (dapp.available / dapp.votes_minted))
self.dapps[dappIdx] = dapp
assert self.SNT.transferFrom(self, dapp.developer, convert(_amount,uint256)), "Transfer failed"
log.Withdraw(_id, _amount, dapp.effective_balance)
# Snipped: receiveApproval