tx · 2sEGqfsCxpa8Zr8QRWbd7ZjsfSAeCGmVJ2TRu4u5r1Nc

3N3gSFrTwgiLAFZHMuJrEttht1nPhnkpvjs:  -0.01000000 Waves

2022.12.29 11:43 [2381340] smart account 3N3gSFrTwgiLAFZHMuJrEttht1nPhnkpvjs > SELF 0.00000000 Waves

{ "type": 13, "id": "2sEGqfsCxpa8Zr8QRWbd7ZjsfSAeCGmVJ2TRu4u5r1Nc", "fee": 1000000, "feeAssetId": null, "timestamp": 1672303361663, "version": 2, "chainId": 84, "sender": "3N3gSFrTwgiLAFZHMuJrEttht1nPhnkpvjs", "senderPublicKey": "5mttAQ8x5ig98rgbv6rZmwXQ2pAYSwgCkQPSVBcr7BVp", "proofs": [ "3Z4jQdjLkiwDSGqp4gb1ktYTLg2YgaqqgBsQD6LinKYt69Mt52U8Cj9iBEBrmrrbdv7biWPjprwtgmBue8zarQd6" ], "script": null, "height": 2381340, "applicationStatus": "succeeded", "spentComplexity": 0 } View: original | compacted Prev: Kfb4d8uvGscodfeAsG2svqG4McDmK6S5RoTtzuUy2JB Next: none Full:
OldNewDifferences
1-{-# STDLIB_VERSION 6 #-}
2-{-# SCRIPT_TYPE ACCOUNT #-}
3-{-# CONTENT_TYPE DAPP #-}
4-let revisionNum = ""
5-
6-let SEP = "__"
7-
8-let LISTSEP = ":"
9-
10-let DEFAULTQUORUM = 500000
11-
12-let URLPATTERN = "https://forum.neutrino.at/"
13-
14-let MAXTITLE = 160
15-
16-let MAXURL = 250
17-
18-let MAXVOTINGTIME = 1209600000
19-
20-let MULT6 = 1000000
21-
22-let DEFAULTPAYMENT = 1000000000
23-
24-let DEFAULTCREATIONGNSBT = 1000000000
25-
26-let PASTMARGIN = 7200000
27-
28-let FUTUREMARGIN = 5400000
29-
30-let govIdxProposalTxId = 1
31-
32-let govIdxType = 2
33-
34-let govIdxAuthor = 3
35-
36-let govIdxUrl = 4
37-
38-let govIdxTitle = 5
39-
40-let govIdxCreationTime = 6
41-
42-let govIdxStart = 7
43-
44-let govIdxEnd = 8
45-
46-let govIdxTxIds = 9
47-
48-let govIdxQuorum = 10
49-
50-let govIdxOptions = 11
51-
52-let govStatusIdxIsValid = 1
53-
54-let govStatusIdxWinOpt = 2
55-
56-let govStatusIdxWinVotes = 3
57-
58-let govStatusIdxTotalVotes = 4
59-
60-let govStatusIdxScApplied = 5
61-
62-let govStatusIdxScTime = 6
63-
64-let govStatusIdxIsCanceled = 7
65-
66-func getStringOrFail (address,key) = valueOrErrorMessage(getString(address, key), makeString(["mandatory ", toString(address), ".", key, " is not defined"], ""))
67-
68-
69-func getIntOrElse (key,defaultVal) = valueOrElse(getInteger(this, key), defaultVal)
70-
71-
72-let IdxControlCfgNeutrinoDapp = 1
73-
74-let IdxControlCfgAuctionDapp = 2
75-
76-let IdxControlCfgRpdDapp = 3
77-
78-let IdxControlCfgMathDapp = 4
79-
80-let IdxControlCfgLiquidationDapp = 5
81-
82-let IdxControlCfgRestDapp = 6
83-
84-let IdxControlCfgNodeRegistryDapp = 7
85-
86-let IdxControlCfgNsbtStakingDapp = 8
87-
88-let IdxControlCfgMediatorDapp = 9
89-
90-let IdxControlCfgSurfStakingDapp = 10
91-
92-let IdxControlCfgGnsbtControllerDapp = 11
93-
94-let IdxControlCfgRestV2Dapp = 12
95-
96-let IdxControlCfgGovernanceDapp = 13
97-
98-func keyControlAddress () = "%s%s__config__controlAddress"
99-
100-
101-func keyControlCfg () = "%s__controlConfig"
102-
103-
104-func readControlCfgOrFail (control) = split_4C(getStringOrFail(control, keyControlCfg()), SEP)
105-
106-
107-func getContractAddressOrFail (controlCfg,idx) = valueOrErrorMessage(addressFromString(controlCfg[idx]), ("Control cfg doesn't contain address at index " + toString(idx)))
108-
109-
110-let controlContract = addressFromStringValue(valueOrElse(getString(this, keyControlAddress()), "3P5Bfd58PPfNvBM2Hy8QfbcDqMeNtzg7KfP"))
111-
112-let controlCfg = readControlCfgOrFail(controlContract)
113-
114-let neutrinoContract = getContractAddressOrFail(controlCfg, IdxControlCfgNeutrinoDapp)
115-
116-let gnsbtControllerContract = getContractAddressOrFail(controlCfg, IdxControlCfgGnsbtControllerDapp)
117-
118-func keyQuorumRequiredPercent (type) = ("%s%s__quorumRequired__" + type)
119-
120-
121-func keyPaymentRequired () = "%s__paymentRequired"
122-
123-
124-func keyGnsbtRequired () = "%s__gNsbtRequired"
125-
126-
127-func keyLastProposalId () = "%s__proposalId"
128-
129-
130-func keyLastUpdateVersion () = "%s__updateVersion"
131-
132-
133-func keyProposalStatusDataById (proposalId) = ("%s%d__proposalStatusData__" + toString(proposalId))
134-
135-
136-func keyProposalDataById (proposalId) = ("%s%d__proposalData__" + toString(proposalId))
137-
138-
139-func keyProposalVotesByIdAndOption (proposalId,opt) = makeString(["%s%d%d", "votesByOpt", toString(proposalId), toString(opt)], SEP)
140-
141-
142-func keyProposalVotesByIdAndUser (proposalId,userAddr) = makeString(["%s%d%s", "votesByUser", toString(proposalId), userAddr], SEP)
143-
144-
145-func keyProposalChoiceByIdAndUser (proposalId,userAddr) = makeString(["%s%d%s", "optionByUser", toString(proposalId), userAddr], SEP)
146-
147-
148-func keyApplyInProgress () = "%s__applyInProgress"
149-
150-
151-func keyProposalIdByTopicId (topicId) = ("%s%d__proposalIdByTopicId__" + toString(topicId))
152-
153-
154-func keyNumUniqueVotersByProposalId (proposalId) = ("%s%d__numVoters__" + toString(proposalId))
155-
156-
157-func keyStatsAverUniqueVoters () = "%s%s%s__stats__avg__uniqueVoters"
158-
159-
160-func keyStatsAverGnsbtVoted () = "%s%s%s__stats__avg__gnsbtVoted"
161-
162-
163-func keyStatsUniqueAuthors () = "%s%s__stats__uniqueAuthors"
164-
165-
166-func keyNumProposalsByAuthor (addressStr) = ("%s%s__numProposalsByAuthor__" + addressStr)
167-
168-
169-func keyApplyHistory (timestamp) = ("%s%d__applyHistory__" + toString(timestamp))
170-
171-
172-func asAnyList (v) = match v {
173- case l: List[Any] =>
174- l
175- case _ =>
176- throw("fail to cast into List[Any]")
177-}
178-
179-
180-func asInt (v) = match v {
181- case i: Int =>
182- i
183- case _ =>
184- throw("fail to cast into Int")
185-}
186-
187-
188-func statusData (isVotingValid,winOption,winOptionVotes,totalVotes,areScriptsApplied,scriptsTimestamp,canceledByTeam) = makeString(["%b%d%d%d%b%d%b", toString(isVotingValid), toString(winOption), toString(winOptionVotes), toString(totalVotes), toString(areScriptsApplied), toString(scriptsTimestamp), toString(canceledByTeam)], SEP)
189-
190-
191-func proposalData (proposalTxId,type,author,forumLink,title,proposalTime,votingStartTime,votingEndTime,txIds,quorumInGnsbt,options) = makeString(["%s%s%s%s%s%d%d%d%s%d%s", proposalTxId, type, author, forumLink, title, toString(proposalTime), toString(votingStartTime), toString(votingEndTime), txIds, toString(quorumInGnsbt), options], SEP)
192-
193-
194-func checkTxList (txList) = if ((size(txList) > 20))
195- then throw(("Too many transactions: " + toString(size(txList))))
196- else {
197- func combiner (acc,tx) = if ((size(fromBase58String(tx)) != 32))
198- then throw(("Wrong txId: " + tx))
199- else if ((acc == ""))
200- then tx
201- else ((acc + LISTSEP) + tx)
202-
203- let $l = txList
204- let $s = size($l)
205- let $acc0 = ""
206- func $f0_1 ($a,$i) = if (($i >= $s))
207- then $a
208- else combiner($a, $l[$i])
209-
210- func $f0_2 ($a,$i) = if (($i >= $s))
211- then $a
212- else throw("List size exceeds 20")
213-
214- $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)
215- }
216-
217-
218-let IdxEffTotal = 0
219-
220-let IdxEffUser = 1
221-
222-func getEffectiveGnsbt (userAddrStrOrEmpty) = {
223- let gnsbtData = asAnyList(invoke(gnsbtControllerContract, "gnsbtInfoSYSREADONLY", [userAddrStrOrEmpty, 0, 0], nil))
224- let nsbtData = asAnyList(gnsbtData[2])
225- let userFromNsbt = asInt(nsbtData[2])
226- let totalFromNsbt = asInt(nsbtData[3])
227- let userMatureFromSurf = asInt(gnsbtData[9])
228- let totalMatureFromSurf = asInt(gnsbtData[6])
229-[(totalFromNsbt + totalMatureFromSurf), (userFromNsbt + userMatureFromSurf)]
230- }
231-
232-
233-func validateLink (url) = if ((value(indexOf(url, URLPATTERN)) != 0))
234- then throw("Invalid url")
235- else if ((size(url) > MAXURL))
236- then throw("Url too long!")
237- else {
238- let topicId = valueOrErrorMessage(parseInt(drop(url, (value(lastIndexOf(url, "/")) + 1))), "Wrong topicId")
239- let registeredId = getInteger(keyProposalIdByTopicId(topicId))
240- if (isDefined(registeredId))
241- then throw(("Voting with such forum link is already registered by id=" + toString(value(registeredId))))
242- else topicId
243- }
244-
245-
246-func initiateVoting (payment,proposalTxId,type,author,forumLink,title,votingStartTime,votingEndTime,status,txList,optionsList) = if ((payment.assetId != unit))
247- then throw("Allowed WAVES payment only!")
248- else {
249- let pmtReq = getIntOrElse(keyPaymentRequired(), DEFAULTPAYMENT)
250- if ((pmtReq > payment.amount))
251- then throw(("Payment attached should be at least " + toString(pmtReq)))
252- else {
253- let topicId = validateLink(forumLink)
254- if ((title == ""))
255- then throw("Title is empty")
256- else if ((size(title) > MAXTITLE))
257- then throw("Too long title")
258- else {
259- let proposalTime = lastBlock.timestamp
260- if ((proposalTime > votingStartTime))
261- then throw(((("votingStartTime=" + toString(votingStartTime)) + " < proposalTime=") + toString(proposalTime)))
262- else if ((votingStartTime > votingEndTime))
263- then throw(((("votingEndTime=" + toString(votingEndTime)) + " < votingStartTime=") + toString(votingStartTime)))
264- else if (((votingEndTime - votingStartTime) > MAXVOTINGTIME))
265- then throw(((("Voting period exceeds max: " + toString((votingEndTime - votingStartTime))) + " > ") + toString(MAXVOTINGTIME)))
266- else {
267- let txIds = if ((type == "IDEA"))
268- then ""
269- else checkTxList(txList)
270- if ((1 >= size(optionsList)))
271- then throw("Too few choices to vote")
272- else {
273- let eff = getEffectiveGnsbt(author)
274- let gnsbtTotal = eff[IdxEffTotal]
275- let gNsbtUser = eff[IdxEffUser]
276- let gnsbtReq = getIntOrElse(keyGnsbtRequired(), DEFAULTCREATIONGNSBT)
277- if ((gnsbtReq > gNsbtUser))
278- then throw((("You need at least " + toString(gnsbtReq)) + " gNsbt to create voting"))
279- else {
280- let amountLeased = invoke(neutrinoContract, "acceptWaves", nil, [payment])
281- if ((amountLeased == amountLeased))
282- then {
283- let quorum = getIntOrElse(keyQuorumRequiredPercent(type), DEFAULTQUORUM)
284- let quorumInGnsbt = fraction(quorum, gnsbtTotal, MULT6)
285- let proposalId = (getIntOrElse(keyLastProposalId(), 0) + 1)
286- let numProposalsByAuthor = (getIntOrElse(keyNumProposalsByAuthor(author), 0) + 1)
287- let uniqAuthors = (getIntOrElse(keyStatsUniqueAuthors(), 0) + (if ((numProposalsByAuthor == 1))
288- then 1
289- else 0))
290- let optionsStr = makeString(optionsList, LISTSEP)
291- $Tuple2([IntegerEntry(keyLastProposalId(), proposalId), IntegerEntry(keyProposalIdByTopicId(topicId), proposalId), StringEntry(keyProposalStatusDataById(proposalId), statusData(false, 0, 0, 0, false, 0, false)), StringEntry(keyProposalDataById(proposalId), proposalData(proposalTxId, type, author, toBase58String(toBytes(forumLink)), toBase58String(toBytes(title)), proposalTime, votingStartTime, votingEndTime, txIds, quorumInGnsbt, optionsStr)), IntegerEntry(keyNumProposalsByAuthor(author), numProposalsByAuthor), IntegerEntry(keyStatsUniqueAuthors(), uniqAuthors)], proposalTxId)
292- }
293- else throw("Strict value is not equal to itself.")
294- }
295- }
296- }
297- }
298- }
299- }
300-
301-
302-func calcWinOption (proposalId,optionsList,isPrevOptional,oldChoice,optionalTotalOld,newChoice,newTotalByNewChoice) = {
303- func findBest (acc,elem) = {
304- let idx = value(indexOf(optionsList, elem))
305- let val = if (isPrevOptional)
306- then if ((idx == newChoice))
307- then newTotalByNewChoice
308- else getIntOrElse(keyProposalVotesByIdAndOption(proposalId, idx), 0)
309- else if ((idx == value(oldChoice)))
310- then optionalTotalOld
311- else if ((idx == newChoice))
312- then newTotalByNewChoice
313- else getIntOrElse(keyProposalVotesByIdAndOption(proposalId, idx), 0)
314- if ((acc._2 > val))
315- then acc
316- else $Tuple2(idx, val)
317- }
318-
319- let $l = optionsList
320- let $s = size($l)
321- let $acc0 = $Tuple2(0, 0)
322- func $f0_1 ($a,$i) = if (($i >= $s))
323- then $a
324- else findBest($a, $l[$i])
325-
326- func $f0_2 ($a,$i) = if (($i >= $s))
327- then $a
328- else throw("List size exceeds 10")
329-
330- $f0_2($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)
331- }
332-
333-
334-func updateStatusData (oldData,isValid,newWinOpt,newTotalVotes) = makeString(["%b%d%d%d%b%d%b", toString(isValid), toString(newWinOpt._1), toString(newWinOpt._2), toString(newTotalVotes), oldData[govStatusIdxScApplied], oldData[govStatusIdxScTime], oldData[govStatusIdxIsCanceled]], SEP)
335-
336-
337-func statusApplyScript (oldData) = makeString(["%b%d%d%d%b%d%b", oldData[govStatusIdxIsValid], oldData[govStatusIdxWinOpt], oldData[govStatusIdxWinVotes], oldData[govStatusIdxTotalVotes], "true", oldData[govStatusIdxScTime], oldData[govStatusIdxIsCanceled]], SEP)
338-
339-
340-func ExecutionHistory (proposalId,title,url) = {
341- let gnsbtTotal = getEffectiveGnsbt("")[IdxEffTotal]
342- let turnout = 500000
343- StringEntry(keyApplyHistory(lastBlock.timestamp), makeString(["%d%d%d%s%s", toString(proposalId), toString(gnsbtTotal), toString(turnout), title, url], SEP))
344- }
345-
346-
347-@Callable(i)
348-func constructor (controlAddr,gNsbtReqToInit,wavesReqToInit,quorumReqPercIdea,quorumReqPercUpdate) = if ((i.caller != this))
349- then throw("Permission denied")
350- else [StringEntry(keyControlAddress(), controlAddr), IntegerEntry(keyGnsbtRequired(), gNsbtReqToInit), IntegerEntry(keyPaymentRequired(), wavesReqToInit), IntegerEntry(keyQuorumRequiredPercent("IDEA"), quorumReqPercIdea), IntegerEntry(keyQuorumRequiredPercent("UPDATE"), quorumReqPercUpdate)]
351-
352-
353-
354-@Callable(i)
355-func castVote (proposalId,choice) = {
356- let userAddressStr = toString(i.caller)
357- let dynamicData = split(getStringOrFail(this, keyProposalStatusDataById(proposalId)), SEP)
358- if ((dynamicData[govStatusIdxIsCanceled] == "true"))
359- then throw("Voting is canceled by team")
360- else {
361- let propData = split(getStringOrFail(this, keyProposalDataById(proposalId)), SEP)
362- let start = parseIntValue(propData[govIdxStart])
363- let end = parseIntValue(propData[govIdxEnd])
364- let now = lastBlock.timestamp
365- if ((start > now))
366- then throw("Voting not started yet")
367- else if ((now >= end))
368- then throw("Voting already finished")
369- else {
370- let availableOptions = split(propData[govIdxOptions], LISTSEP)
371- let numOptions = size(availableOptions)
372- if ((1 >= numOptions))
373- then throw("Too few choices to vote")
374- else if ((choice >= numOptions))
375- then throw(("Unknown choice! Must be 0.." + toString((numOptions - 1))))
376- else {
377- let eff = getEffectiveGnsbt(userAddressStr)
378- let gnsbtAmt = eff[IdxEffUser]
379- if ((0 >= gnsbtAmt))
380- then throw("no gnsbt to vote")
381- else {
382- let gnsbtTotal = eff[IdxEffTotal]
383- let oldChoice = getInteger(keyProposalChoiceByIdAndUser(proposalId, userAddressStr))
384- let oldUserVotes = if (!(isDefined(oldChoice)))
385- then 0
386- else getIntOrElse(keyProposalVotesByIdAndUser(proposalId, userAddressStr), 0)
387- let oldTotalByOldChoice = if (isDefined(oldChoice))
388- then getIntOrElse(keyProposalVotesByIdAndOption(proposalId, value(oldChoice)), 0)
389- else 0
390- let oldTotalByNewChoice = getIntOrElse(keyProposalVotesByIdAndOption(proposalId, choice), 0)
391- let oldTotal = parseIntValue(dynamicData[govStatusIdxTotalVotes])
392- let newTotalByOldChoice = if (!(isDefined(oldChoice)))
393- then 0
394- else ((oldTotalByOldChoice - oldUserVotes) + (if ((value(oldChoice) == choice))
395- then gnsbtAmt
396- else 0))
397- let newTotalByNewChoice = if (if (isDefined(oldChoice))
398- then (value(oldChoice) == choice)
399- else false)
400- then newTotalByOldChoice
401- else (oldTotalByNewChoice + gnsbtAmt)
402- let newTotal = ((oldTotal - oldUserVotes) + gnsbtAmt)
403- let isQuorumReached = (newTotal >= parseIntValue(propData[govIdxQuorum]))
404- let numVotersByProposalId = getIntOrElse(keyNumUniqueVotersByProposalId(proposalId), 0)
405- let oldAverUniqueVoters6 = getIntOrElse(keyStatsAverUniqueVoters(), 0)
406- let numProposals = getIntegerValue(keyLastProposalId())
407- let uniqueDiff = if ((oldUserVotes == 0))
408- then 1
409- else 0
410- let newAverUniqueVoters6 = (oldAverUniqueVoters6 + fraction(uniqueDiff, MULT6, numProposals))
411- let oldAverGnsbt = getIntOrElse(keyStatsAverGnsbtVoted(), 0)
412- let newAverGnsbt = (oldAverGnsbt + ((gnsbtAmt - oldUserVotes) / numProposals))
413- let isPrevOptional = if (!(isDefined(oldChoice)))
414- then true
415- else (value(oldChoice) == choice)
416- let optionalTotalOld = if (isPrevOptional)
417- then nil
418- else [IntegerEntry(keyProposalVotesByIdAndOption(proposalId, value(oldChoice)), newTotalByOldChoice)]
419- let winOpt = calcWinOption(proposalId, availableOptions, isPrevOptional, oldChoice, newTotalByOldChoice, choice, newTotalByNewChoice)
420- $Tuple2(([IntegerEntry(keyProposalChoiceByIdAndUser(proposalId, userAddressStr), choice), IntegerEntry(keyProposalVotesByIdAndUser(proposalId, userAddressStr), gnsbtAmt), IntegerEntry(keyProposalVotesByIdAndOption(proposalId, choice), newTotalByNewChoice), IntegerEntry(keyNumUniqueVotersByProposalId(proposalId), (numVotersByProposalId + uniqueDiff)), IntegerEntry(keyStatsAverUniqueVoters(), newAverUniqueVoters6), IntegerEntry(keyStatsAverGnsbtVoted(), newAverGnsbt), StringEntry(keyProposalStatusDataById(proposalId), updateStatusData(dynamicData, isQuorumReached, winOpt, newTotal))] ++ optionalTotalOld), unit)
421- }
422- }
423- }
424- }
425- }
426-
427-
428-
429-@Callable(i)
430-func initiateIdeaVoting (forumLink,title,votingStartTime,votingEndTime,optionsList) = if ((size(i.payments) != 1))
431- then throw("Exactly one payment required")
432- else if ((size(optionsList) != 2))
433- then throw("Exactly 2 option ['NO', 'YES'] are expected")
434- else if ((optionsList[0] != "NO"))
435- then throw("Option NO should be the first")
436- else if ((optionsList[1] != "YES"))
437- then throw("Option YES should be the second")
438- else initiateVoting(value(i.payments[0]), toBase58String(i.transactionId), "IDEA", toString(i.caller), forumLink, title, votingStartTime, votingEndTime, "PENDING", nil, optionsList)
439-
440-
441-
442-@Callable(i)
443-func initiateUpdateVoting (forumLink,title,votingStartTime,votingEndTime,txList) = if ((size(i.payments) != 1))
444- then throw("Exactly one payment required")
445- else if ((1 > size(txList)))
446- then throw("Transactions list is empty")
447- else if ((i.caller != this))
448- then throw("not authorized")
449- else initiateVoting(value(i.payments[0]), toBase58String(i.transactionId), "UPDATE", toString(i.caller), forumLink, title, votingStartTime, votingEndTime, "PENDING", txList, ["NO", "YES"])
450-
451-
452-
453-@Callable(i)
454-func cancelVoting (proposalId) = if ((i.caller != this))
455- then throw("not authorized")
456- else {
457- let currentData = getStringOrFail(this, keyProposalStatusDataById(proposalId))
458- let updatedData = ((take(currentData, value(lastIndexOf(currentData, SEP))) + SEP) + "true")
459- $Tuple2([StringEntry(keyProposalStatusDataById(proposalId), updatedData)], unit)
460- }
461-
462-
463-
464-@Callable(i)
465-func applyUpdate (proposalId) = {
466- let propData = split(getStringOrFail(this, keyProposalDataById(proposalId)), SEP)
467- let end = parseIntValue(propData[govIdxEnd])
468- let now = lastBlock.timestamp
469- if ((end > now))
470- then throw("Voting is not finished yet")
471- else if (("UPDATE" != propData[govIdxType]))
472- then throw("Only UPDATE type can be applied")
473- else {
474- let dynamicData = split(getStringOrFail(this, keyProposalStatusDataById(proposalId)), SEP)
475- if ((dynamicData[govStatusIdxIsCanceled] == "true"))
476- then throw("Voting is canceled")
477- else if ((dynamicData[govStatusIdxIsValid] != "true"))
478- then throw("Voting status invalid")
479- else if ((dynamicData[govStatusIdxWinOpt] != "1"))
480- then throw("Winner is 'NO' - nothing to apply")
481- else if ((dynamicData[govStatusIdxScApplied] == "true"))
482- then throw("Scripts are already applied")
483- else {
484- let scriptTime = parseIntValue(dynamicData[govStatusIdxScTime])
485- if (((now - PASTMARGIN) > scriptTime))
486- then throw((("Scripts timestamp=" + toString(scriptTime)) + " is too far in the past, max 2 hrs allowed"))
487- else if ((scriptTime > (now + FUTUREMARGIN)))
488- then throw((("Scripts timestamp=" + toString(scriptTime)) + " is too far in the future, max 1.5 hrs allowed"))
489- else {
490- let inProgressId = getIntOrElse(keyApplyInProgress(), -1)
491- if ((inProgressId != -1))
492- then throw((("proposalId=" + toString(inProgressId)) + " is already being applied. Finish it first!"))
493- else {
494- let shutdown = invoke(controlContract, "callEmergencyShutdown", ["Applying Governance UPDATE"], nil)
495- if ((shutdown == shutdown))
496- then $Tuple2([IntegerEntry(keyApplyInProgress(), proposalId)], unit)
497- else throw("Strict value is not equal to itself.")
498- }
499- }
500- }
501- }
502- }
503-
504-
505-
506-@Callable(i)
507-func finishApply () = {
508- let proposalId = valueOrErrorMessage(getInteger(keyApplyInProgress()), "No apply in progress, nothing to finish")
509- let propData = split(getStringOrFail(this, keyProposalDataById(proposalId)), SEP)
510- let txList = split(propData[govIdxTxIds], LISTSEP)
511- let dynamicData = split(getStringOrFail(this, keyProposalStatusDataById(proposalId)), SEP)
512- if ((dynamicData[govStatusIdxScApplied] == "true"))
513- then throw("Scripts are already applied")
514- else {
515- func checker (acc,tx) = if (!(isDefined(transactionHeightById(fromBase58String(tx)))))
516- then throw(("NOT applied txId: " + tx))
517- else unit
518-
519- let ignored = {
520- let $l = txList
521- let $s = size($l)
522- let $acc0 = unit
523- func $f0_1 ($a,$i) = if (($i >= $s))
524- then $a
525- else checker($a, $l[$i])
526-
527- func $f0_2 ($a,$i) = if (($i >= $s))
528- then $a
529- else throw("List size exceeds 20")
530-
531- $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)
532- }
533- let version = (getIntOrElse(keyLastUpdateVersion(), 0) + 1)
534- $Tuple2([DeleteEntry(keyApplyInProgress()), IntegerEntry(keyLastUpdateVersion(), version), StringEntry(keyProposalStatusDataById(proposalId), statusApplyScript(dynamicData)), ExecutionHistory(version, propData[govIdxTitle], propData[govIdxUrl])], ignored)
535- }
536- }
537-
538-
1+# no script

github/deemru/w8io/fabc49c 
40.07 ms