Smart Contract
MoxyData
A.123cb47fe122f6e3.MoxyData
1
2
3pub contract MoxyData {
4
5 pub struct DictionaryMapped {
6 pub var dictionary: {UFix64:AnyStruct}
7 pub var arrayMap: [UFix64]
8
9 pub fun setValue(_ value: AnyStruct) {
10 let timestamp = getCurrentBlock().timestamp
11 self.dictionary[timestamp] = value
12 self.arrayMap.append(timestamp)
13 }
14
15 pub fun valueNow():AnyStruct {
16 return self.valueFor(timestamp: getCurrentBlock().timestamp)
17
18 }
19
20 pub fun valueFor(timestamp: UFix64):AnyStruct {
21 if (self.arrayMap.length == 0 || timestamp < self.arrayMap[0]) {
22 // No values for that timestamp
23 return nil
24 }
25
26 if (timestamp >= self.arrayMap[self.arrayMap.length-1]) {
27 return self.dictionary[self.arrayMap[self.arrayMap.length-1]]!
28 }
29
30 //search
31 var i = 0
32 while (self.arrayMap.length < i && self.arrayMap[i] < timestamp) {
33 i = i + 1
34 }
35
36 if (i > self.arrayMap.length-1) {
37 i = self.arrayMap.length-1
38 }
39
40 return self.dictionary[self.arrayMap[i]]
41 }
42
43 init() {
44 self.dictionary = {}
45 self.arrayMap = []
46 }
47 }
48
49 /** Resource to store key: Timestamp, value: amount
50 * The amounts in dictionary accumulates from last amounts added
51 * so the changes must to be calculated.
52 */
53
54 pub resource OrderedDictionary {
55 pub var dictionary: {UFix64:UFix64}
56 pub var arrayMap: [UFix64]
57 pub var ages: {UFix64:UFix64}
58 pub var agesMap: [UFix64]
59
60 pub fun getDictionary(): {UFix64: UFix64} {
61 return self.dictionary
62 }
63
64 /**
65 Returns the value for the given timestamp. If the timestamp
66 is not found, it returns the most recent timestamp that is
67 less than the parameter received.
68 */
69 pub fun getValueOrMostRecentFor(timestamp: UFix64): UFix64 {
70 let time0000 = MoxyData.getTimestampTo0000(timestamp: timestamp)
71 if (self.dictionary[time0000] != nil) {
72 return self.dictionary[time0000]!
73 }
74
75 // For this day there are no registered balances, look for the
76 // last recorded balance or zero if there are no previous records
77 // per requested day
78 var index = -1
79 var hasActivity = false
80 for time in self.arrayMap {
81 if (time >= time0000 ) {
82 hasActivity = true
83 break
84 }
85 index = index + 1
86 }
87 if (index < 0) {
88 // No previous activity
89 return 0.0
90 }
91 return self.dictionary[self.arrayMap[index]]!
92 }
93
94 pub fun getValueFor(timestamp: UFix64): UFix64 {
95 let time0000 = MoxyData.getTimestampTo0000(timestamp: timestamp)
96 if (self.dictionary[time0000] == nil) {
97 return 0.0
98 }
99 return self.dictionary[time0000]!
100 }
101
102 pub fun getValueForToday(): UFix64 {
103 let balance = self.getValueOrMostRecentFor(timestamp: getCurrentBlock().timestamp)
104 if (balance == nil) {
105 return 0.0
106 }
107 return balance
108 }
109
110 pub fun getValueChangeForToday(): Fix64 {
111 return self.getValueChange(timestamp: getCurrentBlock().timestamp)
112 }
113
114 // Get the difference between the day (represented by timestamp) with the
115 // previous date (previous date could be several days ago, depending on activity)
116 pub fun getValueChange(timestamp: UFix64): Fix64 {
117 let time0000 = MoxyData.getTimestampTo0000(timestamp: timestamp)
118
119 if (self.dictionary.length < 1) {
120 // No records > no change
121 return 0.0
122 }
123 if (self.arrayMap[0] > time0000 ) {
124 // Date is previous to the first registered
125 return 0.0
126 }
127 var lastTimestamp = self.getLastKeyAdded()
128 if (time0000 > lastTimestamp!) {
129 // Date is over last timestamp
130 return 0.0
131 }
132
133 // Balance en la fecha consultada
134 var timestamp = self.dictionary[time0000]
135
136 if (timestamp == nil) {
137 // No records > no changes
138 return 0.0
139 }
140
141 // Look for last balance
142 if (self.arrayMap[0] == time0000 ) {
143 // No previous > change is balance total
144 return Fix64(timestamp!)
145 }
146
147 // There is a balance, we have to look for the previous balance to see
148 // what was the change
149 // search
150 var index = 0
151 for time in self.arrayMap {
152 if (time == time0000) {
153 break
154 }
155 index = index + 1
156 }
157 let indexBefore = index - 1
158 var timestampBefore = self.dictionary[self.arrayMap[indexBefore]]
159
160 return Fix64(timestamp!) - Fix64(timestampBefore!)
161 }
162
163 pub fun getValueChanges(): {UFix64:UFix64} {
164 return self.getValueChangesUpTo(timestamp: getCurrentBlock().timestamp)
165 }
166
167 pub fun getValueChangesUpTo(timestamp: UFix64): {UFix64:UFix64} {
168 let resu: {UFix64:UFix64} = {}
169 var amountBefore = 0.0
170 var timeBefore = 0.0
171 var remaining = 0.0
172
173 for time in self.arrayMap {
174 if (time > timestamp) {
175 // If timestamp
176 continue
177 }
178 if (self.dictionary[time]! > amountBefore ) {
179 let amount = self.dictionary[time]! - amountBefore
180
181 // Add to dictionary
182 resu[time] = amount
183 } else {
184 // Changes are negative
185 remaining = remaining + amountBefore - self.dictionary[time]!
186 }
187 if (remaining > 0.0 && resu[timeBefore] != nil) {
188 if (resu[timeBefore]! > remaining) {
189 resu[timeBefore] = resu[timeBefore]! - remaining
190 remaining = 0.0
191 } else {
192 let amnt = resu[timeBefore]!
193 resu.remove(key: timeBefore) ?? nil
194 remaining = remaining - amnt
195 }
196 }
197 amountBefore = self.dictionary[time]!
198 timeBefore = time
199 }
200 for time in resu.keys {
201 if (remaining == 0.0) {
202 break
203 }
204 if (resu[time]! > remaining) {
205 resu[time] = resu[time]! - remaining
206 remaining = 0.0
207 } else {
208 let amnt = resu[time]!
209 resu.remove(key: time) ?? nil
210 remaining = remaining - amnt
211 }
212 }
213
214 return resu
215 }
216
217 pub fun getLastKeyAdded(): UFix64? {
218 let pos = self.dictionary.length - 1
219 if (pos < 0) {
220 return nil
221 }
222 return self.arrayMap[pos]
223 }
224
225 pub fun getFirstKeyAdded(): UFix64? {
226 if (self.arrayMap.length == 0) {
227 return nil
228 }
229 return self.arrayMap[0]
230 }
231
232 pub fun getLastValue(): UFix64 {
233 let pos = self.dictionary.length - 1
234 if (pos < 0) {
235 return 0.0
236 }
237 return self.dictionary[self.arrayMap[pos]!]!
238 }
239
240 pub fun setAmountFor(timestamp: UFix64, amount: UFix64) {
241 let time0000 = MoxyData.getTimestampTo0000(timestamp: timestamp)
242 let lastTimestamp = self.getLastKeyAdded()
243
244 // Check if timestamp to add exists and that is greater than
245 // the last timestamp added, to keep order on arrayMap
246 if (lastTimestamp == nil || time0000 > lastTimestamp! || self.dictionary[time0000] == nil) {
247 // Assign last value as initial amount for required timestamp
248 self.dictionary[time0000] = self.getLastValue()
249 self.arrayMap.append(time0000)
250 }
251 self.addAge(timestamp: time0000, amount: amount)
252 self.dictionary[time0000] = self.dictionary[time0000]! + amount
253 }
254
255 pub fun addAge(timestamp: UFix64, amount: UFix64) {
256 if (self.ages[timestamp] == nil) {
257 self.ages[timestamp] = 0.0
258 self.agesMap.append(timestamp)
259 }
260 self.ages[timestamp] = self.ages[timestamp]! + amount
261 }
262
263 pub fun subtractOldestAge(amount: UFix64): {UFix64: UFix64} {
264 var amountRemaining = amount
265 let dict: {UFix64:UFix64} = {}
266
267 while(amountRemaining > 0.0 && self.agesMap.length > 0) {
268 // Always ask for index zero as is the oldest deposit
269 let timestamp = self.agesMap[0]
270 let balance = self.ages[timestamp]!
271 if (amountRemaining > balance ) {
272 //balance of the day is not enough for total withdraw
273 amountRemaining = amountRemaining - balance
274 //remove daily balance
275 self.ages.remove(key: timestamp)
276 self.agesMap.remove(at: 0)
277 dict[timestamp] = balance
278 } else {
279 //balance is enough to complete total withdraw
280 self.ages[timestamp] = self.ages[timestamp]! - amountRemaining
281 dict[timestamp] = amountRemaining
282 amountRemaining = 0.0
283 }
284 }
285 if (amountRemaining > 0.0) {
286 panic("Not enough amount to withdraw from dictionary.")
287 }
288 return dict
289 }
290
291
292 pub fun canUpdateTo(timestamp: UFix64): Bool {
293
294 let time0000 = MoxyData.getTimestampTo0000(timestamp: timestamp)
295 let lastTimestamp = self.getLastKeyAdded()
296
297 // Returns true if there are no registered timestamp yet or
298 // if the time to add is equal or greater than the las timestamp added.
299 return lastTimestamp == nil || time0000 >= lastTimestamp!
300 }
301
302 pub fun withdrawValueFromOldest(amount: UFix64): {UFix64: UFix64} {
303 let time0000 = MoxyData.getTimestampTo0000(timestamp: getCurrentBlock().timestamp)
304 let lastTimestamp = self.getLastKeyAdded()
305 let value = self.getLastValue()
306
307 if (value < amount) {
308 panic("Not enough amount to withdraw from dictionary.")
309 }
310
311 let dict = self.subtractOldestAge(amount: amount)
312 for time in dict.keys {
313 self.dictionary[time] = self.dictionary[time]! - dict[time]!
314 }
315
316 return dict
317 }
318
319 pub fun destroyWith(orderedDictionary: @OrderedDictionary) {
320 let dict = orderedDictionary.getDictionary()
321 for timestamp in dict.keys {
322 if (self.dictionary[timestamp] != nil) {
323 self.dictionary[timestamp] = self.dictionary[timestamp]! - dict[timestamp]!
324 }
325 }
326
327 destroy orderedDictionary
328 }
329
330
331 init() {
332 self.dictionary = {}
333 self.arrayMap = []
334 self.ages = {}
335 self.agesMap = []
336 }
337
338 }
339
340 pub resource interface OrderedDictionaryInfo {
341 pub fun getDictionary(): {UFix64: UFix64}
342 }
343
344 pub fun getTimestampTo0000(timestamp: UFix64): UFix64 {
345 let dayInSec = 86400.0
346 let days = timestamp / dayInSec
347 return UFix64(UInt64(days)) * dayInSec
348 }
349
350 pub fun createNewOrderedDictionary(): @OrderedDictionary {
351 return <-create OrderedDictionary()
352 }
353
354
355 init() {
356 }
357}
358
359