tx · 3MTaKvpxojKSdb9aAjhC7S8dX9AN9jjMFhM3f4pXdSj6

3PKwLVb8jYk58oYbfXxs2dUjD3YEKeqGWrX:  -0.01400000 Waves

2021.12.14 15:18 [2898903] smart account 3PKwLVb8jYk58oYbfXxs2dUjD3YEKeqGWrX > SELF 0.00000000 Waves

{ "type": 13, "id": "3MTaKvpxojKSdb9aAjhC7S8dX9AN9jjMFhM3f4pXdSj6", "fee": 1400000, "feeAssetId": null, "timestamp": 1639484328803, "version": 2, "chainId": 87, "sender": "3PKwLVb8jYk58oYbfXxs2dUjD3YEKeqGWrX", "senderPublicKey": "27DK15MykfnsVojpifD1gbr8kRr8rpY5mw6EH4zL8R25", "proofs": [ "4cCX63ENYTFMeSNYGV3NMmsg5YvSm42AwfGGrYZDqiUqP7LQ5KwNHya8oc1gnxCCrmXhf9StBu98WtVnPPHUbcai" ], "script": "base64:AAIFAAAAAAAABqUIAhIECgIICBIGCgQICAEBEgMKAQgSABoaCgFBEhVnZXRDdXJyZW50TGV2ZWxCaWdJbnQaDwoBQhIKa0R1Y2tMZXZlbBoGCgFDEgFzGgwKAUQSByRtYXRjaDEaEgoBRRINZ2V0Q3VycmVudExhcBoYCgFGEhNnZXRMYXN0RmVkVGltZXN0YW1wGhUKAUcSEGxhc3RGZWRUaW1lc3RhbXAaHgoBSBIZY2FsY3VsYXRlTmV3RHVja2xpbmdMZXZlbBoSCgFJEg1wYXltZW50QW1vdW50Gg4KAUoSCWN1cnJlbnRUcxocCgFLEhdsYXN0RmVkVGltZXN0YW1wQ2hlY2tlZBoMCgFMEgdmZWREaWZmGgwKAU0SB3BlbmFsdHkaCwoBThIGZ3Jvd3RoGhEKAU8SDGN1cnJlbnRMZXZlbBoNCgFQEghuZXdMZXZlbBoLCgFREgZyZXN1bHQaFAoBUhIPZ2V0QmFja2VuZFByb29mGhIKAVMSDW1heEZlZWRBbW91bnQaDgoBVBIJdXNlck5vbmNlGgYKAVUSAWkaDAoBVhIHdHhJZFN0choKCgFXEgVhc3NldBoMCgFYEgdhc3NldElkGhUKAVkSEGJhY2tlbmRTaWduYXR1cmUaEgoBWhINYWRkcmVzc1N0cmluZxoVCgFhEhBpbmN1YmF0b3JBZGRyZXNzGgsKAmFBEgUkYWNjMBoLCgJhQhIFJGYwXzEaCAoCYUMSAiRhGggKAmFEEgIkaRoLCgJhRRIFJGYwXzIaCgoCYUYSBHR4SWQaGgoCYUcSFGxhc3RJc3N1ZWREdWNrbGluZ1RzGhEKAmFIEgtmaXZlTWluSW5NcxoJCgJhSRIDcG10GgoKAmFKEgRjYWxsGhQKAmFLEg5rRHVja2xpbmdHcm93bhoICgJhTBICdHgaDAoCYU0SBnZlcmlmeRoSCgJhYRIMYmFja2VuZFByb29mGhMKAmFiEg1rQWRkcmVzc05vbmNlGhIKAmFjEgxjdXJyZW50Tm9uY2UaFAoCYWQSDnJlYWxEdWNrbGluZ0lkGhUKAmFlEg9kdWNrbGluZ0Fzc2V0SWQaCAoCYWYSAmlkGgcKAmFnEgF2GhUKAmFoEg9kdWNrbGluZ0lkQ2hlY2saFgoCYWkSEGR1Y2tsaW5nSWRTdHJpbmcaGAoCYWoSEmtEdWNrbGluZ0xhc3RGZWRUcxoPCgJhaxIJbGFzdEZlZFRzGhQKAmFsEg5jdXJyZW50UGF5bWVudBoPCgJhbRIJa05ld0xldmVsGhAKAmFuEgprVG90YWxGZWVkGg8KAmFvEgl0b3RhbEZlZWQaEgoCYXASDGtGZWVkVHhTdGF0cxoaCgJhcRIUa0FkZHJlc3NGZWRUaW1lc3RhbXAaHgoCYXISGGtEdWNrbGluZ0ZlZExhcFRpbWVzdGFtcBoWCgJhcxIQY2FsY3VsYXRlUmVzdWx0cxoRCgJhdBILZHVja2xpbmdJZHMaFQoCYXUSD2R1Y2tsaW5nSWRzTGlzdBoOCgJhdhIIaGFuZGxlSWQaCQoCYXcSA2FjYxoTCgJheBINc3RhcnRpbmdMZXZlbBoICgJheRICJGwaCAoCYXoSAiRzGhIKAWISDWJhY2tlbmRQdWJLZXkaEwoBYxIOU1RBUlRUSU1FU1RBTVAaDgoBZBIJTEFQTEVOR1RIGg8KAWUSCkVHR0FTU0VUSUQaEgoBZhINRFVDS0xJTkdQUklDRRobCgFnEhZwZXJjZW50R3Jvd3RoUHJlY2lzaW9uGhoKAWgSFWV4aXN0aW5nRHVja1ByZWNpc2lvbhobCgFpEhZLR2xvYmFsSXNzdWVkVGltZXN0YW1wGgwKAWoSB01TSU5EQVkaEQoBaxIMa2V5VG90YWxGZWVkGg8KAWwSCmR1Y2tsaW5nSWQaHwoBbRIaa2V5RHVja2xpbmdGZWRMYXBUaW1lc3RhbXAaCAoBbhIDbGFwGhsKAW8SFmtleUFkZHJlc3NGZWRUaW1lc3RhbXAaDAoBcBIHYWRkcmVzcxoUCgFxEg9rZXlBZGRyZXNzTm9uY2UaIAoBchIba2V5RHVja2xpbmdGZWRMYXN0VGltZXN0YW1wGhUKAXMSEGtleUR1Y2tsaW5nTGV2ZWwaFQoBdBIQa2V5RHVja2xpbmdHcm93bhoSCgF1Eg10cnlHZXRJbnRlZ2VyGggKAXYSA2tleRoICgF3EgN2YWwaDAoBeBIHJG1hdGNoMBoGCgF5EgFiGgwKAXoSB2dldEJvb2wAAAAYAAAAAAFhAQAAABoBV4y1SJ9+nhXVGJeOG3f7CX9nYtrhbWMlSQAAAAABYgEAAAAgn+72lLfgCUvbFbUkOM8Gru0gv8TqG55wbGJJdPJs4hwAAAAA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", "height": 2898903, "applicationStatus": "succeeded", "spentComplexity": 0 } View: original | compacted Prev: 2JxanPcVehFRgfXpuZhXaUzNsPBR78MHyHRknBN9NtLL Next: 6u5GXvZphMoap8Xa9ts3ceiQ889CFN6sDkmbsM2icWSD Diff:
OldNewDifferences
11 {-# STDLIB_VERSION 5 #-}
22 {-# SCRIPT_TYPE ACCOUNT #-}
33 {-# CONTENT_TYPE DAPP #-}
4-let rebirthAddress = base58'3PKL9tfUmNciXvbSA3zpXr6PnDNnhn936ba'
4+let incubatorAddress = base58'3PEktVux2RhchSN63DsDo4b4mz4QqzKSeDv'
55
6-let backendPubKey = base58'BnKJTqkWD69SotKhJXdKu76NRtkdQJEpThqGGeFwF9EL'
6+let backendPubKey = base58'BmKAXRQy7jZm44fa1YxNQDTcAwNSb74EpQwP5CT7UHdV'
77
88 let STARTTIMESTAMP = 1632474000000
99
1010 let LAPLENGTH = 21600000
1111
12-let EGGASSETID = base58'3v7zGkeHS6KrsvmTRzEzvCxm5cdzkCtM7z5cM6efcjCB'
12+let EGGASSETID = base58'C1iWsKGqLwjHUndiQ7iXpdmPum9PeCDFfyXBdJJosDRS'
1313
1414 let DUCKLINGPRICE = 80000000
1515
1717
1818 let existingDuckPrecision = 100000000000000
1919
20+let KGlobalIssuedTimestamp = "global_issued_timestamp"
21+
22+let MSINDAY = 86400000
23+
2024 func keyTotalFeed (ducklingId) = (("duckling_" + ducklingId) + "_feed")
2125
2226
23-func keyDucklingFedLap (ducklingId) = (("duckling_" + ducklingId) + "_fedLap")
27+func keyDucklingFedLapTimestamp (ducklingId,lap) = (((("duckling_" + ducklingId) + "_lap_") + toString(lap)) + "_fedTs")
28+
29+
30+func keyAddressFedTimestamp (address,lap) = (((("address_" + address) + "_lap_") + toString(lap)) + "_fedTs")
31+
32+
33+func keyAddressNonce (address) = (("address_" + address) + "_nonce")
34+
35+
36+func keyDucklingFedLastTimestamp (ducklingId) = (("duckling_" + ducklingId) + "_fedLastTs")
2437
2538
2639 func keyDucklingLevel (ducklingId) = (("duckling_" + ducklingId) + "_level")
2740
2841
29-func keyFeedForLap (address,lap) = (((("address_" + address) + "_lap_") + toString(lap)) + "_feed")
42+func keyDucklingGrown (ducklingId) = (("duckling_" + ducklingId) + "_grown")
3043
3144
3245 func tryGetInteger (key) = {
4457 case b: Boolean =>
4558 b
4659 case _ =>
47- unit
60+ false
4861 }
4962
5063
6780 func getCurrentLap () = (((lastBlock.timestamp - STARTTIMESTAMP) / LAPLENGTH) + 1)
6881
6982
70-func calculateNewDucklingLevel (ducklingId,paymentAmount) = {
71- let kFedLap = keyDucklingFedLap(ducklingId)
72- let lastFedLapNumber = tryGetInteger(kFedLap)
73- let currentlap = getCurrentLap()
74- let lapFedDiff = (currentlap - (lastFedLapNumber - 1))
75- let penalty = if (if ((lastFedLapNumber == 0))
76- then true
77- else (4 >= lapFedDiff))
78- then toBigInt(0)
79- else (toBigInt((lapFedDiff / 4)) * toBigInt(existingDuckPrecision))
80- let growth = fraction(toBigInt(paymentAmount), toBigInt(percentGrowthPrecision), toBigInt(DUCKLINGPRICE))
81- let newLevel = ((getCurrentLevelBigInt(ducklingId) - penalty) + growth)
82- if ((toBigInt(0) > newLevel))
83- then toString(growth)
84- else toString(newLevel)
83+func getLastFedTimestamp (ducklingId) = {
84+ let lastFedTimestamp = tryGetInteger(keyDucklingFedLastTimestamp(ducklingId))
85+ if ((lastFedTimestamp > 0))
86+ then lastFedTimestamp
87+ else lastBlock.timestamp
8588 }
8689
8790
88-func getFeedByLap (address,lap) = {
89- let kFeedForLap = keyFeedForLap(address, lap)
90- tryGetInteger(kFeedForLap)
91+func calculateNewDucklingLevel (ducklingId,paymentAmount) = {
92+ let currentTs = lastBlock.timestamp
93+ let lastFedTimestampChecked = getLastFedTimestamp(ducklingId)
94+ let fedDiff = (currentTs - lastFedTimestampChecked)
95+ let penalty = if (if ((fedDiff == 0))
96+ then true
97+ else ((MSINDAY * 2) >= fedDiff))
98+ then toBigInt(0)
99+ else (toBigInt((fedDiff / MSINDAY)) * toBigInt((existingDuckPrecision / 10)))
100+ let growth = fraction(toBigInt(paymentAmount), toBigInt(percentGrowthPrecision), toBigInt(DUCKLINGPRICE))
101+ let currentLevel = getCurrentLevelBigInt(ducklingId)
102+ let newLevel = ((currentLevel - penalty) + growth)
103+ let result = if ((toBigInt(0) > newLevel))
104+ then toString(growth)
105+ else toString(newLevel)
106+ $Tuple2(result, [("currentLevel=" + toString(currentLevel)), ("newLevel=" + toString(newLevel)), ("growth=" + toString(growth)), ("penalty=" + toString(penalty)), ("lastFedTs=" + toString(lastFedTimestampChecked)), ("fedDiff=" + toString(fedDiff))])
91107 }
92108
93109
94-func stringListToIntList (list) = {
95- func parser (acc,val) = (acc ++ [value(parseInt(val))])
96-
97- let $l = list
98- let $s = size($l)
99- let $acc0 = nil
100- func 1 ($a,$i) = if (($i >= $s))
101- then $a
102- else parser($a, $l[$i])
103-
104- func 2 ($a,$i) = if (($i >= $s))
105- then $a
106- else throw("List size exceeds 10")
107-
108- 2(1(1(1(1(1(1(1(1(1(1($acc0, 0), 1), 2), 3), 4), 5), 6), 7), 8), 9), 10)
109- }
110-
111-
112-func getMaxFeedAmount (laps,rewards,address) = {
113- let currentLap = getCurrentLap()
114- if (if (if (if ((laps[3] != (currentLap - 1)))
115- then true
116- else (laps[2] != (currentLap - 2)))
117- then true
118- else (laps[1] != (currentLap - 3)))
119- then true
120- else (laps[0] != (currentLap - 4)))
121- then throw("Outdated data for laps")
122- else {
123- func getFeedForLap (acc,lap) = (acc :+ getFeedByLap(address, lap))
124-
125- func sum (acc,reward) = (acc + reward)
126-
127- let feedLastLaps = {
128- let $l = laps
129- let $s = size($l)
130- let $acc0 = nil
131- func 1 ($a,$i) = if (($i >= $s))
132- then $a
133- else getFeedForLap($a, $l[$i])
134-
135- func 2 ($a,$i) = if (($i >= $s))
136- then $a
137- else throw("List size exceeds 4")
138-
139- 2(1(1(1(1($acc0, 0), 1), 2), 3), 4)
140- }
141- let maxFeed = max(feedLastLaps)
142- let totalReward = {
143- let $l = rewards
144- let $s = size($l)
145- let $acc0 = 0
146- func 1 ($a,$i) = if (($i >= $s))
147- then $a
148- else sum($a, $l[$i])
149-
150- func 2 ($a,$i) = if (($i >= $s))
151- then $a
152- else throw("List size exceeds 4")
153-
154- 2(1(1(1(1($acc0, 0), 1), 2), 3), 4)
155- }
156- (totalReward - maxFeed)
157- }
158- }
159-
160-
161-func getBackendProof (laps,rewards,address) = {
162- func IntListToByteVector (acc,item) = (acc + item)
163-
164- let lapsStringPart = {
165- let $l = laps
166- let $s = size($l)
167- let $acc0 = ""
168- func 1 ($a,$i) = if (($i >= $s))
169- then $a
170- else IntListToByteVector($a, $l[$i])
171-
172- func 2 ($a,$i) = if (($i >= $s))
173- then $a
174- else throw("List size exceeds 10")
175-
176- 2(1(1(1(1(1(1(1(1(1(1($acc0, 0), 1), 2), 3), 4), 5), 6), 7), 8), 9), 10)
177- }
178- let rewardsStringPart = {
179- let $l = rewards
180- let $s = size($l)
181- let $acc0 = ""
182- func 1 ($a,$i) = if (($i >= $s))
183- then $a
184- else IntListToByteVector($a, $l[$i])
185-
186- func 2 ($a,$i) = if (($i >= $s))
187- then $a
188- else throw("List size exceeds 10")
189-
190- 2(1(1(1(1(1(1(1(1(1(1($acc0, 0), 1), 2), 3), 4), 5), 6), 7), 8), 9), 10)
191- }
192- ((lapsStringPart + rewardsStringPart) + address)
193- }
110+func getBackendProof (maxFeedAmount,userNonce,address) = makeString([toString(maxFeedAmount), toString(userNonce), address], ";")
194111
195112
196113 @Callable(i)
203120
204121
205122 @Callable(i)
206-func feedDuckling (ducklingId,backendSignature,lapsList,rewardsList) = {
123+func feedDuckling (ducklingId,backendSignature,maxFeedAmount,userNonce) = {
207124 let addressString = toString(i.caller)
208- let laps = value(split(lapsList, ","))
209- let rewards = value(split(rewardsList, ","))
210- let backendProof = getBackendProof(laps, rewards, addressString)
125+ let backendProof = getBackendProof(maxFeedAmount, userNonce, addressString)
126+ let kAddressNonce = keyAddressNonce(addressString)
127+ let currentNonce = tryGetInteger(kAddressNonce)
211128 let realDucklingId = if (!(sigVerify_8Kb(toBytes(backendProof), fromBase58String(backendSignature), backendPubKey)))
212129 then throw("Invalid proof from backend")
213130 else if (if ((size(i.payments) != 1))
214131 then true
215132 else (value(i.payments[0]).assetId != EGGASSETID))
216133 then throw("Bad payment attached (asset[s] or amount)")
217- else if ((ducklingId == ""))
218- then {
219- let ducklingAssetId = invoke(this, "issueFreeDuckling", [toString(i.originCaller), toBase58String(i.transactionId)], nil)
220- if ((ducklingAssetId == ducklingAssetId))
134+ else if (getBool(keyDucklingGrown(ducklingId)))
135+ then throw("Duckling is already grown")
136+ else if ((userNonce != (currentNonce + 1)))
137+ then throw(((("User Nonce should be " + toString(currentNonce)) + " + 1, while received ") + toString(userNonce)))
138+ else if ((ducklingId == ""))
221139 then {
222- let id = match ducklingAssetId {
223- case v: String =>
224- v
225- case _ =>
226- throw("Can't generate NFT")
140+ let ducklingAssetId = invoke(this, "issueFreeDuckling", [toString(i.originCaller), toBase58String(i.transactionId)], nil)
141+ if ((ducklingAssetId == ducklingAssetId))
142+ then {
143+ let id = match ducklingAssetId {
144+ case v: String =>
145+ v
146+ case _ =>
147+ throw("Can't generate NFT")
148+ }
149+ id
150+ }
151+ else throw("Strict value is not equal to itself.")
227152 }
228- id
153+ else {
154+ let ducklingIdCheck = value(assetInfo(fromBase58String(ducklingId)))
155+ if ((assetBalance(i.caller, ducklingIdCheck.id) != 1))
156+ then throw("You're not the owner of the duckling")
157+ else if ((ducklingIdCheck.issuer != this))
158+ then throw("Cant find duckling with such id")
159+ else {
160+ let ducklingIdString = toBase58String(ducklingIdCheck.id)
161+ ducklingIdString
162+ }
229163 }
230- else throw("Strict value is not equal to itself.")
231- }
232- else {
233- let ducklingIdCheck = value(assetInfo(fromBase58String(ducklingId)))
234- if ((assetBalance(i.caller, ducklingIdCheck.id) != 1))
235- then throw("You're not the owner of the duckling")
236- else if ((ducklingIdCheck.issuer != this))
237- then throw("Cant find duckling with such id")
238- else {
239- let ducklingIdString = toBase58String(ducklingIdCheck.id)
240- ducklingIdString
241- }
242- }
243- let intLaps = stringListToIntList(laps)
244- let intRewards = stringListToIntList(rewards)
245- let maxFeedAmount = getMaxFeedAmount(intLaps, intRewards, toString(i.caller))
246- let kDucklingFedLap = keyDucklingFedLap(realDucklingId)
247- let lastFedLap = tryGetInteger(kDucklingFedLap)
164+ let kDucklingLastFedTs = keyDucklingFedLastTimestamp(ducklingId)
165+ let lastFedTs = getLastFedTimestamp(realDucklingId)
248166 let currentPayment = value(i.payments[0]).amount
249167 let kNewLevel = keyDucklingLevel(realDucklingId)
250168 let kTotalFeed = keyTotalFeed(realDucklingId)
251169 let totalFeed = tryGetInteger(kTotalFeed)
252- let feedTxStats = ((("duckling_" + realDucklingId) + "_stat_") + toString(lastBlock.timestamp))
170+ let kFeedTxStats = ((("duckling_" + realDucklingId) + "_stat_") + toString(lastBlock.timestamp))
171+ let kAddressFedTimestamp = keyAddressFedTimestamp(addressString, getCurrentLap())
172+ let kDucklingFedLapTimestamp = keyDucklingFedLapTimestamp(realDucklingId, getCurrentLap())
253173 if ((currentPayment > maxFeedAmount))
254174 then throw(("Cannot feed duckling for such amount, max feed amount is: " + toString(maxFeedAmount)))
255- else [IntegerEntry(kDucklingFedLap, getCurrentLap()), IntegerEntry(keyFeedForLap(addressString, intLaps[0]), (getFeedByLap(addressString, intLaps[0]) + currentPayment)), IntegerEntry(keyFeedForLap(addressString, intLaps[1]), (getFeedByLap(addressString, intLaps[1]) + currentPayment)), IntegerEntry(keyFeedForLap(addressString, intLaps[2]), (getFeedByLap(addressString, intLaps[2]) + currentPayment)), IntegerEntry(keyFeedForLap(addressString, intLaps[3]), (getFeedByLap(addressString, intLaps[3]) + currentPayment)), IntegerEntry(kTotalFeed, (totalFeed + currentPayment)), IntegerEntry(feedTxStats, currentPayment), StringEntry(kNewLevel, calculateNewDucklingLevel(realDucklingId, currentPayment))]
175+ else {
176+ let calculateResults = calculateNewDucklingLevel(realDucklingId, currentPayment)
177+[IntegerEntry(kAddressNonce, (currentNonce + 1)), IntegerEntry(kDucklingLastFedTs, lastBlock.timestamp), IntegerEntry(kTotalFeed, (totalFeed + currentPayment)), IntegerEntry(kFeedTxStats, currentPayment), IntegerEntry(kAddressFedTimestamp, currentPayment), IntegerEntry(kDucklingFedLapTimestamp, currentPayment), StringEntry(kNewLevel, calculateResults._1), StringEntry((kFeedTxStats + "_debug"), makeString(calculateResults._2, ";"))]
178+ }
256179 }
257180
258181
279202 let $l = ducklingIdsList
280203 let $s = size($l)
281204 let $acc0 = nil
282- func 1 ($a,$i) = if (($i >= $s))
205+ func $f0_1 ($a,$i) = if (($i >= $s))
283206 then $a
284207 else handleId($a, $l[$i])
285208
286- func 2 ($a,$i) = if (($i >= $s))
209+ func $f0_2 ($a,$i) = if (($i >= $s))
287210 then $a
288211 else throw("List size exceeds 20")
289212
290- 2(1(1(1(1(1(1(1(1(1(1(1(1(1(1(1(1(1(1(1(1($acc0, 0), 1), 2), 3), 4), 5), 6), 7), 8), 9), 10), 11), 12), 13), 14), 15), 16), 17), 18), 19), 20)
213+ $f0_2($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($acc0, 0), 1), 2), 3), 4), 5), 6), 7), 8), 9), 10), 11), 12), 13), 14), 15), 16), 17), 18), 19), 20)
291214 }
292215
293216
294217
295218 @Callable(i)
296-func turnDucklingIntoDuck () = throw("Not implemented yet")
219+func turnDucklingIntoDuck () = {
220+ let address = toString(i.caller)
221+ let txId = toBase58String(i.transactionId)
222+ let lastIssuedDucklingTs = tryGetInteger(KGlobalIssuedTimestamp)
223+ let fiveMinInMs = ((5 * 60) * 1000)
224+ if (if ((lastIssuedDucklingTs > 0))
225+ then (fiveMinInMs > (lastBlock.timestamp - lastIssuedDucklingTs))
226+ else false)
227+ then throw(("Can issue ducklings only once per 5 minutes, please wait for " + toString((fiveMinInMs - (lastBlock.timestamp - lastIssuedDucklingTs)))))
228+ else if ((size(i.payments) != 1))
229+ then throw("Bad payment attached (asset[s] or amount)")
230+ else {
231+ let pmt = value(assetInfo(value(value(i.payments[0]).assetId)))
232+ if ((pmt.issuer != this))
233+ then throw("Can use only ducklings from this dApp")
234+ else {
235+ let call = invoke(Address(incubatorAddress), "issueFreeDuck", [address, txId], nil)
236+ if ((call == call))
237+ then {
238+ let kDucklingGrown = keyDucklingGrown(toBase58String(pmt.id))
239+[BooleanEntry(kDucklingGrown, true), IntegerEntry(KGlobalIssuedTimestamp, lastBlock.timestamp)]
240+ }
241+ else throw("Strict value is not equal to itself.")
242+ }
243+ }
244+ }
297245
298246
299247 @Verifier(tx)
Full:
OldNewDifferences
11 {-# STDLIB_VERSION 5 #-}
22 {-# SCRIPT_TYPE ACCOUNT #-}
33 {-# CONTENT_TYPE DAPP #-}
4-let rebirthAddress = base58'3PKL9tfUmNciXvbSA3zpXr6PnDNnhn936ba'
4+let incubatorAddress = base58'3PEktVux2RhchSN63DsDo4b4mz4QqzKSeDv'
55
6-let backendPubKey = base58'BnKJTqkWD69SotKhJXdKu76NRtkdQJEpThqGGeFwF9EL'
6+let backendPubKey = base58'BmKAXRQy7jZm44fa1YxNQDTcAwNSb74EpQwP5CT7UHdV'
77
88 let STARTTIMESTAMP = 1632474000000
99
1010 let LAPLENGTH = 21600000
1111
12-let EGGASSETID = base58'3v7zGkeHS6KrsvmTRzEzvCxm5cdzkCtM7z5cM6efcjCB'
12+let EGGASSETID = base58'C1iWsKGqLwjHUndiQ7iXpdmPum9PeCDFfyXBdJJosDRS'
1313
1414 let DUCKLINGPRICE = 80000000
1515
1616 let percentGrowthPrecision = 10000000000000000
1717
1818 let existingDuckPrecision = 100000000000000
1919
20+let KGlobalIssuedTimestamp = "global_issued_timestamp"
21+
22+let MSINDAY = 86400000
23+
2024 func keyTotalFeed (ducklingId) = (("duckling_" + ducklingId) + "_feed")
2125
2226
23-func keyDucklingFedLap (ducklingId) = (("duckling_" + ducklingId) + "_fedLap")
27+func keyDucklingFedLapTimestamp (ducklingId,lap) = (((("duckling_" + ducklingId) + "_lap_") + toString(lap)) + "_fedTs")
28+
29+
30+func keyAddressFedTimestamp (address,lap) = (((("address_" + address) + "_lap_") + toString(lap)) + "_fedTs")
31+
32+
33+func keyAddressNonce (address) = (("address_" + address) + "_nonce")
34+
35+
36+func keyDucklingFedLastTimestamp (ducklingId) = (("duckling_" + ducklingId) + "_fedLastTs")
2437
2538
2639 func keyDucklingLevel (ducklingId) = (("duckling_" + ducklingId) + "_level")
2740
2841
29-func keyFeedForLap (address,lap) = (((("address_" + address) + "_lap_") + toString(lap)) + "_feed")
42+func keyDucklingGrown (ducklingId) = (("duckling_" + ducklingId) + "_grown")
3043
3144
3245 func tryGetInteger (key) = {
3346 let val = match getInteger(this, key) {
3447 case b: Int =>
3548 b
3649 case _ =>
3750 0
3851 }
3952 val
4053 }
4154
4255
4356 func getBool (key) = match getBoolean(this, key) {
4457 case b: Boolean =>
4558 b
4659 case _ =>
47- unit
60+ false
4861 }
4962
5063
5164 func getCurrentLevelBigInt (ducklingId) = {
5265 let kDuckLevel = keyDucklingLevel(ducklingId)
5366 match getString(kDuckLevel) {
5467 case s: String =>
5568 parseBigIntValue(s)
5669 case _ =>
5770 match getBoolean((("duckling_" + ducklingId) + "_issuedByFeedCall")) {
5871 case b: Boolean =>
5972 toBigInt(0)
6073 case _ =>
6174 toBigInt((20 * existingDuckPrecision))
6275 }
6376 }
6477 }
6578
6679
6780 func getCurrentLap () = (((lastBlock.timestamp - STARTTIMESTAMP) / LAPLENGTH) + 1)
6881
6982
70-func calculateNewDucklingLevel (ducklingId,paymentAmount) = {
71- let kFedLap = keyDucklingFedLap(ducklingId)
72- let lastFedLapNumber = tryGetInteger(kFedLap)
73- let currentlap = getCurrentLap()
74- let lapFedDiff = (currentlap - (lastFedLapNumber - 1))
75- let penalty = if (if ((lastFedLapNumber == 0))
76- then true
77- else (4 >= lapFedDiff))
78- then toBigInt(0)
79- else (toBigInt((lapFedDiff / 4)) * toBigInt(existingDuckPrecision))
80- let growth = fraction(toBigInt(paymentAmount), toBigInt(percentGrowthPrecision), toBigInt(DUCKLINGPRICE))
81- let newLevel = ((getCurrentLevelBigInt(ducklingId) - penalty) + growth)
82- if ((toBigInt(0) > newLevel))
83- then toString(growth)
84- else toString(newLevel)
83+func getLastFedTimestamp (ducklingId) = {
84+ let lastFedTimestamp = tryGetInteger(keyDucklingFedLastTimestamp(ducklingId))
85+ if ((lastFedTimestamp > 0))
86+ then lastFedTimestamp
87+ else lastBlock.timestamp
8588 }
8689
8790
88-func getFeedByLap (address,lap) = {
89- let kFeedForLap = keyFeedForLap(address, lap)
90- tryGetInteger(kFeedForLap)
91+func calculateNewDucklingLevel (ducklingId,paymentAmount) = {
92+ let currentTs = lastBlock.timestamp
93+ let lastFedTimestampChecked = getLastFedTimestamp(ducklingId)
94+ let fedDiff = (currentTs - lastFedTimestampChecked)
95+ let penalty = if (if ((fedDiff == 0))
96+ then true
97+ else ((MSINDAY * 2) >= fedDiff))
98+ then toBigInt(0)
99+ else (toBigInt((fedDiff / MSINDAY)) * toBigInt((existingDuckPrecision / 10)))
100+ let growth = fraction(toBigInt(paymentAmount), toBigInt(percentGrowthPrecision), toBigInt(DUCKLINGPRICE))
101+ let currentLevel = getCurrentLevelBigInt(ducklingId)
102+ let newLevel = ((currentLevel - penalty) + growth)
103+ let result = if ((toBigInt(0) > newLevel))
104+ then toString(growth)
105+ else toString(newLevel)
106+ $Tuple2(result, [("currentLevel=" + toString(currentLevel)), ("newLevel=" + toString(newLevel)), ("growth=" + toString(growth)), ("penalty=" + toString(penalty)), ("lastFedTs=" + toString(lastFedTimestampChecked)), ("fedDiff=" + toString(fedDiff))])
91107 }
92108
93109
94-func stringListToIntList (list) = {
95- func parser (acc,val) = (acc ++ [value(parseInt(val))])
96-
97- let $l = list
98- let $s = size($l)
99- let $acc0 = nil
100- func 1 ($a,$i) = if (($i >= $s))
101- then $a
102- else parser($a, $l[$i])
103-
104- func 2 ($a,$i) = if (($i >= $s))
105- then $a
106- else throw("List size exceeds 10")
107-
108- 2(1(1(1(1(1(1(1(1(1(1($acc0, 0), 1), 2), 3), 4), 5), 6), 7), 8), 9), 10)
109- }
110-
111-
112-func getMaxFeedAmount (laps,rewards,address) = {
113- let currentLap = getCurrentLap()
114- if (if (if (if ((laps[3] != (currentLap - 1)))
115- then true
116- else (laps[2] != (currentLap - 2)))
117- then true
118- else (laps[1] != (currentLap - 3)))
119- then true
120- else (laps[0] != (currentLap - 4)))
121- then throw("Outdated data for laps")
122- else {
123- func getFeedForLap (acc,lap) = (acc :+ getFeedByLap(address, lap))
124-
125- func sum (acc,reward) = (acc + reward)
126-
127- let feedLastLaps = {
128- let $l = laps
129- let $s = size($l)
130- let $acc0 = nil
131- func 1 ($a,$i) = if (($i >= $s))
132- then $a
133- else getFeedForLap($a, $l[$i])
134-
135- func 2 ($a,$i) = if (($i >= $s))
136- then $a
137- else throw("List size exceeds 4")
138-
139- 2(1(1(1(1($acc0, 0), 1), 2), 3), 4)
140- }
141- let maxFeed = max(feedLastLaps)
142- let totalReward = {
143- let $l = rewards
144- let $s = size($l)
145- let $acc0 = 0
146- func 1 ($a,$i) = if (($i >= $s))
147- then $a
148- else sum($a, $l[$i])
149-
150- func 2 ($a,$i) = if (($i >= $s))
151- then $a
152- else throw("List size exceeds 4")
153-
154- 2(1(1(1(1($acc0, 0), 1), 2), 3), 4)
155- }
156- (totalReward - maxFeed)
157- }
158- }
159-
160-
161-func getBackendProof (laps,rewards,address) = {
162- func IntListToByteVector (acc,item) = (acc + item)
163-
164- let lapsStringPart = {
165- let $l = laps
166- let $s = size($l)
167- let $acc0 = ""
168- func 1 ($a,$i) = if (($i >= $s))
169- then $a
170- else IntListToByteVector($a, $l[$i])
171-
172- func 2 ($a,$i) = if (($i >= $s))
173- then $a
174- else throw("List size exceeds 10")
175-
176- 2(1(1(1(1(1(1(1(1(1(1($acc0, 0), 1), 2), 3), 4), 5), 6), 7), 8), 9), 10)
177- }
178- let rewardsStringPart = {
179- let $l = rewards
180- let $s = size($l)
181- let $acc0 = ""
182- func 1 ($a,$i) = if (($i >= $s))
183- then $a
184- else IntListToByteVector($a, $l[$i])
185-
186- func 2 ($a,$i) = if (($i >= $s))
187- then $a
188- else throw("List size exceeds 10")
189-
190- 2(1(1(1(1(1(1(1(1(1(1($acc0, 0), 1), 2), 3), 4), 5), 6), 7), 8), 9), 10)
191- }
192- ((lapsStringPart + rewardsStringPart) + address)
193- }
110+func getBackendProof (maxFeedAmount,userNonce,address) = makeString([toString(maxFeedAmount), toString(userNonce), address], ";")
194111
195112
196113 @Callable(i)
197114 func issueFreeDuckling (address,txIdStr) = {
198115 let asset = Issue("BABY-11111111-GZ", "", 1, 0, false, unit, height)
199116 let assetId = calculateAssetId(asset)
200117 $Tuple2([StringEntry((((address + "_") + txIdStr) + "_di"), toBase58String(assetId)), IntegerEntry("stats_amount", (tryGetInteger("stats_amount") + 1)), BooleanEntry((("duckling_" + toBase58String(assetId)) + "_issuedByFeedCall"), (i.caller == this)), asset, ScriptTransfer(value(addressFromString(address)), 1, assetId)], toBase58String(assetId))
201118 }
202119
203120
204121
205122 @Callable(i)
206-func feedDuckling (ducklingId,backendSignature,lapsList,rewardsList) = {
123+func feedDuckling (ducklingId,backendSignature,maxFeedAmount,userNonce) = {
207124 let addressString = toString(i.caller)
208- let laps = value(split(lapsList, ","))
209- let rewards = value(split(rewardsList, ","))
210- let backendProof = getBackendProof(laps, rewards, addressString)
125+ let backendProof = getBackendProof(maxFeedAmount, userNonce, addressString)
126+ let kAddressNonce = keyAddressNonce(addressString)
127+ let currentNonce = tryGetInteger(kAddressNonce)
211128 let realDucklingId = if (!(sigVerify_8Kb(toBytes(backendProof), fromBase58String(backendSignature), backendPubKey)))
212129 then throw("Invalid proof from backend")
213130 else if (if ((size(i.payments) != 1))
214131 then true
215132 else (value(i.payments[0]).assetId != EGGASSETID))
216133 then throw("Bad payment attached (asset[s] or amount)")
217- else if ((ducklingId == ""))
218- then {
219- let ducklingAssetId = invoke(this, "issueFreeDuckling", [toString(i.originCaller), toBase58String(i.transactionId)], nil)
220- if ((ducklingAssetId == ducklingAssetId))
134+ else if (getBool(keyDucklingGrown(ducklingId)))
135+ then throw("Duckling is already grown")
136+ else if ((userNonce != (currentNonce + 1)))
137+ then throw(((("User Nonce should be " + toString(currentNonce)) + " + 1, while received ") + toString(userNonce)))
138+ else if ((ducklingId == ""))
221139 then {
222- let id = match ducklingAssetId {
223- case v: String =>
224- v
225- case _ =>
226- throw("Can't generate NFT")
140+ let ducklingAssetId = invoke(this, "issueFreeDuckling", [toString(i.originCaller), toBase58String(i.transactionId)], nil)
141+ if ((ducklingAssetId == ducklingAssetId))
142+ then {
143+ let id = match ducklingAssetId {
144+ case v: String =>
145+ v
146+ case _ =>
147+ throw("Can't generate NFT")
148+ }
149+ id
150+ }
151+ else throw("Strict value is not equal to itself.")
227152 }
228- id
153+ else {
154+ let ducklingIdCheck = value(assetInfo(fromBase58String(ducklingId)))
155+ if ((assetBalance(i.caller, ducklingIdCheck.id) != 1))
156+ then throw("You're not the owner of the duckling")
157+ else if ((ducklingIdCheck.issuer != this))
158+ then throw("Cant find duckling with such id")
159+ else {
160+ let ducklingIdString = toBase58String(ducklingIdCheck.id)
161+ ducklingIdString
162+ }
229163 }
230- else throw("Strict value is not equal to itself.")
231- }
232- else {
233- let ducklingIdCheck = value(assetInfo(fromBase58String(ducklingId)))
234- if ((assetBalance(i.caller, ducklingIdCheck.id) != 1))
235- then throw("You're not the owner of the duckling")
236- else if ((ducklingIdCheck.issuer != this))
237- then throw("Cant find duckling with such id")
238- else {
239- let ducklingIdString = toBase58String(ducklingIdCheck.id)
240- ducklingIdString
241- }
242- }
243- let intLaps = stringListToIntList(laps)
244- let intRewards = stringListToIntList(rewards)
245- let maxFeedAmount = getMaxFeedAmount(intLaps, intRewards, toString(i.caller))
246- let kDucklingFedLap = keyDucklingFedLap(realDucklingId)
247- let lastFedLap = tryGetInteger(kDucklingFedLap)
164+ let kDucklingLastFedTs = keyDucklingFedLastTimestamp(ducklingId)
165+ let lastFedTs = getLastFedTimestamp(realDucklingId)
248166 let currentPayment = value(i.payments[0]).amount
249167 let kNewLevel = keyDucklingLevel(realDucklingId)
250168 let kTotalFeed = keyTotalFeed(realDucklingId)
251169 let totalFeed = tryGetInteger(kTotalFeed)
252- let feedTxStats = ((("duckling_" + realDucklingId) + "_stat_") + toString(lastBlock.timestamp))
170+ let kFeedTxStats = ((("duckling_" + realDucklingId) + "_stat_") + toString(lastBlock.timestamp))
171+ let kAddressFedTimestamp = keyAddressFedTimestamp(addressString, getCurrentLap())
172+ let kDucklingFedLapTimestamp = keyDucklingFedLapTimestamp(realDucklingId, getCurrentLap())
253173 if ((currentPayment > maxFeedAmount))
254174 then throw(("Cannot feed duckling for such amount, max feed amount is: " + toString(maxFeedAmount)))
255- else [IntegerEntry(kDucklingFedLap, getCurrentLap()), IntegerEntry(keyFeedForLap(addressString, intLaps[0]), (getFeedByLap(addressString, intLaps[0]) + currentPayment)), IntegerEntry(keyFeedForLap(addressString, intLaps[1]), (getFeedByLap(addressString, intLaps[1]) + currentPayment)), IntegerEntry(keyFeedForLap(addressString, intLaps[2]), (getFeedByLap(addressString, intLaps[2]) + currentPayment)), IntegerEntry(keyFeedForLap(addressString, intLaps[3]), (getFeedByLap(addressString, intLaps[3]) + currentPayment)), IntegerEntry(kTotalFeed, (totalFeed + currentPayment)), IntegerEntry(feedTxStats, currentPayment), StringEntry(kNewLevel, calculateNewDucklingLevel(realDucklingId, currentPayment))]
175+ else {
176+ let calculateResults = calculateNewDucklingLevel(realDucklingId, currentPayment)
177+[IntegerEntry(kAddressNonce, (currentNonce + 1)), IntegerEntry(kDucklingLastFedTs, lastBlock.timestamp), IntegerEntry(kTotalFeed, (totalFeed + currentPayment)), IntegerEntry(kFeedTxStats, currentPayment), IntegerEntry(kAddressFedTimestamp, currentPayment), IntegerEntry(kDucklingFedLapTimestamp, currentPayment), StringEntry(kNewLevel, calculateResults._1), StringEntry((kFeedTxStats + "_debug"), makeString(calculateResults._2, ";"))]
178+ }
256179 }
257180
258181
259182
260183 @Callable(i)
261184 func fixLevels (ducklingIds) = if ((i.callerPublicKey != base58'GDxBbsDRmeY39quNrDsTXKJzFWbQVtjxHseF4ikxZ7n9'))
262185 then throw("")
263186 else {
264187 let ducklingIdsList = value(split(ducklingIds, ","))
265188 func handleId (acc,id) = {
266189 let kTotalFeed = keyTotalFeed(id)
267190 let totalFeed = tryGetInteger(kTotalFeed)
268191 let kNewLevel = keyDucklingLevel(id)
269192 let startingLevel = match getBoolean((("duckling_" + id) + "_issuedByFeedCall")) {
270193 case b: Boolean =>
271194 toBigInt(0)
272195 case _ =>
273196 toBigInt((20 * existingDuckPrecision))
274197 }
275198 let growth = fraction(toBigInt(totalFeed), toBigInt(percentGrowthPrecision), toBigInt(DUCKLINGPRICE))
276199 (acc ++ [StringEntry(kNewLevel, toString((startingLevel + growth)))])
277200 }
278201
279202 let $l = ducklingIdsList
280203 let $s = size($l)
281204 let $acc0 = nil
282- func 1 ($a,$i) = if (($i >= $s))
205+ func $f0_1 ($a,$i) = if (($i >= $s))
283206 then $a
284207 else handleId($a, $l[$i])
285208
286- func 2 ($a,$i) = if (($i >= $s))
209+ func $f0_2 ($a,$i) = if (($i >= $s))
287210 then $a
288211 else throw("List size exceeds 20")
289212
290- 2(1(1(1(1(1(1(1(1(1(1(1(1(1(1(1(1(1(1(1(1($acc0, 0), 1), 2), 3), 4), 5), 6), 7), 8), 9), 10), 11), 12), 13), 14), 15), 16), 17), 18), 19), 20)
213+ $f0_2($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($acc0, 0), 1), 2), 3), 4), 5), 6), 7), 8), 9), 10), 11), 12), 13), 14), 15), 16), 17), 18), 19), 20)
291214 }
292215
293216
294217
295218 @Callable(i)
296-func turnDucklingIntoDuck () = throw("Not implemented yet")
219+func turnDucklingIntoDuck () = {
220+ let address = toString(i.caller)
221+ let txId = toBase58String(i.transactionId)
222+ let lastIssuedDucklingTs = tryGetInteger(KGlobalIssuedTimestamp)
223+ let fiveMinInMs = ((5 * 60) * 1000)
224+ if (if ((lastIssuedDucklingTs > 0))
225+ then (fiveMinInMs > (lastBlock.timestamp - lastIssuedDucklingTs))
226+ else false)
227+ then throw(("Can issue ducklings only once per 5 minutes, please wait for " + toString((fiveMinInMs - (lastBlock.timestamp - lastIssuedDucklingTs)))))
228+ else if ((size(i.payments) != 1))
229+ then throw("Bad payment attached (asset[s] or amount)")
230+ else {
231+ let pmt = value(assetInfo(value(value(i.payments[0]).assetId)))
232+ if ((pmt.issuer != this))
233+ then throw("Can use only ducklings from this dApp")
234+ else {
235+ let call = invoke(Address(incubatorAddress), "issueFreeDuck", [address, txId], nil)
236+ if ((call == call))
237+ then {
238+ let kDucklingGrown = keyDucklingGrown(toBase58String(pmt.id))
239+[BooleanEntry(kDucklingGrown, true), IntegerEntry(KGlobalIssuedTimestamp, lastBlock.timestamp)]
240+ }
241+ else throw("Strict value is not equal to itself.")
242+ }
243+ }
244+ }
297245
298246
299247 @Verifier(tx)
300248 func verify () = sigVerify(tx.bodyBytes, tx.proofs[0], tx.senderPublicKey)
301249

github/deemru/w8io/3ef1775 
53.95 ms