tx · 2jiAroyWAiEXKxqefXNa1Qxw6LwgiX8HRUaP9822eTq9

3MsUmiMnER3P7xLsS1KRhCxm6VXtXmNF5Rh:  -0.02300000 Waves

2023.02.15 08:58 [2450334] smart account 3MsUmiMnER3P7xLsS1KRhCxm6VXtXmNF5Rh > SELF 0.00000000 Waves

{ "type": 13, "id": "2jiAroyWAiEXKxqefXNa1Qxw6LwgiX8HRUaP9822eTq9", "fee": 2300000, "feeAssetId": null, "timestamp": 1676440784020, "version": 1, "sender": "3MsUmiMnER3P7xLsS1KRhCxm6VXtXmNF5Rh", "senderPublicKey": "3xwy9PjLzgFh1BA1oVKpQukW64fwocA93NM8R9DAbRo9", "proofs": [ "2e1xk1L9L1oUBP82bYfQvYhtVkcBY61fTgzCqi8eq6DEqXtvQZUAswy6ME24AbCLcZ1EeAwtbETqhngAqyV2NVKj" ], "script": "base64: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AAAJZ3d4QW1vdW50AAAABHZvdGUEAAAAA2tleQMFAAAABHZvdGUJAAS5AAAAAgkABEwAAAACAgAAAAQlZCVzCQAETAAAAAIFAAAACWd3eEFtb3VudAkABEwAAAACAgAAAAN5ZXMFAAAAA25pbAUAAAAJc2VwYXJhdG9yCQAEuQAAAAIJAARMAAAAAgIAAAAEJWQlcwkABEwAAAACBQAAAAlnd3hBbW91bnQJAARMAAAAAgIAAAACbm8FAAAAA25pbAUAAAAJc2VwYXJhdG9yBQAAAANrZXkBAAAACWtleUluTGlzdAAAAAEAAAAEcG9vbAQAAAALJHQwMjUyNjI1NjYFAAAABHBvb2wEAAAADWFtb3VudEFzc2V0SWQIBQAAAAskdDAyNTI2MjU2NgAAAAJfMQQAAAAMcHJpY2VBc3NldElkCAUAAAALJHQwMjUyNjI1NjYAAAACXzIJAAS5AAAAAgkABEwAAAACAgAAAAYlcyVzJXMJAARMAAAAAgIAAAAGaW5MaXN0CQAETAAAAAIFAAAADWFtb3VudEFzc2V0SWQJAARMAAAAAgUAAAAMcHJpY2VBc3NldElkBQAAAANuaWwFAAAACXNlcGFyYXRvcgEAAAATa2V5TWFuYWdlclB1YmxpY0tleQAAAAACAAAAFCVzX19tYW5hZ2VyUHVibGljS2V5AQAAABprZXlQZW5kaW5nTWFuYWdlclB1YmxpY0tleQAAAAACAAAAGyVzX19wZW5kaW5nTWFuYWdlclB1YmxpY0tleQAAAAATYXNzZXRzU3RvcmVDb250cmFjdAkBAAAAEUBleHRyTmF0aXZlKDEwNjIpAAAAAQkBAAAAD2dldFN0cmluZ09yRmFpbAAAAAEFAAAAFmtleUFzc2V0c1N0b3JlQ29udHJhY3QAAAAAEGJvb3N0aW5nQ29udHJhY3QJAQAAABFAZXh0ck5hdGl2ZSgxMDYyKQAAAAEJAQAAAA9nZXRTdHJpbmdPckZhaWwAAAABBQAAABNrZXlCb29zdGluZ0NvbnRyYWN0AAAAABBlbWlzc2lvbkNvbnRyYWN0CQEAAAARQGV4dHJOYXRpdmUoMTA2MikAAAABCQEAAAAPZ2V0U3RyaW5nT3JGYWlsAAAAAQUAAAATa2V5RW1pc3Npb25Db250cmFjdAAAAAAPZmFjdG9yeUNvbnRyYWN0CQEAAAARQGV4dHJOYXRpdmUoMTA2MikAAAABCQEAAAAPZ2V0U3RyaW5nT3JGYWlsAAAAAQUAAAASa2V5RmFjdG9yeUNvbnRyYWN0AAAAABB1c2VyUG9vbENvbnRyYWN0CQEAAAARQGV4dHJOYXRpdmUoMTA2MikAAAABCQEAAAAPZ2V0U3RyaW5nT3JGYWlsAAAAAQUAAAATa2V5VXNlclBvb2xDb250cmFjdAAAAAAWdm90aW5nRW1pc3Npb25Db250cmFjdAkBAAAAEUBleHRyTmF0aXZlKDEwNjIpAAAAAQkBAAAAD2dldFN0cmluZ09yRmFpbAAAAAEFAAAAGWtleVZvdGluZ0VtaXNzaW9uQ29udHJhY3QBAAAAFm1hbmFnZXJQdWJsaWNLZXlPclVuaXQAAAAABAAAAAckbWF0Y2gwCQAEIgAAAAEJAQAAABNrZXlNYW5hZ2VyUHVibGljS2V5AAAAAAMJAAABAAAAAgUAAAAHJG1hdGNoMAIAAAAGU3RyaW5nBAAAAAFzBQAAAAckbWF0Y2gwCQACWQAAAAEFAAAAAXMDCQAAAQAAAAIFAAAAByRtYXRjaDACAAAABFVuaXQFAAAABHVuaXQJAAACAAAAAQIAAAALTWF0Y2ggZXJyb3IBAAAAHXBlbmRpbmdNYW5hZ2VyUHVibGljS2V5T3JVbml0AAAAAAQAAAAHJG1hdGNoMAkABCIAAAABCQEAAAAaa2V5UGVuZGluZ01hbmFnZXJQdWJsaWNLZXkAAAAAAwkAAAEAAAACBQAAAAckbWF0Y2gwAgAAAAZTdHJpbmcEAAAAAXMFAAAAByRtYXRjaDAJAAJZAAAAAQUAAAABcwMJAAABAAAAAgUAAAAHJG1hdGNoMAIAAAAEVW5pdAUAAAAEdW5pdAkAAAIAAAABAgAAAAtNYXRjaCBlcnJvcgEAAAAJaXNNYW5hZ2VyAAAAAQAAAAFpBAAAAAckbWF0Y2gwCQEAAAAWbWFuYWdlclB1YmxpY0tleU9yVW5pdAAAAAADCQAAAQAAAAIFAAAAByRtYXRjaDACAAAACkJ5dGVWZWN0b3IEAAAAAnBrBQAAAAckbWF0Y2gwCQAAAAAAAAIIBQAAAAFpAAAAD2NhbGxlclB1YmxpY0tleQUAAAACcGsDCQAAAQAAAAIFAAAAByRtYXRjaDACAAAABFVuaXQJAAAAAAAAAggFAAAAAWkAAAAGY2FsbGVyBQAAAAR0aGlzCQAAAgAAAAECAAAAC01hdGNoIGVycm9yAQAAAAttdXN0TWFuYWdlcgAAAAEAAAABaQMJAQAAAAlpc01hbmFnZXIAAAABBQAAAAFpBgkAAAIAAAABAgAAABFwZXJtaXNzaW9uIGRlbmllZAEAAAAFYXNJbnQAAAABAAAAA3ZhbAQAAAAHJG1hdGNoMAUAAAADdmFsAwkAAAEAAAACBQAAAAckbWF0Y2gwAgAAAANJbnQEAAAABnZhbEludAUAAAAHJG1hdGNoMAUAAAAGdmFsSW50CQAAAgAAAAECAAAAG2ZhaWxlZCB0byBjYXN0IGludG8gSW50ZWdlcgAAAAoAAAABaQEAAAALY29uc3RydWN0b3IAAAALAAAAFmFzc2V0c1N0b3JlQ29udHJhY3RQcm0AAAATYm9vc3RpbmdDb250cmFjdFBybQAAABNlbWlzc2lvbkNvbnRyYWN0UHJtAAAAEmZhY3RvcnlDb250cmFjdFBybQAAABN1c2VyUG9vbENvbnRyYWN0UHJtAAAAGXZvdGluZ0VtaXNzaW9uQ29udHJhY3RQcm0AAAAMZmVlQW1vdW50UHJtAAAADHd4QXNzZXRJZFBybQAAABF2b3RpbmdEdXJhdGlvblBybQAAAA51c2RuQXNzZXRJZFBybQAAABFmaW5hbGl6ZVJld2FyZFBybQQAAAAGY2hlY2tzCQAETAAAAAIJAQAAAAttdXN0TWFuYWdlcgAAAAEFAAAAAWkJAARMAAAAAgMJAQAAAAlpc0RlZmluZWQAAAABCQAEJgAAAAEFAAAAFmFzc2V0c1N0b3JlQ29udHJhY3RQcm0GCQAAAgAAAAECAAAAJEludmFsaWQgYXNzZXRfc3RvcmUgY29udHJhY3QgYWRkcmVzcwkABEwAAAACAwkBAAAACWlzRGVmaW5lZAAAAAEJAAQmAAAAAQUAAAATYm9vc3RpbmdDb250cmFjdFBybQYJAAACAAAAAQIAAAAhSW52YWxpZCBib29zdGluZyBjb250cmFjdCBhZGRyZXNzCQAETAAAAAIDCQEAAAAJaXNEZWZpbmVkAAAAAQkABCYAAAABBQAAABN1c2VyUG9vbENvbnRyYWN0UHJtBgkAAAIAAAABAgAAACNJbnZhbGlkIHVzZXJfcG9vbHMgY29udHJhY3QgYWRkcmVzcwkABEwAAAACAwkBAAAACWlzRGVmaW5lZAAAAAEJAAQmAAAAAQUAAAATZW1pc3Npb25Db250cmFjdFBybQYJAAACAAAAAQIAAAAhSW52YWxpZCBlbWlzc2lvbiBjb250cmFjdCBhZGRyZXNzCQAETAAAAAIDCQEAAAAJaXNEZWZpbmVkAAAAAQkABCYAAAABBQAAABJmYWN0b3J5Q29udHJhY3RQcm0GCQAAAgAAAAECAAAAIEludmFsaWQgZmFjdG9yeSBjb250cmFjdCBhZGRyZXNzCQAETAAAAAIDCQAAZwAAAAIFAAAADGZlZUFtb3VudFBybQAAAAAAAAAAAAYJAAACAAAAAQIAAAASSW52YWxpZCBmZWUgYW1vdW50CQAETAAAAAIDCQAAZgAAAAIFAAAAEXZvdGluZ0R1cmF0aW9uUHJtAAAAAAAAAAAABgkAAAIAAAABAgAAABdJbnZhbGlkIHZvdGluZyBkdXJhdGlvbgkABEwAAAACAwkAAGcAAAACBQAAABFmaW5hbGl6ZVJld2FyZFBybQAAAAAAAAAAAAYJAAACAAAAAQIAAAAXSW52YWxpZCBmaW5hbGl6ZSByZXdhcmQJAARMAAAAAgMJAQAAAAlpc0RlZmluZWQAAAABCQAD7AAAAAEJAAJZAAAAAQUAAAAMd3hBc3NldElkUHJtBgkAAAIAAAABAgAAABNJbnZhbGlkIFdYIGFzc2V0IElECQAETAAAAAIDCQEAAAAJaXNEZWZpbmVkAAAAAQkAA+wAAAABCQACWQAAAAEFAAAADnVzZG5Bc3NldElkUHJtBgkAAAIAAAABAgAAABVJbnZhbGlkIFVTRE4gYXNzZXQgSUQFAAAAA25pbAMJAAAAAAAAAgUAAAAGY2hlY2tzBQAAAAZjaGVja3MJAAUUAAAAAgkABEwAAAACCQEAAAALU3RyaW5nRW50cnkAAAACBQAAABZrZXlBc3NldHNTdG9yZUNvbnRyYWN0BQAAABZhc3NldHNTdG9yZUNvbnRyYWN0UHJtCQAETAAAAAIJAQAAAAtTdHJpbmdFbnRyeQAAAAIFAAAAE2tleUJvb3N0aW5nQ29udHJhY3QFAAAAE2Jvb3N0aW5nQ29udHJhY3RQcm0JAARMAAAAAgkBAAAAC1N0cmluZ0VudHJ5AAAAAgUAAAATa2V5RW1pc3Npb25Db250cmFjdAUAAAATZW1pc3Npb25Db250cmFjdFBybQkABEwAAAACCQEAAAALU3RyaW5nRW50cnkAAAACBQAAABJrZXlGYWN0b3J5Q29udHJhY3QFAAAAEmZhY3RvcnlDb250cmFjdFBybQkABEwAAAACCQEAAAALU3RyaW5nRW50cnkAAAACBQAAABNrZXlVc2VyUG9vbENvbnRyYWN0BQAAABN1c2VyUG9vbENvbnRyYWN0UHJtCQAETAAAAAIJAQAAAAtTdHJpbmdFbnRyeQAAAAIFAAAAGWtleVZvdGluZ0VtaXNzaW9uQ29udHJhY3QFAAAAGXZvdGluZ0VtaXNzaW9uQ29udHJhY3RQcm0JAARMAAAAAgkBAAAADEludGVnZXJFbnRyeQAAAAIFAAAADGtleUZlZUFtb3VudAUAAAAMZmVlQW1vdW50UHJtCQAETAAAAAIJAQAAAAxJbnRlZ2VyRW50cnkAAAACBQAAAA5rZXlFcG9jaExlbmd0aAUAAAARdm90aW5nRHVyYXRpb25Qcm0JAARMAAAAAgkBAAAADEludGVnZXJFbnRyeQAAAAIFAAAAEWtleUZpbmFsaXplUmV3YXJkBQAAABFmaW5hbGl6ZVJld2FyZFBybQkABEwAAAACCQEAAAALU3RyaW5nRW50cnkAAAACBQAAAAxrZXlXeEFzc2V0SWQFAAAADHd4QXNzZXRJZFBybQkABEwAAAACCQEAAAALU3RyaW5nRW50cnkAAAACBQAAAA5rZXlVc2RuQXNzZXRJZAUAAAAOdXNkbkFzc2V0SWRQcm0FAAAAA25pbAUAAAAEdW5pdAkAAAIAAAABAgAAACRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4AAAABaQEAAAANY29uc3RydWN0b3JWMgAAAAEAAAAJdGhyZXNob2xkBAAAAAZjaGVja3MJAARMAAAAAgkBAAAAC211c3RNYW5hZ2VyAAAAAQUAAAABaQkABEwAAAACAwkAAGYAAAACBQAAAAl0aHJlc2hvbGQAAAAAAAAAAAAGCQAAAgAAAAECAAAAEWludmFsaWQgdGhyZXNob2xkBQAAAANuaWwDCQAAAAAAAAIFAAAABmNoZWNrcwUAAAAG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bm90IHZlcmlmaWVkAwkAAAAAAAACBQAAAANpbnYFAAAAA2ludgUAAAAEdW5pdAkAAAIAAAABAgAAACRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4DCQAAAAAAAAIFAAAAGWVuc3VyZUFtb3VudEFzc2V0VmVyaWZpZWQFAAAAGWVuc3VyZUFtb3VudEFzc2V0VmVyaWZpZWQEAAAACmJ1cm5GZWVJbnYJAAP8AAAABAUAAAAQZW1pc3Npb25Db250cmFjdAIAAAAEYnVybgUAAAADbmlsCQAETAAAAAIJAQAAAA9BdHRhY2hlZFBheW1lbnQAAAACCAUAAAAHcGF5bWVudAAAAAdhc3NldElkCAUAAAAHcGF5bWVudAAAAAZhbW91bnQFAAAAA25pbAMJAAAAAAAAAgUAAAAKYnVybkZlZUludgUAAAAKYnVybkZlZUludgQAAAAPbmV3U3VnZ2VzdEluZGV4BAAAAAckbWF0Y2gwCQAEHwAAAAEJAQAAAA9rZXlTdWdnZXN0SW5kZXgAAAACBQAAAA1hbW91bnRBc3NldElkBQAAAAxwcmljZUFzc2V0SWQDCQAAAQAAAAIFAAAAByRtYXRjaDACAAAAA0ludAQAAAADaW50BQAAAAckbWF0Y2gwCQAAZAAAAAIFAAAAA2ludAAAAAAAAAAAAQAAAAAAAAAAAAkABRQAAAACCQAETAAAAAIJAQAAAAxJbnRlZ2VyRW50cnkAAAACCQEAAAAQa2V5SW5MaXN0QXNzZXRJZAAAAAIFAAAADWFtb3VudEFzc2V0SWQFAAAADHByaWNlQXNzZXRJZAUAAAAPbmV3U3VnZ2VzdEluZGV4CQAETAAAAAIJAQAAAAxJbnRlZ2VyRW50cnkAAAACCQEAAAAOa2V5U3RhcnRIZWlnaHQAAAADBQAAAA1hbW91bnRBc3NldElkBQAAAAxwcmljZUFzc2V0SWQFAAAAD25ld1N1Z2dlc3RJbmRleAUAAAAGaGVpZ2h0CQAETAAAAAIJAQAAAAtTdHJpbmdFbnRyeQAAAAIJAQAAAA9rZXlWb3RpbmdSZXN1bHQAAAADBQAAAA1hbW91bnRBc3NldElkBQAAAAxwcmljZUFzc2V0SWQFAAAAD25ld1N1Z2dlc3RJbmRleAkBAAAACnRvdGFsVm90ZXMA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AAAAAQUAAAAJdm90ZVZhbHVlBwQAAAAQaXNOZXdWb3RlU2ltaWxhcgMDBQAAABFpc1ZvdGVUeXBlU2ltaWxhcgUAAAASaXNWb3RlVmFsdWVTaW1pbGFyBwkAAAIAAAABAgAAABF5b3UgYWxyZWFkeSB2b3RlZAkAA/wAAAAEBQAAAAR0aGlzAgAAAApjYW5jZWxWb3RlCQAETAAAAAIFAAAADWFtb3VudEFzc2V0SWQJAARMAAAAAgUAAAAMcHJpY2VBc3NldElkBQAAAANuaWwFAAAAA25pbAUAAAAQaXNOZXdWb3RlU2ltaWxhcgMJAAABAAAAAgUAAAAHJG1hdGNoMAIAAAAEVW5pdAQAAAABdQUAAAAHJG1hdGNoMAUAAAABdQkAAAIAAAABAgAAAAtNYXRjaCBlcnJvcgMJAAAAAAAAAgUAAAAEdm90ZQUAAAAEdm90ZQQAAAAMdm90aW5nUmVzdWx0CQAEtQAAAAIJAQAAAAV2YWx1ZQAAAAEJAAQiAAAAAQkBAAAAD2tleVZvdGluZ1Jlc3VsdAAAAAMFAAAADWFtb3VudEFzc2V0SWQFAAAADHByaWNlQXNzZXRJZAUAAAAMc3VnZ2VzdEluZGV4BQAAAAlzZXBhcmF0b3IEAAAADXBvc2l0aXZlVm90ZXMJAAGRAAAAAgUAAAAMdm90aW5nUmVzdWx0AAAAAAAAAAABBAAAAA1uZWdhdGl2ZVZvdGVzCQABkQAAAAIFAAAADHZvdGluZ1Jlc3VsdAAAAAAAAAAAAgQAAAAbbmV3UG9zaXRpdmVBbmROZWdhdGl2ZVZvdGVzAwUAAAAHaW5GYXZvcgQAAAAQbmV3UG9zaXRpdmVWb3RlcwkAAGQAAAACCQEAAAANcGFyc2VJbnRWYWx1ZQAAAAEFAAAADXBvc2l0aXZlVm90ZXMJAQAAAAVhc0ludAAAAAEFAAAACWd3eEFtb3VudAkABEwAAAACCQABpAAAAAEFAAAAEG5ld1Bvc2l0aXZlVm90ZXMJAARMAAAAAgUAAAANbmVnYXRpdmVWb3RlcwUAAAADbmlsBAAAABBuZXdOZWdhdGl2ZVZvdGVzCQAAZAAAAAIJAQAAAA1wYXJzZUludFZhbHVlAAAAAQUAAAANbmVnYXRpdmVWb3RlcwkBAAAABWFzSW50AAAAAQUAAAAJZ3d4QW1vdW50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AAAAAgkBAAAAC1N0cmluZ0VudHJ5AAAAAgkBAAAAD2tleVZvdGluZ1Jlc3VsdAAAAAMFAAAADWFtb3VudEFzc2V0SWQFAAAADHByaWNlQXNzZXRJZAUAAAAMc3VnZ2VzdEluZGV4CQEAAAAKdG90YWxWb3RlcwAAAAIJAAGkAAAAAQUAAAAQbmV3UG9zaXRpdmVWb3RlcwUAAAANbmVnYXRpdmVWb3RlcwkABEwAAAACCQEAAAALRGVsZXRlRW50cnkAAAABCQEAAAAHa2V5Vm90ZQAAAAQFAAAADWFtb3VudEFzc2V0SWQFAAAADHByaWNlQXNzZXRJZAUAAAAMc3VnZ2VzdEluZGV4BQAAAAt1c2VyQWRkcmVzcwUAAAADbmlsBQAAAAR1bml0BAAAABBuZXdOZWdhdGl2ZVZvdGVzCQAAZQAAAAIJAQAAAA1wYXJzZUludFZhbHVlAAAAAQUAAAANbmVnYXRpdmVWb3RlcwkBAAAADXBhcnNlSW50VmFsdWUAAAABBQAAAAl2b3RlVmFsdWUJAAUUAAAAAgkABEwAAAACCQEAAAALU3RyaW5nRW50cnkAAAACCQEAAAAPa2V5Vm90aW5nUmVzdWx0AAAAAwUAAAANYW1vdW50QXNzZXRJZAUAAAAMcHJpY2VBc3NldElkBQAAAAxzdWdnZXN0SW5kZXgJAQAAAAp0b3RhbFZvdGVzAAAAAgUAAAANcG9zaXRpdmVWb3RlcwkAAaQAAAABBQAAABBuZXdOZWdhdGl2ZVZvdGVzCQAETAAAAAIJAQAAAAtEZWxldGVFbnRyeQAAAAEJAQAAAAdrZXlWb3RlAAAABAUAAAANYW1vdW50QXNzZXRJZAUAAAAMcHJpY2VBc3NldElkBQAAAAxzdWdnZXN0SW5kZXgFAAAAC3VzZXJBZGRyZXNzBQAAAANuaWwFAAAABHVuaXQFAAAAB2FjdGlvbnMJAAACAAAAAQIAAAAkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuAAAAAWkBAAAABnNldEZlZQAAAAEAAAAGbmV3RmVlBAAAAAZjaGVja3MJAARMAAAAAgkBAAAAC211c3RNYW5hZ2VyAAAAAQUAAAABaQUAAAADbmlsAwkAAAAAAAACBQAAAAZjaGVja3MFAAAABmNoZWNrcwkABEwAAAACCQEAAAAMSW50ZWdlckVudHJ5AAAAAgUAAAAMa2V5RmVlQW1vdW50BQAAAAZuZXdGZWUFAAAAA25pbAkAAAIAAAABAgAAACRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4AAAABaQEAAAAMc2V0VGhyZXNob2xkAAAAAQAAAAxuZXdUaHJlc2hvbGQEAAAABmNoZWNrcwkABEwAAAACCQEAAAALbXVzdE1hbmFnZXIAAAABBQAAAAFpCQAETAAAAAIDCQAAZgAAAAIFAAAADG5ld1RocmVzaG9sZAAAAAAAAAAAAAYJAAACAAAAAQIAAAARaW52YWxpZCB0aHJlc2hvbGQFAAAAA25pbAMJAAAAAAAAAgUAAAAGY2hlY2tzBQAAAAZjaGVja3MJAAUUAAAAAgkABEwAAAACCQEAAAAMSW50ZWdlckVudHJ5AAAAAgUAAAAMa2V5VGhyZXNob2xkBQAAAAxuZXdUaHJlc2hvbGQFAAAAA25pbAUAAAAEdW5pdAkAAAIAAAABAgAAACRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4AAAABaQEAAAAIZmluYWxpemUAAAACAAAADWFtb3VudEFzc2V0SWQAAAAMcHJpY2VBc3NldElkBAAAAAxzdWdnZXN0SW5kZXgJAQAAAAV2YWx1ZQAAAAEJAAQfAAAAAQkBAAAAEGtleUluTGlzdEFzc2V0SWQAAAACBQAAAA1hbW91bnRBc3NldElkBQAAAAxwcmljZUFzc2V0SWQEAAAAEnZvdGluZ0ZpbmlzaEhlaWdodAkAAGQAAAACCQEAAAAFdmFsdWUAAAABCQAEHwAAAAEJAQAAAA5rZXlTdGFydEhlaWdodAAAAAMFAAAADWFtb3VudEFzc2V0SWQFAAAADHByaWNlQXNzZXRJZAUAAAAMc3VnZ2VzdEluZGV4CQEAAAAFdmFsdWUAAAABCQAEHwAAAAEFAAAADmtleUVwb2NoTGVuZ3RoBAAAAAZjaGVja3MJAARMAAAAAgMJAQAAAAlpc0RlZmluZWQAAAABCQAEHwAAAAEJAQAAABBrZXlJbkxpc3RBc3NldElkAAAAAgUAAAANYW1vdW50QXNzZXRJZAUAAAAMcHJpY2VBc3NldElkBgkAAAIAAAABAgAAAA5ubyBhc3NldHMgcGFpcgkABEwAAAACAwkAAGcAAAACBQAAAAZoZWlnaHQFAAAAEnZvdGluZ0ZpbmlzaEhlaWdodAYJAAACAAAAAQIAAAAiaW5zdWZmaWNpZW50IGhlaWdodCBmb3IgY29tcGxldGlvbgUAAAADbmlsAwkAAAAAAAACBQAAAAZjaGVja3MFAAAABmNoZWNrcwQAAAAMdm90aW5nUmVzdWx0CQAEtQAAAAIJAQAAAAV2YWx1ZQAAAAEJAAQiAAAAAQkBAAAAD2tleVZvdGluZ1Jlc3VsdAAAAAMFAAAADWFtb3VudEFzc2V0SWQFAAAADHByaWNlQXNzZXRJZAUAAAAMc3VnZ2VzdEluZGV4BQAAAAlzZXBhcmF0b3IEAAAADXBvc2l0aXZlVm90ZXMJAQAAAA1wYXJzZUludFZhbHVlAAAAAQkAAZEAAAACBQAAAAx2b3RpbmdSZXN1bHQAAAAAAAAAAAEEAAAADW5lZ2F0aXZlVm90ZXMJAQAAAA1wYXJzZUludFZhbHVlAAAAAQkAAZEAAAACBQAAAAx2b3RpbmdSZXN1bHQAAAAAAAAAAAIEAAAACGFsbFZvdGVzCQAAZAAAAAIFAAAADXBvc2l0aXZlVm90ZXMFAAAADW5lZ2F0aXZlVm90ZXMEAAAACXRocmVzaG9sZAkBAAAADGdldEludE9yRmFpbAAAAAEFAAAADGtleVRocmVzaG9sZAQAAAAHYWN0aW9ucwMDCQAAZwAAAAIFAAAACGFsbFZvdGVzBQAAAAl0aHJlc2hvbGQJAABmAAAAAgUAAAANcG9zaXRpdmVWb3RlcwUAAAANbmVnYXRpdmVWb3RlcwcEAAAAA3JlcwkAA/wAAAAEBQAAAA9mYWN0b3J5Q29udHJhY3QCAAAAFnNldFd4RW1pc3Npb25Qb29sTGFiZWwJAARMAAAAAgUAAAANYW1vdW50QXNzZXRJZAkABEwAAAACBQAAAAxwcmljZUFzc2V0SWQFAAAAA25pbAUAAAADbmlsAwkAAAAAAAACBQAAAANyZXMFAAAAA3JlcwQAAAARdm90aW5nRW1pc3Npb25JbnYJAAP8AAAABAUAAAAWdm90aW5nRW1pc3Npb25Db250cmFjdAIAAAAGY3JlYXRlCQAETAAAAAIFAAAADWFtb3VudEFzc2V0SWQJAARMAAAAAgUAAAAMcHJpY2VBc3NldElkBQAAAANuaWwFAAAAA25pbAMJAAAAAAAAAgUAAAARdm90aW5nRW1pc3Npb25JbnYFAAAAEXZvdGluZ0VtaXNzaW9uSW52CQAFFAAAAAIJAARMAAAAAgkBAAAAC0RlbGV0ZUVudHJ5AAAAAQkBAAAAEGtleUluTGlzdEFzc2V0SWQAAAACBQAAAA1hbW91bnRBc3NldElkBQAAAAxwcmljZUFzc2V0SWQFAAAAA25pbAUAAAAEdW5pdAkAAAIAAAABAgAAACRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4JAAACAAAAAQIAAAAkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuCQAFFAAAAAIJAARMAAAAAgkBAAAAC0RlbGV0ZUVudHJ5AAAAAQkBAAAAEGtleUluTGlzdEFzc2V0SWQAAAACBQAAAA1hbW91bnRBc3NldElkBQAAAAxwcmljZUFzc2V0SWQFAAAAA25pbAUAAAAEdW5pdAUAAAAHYWN0aW9ucwkAAAIAAAABAgAAACRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4AAAABaQEAAAAKc2V0TWFuYWdlcgAAAAEAAAAXcGVuZGluZ01hbmFnZXJQdWJsaWNLZXkEAAAAC2NoZWNrQ2FsbGVyCQEAAAALbXVzdE1hbmFnZXIAAAABBQAAAAFpAwkAAAAAAAACBQAAAAtjaGVja0NhbGxlcgUAAAALY2hlY2tDYWxsZXIEAAAAFWNoZWNrTWFuYWdlclB1YmxpY0tleQkAAlkAAAABBQAAABdwZW5kaW5nTWFuYWdlclB1YmxpY0tleQMJAAAAAAAAAgUAAAAVY2hlY2tNYW5hZ2VyUHVibGljS2V5BQAAABVjaGVja01hbmFnZXJQdWJsaWNLZXkJAARMAAAAAgkBAAAAC1N0cmluZ0VudHJ5AAAAAgkBAAAAGmtleVBlbmRpbmdNYW5hZ2VyUHVibGljS2V5AAAAAAUAAAAXcGVuZGluZ01hbmFnZXJQdWJsaWNLZXkFAAAAA25pbAkAAAIAAAABAgAAACRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4JAAACAAAAAQIAAAAkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuAAAAAWkBAAAADmNvbmZpcm1NYW5hZ2VyAAAAAAQAAAACcG0JAQAAAB1wZW5kaW5nTWFuYWdlclB1YmxpY0tleU9yVW5pdAAAAAAEAAAABWhhc1BNAwkBAAAACWlzRGVmaW5lZAAAAAEFAAAAAnBtBgkAAAIAAAABAgAAABJubyBwZW5kaW5nIG1hbmFnZXIDCQAAAAAAAAIFAAAABWhhc1BNBQAAAAVoYXNQTQQAAAAHY2hlY2tQTQMJAAAAAAAAAggFAAAAAWkAAAAPY2FsbGVyUHVibGljS2V5CQEAAAAFdmFsdWUAAAABBQAAAAJwbQYJAAACAAAAAQIAAAAbeW91IGFyZSBub3QgcGVuZGluZyBtYW5hZ2VyAwkAAAAAAAACBQAAAAdjaGVja1BNBQAAAAdjaGVja1BNCQAETAAAAAIJAQAAAAtTdHJpbmdFbnRyeQAAAAIJAQAAABNrZXlNYW5hZ2VyUHVibGljS2V5AAAAAAkAAlgAAAABCQEAAAAFdmFsdWUAAAABBQAAAAJwbQkABEwAAAACCQEAAAALRGVsZXRlRW50cnkAAAABCQEAAAAaa2V5UGVuZGluZ01hbmFnZXJQdWJsaWNLZXkAAAAABQAAAANuaWwJAAACAAAAAQIAAAAkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuCQAAAgAAAAECAAAAJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgAAAAEAAAACdHgBAAAABnZlcmlmeQAAAAAEAAAAD3RhcmdldFB1YmxpY0tleQQAAAAHJG1hdGNoMAkBAAAAFm1hbmFnZXJQdWJsaWNLZXlPclVuaXQAAAAAAwkAAAEAAAACBQAAAAckbWF0Y2gwAgAAAApCeXRlVmVjdG9yBAAAAAJwawUAAAAHJG1hdGNoMAUAAAACcGsDCQAAAQAAAAIFAAAAByRtYXRjaDACAAAABFVuaXQIBQAAAAJ0eAAAAA9zZW5kZXJQdWJsaWNLZXkJAAACAAAAAQIAAAALTWF0Y2ggZXJyb3IJAAH0AAAAAwgFAAAAAnR4AAAACWJvZHlCeXRlcwkAAZEAAAACCAUAAAACdHgAAAAGcHJvb2ZzAAAAAAAAAAAABQAAAA90YXJnZXRQdWJsaWNLZXlV7N8C", "chainId": 84, "height": 2450334, "applicationStatus": "succeeded", "spentComplexity": 0 } View: original | compacted Prev: 4uwaBne7gBeXF6e9c5H1cSfdmGnScgk6yD1vTw3fQVve Next: none Diff:
OldNewDifferences
33 {-# CONTENT_TYPE DAPP #-}
44 let separator = "__"
55
6-let maxDepthDefault = 10
6+let keyFeeAmount = makeString(["%s", "fee"], separator)
7+
8+let keyUsdnAssetId = makeString(["%s", "usdnAssetId"], separator)
9+
10+let keyEpochLength = makeString(["%s", "epochLength"], separator)
11+
12+let keyFinalizeReward = makeString(["%s", "finalizeReward"], separator)
13+
14+let keyWxAssetId = makeString(["%s", "wxAssetId"], separator)
15+
16+let keyAssetsStoreContract = makeString(["%s", "assetsStoreContract"], separator)
17+
18+let keyUserPoolContract = makeString(["%s", "userPoolContract"], separator)
19+
20+let keyEmissionContract = makeString(["%s", "emissionContract"], separator)
21+
22+let keyBoostingContract = makeString(["%s", "boostingContract"], separator)
23+
24+let keyFactoryContract = makeString(["%s", "factoryContract"], separator)
25+
26+let keyVotingEmissionContract = makeString(["%s", "votingEmissionContract"], separator)
27+
28+let keyThreshold = makeString(["%s", "votingThreshold"], separator)
29+
30+func getStringOrFail (key) = valueOrErrorMessage(getString(this, key), (key + " is not defined"))
31+
32+
33+func getIntOrFail (key) = valueOrErrorMessage(getInteger(this, key), (key + " is not defined"))
34+
35+
36+func keyInListAssetId (amountAssetId,priceAssetId) = makeString(["%s%s%s", "inList", amountAssetId, priceAssetId], separator)
37+
38+
39+func keySuggestIndex (amountAssetId,priceAssetId) = makeString(["%s%s%s", "suggestIndex", amountAssetId, priceAssetId], separator)
40+
41+
42+func keyStartHeight (amountAssetId,priceAssetId,suggestIndex) = makeString(["%s%s%s%d", "startHeight", amountAssetId, priceAssetId, toString(suggestIndex)], separator)
43+
44+
45+func keyVotingResult (amountAssetId,priceAssetId,suggestIndex) = makeString(["%s%s%s%d", "votingResult", amountAssetId, priceAssetId, toString(suggestIndex)], separator)
46+
47+
48+func totalVotes (totalYes,totalNo) = makeString(["%d%d", totalYes, totalNo], separator)
49+
50+
51+func keyVote (amountAssetId,priceAssetId,suggestIndex,voterAddress) = makeString(["%s%s%s%d%s", "vote", amountAssetId, priceAssetId, toString(suggestIndex), voterAddress], separator)
52+
53+
54+func keyVoteValue (gwxAmount,vote) = {
55+ let key = if (vote)
56+ then makeString(["%d%s", gwxAmount, "yes"], separator)
57+ else makeString(["%d%s", gwxAmount, "no"], separator)
58+ key
59+ }
60+
61+
62+func keyInList (pool) = {
63+ let $t025262566 = pool
64+ let amountAssetId = $t025262566._1
65+ let priceAssetId = $t025262566._2
66+ makeString(["%s%s%s", "inList", amountAssetId, priceAssetId], separator)
67+ }
68+
69+
70+func keyManagerPublicKey () = "%s__managerPublicKey"
71+
72+
73+func keyPendingManagerPublicKey () = "%s__pendingManagerPublicKey"
74+
75+
76+let assetsStoreContract = addressFromStringValue(getStringOrFail(keyAssetsStoreContract))
77+
78+let boostingContract = addressFromStringValue(getStringOrFail(keyBoostingContract))
79+
80+let emissionContract = addressFromStringValue(getStringOrFail(keyEmissionContract))
81+
82+let factoryContract = addressFromStringValue(getStringOrFail(keyFactoryContract))
83+
84+let userPoolContract = addressFromStringValue(getStringOrFail(keyUserPoolContract))
85+
86+let votingEmissionContract = addressFromStringValue(getStringOrFail(keyVotingEmissionContract))
87+
88+func managerPublicKeyOrUnit () = match getString(keyManagerPublicKey()) {
89+ case s: String =>
90+ fromBase58String(s)
91+ case _: Unit =>
92+ unit
93+ case _ =>
94+ throw("Match error")
95+}
96+
97+
98+func pendingManagerPublicKeyOrUnit () = match getString(keyPendingManagerPublicKey()) {
99+ case s: String =>
100+ fromBase58String(s)
101+ case _: Unit =>
102+ unit
103+ case _ =>
104+ throw("Match error")
105+}
106+
107+
108+func isManager (i) = match managerPublicKeyOrUnit() {
109+ case pk: ByteVector =>
110+ (i.callerPublicKey == pk)
111+ case _: Unit =>
112+ (i.caller == this)
113+ case _ =>
114+ throw("Match error")
115+}
116+
117+
118+func mustManager (i) = if (isManager(i))
119+ then true
120+ else throw("permission denied")
121+
7122
8123 func asInt (val) = match val {
9124 case valInt: Int =>
10125 valInt
11126 case _ =>
12- throw("Failed to cast into Integer")
127+ throw("failed to cast into Integer")
13128 }
14129
15130
16-func asBool (val) = match val {
17- case valBool: Boolean =>
18- valBool
19- case _ =>
20- throw("Failed to cast into Boolean")
21-}
22-
23-
24-let keyFeeAmount = makeString(["%s", "fee"], separator)
25-
26-let keyWxAssetId = makeString(["%s", "wxAssetId"], separator)
27-
28-let keyVotingThreshold = makeString(["%s", "votingThreshold"], separator)
29-
30-let keyVotingDuration = makeString(["%s", "epochLength"], separator)
31-
32-let keyVoteBeforeElimination = makeString(["%s", "voteBeforeElimination"], separator)
33-
34-let keyStartHeight = makeString(["%s", "currentVotingHeightStart"], separator)
35-
36-let keyCurrentPeriod = makeString(["%s", "currentEpoch"], separator)
37-
38-let keyBoostingContract = makeString(["%s", "boostingContract"], separator)
39-
40-let keyEmissionContract = makeString(["%s", "emissionContract"], separator)
41-
42-let keyAssetsStoreContract = makeString(["%s", "assetsStoreContract"], separator)
43-
44-let keyLatestProcessedAsset = makeString(["%s", "latestProcessedAsset"], separator)
45-
46-let keyLatestProcessedUser = makeString(["%s", "latestProcessedUser"], separator)
47-
48-let keyLatestProcessedUserRemove = makeString(["%s", "latestProcessedUserRemove"], separator)
49-
50-let keyLatestProcessedAssetTransfer = makeString(["%s", "latestProcessedAssetTransfer"], separator)
51-
52-let keyLatestProcessedUserTransfer = makeString(["%s", "latestProcessedUserTransfer"], separator)
53-
54-let keyLatestProcessedUserRemoveTransfer = makeString(["%s", "latestProcessedUserRemoveTransfer"], separator)
55-
56-let keyMaxDepth = makeString(["%s", "maxDepth"], separator)
57-
58-func keyVotesTransferFinishedByPeriod (period) = makeString(["%s", "votesTransferFinished", toString(period)], separator)
59-
60-
61-let assetsListName = "__assets"
62-
63-func getVotesListName (assetId) = ("%s__votes__" + assetId)
64-
65-
66-func keyListHead (listName) = makeString([("%s%s" + listName), "head"], separator)
67-
68-
69-func keyListSize (listName) = makeString([("%s%s" + listName), "size"], separator)
70-
71-
72-func keyListPrev (listName,id) = makeString([("%s%s%s" + listName), id, "prev"], separator)
73-
74-
75-func keyListNext (listName,id) = makeString([("%s%s%s" + listName), id, "next"], separator)
76-
77-
78-func keyAssetVerifiedByPeriod (assetId,period) = makeString(["%s%d%s", "verifiedAt", toString(period), assetId], separator)
79-
80-
81-func keyAssetVerified (assetId) = makeString(["%s%s", "verified", assetId], separator)
82-
83-
84-func keyAssetWasEliminated (assetId,period) = makeString(["%s%s%d", "eliminated", assetId, toString(period)], separator)
85-
86-
87-func keyVoteResultByPeriod (assetId,period) = makeString(["%s%d%s", "votingResultAtAsset", toString(period), assetId], separator)
88-
89-
90-func formatVoteResult (totalYes,totalNo,verified) = makeString(["%d%d%s", toString(totalYes), toString(totalNo), toString(verified)], separator)
91-
92-
93-func parseVoteResult (input) = {
94- let parts = split(input, separator)
95- let totalYesIdx = 1
96- let totalNoIdx = 2
97- let verifiedIdx = 3
98- let totalYes = parseIntValue(parts[totalYesIdx])
99- let totalNo = parseIntValue(parts[totalNoIdx])
100- let verified = if ((size(parts) == 4))
101- then (parts[verifiedIdx] == "true")
102- else false
103- $Tuple3(totalYes, totalNo, verified)
104- }
105-
106-
107-func keyUserVoteByPeriod (userAddress,assetId,period) = makeString(["%s%d%s%s", "vru", toString(period), assetId, userAddress], separator)
108-
109-
110-func formatUserVote (total,inFavor) = {
111- let totalYes = if (inFavor)
112- then total
113- else 0
114- let totalNo = if (inFavor)
115- then 0
116- else total
117- makeString(["%d%d", toString(totalYes), toString(totalNo)], separator)
118- }
119-
120-
121-func parseUserVote (input) = {
122- let parts = split(input, separator)
123- let totalYesIdx = 1
124- let totalNoIdx = 2
125- let totalYes = parseIntValue(parts[totalYesIdx])
126- let totalNo = parseIntValue(parts[totalNoIdx])
127- let inFavor = if ((totalYes > 0))
128- then (totalNo == 0)
129- else false
130- let against = if ((totalYes == 0))
131- then (totalNo > 0)
132- else false
133- let checkTotals = if (if (inFavor)
131+@Callable(i)
132+func constructor (assetsStoreContractPrm,boostingContractPrm,emissionContractPrm,factoryContractPrm,userPoolContractPrm,votingEmissionContractPrm,feeAmountPrm,wxAssetIdPrm,votingDurationPrm,usdnAssetIdPrm,finalizeRewardPrm) = {
133+ let checks = [mustManager(i), if (isDefined(addressFromString(assetsStoreContractPrm)))
134134 then true
135- else against)
135+ else throw("Invalid asset_store contract address"), if (isDefined(addressFromString(boostingContractPrm)))
136136 then true
137- else throw("Invalid user vote value")
138- if ((checkTotals == checkTotals))
139- then {
140- let total = if (inFavor)
141- then totalYes
142- else totalNo
143- $Tuple2(total, inFavor)
144- }
145- else throw("Strict value is not equal to itself.")
146- }
147-
148-
149-func keyUserVoteHistory (type,userAddress,assetId,txId,period) = makeString(["%s%s%s%s%s%d", "history", type, assetId, userAddress, txId, toString(period)], separator)
150-
151-
152-func thisOnly (i) = if ((i.caller == this))
153- then true
154- else throw("Permission denied")
155-
156-
157-func getIntegerOrZero (key) = valueOrElse(getInteger(this, key), 0)
158-
159-
160-func getIntegerOrFail (key) = valueOrErrorMessage(getInteger(this, key), (key + " is not defined"))
161-
162-
163-func getStringOrEmpty (key) = valueOrElse(getString(this, key), "")
164-
165-
166-func getStringOrFail (key) = valueOrErrorMessage(getString(this, key), (key + " is not defined"))
167-
168-
169-let feeAmount = getIntegerOrFail(keyFeeAmount)
170-
171-let wxAssetId = fromBase58String(getStringOrFail(keyWxAssetId))
172-
173-let votingThreshold = getIntegerOrFail(keyVotingThreshold)
174-
175-let votingDuration = getIntegerOrFail(keyVotingDuration)
176-
177-let voteBeforeElimination = getIntegerOrFail(keyVoteBeforeElimination)
178-
179-let startHeight = getIntegerOrFail(keyStartHeight)
180-
181-let currentPeriod = getIntegerOrFail(keyCurrentPeriod)
182-
183-let boostingContract = addressFromStringValue(getStringOrFail(keyBoostingContract))
184-
185-let emissionContract = addressFromStringValue(getStringOrFail(keyEmissionContract))
186-
187-let assetsStoreContract = addressFromStringValue(getStringOrFail(keyAssetsStoreContract))
188-
189-let maxDepth = valueOrElse(getInteger(keyMaxDepth), maxDepthDefault)
190-
191-let endHeight = (startHeight + votingDuration)
192-
193-func getUserGwxAmountAtHeight (userAddress,targetHeight) = {
194- let gwxAmount = invoke(boostingContract, "getUserGwxAmountAtHeightREADONLY", [userAddress, targetHeight], nil)
195- asInt(gwxAmount)
196- }
197-
198-
199-func getVoteResultAction (assetId,total,inFavor,period,verified) = {
200- let voteResultKey = keyVoteResultByPeriod(assetId, period)
201- let $t062886597 = match getString(voteResultKey) {
202- case s: String =>
203- parseVoteResult(s)
204- case _: Unit =>
205- match getString(keyVoteResultByPeriod(assetId, (period - 1))) {
206- case s: String =>
207- $Tuple3(0, 0, parseVoteResult(s)._3)
208- case _: Unit =>
209- $Tuple3(0, 0, false)
210- case _ =>
211- throw("Match error")
212- }
213- case _ =>
214- throw("Match error")
215- }
216- let oldTotalYes = $t062886597._1
217- let oldTotalNo = $t062886597._2
218- let oldVerified = $t062886597._3
219- StringEntry(voteResultKey, formatVoteResult((oldTotalYes + (if (inFavor)
220- then total
221- else 0)), (oldTotalNo + (if (inFavor)
222- then 0
223- else total)), if ((verified == unit))
224- then oldVerified
225- else value(verified)))
226- }
227-
228-
229-func containsNode (listName,id) = {
230- let head = getString(this, keyListHead(listName))
231- let prev = getString(this, keyListPrev(listName, id))
232- let next = getString(this, keyListNext(listName, id))
233- if (if ((id == head))
137+ else throw("Invalid boosting contract address"), if (isDefined(addressFromString(userPoolContractPrm)))
234138 then true
235- else (prev != unit))
139+ else throw("Invalid user_pools contract address"), if (isDefined(addressFromString(emissionContractPrm)))
236140 then true
237- else (next != unit)
238- }
239-
240-
241-func insertNode (listName,id) = {
242- let head = getString(this, keyListHead(listName))
243- let listSize = valueOrElse(getInteger(this, keyListSize(listName)), 0)
244- let checkNode = if (!(containsNode(listName, id)))
141+ else throw("Invalid emission contract address"), if (isDefined(addressFromString(factoryContractPrm)))
245142 then true
246- else throw("Node exists")
247- if ((checkNode == checkNode))
248- then (([IntegerEntry(keyListSize(listName), (listSize + 1))] ++ (if (isDefined(head))
249- then [StringEntry(keyListNext(listName, id), value(head)), StringEntry(keyListPrev(listName, value(head)), id)]
250- else nil)) ++ [StringEntry(keyListHead(listName), id)])
251- else throw("Strict value is not equal to itself.")
252- }
253-
254-
255-func deleteNode (listName,id) = {
256- let head = getString(this, keyListHead(listName))
257- let listSize = valueOrElse(getInteger(this, keyListSize(listName)), 0)
258- let prev = getString(this, keyListPrev(listName, id))
259- let next = getString(this, keyListNext(listName, id))
260- ([IntegerEntry(keyListSize(listName), (listSize - 1))] ++ (if (if (isDefined(prev))
261- then isDefined(next)
262- else false)
263- then [StringEntry(keyListNext(listName, value(prev)), value(next)), StringEntry(keyListPrev(listName, value(next)), value(prev)), DeleteEntry(keyListPrev(listName, id)), DeleteEntry(keyListNext(listName, id))]
264- else if (isDefined(next))
265- then [StringEntry(keyListHead(listName), value(next)), DeleteEntry(keyListNext(listName, id)), DeleteEntry(keyListPrev(listName, value(next)))]
266- else if (isDefined(prev))
267- then [DeleteEntry(keyListPrev(listName, id)), DeleteEntry(keyListNext(listName, value(prev)))]
268- else if ((id == head))
269- then [DeleteEntry(keyListHead(listName))]
270- else throw("Invalid node")))
271- }
272-
273-
274-func processVote (assetId,userAddressOrUnit,latestProcessedAssetKey,latestProcessedUserKey,latestProcessedUserRemoveKey) = {
275- let updateLatestProcessedAssetAction = StringEntry(latestProcessedAssetKey, assetId)
276- let deleteLatestProcessedUserAction = DeleteEntry(latestProcessedUserKey)
277- if ((userAddressOrUnit == unit))
278- then [updateLatestProcessedAssetAction, deleteLatestProcessedUserAction]
279- else {
280- let userAddress = value(userAddressOrUnit)
281- let updateLatestProcessedUserAction = StringEntry(latestProcessedUserKey, userAddress)
282- let userVoteKey = keyUserVoteByPeriod(userAddress, assetId, currentPeriod)
283- let userVoteOrUnit = getString(userVoteKey)
284- let voteActions = if ((userVoteOrUnit == unit))
285- then {
286- let userGwxAmountAtEndHeight = getUserGwxAmountAtHeight(userAddress, endHeight)
287- if ((userGwxAmountAtEndHeight == 0))
288- then [BooleanEntry(latestProcessedUserRemoveKey, true)]
289- else {
290- let previousPeriod = (currentPeriod - 1)
291- let assetWasEliminated = valueOrElse(getBoolean(keyAssetWasEliminated(assetId, previousPeriod)), false)
292- let userPreviousVoteOrUnit = if (assetWasEliminated)
293- then unit
294- else getString(keyUserVoteByPeriod(userAddress, assetId, previousPeriod))
295- if ((userPreviousVoteOrUnit == unit))
296- then nil
297- else {
298- let $t01080110874 = parseUserVote(value(userPreviousVoteOrUnit))
299- let prevTotal = $t01080110874._1
300- let inFavor = $t01080110874._2
301- let total = min([prevTotal, userGwxAmountAtEndHeight])
302-[StringEntry(userVoteKey, formatUserVote(total, inFavor)), getVoteResultAction(assetId, total, inFavor, currentPeriod, unit)]
303- }
304- }
305- }
306- else nil
307- ((voteActions :+ updateLatestProcessedAssetAction) :+ updateLatestProcessedUserAction)
308- }
309- }
310-
311-
312-func assetShouldBeEliminated (assetId,period) = !(valueOrElse(getBoolean(keyAssetVerifiedByPeriod(assetId, period)), true))
313-
314-
315-func eliminationCheck (assetId) = if (if (assetShouldBeEliminated(assetId, (currentPeriod - 1)))
316- then assetShouldBeEliminated(assetId, (currentPeriod - 2))
317- else false)
318- then assetShouldBeEliminated(assetId, (currentPeriod - 3))
319- else false
320-
321-
322-@Callable(i)
323-func constructor (boostingContractPrm,emissionContractPrm,assetsStoreContractPrm,feeAmountPrm,wxAssetIdPrm,votingThresholdPrm,votingDurationPrm,voteBeforeEliminationPrm,startHeightPrm,maxDepthPrm) = {
324- let checks = [thisOnly(i), if (isDefined(addressFromString(boostingContractPrm)))
143+ else throw("Invalid factory contract address"), if ((feeAmountPrm >= 0))
325144 then true
326- else throw("Invalid boosting contract address"), if (isDefined(addressFromString(emissionContractPrm)))
145+ else throw("Invalid fee amount"), if ((votingDurationPrm > 0))
327146 then true
328- else throw("Invalid emission contract address"), if (isDefined(addressFromString(assetsStoreContractPrm)))
147+ else throw("Invalid voting duration"), if ((finalizeRewardPrm >= 0))
329148 then true
330- else throw("Invalid asset_store contract address"), if ((feeAmountPrm >= 0))
149+ else throw("Invalid finalize reward"), if (isDefined(assetInfo(fromBase58String(wxAssetIdPrm))))
331150 then true
332- else throw("Invalid fee amount"), if (isDefined(assetInfo(fromBase58String(wxAssetIdPrm))))
151+ else throw("Invalid WX asset ID"), if (isDefined(assetInfo(fromBase58String(usdnAssetIdPrm))))
333152 then true
334- else throw("Invalid WX asset ID"), if ((votingThresholdPrm >= 0))
335- then true
336- else throw("Invalid voting threshold"), if ((votingDurationPrm > 0))
337- then true
338- else throw("Invalid voting duration"), if (((startHeightPrm + votingDurationPrm) > height))
339- then true
340- else throw("Invalid start height")]
153+ else throw("Invalid USDN asset ID")]
341154 if ((checks == checks))
342- then $Tuple2([StringEntry(keyBoostingContract, boostingContractPrm), StringEntry(keyEmissionContract, emissionContractPrm), StringEntry(keyAssetsStoreContract, assetsStoreContractPrm), IntegerEntry(keyFeeAmount, feeAmountPrm), StringEntry(keyWxAssetId, wxAssetIdPrm), IntegerEntry(keyVotingThreshold, votingThresholdPrm), IntegerEntry(keyVotingDuration, votingDurationPrm), IntegerEntry(keyVoteBeforeElimination, voteBeforeEliminationPrm), IntegerEntry(keyStartHeight, startHeightPrm), IntegerEntry(keyCurrentPeriod, 0), IntegerEntry(keyMaxDepth, maxDepthPrm)], unit)
155+ then $Tuple2([StringEntry(keyAssetsStoreContract, assetsStoreContractPrm), StringEntry(keyBoostingContract, boostingContractPrm), StringEntry(keyEmissionContract, emissionContractPrm), StringEntry(keyFactoryContract, factoryContractPrm), StringEntry(keyUserPoolContract, userPoolContractPrm), StringEntry(keyVotingEmissionContract, votingEmissionContractPrm), IntegerEntry(keyFeeAmount, feeAmountPrm), IntegerEntry(keyEpochLength, votingDurationPrm), IntegerEntry(keyFinalizeReward, finalizeRewardPrm), StringEntry(keyWxAssetId, wxAssetIdPrm), StringEntry(keyUsdnAssetId, usdnAssetIdPrm)], unit)
343156 else throw("Strict value is not equal to itself.")
344157 }
345158
346159
347160
348161 @Callable(i)
349-func suggest (assetId,assetImage) = {
350- let info = valueOrErrorMessage(assetInfo(fromBase58String(assetId)), "Invalid asset ID")
351- let payment = value(i.payments[0])
352- let checks = [if ((info.issuer == i.caller))
162+func constructorV2 (threshold) = {
163+ let checks = [mustManager(i), if ((threshold > 0))
353164 then true
354- else throw("Asset can only be suggested by its issuer"), if ((value(payment.assetId) == wxAssetId))
355- then true
356- else throw("Invalid fee asset"), if ((payment.amount == feeAmount))
357- then true
358- else throw("Invalid fee amount")]
165+ else throw("invalid threshold")]
359166 if ((checks == checks))
360- then {
361- let assetsStoreCreateOrUpdateInv = invoke(assetsStoreContract, "createOrUpdate", [assetId, assetImage, false], nil)
362- if ((assetsStoreCreateOrUpdateInv == assetsStoreCreateOrUpdateInv))
363- then {
364- let burnFeeInv = invoke(emissionContract, "burn", nil, [AttachedPayment(payment.assetId, payment.amount)])
365- if ((burnFeeInv == burnFeeInv))
366- then {
367- let addAssetActions = insertNode(assetsListName, assetId)
368- let nextPeriod = (currentPeriod + 1)
369- let targetPeriod = if ((endHeight > height))
370- then currentPeriod
371- else nextPeriod
372- $Tuple2((addAssetActions :+ getVoteResultAction(assetId, 0, true, targetPeriod, false)), unit)
373- }
374- else throw("Strict value is not equal to itself.")
375- }
376- else throw("Strict value is not equal to itself.")
377- }
167+ then $Tuple2([IntegerEntry(keyThreshold, threshold)], unit)
378168 else throw("Strict value is not equal to itself.")
379169 }
380170
381171
382172
383173 @Callable(i)
384-func vote (assetId,inFavor) = {
385- let checkAsset = if (containsNode(assetsListName, assetId))
386- then true
387- else throw("Invalid asset")
388- if ((checkAsset == checkAsset))
174+func suggest (amountAssetId,priceAssetId) = {
175+ let pool = $Tuple2(amountAssetId, priceAssetId)
176+ let payment = value(i.payments[0])
177+ let checkAmountAsset = if ((amountAssetId == "WAVES"))
178+ then unit
179+ else {
180+ let info = valueOrErrorMessage(assetInfo(fromBase58String(amountAssetId)), "invalid amountAssetId ID")
181+ if ((info.scripted == false))
182+ then true
183+ else throw("asset is smart")
184+ }
185+ if ((checkAmountAsset == checkAmountAsset))
389186 then {
390- let checkHeight = if ((endHeight > height))
187+ let checks = [if ((toBase58String(value(payment.assetId)) == value(getString(keyWxAssetId))))
391188 then true
392- else throw("Current voting is over but results are not finalized")
393- if ((checkHeight == checkHeight))
189+ else throw("invalid fee asset"), if ((payment.amount == value(getInteger(keyFeeAmount))))
190+ then true
191+ else throw("invalid fee amount"), if ((getInteger(keyInListAssetId(amountAssetId, priceAssetId)) == unit))
192+ then true
193+ else throw("already in voting list"), if (!(valueOrElse(getBoolean(votingEmissionContract, keyInList(pool)), false)))
194+ then true
195+ else throw("pool is in emission voting list")]
196+ if ((checks == checks))
394197 then {
395- let userAddress = toString(i.caller)
396- let gwxAmountAtEnd = getUserGwxAmountAtHeight(userAddress, endHeight)
397- let checkGwxAmountAtEnd = if ((gwxAmountAtEnd > 0))
398- then true
399- else throw("You'll not have gWX at the end of voting")
400- if ((checkGwxAmountAtEnd == checkGwxAmountAtEnd))
198+ let ensureActive = match invoke(userPoolContract, "statusREADONLY", [amountAssetId, priceAssetId], nil) {
199+ case s: String =>
200+ if ((s == "active"))
201+ then true
202+ else throw("user pool is not active")
203+ case _ =>
204+ throw("user pool is not active")
205+ }
206+ if ((ensureActive == ensureActive))
401207 then {
402- let votesListName = getVotesListName(assetId)
403- let userVoteKey = keyUserVoteByPeriod(userAddress, assetId, currentPeriod)
404- let userVoteOrUnit = getString(userVoteKey)
405- let cancelVoteInv = if ((userVoteOrUnit == unit))
208+ let ensureAmountAssetVerified = if (isManager(i))
406209 then unit
407- else invoke(this, "cancelVote", [assetId], nil)
408- if ((cancelVoteInv == cancelVoteInv))
210+ else {
211+ let inv = match invoke(assetsStoreContract, "isVerifiedREADONLY", [amountAssetId], nil) {
212+ case b: Boolean =>
213+ if (b)
214+ then true
215+ else throw("asset is not verified")
216+ case _ =>
217+ throw("asset is not verified")
218+ }
219+ if ((inv == inv))
220+ then unit
221+ else throw("Strict value is not equal to itself.")
222+ }
223+ if ((ensureAmountAssetVerified == ensureAmountAssetVerified))
409224 then {
410- let userVoteActions = [StringEntry(userVoteKey, formatUserVote(gwxAmountAtEnd, inFavor)), StringEntry(keyUserVoteHistory("vote", userAddress, assetId, toBase58String(i.transactionId), currentPeriod), formatUserVote(gwxAmountAtEnd, inFavor)), getVoteResultAction(assetId, gwxAmountAtEnd, inFavor, currentPeriod, unit)]
411- let votesListActions = if (containsNode(votesListName, userAddress))
412- then nil
413- else insertNode(votesListName, userAddress)
414- $Tuple2((votesListActions ++ userVoteActions), unit)
225+ let burnFeeInv = invoke(emissionContract, "burn", nil, [AttachedPayment(payment.assetId, payment.amount)])
226+ if ((burnFeeInv == burnFeeInv))
227+ then {
228+ let newSuggestIndex = match getInteger(keySuggestIndex(amountAssetId, priceAssetId)) {
229+ case int: Int =>
230+ (int + 1)
231+ case _ =>
232+ 0
233+ }
234+ $Tuple2([IntegerEntry(keyInListAssetId(amountAssetId, priceAssetId), newSuggestIndex), IntegerEntry(keyStartHeight(amountAssetId, priceAssetId, newSuggestIndex), height), StringEntry(keyVotingResult(amountAssetId, priceAssetId, newSuggestIndex), totalVotes("0", "0")), IntegerEntry(keySuggestIndex(amountAssetId, priceAssetId), newSuggestIndex)], unit)
235+ }
236+ else throw("Strict value is not equal to itself.")
415237 }
416238 else throw("Strict value is not equal to itself.")
417239 }
425247
426248
427249 @Callable(i)
428-func cancelVote (assetId) = {
429- let userAddress = if ((i.caller == this))
430- then toString(i.originCaller)
431- else toString(i.caller)
432- let votesListName = getVotesListName(assetId)
433- let userVoteKey = keyUserVoteByPeriod(userAddress, assetId, currentPeriod)
434- let userVoteOrUnit = getString(userVoteKey)
435- let $t01631416408 = parseUserVote(valueOrErrorMessage(userVoteOrUnit, "Nothing to cancel"))
436- let total = $t01631416408._1
437- let inFavor = $t01631416408._2
438- let votesListActions = deleteNode(votesListName, userAddress)
439- let userVoteActions = [DeleteEntry(userVoteKey), StringEntry(keyUserVoteHistory("cancelVote", userAddress, assetId, toBase58String(i.transactionId), currentPeriod), formatUserVote(0, true)), getVoteResultAction(assetId, -(total), inFavor, currentPeriod, unit)]
440- $Tuple2((votesListActions ++ userVoteActions), unit)
441- }
442-
443-
444-
445-@Callable(i)
446-func finalizeAssetINTERNAL (assetId,period) = {
447- let checkCaller = thisOnly(i)
448- if ((checkCaller == checkCaller))
250+func vote (amountAssetId,priceAssetId,inFavor) = {
251+ let suggestIndex = value(getInteger(keyInListAssetId(amountAssetId, priceAssetId)))
252+ let votingFinishHeight = (value(getInteger(keyStartHeight(amountAssetId, priceAssetId, suggestIndex))) + value(getInteger(keyEpochLength)))
253+ let checks = [if (isDefined(getInteger(keyInListAssetId(amountAssetId, priceAssetId))))
254+ then true
255+ else throw("the token isn't on the voting list"), if ((votingFinishHeight > height))
256+ then true
257+ else throw("too late to vote")]
258+ if ((checks == checks))
449259 then {
450- let voteResultKey = keyVoteResultByPeriod(assetId, period)
451- let $t01706317242 = match getString(voteResultKey) {
452- case s: String =>
453- let r = parseVoteResult(s)
454- $Tuple2(r._1, r._2)
455- case _: Unit =>
456- $Tuple2(0, 0)
457- case _ =>
458- throw("Match error")
459- }
460- let totalYes = $t01706317242._1
461- let totalNo = $t01706317242._2
462- let total = (totalYes + totalNo)
463- let verified = if ((total >= votingThreshold))
464- then (totalYes > totalNo)
465- else false
466- let assetVerifiedActions = [BooleanEntry(keyAssetVerifiedByPeriod(assetId, period), verified), if (verified)
467- then BooleanEntry(keyAssetVerified(assetId), true)
468- else DeleteEntry(keyAssetVerified(assetId))]
469- let assetsStoreSetVerifiedInv = invoke(assetsStoreContract, "setVerified", [assetId, verified], nil)
470- if ((assetsStoreSetVerifiedInv == assetsStoreSetVerifiedInv))
260+ let gwxAmount = invoke(boostingContract, "getUserGwxAmountAtHeightREADONLY", [toString(i.caller), votingFinishHeight], nil)
261+ let notZero = if ((asInt(gwxAmount) > 0))
262+ then true
263+ else throw("you don't have gwx")
264+ if ((notZero == notZero))
471265 then {
472- let eliminate = if (verified)
473- then false
474- else eliminationCheck(assetId)
475- let assetWasEliminatedActions = if (eliminate)
476- then [BooleanEntry(keyAssetWasEliminated(assetId, currentPeriod), true)]
477- else nil
478- let voteResultActions = if (eliminate)
479- then nil
480- else [getVoteResultAction(assetId, 0, true, (currentPeriod + 1), verified)]
481- let votesListActions = if (eliminate)
482- then deleteNode(assetsListName, assetId)
483- else nil
484- let onEliminationInv = if (eliminate)
485- then invoke(assetsStoreContract, "onEliminate", [assetId], nil)
486- else unit
487- if ((onEliminationInv == onEliminationInv))
488- then $Tuple2(((votesListActions ++ voteResultActions) ++ assetVerifiedActions), unit)
266+ let vote = match getString(keyVote(amountAssetId, priceAssetId, suggestIndex, toString(i.caller))) {
267+ case s: String =>
268+ let vote = split(s, separator)
269+ let voteValue = vote[1]
270+ let voteType = vote[2]
271+ let isVoteTypeSimilar = if (if ((voteType == "yes"))
272+ then (inFavor == true)
273+ else false)
274+ then true
275+ else if ((voteType == "no"))
276+ then (inFavor == false)
277+ else false
278+ let isVoteValueSimilar = if (isVoteTypeSimilar)
279+ then (asInt(gwxAmount) == parseIntValue(voteValue))
280+ else false
281+ let isNewVoteSimilar = if (if (isVoteTypeSimilar)
282+ then isVoteValueSimilar
283+ else false)
284+ then throw("you already voted")
285+ else invoke(this, "cancelVote", [amountAssetId, priceAssetId], nil)
286+ isNewVoteSimilar
287+ case u: Unit =>
288+ u
289+ case _ =>
290+ throw("Match error")
291+ }
292+ if ((vote == vote))
293+ then {
294+ let votingResult = split(value(getString(keyVotingResult(amountAssetId, priceAssetId, suggestIndex))), separator)
295+ let positiveVotes = votingResult[1]
296+ let negativeVotes = votingResult[2]
297+ let newPositiveAndNegativeVotes = if (inFavor)
298+ then {
299+ let newPositiveVotes = (parseIntValue(positiveVotes) + asInt(gwxAmount))
300+[toString(newPositiveVotes), negativeVotes]
301+ }
302+ else {
303+ let newNegativeVotes = (parseIntValue(negativeVotes) + asInt(gwxAmount))
304+[positiveVotes, toString(newNegativeVotes)]
305+ }
306+ let voteKey = keyVote(amountAssetId, priceAssetId, suggestIndex, toString(i.caller))
307+ let voteValue = keyVoteValue(toString(asInt(gwxAmount)), inFavor)
308+ $Tuple2([StringEntry(keyVotingResult(amountAssetId, priceAssetId, suggestIndex), totalVotes(newPositiveAndNegativeVotes[0], newPositiveAndNegativeVotes[1])), StringEntry(voteKey, voteValue)], unit)
309+ }
489310 else throw("Strict value is not equal to itself.")
490311 }
491312 else throw("Strict value is not equal to itself.")
496317
497318
498319 @Callable(i)
499-func deleteUserNodeINTERNAL (assetId,userAddress,latestProcessedUserRemoveKey) = {
500- let checkCaller = thisOnly(i)
501- if ((checkCaller == checkCaller))
502- then $Tuple2((deleteNode(getVotesListName(assetId), userAddress) :+ DeleteEntry(latestProcessedUserRemoveKey)), unit)
503- else throw("Strict value is not equal to itself.")
504- }
505-
506-
507-
508-@Callable(i)
509-func finalizeVotingHelper () = if ((endHeight > height))
510- then $Tuple2(nil, false)
511- else {
512- let latestProcessedAssetOrUnit = getString(keyLatestProcessedAsset)
513- let latestProcessedUserOrUnit = getString(keyLatestProcessedUser)
514- let nextPeriodDelay = 0
515- let finish = $Tuple2([IntegerEntry(keyStartHeight, (height + nextPeriodDelay)), IntegerEntry(keyCurrentPeriod, (currentPeriod + 1)), DeleteEntry(keyLatestProcessedAsset), DeleteEntry(keyLatestProcessedUser), DeleteEntry(keyLatestProcessedAssetTransfer), DeleteEntry(keyLatestProcessedUserTransfer)], true)
516- if ((latestProcessedAssetOrUnit == unit))
517- then {
518- let assetsHeadOrUnit = getString(keyListHead(assetsListName))
519- if ((assetsHeadOrUnit == unit))
520- then finish
521- else {
522- let asset = value(assetsHeadOrUnit)
523- let userAddressOrUnit = getString(keyListHead(getVotesListName(asset)))
524- let processVoteActions = processVote(asset, userAddressOrUnit, keyLatestProcessedAsset, keyLatestProcessedUser, keyLatestProcessedUserRemove)
525- $Tuple2(processVoteActions, true)
526- }
527- }
528- else {
529- let latestProcessedAsset = value(latestProcessedAssetOrUnit)
530- if ((latestProcessedUserOrUnit == unit))
531- then {
532- let assetOrUnit = getString(keyListNext(assetsListName, latestProcessedAsset))
533- if ((assetOrUnit == assetOrUnit))
534- then {
535- let finalizeAssetInv = invoke(this, "finalizeAssetINTERNAL", [latestProcessedAsset, currentPeriod], nil)
536- if ((finalizeAssetInv == finalizeAssetInv))
537- then if ((assetOrUnit == unit))
538- then finish
539- else {
540- let asset = value(assetOrUnit)
541- let userAddressOrUnit = getString(keyListHead(getVotesListName(asset)))
542- let processVoteActions = processVote(asset, userAddressOrUnit, keyLatestProcessedAsset, keyLatestProcessedUser, keyLatestProcessedUserRemove)
543- $Tuple2(processVoteActions, true)
544- }
545- else throw("Strict value is not equal to itself.")
546- }
547- else throw("Strict value is not equal to itself.")
548- }
549- else {
550- let latestProcessedUser = value(latestProcessedUserOrUnit)
551- let userAddressOrUnit = getString(keyListNext(getVotesListName(latestProcessedAsset), latestProcessedUser))
552- if ((userAddressOrUnit == userAddressOrUnit))
553- then {
554- let removeLatestUser = valueOrElse(getBoolean(keyLatestProcessedUserRemove), false)
555- let deleteUserInv = if (removeLatestUser)
556- then invoke(this, "deleteUserNodeINTERNAL", [latestProcessedAsset, latestProcessedUser, keyLatestProcessedUserRemove], nil)
557- else unit
558- if ((deleteUserInv == deleteUserInv))
559- then {
560- let processVoteActions = processVote(latestProcessedAsset, userAddressOrUnit, keyLatestProcessedAsset, keyLatestProcessedUser, keyLatestProcessedUserRemove)
561- $Tuple2(processVoteActions, true)
562- }
563- else throw("Strict value is not equal to itself.")
564- }
565- else throw("Strict value is not equal to itself.")
566- }
567- }
568- }
569-
570-
571-
572-@Callable(i)
573-func finalizeVotingWrapper (counter) = {
574- let result = asBool(invoke(this, "finalizeVotingHelper", nil, nil))
575- if ((result == result))
576- then if (!(result))
577- then if ((counter == 0))
578- then throw("Current voting is not over yet")
579- else $Tuple2(nil, unit)
580- else if ((maxDepth > counter))
320+func cancelVote (amountAssetId,priceAssetId) = {
321+ let userAddress = if ((i.caller == this))
322+ then toString(i.originCaller)
323+ else toString(i.caller)
324+ let suggestIndex = value(getInteger(keyInListAssetId(amountAssetId, priceAssetId)))
325+ let checks = [if (isDefined(getString(keyVotingResult(amountAssetId, priceAssetId, suggestIndex))))
326+ then true
327+ else throw("no vote for assets pair")]
328+ if ((checks == checks))
329+ then {
330+ let vote = split(value(getString(keyVote(amountAssetId, priceAssetId, suggestIndex, userAddress))), separator)
331+ let voteValue = vote[1]
332+ let voteType = vote[2]
333+ let votingResult = split(value(getString(keyVotingResult(amountAssetId, priceAssetId, suggestIndex))), separator)
334+ let positiveVotes = votingResult[1]
335+ let negativeVotes = votingResult[2]
336+ let actions = if ((voteType == "yes"))
581337 then {
582- let inv = invoke(this, "finalizeVotingWrapper", [(counter + 1)], nil)
583- if ((inv == inv))
584- then $Tuple2(nil, unit)
585- else throw("Strict value is not equal to itself.")
586- }
587- else $Tuple2(nil, unit)
588- else throw("Strict value is not equal to itself.")
589- }
590-
591-
592-
593-@Callable(i)
594-func finalizeVoting () = {
595- let inv = invoke(this, "finalizeVotingWrapper", [0], nil)
596- if ((inv == inv))
597- then $Tuple2(nil, unit)
598- else throw("Strict value is not equal to itself.")
599- }
600-
601-
602-
603-@Callable(i)
604-func transferVotesHelper () = {
605- let votesTransferFinishedKey = keyVotesTransferFinishedByPeriod(currentPeriod)
606- let votesTransferFinished = valueOrElse(getBoolean(votesTransferFinishedKey), false)
607- if (if (if ((startHeight > height))
608- then true
609- else (height >= endHeight))
610- then true
611- else votesTransferFinished)
612- then $Tuple2(nil, false)
613- else {
614- let latestProcessedAssetOrUnit = getString(keyLatestProcessedAssetTransfer)
615- let latestProcessedUserOrUnit = getString(keyLatestProcessedUserTransfer)
616- let finish = $Tuple2([BooleanEntry(votesTransferFinishedKey, true), DeleteEntry(keyLatestProcessedAssetTransfer), DeleteEntry(keyLatestProcessedUserTransfer)], true)
617- if ((latestProcessedAssetOrUnit == unit))
618- then {
619- let assetsHeadOrUnit = getString(keyListHead(assetsListName))
620- if ((assetsHeadOrUnit == unit))
621- then finish
622- else {
623- let asset = value(assetsHeadOrUnit)
624- let userAddressOrUnit = getString(keyListHead(getVotesListName(asset)))
625- let processVoteActions = processVote(asset, userAddressOrUnit, keyLatestProcessedAssetTransfer, keyLatestProcessedUserTransfer, keyLatestProcessedUserRemoveTransfer)
626- $Tuple2(processVoteActions, true)
627- }
338+ let newPositiveVotes = (parseIntValue(positiveVotes) - parseIntValue(voteValue))
339+ $Tuple2([StringEntry(keyVotingResult(amountAssetId, priceAssetId, suggestIndex), totalVotes(toString(newPositiveVotes), negativeVotes)), DeleteEntry(keyVote(amountAssetId, priceAssetId, suggestIndex, userAddress))], unit)
628340 }
629341 else {
630- let latestProcessedAsset = value(latestProcessedAssetOrUnit)
631- if ((latestProcessedUserOrUnit == unit))
632- then {
633- let assetOrUnit = getString(keyListNext(assetsListName, latestProcessedAsset))
634- if ((assetOrUnit == unit))
635- then finish
636- else {
637- let asset = value(assetOrUnit)
638- let userAddressOrUnit = getString(keyListHead(getVotesListName(asset)))
639- let processVoteActions = processVote(asset, userAddressOrUnit, keyLatestProcessedAssetTransfer, keyLatestProcessedUserTransfer, keyLatestProcessedUserRemoveTransfer)
640- $Tuple2(processVoteActions, true)
641- }
642- }
643- else {
644- let latestProcessedUser = value(latestProcessedUserOrUnit)
645- let userAddressOrUnit = getString(keyListNext(getVotesListName(latestProcessedAsset), latestProcessedUser))
646- if ((userAddressOrUnit == userAddressOrUnit))
647- then {
648- let removeLatestUser = valueOrElse(getBoolean(keyLatestProcessedUserRemoveTransfer), false)
649- let deleteUserInv = if (removeLatestUser)
650- then invoke(this, "deleteUserNodeINTERNAL", [latestProcessedAsset, latestProcessedUser, keyLatestProcessedUserRemoveTransfer], nil)
651- else unit
652- if ((deleteUserInv == deleteUserInv))
653- then {
654- let processVoteActions = processVote(latestProcessedAsset, userAddressOrUnit, keyLatestProcessedAssetTransfer, keyLatestProcessedUserTransfer, keyLatestProcessedUserRemoveTransfer)
655- $Tuple2(processVoteActions, true)
656- }
657- else throw("Strict value is not equal to itself.")
658- }
659- else throw("Strict value is not equal to itself.")
660- }
342+ let newNegativeVotes = (parseIntValue(negativeVotes) - parseIntValue(voteValue))
343+ $Tuple2([StringEntry(keyVotingResult(amountAssetId, priceAssetId, suggestIndex), totalVotes(positiveVotes, toString(newNegativeVotes))), DeleteEntry(keyVote(amountAssetId, priceAssetId, suggestIndex, userAddress))], unit)
661344 }
345+ actions
662346 }
663- }
664-
665-
666-
667-@Callable(i)
668-func transferVotesWrapper (counter) = {
669- let result = asBool(invoke(this, "transferVotesHelper", nil, nil))
670- if ((result == result))
671- then if (!(result))
672- then if ((counter == 0))
673- then throw("Voting is not started yet")
674- else $Tuple2(nil, unit)
675- else if ((maxDepth > counter))
676- then {
677- let inv = invoke(this, "transferVotesWrapper", [(counter + 1)], nil)
678- if ((inv == inv))
679- then $Tuple2(nil, unit)
680- else throw("Strict value is not equal to itself.")
681- }
682- else $Tuple2(nil, unit)
683- else throw("Strict value is not equal to itself.")
684- }
685-
686-
687-
688-@Callable(i)
689-func transferVotes () = {
690- let inv = invoke(this, "transferVotesWrapper", [0], nil)
691- if ((inv == inv))
692- then $Tuple2(nil, unit)
693- else throw("Strict value is not equal to itself.")
694- }
695-
696-
697-
698-@Callable(i)
699-func setVotingThreshold (newThreshold) = {
700- let checkCaller = thisOnly(i)
701- if ((checkCaller == checkCaller))
702- then $Tuple2([IntegerEntry(keyVotingThreshold, newThreshold)], unit)
703347 else throw("Strict value is not equal to itself.")
704348 }
705349
707351
708352 @Callable(i)
709353 func setFee (newFee) = {
710- let checkCaller = thisOnly(i)
711- if ((checkCaller == checkCaller))
712- then $Tuple2([IntegerEntry(keyFeeAmount, newFee)], unit)
354+ let checks = [mustManager(i)]
355+ if ((checks == checks))
356+ then [IntegerEntry(keyFeeAmount, newFee)]
713357 else throw("Strict value is not equal to itself.")
714358 }
715359
716360
717361
718362 @Callable(i)
719-func gwxAvailableForVoteREADONLY (userAddress) = {
720- let gwxAmountAtEnd = getUserGwxAmountAtHeight(userAddress, endHeight)
721- $Tuple2(nil, gwxAmountAtEnd)
363+func setThreshold (newThreshold) = {
364+ let checks = [mustManager(i), if ((newThreshold > 0))
365+ then true
366+ else throw("invalid threshold")]
367+ if ((checks == checks))
368+ then $Tuple2([IntegerEntry(keyThreshold, newThreshold)], unit)
369+ else throw("Strict value is not equal to itself.")
722370 }
723371
372+
373+
374+@Callable(i)
375+func finalize (amountAssetId,priceAssetId) = {
376+ let suggestIndex = value(getInteger(keyInListAssetId(amountAssetId, priceAssetId)))
377+ let votingFinishHeight = (value(getInteger(keyStartHeight(amountAssetId, priceAssetId, suggestIndex))) + value(getInteger(keyEpochLength)))
378+ let checks = [if (isDefined(getInteger(keyInListAssetId(amountAssetId, priceAssetId))))
379+ then true
380+ else throw("no assets pair"), if ((height >= votingFinishHeight))
381+ then true
382+ else throw("insufficient height for completion")]
383+ if ((checks == checks))
384+ then {
385+ let votingResult = split(value(getString(keyVotingResult(amountAssetId, priceAssetId, suggestIndex))), separator)
386+ let positiveVotes = parseIntValue(votingResult[1])
387+ let negativeVotes = parseIntValue(votingResult[2])
388+ let allVotes = (positiveVotes + negativeVotes)
389+ let threshold = getIntOrFail(keyThreshold)
390+ let actions = if (if ((allVotes >= threshold))
391+ then (positiveVotes > negativeVotes)
392+ else false)
393+ then {
394+ let res = invoke(factoryContract, "setWxEmissionPoolLabel", [amountAssetId, priceAssetId], nil)
395+ if ((res == res))
396+ then {
397+ let votingEmissionInv = invoke(votingEmissionContract, "create", [amountAssetId, priceAssetId], nil)
398+ if ((votingEmissionInv == votingEmissionInv))
399+ then $Tuple2([DeleteEntry(keyInListAssetId(amountAssetId, priceAssetId))], unit)
400+ else throw("Strict value is not equal to itself.")
401+ }
402+ else throw("Strict value is not equal to itself.")
403+ }
404+ else $Tuple2([DeleteEntry(keyInListAssetId(amountAssetId, priceAssetId))], unit)
405+ actions
406+ }
407+ else throw("Strict value is not equal to itself.")
408+ }
409+
410+
411+
412+@Callable(i)
413+func setManager (pendingManagerPublicKey) = {
414+ let checkCaller = mustManager(i)
415+ if ((checkCaller == checkCaller))
416+ then {
417+ let checkManagerPublicKey = fromBase58String(pendingManagerPublicKey)
418+ if ((checkManagerPublicKey == checkManagerPublicKey))
419+ then [StringEntry(keyPendingManagerPublicKey(), pendingManagerPublicKey)]
420+ else throw("Strict value is not equal to itself.")
421+ }
422+ else throw("Strict value is not equal to itself.")
423+ }
424+
425+
426+
427+@Callable(i)
428+func confirmManager () = {
429+ let pm = pendingManagerPublicKeyOrUnit()
430+ let hasPM = if (isDefined(pm))
431+ then true
432+ else throw("no pending manager")
433+ if ((hasPM == hasPM))
434+ then {
435+ let checkPM = if ((i.callerPublicKey == value(pm)))
436+ then true
437+ else throw("you are not pending manager")
438+ if ((checkPM == checkPM))
439+ then [StringEntry(keyManagerPublicKey(), toBase58String(value(pm))), DeleteEntry(keyPendingManagerPublicKey())]
440+ else throw("Strict value is not equal to itself.")
441+ }
442+ else throw("Strict value is not equal to itself.")
443+ }
444+
445+
446+@Verifier(tx)
447+func verify () = {
448+ let targetPublicKey = match managerPublicKeyOrUnit() {
449+ case pk: ByteVector =>
450+ pk
451+ case _: Unit =>
452+ tx.senderPublicKey
453+ case _ =>
454+ throw("Match error")
455+ }
456+ sigVerify(tx.bodyBytes, tx.proofs[0], targetPublicKey)
457+ }
724458
Full:
OldNewDifferences
11 {-# STDLIB_VERSION 5 #-}
22 {-# SCRIPT_TYPE ACCOUNT #-}
33 {-# CONTENT_TYPE DAPP #-}
44 let separator = "__"
55
6-let maxDepthDefault = 10
6+let keyFeeAmount = makeString(["%s", "fee"], separator)
7+
8+let keyUsdnAssetId = makeString(["%s", "usdnAssetId"], separator)
9+
10+let keyEpochLength = makeString(["%s", "epochLength"], separator)
11+
12+let keyFinalizeReward = makeString(["%s", "finalizeReward"], separator)
13+
14+let keyWxAssetId = makeString(["%s", "wxAssetId"], separator)
15+
16+let keyAssetsStoreContract = makeString(["%s", "assetsStoreContract"], separator)
17+
18+let keyUserPoolContract = makeString(["%s", "userPoolContract"], separator)
19+
20+let keyEmissionContract = makeString(["%s", "emissionContract"], separator)
21+
22+let keyBoostingContract = makeString(["%s", "boostingContract"], separator)
23+
24+let keyFactoryContract = makeString(["%s", "factoryContract"], separator)
25+
26+let keyVotingEmissionContract = makeString(["%s", "votingEmissionContract"], separator)
27+
28+let keyThreshold = makeString(["%s", "votingThreshold"], separator)
29+
30+func getStringOrFail (key) = valueOrErrorMessage(getString(this, key), (key + " is not defined"))
31+
32+
33+func getIntOrFail (key) = valueOrErrorMessage(getInteger(this, key), (key + " is not defined"))
34+
35+
36+func keyInListAssetId (amountAssetId,priceAssetId) = makeString(["%s%s%s", "inList", amountAssetId, priceAssetId], separator)
37+
38+
39+func keySuggestIndex (amountAssetId,priceAssetId) = makeString(["%s%s%s", "suggestIndex", amountAssetId, priceAssetId], separator)
40+
41+
42+func keyStartHeight (amountAssetId,priceAssetId,suggestIndex) = makeString(["%s%s%s%d", "startHeight", amountAssetId, priceAssetId, toString(suggestIndex)], separator)
43+
44+
45+func keyVotingResult (amountAssetId,priceAssetId,suggestIndex) = makeString(["%s%s%s%d", "votingResult", amountAssetId, priceAssetId, toString(suggestIndex)], separator)
46+
47+
48+func totalVotes (totalYes,totalNo) = makeString(["%d%d", totalYes, totalNo], separator)
49+
50+
51+func keyVote (amountAssetId,priceAssetId,suggestIndex,voterAddress) = makeString(["%s%s%s%d%s", "vote", amountAssetId, priceAssetId, toString(suggestIndex), voterAddress], separator)
52+
53+
54+func keyVoteValue (gwxAmount,vote) = {
55+ let key = if (vote)
56+ then makeString(["%d%s", gwxAmount, "yes"], separator)
57+ else makeString(["%d%s", gwxAmount, "no"], separator)
58+ key
59+ }
60+
61+
62+func keyInList (pool) = {
63+ let $t025262566 = pool
64+ let amountAssetId = $t025262566._1
65+ let priceAssetId = $t025262566._2
66+ makeString(["%s%s%s", "inList", amountAssetId, priceAssetId], separator)
67+ }
68+
69+
70+func keyManagerPublicKey () = "%s__managerPublicKey"
71+
72+
73+func keyPendingManagerPublicKey () = "%s__pendingManagerPublicKey"
74+
75+
76+let assetsStoreContract = addressFromStringValue(getStringOrFail(keyAssetsStoreContract))
77+
78+let boostingContract = addressFromStringValue(getStringOrFail(keyBoostingContract))
79+
80+let emissionContract = addressFromStringValue(getStringOrFail(keyEmissionContract))
81+
82+let factoryContract = addressFromStringValue(getStringOrFail(keyFactoryContract))
83+
84+let userPoolContract = addressFromStringValue(getStringOrFail(keyUserPoolContract))
85+
86+let votingEmissionContract = addressFromStringValue(getStringOrFail(keyVotingEmissionContract))
87+
88+func managerPublicKeyOrUnit () = match getString(keyManagerPublicKey()) {
89+ case s: String =>
90+ fromBase58String(s)
91+ case _: Unit =>
92+ unit
93+ case _ =>
94+ throw("Match error")
95+}
96+
97+
98+func pendingManagerPublicKeyOrUnit () = match getString(keyPendingManagerPublicKey()) {
99+ case s: String =>
100+ fromBase58String(s)
101+ case _: Unit =>
102+ unit
103+ case _ =>
104+ throw("Match error")
105+}
106+
107+
108+func isManager (i) = match managerPublicKeyOrUnit() {
109+ case pk: ByteVector =>
110+ (i.callerPublicKey == pk)
111+ case _: Unit =>
112+ (i.caller == this)
113+ case _ =>
114+ throw("Match error")
115+}
116+
117+
118+func mustManager (i) = if (isManager(i))
119+ then true
120+ else throw("permission denied")
121+
7122
8123 func asInt (val) = match val {
9124 case valInt: Int =>
10125 valInt
11126 case _ =>
12- throw("Failed to cast into Integer")
127+ throw("failed to cast into Integer")
13128 }
14129
15130
16-func asBool (val) = match val {
17- case valBool: Boolean =>
18- valBool
19- case _ =>
20- throw("Failed to cast into Boolean")
21-}
22-
23-
24-let keyFeeAmount = makeString(["%s", "fee"], separator)
25-
26-let keyWxAssetId = makeString(["%s", "wxAssetId"], separator)
27-
28-let keyVotingThreshold = makeString(["%s", "votingThreshold"], separator)
29-
30-let keyVotingDuration = makeString(["%s", "epochLength"], separator)
31-
32-let keyVoteBeforeElimination = makeString(["%s", "voteBeforeElimination"], separator)
33-
34-let keyStartHeight = makeString(["%s", "currentVotingHeightStart"], separator)
35-
36-let keyCurrentPeriod = makeString(["%s", "currentEpoch"], separator)
37-
38-let keyBoostingContract = makeString(["%s", "boostingContract"], separator)
39-
40-let keyEmissionContract = makeString(["%s", "emissionContract"], separator)
41-
42-let keyAssetsStoreContract = makeString(["%s", "assetsStoreContract"], separator)
43-
44-let keyLatestProcessedAsset = makeString(["%s", "latestProcessedAsset"], separator)
45-
46-let keyLatestProcessedUser = makeString(["%s", "latestProcessedUser"], separator)
47-
48-let keyLatestProcessedUserRemove = makeString(["%s", "latestProcessedUserRemove"], separator)
49-
50-let keyLatestProcessedAssetTransfer = makeString(["%s", "latestProcessedAssetTransfer"], separator)
51-
52-let keyLatestProcessedUserTransfer = makeString(["%s", "latestProcessedUserTransfer"], separator)
53-
54-let keyLatestProcessedUserRemoveTransfer = makeString(["%s", "latestProcessedUserRemoveTransfer"], separator)
55-
56-let keyMaxDepth = makeString(["%s", "maxDepth"], separator)
57-
58-func keyVotesTransferFinishedByPeriod (period) = makeString(["%s", "votesTransferFinished", toString(period)], separator)
59-
60-
61-let assetsListName = "__assets"
62-
63-func getVotesListName (assetId) = ("%s__votes__" + assetId)
64-
65-
66-func keyListHead (listName) = makeString([("%s%s" + listName), "head"], separator)
67-
68-
69-func keyListSize (listName) = makeString([("%s%s" + listName), "size"], separator)
70-
71-
72-func keyListPrev (listName,id) = makeString([("%s%s%s" + listName), id, "prev"], separator)
73-
74-
75-func keyListNext (listName,id) = makeString([("%s%s%s" + listName), id, "next"], separator)
76-
77-
78-func keyAssetVerifiedByPeriod (assetId,period) = makeString(["%s%d%s", "verifiedAt", toString(period), assetId], separator)
79-
80-
81-func keyAssetVerified (assetId) = makeString(["%s%s", "verified", assetId], separator)
82-
83-
84-func keyAssetWasEliminated (assetId,period) = makeString(["%s%s%d", "eliminated", assetId, toString(period)], separator)
85-
86-
87-func keyVoteResultByPeriod (assetId,period) = makeString(["%s%d%s", "votingResultAtAsset", toString(period), assetId], separator)
88-
89-
90-func formatVoteResult (totalYes,totalNo,verified) = makeString(["%d%d%s", toString(totalYes), toString(totalNo), toString(verified)], separator)
91-
92-
93-func parseVoteResult (input) = {
94- let parts = split(input, separator)
95- let totalYesIdx = 1
96- let totalNoIdx = 2
97- let verifiedIdx = 3
98- let totalYes = parseIntValue(parts[totalYesIdx])
99- let totalNo = parseIntValue(parts[totalNoIdx])
100- let verified = if ((size(parts) == 4))
101- then (parts[verifiedIdx] == "true")
102- else false
103- $Tuple3(totalYes, totalNo, verified)
104- }
105-
106-
107-func keyUserVoteByPeriod (userAddress,assetId,period) = makeString(["%s%d%s%s", "vru", toString(period), assetId, userAddress], separator)
108-
109-
110-func formatUserVote (total,inFavor) = {
111- let totalYes = if (inFavor)
112- then total
113- else 0
114- let totalNo = if (inFavor)
115- then 0
116- else total
117- makeString(["%d%d", toString(totalYes), toString(totalNo)], separator)
118- }
119-
120-
121-func parseUserVote (input) = {
122- let parts = split(input, separator)
123- let totalYesIdx = 1
124- let totalNoIdx = 2
125- let totalYes = parseIntValue(parts[totalYesIdx])
126- let totalNo = parseIntValue(parts[totalNoIdx])
127- let inFavor = if ((totalYes > 0))
128- then (totalNo == 0)
129- else false
130- let against = if ((totalYes == 0))
131- then (totalNo > 0)
132- else false
133- let checkTotals = if (if (inFavor)
131+@Callable(i)
132+func constructor (assetsStoreContractPrm,boostingContractPrm,emissionContractPrm,factoryContractPrm,userPoolContractPrm,votingEmissionContractPrm,feeAmountPrm,wxAssetIdPrm,votingDurationPrm,usdnAssetIdPrm,finalizeRewardPrm) = {
133+ let checks = [mustManager(i), if (isDefined(addressFromString(assetsStoreContractPrm)))
134134 then true
135- else against)
135+ else throw("Invalid asset_store contract address"), if (isDefined(addressFromString(boostingContractPrm)))
136136 then true
137- else throw("Invalid user vote value")
138- if ((checkTotals == checkTotals))
139- then {
140- let total = if (inFavor)
141- then totalYes
142- else totalNo
143- $Tuple2(total, inFavor)
144- }
145- else throw("Strict value is not equal to itself.")
146- }
147-
148-
149-func keyUserVoteHistory (type,userAddress,assetId,txId,period) = makeString(["%s%s%s%s%s%d", "history", type, assetId, userAddress, txId, toString(period)], separator)
150-
151-
152-func thisOnly (i) = if ((i.caller == this))
153- then true
154- else throw("Permission denied")
155-
156-
157-func getIntegerOrZero (key) = valueOrElse(getInteger(this, key), 0)
158-
159-
160-func getIntegerOrFail (key) = valueOrErrorMessage(getInteger(this, key), (key + " is not defined"))
161-
162-
163-func getStringOrEmpty (key) = valueOrElse(getString(this, key), "")
164-
165-
166-func getStringOrFail (key) = valueOrErrorMessage(getString(this, key), (key + " is not defined"))
167-
168-
169-let feeAmount = getIntegerOrFail(keyFeeAmount)
170-
171-let wxAssetId = fromBase58String(getStringOrFail(keyWxAssetId))
172-
173-let votingThreshold = getIntegerOrFail(keyVotingThreshold)
174-
175-let votingDuration = getIntegerOrFail(keyVotingDuration)
176-
177-let voteBeforeElimination = getIntegerOrFail(keyVoteBeforeElimination)
178-
179-let startHeight = getIntegerOrFail(keyStartHeight)
180-
181-let currentPeriod = getIntegerOrFail(keyCurrentPeriod)
182-
183-let boostingContract = addressFromStringValue(getStringOrFail(keyBoostingContract))
184-
185-let emissionContract = addressFromStringValue(getStringOrFail(keyEmissionContract))
186-
187-let assetsStoreContract = addressFromStringValue(getStringOrFail(keyAssetsStoreContract))
188-
189-let maxDepth = valueOrElse(getInteger(keyMaxDepth), maxDepthDefault)
190-
191-let endHeight = (startHeight + votingDuration)
192-
193-func getUserGwxAmountAtHeight (userAddress,targetHeight) = {
194- let gwxAmount = invoke(boostingContract, "getUserGwxAmountAtHeightREADONLY", [userAddress, targetHeight], nil)
195- asInt(gwxAmount)
196- }
197-
198-
199-func getVoteResultAction (assetId,total,inFavor,period,verified) = {
200- let voteResultKey = keyVoteResultByPeriod(assetId, period)
201- let $t062886597 = match getString(voteResultKey) {
202- case s: String =>
203- parseVoteResult(s)
204- case _: Unit =>
205- match getString(keyVoteResultByPeriod(assetId, (period - 1))) {
206- case s: String =>
207- $Tuple3(0, 0, parseVoteResult(s)._3)
208- case _: Unit =>
209- $Tuple3(0, 0, false)
210- case _ =>
211- throw("Match error")
212- }
213- case _ =>
214- throw("Match error")
215- }
216- let oldTotalYes = $t062886597._1
217- let oldTotalNo = $t062886597._2
218- let oldVerified = $t062886597._3
219- StringEntry(voteResultKey, formatVoteResult((oldTotalYes + (if (inFavor)
220- then total
221- else 0)), (oldTotalNo + (if (inFavor)
222- then 0
223- else total)), if ((verified == unit))
224- then oldVerified
225- else value(verified)))
226- }
227-
228-
229-func containsNode (listName,id) = {
230- let head = getString(this, keyListHead(listName))
231- let prev = getString(this, keyListPrev(listName, id))
232- let next = getString(this, keyListNext(listName, id))
233- if (if ((id == head))
137+ else throw("Invalid boosting contract address"), if (isDefined(addressFromString(userPoolContractPrm)))
234138 then true
235- else (prev != unit))
139+ else throw("Invalid user_pools contract address"), if (isDefined(addressFromString(emissionContractPrm)))
236140 then true
237- else (next != unit)
238- }
239-
240-
241-func insertNode (listName,id) = {
242- let head = getString(this, keyListHead(listName))
243- let listSize = valueOrElse(getInteger(this, keyListSize(listName)), 0)
244- let checkNode = if (!(containsNode(listName, id)))
141+ else throw("Invalid emission contract address"), if (isDefined(addressFromString(factoryContractPrm)))
245142 then true
246- else throw("Node exists")
247- if ((checkNode == checkNode))
248- then (([IntegerEntry(keyListSize(listName), (listSize + 1))] ++ (if (isDefined(head))
249- then [StringEntry(keyListNext(listName, id), value(head)), StringEntry(keyListPrev(listName, value(head)), id)]
250- else nil)) ++ [StringEntry(keyListHead(listName), id)])
251- else throw("Strict value is not equal to itself.")
252- }
253-
254-
255-func deleteNode (listName,id) = {
256- let head = getString(this, keyListHead(listName))
257- let listSize = valueOrElse(getInteger(this, keyListSize(listName)), 0)
258- let prev = getString(this, keyListPrev(listName, id))
259- let next = getString(this, keyListNext(listName, id))
260- ([IntegerEntry(keyListSize(listName), (listSize - 1))] ++ (if (if (isDefined(prev))
261- then isDefined(next)
262- else false)
263- then [StringEntry(keyListNext(listName, value(prev)), value(next)), StringEntry(keyListPrev(listName, value(next)), value(prev)), DeleteEntry(keyListPrev(listName, id)), DeleteEntry(keyListNext(listName, id))]
264- else if (isDefined(next))
265- then [StringEntry(keyListHead(listName), value(next)), DeleteEntry(keyListNext(listName, id)), DeleteEntry(keyListPrev(listName, value(next)))]
266- else if (isDefined(prev))
267- then [DeleteEntry(keyListPrev(listName, id)), DeleteEntry(keyListNext(listName, value(prev)))]
268- else if ((id == head))
269- then [DeleteEntry(keyListHead(listName))]
270- else throw("Invalid node")))
271- }
272-
273-
274-func processVote (assetId,userAddressOrUnit,latestProcessedAssetKey,latestProcessedUserKey,latestProcessedUserRemoveKey) = {
275- let updateLatestProcessedAssetAction = StringEntry(latestProcessedAssetKey, assetId)
276- let deleteLatestProcessedUserAction = DeleteEntry(latestProcessedUserKey)
277- if ((userAddressOrUnit == unit))
278- then [updateLatestProcessedAssetAction, deleteLatestProcessedUserAction]
279- else {
280- let userAddress = value(userAddressOrUnit)
281- let updateLatestProcessedUserAction = StringEntry(latestProcessedUserKey, userAddress)
282- let userVoteKey = keyUserVoteByPeriod(userAddress, assetId, currentPeriod)
283- let userVoteOrUnit = getString(userVoteKey)
284- let voteActions = if ((userVoteOrUnit == unit))
285- then {
286- let userGwxAmountAtEndHeight = getUserGwxAmountAtHeight(userAddress, endHeight)
287- if ((userGwxAmountAtEndHeight == 0))
288- then [BooleanEntry(latestProcessedUserRemoveKey, true)]
289- else {
290- let previousPeriod = (currentPeriod - 1)
291- let assetWasEliminated = valueOrElse(getBoolean(keyAssetWasEliminated(assetId, previousPeriod)), false)
292- let userPreviousVoteOrUnit = if (assetWasEliminated)
293- then unit
294- else getString(keyUserVoteByPeriod(userAddress, assetId, previousPeriod))
295- if ((userPreviousVoteOrUnit == unit))
296- then nil
297- else {
298- let $t01080110874 = parseUserVote(value(userPreviousVoteOrUnit))
299- let prevTotal = $t01080110874._1
300- let inFavor = $t01080110874._2
301- let total = min([prevTotal, userGwxAmountAtEndHeight])
302-[StringEntry(userVoteKey, formatUserVote(total, inFavor)), getVoteResultAction(assetId, total, inFavor, currentPeriod, unit)]
303- }
304- }
305- }
306- else nil
307- ((voteActions :+ updateLatestProcessedAssetAction) :+ updateLatestProcessedUserAction)
308- }
309- }
310-
311-
312-func assetShouldBeEliminated (assetId,period) = !(valueOrElse(getBoolean(keyAssetVerifiedByPeriod(assetId, period)), true))
313-
314-
315-func eliminationCheck (assetId) = if (if (assetShouldBeEliminated(assetId, (currentPeriod - 1)))
316- then assetShouldBeEliminated(assetId, (currentPeriod - 2))
317- else false)
318- then assetShouldBeEliminated(assetId, (currentPeriod - 3))
319- else false
320-
321-
322-@Callable(i)
323-func constructor (boostingContractPrm,emissionContractPrm,assetsStoreContractPrm,feeAmountPrm,wxAssetIdPrm,votingThresholdPrm,votingDurationPrm,voteBeforeEliminationPrm,startHeightPrm,maxDepthPrm) = {
324- let checks = [thisOnly(i), if (isDefined(addressFromString(boostingContractPrm)))
143+ else throw("Invalid factory contract address"), if ((feeAmountPrm >= 0))
325144 then true
326- else throw("Invalid boosting contract address"), if (isDefined(addressFromString(emissionContractPrm)))
145+ else throw("Invalid fee amount"), if ((votingDurationPrm > 0))
327146 then true
328- else throw("Invalid emission contract address"), if (isDefined(addressFromString(assetsStoreContractPrm)))
147+ else throw("Invalid voting duration"), if ((finalizeRewardPrm >= 0))
329148 then true
330- else throw("Invalid asset_store contract address"), if ((feeAmountPrm >= 0))
149+ else throw("Invalid finalize reward"), if (isDefined(assetInfo(fromBase58String(wxAssetIdPrm))))
331150 then true
332- else throw("Invalid fee amount"), if (isDefined(assetInfo(fromBase58String(wxAssetIdPrm))))
151+ else throw("Invalid WX asset ID"), if (isDefined(assetInfo(fromBase58String(usdnAssetIdPrm))))
333152 then true
334- else throw("Invalid WX asset ID"), if ((votingThresholdPrm >= 0))
335- then true
336- else throw("Invalid voting threshold"), if ((votingDurationPrm > 0))
337- then true
338- else throw("Invalid voting duration"), if (((startHeightPrm + votingDurationPrm) > height))
339- then true
340- else throw("Invalid start height")]
153+ else throw("Invalid USDN asset ID")]
341154 if ((checks == checks))
342- then $Tuple2([StringEntry(keyBoostingContract, boostingContractPrm), StringEntry(keyEmissionContract, emissionContractPrm), StringEntry(keyAssetsStoreContract, assetsStoreContractPrm), IntegerEntry(keyFeeAmount, feeAmountPrm), StringEntry(keyWxAssetId, wxAssetIdPrm), IntegerEntry(keyVotingThreshold, votingThresholdPrm), IntegerEntry(keyVotingDuration, votingDurationPrm), IntegerEntry(keyVoteBeforeElimination, voteBeforeEliminationPrm), IntegerEntry(keyStartHeight, startHeightPrm), IntegerEntry(keyCurrentPeriod, 0), IntegerEntry(keyMaxDepth, maxDepthPrm)], unit)
155+ then $Tuple2([StringEntry(keyAssetsStoreContract, assetsStoreContractPrm), StringEntry(keyBoostingContract, boostingContractPrm), StringEntry(keyEmissionContract, emissionContractPrm), StringEntry(keyFactoryContract, factoryContractPrm), StringEntry(keyUserPoolContract, userPoolContractPrm), StringEntry(keyVotingEmissionContract, votingEmissionContractPrm), IntegerEntry(keyFeeAmount, feeAmountPrm), IntegerEntry(keyEpochLength, votingDurationPrm), IntegerEntry(keyFinalizeReward, finalizeRewardPrm), StringEntry(keyWxAssetId, wxAssetIdPrm), StringEntry(keyUsdnAssetId, usdnAssetIdPrm)], unit)
343156 else throw("Strict value is not equal to itself.")
344157 }
345158
346159
347160
348161 @Callable(i)
349-func suggest (assetId,assetImage) = {
350- let info = valueOrErrorMessage(assetInfo(fromBase58String(assetId)), "Invalid asset ID")
351- let payment = value(i.payments[0])
352- let checks = [if ((info.issuer == i.caller))
162+func constructorV2 (threshold) = {
163+ let checks = [mustManager(i), if ((threshold > 0))
353164 then true
354- else throw("Asset can only be suggested by its issuer"), if ((value(payment.assetId) == wxAssetId))
355- then true
356- else throw("Invalid fee asset"), if ((payment.amount == feeAmount))
357- then true
358- else throw("Invalid fee amount")]
165+ else throw("invalid threshold")]
359166 if ((checks == checks))
360- then {
361- let assetsStoreCreateOrUpdateInv = invoke(assetsStoreContract, "createOrUpdate", [assetId, assetImage, false], nil)
362- if ((assetsStoreCreateOrUpdateInv == assetsStoreCreateOrUpdateInv))
363- then {
364- let burnFeeInv = invoke(emissionContract, "burn", nil, [AttachedPayment(payment.assetId, payment.amount)])
365- if ((burnFeeInv == burnFeeInv))
366- then {
367- let addAssetActions = insertNode(assetsListName, assetId)
368- let nextPeriod = (currentPeriod + 1)
369- let targetPeriod = if ((endHeight > height))
370- then currentPeriod
371- else nextPeriod
372- $Tuple2((addAssetActions :+ getVoteResultAction(assetId, 0, true, targetPeriod, false)), unit)
373- }
374- else throw("Strict value is not equal to itself.")
375- }
376- else throw("Strict value is not equal to itself.")
377- }
167+ then $Tuple2([IntegerEntry(keyThreshold, threshold)], unit)
378168 else throw("Strict value is not equal to itself.")
379169 }
380170
381171
382172
383173 @Callable(i)
384-func vote (assetId,inFavor) = {
385- let checkAsset = if (containsNode(assetsListName, assetId))
386- then true
387- else throw("Invalid asset")
388- if ((checkAsset == checkAsset))
174+func suggest (amountAssetId,priceAssetId) = {
175+ let pool = $Tuple2(amountAssetId, priceAssetId)
176+ let payment = value(i.payments[0])
177+ let checkAmountAsset = if ((amountAssetId == "WAVES"))
178+ then unit
179+ else {
180+ let info = valueOrErrorMessage(assetInfo(fromBase58String(amountAssetId)), "invalid amountAssetId ID")
181+ if ((info.scripted == false))
182+ then true
183+ else throw("asset is smart")
184+ }
185+ if ((checkAmountAsset == checkAmountAsset))
389186 then {
390- let checkHeight = if ((endHeight > height))
187+ let checks = [if ((toBase58String(value(payment.assetId)) == value(getString(keyWxAssetId))))
391188 then true
392- else throw("Current voting is over but results are not finalized")
393- if ((checkHeight == checkHeight))
189+ else throw("invalid fee asset"), if ((payment.amount == value(getInteger(keyFeeAmount))))
190+ then true
191+ else throw("invalid fee amount"), if ((getInteger(keyInListAssetId(amountAssetId, priceAssetId)) == unit))
192+ then true
193+ else throw("already in voting list"), if (!(valueOrElse(getBoolean(votingEmissionContract, keyInList(pool)), false)))
194+ then true
195+ else throw("pool is in emission voting list")]
196+ if ((checks == checks))
394197 then {
395- let userAddress = toString(i.caller)
396- let gwxAmountAtEnd = getUserGwxAmountAtHeight(userAddress, endHeight)
397- let checkGwxAmountAtEnd = if ((gwxAmountAtEnd > 0))
398- then true
399- else throw("You'll not have gWX at the end of voting")
400- if ((checkGwxAmountAtEnd == checkGwxAmountAtEnd))
198+ let ensureActive = match invoke(userPoolContract, "statusREADONLY", [amountAssetId, priceAssetId], nil) {
199+ case s: String =>
200+ if ((s == "active"))
201+ then true
202+ else throw("user pool is not active")
203+ case _ =>
204+ throw("user pool is not active")
205+ }
206+ if ((ensureActive == ensureActive))
401207 then {
402- let votesListName = getVotesListName(assetId)
403- let userVoteKey = keyUserVoteByPeriod(userAddress, assetId, currentPeriod)
404- let userVoteOrUnit = getString(userVoteKey)
405- let cancelVoteInv = if ((userVoteOrUnit == unit))
208+ let ensureAmountAssetVerified = if (isManager(i))
406209 then unit
407- else invoke(this, "cancelVote", [assetId], nil)
408- if ((cancelVoteInv == cancelVoteInv))
210+ else {
211+ let inv = match invoke(assetsStoreContract, "isVerifiedREADONLY", [amountAssetId], nil) {
212+ case b: Boolean =>
213+ if (b)
214+ then true
215+ else throw("asset is not verified")
216+ case _ =>
217+ throw("asset is not verified")
218+ }
219+ if ((inv == inv))
220+ then unit
221+ else throw("Strict value is not equal to itself.")
222+ }
223+ if ((ensureAmountAssetVerified == ensureAmountAssetVerified))
409224 then {
410- let userVoteActions = [StringEntry(userVoteKey, formatUserVote(gwxAmountAtEnd, inFavor)), StringEntry(keyUserVoteHistory("vote", userAddress, assetId, toBase58String(i.transactionId), currentPeriod), formatUserVote(gwxAmountAtEnd, inFavor)), getVoteResultAction(assetId, gwxAmountAtEnd, inFavor, currentPeriod, unit)]
411- let votesListActions = if (containsNode(votesListName, userAddress))
412- then nil
413- else insertNode(votesListName, userAddress)
414- $Tuple2((votesListActions ++ userVoteActions), unit)
225+ let burnFeeInv = invoke(emissionContract, "burn", nil, [AttachedPayment(payment.assetId, payment.amount)])
226+ if ((burnFeeInv == burnFeeInv))
227+ then {
228+ let newSuggestIndex = match getInteger(keySuggestIndex(amountAssetId, priceAssetId)) {
229+ case int: Int =>
230+ (int + 1)
231+ case _ =>
232+ 0
233+ }
234+ $Tuple2([IntegerEntry(keyInListAssetId(amountAssetId, priceAssetId), newSuggestIndex), IntegerEntry(keyStartHeight(amountAssetId, priceAssetId, newSuggestIndex), height), StringEntry(keyVotingResult(amountAssetId, priceAssetId, newSuggestIndex), totalVotes("0", "0")), IntegerEntry(keySuggestIndex(amountAssetId, priceAssetId), newSuggestIndex)], unit)
235+ }
236+ else throw("Strict value is not equal to itself.")
415237 }
416238 else throw("Strict value is not equal to itself.")
417239 }
418240 else throw("Strict value is not equal to itself.")
419241 }
420242 else throw("Strict value is not equal to itself.")
421243 }
422244 else throw("Strict value is not equal to itself.")
423245 }
424246
425247
426248
427249 @Callable(i)
428-func cancelVote (assetId) = {
429- let userAddress = if ((i.caller == this))
430- then toString(i.originCaller)
431- else toString(i.caller)
432- let votesListName = getVotesListName(assetId)
433- let userVoteKey = keyUserVoteByPeriod(userAddress, assetId, currentPeriod)
434- let userVoteOrUnit = getString(userVoteKey)
435- let $t01631416408 = parseUserVote(valueOrErrorMessage(userVoteOrUnit, "Nothing to cancel"))
436- let total = $t01631416408._1
437- let inFavor = $t01631416408._2
438- let votesListActions = deleteNode(votesListName, userAddress)
439- let userVoteActions = [DeleteEntry(userVoteKey), StringEntry(keyUserVoteHistory("cancelVote", userAddress, assetId, toBase58String(i.transactionId), currentPeriod), formatUserVote(0, true)), getVoteResultAction(assetId, -(total), inFavor, currentPeriod, unit)]
440- $Tuple2((votesListActions ++ userVoteActions), unit)
441- }
442-
443-
444-
445-@Callable(i)
446-func finalizeAssetINTERNAL (assetId,period) = {
447- let checkCaller = thisOnly(i)
448- if ((checkCaller == checkCaller))
250+func vote (amountAssetId,priceAssetId,inFavor) = {
251+ let suggestIndex = value(getInteger(keyInListAssetId(amountAssetId, priceAssetId)))
252+ let votingFinishHeight = (value(getInteger(keyStartHeight(amountAssetId, priceAssetId, suggestIndex))) + value(getInteger(keyEpochLength)))
253+ let checks = [if (isDefined(getInteger(keyInListAssetId(amountAssetId, priceAssetId))))
254+ then true
255+ else throw("the token isn't on the voting list"), if ((votingFinishHeight > height))
256+ then true
257+ else throw("too late to vote")]
258+ if ((checks == checks))
449259 then {
450- let voteResultKey = keyVoteResultByPeriod(assetId, period)
451- let $t01706317242 = match getString(voteResultKey) {
452- case s: String =>
453- let r = parseVoteResult(s)
454- $Tuple2(r._1, r._2)
455- case _: Unit =>
456- $Tuple2(0, 0)
457- case _ =>
458- throw("Match error")
459- }
460- let totalYes = $t01706317242._1
461- let totalNo = $t01706317242._2
462- let total = (totalYes + totalNo)
463- let verified = if ((total >= votingThreshold))
464- then (totalYes > totalNo)
465- else false
466- let assetVerifiedActions = [BooleanEntry(keyAssetVerifiedByPeriod(assetId, period), verified), if (verified)
467- then BooleanEntry(keyAssetVerified(assetId), true)
468- else DeleteEntry(keyAssetVerified(assetId))]
469- let assetsStoreSetVerifiedInv = invoke(assetsStoreContract, "setVerified", [assetId, verified], nil)
470- if ((assetsStoreSetVerifiedInv == assetsStoreSetVerifiedInv))
260+ let gwxAmount = invoke(boostingContract, "getUserGwxAmountAtHeightREADONLY", [toString(i.caller), votingFinishHeight], nil)
261+ let notZero = if ((asInt(gwxAmount) > 0))
262+ then true
263+ else throw("you don't have gwx")
264+ if ((notZero == notZero))
471265 then {
472- let eliminate = if (verified)
473- then false
474- else eliminationCheck(assetId)
475- let assetWasEliminatedActions = if (eliminate)
476- then [BooleanEntry(keyAssetWasEliminated(assetId, currentPeriod), true)]
477- else nil
478- let voteResultActions = if (eliminate)
479- then nil
480- else [getVoteResultAction(assetId, 0, true, (currentPeriod + 1), verified)]
481- let votesListActions = if (eliminate)
482- then deleteNode(assetsListName, assetId)
483- else nil
484- let onEliminationInv = if (eliminate)
485- then invoke(assetsStoreContract, "onEliminate", [assetId], nil)
486- else unit
487- if ((onEliminationInv == onEliminationInv))
488- then $Tuple2(((votesListActions ++ voteResultActions) ++ assetVerifiedActions), unit)
266+ let vote = match getString(keyVote(amountAssetId, priceAssetId, suggestIndex, toString(i.caller))) {
267+ case s: String =>
268+ let vote = split(s, separator)
269+ let voteValue = vote[1]
270+ let voteType = vote[2]
271+ let isVoteTypeSimilar = if (if ((voteType == "yes"))
272+ then (inFavor == true)
273+ else false)
274+ then true
275+ else if ((voteType == "no"))
276+ then (inFavor == false)
277+ else false
278+ let isVoteValueSimilar = if (isVoteTypeSimilar)
279+ then (asInt(gwxAmount) == parseIntValue(voteValue))
280+ else false
281+ let isNewVoteSimilar = if (if (isVoteTypeSimilar)
282+ then isVoteValueSimilar
283+ else false)
284+ then throw("you already voted")
285+ else invoke(this, "cancelVote", [amountAssetId, priceAssetId], nil)
286+ isNewVoteSimilar
287+ case u: Unit =>
288+ u
289+ case _ =>
290+ throw("Match error")
291+ }
292+ if ((vote == vote))
293+ then {
294+ let votingResult = split(value(getString(keyVotingResult(amountAssetId, priceAssetId, suggestIndex))), separator)
295+ let positiveVotes = votingResult[1]
296+ let negativeVotes = votingResult[2]
297+ let newPositiveAndNegativeVotes = if (inFavor)
298+ then {
299+ let newPositiveVotes = (parseIntValue(positiveVotes) + asInt(gwxAmount))
300+[toString(newPositiveVotes), negativeVotes]
301+ }
302+ else {
303+ let newNegativeVotes = (parseIntValue(negativeVotes) + asInt(gwxAmount))
304+[positiveVotes, toString(newNegativeVotes)]
305+ }
306+ let voteKey = keyVote(amountAssetId, priceAssetId, suggestIndex, toString(i.caller))
307+ let voteValue = keyVoteValue(toString(asInt(gwxAmount)), inFavor)
308+ $Tuple2([StringEntry(keyVotingResult(amountAssetId, priceAssetId, suggestIndex), totalVotes(newPositiveAndNegativeVotes[0], newPositiveAndNegativeVotes[1])), StringEntry(voteKey, voteValue)], unit)
309+ }
489310 else throw("Strict value is not equal to itself.")
490311 }
491312 else throw("Strict value is not equal to itself.")
492313 }
493314 else throw("Strict value is not equal to itself.")
494315 }
495316
496317
497318
498319 @Callable(i)
499-func deleteUserNodeINTERNAL (assetId,userAddress,latestProcessedUserRemoveKey) = {
500- let checkCaller = thisOnly(i)
501- if ((checkCaller == checkCaller))
502- then $Tuple2((deleteNode(getVotesListName(assetId), userAddress) :+ DeleteEntry(latestProcessedUserRemoveKey)), unit)
503- else throw("Strict value is not equal to itself.")
504- }
505-
506-
507-
508-@Callable(i)
509-func finalizeVotingHelper () = if ((endHeight > height))
510- then $Tuple2(nil, false)
511- else {
512- let latestProcessedAssetOrUnit = getString(keyLatestProcessedAsset)
513- let latestProcessedUserOrUnit = getString(keyLatestProcessedUser)
514- let nextPeriodDelay = 0
515- let finish = $Tuple2([IntegerEntry(keyStartHeight, (height + nextPeriodDelay)), IntegerEntry(keyCurrentPeriod, (currentPeriod + 1)), DeleteEntry(keyLatestProcessedAsset), DeleteEntry(keyLatestProcessedUser), DeleteEntry(keyLatestProcessedAssetTransfer), DeleteEntry(keyLatestProcessedUserTransfer)], true)
516- if ((latestProcessedAssetOrUnit == unit))
517- then {
518- let assetsHeadOrUnit = getString(keyListHead(assetsListName))
519- if ((assetsHeadOrUnit == unit))
520- then finish
521- else {
522- let asset = value(assetsHeadOrUnit)
523- let userAddressOrUnit = getString(keyListHead(getVotesListName(asset)))
524- let processVoteActions = processVote(asset, userAddressOrUnit, keyLatestProcessedAsset, keyLatestProcessedUser, keyLatestProcessedUserRemove)
525- $Tuple2(processVoteActions, true)
526- }
527- }
528- else {
529- let latestProcessedAsset = value(latestProcessedAssetOrUnit)
530- if ((latestProcessedUserOrUnit == unit))
531- then {
532- let assetOrUnit = getString(keyListNext(assetsListName, latestProcessedAsset))
533- if ((assetOrUnit == assetOrUnit))
534- then {
535- let finalizeAssetInv = invoke(this, "finalizeAssetINTERNAL", [latestProcessedAsset, currentPeriod], nil)
536- if ((finalizeAssetInv == finalizeAssetInv))
537- then if ((assetOrUnit == unit))
538- then finish
539- else {
540- let asset = value(assetOrUnit)
541- let userAddressOrUnit = getString(keyListHead(getVotesListName(asset)))
542- let processVoteActions = processVote(asset, userAddressOrUnit, keyLatestProcessedAsset, keyLatestProcessedUser, keyLatestProcessedUserRemove)
543- $Tuple2(processVoteActions, true)
544- }
545- else throw("Strict value is not equal to itself.")
546- }
547- else throw("Strict value is not equal to itself.")
548- }
549- else {
550- let latestProcessedUser = value(latestProcessedUserOrUnit)
551- let userAddressOrUnit = getString(keyListNext(getVotesListName(latestProcessedAsset), latestProcessedUser))
552- if ((userAddressOrUnit == userAddressOrUnit))
553- then {
554- let removeLatestUser = valueOrElse(getBoolean(keyLatestProcessedUserRemove), false)
555- let deleteUserInv = if (removeLatestUser)
556- then invoke(this, "deleteUserNodeINTERNAL", [latestProcessedAsset, latestProcessedUser, keyLatestProcessedUserRemove], nil)
557- else unit
558- if ((deleteUserInv == deleteUserInv))
559- then {
560- let processVoteActions = processVote(latestProcessedAsset, userAddressOrUnit, keyLatestProcessedAsset, keyLatestProcessedUser, keyLatestProcessedUserRemove)
561- $Tuple2(processVoteActions, true)
562- }
563- else throw("Strict value is not equal to itself.")
564- }
565- else throw("Strict value is not equal to itself.")
566- }
567- }
568- }
569-
570-
571-
572-@Callable(i)
573-func finalizeVotingWrapper (counter) = {
574- let result = asBool(invoke(this, "finalizeVotingHelper", nil, nil))
575- if ((result == result))
576- then if (!(result))
577- then if ((counter == 0))
578- then throw("Current voting is not over yet")
579- else $Tuple2(nil, unit)
580- else if ((maxDepth > counter))
320+func cancelVote (amountAssetId,priceAssetId) = {
321+ let userAddress = if ((i.caller == this))
322+ then toString(i.originCaller)
323+ else toString(i.caller)
324+ let suggestIndex = value(getInteger(keyInListAssetId(amountAssetId, priceAssetId)))
325+ let checks = [if (isDefined(getString(keyVotingResult(amountAssetId, priceAssetId, suggestIndex))))
326+ then true
327+ else throw("no vote for assets pair")]
328+ if ((checks == checks))
329+ then {
330+ let vote = split(value(getString(keyVote(amountAssetId, priceAssetId, suggestIndex, userAddress))), separator)
331+ let voteValue = vote[1]
332+ let voteType = vote[2]
333+ let votingResult = split(value(getString(keyVotingResult(amountAssetId, priceAssetId, suggestIndex))), separator)
334+ let positiveVotes = votingResult[1]
335+ let negativeVotes = votingResult[2]
336+ let actions = if ((voteType == "yes"))
581337 then {
582- let inv = invoke(this, "finalizeVotingWrapper", [(counter + 1)], nil)
583- if ((inv == inv))
584- then $Tuple2(nil, unit)
585- else throw("Strict value is not equal to itself.")
586- }
587- else $Tuple2(nil, unit)
588- else throw("Strict value is not equal to itself.")
589- }
590-
591-
592-
593-@Callable(i)
594-func finalizeVoting () = {
595- let inv = invoke(this, "finalizeVotingWrapper", [0], nil)
596- if ((inv == inv))
597- then $Tuple2(nil, unit)
598- else throw("Strict value is not equal to itself.")
599- }
600-
601-
602-
603-@Callable(i)
604-func transferVotesHelper () = {
605- let votesTransferFinishedKey = keyVotesTransferFinishedByPeriod(currentPeriod)
606- let votesTransferFinished = valueOrElse(getBoolean(votesTransferFinishedKey), false)
607- if (if (if ((startHeight > height))
608- then true
609- else (height >= endHeight))
610- then true
611- else votesTransferFinished)
612- then $Tuple2(nil, false)
613- else {
614- let latestProcessedAssetOrUnit = getString(keyLatestProcessedAssetTransfer)
615- let latestProcessedUserOrUnit = getString(keyLatestProcessedUserTransfer)
616- let finish = $Tuple2([BooleanEntry(votesTransferFinishedKey, true), DeleteEntry(keyLatestProcessedAssetTransfer), DeleteEntry(keyLatestProcessedUserTransfer)], true)
617- if ((latestProcessedAssetOrUnit == unit))
618- then {
619- let assetsHeadOrUnit = getString(keyListHead(assetsListName))
620- if ((assetsHeadOrUnit == unit))
621- then finish
622- else {
623- let asset = value(assetsHeadOrUnit)
624- let userAddressOrUnit = getString(keyListHead(getVotesListName(asset)))
625- let processVoteActions = processVote(asset, userAddressOrUnit, keyLatestProcessedAssetTransfer, keyLatestProcessedUserTransfer, keyLatestProcessedUserRemoveTransfer)
626- $Tuple2(processVoteActions, true)
627- }
338+ let newPositiveVotes = (parseIntValue(positiveVotes) - parseIntValue(voteValue))
339+ $Tuple2([StringEntry(keyVotingResult(amountAssetId, priceAssetId, suggestIndex), totalVotes(toString(newPositiveVotes), negativeVotes)), DeleteEntry(keyVote(amountAssetId, priceAssetId, suggestIndex, userAddress))], unit)
628340 }
629341 else {
630- let latestProcessedAsset = value(latestProcessedAssetOrUnit)
631- if ((latestProcessedUserOrUnit == unit))
632- then {
633- let assetOrUnit = getString(keyListNext(assetsListName, latestProcessedAsset))
634- if ((assetOrUnit == unit))
635- then finish
636- else {
637- let asset = value(assetOrUnit)
638- let userAddressOrUnit = getString(keyListHead(getVotesListName(asset)))
639- let processVoteActions = processVote(asset, userAddressOrUnit, keyLatestProcessedAssetTransfer, keyLatestProcessedUserTransfer, keyLatestProcessedUserRemoveTransfer)
640- $Tuple2(processVoteActions, true)
641- }
642- }
643- else {
644- let latestProcessedUser = value(latestProcessedUserOrUnit)
645- let userAddressOrUnit = getString(keyListNext(getVotesListName(latestProcessedAsset), latestProcessedUser))
646- if ((userAddressOrUnit == userAddressOrUnit))
647- then {
648- let removeLatestUser = valueOrElse(getBoolean(keyLatestProcessedUserRemoveTransfer), false)
649- let deleteUserInv = if (removeLatestUser)
650- then invoke(this, "deleteUserNodeINTERNAL", [latestProcessedAsset, latestProcessedUser, keyLatestProcessedUserRemoveTransfer], nil)
651- else unit
652- if ((deleteUserInv == deleteUserInv))
653- then {
654- let processVoteActions = processVote(latestProcessedAsset, userAddressOrUnit, keyLatestProcessedAssetTransfer, keyLatestProcessedUserTransfer, keyLatestProcessedUserRemoveTransfer)
655- $Tuple2(processVoteActions, true)
656- }
657- else throw("Strict value is not equal to itself.")
658- }
659- else throw("Strict value is not equal to itself.")
660- }
342+ let newNegativeVotes = (parseIntValue(negativeVotes) - parseIntValue(voteValue))
343+ $Tuple2([StringEntry(keyVotingResult(amountAssetId, priceAssetId, suggestIndex), totalVotes(positiveVotes, toString(newNegativeVotes))), DeleteEntry(keyVote(amountAssetId, priceAssetId, suggestIndex, userAddress))], unit)
661344 }
345+ actions
662346 }
663- }
664-
665-
666-
667-@Callable(i)
668-func transferVotesWrapper (counter) = {
669- let result = asBool(invoke(this, "transferVotesHelper", nil, nil))
670- if ((result == result))
671- then if (!(result))
672- then if ((counter == 0))
673- then throw("Voting is not started yet")
674- else $Tuple2(nil, unit)
675- else if ((maxDepth > counter))
676- then {
677- let inv = invoke(this, "transferVotesWrapper", [(counter + 1)], nil)
678- if ((inv == inv))
679- then $Tuple2(nil, unit)
680- else throw("Strict value is not equal to itself.")
681- }
682- else $Tuple2(nil, unit)
683- else throw("Strict value is not equal to itself.")
684- }
685-
686-
687-
688-@Callable(i)
689-func transferVotes () = {
690- let inv = invoke(this, "transferVotesWrapper", [0], nil)
691- if ((inv == inv))
692- then $Tuple2(nil, unit)
693- else throw("Strict value is not equal to itself.")
694- }
695-
696-
697-
698-@Callable(i)
699-func setVotingThreshold (newThreshold) = {
700- let checkCaller = thisOnly(i)
701- if ((checkCaller == checkCaller))
702- then $Tuple2([IntegerEntry(keyVotingThreshold, newThreshold)], unit)
703347 else throw("Strict value is not equal to itself.")
704348 }
705349
706350
707351
708352 @Callable(i)
709353 func setFee (newFee) = {
710- let checkCaller = thisOnly(i)
711- if ((checkCaller == checkCaller))
712- then $Tuple2([IntegerEntry(keyFeeAmount, newFee)], unit)
354+ let checks = [mustManager(i)]
355+ if ((checks == checks))
356+ then [IntegerEntry(keyFeeAmount, newFee)]
713357 else throw("Strict value is not equal to itself.")
714358 }
715359
716360
717361
718362 @Callable(i)
719-func gwxAvailableForVoteREADONLY (userAddress) = {
720- let gwxAmountAtEnd = getUserGwxAmountAtHeight(userAddress, endHeight)
721- $Tuple2(nil, gwxAmountAtEnd)
363+func setThreshold (newThreshold) = {
364+ let checks = [mustManager(i), if ((newThreshold > 0))
365+ then true
366+ else throw("invalid threshold")]
367+ if ((checks == checks))
368+ then $Tuple2([IntegerEntry(keyThreshold, newThreshold)], unit)
369+ else throw("Strict value is not equal to itself.")
722370 }
723371
372+
373+
374+@Callable(i)
375+func finalize (amountAssetId,priceAssetId) = {
376+ let suggestIndex = value(getInteger(keyInListAssetId(amountAssetId, priceAssetId)))
377+ let votingFinishHeight = (value(getInteger(keyStartHeight(amountAssetId, priceAssetId, suggestIndex))) + value(getInteger(keyEpochLength)))
378+ let checks = [if (isDefined(getInteger(keyInListAssetId(amountAssetId, priceAssetId))))
379+ then true
380+ else throw("no assets pair"), if ((height >= votingFinishHeight))
381+ then true
382+ else throw("insufficient height for completion")]
383+ if ((checks == checks))
384+ then {
385+ let votingResult = split(value(getString(keyVotingResult(amountAssetId, priceAssetId, suggestIndex))), separator)
386+ let positiveVotes = parseIntValue(votingResult[1])
387+ let negativeVotes = parseIntValue(votingResult[2])
388+ let allVotes = (positiveVotes + negativeVotes)
389+ let threshold = getIntOrFail(keyThreshold)
390+ let actions = if (if ((allVotes >= threshold))
391+ then (positiveVotes > negativeVotes)
392+ else false)
393+ then {
394+ let res = invoke(factoryContract, "setWxEmissionPoolLabel", [amountAssetId, priceAssetId], nil)
395+ if ((res == res))
396+ then {
397+ let votingEmissionInv = invoke(votingEmissionContract, "create", [amountAssetId, priceAssetId], nil)
398+ if ((votingEmissionInv == votingEmissionInv))
399+ then $Tuple2([DeleteEntry(keyInListAssetId(amountAssetId, priceAssetId))], unit)
400+ else throw("Strict value is not equal to itself.")
401+ }
402+ else throw("Strict value is not equal to itself.")
403+ }
404+ else $Tuple2([DeleteEntry(keyInListAssetId(amountAssetId, priceAssetId))], unit)
405+ actions
406+ }
407+ else throw("Strict value is not equal to itself.")
408+ }
409+
410+
411+
412+@Callable(i)
413+func setManager (pendingManagerPublicKey) = {
414+ let checkCaller = mustManager(i)
415+ if ((checkCaller == checkCaller))
416+ then {
417+ let checkManagerPublicKey = fromBase58String(pendingManagerPublicKey)
418+ if ((checkManagerPublicKey == checkManagerPublicKey))
419+ then [StringEntry(keyPendingManagerPublicKey(), pendingManagerPublicKey)]
420+ else throw("Strict value is not equal to itself.")
421+ }
422+ else throw("Strict value is not equal to itself.")
423+ }
424+
425+
426+
427+@Callable(i)
428+func confirmManager () = {
429+ let pm = pendingManagerPublicKeyOrUnit()
430+ let hasPM = if (isDefined(pm))
431+ then true
432+ else throw("no pending manager")
433+ if ((hasPM == hasPM))
434+ then {
435+ let checkPM = if ((i.callerPublicKey == value(pm)))
436+ then true
437+ else throw("you are not pending manager")
438+ if ((checkPM == checkPM))
439+ then [StringEntry(keyManagerPublicKey(), toBase58String(value(pm))), DeleteEntry(keyPendingManagerPublicKey())]
440+ else throw("Strict value is not equal to itself.")
441+ }
442+ else throw("Strict value is not equal to itself.")
443+ }
444+
445+
446+@Verifier(tx)
447+func verify () = {
448+ let targetPublicKey = match managerPublicKeyOrUnit() {
449+ case pk: ByteVector =>
450+ pk
451+ case _: Unit =>
452+ tx.senderPublicKey
453+ case _ =>
454+ throw("Match error")
455+ }
456+ sigVerify(tx.bodyBytes, tx.proofs[0], targetPublicKey)
457+ }
724458

github/deemru/w8io/169f3d6 
79.72 ms