tx · 6QSZ8tuWBu3ux4tWRxSGi3fZ6ajv3wR8tuE8fkF56ArW

3MuGfNhF98CNBCfthhoJEo6SYUv7zTgkK4J:  -0.01400000 Waves

2022.03.16 11:21 [1966279] smart account 3MuGfNhF98CNBCfthhoJEo6SYUv7zTgkK4J > SELF 0.00000000 Waves

{ "type": 13, "id": "6QSZ8tuWBu3ux4tWRxSGi3fZ6ajv3wR8tuE8fkF56ArW", "fee": 1400000, "feeAssetId": null, "timestamp": 1647418922456, "version": 1, "sender": "3MuGfNhF98CNBCfthhoJEo6SYUv7zTgkK4J", "senderPublicKey": "4DthuG3xjZV9WtZ34Y66AummdAr67wRzwWsVQL4y2bob", "proofs": [ "5UoQrt2x2WkAR91UiSHWk8NrvENYRvUJfYb6mDLQsVEAA6jea4WuES4Ap8z2MtiYvRdAnC5E9r4Sn4YR4WJFuv21" ], "script": "base64:AAIFAAAAAAAAACUIAhIHCgUICAEBCBIAEgMKAQESABIAEgMKAQgSAwoBCBIDCgEIAAAAMQAAAAAJc2VwYXJhdG9yAgAAAAJfXwAAAAAFTVVMVDYAAAAAAAAPQkAAAAAABU1VTFQ4AAAAAAAF9eEAAAAAAAZNVUxUWDYJAAE2AAAAAQUAAAAFTVVMVDYAAAAABk1VTFRYOAkAATYAAAABBQAAAAVNVUxUOAAAAAAHTVVMVFgxOAkAATYAAAABAA3gtrOnZAAAAAAAAApXQVZFU0lEU1RSAgAAAAVXQVZFUwAAAAAHV0FWRVNJRAkAAlkAAAABBQAAAApXQVZFU0lEU1RSAQAAAAxrZXlCb25kQXNzZXQAAAAAAgAAAA1ib25kX2Fzc2V0X2lkAQAAABlrZXlBdWN0aW9uQ29udHJhY3RBZGRyZXNzAAAAAAIAAAAQYXVjdGlvbl9jb250cmFjdAEAAAAaa2V5TmV1dHJpbm9Db250cmFjdEFkZHJlc3MAAAAAAgAAABslc19fbmV1dHJpbm9Db250cmFjdEFkZHJlc3MBAAAAFmtleU1hdGhDb250cmFjdEFkZHJlc3MAAAAAAgAAABAlc19fbWF0aENvbnRyYWN0AQAAABBrZXlNaW5Mb2NrQW1vdW50AAAAAAIAAAARJXNfX21pbkxvY2tBbW91bnQBAAAAC2tleUhhbGZMaWZlAAAAAAIAAAAMJXNfX2hhbGZMaWZlAQAAABZrZXlMb2NrUGFyYW1Vc2VyQW1vdW50AAAAAQAAAAt1c2VyQWRkcmVzcwkABLkAAAACCQAETAAAAAICAAAABiVzJXMlcwkABEwAAAACAgAAAAtwYXJhbUJ5VXNlcgkABEwAAAACCQAEJQAAAAEFAAAAC3VzZXJBZGRyZXNzCQAETAAAAAICAAAABmFtb3VudAUAAAADbmlsBQAAAAlzZXBhcmF0b3IBAAAAFmtleUxvY2tQYXJhbVN0YXJ0QmxvY2sAAAABAAAAC3VzZXJBZGRyZXNzCQAEuQAAAAIJAARMAAAAAgIAAAAGJXMlcyVzCQAETAAAAAICAAAAC3BhcmFtQnlVc2VyCQAETAAAAAIJAAQlAAAAAQUAAAALdXNlckFkZHJlc3MJAARMAAAAAgIAAAAFc3RhcnQFAAAAA25pbAUAAAAJc2VwYXJhdG9yAQAAABBrZXlIaXN0b3J5UmVjb3JkAAAAAwAAAAR0eXBlAAAAC3VzZXJBZGRyZXNzAAAABHR4SWQJAAS5AAAAAgkABEwAAAACAgAAAAglcyVzJXMlcwkABEwAAAACAgAAAAdoaXN0b3J5CQAETAAAAAIFAAAABHR5cGUJAARMAAAAAgkABCUAAAABBQAAAAt1c2VyQWRkcmVzcwkABEwAAAACCQACWAAAAAEFAAAABHR4SWQFAAAAA25pbAUAAAAJc2VwYXJhdG9yAQAAABdrZXlMb2NrUGFyYW1Ub3RhbEFtb3VudAAAAAAJAAS5AAAAAgkABEwAAAACAgAAAAQlcyVzCQAETAAAAAICAAAABXN0YXRzCQAETAAAAAICAAAAEWFjdGl2ZVRvdGFsTG9ja2VkBQAAAANuaWwFAAAACXNlcGFyYXRvcgEAAAASa2V5U3RhdHNMb2Nrc0NvdW50AAAAAAkABLkAAAACCQAETAAAAAICAAAABCVzJXMJAARMAAAAAgIAAAAFc3RhdHMJAARMAAAAAgIAAAAKbG9ja3NDb3VudAUAAAADbmlsBQAAAAlzZXBhcmF0b3IBAAAAEmtleVN0YXRzVXNlcnNDb3VudAAAAAAJAAS5AAAAAgkABEwAAAACAgAAAAQlcyVzCQAETAAAAAICAAAABXN0YXRzCQAETAAAAAICAAAAEGFjdGl2ZVVzZXJzQ291bnQFAAAAA25pbAUAAAAJc2VwYXJhdG9yAQAAAA1rZXlOZXh0UGVyaW9kAAAAAAIAAAAOJXNfX25leHRQZXJpb2QBAAAAGGtleVN1cHBvcnRlZFJld2FyZEFzc2V0cwAAAAACAAAAFXN1cHBvcnRlZFJld2FyZEFzc2V0cwEAAAARa2V5RGVwb3NpdE51bUxhc3QAAAAACQAEuQAAAAIJAARMAAAAAgIAAAAGJXMlcyVzCQAETAAAAAICAAAAA2RlcAkABEwAAAACAgAAAAdsYXN0TnVtBQAAAANuaWwFAAAACXNlcGFyYXRvcgEAAAAba2V5VXNlclJld2FyZEZyb21EZXBvc2l0TnVtAAAAAQAAAAt1c2VyQWRkcmVzcwkABLkAAAACCQAETAAAAAICAAAABiVzJXMlcwkABEwAAAACAgAAABF1c2VyUndkRnJvbURlcE51bQkABEwAAAACBQAAAAt1c2VyQWRkcmVzcwUAAAADbmlsBQAAAAlzZXBhcmF0b3IBAAAAFWtleVJld2FyZFBlck5zYnRTdW1BdAAAAAIAAAAKZGVwb3NpdE51bQAAAAN0a24JAAS5AAAAAgkABEwAAAACAgAAAAQlcyVkCQAETAAAAAICAAAAFXJ3ZFBlck5zYnRTdW1CeURlcE51bQkABEwAAAACCQABpAAAAAEFAAAACmRlcG9zaXROdW0JAARMAAAAAgUAAAADdGtuBQAAAANuaWwFAAAACXNlcGFyYXRvcgEAAAAJa2V5UmV3YXJkAAAAAgAAAAt1c2VyQWRkcmVzcwAAAAN0a24JAAS5AAAAAgkABEwAAAACAgAAAAYlcyVzJXMJAARMAAAAAgIAAAADcndkCQAETAAAAAIFAAAAC3VzZXJBZGRyZXNzCQAETAAAAAIFAAAAA3RrbgUAAAADbmlsBQAAAAlzZXBhcmF0b3IBAAAAF2tleU5vdERpc3RyaWJ1dGVkUmV3YXJkAAAAAQAAAAN0a24JAAS5AAAAAgkABEwAAAACAgAAAAQlcyVzCQAETAAAAAICAAAADm5vdERpc3RyaWJ1dGVkCQAETAAAAAIFAAAAA3RrbgUAAAADbmlsBQAAAAlzZXBhcmF0b3IBAAAABXRvWDE4AAAAAgAAAAdvcmlnVmFsAAAACG9yaWdNdWx0CQABPAAAAAMJAAE2AAAAAQUAAAAHb3JpZ1ZhbAUAAAAHTVVMVFgxOAUAAAAIb3JpZ011bHQBAAAADGdldEludE9yWmVybwAAAAEAAAADa2V5CQEAAAALdmFsdWVPckVsc2UAAAACCQAEGgAAAAIFAAAABHRoaXMFAAAAA2tleQAAAAAAAAAAAAEAAAAMZ2V0SW50T3JFbHNlAAAAAgAAAANrZXkAAAAKZGVmYXVsdFZhbAkBAAAAC3ZhbHVlT3JFbHNlAAAAAgkABBoAAAACBQAAAAR0aGlzBQAAAANrZXkFAAAACmRlZmF1bHRWYWwBAAAADGdldEludE9yRmFpbAAAAAEAAAADa2V5CQEAAAATdmFsdWVPckVycm9yTWVzc2FnZQAAAAIJAAQaAAAAAgUAAAAEdGhpcwUAAAADa2V5CQABLAAAAAIJAAEsAAAAAgIAAAAPTWFuZGF0b3J5IHRoaXMuBQAAAANrZXkCAAAADyBpcyBub3QgZGVmaW5lZAEAAAAMZ2V0U3RyT3JFbHNlAAAAAgAAAANrZXkAAAAKZGVmYXVsdFZhbAkBAAAAC3ZhbHVlT3JFbHNlAAAAAgkABB0AAAACBQAAAAR0aGlzBQAAAANrZXkFAAAACmRlZmF1bHRWYWwBAAAAD2dldFN0cmluZ09yRmFpbAAAAAEAAAADa2V5CQEAAAATdmFsdWVPckVycm9yTWVzc2FnZQAAAAIJAAQdAAAAAgUAAAAEdGhpcwUAAAADa2V5CQABLAAAAAIJAAEsAAAAAgIAAAAPTWFuZGF0b3J5IHRoaXMuBQAAAANrZXkCAAAADyBpcyBub3QgZGVmaW5lZAEAAAAPdG9BZGRyZXNzT3JGYWlsAAAAAQAAAAphZGRyZXNzU3RyCQEAAAATdmFsdWVPckVycm9yTWVzc2FnZQAAAAIJAAQmAAAAAQUAAAAKYWRkcmVzc1N0cgkAASwAAAACAgAAACFjb3VsZG4ndCBwYXJzZSBwYXNzZWQgYWRkcmVzc1N0cj0FAAAACmFkZHJlc3NTdHIBAAAAC3RvQXNzZXRWZWN0AAAAAQAAAAhhc3NldFN0cgMJAAAAAAAAAgUAAAAIYXNzZXRTdHIFAAAACldBVkVTSURTVFIFAAAABHVuaXQJAAJZAAAAAQUAAAAIYXNzZXRTdHIBAAAABWFzSW50AAAAAQAAAAN2YWwEAAAAByRtYXRjaDAFAAAAA3ZhbAMJAAABAAAAAgUAAAAHJG1hdGNoMAIAAAADSW50BAAAAAZ2YWxJbnQFAAAAByRtYXRjaDAFAAAABnZhbEludAkAAAIAAAABAgAAABVmYWlsIHRvIGNhc3QgaW50byBJbnQBAAAAE2Zvcm1hdEhpc3RvcnlSZWNvcmQAAAAEAAAACW9sZEFtb3VudAAAAAhvbGRTdGFydAAAAAluZXdBbW91bnQAAAAIbmV3U3RhcnQJAAS5AAAAAgkABEwAAAACAgAAAAwlZCVkJWQlZCVkJWQJAARMAAAAAgkAAaQAAAABCAUAAAAJbGFzdEJsb2NrAAAABmhlaWdodAkABEwAAAACCQABpAAAAAEIBQAAAAlsYXN0QmxvY2sAAAAJdGltZXN0YW1wCQAETAAAAAIJAAGkAAAAAQUAAAAJb2xkQW1vdW50CQAETAAAAAIJAAGkAAAAAQUAAAAIb2xkU3RhcnQJAARMAAAAAgkAAaQAAAABBQAAAAluZXdBbW91bnQJAARMAAAAAgkAAaQAAAABBQAAAAhuZXdTdGFydAUAAAADbmlsBQAAAAlzZXBhcmF0b3IBAAAAEkhpc3RvcnlSZWNvcmRFbnRyeQAAAAcAAAAEdHlwZQAAAAt1c2VyQWRkcmVzcwAAAAR0eElkAAAACW9sZEFtb3VudAAAAAhvbGRTdGFydAAAAAluZXdBbW91bnQAAAAIbmV3U3RhcnQJAQAAAAtTdHJpbmdFbnRyeQAAAAIJAQAAABBrZXlIaXN0b3J5UmVjb3JkAAAAAwUAAAAEdHlwZQUAAAALdXNlckFkZHJlc3MFAAAABHR4SWQJAQAAABNmb3JtYXRIaXN0b3J5UmVjb3JkAAAABAUAAAAJb2xkQW1vdW50BQAAAAhvbGRTdGFydAUAAAAJbmV3QW1vdW50BQAAAAhuZXdTdGFydAEAAAALU3RhdHNSZXN1bHQAAAADAAAADnRvdGFsTG9ja2VkSW5jAAAADGxvY2tDb3VudEluYwAAAA11c2Vyc0NvdW50SW5jBAAAAApsb2Nrc0NvdW50CQEAAAAMZ2V0SW50T3JaZXJvAAAAAQkBAAAAEmtleVN0YXRzTG9ja3NDb3VudAAAAAAEAAAACnVzZXJzQ291bnQJAQAAAAxnZXRJbnRPclplcm8AAAABCQEAAAASa2V5U3RhdHNVc2Vyc0NvdW50AAAAAAQAAAALdG90YWxBbW91bnQJAQAAAAxnZXRJbnRPclplcm8AAAABCQEAAAAXa2V5TG9ja1BhcmFtVG90YWxBbW91bnQAAAAABAAAAA50b3RhbEFtb3VudE5ldwkAAGQAAAACBQAAAAt0b3RhbEFtb3VudAUAAAAOdG90YWxMb2NrZWRJbmMJAAUVAAAAAwkABEwAAAACCQEAAAAMSW50ZWdlckVudHJ5AAAAAgkBAAAAEmtleVN0YXRzTG9ja3NDb3VudAAAAAAJAABkAAAAAgUAAAAKbG9ja3NDb3VudAUAAAAMbG9ja0NvdW50SW5jCQAETAAAAAIJAQAAAAxJbnRlZ2VyRW50cnkAAAACCQEAAAASa2V5U3RhdHNVc2Vyc0NvdW50AAAAAAkAAGQAAAACBQAAAAp1c2Vyc0NvdW50BQAAAA11c2Vyc0NvdW50SW5jCQAETAAAAAIJAQAAAAxJbnRlZ2VyRW50cnkAAAACCQEAAAAXa2V5TG9ja1BhcmFtVG90YWxBbW91bnQAAAAABQAAAA50b3RhbEFtb3VudE5ldwUAAAADbmlsBQAAAAt0b3RhbEFtb3VudAUAAAAOdG90YWxBbW91bnROZXcBAAAAD0xvY2tQYXJhbXNFbnRyeQAAAAMAAAALdXNlckFkZHJlc3MAAAAGYW1vdW50AAAABXN0YXJ0CQAETAAAAAIJAQAAAAxJbnRlZ2VyRW50cnkAAAACCQEAAAAWa2V5TG9ja1BhcmFtVXNlckFtb3VudAAAAAEFAAAAC3VzZXJBZGRyZXNzBQAAAAZhbW91bnQJAARMAAAAAgkBAAAADEludGVnZXJFbnRyeQAAAAIJAQAAABZrZXlMb2NrUGFyYW1TdGFydEJsb2NrAAAAAQUAAAALdXNlckFkZHJlc3MFAAAABXN0YXJ0BQAAAANuaWwBAAAAD2dldFBhcmFtc09yRmFpbAAAAAAEAAAAEG5ldXRyaW5vQ29udHJhY3QJAQAAABFAZXh0ck5hdGl2ZSgxMDYyKQAAAAEJAQAAAA9nZXRTdHJpbmdPckZhaWwAAAABCQEAAAAaa2V5TmV1dHJpbm9Db250cmFjdEFkZHJlc3MAAAAACQAFFgAAAAQJAAJZAAAAAQkBAAAAEUBleHRyTmF0aXZlKDEwNTMpAAAAAgUAAAAQbmV1dHJpbm9Db250cmFjdAkBAAAAGWtleUF1Y3Rpb25Db250cmFjdEFkZHJlc3MAAAAACQACWQAAAAEJAQAAABFAZXh0ck5hdGl2ZSgxMDUzKQAAAAIFAAAAEG5ldXRyaW5vQ29udHJhY3QJAQAAAAxrZXlCb25kQXNzZXQAAAAACQEAAAAMZ2V0SW50T3JGYWlsAAAAAQkBAAAAEGtleU1pbkxvY2tBbW91bnQAAAAACQEAAAAMZ2V0SW50T3JGYWlsAAAAAQkBAAAAC2tleUhhbGZMaWZlAAAAAAEAAAAMaXNBY3RpdmVVc2VyAAAAAQAAAAt1c2VyQWRkcmVzcwkAAGYAAAACCQEAAAAMZ2V0SW50T3JFbHNlAAAAAgkBAAAAFmtleUxvY2tQYXJhbVVzZXJBbW91bnQAAAABBQAAAAt1c2VyQWRkcmVzcwAAAAAAAAAAAAAAAAAAAAAAAAEAAAATZ2V0VXNlclBhcmFtc09yVW5pdAAAAAEAAAALdXNlckFkZHJlc3MDCQEAAAAMaXNBY3RpdmVVc2VyAAAAAQUAAAALdXNlckFkZHJlc3MJAAUVAAAAAwcJAQAAAAxnZXRJbnRPckZhaWwAAAABCQEAAAAWa2V5TG9ja1BhcmFtVXNlckFtb3VudAAAAAEFAAAAC3VzZXJBZGRyZXNzCQEAAAAMZ2V0SW50T3JGYWlsAAAAAQkBAAAAFmtleUxvY2tQYXJhbVN0YXJ0QmxvY2sAAAABBQAAAAt1c2VyQWRkcmVzcwUAAAAEdW5pdAEAAAATZ2V0VXNlclBhcmFtc09yRmFpbAAAAAEAAAALdXNlckFkZHJlc3MJAQAAABN2YWx1ZU9yRXJyb3JNZXNzYWdlAAAAAgkBAAAAE2dldFVzZXJQYXJhbXNPclVuaXQAAAABBQAAAAt1c2VyQWRkcmVzcwkAASwAAAACCQABLAAAAAICAAAABVVzZXIgCQAEJQAAAAEFAAAAC3VzZXJBZGRyZXNzAgAAAA8gaXMgbm90IGRlZmluZWQAAAAAEnN1cHBvcnRlZEFzc2V0c1N0cgkBAAAADGdldFN0ck9yRWxzZQAAAAIJAQAAABhrZXlTdXBwb3J0ZWRSZXdhcmRBc3NldHMAAAAAAgAAAAAAAAAAE3N1cHBvcnRlZEFzc2V0c0xpc3QJAAS1AAAAAgUAAAASc3VwcG9ydGVkQXNzZXRzU3RyAgAAAAFfAQAAAApjYWxjUmV3YXJkAAAABQAAAAt1c2VyQWRkcmVzcwAAAAdhc3NldElkAAAADXN0YWtlZEFtb3VudFgAAAAOZGVwb3NpdE51bVVzZXIAAAAOZGVwb3NpdE51bUxhc3QEAAAAF3Jld2FyZFBlck5zYnRTdW1MYXN0S0VZCQEAAAAVa2V5UmV3YXJkUGVyTnNidFN1bUF0AAAAAgUAAAAOZGVwb3NpdE51bUxhc3QFAAAAB2Fzc2V0SWQEAAAACnN1bUxhc3RYMTgJAAGnAAAAAQkBAAAADGdldFN0ck9yRWxzZQAAAAIJAQAAABVrZXlSZXdhcmRQZXJOc2J0U3VtQXQAAAACBQAAAA5kZXBvc2l0TnVtTGFzdAUAAAAHYXNzZXRJZAIAAAABMAQAAAAKc3VtVXNlclgxOAkAAacAAAABCQEAAAAMZ2V0U3RyT3JFbHNlAAAAAgkBAAAAFWtleVJld2FyZFBlck5zYnRTdW1BdAAAAAIFAAAADmRlcG9zaXROdW1Vc2VyBQAAAAdhc3NldElkAgAAAAEwBAAAABFyZXdhcmREeW5hbWljUGFydAkAAaAAAAABCQABPAAAAAMJAAE4AAAAAgUAAAAKc3VtTGFzdFgxOAUAAAAKc3VtVXNlclgxOAUAAAANc3Rha2VkQW1vdW50WAUAAAAHTVVMVFgxOAQAAAATcmV3YXJkQ2FjaGVkUGFydEtFWQkBAAAACWtleVJld2FyZAAAAAIFAAAAC3VzZXJBZGRyZXNzBQAAAAdhc3NldElkBAAAABByZXdhcmRDYWNoZWRQYXJ0CQEAAAAMZ2V0SW50T3JFbHNlAAAAAgUAAAATcmV3YXJkQ2FjaGVkUGFydEtFWQAAAAAAAAAAAAkABRYAAAAECQAAZAAAAAIFAAAAEHJld2FyZENhY2hlZFBhcnQFAAAAEXJld2FyZER5bmFtaWNQYXJ0BQAAABByZXdhcmRDYWNoZWRQYXJ0BQAAABFyZXdhcmREeW5hbWljUGFydAUAAAATcmV3YXJkQ2FjaGVkUGFydEtFWQEAAAANUmV3YXJkRW50cmllcwAAAAMAAAAJaXNOZXdVc2VyAAAAC3VzZXJBZGRyZXNzAAAADHN0YWtlZEFtb3VudAQAAAANc3Rha2VkQW1vdW50WAkAATYAAAABBQAAAAxzdGFrZWRBbW91bnQEAAAAG3VzZXJSZXdhcmRGcm9tRGVwb3NpdE51bUtFWQkBAAAAG2tleVVzZXJSZXdhcmRGcm9tRGVwb3NpdE51bQAAAAEFAAAAC3VzZXJBZGRyZXNzBAAAAA5kZXBvc2l0TnVtVXNlcgkBAAAADGdldEludE9yRWxzZQAAAAIFAAAAG3VzZXJSZXdhcmRGcm9tRGVwb3NpdE51bUtFWQD//////////wQAAAAOZGVwb3NpdE51bUxhc3QJAQAAAAxnZXRJbnRPckVsc2UAAAACCQEAAAARa2V5RGVwb3NpdE51bUxhc3QAAAAAAP//////////CgEAAAAbZm9yRWFjaEFzc2V0Q2FjaGVVc2VyUmV3YXJkAAAAAgAAAAVhY2N1bQAAAAVhc3NldAQAAAALJHQwNjc5ODY5MzMJAQAAAApjYWxjUmV3YXJkAAAABQUAAAAL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AAEFAAAADmRlcG9zaXROdW1Vc2VyAQAAACJJbmNyZW1lbnROb3REaXN0cmlidXRlZFJld2FyZEVudHJ5AAAAAgAAAAN0a24AAAAJYW1vdW50SW5jBAAAABdub3REaXN0cmlidXRlZFJld2FyZEtFWQkBAAAAF2tleU5vdERpc3RyaWJ1dGVkUmV3YXJkAAAAAQUAAAADdGtuBAAAABRub3REaXN0cmlidXRlZFJld2FyZAkBAAAADGdldEludE9yRWxzZQAAAAIFAAAAF25vdERpc3RyaWJ1dGVkUmV3YXJkS0VZAAAAAAAAAAAACQAETAAAAAIJAQAAAAxJbnRlZ2VyRW50cnkAAAACBQAAABdub3REaXN0cmlidXRlZFJld2FyZEtFWQkAAGQAAAACBQAAABRub3REaXN0cmlidXRlZFJld2FyZAUAAAAJYW1vdW50SW5jBQAAAANuaWwAAAAIAAAAAWkBAAAAC2NvbnN0cnVjdG9yAAAABQAAABduZXV0cmlub0NvbnRyYWN0QWRkcmVzcwAAABNtYXRoQ29udHJhY3RBZGRyZXNzAAAADW1pbkxvY2tBbW91bnQAAAAIaGFsZkxpZmUAAAAVc3VwcG9ydGVkUmV3YXJkQXNzZXRzAwkBAAAAAiE9AAAAAggFAAAAAWkAAAAGY2FsbGVyBQAAAAR0aGlzCQAAAgAAAAECAAAAEVBlcm1pc3Npb24gZGVuaWVkCQAETAAAAAIJAQAAAAtTdHJpbmdFbnRyeQAAAAIJAQAAABprZXlOZXV0cmlub0NvbnRyYWN0QWRkcmVzcwAAAAAFAAAAF25ldXRyaW5vQ29udHJhY3RBZGRyZXNzCQAETAAAAAIJAQAAAAtTdHJpbmdFbnRyeQAAAAIJAQAAABZrZXlNYXRoQ29udHJhY3RBZGRyZXNzAAAAAAUAAAATbWF0aENvbnRyYWN0QWRkcmVzcwkABEwAAAACCQEAAAAMSW50ZWdlckVudHJ5AAAAAgkBAAAAEGtleU1pbkxvY2tBbW91bnQAAAAABQAAAA1taW5Mb2NrQW1vdW50CQAETAAAAAIJAQAAAAxJbnRlZ2VyRW50cnkAAAACCQEAAAALa2V5SGFsZkxpZmUAAAAABQAAAAhoYWxmTGlmZQkABEwAAAACCQEAAAALU3RyaW5nRW50cnkAAAACCQEAAAAYa2V5U3VwcG9ydGVkUmV3YXJkQXNzZXRzAAAAAAUAAAAVc3VwcG9ydGVkUmV3YXJkQXNzZXRzBQAAAANuaWwAAAABaQEAAAAFc3Rha2UAAAAABAAAAA0kdDAxMDgwMzEwODgyCQEAAAAPZ2V0UGFyYW1zT3JGYWlsAAAAAAQAAAAPYXVjdGlvbkNvbnRyYWN0CAUAAAANJHQwMTA4MDMxMDg4MgAAAAJfMQQAAAALYm9uZEFzc2V0SWQIBQAAAA0kdDAxMDgwMzEwODgyAAAAAl8yBAAAAA1taW5Mb2NrQW1vdW50CAUAAAANJHQwMTA4MDMxMDg4MgAAAAJfMwQAAAAIaGFsZkxpZmUIBQAAAA0kdDAxMDgwMzEwODgyAAAAAl80AwkBAAAAAiE9AAAAAgkAAZAAAAABCAUAAAABaQAAAAhwYXltZW50cwAAAAAAAAAAAQkAAAIAAAABAgAAABVJbnZhbGlkIHBheW1lbnRzIHNpemUEAAAAB3BheW1lbnQJAAGRAAAAAggFAAAAAWkAAAAIcGF5bWVudHMAAAAAAAAAAAAEAAAABmFtb3VudAgFAAAAB3BheW1lbnQAAAAGYW1vdW50BAAAABNpbnZhbGlkQXNzZXRNZXNzYWdlCQABLAAAAAIJAAEsAAAAAgIAAAAPSW52YWxpZCBhc3NldC4gCQACWAAAAAEFAAAAC2JvbmRBc3NldElkAgAAAAwgaXMgZXhwZWN0ZWQEAAAAB2Fzc2V0SWQJAQAAABN2YWx1ZU9yRXJyb3JNZXNzYWdlAAAAAggFAAAAB3BheW1lbnQAAAAHYXNzZXRJZAUAAAATaW52YWxpZEFzc2V0TWVzc2FnZQMJAQAAAAIhPQAAAAIFAAAAB2Fzc2V0SWQFAAAAC2JvbmRBc3NldElkCQAAAgAAAAEFAAAAE2ludmFsaWRBc3NldE1lc3NhZ2UEAAAAC3VzZXJBZGRyZXNzCAUAAAABaQAAAAZjYWxsZXIEAAAADnVzZXJBZGRyZXNzU3RyCQAEJQAAAAEIBQAAAAFpAAAABmNhbGxlcgQAAAANJHQwMTE0MjQxMTUzMQkBAAAAC3ZhbHVlT3JFbHNlAAAAAgkBAAAAE2dldFVzZXJQYXJhbXNPclVuaXQAAAABBQAAAAt1c2VyQWRkcmVzcwkABRUAAAADBgAAAAAAAAAAAAD//////////wQAAAAJaXNOZXdVc2VyCAUAAAANJHQwMTE0MjQxMTUzMQAAAAJfMQQAAAAKbG9ja0Ftb3VudAgFAAAADSR0MDExNDI0MTE1MzEAAAACXzIEAAAAD2xvY2tTdGFydEhlaWdodAgFAAAADSR0MDExNDI0MTE1MzEAAAACXzMEAAAADG1lcmdlZEFtb3VudAMFAAAACWlzTmV3VXNlcgUAAAAGYW1vdW50CQAAZAAAAAIFAAAABmFtb3VudAUAAAAKbG9ja0Ftb3VudAQAAAARbWVyZ2VkU3RhcnRIZWlnaHQDBQAAAAlpc05ld1VzZXIFAAAABmhlaWdodAQAAAAMbWF0aENvbnRyYWN0CQEAAAARQGV4dHJOYXRpdmUoMTA2MikAAAABCQEAAAAPZ2V0U3RyaW5nT3JGYWlsAAAAAQkBAAAAFmtleU1hdGhDb250cmFjdEFkZHJlc3MAAAAACQEAAAAFYXNJbnQAAAABCQAD/AAAAAQFAAAADG1hdGhDb250cmFjdAIAAAATbWVyZ2VTdGFrZXNSRUFET05MWQkABEwAAAACBQAAAAZhbW91bnQJAARMAAAAAgUAAAAGaGVpZ2h0CQAETAAAAAIFAAAACmxvY2tBbW91bnQJAARMAAAAAgUAAAAPbG9ja1N0YXJ0SGVpZ2h0CQAETAAAAAIFAAAACGhhbGZMaWZlBQAAAANuaWwFAAAAA25pbAMJAABmAAAAAgUAAAANbWluTG9ja0Ftb3VudAUAAAAMbWVyZ2VkQW1vdW50CQAAAgAAAAEJAAEsAAAAAgIAAAATTWluIGxvY2sgYW1vdW50IGlzIAkAAaQAAAABBQAAAA1taW5Mb2NrQW1vdW50BAAAAA0kdDAxMTk4MTEyMDgzCQEAAAALU3RhdHNSZXN1bHQAAAADBQAAAAZhbW91bnQAAAAAAAAAAAEDBQAAAAlpc05ld1VzZXIAAAAAAAAAAAEAAAAAAAAAAAAEAAAADHN0YXRzRW50cmllcwgFAAAADSR0MDExOTgxMTIwODMAAAACXzEEAAAAC3RvdGFsU3Rha2VkCAUAAAANJHQwMTE5ODExMjA4MwAAAAJfMgQAAAAOdG90YWxTdGFrZWROZXcIBQAAAA0kdDAxMTk4MTEyMDgzAAAAAl8zBAAAAA5kZXBvc2l0TnVtTGFzdAkBAAAADGdldEludE9yRWxzZQAAAAIJAQAAABFrZXlEZXBvc2l0TnVtTGFzdAAAAAAA//////////8JAARNAAAAAgkABE4AAAACCQAETgAAAAIJAARMAAAAAgkBAAAAEkhpc3RvcnlSZWNvcmRFbnRyeQAAAAcCAAAABXN0YWtlBQAAAAt1c2VyQWRkcmVzcwgFAAAAAWkAAAANdHJhbnNhY3Rpb25JZAUAAAAKbG9ja0Ftb3VudAUAAAAPbG9ja1N0YXJ0SGVpZ2h0BQAAAAxtZXJnZWRBbW91bnQFAAAAEW1lcmdlZFN0YXJ0SGVpZ2h0BQAAAANuaWwJAQAAAA9Mb2NrUGFyYW1zRW50cnkAAAADBQAAAAt1c2VyQWRkcmVzcwUAAAAMbWVyZ2VkQW1vdW50BQAAABFtZXJnZWRTdGFydEhlaWdodAUAAAAMc3RhdHNFbnRyaWVzCQEAAAAMSW50ZWdlckVudHJ5AAAAAgkBAAAAG2tleVVzZXJSZXdhcmRGcm9tRGVwb3NpdE51bQAAAAEFAAAADnVzZXJBZGRyZXNzU3RyCQEAAAAMZ2V0SW50T3JFbHNlAAAAAgkBAAAAEWtleURlcG9zaXROdW1MYXN0AAAAAAD//////////wAAAAFpAQAAAAd1bnN0YWtlAAAAAQAAAAZhbW91bnQEAAAAC3VzZXJBZGRyZXNzCAUAAAABaQAAAAZjYWxsZXIEAAAADnVzZXJBZGRyZXNzU3RyCQAEJQAAAAEFAAAAC3VzZXJBZGRyZXNzBAAAAA0kdDAxMjU4ODEyNjY2CQEAAAAPZ2V0UGFyYW1zT3JGYWlsAAAAAAQAAAAOYXVjdGlvbkFkZHJlc3MIBQAAAA0kdDAxMjU4ODEyNjY2AAAAAl8xBAAAAAtib25kQXNzZXRJZAgFAAAADSR0MDEyNTg4MTI2NjYAAAACXzIEAAAADW1pbkxvY2tBbW91bnQIBQAAAA0kdDAxMjU4ODEyNjY2AAAAAl8zBAAAAAhoYWxmTGlmZQgFAAAADSR0MDEyNTg4MTI2NjYAAAACXzQEAAAADSR0MDEyNjY5MTI3NDMJAQAAABNnZXRVc2VyUGFyYW1zT3JGYWlsAAAAAQUAAAALdXNlckFkZHJlc3MEAAAACWlzTmV3VXNlcggFAAAADSR0MDEyNjY5MTI3NDMAAAACXzEEAAAACmxvY2tBbW91bnQIBQAAAA0kdDAxMjY2OTEyNzQzAAAAAl8yBAAAAAlsb2NrU3RhcnQIBQAAAA0kdDAxMjY2OTEyNzQzAAAAAl8zAwkAAGcAAAACAAAAAAAAAAAABQAAAApsb2NrQW1vdW50CQAAAgAAAAECAAAAEk5vdGhpbmcgdG8gdW5zdGFrZQMJAABmAAAAAgUAAAAGYW1vdW50BQAAAApsb2NrQW1vdW50CQAAAgAAAAEJAAEsAAAAAgkAASwAAAACCQABLAAAAAICAAAAClJlcXVlc3RlZCAJAAGkAAAAAQUAAAAGYW1vdW50AgAAABIsIGJ1dCBzdGFrZWQgb25seSAJAAGkAAAAAQUAAAAKbG9ja0Ftb3VudAQAAAAMbWF0aENvbnRyYWN0CQEAAAARQGV4dHJOYXRpdmUoMTA2MikAAAABCQEAAAAPZ2V0U3RyaW5nT3JGYWlsAAAAAQkBAAAAFmtleU1hdGhDb250cmFjdEFkZHJlc3MAAAAABAAAAA9jb21pc3Npb25BbW91bnQJAQAAAAVhc0ludAAAAAEJAAP8AAAABAUAAAAMbWF0aENvbnRyYWN0AgAAACFnZXRVbnN0YWtlQ29taXNzaW9uQW1vdW50UkVBRE9OTFkJAARMAAAAAgUAAAAGYW1vdW50CQAETAAAAAIFAAAACWxvY2tTdGFydAkABEwAAAACBQAAAAhoYWxmTGlmZQUAAAADbmlsBQAAAANuaWwEAAAADSR0MDEzMTQ0MTMyOTgJAQAAAAtTdGF0c1Jlc3VsdAAAAAMJAQAAAAEtAAAAAQUAAAAGYW1vdW50AwkAAAAAAAACBQAAAAZhbW91bnQFAAAACmxvY2tBbW91bnQA//////////8AAAAAAAAAAAADCQAAAAAAAAIFAAAABmFtb3VudAUAAAAKbG9ja0Ftb3VudAD//////////wAAAAAAAAAAAAQAAAAMc3RhdHNFbnRyaWVzCAUAAAANJHQwMTMxNDQxMzI5OAAAAAJfMQQAAAALdG90YWxTdGFrZWQIBQAAAA0kdDAxMzE0NDEzMjk4AAAAAl8yBAAAAA50b3RhbFN0YWtlZE5ldwgFAAAADSR0MDEzMTQ0MTMyOTgAAAACXzMJAAROAAAAAgkABE4AAAACCQAETgAAAAIJAARMAAAAAgkBAAAADlNjcmlwdFRyYW5zZmVyAAAAAwUAAAALdXNlckFkZHJlc3MJAABlAAAAAgUAAAAGYW1vdW50BQAAAA9jb21pc3Npb25BbW91bnQFAAAAC2JvbmRBc3NldElkCQAETAAAAAIJAQAAAA5TY3JpcHRUcmFuc2ZlcgAAAAMJAQAAAAdBZGRyZXNzAAAAAQUAAAAOYXVjdGlvbkFkZHJlc3MFAAAAD2NvbWlzc2lvbkFtb3VudAUAAAALYm9uZEFzc2V0SWQJAARMAAAAAgkBAAAAEkhpc3RvcnlSZWNvcmRFbnRyeQAAAAcCAAAAB3Vuc3Rha2UFAAAAC3VzZXJBZGRyZXNzCAUAAAABaQAAAA10cmFuc2FjdGlvbklkBQAAAApsb2NrQW1vdW50BQAAAAlsb2NrU3RhcnQJAABlAAAAAgUAAAAKbG9ja0Ftb3VudAUAAAAGYW1vdW50BQAAAAlsb2NrU3RhcnQFAAAAA25pbAkBAAAADVJld2FyZEVudHJpZXMAAAADBwUAAAAOdXNlckFkZHJlc3NTdHIFAAAACmxvY2tBbW91bnQJAQAAAA9Mb2NrUGFyYW1zRW50cnkAAAADBQAAAAt1c2VyQWRkcmVzcwkAAGUAAAACBQAAAApsb2NrQW1vdW50BQAAAAZhbW91bnQFAAAACWxvY2tTdGFydAUAAAAMc3RhdHNFbnRyaWVzAAAAAWkBAAAAB2RlcG9zaXQAAAAAAwkBAAAAAiE9AAAAAgkAAZAAAAABCAUAAAABaQAAAAhwYXltZW50cwAAAAAAAAAAAQkAAAIAAAABAgAAAB9leGFjdCAxIHBheW1lbnQgaXMgYWxsb3dlZCBvbmx5BAAAAANwbXQJAAGRAAAAAggFAAAAAWkAAAAIcGF5bWVudHMAAAAAAAAAAAAEAAAABmFtb3VudAgFAAAAA3BtdAAAAAZhbW91bnQEAAAACnBtdEFzc2V0SWQJAQAAAAt2YWx1ZU9yRWxzZQAAAAIIBQAAAANwbXQAAAAHYXNzZXRJZAUAAAAHV0FWRVNJRAQAAAANcG10QXNzZXRJZFN0cgkAAlgAAAABBQAAAApwbXRBc3NldElkBAAAAAhwbXRNdWx0WAMJAAAAAAAAAgUAAAAKcG10QXNzZXRJZAUAAAAHV0FWRVNJRAUAAAAGTVVMVFg4BQAAAAZNVUxUWDYEAAAAB2Ftb3VudFgJAAE2AAAAAQUAAAAGYW1vdW50BAAAAAt0b3RhbFN0YWtlZAkBAAAADGdldEludE9yRWxzZQAAAAIJAQAAABdrZXlMb2NrUGFyYW1Ub3RhbEFtb3VudAAAAAAAAAAAAAAAAAAEAAAADHRvdGFsU3Rha2VkWAkAATYAAAABBQAAAAt0b3RhbFN0YWtlZAMJAABmAAAAAgAAAAAAAAAAAAUAAAALdG90YWxTdGFrZWQJAAACAAAAAQIAAAAbVE9ETzogY2FzZSBpcyBub3Qgc3VwcG9ydGVkAwkAAAAAAAACBQAAAAt0b3RhbFN0YWtlZAAAAAAAAAAAAAkBAAAAIkluY3JlbWVudE5vdERpc3RyaWJ1dGVkUmV3YXJkRW50cnkAAAACBQAAAA1wbXRBc3NldElkU3RyBQAAAAZhbW91bnQEAAAAEHJld2FyZFBlck5zYnRYMTgJAAE8AAAAAwUAAAAHYW1vdW50WAUAAAAHTVVMVFgxOAUAAAAMdG90YWxTdGFrZWRYBAAAABFkZXBvc2l0TnVtTGFzdEtFWQkBAAAAEWtleURlcG9zaXROdW1MYXN0AAAAAAQAAAAOZGVwb3NpdE51bUxhc3QJAQAAAAxnZXRJbnRPckVsc2UAAAACBQAAABFkZXBvc2l0TnVtTGFzdEtFWQD//////////wQAAAANZGVwb3NpdE51bU5ldwkAAGQAAAACBQAAAA5kZXBvc2l0TnVtTGFzdAAAAAAAAAAAAQMJAQAAAAEhAAAAAQkBAAAACGNvbnRhaW5zAAAAAgUAAAASc3VwcG9ydGVkQXNzZXRzU3RyBQAAAA1wbXRBc3NldElkU3RyCQAAAgAAAAEJAAEsAAAAAgkAASwAAAACBQAAABJzdXBwb3J0ZWRBc3NldHNTdHICAAAAESBkb2Vzbid0IGNvbnRhaW4gBQAAAA1wbXRBc3NldElkU3RyCgEAAAAXcmVmcmVzaFJld2FyZFBlck5zYnRTVU0AAAACAAAABWFjY3VtAAAACW5leHRBc3NldAQAAAAWcmV3YXJkUGVyTnNidFN1bU5ld0tFWQkBAAAAFWtleVJld2FyZFBlck5zYnRTdW1BdAAAAAIFAAAADWRlcG9zaXROdW1OZXcFAAAACW5leHRBc3NldAQAAAAKc3VtTGFzdFN0cgkBAAAADGdldFN0ck9yRWxzZQAAAAIJAQAAABVrZXlSZXdhcmRQZXJOc2J0U3VtQXQAAAACBQAAAA5kZXBvc2l0TnVtTGFzdAUAAAAJbmV4dEFzc2V0AgAAAAEwCQAETQAAAAIFAAAABWFjY3VtAwkAAAAAAAACBQAAAAluZXh0QXNzZXQFAAAADXBtdEFzc2V0SWRTdHIJAQAAAAtTdHJpbmdFbnRyeQAAAAIFAAAAFnJld2FyZFBlck5zYnRTdW1OZXdLRVkJAAGmAAAAAQkAATcAAAACCQABpwAAAAEFAAAACnN1bUxhc3RTdHIFAAAAEHJld2FyZFBlck5zYnRYMTgJAQAAAAtTdHJpbmdFbnRyeQAAAAIFAAAAFnJld2FyZFBlck5zYnRTdW1OZXdLRVkFAAAACnN1bUxhc3RTdHIJAARNAAAAAgoAAAAAAiRsBQAAABNzdXBwb3J0ZWRBc3NldHNMaXN0CgAAAAACJHMJAAGQAAAAAQUAAAACJGwKAAAAAAUkYWNjMAUAAAADbmlsCgEAAAAFJGYwXzEAAAACAAAAAiRhAAAAAiRpAwkAAGcAAAACBQAAAAIkaQUAAAACJHMFAAAAAiRhCQEAAAAXcmVmcmVzaFJld2FyZFBlck5zYnRTVU0AAAACBQAAAAIkYQkAAZEAAAACBQAAAAIkbAUAAAACJGkKAQAAAAUkZjBfMgAAAAIAAAACJGEAAAACJGkDCQAAZwAAAAIFAAAAAiRpBQAAAAIkcwUAAAACJGEJAAACAAAAAQIAAAAUTGlzdCBzaXplIGV4Y2VlZHMgMTAJAQAAAAUkZjBfMgAAAAIJAQAAAAUkZjBfMQAAAAIJAQAAAAUkZjBfMQAAAAIJAQAAAAUkZjBfMQAAAAIJAQAAAAUkZjBfMQAAAAIJAQAAAAUkZjBfMQAAAAIJAQAAAAUkZjBfMQAAAAIJAQAAAAUkZjBfMQAAAAIJAQAAAAUkZjBfMQAAAAIJAQAAAAUkZjBfMQAAAAIJAQAAAAUkZjBfMQAAAAIFAAAABSRhY2MwAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAACAAAAAAAAAAADAAAAAAAAAAAEAAAAAAAAAAAFAAAAAAAAAAAGAAAAAAAAAAAHAAAAAAAAAAAIAAAAAAAAAAAJAAAAAAAAAAAKCQEAAAAMSW50ZWdlckVudHJ5AAAAAgUAAAARZGVwb3NpdE51bUxhc3RLRVkFAAAADWRlcG9zaXROdW1OZXcAAAABaQEAAAAMY2xhaW1SZXdhcmRzAAAAAAQAAAALdXNlckFkZHJlc3MIBQAAAAFpAAAABmNhbGxlcgQAAAAOdXNlckFkZHJlc3NTdHIJAAQlAAAAAQUAAAALdXNlckFkZHJlc3MEAAAADSR0MDE1NDgwMTU1ODUJAQAAAAt2YWx1ZU9yRWxzZQAAAAIJAQAAABNnZXRVc2VyUGFyYW1zT3JVbml0AAAAAQUAAAALdXNlckFkZHJlc3MJAAUVAAAAAwYAAAAAAAAAAAAAAAAAAAAAAAAEAAAACWlzTmV3VXNlcggFAAAADSR0MDE1NDgwMTU1ODUAAAACXzEEAAAADHN0YWtlZEFtb3VudAgFAAAADSR0MDE1NDgwMTU1ODUAAAACXzIEAAAADHN0YWtpbmdTdGFydAgFAAAADSR0MDE1NDgwMTU1ODUAAAACXzMEAAAADXN0YWtlZEFtb3VudFgJAAE2AAAAAQUAAAAMc3Rha2VkQW1vdW50BAAAABt1c2VyUmV3YXJkRnJvbURlcG9zaXROdW1LRVkJAQAAABtrZXlVc2VyUmV3YXJkRnJvbURlcG9zaXROdW0AAAABBQAAAA51c2VyQWRkcmVzc1N0cgQAAAAOZGVwb3NpdE51bVVzZXIJAQAAAAxnZXRJbnRPckVsc2UAAAACBQAAABt1c2VyUmV3YXJkRnJvbURlcG9zaXROdW1LRVkA//////////8EAAAADmRlcG9zaXROdW1MYXN0CQEAAAAMZ2V0SW50T3JFbHNlAAAAAgkBAAAAEWtleURlcG9zaXROdW1MYXN0AAAAAAD//////////woBAAAAH2ZvckVhY2hBc3NldENhbGNVbmNsYWltZWRSZXdhcmQAAAACAAAABWFjY3VtAAAABWFzc2V0BAAAAA0kdDAxNTk0NjE2MDg0CQEAAAAKY2FsY1Jld2FyZAAAAAUFAAAADnVzZXJBZGRyZXNzU3RyBQAAAAVhc3NldAUAAAANc3Rha2VkQW1vdW50WAUAAAAOZGVwb3NpdE51bVVzZXIFAAAADmRlcG9zaXROdW1MYXN0BAAAAAtyZXdhcmRUb3RhbAgFAAAADSR0MDE1OTQ2MTYwODQAAAACXzEEAAAABmNhY2hl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CgQAAAALdXNlckFkZHJlc3MJAQAAABFAZXh0ck5hdGl2ZSgxMDYyKQAAAAEFAAAADnVzZXJBZGRyZXNzU3RyBAAAAA0kdDAxNjg0ODE2OTUzCQEAAAALdmFsdWVPckVsc2UAAAACCQEAAAATZ2V0VXNlclBhcmFtc09yVW5pdAAAAAEFAAAAC3VzZXJBZGRyZXNzCQAFFQAAAAMGAAAAAAAAAAAAAAAAAAAAAAAABAAAAAlpc05ld1VzZXIIBQAAAA0kdDAxNjg0ODE2OTUzAAAAAl8xBAAAAAxzdGFrZWRBbW91bnQIBQAAAA0kdDAxNjg0ODE2OTUzAAAAAl8yBAAAAAxzdGFraW5nU3RhcnQIBQAAAA0kdDAxNjg0ODE2OTUzAAAAAl8zBAAAAA1zdGFrZWRBbW91bnRYCQABNgAAAAEFAAAADHN0YWtlZEFtb3VudAQAAAAbdXNlclJld2FyZEZyb21EZXBvc2l0TnVtS0VZCQEAAAAba2V5VXNlclJld2FyZEZyb21EZXBvc2l0TnVtAAAAAQUAAAAOdXNlckFkZHJlc3NTdHIEAAAADmRlcG9zaXROdW1Vc2VyCQEAAAAMZ2V0SW50T3JFbHNlAAAAAgUAAAAbdXNlclJld2FyZEZyb21EZXBvc2l0TnVtS0VZAP//////////BAAAAA5kZXBvc2l0TnVtTGFzdAkBAAAADGdldEludE9yRWxzZQAAAAIJAQAAABFrZXlEZXBvc2l0TnVtTGFzdAAAAAAA//////////8KAQAAAB9mb3JFYWNoQXNzZXRDYWxjVW5jbGFpbWVkUmV3YXJkAAAAAgAAAAVhY2N1bQAAAAVhc3NldAQAAAANJHQwMTcyOTkxNzQzNwkBAAAACmNhbGNSZXdhcmQAAAAFBQAAAA51c2VyQWRkcmVzc1N0cgUAAAAFYXNzZXQFAAAADXN0YWtlZEFtb3VudFgFAAAADmRlcG9zaXROdW1Vc2VyBQAAAA5kZXBvc2l0TnVtTGFzdAQAAAALcmV3YXJkVG90YWwIBQAAAA0kdDAxNzI5OTE3NDM3AAAAAl8xBAAAAAZjYWNoZWQIBQAAAA0kdDAxNzI5OTE3NDM3AAAAAl8yBAAAAAdkeW5hbWljCAUAAAANJHQwMTcyOTkxNzQzNwAAAAJfMwQAAAATcmV3YXJkQ2FjaGVk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", "chainId": 84, "height": 1966279, "applicationStatus": "succeeded", "spentComplexity": 0 } View: original | compacted Prev: 2gVDhZ8weEKXjsrpqPRbLJn4y7eYuRoaJZCE9TAb6gLA Next: 4SajAxuPevgLaVq1n46WFcN2jw5jxd6ew1AEQWtWtyTS Diff:
OldNewDifferences
33 {-# CONTENT_TYPE DAPP #-}
44 let separator = "__"
55
6+let MULT6 = 1000000
7+
68 let MULT8 = 100000000
9+
10+let MULTX6 = toBigInt(MULT6)
11+
12+let MULTX8 = toBigInt(MULT8)
13+
14+let MULTX18 = toBigInt(1000000000000000000)
15+
16+let WAVESIDSTR = "WAVES"
17+
18+let WAVESID = fromBase58String(WAVESIDSTR)
719
820 func keyBondAsset () = "bond_asset_id"
921
4456 func keyNextPeriod () = "%s__nextPeriod"
4557
4658
59+func keySupportedRewardAssets () = "supportedRewardAssets"
60+
61+
62+func keyDepositNumLast () = makeString(["%s%s%s", "dep", "lastNum"], separator)
63+
64+
65+func keyUserRewardFromDepositNum (userAddress) = makeString(["%s%s%s", "userRwdFromDepNum", userAddress], separator)
66+
67+
68+func keyRewardPerNsbtSumAt (depositNum,tkn) = makeString(["%s%d", "rwdPerNsbtSumByDepNum", toString(depositNum), tkn], separator)
69+
70+
71+func keyReward (userAddress,tkn) = makeString(["%s%s%s", "rwd", userAddress, tkn], separator)
72+
73+
74+func keyNotDistributedReward (tkn) = makeString(["%s%s", "notDistributed", tkn], separator)
75+
76+
77+func toX18 (origVal,origMult) = fraction(toBigInt(origVal), MULTX18, origMult)
78+
79+
4780 func getIntOrZero (key) = valueOrElse(getInteger(this, key), 0)
81+
82+
83+func getIntOrElse (key,defaultVal) = valueOrElse(getInteger(this, key), defaultVal)
4884
4985
5086 func getIntOrFail (key) = valueOrErrorMessage(getInteger(this, key), (("Mandatory this." + key) + " is not defined"))
5187
5288
89+func getStrOrElse (key,defaultVal) = valueOrElse(getString(this, key), defaultVal)
90+
91+
5392 func getStringOrFail (key) = valueOrErrorMessage(getString(this, key), (("Mandatory this." + key) + " is not defined"))
93+
94+
95+func toAddressOrFail (addressStr) = valueOrErrorMessage(addressFromString(addressStr), ("couldn't parse passed addressStr=" + addressStr))
96+
97+
98+func toAssetVect (assetStr) = if ((assetStr == WAVESIDSTR))
99+ then unit
100+ else fromBase58String(assetStr)
54101
55102
56103 func asInt (val) = match val {
67114 func HistoryRecordEntry (type,userAddress,txId,oldAmount,oldStart,newAmount,newStart) = StringEntry(keyHistoryRecord(type, userAddress, txId), formatHistoryRecord(oldAmount, oldStart, newAmount, newStart))
68115
69116
70-func StatsEntry (totalLockedInc,lockCountInc,usersCountInc) = {
117+func StatsResult (totalLockedInc,lockCountInc,usersCountInc) = {
71118 let locksCount = getIntOrZero(keyStatsLocksCount())
72119 let usersCount = getIntOrZero(keyStatsUsersCount())
73120 let totalAmount = getIntOrZero(keyLockParamTotalAmount())
74-[IntegerEntry(keyStatsLocksCount(), (locksCount + lockCountInc)), IntegerEntry(keyStatsUsersCount(), (usersCount + usersCountInc)), IntegerEntry(keyLockParamTotalAmount(), (totalAmount + totalLockedInc))]
121+ let totalAmountNew = (totalAmount + totalLockedInc)
122+ $Tuple3([IntegerEntry(keyStatsLocksCount(), (locksCount + lockCountInc)), IntegerEntry(keyStatsUsersCount(), (usersCount + usersCountInc)), IntegerEntry(keyLockParamTotalAmount(), totalAmountNew)], totalAmount, totalAmountNew)
75123 }
76124
77125
84132 }
85133
86134
87-func isUserExists (userAddress) = isDefined(getInteger(this, keyLockParamStartBlock(userAddress)))
135+func isActiveUser (userAddress) = (getIntOrElse(keyLockParamUserAmount(userAddress), 0) > 0)
88136
89137
90-func getUserParamsOrUnit (userAddress) = if (isUserExists(userAddress))
91- then $Tuple2(getIntOrFail(keyLockParamUserAmount(userAddress)), getIntOrFail(keyLockParamStartBlock(userAddress)))
138+func getUserParamsOrUnit (userAddress) = if (isActiveUser(userAddress))
139+ then $Tuple3(false, getIntOrFail(keyLockParamUserAmount(userAddress)), getIntOrFail(keyLockParamStartBlock(userAddress)))
92140 else unit
93141
94142
95143 func getUserParamsOrFail (userAddress) = valueOrErrorMessage(getUserParamsOrUnit(userAddress), (("User " + toString(userAddress)) + " is not defined"))
96144
97145
146+let supportedAssetsStr = getStrOrElse(keySupportedRewardAssets(), "")
147+
148+let supportedAssetsList = split(supportedAssetsStr, "_")
149+
150+func calcReward (userAddress,assetId,stakedAmountX,depositNumUser,depositNumLast) = {
151+ let rewardPerNsbtSumLastKEY = keyRewardPerNsbtSumAt(depositNumLast, assetId)
152+ let sumLastX18 = parseBigIntValue(getStrOrElse(keyRewardPerNsbtSumAt(depositNumLast, assetId), "0"))
153+ let sumUserX18 = parseBigIntValue(getStrOrElse(keyRewardPerNsbtSumAt(depositNumUser, assetId), "0"))
154+ let rewardDynamicPart = toInt(fraction((sumLastX18 - sumUserX18), stakedAmountX, MULTX18))
155+ let rewardCachedPartKEY = keyReward(userAddress, assetId)
156+ let rewardCachedPart = getIntOrElse(rewardCachedPartKEY, 0)
157+ $Tuple4((rewardCachedPart + rewardDynamicPart), rewardCachedPart, rewardDynamicPart, rewardCachedPartKEY)
158+ }
159+
160+
161+func RewardEntries (isNewUser,userAddress,stakedAmount) = {
162+ let stakedAmountX = toBigInt(stakedAmount)
163+ let userRewardFromDepositNumKEY = keyUserRewardFromDepositNum(userAddress)
164+ let depositNumUser = getIntOrElse(userRewardFromDepositNumKEY, -1)
165+ let depositNumLast = getIntOrElse(keyDepositNumLast(), -1)
166+ func forEachAssetCacheUserReward (accum,asset) = {
167+ let $t067986933 = calcReward(userAddress, asset, stakedAmountX, depositNumUser, depositNumLast)
168+ let rewardTotal = $t067986933._1
169+ let cached = $t067986933._2
170+ let dynamic = $t067986933._3
171+ let rewardCachedPartKEY = $t067986933._4
172+ (accum :+ IntegerEntry(rewardCachedPartKEY, rewardTotal))
173+ }
174+
175+ if (if ((depositNumLast == -1))
176+ then (depositNumUser == -1)
177+ else false)
178+ then nil
179+ else if (if ((depositNumLast == -1))
180+ then (depositNumUser > -1)
181+ else false)
182+ then throw("invalid depositNumLast and depositNumUser state")
183+ else if (if ((depositNumLast > -1))
184+ then (depositNumUser == -1)
185+ else false)
186+ then if (isNewUser)
187+ then [IntegerEntry(userRewardFromDepositNumKEY, depositNumLast)]
188+ else throw("isNewUser must not be false in 5 + 6 cases")
189+ else if (if ((depositNumLast > -1))
190+ then (depositNumUser > -1)
191+ else false)
192+ then if (isNewUser)
193+ then throw((((("invalid management for isNewUser=" + toString(isNewUser)) + " and depositNumUser=") + toString(depositNumUser)) + " during full unstake"))
194+ else ({
195+ let $l = supportedAssetsList
196+ let $s = size($l)
197+ let $acc0 = nil
198+ func $f0_1 ($a,$i) = if (($i >= $s))
199+ then $a
200+ else forEachAssetCacheUserReward($a, $l[$i])
201+
202+ func $f0_2 ($a,$i) = if (($i >= $s))
203+ then $a
204+ else throw("List size exceeds 10")
205+
206+ $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)
207+ } :+ IntegerEntry(userRewardFromDepositNumKEY, depositNumLast))
208+ else throw(((("uncovered condition: depositNumLast=" + toString(depositNumLast)) + " depositNumUser=") + toString(depositNumUser)))
209+ }
210+
211+
212+func IncrementNotDistributedRewardEntry (tkn,amountInc) = {
213+ let notDistributedRewardKEY = keyNotDistributedReward(tkn)
214+ let notDistributedReward = getIntOrElse(notDistributedRewardKEY, 0)
215+[IntegerEntry(notDistributedRewardKEY, (notDistributedReward + amountInc))]
216+ }
217+
218+
98219 @Callable(i)
99-func constructor (neutrinoContractAddress,mathContractAddress,minLockAmount,halfLife) = if ((i.caller != this))
220+func constructor (neutrinoContractAddress,mathContractAddress,minLockAmount,halfLife,supportedRewardAssets) = if ((i.caller != this))
100221 then throw("Permission denied")
101- else [StringEntry(keyNeutrinoContractAddress(), neutrinoContractAddress), StringEntry(keyMathContractAddress(), mathContractAddress), IntegerEntry(keyMinLockAmount(), minLockAmount), IntegerEntry(keyHalfLife(), halfLife)]
222+ else [StringEntry(keyNeutrinoContractAddress(), neutrinoContractAddress), StringEntry(keyMathContractAddress(), mathContractAddress), IntegerEntry(keyMinLockAmount(), minLockAmount), IntegerEntry(keyHalfLife(), halfLife), StringEntry(keySupportedRewardAssets(), supportedRewardAssets)]
102223
103224
104225
105226 @Callable(i)
106227 func stake () = {
107- let $t044424521 = getParamsOrFail()
108- let auctionContract = $t044424521._1
109- let bondAssetId = $t044424521._2
110- let minLockAmount = $t044424521._3
111- let halfLife = $t044424521._4
228+ let $t01080310882 = getParamsOrFail()
229+ let auctionContract = $t01080310882._1
230+ let bondAssetId = $t01080310882._2
231+ let minLockAmount = $t01080310882._3
232+ let halfLife = $t01080310882._4
112233 if ((size(i.payments) != 1))
113234 then throw("Invalid payments size")
114235 else {
120241 then throw(invalidAssetMessage)
121242 else {
122243 let userAddress = i.caller
123- let $t050205192 = match getUserParamsOrUnit(userAddress) {
124- case p: (Int, Int) =>
125- $Tuple3(false, p._1, p._2)
126- case _: Unit =>
127- $Tuple3(true, 0, -1)
128- case _ =>
129- throw("Match error")
130- }
131- let isNewUser = $t050205192._1
132- let lockAmount = $t050205192._2
133- let lockStartHeight = $t050205192._3
244+ let userAddressStr = toString(i.caller)
245+ let $t01142411531 = valueOrElse(getUserParamsOrUnit(userAddress), $Tuple3(true, 0, -1))
246+ let isNewUser = $t01142411531._1
247+ let lockAmount = $t01142411531._2
248+ let lockStartHeight = $t01142411531._3
134249 let mergedAmount = if (isNewUser)
135250 then amount
136251 else (amount + lockAmount)
138253 then height
139254 else {
140255 let mathContract = addressFromStringValue(getStringOrFail(keyMathContractAddress()))
141- asInt(invoke(mathContract, "mergeStakesMATH", [amount, height, lockAmount, lockStartHeight], nil))
256+ asInt(invoke(mathContract, "mergeStakesREADONLY", [amount, height, lockAmount, lockStartHeight, halfLife], nil))
142257 }
143258 if ((minLockAmount > mergedAmount))
144259 then throw(("Min lock amount is " + toString(minLockAmount)))
145- else (([HistoryRecordEntry("stake", userAddress, i.transactionId, lockAmount, lockStartHeight, mergedAmount, mergedStartHeight)] ++ LockParamsEntry(userAddress, mergedAmount, mergedStartHeight)) ++ StatsEntry(amount, 1, if (isNewUser)
146- then 1
147- else 0))
260+ else {
261+ let $t01198112083 = StatsResult(amount, 1, if (isNewUser)
262+ then 1
263+ else 0)
264+ let statsEntries = $t01198112083._1
265+ let totalStaked = $t01198112083._2
266+ let totalStakedNew = $t01198112083._3
267+ let depositNumLast = getIntOrElse(keyDepositNumLast(), -1)
268+ ((([HistoryRecordEntry("stake", userAddress, i.transactionId, lockAmount, lockStartHeight, mergedAmount, mergedStartHeight)] ++ LockParamsEntry(userAddress, mergedAmount, mergedStartHeight)) ++ statsEntries) :+ IntegerEntry(keyUserRewardFromDepositNum(userAddressStr), getIntOrElse(keyDepositNumLast(), -1)))
269+ }
148270 }
149271 }
150272 }
154276 @Callable(i)
155277 func unstake (amount) = {
156278 let userAddress = i.caller
157- let $t059606038 = getParamsOrFail()
158- let auctionAddress = $t059606038._1
159- let bondAssetId = $t059606038._2
160- let minLockAmount = $t059606038._3
161- let halfLife = $t059606038._4
162- let $t060416104 = getUserParamsOrFail(userAddress)
163- let lockAmount = $t060416104._1
164- let lockStart = $t060416104._2
279+ let userAddressStr = toString(userAddress)
280+ let $t01258812666 = getParamsOrFail()
281+ let auctionAddress = $t01258812666._1
282+ let bondAssetId = $t01258812666._2
283+ let minLockAmount = $t01258812666._3
284+ let halfLife = $t01258812666._4
285+ let $t01266912743 = getUserParamsOrFail(userAddress)
286+ let isNewUser = $t01266912743._1
287+ let lockAmount = $t01266912743._2
288+ let lockStart = $t01266912743._3
165289 if ((0 >= lockAmount))
166290 then throw("Nothing to unstake")
167291 else if ((amount > lockAmount))
168292 then throw(((("Requested " + toString(amount)) + ", but staked only ") + toString(lockAmount)))
169293 else {
170294 let mathContract = addressFromStringValue(getStringOrFail(keyMathContractAddress()))
171- let comissionAmount = asInt(invoke(mathContract, "getUnstakeComissionAmountMATH", [amount, lockStart], nil))
172- (([ScriptTransfer(userAddress, (amount - comissionAmount), bondAssetId), ScriptTransfer(Address(auctionAddress), comissionAmount, bondAssetId), HistoryRecordEntry("unstake", userAddress, i.transactionId, lockAmount, lockStart, (lockAmount - amount), lockStart)] ++ LockParamsEntry(userAddress, (lockAmount - amount), lockStart)) ++ StatsEntry(-(amount), if ((amount == lockAmount))
295+ let comissionAmount = asInt(invoke(mathContract, "getUnstakeComissionAmountREADONLY", [amount, lockStart, halfLife], nil))
296+ let $t01314413298 = StatsResult(-(amount), if ((amount == lockAmount))
173297 then -1
174298 else 0, if ((amount == lockAmount))
175299 then -1
176- else 0))
300+ else 0)
301+ let statsEntries = $t01314413298._1
302+ let totalStaked = $t01314413298._2
303+ let totalStakedNew = $t01314413298._3
304+ ((([ScriptTransfer(userAddress, (amount - comissionAmount), bondAssetId), ScriptTransfer(Address(auctionAddress), comissionAmount, bondAssetId), HistoryRecordEntry("unstake", userAddress, i.transactionId, lockAmount, lockStart, (lockAmount - amount), lockStart)] ++ RewardEntries(false, userAddressStr, lockAmount)) ++ LockParamsEntry(userAddress, (lockAmount - amount), lockStart)) ++ statsEntries)
177305 }
178306 }
179307
180308
181309
182310 @Callable(i)
183-func gNsbtAmountREADONLY (userAddressStr) = {
184- let userAddress = addressFromStringValue(userAddressStr)
185- $Tuple2(nil, if (isUserExists(userAddress))
186- then getUserParamsOrFail(userAddress)._1
187- else 0)
311+func deposit () = if ((size(i.payments) != 1))
312+ then throw("exact 1 payment is allowed only")
313+ else {
314+ let pmt = i.payments[0]
315+ let amount = pmt.amount
316+ let pmtAssetId = valueOrElse(pmt.assetId, WAVESID)
317+ let pmtAssetIdStr = toBase58String(pmtAssetId)
318+ let pmtMultX = if ((pmtAssetId == WAVESID))
319+ then MULTX8
320+ else MULTX6
321+ let amountX = toBigInt(amount)
322+ let totalStaked = getIntOrElse(keyLockParamTotalAmount(), 0)
323+ let totalStakedX = toBigInt(totalStaked)
324+ if ((0 > totalStaked))
325+ then throw("TODO: case is not supported")
326+ else if ((totalStaked == 0))
327+ then IncrementNotDistributedRewardEntry(pmtAssetIdStr, amount)
328+ else {
329+ let rewardPerNsbtX18 = fraction(amountX, MULTX18, totalStakedX)
330+ let depositNumLastKEY = keyDepositNumLast()
331+ let depositNumLast = getIntOrElse(depositNumLastKEY, -1)
332+ let depositNumNew = (depositNumLast + 1)
333+ if (!(contains(supportedAssetsStr, pmtAssetIdStr)))
334+ then throw(((supportedAssetsStr + " doesn't contain ") + pmtAssetIdStr))
335+ else {
336+ func refreshRewardPerNsbtSUM (accum,nextAsset) = {
337+ let rewardPerNsbtSumNewKEY = keyRewardPerNsbtSumAt(depositNumNew, nextAsset)
338+ let sumLastStr = getStrOrElse(keyRewardPerNsbtSumAt(depositNumLast, nextAsset), "0")
339+ (accum :+ (if ((nextAsset == pmtAssetIdStr))
340+ then StringEntry(rewardPerNsbtSumNewKEY, toString((parseBigIntValue(sumLastStr) + rewardPerNsbtX18)))
341+ else StringEntry(rewardPerNsbtSumNewKEY, sumLastStr)))
342+ }
343+
344+ ({
345+ let $l = supportedAssetsList
346+ let $s = size($l)
347+ let $acc0 = nil
348+ func $f0_1 ($a,$i) = if (($i >= $s))
349+ then $a
350+ else refreshRewardPerNsbtSUM($a, $l[$i])
351+
352+ func $f0_2 ($a,$i) = if (($i >= $s))
353+ then $a
354+ else throw("List size exceeds 10")
355+
356+ $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)
357+ } :+ IntegerEntry(depositNumLastKEY, depositNumNew))
358+ }
359+ }
360+ }
361+
362+
363+
364+@Callable(i)
365+func claimRewards () = {
366+ let userAddress = i.caller
367+ let userAddressStr = toString(userAddress)
368+ let $t01548015585 = valueOrElse(getUserParamsOrUnit(userAddress), $Tuple3(true, 0, 0))
369+ let isNewUser = $t01548015585._1
370+ let stakedAmount = $t01548015585._2
371+ let stakingStart = $t01548015585._3
372+ let stakedAmountX = toBigInt(stakedAmount)
373+ let userRewardFromDepositNumKEY = keyUserRewardFromDepositNum(userAddressStr)
374+ let depositNumUser = getIntOrElse(userRewardFromDepositNumKEY, -1)
375+ let depositNumLast = getIntOrElse(keyDepositNumLast(), -1)
376+ func forEachAssetCalcUnclaimedReward (accum,asset) = {
377+ let $t01594616084 = calcReward(userAddressStr, asset, stakedAmountX, depositNumUser, depositNumLast)
378+ let rewardTotal = $t01594616084._1
379+ let cached = $t01594616084._2
380+ let dynamic = $t01594616084._3
381+ let rewardCachedPartKEY = $t01594616084._4
382+ if ((0 >= rewardTotal))
383+ then accum
384+ else ((accum :+ ScriptTransfer(userAddress, rewardTotal, toAssetVect(asset))) :+ IntegerEntry(rewardCachedPartKEY, 0))
385+ }
386+
387+ let transfers = {
388+ let $l = supportedAssetsList
389+ let $s = size($l)
390+ let $acc0 = nil
391+ func $f0_1 ($a,$i) = if (($i >= $s))
392+ then $a
393+ else forEachAssetCalcUnclaimedReward($a, $l[$i])
394+
395+ func $f0_2 ($a,$i) = if (($i >= $s))
396+ then $a
397+ else throw("List size exceeds 10")
398+
399+ $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)
400+ }
401+ if ((0 >= size(transfers)))
402+ then throw("nothing to claim")
403+ else transfers
188404 }
189405
190406
191407
192408 @Callable(i)
193-func nsbtStakingInfoWrapperREADONLY (userAddressStr) = {
194- let $t073267626 = if ((userAddressStr == ""))
195- then $Tuple2(0, 0)
409+func unclaimedRewardsREADONLY (userAddressStr) = {
410+ func forEachAssetZeroReward (accum,asset) = ((accum + makeString([asset, "0", "0"], ":")) + "_")
411+
412+ let unclaimedRewardStr = if ((userAddressStr == ""))
413+ then {
414+ let $l = supportedAssetsList
415+ let $s = size($l)
416+ let $acc0 = ""
417+ func $f0_1 ($a,$i) = if (($i >= $s))
418+ then $a
419+ else forEachAssetZeroReward($a, $l[$i])
420+
421+ func $f0_2 ($a,$i) = if (($i >= $s))
422+ then $a
423+ else throw("List size exceeds 10")
424+
425+ $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)
426+ }
196427 else {
197428 let userAddress = addressFromStringValue(userAddressStr)
198- if (isUserExists(userAddress))
199- then {
200- let $t075157570 = getUserParamsOrFail(userAddress)
201- let amount = $t075157570._1
202- let start = $t075157570._2
203- $Tuple2(nil, $Tuple2(amount, start))
204- }
205- else $Tuple2(nil, $Tuple2(0, 0))
429+ let $t01684816953 = valueOrElse(getUserParamsOrUnit(userAddress), $Tuple3(true, 0, 0))
430+ let isNewUser = $t01684816953._1
431+ let stakedAmount = $t01684816953._2
432+ let stakingStart = $t01684816953._3
433+ let stakedAmountX = toBigInt(stakedAmount)
434+ let userRewardFromDepositNumKEY = keyUserRewardFromDepositNum(userAddressStr)
435+ let depositNumUser = getIntOrElse(userRewardFromDepositNumKEY, -1)
436+ let depositNumLast = getIntOrElse(keyDepositNumLast(), -1)
437+ func forEachAssetCalcUnclaimedReward (accum,asset) = {
438+ let $t01729917437 = calcReward(userAddressStr, asset, stakedAmountX, depositNumUser, depositNumLast)
439+ let rewardTotal = $t01729917437._1
440+ let cached = $t01729917437._2
441+ let dynamic = $t01729917437._3
442+ let rewardCachedPartKEY = $t01729917437._4
443+ ((accum + makeString([asset, toString(rewardTotal), "0"], ":")) + "_")
444+ }
445+
446+ let $l = supportedAssetsList
447+ let $s = size($l)
448+ let $acc0 = ""
449+ func $f0_1 ($a,$i) = if (($i >= $s))
450+ then $a
451+ else forEachAssetCalcUnclaimedReward($a, $l[$i])
452+
453+ func $f0_2 ($a,$i) = if (($i >= $s))
454+ then $a
455+ else throw("List size exceeds 10")
456+
457+ $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)
206458 }
207- let nsbtAmt = $t073267626._1
208- let stakingStart = $t073267626._2
209- $Tuple2(nil, $Tuple2(nsbtAmt, stakingStart))
459+ $Tuple2(nil, dropRight(unclaimedRewardStr, 1))
210460 }
461+
462+
463+
464+@Callable(i)
465+func gNsbtAmountREADONLY (userAddressStr) = if ((userAddressStr == ""))
466+ then $Tuple2(nil, 0)
467+ else {
468+ let userAddress = toAddressOrFail(userAddressStr)
469+ let $t01782917955 = valueOrElse(getUserParamsOrUnit(userAddress), $Tuple3(true, 0, 0))
470+ let isNewUser = $t01782917955._1
471+ let nsbtAmt = $t01782917955._2
472+ let stakingStart = $t01782917955._3
473+ $Tuple2(nil, nsbtAmt)
474+ }
475+
476+
477+
478+@Callable(i)
479+func nsbtStakingInfoWrapperREADONLY (userAddressStr) = if ((userAddressStr == ""))
480+ then $Tuple2(nil, $Tuple2(0, 0))
481+ else {
482+ let userAddress = toAddressOrFail(userAddressStr)
483+ let $t01821118311 = valueOrElse(getUserParamsOrUnit(userAddress), $Tuple3(true, 0, 0))
484+ let isNewUser = $t01821118311._1
485+ let nsbtAmt = $t01821118311._2
486+ let stakingStart = $t01821118311._3
487+ $Tuple2(nil, $Tuple2(nsbtAmt, stakingStart))
488+ }
211489
212490
Full:
OldNewDifferences
11 {-# STDLIB_VERSION 5 #-}
22 {-# SCRIPT_TYPE ACCOUNT #-}
33 {-# CONTENT_TYPE DAPP #-}
44 let separator = "__"
55
6+let MULT6 = 1000000
7+
68 let MULT8 = 100000000
9+
10+let MULTX6 = toBigInt(MULT6)
11+
12+let MULTX8 = toBigInt(MULT8)
13+
14+let MULTX18 = toBigInt(1000000000000000000)
15+
16+let WAVESIDSTR = "WAVES"
17+
18+let WAVESID = fromBase58String(WAVESIDSTR)
719
820 func keyBondAsset () = "bond_asset_id"
921
1022
1123 func keyAuctionContractAddress () = "auction_contract"
1224
1325
1426 func keyNeutrinoContractAddress () = "%s__neutrinoContractAddress"
1527
1628
1729 func keyMathContractAddress () = "%s__mathContract"
1830
1931
2032 func keyMinLockAmount () = "%s__minLockAmount"
2133
2234
2335 func keyHalfLife () = "%s__halfLife"
2436
2537
2638 func keyLockParamUserAmount (userAddress) = makeString(["%s%s%s", "paramByUser", toString(userAddress), "amount"], separator)
2739
2840
2941 func keyLockParamStartBlock (userAddress) = makeString(["%s%s%s", "paramByUser", toString(userAddress), "start"], separator)
3042
3143
3244 func keyHistoryRecord (type,userAddress,txId) = makeString(["%s%s%s%s", "history", type, toString(userAddress), toBase58String(txId)], separator)
3345
3446
3547 func keyLockParamTotalAmount () = makeString(["%s%s", "stats", "activeTotalLocked"], separator)
3648
3749
3850 func keyStatsLocksCount () = makeString(["%s%s", "stats", "locksCount"], separator)
3951
4052
4153 func keyStatsUsersCount () = makeString(["%s%s", "stats", "activeUsersCount"], separator)
4254
4355
4456 func keyNextPeriod () = "%s__nextPeriod"
4557
4658
59+func keySupportedRewardAssets () = "supportedRewardAssets"
60+
61+
62+func keyDepositNumLast () = makeString(["%s%s%s", "dep", "lastNum"], separator)
63+
64+
65+func keyUserRewardFromDepositNum (userAddress) = makeString(["%s%s%s", "userRwdFromDepNum", userAddress], separator)
66+
67+
68+func keyRewardPerNsbtSumAt (depositNum,tkn) = makeString(["%s%d", "rwdPerNsbtSumByDepNum", toString(depositNum), tkn], separator)
69+
70+
71+func keyReward (userAddress,tkn) = makeString(["%s%s%s", "rwd", userAddress, tkn], separator)
72+
73+
74+func keyNotDistributedReward (tkn) = makeString(["%s%s", "notDistributed", tkn], separator)
75+
76+
77+func toX18 (origVal,origMult) = fraction(toBigInt(origVal), MULTX18, origMult)
78+
79+
4780 func getIntOrZero (key) = valueOrElse(getInteger(this, key), 0)
81+
82+
83+func getIntOrElse (key,defaultVal) = valueOrElse(getInteger(this, key), defaultVal)
4884
4985
5086 func getIntOrFail (key) = valueOrErrorMessage(getInteger(this, key), (("Mandatory this." + key) + " is not defined"))
5187
5288
89+func getStrOrElse (key,defaultVal) = valueOrElse(getString(this, key), defaultVal)
90+
91+
5392 func getStringOrFail (key) = valueOrErrorMessage(getString(this, key), (("Mandatory this." + key) + " is not defined"))
93+
94+
95+func toAddressOrFail (addressStr) = valueOrErrorMessage(addressFromString(addressStr), ("couldn't parse passed addressStr=" + addressStr))
96+
97+
98+func toAssetVect (assetStr) = if ((assetStr == WAVESIDSTR))
99+ then unit
100+ else fromBase58String(assetStr)
54101
55102
56103 func asInt (val) = match val {
57104 case valInt: Int =>
58105 valInt
59106 case _ =>
60107 throw("fail to cast into Int")
61108 }
62109
63110
64111 func formatHistoryRecord (oldAmount,oldStart,newAmount,newStart) = makeString(["%d%d%d%d%d%d", toString(lastBlock.height), toString(lastBlock.timestamp), toString(oldAmount), toString(oldStart), toString(newAmount), toString(newStart)], separator)
65112
66113
67114 func HistoryRecordEntry (type,userAddress,txId,oldAmount,oldStart,newAmount,newStart) = StringEntry(keyHistoryRecord(type, userAddress, txId), formatHistoryRecord(oldAmount, oldStart, newAmount, newStart))
68115
69116
70-func StatsEntry (totalLockedInc,lockCountInc,usersCountInc) = {
117+func StatsResult (totalLockedInc,lockCountInc,usersCountInc) = {
71118 let locksCount = getIntOrZero(keyStatsLocksCount())
72119 let usersCount = getIntOrZero(keyStatsUsersCount())
73120 let totalAmount = getIntOrZero(keyLockParamTotalAmount())
74-[IntegerEntry(keyStatsLocksCount(), (locksCount + lockCountInc)), IntegerEntry(keyStatsUsersCount(), (usersCount + usersCountInc)), IntegerEntry(keyLockParamTotalAmount(), (totalAmount + totalLockedInc))]
121+ let totalAmountNew = (totalAmount + totalLockedInc)
122+ $Tuple3([IntegerEntry(keyStatsLocksCount(), (locksCount + lockCountInc)), IntegerEntry(keyStatsUsersCount(), (usersCount + usersCountInc)), IntegerEntry(keyLockParamTotalAmount(), totalAmountNew)], totalAmount, totalAmountNew)
75123 }
76124
77125
78126 func LockParamsEntry (userAddress,amount,start) = [IntegerEntry(keyLockParamUserAmount(userAddress), amount), IntegerEntry(keyLockParamStartBlock(userAddress), start)]
79127
80128
81129 func getParamsOrFail () = {
82130 let neutrinoContract = addressFromStringValue(getStringOrFail(keyNeutrinoContractAddress()))
83131 $Tuple4(fromBase58String(getStringValue(neutrinoContract, keyAuctionContractAddress())), fromBase58String(getStringValue(neutrinoContract, keyBondAsset())), getIntOrFail(keyMinLockAmount()), getIntOrFail(keyHalfLife()))
84132 }
85133
86134
87-func isUserExists (userAddress) = isDefined(getInteger(this, keyLockParamStartBlock(userAddress)))
135+func isActiveUser (userAddress) = (getIntOrElse(keyLockParamUserAmount(userAddress), 0) > 0)
88136
89137
90-func getUserParamsOrUnit (userAddress) = if (isUserExists(userAddress))
91- then $Tuple2(getIntOrFail(keyLockParamUserAmount(userAddress)), getIntOrFail(keyLockParamStartBlock(userAddress)))
138+func getUserParamsOrUnit (userAddress) = if (isActiveUser(userAddress))
139+ then $Tuple3(false, getIntOrFail(keyLockParamUserAmount(userAddress)), getIntOrFail(keyLockParamStartBlock(userAddress)))
92140 else unit
93141
94142
95143 func getUserParamsOrFail (userAddress) = valueOrErrorMessage(getUserParamsOrUnit(userAddress), (("User " + toString(userAddress)) + " is not defined"))
96144
97145
146+let supportedAssetsStr = getStrOrElse(keySupportedRewardAssets(), "")
147+
148+let supportedAssetsList = split(supportedAssetsStr, "_")
149+
150+func calcReward (userAddress,assetId,stakedAmountX,depositNumUser,depositNumLast) = {
151+ let rewardPerNsbtSumLastKEY = keyRewardPerNsbtSumAt(depositNumLast, assetId)
152+ let sumLastX18 = parseBigIntValue(getStrOrElse(keyRewardPerNsbtSumAt(depositNumLast, assetId), "0"))
153+ let sumUserX18 = parseBigIntValue(getStrOrElse(keyRewardPerNsbtSumAt(depositNumUser, assetId), "0"))
154+ let rewardDynamicPart = toInt(fraction((sumLastX18 - sumUserX18), stakedAmountX, MULTX18))
155+ let rewardCachedPartKEY = keyReward(userAddress, assetId)
156+ let rewardCachedPart = getIntOrElse(rewardCachedPartKEY, 0)
157+ $Tuple4((rewardCachedPart + rewardDynamicPart), rewardCachedPart, rewardDynamicPart, rewardCachedPartKEY)
158+ }
159+
160+
161+func RewardEntries (isNewUser,userAddress,stakedAmount) = {
162+ let stakedAmountX = toBigInt(stakedAmount)
163+ let userRewardFromDepositNumKEY = keyUserRewardFromDepositNum(userAddress)
164+ let depositNumUser = getIntOrElse(userRewardFromDepositNumKEY, -1)
165+ let depositNumLast = getIntOrElse(keyDepositNumLast(), -1)
166+ func forEachAssetCacheUserReward (accum,asset) = {
167+ let $t067986933 = calcReward(userAddress, asset, stakedAmountX, depositNumUser, depositNumLast)
168+ let rewardTotal = $t067986933._1
169+ let cached = $t067986933._2
170+ let dynamic = $t067986933._3
171+ let rewardCachedPartKEY = $t067986933._4
172+ (accum :+ IntegerEntry(rewardCachedPartKEY, rewardTotal))
173+ }
174+
175+ if (if ((depositNumLast == -1))
176+ then (depositNumUser == -1)
177+ else false)
178+ then nil
179+ else if (if ((depositNumLast == -1))
180+ then (depositNumUser > -1)
181+ else false)
182+ then throw("invalid depositNumLast and depositNumUser state")
183+ else if (if ((depositNumLast > -1))
184+ then (depositNumUser == -1)
185+ else false)
186+ then if (isNewUser)
187+ then [IntegerEntry(userRewardFromDepositNumKEY, depositNumLast)]
188+ else throw("isNewUser must not be false in 5 + 6 cases")
189+ else if (if ((depositNumLast > -1))
190+ then (depositNumUser > -1)
191+ else false)
192+ then if (isNewUser)
193+ then throw((((("invalid management for isNewUser=" + toString(isNewUser)) + " and depositNumUser=") + toString(depositNumUser)) + " during full unstake"))
194+ else ({
195+ let $l = supportedAssetsList
196+ let $s = size($l)
197+ let $acc0 = nil
198+ func $f0_1 ($a,$i) = if (($i >= $s))
199+ then $a
200+ else forEachAssetCacheUserReward($a, $l[$i])
201+
202+ func $f0_2 ($a,$i) = if (($i >= $s))
203+ then $a
204+ else throw("List size exceeds 10")
205+
206+ $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)
207+ } :+ IntegerEntry(userRewardFromDepositNumKEY, depositNumLast))
208+ else throw(((("uncovered condition: depositNumLast=" + toString(depositNumLast)) + " depositNumUser=") + toString(depositNumUser)))
209+ }
210+
211+
212+func IncrementNotDistributedRewardEntry (tkn,amountInc) = {
213+ let notDistributedRewardKEY = keyNotDistributedReward(tkn)
214+ let notDistributedReward = getIntOrElse(notDistributedRewardKEY, 0)
215+[IntegerEntry(notDistributedRewardKEY, (notDistributedReward + amountInc))]
216+ }
217+
218+
98219 @Callable(i)
99-func constructor (neutrinoContractAddress,mathContractAddress,minLockAmount,halfLife) = if ((i.caller != this))
220+func constructor (neutrinoContractAddress,mathContractAddress,minLockAmount,halfLife,supportedRewardAssets) = if ((i.caller != this))
100221 then throw("Permission denied")
101- else [StringEntry(keyNeutrinoContractAddress(), neutrinoContractAddress), StringEntry(keyMathContractAddress(), mathContractAddress), IntegerEntry(keyMinLockAmount(), minLockAmount), IntegerEntry(keyHalfLife(), halfLife)]
222+ else [StringEntry(keyNeutrinoContractAddress(), neutrinoContractAddress), StringEntry(keyMathContractAddress(), mathContractAddress), IntegerEntry(keyMinLockAmount(), minLockAmount), IntegerEntry(keyHalfLife(), halfLife), StringEntry(keySupportedRewardAssets(), supportedRewardAssets)]
102223
103224
104225
105226 @Callable(i)
106227 func stake () = {
107- let $t044424521 = getParamsOrFail()
108- let auctionContract = $t044424521._1
109- let bondAssetId = $t044424521._2
110- let minLockAmount = $t044424521._3
111- let halfLife = $t044424521._4
228+ let $t01080310882 = getParamsOrFail()
229+ let auctionContract = $t01080310882._1
230+ let bondAssetId = $t01080310882._2
231+ let minLockAmount = $t01080310882._3
232+ let halfLife = $t01080310882._4
112233 if ((size(i.payments) != 1))
113234 then throw("Invalid payments size")
114235 else {
115236 let payment = i.payments[0]
116237 let amount = payment.amount
117238 let invalidAssetMessage = (("Invalid asset. " + toBase58String(bondAssetId)) + " is expected")
118239 let assetId = valueOrErrorMessage(payment.assetId, invalidAssetMessage)
119240 if ((assetId != bondAssetId))
120241 then throw(invalidAssetMessage)
121242 else {
122243 let userAddress = i.caller
123- let $t050205192 = match getUserParamsOrUnit(userAddress) {
124- case p: (Int, Int) =>
125- $Tuple3(false, p._1, p._2)
126- case _: Unit =>
127- $Tuple3(true, 0, -1)
128- case _ =>
129- throw("Match error")
130- }
131- let isNewUser = $t050205192._1
132- let lockAmount = $t050205192._2
133- let lockStartHeight = $t050205192._3
244+ let userAddressStr = toString(i.caller)
245+ let $t01142411531 = valueOrElse(getUserParamsOrUnit(userAddress), $Tuple3(true, 0, -1))
246+ let isNewUser = $t01142411531._1
247+ let lockAmount = $t01142411531._2
248+ let lockStartHeight = $t01142411531._3
134249 let mergedAmount = if (isNewUser)
135250 then amount
136251 else (amount + lockAmount)
137252 let mergedStartHeight = if (isNewUser)
138253 then height
139254 else {
140255 let mathContract = addressFromStringValue(getStringOrFail(keyMathContractAddress()))
141- asInt(invoke(mathContract, "mergeStakesMATH", [amount, height, lockAmount, lockStartHeight], nil))
256+ asInt(invoke(mathContract, "mergeStakesREADONLY", [amount, height, lockAmount, lockStartHeight, halfLife], nil))
142257 }
143258 if ((minLockAmount > mergedAmount))
144259 then throw(("Min lock amount is " + toString(minLockAmount)))
145- else (([HistoryRecordEntry("stake", userAddress, i.transactionId, lockAmount, lockStartHeight, mergedAmount, mergedStartHeight)] ++ LockParamsEntry(userAddress, mergedAmount, mergedStartHeight)) ++ StatsEntry(amount, 1, if (isNewUser)
146- then 1
147- else 0))
260+ else {
261+ let $t01198112083 = StatsResult(amount, 1, if (isNewUser)
262+ then 1
263+ else 0)
264+ let statsEntries = $t01198112083._1
265+ let totalStaked = $t01198112083._2
266+ let totalStakedNew = $t01198112083._3
267+ let depositNumLast = getIntOrElse(keyDepositNumLast(), -1)
268+ ((([HistoryRecordEntry("stake", userAddress, i.transactionId, lockAmount, lockStartHeight, mergedAmount, mergedStartHeight)] ++ LockParamsEntry(userAddress, mergedAmount, mergedStartHeight)) ++ statsEntries) :+ IntegerEntry(keyUserRewardFromDepositNum(userAddressStr), getIntOrElse(keyDepositNumLast(), -1)))
269+ }
148270 }
149271 }
150272 }
151273
152274
153275
154276 @Callable(i)
155277 func unstake (amount) = {
156278 let userAddress = i.caller
157- let $t059606038 = getParamsOrFail()
158- let auctionAddress = $t059606038._1
159- let bondAssetId = $t059606038._2
160- let minLockAmount = $t059606038._3
161- let halfLife = $t059606038._4
162- let $t060416104 = getUserParamsOrFail(userAddress)
163- let lockAmount = $t060416104._1
164- let lockStart = $t060416104._2
279+ let userAddressStr = toString(userAddress)
280+ let $t01258812666 = getParamsOrFail()
281+ let auctionAddress = $t01258812666._1
282+ let bondAssetId = $t01258812666._2
283+ let minLockAmount = $t01258812666._3
284+ let halfLife = $t01258812666._4
285+ let $t01266912743 = getUserParamsOrFail(userAddress)
286+ let isNewUser = $t01266912743._1
287+ let lockAmount = $t01266912743._2
288+ let lockStart = $t01266912743._3
165289 if ((0 >= lockAmount))
166290 then throw("Nothing to unstake")
167291 else if ((amount > lockAmount))
168292 then throw(((("Requested " + toString(amount)) + ", but staked only ") + toString(lockAmount)))
169293 else {
170294 let mathContract = addressFromStringValue(getStringOrFail(keyMathContractAddress()))
171- let comissionAmount = asInt(invoke(mathContract, "getUnstakeComissionAmountMATH", [amount, lockStart], nil))
172- (([ScriptTransfer(userAddress, (amount - comissionAmount), bondAssetId), ScriptTransfer(Address(auctionAddress), comissionAmount, bondAssetId), HistoryRecordEntry("unstake", userAddress, i.transactionId, lockAmount, lockStart, (lockAmount - amount), lockStart)] ++ LockParamsEntry(userAddress, (lockAmount - amount), lockStart)) ++ StatsEntry(-(amount), if ((amount == lockAmount))
295+ let comissionAmount = asInt(invoke(mathContract, "getUnstakeComissionAmountREADONLY", [amount, lockStart, halfLife], nil))
296+ let $t01314413298 = StatsResult(-(amount), if ((amount == lockAmount))
173297 then -1
174298 else 0, if ((amount == lockAmount))
175299 then -1
176- else 0))
300+ else 0)
301+ let statsEntries = $t01314413298._1
302+ let totalStaked = $t01314413298._2
303+ let totalStakedNew = $t01314413298._3
304+ ((([ScriptTransfer(userAddress, (amount - comissionAmount), bondAssetId), ScriptTransfer(Address(auctionAddress), comissionAmount, bondAssetId), HistoryRecordEntry("unstake", userAddress, i.transactionId, lockAmount, lockStart, (lockAmount - amount), lockStart)] ++ RewardEntries(false, userAddressStr, lockAmount)) ++ LockParamsEntry(userAddress, (lockAmount - amount), lockStart)) ++ statsEntries)
177305 }
178306 }
179307
180308
181309
182310 @Callable(i)
183-func gNsbtAmountREADONLY (userAddressStr) = {
184- let userAddress = addressFromStringValue(userAddressStr)
185- $Tuple2(nil, if (isUserExists(userAddress))
186- then getUserParamsOrFail(userAddress)._1
187- else 0)
311+func deposit () = if ((size(i.payments) != 1))
312+ then throw("exact 1 payment is allowed only")
313+ else {
314+ let pmt = i.payments[0]
315+ let amount = pmt.amount
316+ let pmtAssetId = valueOrElse(pmt.assetId, WAVESID)
317+ let pmtAssetIdStr = toBase58String(pmtAssetId)
318+ let pmtMultX = if ((pmtAssetId == WAVESID))
319+ then MULTX8
320+ else MULTX6
321+ let amountX = toBigInt(amount)
322+ let totalStaked = getIntOrElse(keyLockParamTotalAmount(), 0)
323+ let totalStakedX = toBigInt(totalStaked)
324+ if ((0 > totalStaked))
325+ then throw("TODO: case is not supported")
326+ else if ((totalStaked == 0))
327+ then IncrementNotDistributedRewardEntry(pmtAssetIdStr, amount)
328+ else {
329+ let rewardPerNsbtX18 = fraction(amountX, MULTX18, totalStakedX)
330+ let depositNumLastKEY = keyDepositNumLast()
331+ let depositNumLast = getIntOrElse(depositNumLastKEY, -1)
332+ let depositNumNew = (depositNumLast + 1)
333+ if (!(contains(supportedAssetsStr, pmtAssetIdStr)))
334+ then throw(((supportedAssetsStr + " doesn't contain ") + pmtAssetIdStr))
335+ else {
336+ func refreshRewardPerNsbtSUM (accum,nextAsset) = {
337+ let rewardPerNsbtSumNewKEY = keyRewardPerNsbtSumAt(depositNumNew, nextAsset)
338+ let sumLastStr = getStrOrElse(keyRewardPerNsbtSumAt(depositNumLast, nextAsset), "0")
339+ (accum :+ (if ((nextAsset == pmtAssetIdStr))
340+ then StringEntry(rewardPerNsbtSumNewKEY, toString((parseBigIntValue(sumLastStr) + rewardPerNsbtX18)))
341+ else StringEntry(rewardPerNsbtSumNewKEY, sumLastStr)))
342+ }
343+
344+ ({
345+ let $l = supportedAssetsList
346+ let $s = size($l)
347+ let $acc0 = nil
348+ func $f0_1 ($a,$i) = if (($i >= $s))
349+ then $a
350+ else refreshRewardPerNsbtSUM($a, $l[$i])
351+
352+ func $f0_2 ($a,$i) = if (($i >= $s))
353+ then $a
354+ else throw("List size exceeds 10")
355+
356+ $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)
357+ } :+ IntegerEntry(depositNumLastKEY, depositNumNew))
358+ }
359+ }
360+ }
361+
362+
363+
364+@Callable(i)
365+func claimRewards () = {
366+ let userAddress = i.caller
367+ let userAddressStr = toString(userAddress)
368+ let $t01548015585 = valueOrElse(getUserParamsOrUnit(userAddress), $Tuple3(true, 0, 0))
369+ let isNewUser = $t01548015585._1
370+ let stakedAmount = $t01548015585._2
371+ let stakingStart = $t01548015585._3
372+ let stakedAmountX = toBigInt(stakedAmount)
373+ let userRewardFromDepositNumKEY = keyUserRewardFromDepositNum(userAddressStr)
374+ let depositNumUser = getIntOrElse(userRewardFromDepositNumKEY, -1)
375+ let depositNumLast = getIntOrElse(keyDepositNumLast(), -1)
376+ func forEachAssetCalcUnclaimedReward (accum,asset) = {
377+ let $t01594616084 = calcReward(userAddressStr, asset, stakedAmountX, depositNumUser, depositNumLast)
378+ let rewardTotal = $t01594616084._1
379+ let cached = $t01594616084._2
380+ let dynamic = $t01594616084._3
381+ let rewardCachedPartKEY = $t01594616084._4
382+ if ((0 >= rewardTotal))
383+ then accum
384+ else ((accum :+ ScriptTransfer(userAddress, rewardTotal, toAssetVect(asset))) :+ IntegerEntry(rewardCachedPartKEY, 0))
385+ }
386+
387+ let transfers = {
388+ let $l = supportedAssetsList
389+ let $s = size($l)
390+ let $acc0 = nil
391+ func $f0_1 ($a,$i) = if (($i >= $s))
392+ then $a
393+ else forEachAssetCalcUnclaimedReward($a, $l[$i])
394+
395+ func $f0_2 ($a,$i) = if (($i >= $s))
396+ then $a
397+ else throw("List size exceeds 10")
398+
399+ $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)
400+ }
401+ if ((0 >= size(transfers)))
402+ then throw("nothing to claim")
403+ else transfers
188404 }
189405
190406
191407
192408 @Callable(i)
193-func nsbtStakingInfoWrapperREADONLY (userAddressStr) = {
194- let $t073267626 = if ((userAddressStr == ""))
195- then $Tuple2(0, 0)
409+func unclaimedRewardsREADONLY (userAddressStr) = {
410+ func forEachAssetZeroReward (accum,asset) = ((accum + makeString([asset, "0", "0"], ":")) + "_")
411+
412+ let unclaimedRewardStr = if ((userAddressStr == ""))
413+ then {
414+ let $l = supportedAssetsList
415+ let $s = size($l)
416+ let $acc0 = ""
417+ func $f0_1 ($a,$i) = if (($i >= $s))
418+ then $a
419+ else forEachAssetZeroReward($a, $l[$i])
420+
421+ func $f0_2 ($a,$i) = if (($i >= $s))
422+ then $a
423+ else throw("List size exceeds 10")
424+
425+ $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)
426+ }
196427 else {
197428 let userAddress = addressFromStringValue(userAddressStr)
198- if (isUserExists(userAddress))
199- then {
200- let $t075157570 = getUserParamsOrFail(userAddress)
201- let amount = $t075157570._1
202- let start = $t075157570._2
203- $Tuple2(nil, $Tuple2(amount, start))
204- }
205- else $Tuple2(nil, $Tuple2(0, 0))
429+ let $t01684816953 = valueOrElse(getUserParamsOrUnit(userAddress), $Tuple3(true, 0, 0))
430+ let isNewUser = $t01684816953._1
431+ let stakedAmount = $t01684816953._2
432+ let stakingStart = $t01684816953._3
433+ let stakedAmountX = toBigInt(stakedAmount)
434+ let userRewardFromDepositNumKEY = keyUserRewardFromDepositNum(userAddressStr)
435+ let depositNumUser = getIntOrElse(userRewardFromDepositNumKEY, -1)
436+ let depositNumLast = getIntOrElse(keyDepositNumLast(), -1)
437+ func forEachAssetCalcUnclaimedReward (accum,asset) = {
438+ let $t01729917437 = calcReward(userAddressStr, asset, stakedAmountX, depositNumUser, depositNumLast)
439+ let rewardTotal = $t01729917437._1
440+ let cached = $t01729917437._2
441+ let dynamic = $t01729917437._3
442+ let rewardCachedPartKEY = $t01729917437._4
443+ ((accum + makeString([asset, toString(rewardTotal), "0"], ":")) + "_")
444+ }
445+
446+ let $l = supportedAssetsList
447+ let $s = size($l)
448+ let $acc0 = ""
449+ func $f0_1 ($a,$i) = if (($i >= $s))
450+ then $a
451+ else forEachAssetCalcUnclaimedReward($a, $l[$i])
452+
453+ func $f0_2 ($a,$i) = if (($i >= $s))
454+ then $a
455+ else throw("List size exceeds 10")
456+
457+ $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)
206458 }
207- let nsbtAmt = $t073267626._1
208- let stakingStart = $t073267626._2
209- $Tuple2(nil, $Tuple2(nsbtAmt, stakingStart))
459+ $Tuple2(nil, dropRight(unclaimedRewardStr, 1))
210460 }
461+
462+
463+
464+@Callable(i)
465+func gNsbtAmountREADONLY (userAddressStr) = if ((userAddressStr == ""))
466+ then $Tuple2(nil, 0)
467+ else {
468+ let userAddress = toAddressOrFail(userAddressStr)
469+ let $t01782917955 = valueOrElse(getUserParamsOrUnit(userAddress), $Tuple3(true, 0, 0))
470+ let isNewUser = $t01782917955._1
471+ let nsbtAmt = $t01782917955._2
472+ let stakingStart = $t01782917955._3
473+ $Tuple2(nil, nsbtAmt)
474+ }
475+
476+
477+
478+@Callable(i)
479+func nsbtStakingInfoWrapperREADONLY (userAddressStr) = if ((userAddressStr == ""))
480+ then $Tuple2(nil, $Tuple2(0, 0))
481+ else {
482+ let userAddress = toAddressOrFail(userAddressStr)
483+ let $t01821118311 = valueOrElse(getUserParamsOrUnit(userAddress), $Tuple3(true, 0, 0))
484+ let isNewUser = $t01821118311._1
485+ let nsbtAmt = $t01821118311._2
486+ let stakingStart = $t01821118311._3
487+ $Tuple2(nil, $Tuple2(nsbtAmt, stakingStart))
488+ }
211489
212490

github/deemru/w8io/169f3d6 
108.57 ms