tx · GFmJ2zp2Ji635f7RwY8YMY1X2jmVCe66S2RUB79qabVE 3MriZGLhQEu1jMDKDAbnsbh6TZuZueBAHSY: -0.04000000 Waves 2021.03.13 03:18 [1435172] smart account 3MriZGLhQEu1jMDKDAbnsbh6TZuZueBAHSY > SELF 0.00000000 Waves
{ "type": 13, "id": "GFmJ2zp2Ji635f7RwY8YMY1X2jmVCe66S2RUB79qabVE", "fee": 4000000, "feeAssetId": null, "timestamp": 1615594748652, "version": 2, "chainId": 84, "sender": "3MriZGLhQEu1jMDKDAbnsbh6TZuZueBAHSY", "senderPublicKey": "3idTJAmqkivFDf1EUKZ85Tt43Rp22pZb3RDiVXTWrgBq", "proofs": [ "5fDcs2aHUMKc392wPTbgffcAfHkdCEqZnNK4TDocQEEFAuRWxGQDm28s3o7LtsbSLZ4bCXg4q2ut2nGBipoU6K2" ], "script": "base64:AAIEAAAAAAAAAAsIAhIDCgEIEgASAAAAAAkAAAAAEXRhc2tTdGF0dXNEZWZhdWx0AgAAAAppbmNvbXBsZXRlAAAAABJ0YXNrU3RhdHVzQ29tcGxldGUCAAAACGNvbXBsZXRlAAAAAAxhZGRyQ29udHJhY3QJAQAAAAdBZGRyZXNzAAAAAQEAAAAaAVQeulVTb30gIatILq7oKevTQfmT23BHXzEAAAAADGFzc2V0V2F2ZXNJZAUAAAAEdW5pdAAAAAASYXNzZXROZnRJbml0QW1vdW50AAAAAAAAAAABAAAAABBhc3NldE5mdERlY2ltYWxzAAAAAAAAAAAAAAAAABJhc3NldE5mdFJlaXNzdWFibGUHAAAAABdhc3NldEFtb3VudE1pblRocmVzaG9sZAAAAAAAAAAD6AAAAAASZGVjaW1hbHNBc3NldFdhdmVzAAAAAAAAAAAIAAAAAwAAAAFpAQAAAAdzZXRUYXNrAAAAAQAAAAh0YXNrTmFtZQQAAAAJYXNzZXROYW1lCQABLAAAAAIJAAEsAAAAAgkAAlgAAAABCQEAAAAFdmFsdWUAAAABCAUAAAABaQAAAA9jYWxsZXJQdWJsaWNLZXkCAAAACCwgdGFzazogBQAAAAh0YXNrTmFtZQQAAAAQYXNzZXREZXNjcmlwdGlvbgkAASwAAAACAgAAAB5ORlQtdG9rZW4gb2YgdGFzayBtYW5hZ2VyIGZyb20JAAQlAAAAAQUAAAAEdGhpcwQAAAAVaW50QXNzZXRpbnB1dElkQW1vdW50CAkAAZEAAAACCAUAAAABaQAAAAhwYXltZW50cwAAAAAAAAAAAAAAAAZhbW91bnQEAAAACnNoYXJlSXNzdWUJAARCAAAABQUAAAAJYXNzZXROYW1lBQAAABBhc3NldERlc2NyaXB0aW9uBQAAABJhc3NldE5mdEluaXRBbW91bnQFAAAAEGFzc2V0TmZ0RGVjaW1hbHMFAAAAEmFzc2V0TmZ0UmVpc3N1YWJsZQQAAAAMc2hhcmVJc3N1ZUlkCQACWAAAAAEJAAQ4AAAAAQUAAAAKc2hhcmVJc3N1ZQQAAAANbGFiZWxUYXNrTmFtZQkAASwAAAACAgAAAAl0YXNrTmFtZV8FAAAADHNoYXJlSXNzdWVJZAQAAAAPbGFiZWxUYXNrU3RhdHVzCQABLAAAAAICAAAAC3Rhc2tTdGF0dXNfBQAAAAxzaGFyZUlzc3VlSWQEAAAAD2xhYmVsVGFza0Ftb3VudAkAASwAAAACAgAAAAt0YXNrQW1vdW50XwUAAAAMc2hhcmVJc3N1ZUlkAwkAAGYAAAACCQABkAAAAAEIBQAAAAFpAAAACHBheW1lbnRzAAAAAAAAAAABCQAAAgAAAAECAAAAHE9uZSBhdHRhY2hlZCBhc3NldHMgZXhwZWN0ZWQDCQEAAAACIT0AAAACCAkAAZEAAAACCAUAAAABaQAAAAhwYXltZW50cwAAAAAAAAAAAAAAAAdhc3NldElkBQAAAAxhc3NldFdhdmVzSWQJAAACAAAAAQIAAAAdbXVzdCBiZSBhdHRhY2hlZCBhc3NldDogV0FWRVMDCQAAZgAAAAIFAAAAF2Fzc2V0QW1vdW50TWluVGhyZXNob2xkCAkAAZEAAAACCAUAAAABaQAAAAhwYXltZW50cwAAAAAAAAAAAAAAAAZhbW91bnQJAAACAAAAAQIAAAA/YW1vdW50IG11c3QgYmUgZ3JlYXRlciB0aGFuIHRoZSBtaW5pbXVtIHRocmVzaG9sZDogMC4wMDAxIFdBVkVTAwkAAAAAAAACCAUAAAABaQAAAAZjYWxsZXIFAAAADGFkZHJDb250cmFjdAkAAAIAAAABAgAAACptZXRob2QgbWF5IG5vdCBiZSBjYWxsZWQgZnJvbSB0aGUgY29udHJhY3QJAARMAAAAAgkBAAAAC1N0cmluZ0VudHJ5AAAAAgUAAAANbGFiZWxUYXNrTmFtZQUAAAAIdGFza05hbWUJAARMAAAAAgkBAAAAC1N0cmluZ0VudHJ5AAAAAgUAAAAPbGFiZWxUYXNrU3RhdHVzBQAAABF0YXNrU3RhdHVzRGVmYXVsdAkABEwAAAACBQAAAApzaGFyZUlzc3VlCQAETAAAAAIJAQAAAA5TY3JpcHRUcmFuc2ZlcgAAAAMIBQAAAAFpAAAABmNhbGxlcgUAAAASYXNzZXROZnRJbml0QW1vdW50CQACWQAAAAEFAAAADHNoYXJlSXNzdWVJZAkABEwAAAACCQEAAAALU3RyaW5nRW50cnkAAAACBQAAAA9sYWJlbFRhc2tTdGF0dXMFAAAAEXRhc2tTdGF0dXNEZWZhdWx0CQAETAAAAAIJAQAAAA5TY3JpcHRUcmFuc2ZlcgAAAAMFAAAADGFkZHJDb250cmFjdAUAAAAVaW50QXNzZXRpbnB1dElkQW1vdW50BQAAAAxhc3NldFdhdmVzSWQJAARMAAAAAgkBAAAADEludGVnZXJFbnRyeQAAAAIFAAAAD2xhYmVsVGFza0Ftb3VudAUAAAAVaW50QXNzZXRpbnB1dElkQW1vdW50BQAAAANuaWwAAAABaQEAAAAMY29tcGxldGVUYXNrAAAAAAQAAAALJHQwMjEwMzIxOTUJAAUUAAAAAggJAAGRAAAAAggFAAAAAWkAAAAIcGF5bWVudHMAAAAAAAAAAAAAAAAGYW1vdW50CQEAAAAFdmFsdWUAAAABCAkAAZEAAAACCAUAAAABaQAAAAhwYXltZW50cwAAAAAAAAAAAAAAAAdhc3NldElkBAAAABBhc3NldElucHV0QW1vdW50CAUAAAALJHQwMjEwMzIxOTUAAAACXzEEAAAADGFzc2V0SW5wdXRJZAgFAAAACyR0MDIxMDMyMTk1AAAAAl8yBAAAAA9sYWJlbFRhc2tTdGF0dXMJAAEsAAAAAgIAAAALdGFza1N0YXR1c18JAAJYAAAAAQUAAAAMYXNzZXRJbnB1dElkBAAAAA9sYWJlbFRhc2tBbW91bnQJAAEsAAAAAgIAAAALdGFza0Ftb3VudF8JAAJYAAAAAQUAAAAMYXNzZXRJbnB1dElkBAAAABJzdHJpbmdBc3NldElucHV0SWQJAAJYAAAAAQkBAAAABXZhbHVlAAAAAQgJAAGRAAAAAggFAAAAAWkAAAAIcGF5bWVudHMAAAAAAAAAAAAAAAAHYXNzZXRJZAQAAAAOaW5mb0Fzc2V0SW5wdXQJAQAAABN2YWx1ZU9yRXJyb3JNZXNzYWdlAAAAAgkAA+wAAAABCQEAAAAFdmFsdWUAAAABCAkAAZEAAAACCAUAAAABaQAAAAhwYXltZW50cwAAAAAAAAAAAAAAAAdhc3NldElkCQABLAAAAAIJAAEsAAAAAgIAAAAGQXNzZXQgBQAAABJzdHJpbmdBc3NldElucHV0SWQCAAAADiBkb2Vzbid0IGV4aXN0BAAAABZpbnRBc3NldE91dHB1dElkQW1vdW50CQEAAAARQGV4dHJOYXRpdmUoMTA1MCkAAAACBQAAAAR0aGlzBQAAAA9sYWJlbFRhc2tBbW91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ZXRJbnB1dAAAAApyZWlzc3VhYmxlBQAAABJhc3NldE5mdFJlaXNzdWFibGUHCQEAAAAJaXNEZWZpbmVkAAAAAQkABB0AAAACBQAAAAR0aGlzBQAAAA9sYWJlbFRhc2tTdGF0dXMHBAAAAAp0YXNrU3RhdHVzCQAEHQAAAAIFAAAABHRoaXMFAAAAD2xhYmVsVGFza1N0YXR1cwMDCQAAAAAAAAIFAAAACnRhc2tTdGF0dXMFAAAAEXRhc2tTdGF0dXNEZWZhdWx0CQAAAAAAAAIJAQAAAAV2YWx1ZQAAAAEICQABkQAAAAIIBQAAAAFpAAAACHBheW1lbnRzAAAAAAAAAAAAAAAABmFtb3VudAUAAAASYXNzZXROZnRJbml0QW1vdW50BwQAAAAWaW50QXNzZXRPdXRwdXRJZEFtb3VudAkAAGwAAAAGCQEAAAARQGV4dHJOYXRpdmUoMTA1MCkAAAACBQAAAAR0aGlzBQAAAA9sYWJlbFRhc2tBbW91bnQAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAFAAAAEmRlY2ltYWxzQXNzZXRXYXZlcwUAAAAERE9XTgkABEwAAAACCQEAAAAEQnVybgAAAAIFAAAADGFzc2V0SW5wdXRJZAUAAAAQYXNzZXRJbnB1dEFtb3VudAkABEwAAAACCQEAAAAOU2NyaXB0VHJhbnNmZXIAAAADCAUAAAABaQAAAAZjYWxsZXIFAAAAFmludEFzc2V0T3V0cHV0SWRBbW91bnQFAAAADGFzc2V0V2F2ZXNJZAkABEwAAAACCQEAAAALRGVsZXRlRW50cnkAAAABBQAAAA9sYWJlbFRhc2tBbW91bnQJAARMAAAAAgkBAAAAC0RlbGV0ZUVudHJ5AAAAAQUAAAAPbGFiZWxUYXNrU3RhdHVzCQAETAAAAAIJAQAAAAtEZWxldGVFbnRyeQAAAAEFAAAADWxhYmVsVGFza05hbWUFAAAAA25pbAMJAAAAAAAAAgUAAAASdGFza1N0YXR1c0NvbXBsZXRlBQAAAAp0YXNrU3RhdHVzCQAETAAAAAIJAQAAAAtEZWxldGVFbnRyeQAAAAEFAAAAD2xhYmVsVGFza0Ftb3VudAkABEwAAAACCQEAAAALRGVsZXRlRW50cnkAAAABBQAAAA9sYWJlbFRhc2tTdGF0dXMJAARMAAAAAgkBAAAAC0RlbGV0ZUVudHJ5AAAAAQUAAAANbGFiZWxUYXNrTmFtZQUAAAADbmlsCQAAAgAAAAECAAAAGVRhc2sgaGF2ZSBhIGJyb2tlbiBzdGF0dXMJAAACAAAAAQIAAAAeQXR0YWNoZWQgYXNzZXQgaXMgbm90IGV4cGVjdGVkAAAAAF4JgeU=", "height": 1435172, "applicationStatus": "succeeded", "spentComplexity": 0 } View: original | compacted Prev: 4L2KKp2Aeh8ti4Zs5eDJ9VSAubf695sEGwddcWNvhp3K Next: 55yabRpXNicqUoA7bgc87tvCiPNHB74A1WG11EHxhFCs Full:
Old | New | Differences | |
---|---|---|---|
1 | - | {-# STDLIB_VERSION 2 #-} | |
2 | - | {-# CONTENT_TYPE EXPRESSION #-} | |
3 | - | let this = extract(tx.sender) | |
4 | - | match tx { | |
5 | - | case t: TransferTransaction => | |
6 | - | let correctAnswer = extract(getBinary(this, "hashed answer")) | |
7 | - | let answer = keccak256(t.attachment) | |
8 | - | if ((correctAnswer == answer)) | |
9 | - | then !(isDefined(t.assetId)) | |
10 | - | else false | |
11 | - | case s: DataTransaction|SetScriptTransaction => | |
12 | - | sigVerify(s.bodyBytes, s.proofs[0], s.senderPublicKey) | |
13 | - | case _ => | |
14 | - | false | |
15 | - | } | |
1 | + | {-# STDLIB_VERSION 4 #-} | |
2 | + | {-# SCRIPT_TYPE ACCOUNT #-} | |
3 | + | {-# CONTENT_TYPE DAPP #-} | |
4 | + | let a = "incomplete" | |
5 | + | ||
6 | + | let b = "complete" | |
7 | + | ||
8 | + | let c = Address(base58'3MriZGLhQEu1jMDKDAbnsbh6TZuZueBAHSY') | |
9 | + | ||
10 | + | let d = unit | |
11 | + | ||
12 | + | let e = 1 | |
13 | + | ||
14 | + | let f = 0 | |
15 | + | ||
16 | + | let g = false | |
17 | + | ||
18 | + | let h = 1000 | |
19 | + | ||
20 | + | let i = 8 | |
21 | + | ||
22 | + | @Callable(j) | |
23 | + | func setTask (k) = { | |
24 | + | let l = ((toBase58String(value(j.callerPublicKey)) + ", task: ") + k) | |
25 | + | let m = ("NFT-token of task manager from" + toString(this)) | |
26 | + | let n = j.payments[0].amount | |
27 | + | let o = Issue(l, m, e, f, g) | |
28 | + | let p = toBase58String(calculateAssetId(o)) | |
29 | + | let q = ("taskName_" + p) | |
30 | + | let r = ("taskStatus_" + p) | |
31 | + | let s = ("taskAmount_" + p) | |
32 | + | if ((size(j.payments) > 1)) | |
33 | + | then throw("One attached assets expected") | |
34 | + | else if ((j.payments[0].assetId != d)) | |
35 | + | then throw("must be attached asset: WAVES") | |
36 | + | else if ((h > j.payments[0].amount)) | |
37 | + | then throw("amount must be greater than the minimum threshold: 0.0001 WAVES") | |
38 | + | else if ((j.caller == c)) | |
39 | + | then throw("method may not be called from the contract") | |
40 | + | else [StringEntry(q, k), StringEntry(r, a), o, ScriptTransfer(j.caller, e, fromBase58String(p)), StringEntry(r, a), ScriptTransfer(c, n, d), IntegerEntry(s, n)] | |
41 | + | } | |
42 | + | ||
43 | + | ||
44 | + | ||
45 | + | @Callable(j) | |
46 | + | func completeTask () = { | |
47 | + | let t = $Tuple2(j.payments[0].amount, value(j.payments[0].assetId)) | |
48 | + | let u = t._1 | |
49 | + | let v = t._2 | |
50 | + | let r = ("taskStatus_" + toBase58String(v)) | |
51 | + | let s = ("taskAmount_" + toBase58String(v)) | |
52 | + | let w = toBase58String(value(j.payments[0].assetId)) | |
53 | + | let x = valueOrErrorMessage(assetInfo(value(j.payments[0].assetId)), (("Asset " + w) + " doesn't exist")) | |
54 | + | let y = getIntegerValue(this, s) | |
55 | + | if ((size(j.payments) > 1)) | |
56 | + | then throw("One attached assets expected") | |
57 | + | else if ((j.caller == c)) | |
58 | + | then throw("method may not be called from the contract") | |
59 | + | else if (if (if (if ((value(j.payments[0].amount) == e)) | |
60 | + | then (value(x.decimals) == f) | |
61 | + | else false) | |
62 | + | then (value(x.reissuable) == g) | |
63 | + | else false) | |
64 | + | then isDefined(getString(this, r)) | |
65 | + | else false) | |
66 | + | then [StringEntry(r, b), Burn(v, u), ScriptTransfer(j.caller, y, d)] | |
67 | + | else throw("Attached asset is not expected") | |
68 | + | } | |
69 | + | ||
70 | + | ||
71 | + | ||
72 | + | @Callable(j) | |
73 | + | func removeTask () = { | |
74 | + | let z = $Tuple2(j.payments[0].amount, value(j.payments[0].assetId)) | |
75 | + | let u = z._1 | |
76 | + | let v = z._2 | |
77 | + | let s = ("taskAmount_" + toBase58String(v)) | |
78 | + | let r = ("taskStatus_" + toBase58String(v)) | |
79 | + | let q = ("taskName_" + toBase58String(v)) | |
80 | + | let w = toBase58String(value(j.payments[0].assetId)) | |
81 | + | let x = valueOrErrorMessage(assetInfo(value(j.payments[0].assetId)), (("Asset " + w) + " doesn't exist")) | |
82 | + | if ((size(j.payments) > 1)) | |
83 | + | then throw("One attached assets expected") | |
84 | + | else if ((j.caller == c)) | |
85 | + | then throw("method may not be called from the contract") | |
86 | + | else if (if (if ((value(x.decimals) == f)) | |
87 | + | then (value(x.reissuable) == g) | |
88 | + | else false) | |
89 | + | then isDefined(getString(this, r)) | |
90 | + | else false) | |
91 | + | then { | |
92 | + | let A = getString(this, r) | |
93 | + | if (if ((A == a)) | |
94 | + | then (value(j.payments[0].amount) == e) | |
95 | + | else false) | |
96 | + | then { | |
97 | + | let y = pow(getIntegerValue(this, s), 0, 1, 0, i, DOWN) | |
98 | + | [Burn(v, u), ScriptTransfer(j.caller, y, d), DeleteEntry(s), DeleteEntry(r), DeleteEntry(q)] | |
99 | + | } | |
100 | + | else if ((b == A)) | |
101 | + | then [DeleteEntry(s), DeleteEntry(r), DeleteEntry(q)] | |
102 | + | else throw("Task have a broken status") | |
103 | + | } | |
104 | + | else throw("Attached asset is not expected") | |
105 | + | } | |
106 | + | ||
107 | + |
github/deemru/w8io/026f985 25.15 ms ◑