2021.07.12 18:40 [2675360] smart account 3PC9BfRwJWWiw9AREE2B3eWzCks3CYtg4yo > SELF 0.00000000 Waves

{ "type": 13, "id": "PLCCMdvXSZBNrn5QuDU4Nxiabwmq1NQPjv4ADKWCmdT", "fee": 14000000, "feeAssetId": null, "timestamp": 1626106838826, "version": 1, "sender": "3PC9BfRwJWWiw9AREE2B3eWzCks3CYtg4yo", "senderPublicKey": "BRnVwSVctnV8pge5vRpsJdWnkjWEJspFb6QvrmZvu3Ht", "proofs": [ "5s1mY2km4A3aBYEV1rjXhNHhMnuRc6tsEV4Z5nY6cSiEw6gS1TFWJ8Y182SEdcKVPcRwSBqvm7uvSXe52d6bwjc1", "3gzUvepqHJvWqw27tqiTwLKuBHS8ZyDAkZGqKqoDbGtx2Y3HSS6vEmacBdCgxY2bmkTouCSjHjKXojrUVxNfLDYV", "3zSeDBB12cNCRLmywH8vDvsMLiTDNHx16Lu5H5MdPfPRhjfU1MrDumCq9j2dm2RZDuMBN5ey7R1U1mu7BbdahdL", "5jJJkNJF4JEvVQ8WH7tRQ4B2MdvLs6eAfpcHRgFCPpzjthNmWVKyrC5Y1vFe9BmE61vMoCBfkCULC6fFkq5ZSgAN" ], "script": "base64:AAIEAAAAAAAAABwIAhIAEgASBQoDCAEIEgYKBAgBCAISABIDCgEIAAAAaQEAAAAOZ2V0TnVtYmVyQnlLZXkAAAABAAAAA2tleQkBAAAAC3ZhbHVlT3JFbHNlAAAAAgkABBoAAAACBQAAAAR0aGlzBQAAAANrZXkAAAAAAAAAAAABAAAADmdldFN0cmluZ0J5S2V5AAAAAQAAAANrZXkJAQAAAAt2YWx1ZU9yRWxzZQAAAAIJAAQdAAAAAgUAAAAEdGhpcwUAAAADa2V5AgAAAAABAAAADGdldEJvb2xCeUtleQAAAAEAAAADa2V5CQEAAAALdmFsdWVPckVsc2UAAAACCQAEGwAAAAIFAAAABHRoaXMFAAAAA2tleQcBAAAAGGdldE51bWJlckJ5QWRkcmVzc0FuZEtleQAAAAIAAAAHYWRkcmVzcwAAAANrZXkJAQAAAAt2YWx1ZU9yRWxzZQAAAAIJAAQaAAAAAgkBAAAAEUBleHRyTmF0aXZlKDEwNjIpAAAAAQUAAAAHYWRkcmVzcwUAAAADa2V5AAAAAAAAAAAAAQAAABhnZXRTdHJpbmdCeUFkZHJlc3NBbmRLZXkAAAACAAAAB2FkZHJlc3MAAAADa2V5CQEAAAALdmFsdWVPckVsc2UAAAACCQAEHQAAAAIJAQAAABFAZXh0ck5hdGl2ZSgxMDYyKQAAAAEFAAAAB2FkZHJlc3MFAAAAA2tleQIAAAAAAQAAABZnZXRCb29sQnlBZGRyZXNzQW5kS2V5AAAAAgAAAAdhZGRyZXNzAAAAA2tleQkBAAAAC3ZhbHVlT3JFbHNlAAAAAgkABBsAAAACCQEAAAARQGV4dHJOYXRpdmUoMTA2MikAAAABBQAAAAdhZGRyZXNzBQAAAANrZXkHAAAAABBwdWJLZXlBZG1pbnNMaXN0CQAETAAAAAICAAAALEdKZExTYUxpdjVLN3h1ZWphYzhtY1JjSG95bzNkUHJFU3J2a3RHM2E2TUFSCQAETAAAAAICAAAALEZXVmZmWXIyQUxtSE1lalptM1dxZUx6NlNkeW0zZ0xGR3RKbjRLVHd5VTV4CQAETAAAAAICAAAALDNXaDJMYVdjYjVnZzdLMnBQY1czRXA2RUF1UkJ6WWtBZ3JkcHQ0M2pUREZhCQAETAAAAAICAAAALDVXUlhGU2p3Y1RiTmZLY0pzOFpxWG1TU1dZc1NWSlV0TXZNcVpqNWhINE5jBQAAAANuaWwAAAAAA1NFUAIAAAACX18AAAAAB1dBVkVMRVQAAAAAAAX14QAAAAAABVBBVUxJAAAAAAAAD0JAAAAAAAhQUklDRUxFVAAAAAAAAA9CQAAAAAAOREVGQVVMVFNXQVBGRUUAAAAAAAAATiAAAAAADElkeE5ldEFtb3VudAAAAAAAAAAAAAAAAAAMSWR4RmVlQW1vdW50AAAAAAAAAAABAAAAAA5JZHhHcm9zc0Ftb3VudAAAAAAAAAAAAgAAAAASTmV1dHJpbm9Bc3NldElkS2V5AgAAABFuZXV0cmlub19hc3NldF9pZAAAAAAOQm9uZEFzc2V0SWRLZXkCAAAADWJvbmRfYXNzZXRfaWQAAAAAEkF1Y3Rpb25Db250cmFjdEtleQIAAAAQYXVjdGlvbl9jb250cmFjdAAAAAAWTGlxdWlkYXRpb25Db250cmFjdEtleQIAAAAUbGlxdWlkYXRpb25fY29udHJhY3QAAAAADlJQRENvbnRyYWN0S2V5AgAAAAxycGRfY29udHJhY3QAAAAAEUNvbnRvbENvbnRyYWN0S2V5AgAAABBjb250cm9sX2NvbnRyYWN0AAAAABtCYWxhbmNlV2F2ZXNMb2NrSW50ZXJ2YWxLZXkCAAAAG2JhbGFuY2Vfd2F2ZXNfbG9ja19pbnRlcnZhbAAAAAAeQmFsYW5jZU5ldXRyaW5vTG9ja0ludGVydmFsS2V5AgAAAB5iYWxhbmNlX25ldXRyaW5vX2xvY2tfaW50ZXJ2YWwAAAAAFU1pbldhdmVzU3dhcEFtb3VudEtleQIAAAAVbWluX3dhdmVzX3N3YXBfYW1vdW50AAAAABhNaW5OZXV0cmlub1N3YXBBbW91bnRLZXkCAAAAGG1pbl9uZXV0cmlub19zd2FwX2Ftb3VudAAAAAAbTm9kZU9yYWNsZVByb3ZpZGVyUHViS2V5S2V5AgAAABRub2RlX29yYWNsZV9wcm92aWRlcgAAAAAVTmV1dHJpbm9PdXRGZWVQYXJ0S2V5AgAAABhuZXV0cmlub091dF9zd2FwX2ZlZVBhcnQAAAAAEldhdmVzT3V0RmVlUGFydEtleQIAAAAVd2F2ZXNPdXRfc3dhcF9mZWVQYXJ0AAAAABVGZWVzTWFuYWdlckFkZHJlc3NLZXkCAAAAFGZlZXNfbWFuYWdlcl9hZGRyZXNzAAAAABJSc2FSYW5kUHVibGljNThLZXkCAAAAD3JhbmRfcnNhX3B1YmxpYwAAAAAIUHJpY2VLZXkCAAAABXByaWNlAAAAAA1QcmljZUluZGV4S2V5AgAAAAtwcmljZV9pbmRleAAAAAAMSXNCbG9ja2VkS2V5AgAAAAppc19ibG9ja2VkAQAAABJnZXRQcmljZUhpc3RvcnlLZXkAAAABAAAABWJsb2NrCQABLAAAAAIJAAEsAAAAAgUAAAAIUHJpY2VLZXkCAAAAAV8JAAGkAAAAAQUAAAAFYmxvY2sBAAAAGGdldEhlaWdodFByaWNlQnlJbmRleEtleQAAAAEAAAAFaW5kZXgJAAEsAAAAAgkAASwAAAACBQAAAA1QcmljZUluZGV4S2V5AgAAAAFfCQABpAAAAAEFAAAABWluZGV4AQAAABhnZXRCYWxhbmNlVW5sb2NrQmxvY2tLZXkAAAABAAAABW93bmVyCQABLAAAAAICAAAAFWJhbGFuY2VfdW5sb2NrX2Jsb2NrXwUAAAAFb3duZXIBAAAAEG1pblN3YXBBbW91bnRLRVkAAAABAAAACHN3YXBUeXBlCQABLAAAAAIJAAEsAAAAAgIAAAAEbWluXwUAAAAIc3dhcFR5cGUCAAAADF9zd2FwX2Ftb3VudAEAAAAOdG90YWxMb2NrZWRLRVkAAAABAAAACHN3YXBUeXBlCQABLAAAAAICAAAADWJhbGFuY2VfbG9ja18FAAAACHN3YXBUeXBlAQAAABR0b3RhbExvY2tlZEJ5VXNlcktFWQAAAAIAAAAIc3dhcFR5cGUAAAAFb3duZXIJAAS5AAAAAgkABEwAAAACAgAAAAxiYWxhbmNlX2xvY2sJAARMAAAAAgUAAAAIc3dhcFR5cGUJAARMAAAAAgUAAAAFb3duZXIFAAAAA25pbAIAAAABXwEAAAAWYmFsYW5jZUxvY2tJbnRlcnZhbEtFWQAAAAEAAAAIc3dhcFR5cGUJAAEsAAAAAgkAASwAAAACAgAAAAhiYWxhbmNlXwUAAAAIc3dhcFR5cGUCAAAADl9sb2NrX2ludGVydmFsAQAAABltaW5CYWxhbmNlTG9ja0ludGVydmFsS0VZAAAAAQAAAAhzd2FwVHlwZQkAASwAAAACCQABLAAAAAICAAAACGJhbGFuY2VfBQAAAAhzd2FwVHlwZQIAAAAWX2xvY2tfaW50ZXJ2YWxfbWluaW11bQEAAAANb3V0RmVlUGFydEtFWQAAAAEAAAAIc3dhcFR5cGUJAAEsAAAAAgUAAAAIc3dhcFR5cGUCAAAAEE91dF9zd2FwX2ZlZVBhcnQBAAAAEW1pblN3YXBBbW91bnRSRUFEAAAAAQAAAAhzd2FwVHlwZQkBAAAAC3ZhbHVlT3JFbHNlAAAAAgkABBoAAAACBQAAAAR0aGlzCQEAAAAQbWluU3dhcEFtb3VudEtFWQAAAAEFAAAACHN3YXBUeXBlAAAAAAAAAAAAAQAAAA90b3RhbExvY2tlZFJFQUQAAAABAAAACHN3YXBUeXBlCQEAAAALdmFsdWVPckVsc2UAAAACCQAEGgAAAAIFAAAABHRoaXMJAQAAAA50b3RhbExvY2tlZEtFWQAAAAEFAAAACHN3YXBUeXBlAAAAAAAAAAAAAQAAABV0b3RhbExvY2tlZEJ5VXNlclJFQUQAAAACAAAACHN3YXBUeXBlAAAABW93bmVyCQEAAAALdmFsdWVPckVsc2UAAAACCQAEGgAAAAIFAAAABHRoaXMJAQAAABR0b3RhbExvY2tlZEJ5VXNlcktFWQAAAAIFAAAACHN3YXBUeXBlBQAAAAVvd25lcgAAAAAAAAAAAAEAAAAXYmFsYW5jZUxvY2tJbnRlcnZhbFJFQUQAAAABAAAACHN3YXBUeXBlCQEAAAALdmFsdWVPckVsc2UAAAACCQAEGgAAAAIFAAAABHRoaXMJAQAAABZiYWxhbmNlTG9ja0ludGVydmFsS0VZAAAAAQUAAAAIc3dhcFR5cGUAAAAAAAAABaABAAAAGm1pbkJhbGFuY2VMb2NrSW50ZXJ2YWxSRUFEAAAAAQAAAAhzd2FwVHlwZQkBAAAAC3ZhbHVlT3JFbHNlAAAAAgkABBoAAAACBQAAAAR0aGlzCQEAAAAZbWluQmFsYW5jZUxvY2tJbnRlcnZhbEtFWQAAAAEFAAAACHN3YXBUeXBlAAAAAAAAAAA8AQAAABVmZWVNYW5hZ2VyQWRkcmVzc1JFQUQAAAAACQEAAAATdmFsdWVPckVycm9yTWVzc2FnZQAAAAIJAAQmAAAAAQkBAAAAE3ZhbHVlT3JFcnJvck1lc3NhZ2UAAAACCQAEHQAAAAIFAAAABHRoaXMFAAAAFUZlZXNNYW5hZ2VyQWRkcmVzc0tleQkAASwAAAACBQAAABVGZWVzTWFuYWdlckFkZHJlc3NLZXkCAAAAESBpcyBub3Qgc3BlY2lmaWVkCQABLAAAAAIFAAAAFUZlZXNNYW5hZ2VyQWRkcmVzc0tleQIAAAAXIGludmFsaWQgYWRkcmVzcyBmb3JtYXQBAAAAFmNvbnZlcnROZXV0cmlub1RvV2F2ZXMAAAACAAAABmFtb3VudAAAAAVwcmljZQkAAGsAAAADCQAAawAAAAMFAAAABmFtb3VudAUAAAAIUFJJQ0VMRVQFAAAABXByaWNlBQAAAAdXQVZFTEVUBQAAAAVQQVVMSQEAAAAWY29udmVydFdhdmVzVG9OZXV0cmlubwAAAAIAAAAGYW1vdW50AAAABXByaWNlCQAAawAAAAMJAABrAAAAAwUAAAAGYW1vdW50BQAAAAVwcmljZQUAAAAIUFJJQ0VMRVQFAAAABVBBVUxJBQAAAAdXQVZFTEVUAQAAABJjb252ZXJ0V2F2ZXNUb0JvbmQAAAACAAAABmFtb3VudAAAAAVwcmljZQkBAAAAFmNvbnZlcnRXYXZlc1RvTmV1dHJpbm8AAAACBQAAAAZhbW91bnQFAAAABXByaWNlAQAAABZjb252ZXJ0SnNvbkFycmF5VG9MaXN0AAAAAQAAAAlqc29uQXJyYXkJAAS1AAAAAgUAAAAJanNvbkFycmF5AgAAAAEsAQAAABFtaW5Td2FwQW1vdW50RkFJTAAAAAIAAAAIc3dhcFR5cGUAAAANbWluU3dhcEFtb3VudAkAAAIAAAABCQABLAAAAAIJAAEsAAAAAgkAASwAAAACAgAAABhUaGUgc3BlY2lmaWVkIGFtb3VudCBpbiAFAAAACHN3YXBUeXBlAgAAACsgc3dhcCBpcyBsZXNzIHRoYW4gdGhlIHJlcXVpcmVkIG1pbmltdW0gb2YgCQABpAAAAAEFAAAADW1pblN3YXBBbW91bnQBAAAAFWVtZXJnZW5jeVNodXRkb3duRkFJTAAAAAAJAAACAAAAAQIAAABaY29udHJhY3QgaXMgYmxvY2tlZCBieSBFTUVSR0VOQ1kgU0hVVERPV04gYWN0aW9ucyB1bnRpbGwgcmVhY3RpdmF0aW9uIGJ5IGVtZXJnZW5jeSBvcmFjbGVzAQAAAA5wcmljZUluZGV4RkFJTAAAAAUAAAAFaW5kZXgAAAAKcHJpY2VJbmRleAAAAAtpbmRleEhlaWdodAAAAAx1bmxvY2tIZWlnaHQAAAAPcHJldkluZGV4SGVpZ2h0CQAAAgAAAAEJAAEsAAAAAgkAASwAAAACCQABLAAAAAIJAAEsAAAAAgkAASwAAAACCQABLAAAAAIJAAEsAAAAAgkAASwAAAACCQABLAAAAAICAAAAI2ludmFsaWQgcHJpY2UgaGlzdG9yeSBpbmRleDogaW5kZXg9CQABpAAAAAEFAAAABWluZGV4AgAAAAwgcHJpY2VJbmRleD0JAAGkAAAAAQUAAAAKcHJpY2VJbmRleAIAAAANIGluZGV4SGVpZ2h0PQkAAaQAAAABBQAAAAtpbmRleEhlaWdodAIAAAAOIHVubG9ja0hlaWdodD0JAAGkAAAAAQUAAAAMdW5sb2NrSGVpZ2h0AgAAABEgcHJldkluZGV4SGVpZ2h0PQkAAaQAAAABBQAAAA9wcmV2SW5kZXhIZWlnaHQAAAAAE2xpcXVpZGF0aW9uQ29udHJhY3QJAQAAAA5nZXRTdHJpbmdCeUtleQAAAAEFAAAAFkxpcXVpZGF0aW9uQ29udHJhY3RLZXkAAAAAD25ldXRyaW5vQXNzZXRJZAkAAlkAAAABCQEAAAAOZ2V0U3RyaW5nQnlLZXkAAAABBQAAABJOZXV0cmlub0Fzc2V0SWRLZXkAAAAAD2F1Y3Rpb25Db250cmFjdAkBAAAADmdldFN0cmluZ0J5S2V5AAAAAQUAAAASQXVjdGlvbkNvbnRyYWN0S2V5AAAAAAtycGRDb250cmFjdAkBAAAADmdldFN0cmluZ0J5S2V5AAAAAQUAAAAOUlBEQ29udHJhY3RLZXkAAAAAD2NvbnRyb2xDb250cmFjdAkBAAAADmdldFN0cmluZ0J5S2V5AAAAAQUAAAARQ29udG9sQ29udHJhY3RLZXkAAAAACnByaWNlSW5kZXgJAQAAABhnZXROdW1iZXJCeUFkZHJlc3NBbmRLZXkAAAACBQAAAA9jb250cm9sQ29udHJhY3QFAAAADVByaWNlSW5kZXhLZXkAAAAACWlzQmxvY2tlZAkBAAAAFmdldEJvb2xCeUFkZHJlc3NBbmRLZXkAAAACBQAAAA9jb250cm9sQ29udHJhY3QFAAAADElzQmxvY2tlZEtleQAAAAAYbm9kZU9yYWNsZVByb3ZpZGVyUHViS2V5CQACWQAAAAEJAQAAAA5nZXRTdHJpbmdCeUtleQAAAAEFAAAAG05vZGVPcmFjbGVQcm92aWRlclB1YktleUtleQAAAAALYm9uZEFzc2V0SWQJAAJZAAAAAQIAAAAsNm5TcFZ5Tkg3eU02OWVnNDQ2d3JRUjk0aXBiYmNtWk1VMUVOUHdhbkM5N2cAAAAAFWRlcHJlY2F0ZWRCb25kQXNzZXRJZAkAAlkAAAABAgAAACw5NzVha1pCZm5NajUxM1U3TVphSEt6UXJtc0V4NWFFM3dkV0tUckhCaGJqRgAAAAAGcnNhUHViCQACWwAAAAEJAQAAABN2YWx1ZU9yRXJyb3JNZXNzYWdlAAAAAgkABB0AAAACBQAAAAR0aGlzBQAAABJSc2FSYW5kUHVibGljNThLZXkCAAAAJVJTQSBwdWJsaWMga2V5IGhhcyBub3QgYmVlbiBzcGVjaWZpZWQAAAAAEG5ldXRyaW5vQ29udHJhY3QFAAAABHRoaXMAAAAADGN1cnJlbnRQcmljZQkBAAAAGGdldE51bWJlckJ5QWRkcmVzc0FuZEtleQAAAAIFAAAAD2NvbnRyb2xDb250cmFjdAUAAAAIUHJpY2VLZXkAAAAAFW5ldXRyaW5vTG9ja2VkQmFsYW5jZQkBAAAAD3RvdGFsTG9ja2VkUkVBRAAAAAECAAAACG5ldXRyaW5vAAAAABJ3YXZlc0xvY2tlZEJhbGFuY2UJAQAAAA90b3RhbExvY2tlZFJFQUQAAAABAgAAAAV3YXZlcwAAAAAHcmVzZXJ2ZQkAAGUAAAACCAkAA+8AAAABBQAAABBuZXV0cmlub0NvbnRyYWN0AAAAB3JlZ3VsYXIFAAAAEndhdmVzTG9ja2VkQmFsYW5jZQAAAAAObmV1dHJpbm9TdXBwbHkJAABlAAAAAgkAAGUAAAACCQAAZAAAAAIFAAAAFW5ldXRyaW5vTG9ja2VkQmFsYW5jZQgJAQAAAAV2YWx1ZQAAAAEJAAPsAAAAAQUAAAAPbmV1dHJpbm9Bc3NldElkAAAACHF1YW50aXR5CQAD8AAAAAIFAAAAEG5ldXRyaW5vQ29udHJhY3QFAAAAD25ldXRyaW5vQXNzZXRJZAkAA/AAAAACCQEAAAARQGV4dHJOYXRpdmUoMTA2MikAAAABBQAAABNsaXF1aWRhdGlvbkNvbnRyYWN0BQAAAA9uZXV0cmlub0Fzc2V0SWQAAAAAB3N1cnBsdXMJAABlAAAAAgkBAAAAFmNvbnZlcnRXYXZlc1RvTmV1dHJpbm8AAAACBQAAAAdyZXNlcnZlBQAAAAxjdXJyZW50UHJpY2UFAAAADm5ldXRyaW5vU3VwcGx5AAAAAAdkZWZpY2l0CQAAZQAAAAIFAAAADm5ldXRyaW5vU3VwcGx5CQEAAAAWY29udmVydFdhdmVzVG9OZXV0cmlubwAAAAIFAAAAB3Jlc2VydmUFAAAADGN1cnJlbnRQcmljZQEAAAAbY2hlY2tJc1ZhbGlkTWluU3BvbnNvcmVkRmVlAAAAAQAAAAJ0eAQAAAAOTUlOVFJBTlNGRVJGRUUAAAAAAAABhqAEAAAAFlNwb25zb3JlZEZlZVVwcGVyQm91bmQAAAAAAAAAA+gEAAAAD3JlYWxOZXV0cmlub0ZlZQkBAAAAFmNvbnZlcnRXYXZlc1RvTmV1dHJpbm8AAAACBQAAAA5NSU5UUkFOU0ZFUkZFRQUAAAAMY3VycmVudFByaWNlBAAAAA5taW5OZXV0cmlub0ZlZQkAAGgAAAACBQAAAA9yZWFsTmV1dHJpbm9GZWUAAAAAAAAAAAIEAAAADm1heE5ldXRyaW5vRmVlCQAAawAAAAMFAAAAD3JlYWxOZXV0cmlub0ZlZQUAAAAWU3BvbnNvcmVkRmVlVXBwZXJCb3VuZAAAAAAAAAAAZAQAAAAIaW5wdXRGZWUJAQAAAAV2YWx1ZQAAAAEIBQAAAAJ0eAAAABRtaW5TcG9uc29yZWRBc3NldEZlZQMDCQAAZwAAAAIFAAAACGlucHV0RmVlBQAAAA5taW5OZXV0cmlub0ZlZQkAAGcAAAACBQAAAA5tYXhOZXV0cmlub0ZlZQUAAAAIaW5wdXRGZWUHCQAAAAAAAAIIBQAAAAJ0eAAAAAdhc3NldElkBQAAAA9uZXV0cmlub0Fzc2V0SWQHAQAAAA9nZXRQcmljZUhpc3RvcnkAAAABAAAABWJsb2NrCQEAAAAYZ2V0TnVtYmVyQnlBZGRyZXNzQW5kS2V5AAAAAgUAAAAPY29udHJvbENvbnRyYWN0CQEAAAASZ2V0UHJpY2VIaXN0b3J5S2V5AAAAAQUAAAAFYmxvY2sBAAAAFWdldEhlaWdodFByaWNlQnlJbmRleAAAAAEAAAAFaW5kZXgJAQAAABhnZXROdW1iZXJCeUFkZHJlc3NBbmRLZXkAAAACBQAAAA9jb250cm9sQ29udHJhY3QJAQAAABhnZXRIZWlnaHRQcmljZUJ5SW5kZXhLZXkAAAABBQAAAAVpbmRleAAAAAAMc0lkeFN3YXBUeXBlAAAAAAAAAAABAAAAAApzSWR4U3RhdHVzAAAAAAAAAAACAAAAAAxzSWR4SW5BbW91bnQAAAAAAAAAAAMAAAAACXNJZHhQcmljZQAAAAAAAAAABAAAAAAQc0lkeE91dE5ldEFtb3VudAAAAAAAAAAABQAAAAAQc0lkeE91dEZlZUFtb3VudAAAAAAAAAAABgAAAAAPc0lkeFN0YXJ0SGVpZ2h0AAAAAAAAAAAHAAAAABJzSWR4U3RhcnRUaW1lc3RhbXAAAAAAAAAAAAgAAAAADXNJZHhFbmRIZWlnaHQAAAAAAAAAAAkAAAAAEHNJZHhFbmRUaW1lc3RhbXAAAAAAAAAAAAoAAAAAFHNJZHhTZWxmVW5sb2NrSGVpZ2h0AAAAAAAAAAALAAAAABRzSWR4UmFuZFVubG9ja0hlaWdodAAAAAAAAAAADAEAAAAHc3dhcEtFWQAAAAIAAAALdXNlckFkZHJlc3MAAAAEdHhJZAkABLkAAAACCQAETAAAAAICAAAABCVzJXMJAARMAAAAAgUAAAALdXNlckFkZHJlc3MJAARMAAAAAgUAAAAEdHhJZAUAAAADbmlsBQAAAANTRVABAAAAC3N0clN3YXBEQVRBAAAADAAAAAhzd2FwVHlwZQAAAAZzdGF0dXMAAAAIaW5BbW91bnQAAAAFcHJpY2UAAAAMb3V0TmV0QW1vdW50AAAADG91dEZlZUFtb3VudAAAAAtzdGFydEhlaWdodAAAAA5zdGFydFRpbWVzdGFtcAAAAAllbmRIZWlnaHQAAAAMZW5kVGltZXN0YW1wAAAAEHNlbGZVbmxvY2tIZWlnaHQAAAAQcmFuZFVubG9ja0hlaWdodAkABLkAAAACCQAETAAAAAICAAAAGCVzJXMlZCVkJWQlZCVkJWQlZCVkJWQlZAkABEwAAAACBQAAAAhzd2FwVHlwZQkABEwAAAACBQAAAAZzdGF0dXMJAARMAAAAAgUAAAAIaW5BbW91bnQJAARMAAAAAgUAAAAFcHJpY2UJAARMAAAAAgUAAAAMb3V0TmV0QW1vdW50CQAETAAAAAIFAAAADG91dEZlZUFtb3VudAkABEwAAAACBQAAAAtzdGFydEhlaWdodAkABEwAAAACBQAAAA5zdGFydFRpbWVzdGFtcAkABEwAAAACBQAAAAllbmRIZWlnaHQJAARMAAAAAgUAAAAMZW5kVGltZXN0YW1wCQAETAAAAAIFAAAAEHNlbGZVbmxvY2tIZWlnaHQJAARMAAAAAgUAAAAQcmFuZFVubG9ja0hlaWdodAUAAAADbmlsBQAAAANTRVABAAAAD3BlbmRpbmdTd2FwREFUQQAAAAMAAAAIc3dhcFR5cGUAAAANaW5Bc3NldEFtb3VudAAAABBzZWxmVW5sb2NrSGVpZ2h0CQEAAAALc3RyU3dhcERBVEEAAAAMBQAAAAhzd2FwVHlwZQIAAAAHUEVORElORwkAAaQAAAABBQAAAA1pbkFzc2V0QW1vdW50AgAAAAEwAgAAAAEwAgAAAAEwCQABpAAAAAEFAAAABmhlaWdodAkAAaQAAAABCAUAAAAJbGFzdEJsb2NrAAAACXRpbWVzdGFtcAIAAAABMAIAAAABMAkAAaQAAAABBQAAABBzZWxmVW5sb2NrSGVpZ2h0AgAAAAEwAQAAAA5maW5pc2hTd2FwREFUQQAAAAUAAAAJZGF0YUFycmF5AAAABXByaWNlAAAADG91dE5ldEFtb3VudAAAAAxvdXRGZWVBbW91bnQAAAAQcmFuZFVubG9ja0hlaWdodAkBAAAAC3N0clN3YXBEQVRBAAAADAkAAZEAAAACBQAAAAlkYXRhQXJyYXkFAAAADHNJZHhTd2FwVHlwZQIAAAAIRklOSVNIRUQJAAGRAAAAAgUAAAAJZGF0YUFycmF5BQAAAAxzSWR4SW5BbW91bnQJAAGkAAAAAQUAAAAFcHJpY2UJAAGkAAAAAQUAAAAMb3V0TmV0QW1vdW50CQABpAAAAAEFAAAADG91dEZlZUFtb3VudAkAAZEAAAACBQAAAAlkYXRhQXJyYXkFAAAAD3NJZHhTdGFydEhlaWdodAkAAZEAAAACBQAAAAlkYXRhQXJyYXkFAAAAEnNJZHhTdGFydFRpbWVzdGFtcAkAAaQAAAABBQAAAAZoZWlnaHQJAAGkAAAAAQgFAAAACWxhc3RCbG9jawAAAAl0aW1lc3RhbXAJAAGRAAAAAgUAAAAJZGF0YUFycmF5BQAAABRzSWR4U2VsZlVubG9ja0hlaWdodAkAAaQAAAABBQAAABByYW5kVW5sb2NrSGVpZ2h0AQAAABJzd2FwRGF0YUZhaWxPclJFQUQAAAACAAAAC3VzZXJBZGRyZXNzAAAACHN3YXBUeElkBAAAAAdzd2FwS2V5CQEAAAAHc3dhcEtFWQAAAAIFAAAAC3VzZXJBZGRyZXNzBQAAAAhzd2FwVHhJZAkABLUAAAACCQEAAAATdmFsdWVPckVycm9yTWVzc2FnZQAAAAIJAAQdAAAAAgUAAAAEdGhpcwUAAAAHc3dhcEtleQkAASwAAAACAgAAABFubyBzd2FwIGRhdGEgZm9yIAUAAAAHc3dhcEtleQUAAAADU0VQAQAAAAlhcHBseUZlZXMAAAACAAAAC2Ftb3VudEdyb3NzAAAAB2ZlZVBhcnQEAAAACWZlZUFtb3VudAkAAGsAAAADBQAAAAthbW91bnRHcm9zcwUAAAAHZmVlUGFydAUAAAAFUEFVTEkJAARMAAAAAgkAAGUAAAACBQAAAAthbW91bnRHcm9zcwUAAAAJZmVlQW1vdW50CQAETAAAAAIFAAAACWZlZUFtb3VudAkABEwAAAACBQAAAAthbW91bnRHcm9zcwUAAAADbmlsAQAAABZyYW5kVW5sb2NrSGVpZ2h0T3JGYWlsAAAABAAAAAR0eElkAAAABnJzYVNpZwAAAAhzd2FwVHlwZQAAAAtzdGFydEhlaWdodAQAAAAKaXNSc2FWYWxpZAkACigAAAAEBQAAAAZTSEEyNTYJAAGbAAAAAQUAAAAEdHhJZAUAAAAGcnNhU2lnBQAAAAZyc2FQdWIDCQEAAAABIQAAAAEFAAAACmlzUnNhVmFsaWQJAAACAAAAAQIAAAAVaW52YWxpZCBSU0Egc2lnbmF0dXJlBAAAABZtYXhCYWxhbmNlTG9ja0ludGVydmFsCQEAAAAXYmFsYW5jZUxvY2tJbnRlcnZhbFJFQUQAAAABBQAAAAhzd2FwVHlwZQQAAAAWbWluQmFsYW5jZUxvY2tJbnRlcnZhbAkBAAAAGm1pbkJhbGFuY2VMb2NrSW50ZXJ2YWxSRUFEAAAAAQUAAAAIc3dhcFR5cGUEAAAABHJhbmQJAABqAAAAAgkABLEAAAABCQALVAAAAAEFAAAABnJzYVNpZwkAAGUAAAACBQAAABZtYXhCYWxhbmNlTG9ja0ludGVydmFsBQAAABZtaW5CYWxhbmNlTG9ja0ludGVydmFsBAAAABByYW5kTG9ja0ludGVydmFsCQAAZAAAAAIFAAAAFm1pbkJhbGFuY2VMb2NrSW50ZXJ2YWwDCQAAZgAAAAIAAAAAAAAAAAAFAAAABHJhbmQJAQAAAAEtAAAAAQUAAAAEcmFuZAUAAAAEcmFuZAkAAGQAAAACBQAAAAtzdGFydEhlaWdodAUAAAAQcmFuZExvY2tJbnRlcnZhbAAAAAARQmFsYW5jZUxvY2tlZGtLZXkCAAAADWJhbGFuY2VfbG9ja18AAAAAFVdhdmVzTG9ja2VkQmFsYW5jZUtleQkAASwAAAACBQAAABFCYWxhbmNlTG9ja2Vka0tleQIAAAAFd2F2ZXMAAAAAGE5ldXRyaW5vTG9ja2VkQmFsYW5jZUtleQkAASwAAAACBQAAABFCYWxhbmNlTG9ja2Vka0tleQIAAAAIbmV1dHJpbm8BAAAAGGdldFdhdmVzTG9ja2VkQmFsYW5jZUtleQAAAAEAAAAFb3duZXIJAAEsAAAAAgkAASwAAAACBQAAABVXYXZlc0xvY2tlZEJhbGFuY2VLZXkCAAAAAV8FAAAABW93bmVyAQAAABtnZXROZXV0cmlub0xvY2tlZEJhbGFuY2VLZXkAAAABAAAABW93bmVyCQABLAAAAAIJAAEsAAAAAgUAAAAYTmV1dHJpbm9Mb2NrZWRCYWxhbmNlS2V5AgAAAAFfBQAAAAVvd25lcgEAAAAVZ2V0V2F2ZXNMb2NrZWRCYWxhbmNlAAAAAQAAAAVvd25lcgkBAAAADmdldE51bWJlckJ5S2V5AAAAAQkBAAAAGGdldFdhdmVzTG9ja2VkQmFsYW5jZUtleQAAAAEFAAAABW93bmVyAQAAABhnZXROZXV0cmlub0xvY2tlZEJhbGFuY2UAAAABAAAABW93bmVyCQEAAAAOZ2V0TnVtYmVyQnlLZXkAAAABCQEAAAAbZ2V0TmV1dHJpbm9Mb2NrZWRCYWxhbmNlS2V5AAAAAQUAAAAFb3duZXIBAAAACmNvbW1vblN3YXAAAAACAAAACHN3YXBUeXBlAAAAAWkEAAAAA3BtdAkBAAAABXZhbHVlAAAAAQkAAZEAAAACCAUAAAABaQAAAAhwYXltZW50cwAAAAAAAAAAAAQAAAAHYWNjb3VudAkABCUAAAABCAUAAAABaQAAAAZjYWxsZXIEAAAABnR4SWQ1OAkAAlgAAAABCAUAAAABaQAAAA10cmFuc2FjdGlvbklkBAAAAA1taW5Td2FwQW1vdW50CQEAAAARbWluU3dhcEFtb3VudFJFQUQAAAABBQAAAAhzd2FwVHlwZQQAAAALdG90YWxMb2NrZWQJAQAAAA90b3RhbExvY2tlZFJFQUQAAAABBQAAAAhzd2FwVHlwZQQAAAARdG90YWxMb2NrZWRCeVVzZXIJAQAAABV0b3RhbExvY2tlZEJ5VXNlclJFQUQAAAACBQAAAAhzd2FwVHlwZQUAAAAHYWNjb3VudAQAAAAWYmFsYW5jZUxvY2tNYXhJbnRlcnZhbAkBAAAAF2JhbGFuY2VMb2NrSW50ZXJ2YWxSRUFEAAAAAQUAAAAIc3dhcFR5cGUEAAAAEHNlbGZVbmxvY2tIZWlnaHQJAABkAAAAAgUAAAAGaGVpZ2h0BQAAABZiYWxhbmNlTG9ja01heEludGVydmFsAwkAAGYAAAACBQAAAA1taW5Td2FwQW1vdW50CAUAAAADcG10AAAABmFtb3VudAkBAAAAEW1pblN3YXBBbW91bnRGQUlMAAAAAgUAAAAIc3dhcFR5cGUFAAAADW1pblN3YXBBbW91bnQDBQAAAAlpc0Jsb2NrZWQJAQAAABVlbWVyZ2VuY3lTaHV0ZG93bkZBSUwAAAAACQAETAAAAAIJAQAAAAxJbnRlZ2VyRW50cnkAAAACCQEAAAAUdG90YWxMb2NrZWRCeVVzZXJLRVkAAAACBQAAAAhzd2FwVHlwZQUAAAAHYWNjb3VudAkAAGQAAAACBQAAABF0b3RhbExvY2tlZEJ5VXNlcggFAAAAA3BtdAAAAAZhbW91bnQJAARMAAAAAgkBAAAADEludGVnZXJFbnRyeQAAAAIJAQAAABhnZXRCYWxhbmNlVW5sb2NrQmxvY2tLZXkAAAABBQAAAAdhY2NvdW50CQAAZAAAAAIFAAAABmhlaWdodAUAAAAWYmFsYW5jZUxvY2tNYXhJbnRlcnZhbAkABEwAAAACCQEAAAAMSW50ZWdlckVudHJ5AAAAAgkBAAAADnRvdGFsTG9ja2VkS0VZAAAAAQUAAAAIc3dhcFR5cGUJAABkAAAAAgUAAAALdG90YWxMb2NrZWQIBQAAAANwbXQAAAAGYW1vdW50CQAETAAAAAIJAQAAAAtTdHJpbmdFbnRyeQAAAAIJAQAAAAdzd2FwS0VZAAAAAgUAAAAHYWNjb3VudAUAAAAGdHhJZDU4CQEAAAAPcGVuZGluZ1N3YXBEQVRBAAAAAwUAAAAIc3dhcFR5cGUIBQAAAANwbXQAAAAGYW1vdW50BQAAABBzZWxmVW5sb2NrSGVpZ2h0BQAAAANuaWwBAAAADmNvbW1vbldpdGhkcmF3AAAABQAAAAdhY2NvdW50AAAABWluZGV4AAAACHN3YXBUeElkAAAADHJzYVNpZ09yVW5pdAAAAAFpBAAAAAt1c2VyQWRkcmVzcwkBAAAAEUBleHRyTmF0aXZlKDEwNjIpAAAAAQUAAAAHYWNjb3VudAQAAAARZmVlTWFuYWdlckFkZHJlc3MJAQAAABVmZWVNYW5hZ2VyQWRkcmVzc1JFQUQAAAAABAAAAAlkYXRhQXJyYXkJAQAAABJzd2FwRGF0YUZhaWxPclJFQUQAAAACBQAAAAdhY2NvdW50BQAAAAhzd2FwVHhJZAQAAAAQc2VsZlVubG9ja0hlaWdodAkBAAAADXBhcnNlSW50VmFsdWUAAAABCQABkQAAAAIFAAAACWRhdGFBcnJheQUAAAAUc0lkeFNlbGZVbmxvY2tIZWlnaHQEAAAACHN3YXBUeXBlCQABkQAAAAIFAAAACWRhdGFBcnJheQUAAAAMc0lkeFN3YXBUeXBlBAAAAAhpbkFtb3VudAkBAAAADXBhcnNlSW50VmFsdWUAAAABCQABkQAAAAIFAAAACWRhdGFBcnJheQUAAAAMc0lkeEluQW1vdW50BAAAAApzd2FwU3RhdHVzCQABkQAAAAIFAAAACWRhdGFBcnJheQUAAAAKc0lkeFN0YXR1cwQAAAALc3RhcnRIZWlnaHQJAQAAAA1wYXJzZUludFZhbHVlAAAAAQkAAZEAAAACBQAAAAlkYXRhQXJyYXkFAAAAD3NJZHhTdGFydEhlaWdodAQAAAAKb3V0RmVlUGFydAkBAAAAC3ZhbHVlT3JFbHNlAAAAAgkABBoAAAACBQAAAAR0aGlzCQEAAAANb3V0RmVlUGFydEtFWQAAAAEFAAAACHN3YXBUeXBlBQAAAA5ERUZBVUxUU1dBUEZFRQQAAAALdG90YWxMb2NrZWQJAQAAAA90b3RhbExvY2tlZFJFQUQAAAABBQAAAAhzd2FwVHlwZQQAAAARdG90YWxMb2NrZWRCeVVzZXIJAQAAABV0b3RhbExvY2tlZEJ5VXNlclJFQUQAAAACBQAAAAhzd2FwVHlwZQUAAAAHYWNjb3VudAQAAAAMdW5sb2NrSGVpZ2h0BAAAAAckbWF0Y2gwBQAAAAxyc2FTaWdPclVuaXQDCQAAAQAAAAIFAAAAByRtYXRjaDACAAAACkJ5dGVWZWN0b3IEAAAABnJzYVNpZwUAAAAHJG1hdGNoMAkBAAAAFnJhbmRVbmxvY2tIZWlnaHRPckZhaWwAAAAEBQAAAAhzd2FwVHhJZAUAAAAGcnNhU2lnBQAAAAhzd2FwVHlwZQUAAAALc3RhcnRIZWlnaHQDCQAAAQAAAAIFAAAAByRtYXRjaDACAAAABFVuaXQFAAAAEHNlbGZVbmxvY2tIZWlnaHQJAAACAAAAAQIAAAALTWF0Y2ggZXJyb3IEAAAAC2luZGV4SGVpZ2h0CQEAAAAVZ2V0SGVpZ2h0UHJpY2VCeUluZGV4AAAAAQUAAAAFaW5kZXgEAAAAD3ByZXZJbmRleEhlaWdodAkBAAAAFWdldEhlaWdodFByaWNlQnlJbmRleAAAAAEJAABlAAAAAgUAAAAFaW5kZXgAAAAAAAAAAAEEAAAADHByaWNlQnlJbmRleAkBAAAAD2dldFByaWNlSGlzdG9yeQAAAAEFAAAAC2luZGV4SGVpZ2h0BAAAABNvdXRBbW91bnRHcm9zc1R1cGxlAwkAAAAAAAACBQAAAAhzd2FwVHlwZQIAAAAFd2F2ZXMJAAUUAAAAAgkBAAAAFmNvbnZlcnRXYXZlc1RvTmV1dHJpbm8AAAACBQAAAAhpbkFtb3VudAUAAAAMcHJpY2VCeUluZGV4BQAAAA9uZXV0cmlub0Fzc2V0SWQDCQAAAAAAAAIFAAAACHN3YXBUeXBlAgAAAAhuZXV0cmlubwkABRQAAAACCQEAAAAWY29udmVydE5ldXRyaW5vVG9XYXZlcwAAAAIFAAAACGluQW1vdW50BQAAAAxwcmljZUJ5SW5kZXgFAAAABHVuaXQJAAACAAAAAQkAASwAAAACAgAAABZVbnN1cHBvcnRlZCBzd2FwIHR5cGUgBQAAAAhzd2FwVHlwZQQAAAAMcGF5b3V0c0FycmF5CQEAAAAJYXBwbHlGZWVzAAAAAggFAAAAE291dEFtb3VudEdyb3NzVHVwbGUAAAACXzEFAAAACm91dEZlZVBhcnQEAAAADG91dE5ldEFtb3VudAkAAZEAAAACBQAAAAxwYXlvdXRzQXJyYXkFAAAADElkeE5ldEFtb3VudAQAAAAMb3V0RmVlQW1vdW50CQABkQAAAAIFAAAADHBheW91dHNBcnJheQUAAAAMSWR4RmVlQW1vdW50AwUAAAAJaXNCbG9ja2VkCQEAAAAVZW1lcmdlbmN5U2h1dGRvd25GQUlMAAAAAAMJAQAAAAIhPQAAAAIFAAAACnN3YXBTdGF0dXMCAAAAB1BFTkRJTkcJAAACAAAAAQIAAAAfc3dhcCBoYXMgYmVlbiBhbHJlYWR5IHByb2Nlc3NlZAMJAABmAAAAAgUAAAAMdW5sb2NrSGVpZ2h0BQAAAAZoZWlnaHQJAAACAAAAAQkAASwAAAACCQABLAAAAAICAAAAEXBsZWFzZSB3YWl0IGZvcjogCQABpAAAAAEFAAAADHVubG9ja0hlaWdodAIAAAAfIGJsb2NrIGhlaWdodCB0byB3aXRoZHJhdyBmdW5kcwMDAwkAAGYAAAACBQAAAAVpbmRleAUAAAAKcHJpY2VJbmRleAYJAABmAAAAAgUAAAAMdW5sb2NrSGVpZ2h0BQAAAAtpbmRleEhlaWdodAYDCQEAAAACIT0AAAACBQAAAA9wcmV2SW5kZXhIZWlnaHQAAAAAAAAAAAAJAABnAAAAAgUAAAAPcHJldkluZGV4SGVpZ2h0BQAAAAx1bmxvY2tIZWlnaHQHCQEAAAAOcHJpY2VJbmRleEZBSUwAAAAFBQAAAAVpbmRleAUAAAAKcHJpY2VJbmRleAUAAAALaW5kZXhIZWlnaHQFAAAADHVubG9ja0hlaWdodAUAAAAPcHJldkluZGV4SGVpZ2h0AwkAAGcAAAACAAAAAAAAAAAACQABkQAAAAIFAAAADHBheW91dHNBcnJheQUAAAAOSWR4R3Jvc3NBbW91bnQJAAACAAAAAQIAAAATYmFsYW5jZSBlcXVhbHMgemVybwMDCQAAZgAAAAIAAAAAAAAAAAAFAAAACm91dEZlZVBhcnQGCQAAZwAAAAIFAAAACm91dEZlZVBhcnQFAAAABVBBVUxJCQAAAgAAAAEJAAEsAAAAAgkAASwAAAACCQABLAAAAAICAAAAHmludmFsaWQgb3V0RmVlUGFydCBjb25maWcgZm9yIAUAAAAIc3dhcFR5cGUCAAAAEiBzd2FwOiBvdXRGZWVQYXJ0PQkAAaQAAAABBQAAAApvdXRGZWVQYXJ0CQAETAAAAAIJAQAAAAxJbnRlZ2VyRW50cnkAAAACCQEAAAAUdG90YWxMb2NrZWRCeVVzZXJLRVkAAAACBQAAAAhzd2FwVHlwZQUAAAAHYWNjb3VudAkAAGUAAAACBQAAABF0b3RhbExvY2tlZEJ5VXNlcgUAAAAIaW5BbW91bnQJAARMAAAAAgkBAAAADEludGVnZXJFbnRyeQAAAAIJAQAAAA50b3RhbExvY2tlZEtFWQAAAAEFAAAACHN3YXBUeXBlCQAAZQAAAAIFAAAAC3RvdGFsTG9ja2VkBQAAAAhpbkFtb3VudAkABEwAAAACCQEAAAAOU2NyaXB0VHJhbnNmZXIAAAADBQAAAAt1c2VyQWRkcmVzcwUAAAAMb3V0TmV0QW1vdW50CAUAAAATb3V0QW1vdW50R3Jvc3NUdXBsZQAAAAJfMgkABEwAAAACCQEAAAAOU2NyaXB0VHJhbnNmZXIAAAADBQAAABFmZWVNYW5hZ2VyQWRkcmVzcwUAAAAMb3V0RmVlQW1vdW50CAUAAAATb3V0QW1vdW50R3Jvc3NUdXBsZQAAAAJfMgkABEwAAAACCQEAAAALU3RyaW5nRW50cnkAAAACCQEAAAAHc3dhcEtFWQAAAAIFAAAAB2FjY291bnQFAAAACHN3YXBUeElkCQEAAAAOZmluaXNoU3dhcERBVEEAAAAFBQAAAAlkYXRhQXJyYXkFAAAADHByaWNlQnlJbmRleAUAAAAMb3V0TmV0QW1vdW50BQAAAAxvdXRGZWVBbW91bnQFAAAADHVubG9ja0hlaWdodAUAAAADbmlsAAAABgAAAAFpAQAAABNzd2FwV2F2ZXNUb05ldXRyaW5vAAAAAAQAAAADcG10CQEAAAAFdmFsdWUAAAABCQABkQAAAAIIBQAAAAFpAAAACHBheW1lbnRzAAAAAAAAAAAAAwkBAAAACWlzRGVmaW5lZAAAAAEIBQAAAANwbXQAAAAHYXNzZXRJZAkAAAIAAAABAgAAAClPbmx5IFdhdmVzIHRva2VuIGlzIGFsbG93ZWQgZm9yIHN3YXBwaW5nLgkBAAAACmNvbW1vblN3YXAAAAACAgAAAAV3YXZlcwUAAAABaQAAAAFpAQAAABNzd2FwTmV1dHJpbm9Ub1dhdmVzAAAAAAQAAAADcG10CQEAAAAFdmFsdWUAAAABCQABkQAAAAIIBQAAAAFpAAAACHBheW1lbnRzAAAAAAAAAAAAAwkBAAAAAiE9AAAAAggFAAAAA3BtdAAAAAdhc3NldElkBQAAAA9uZXV0cmlub0Fzc2V0SWQJAAACAAAAAQIAAAA6T25seSBhcHByb3ByaWF0ZSBOZXV0cmlubyB0b2tlbnMgYXJlIGFsbG93ZWQgZm9yIHN3YXBwaW5nLgkBAAAACmNvbW1vblN3YXAAAAACAgAAAAhuZXV0cmlubwUAAAABaQAAAAFpAQAAAAh3aXRoZHJhdwAAAAMAAAAHYWNjb3VudAAAAAVpbmRleAAAAAhzd2FwVHhJZAkBAAAADmNvbW1vbldpdGhkcmF3AAAABQUAAAAHYWNjb3VudAUAAAAFaW5kZXgFAAAACHN3YXBUeElkBQAAAAR1bml0BQAAAAFpAAAAAWkBAAAADHdpdGhkcmF3UmFuZAAAAAQAAAAHYWNjb3VudAAAAAVpbmRleAAAAAhzd2FwVHhJZAAAAAZyc2FTaWcJAQAAAA5jb21tb25XaXRoZHJhdwAAAAUFAAAAB2FjY291bnQFAAAABWluZGV4BQAAAAhzd2FwVHhJZAUAAAAGcnNhU2lnBQAAAAFpAAAAAWkBAAAAEXRyYW5zZmVyVG9BdWN0aW9uAAAAAAQAAAAPYXVjdGlvbk5CQW1vdW50CQAAZQAAAAIFAAAADm5ldXRyaW5vU3VwcGx5CQAD8AAAAAIJAQAAABFAZXh0ck5hdGl2ZSgxMDYyKQAAAAEFAAAAD2F1Y3Rpb25Db250cmFjdAUAAAALYm9uZEFzc2V0SWQEAAAAFnN1cnBsdXNXaXRoTGlxdWlkYXRpb24JAABlAAAAAgUAAAAHc3VycGx1cwkAA/AAAAACCQEAAAARQGV4dHJOYXRpdmUoMTA2MikAAAABBQAAABNsaXF1aWRhdGlvbkNvbnRyYWN0BQAAAA9uZXV0cmlub0Fzc2V0SWQDBQAAAAlpc0Jsb2NrZWQJAAACAAAAAQIAAABaY29udHJhY3QgaXMgYmxvY2tlZCBieSBFTUVSR0VOQ1kgU0hVVERPV04gYWN0aW9ucyB1bnRpbGwgcmVhY3RpdmF0aW9uIGJ5IGVtZXJnZW5jeSBvcmFjbGVzAwkAAGYAAAACBQAAAA9hdWN0aW9uTkJBbW91bnQJAABoAAAAAgAAAAAAAAAAAQUAAAAFUEFVTEkJAARMAAAAAgkBAAAADlNjcmlwdFRyYW5zZmVyAAAAAwkBAAAAEUBleHRyTmF0aXZlKDEwNjIpAAAAAQUAAAAPYXVjdGlvbkNvbnRyYWN0BQAAAA9hdWN0aW9uTkJBbW91bnQFAAAAC2JvbmRBc3NldElkBQAAAANuaWwDCQAAZwAAAAIFAAAAFnN1cnBsdXNXaXRoTGlxdWlkYXRpb24JAABoAAAAAgAAAAAAAAAAAQUAAAAFUEFVTEkJAARMAAAAAgkBAAAADlNjcmlwdFRyYW5zZmVyAAAAAwkBAAAAEUBleHRyTmF0aXZlKDEwNjIpAAAAAQUAAAATbGlxdWlkYXRpb25Db250cmFjdAUAAAAWc3VycGx1c1dpdGhMaXF1aWRhdGlvbgUAAAAPbmV1dHJpbm9Bc3NldElkBQAAAANuaWwJAAACAAAAAQkAASwAAAACCQABLAAAAAIJAAEsAAAAAgkAASwAAAACCQABLAAAAAIJAAEsAAAAAgkAASwAAAACAgAAAC9ib25kIHdlcmUgZ2VuZXJhdGVkIG9yIGRvIG5vdCBuZWVkIGl0LiBEZWZpY2l0OgkAAaQAAAABBQAAAA9hdWN0aW9uTkJBbW91bnQCAAAAAXwJAAGkAAAAAQAAAAAAAAAAAAIAAAAKLiBTdXJwbHVzOgkAAaQAAAABBQAAABZzdXJwbHVzV2l0aExpcXVpZGF0aW9uAgAAAAF8CQABpAAAAAEFAAAAB3N1cnBsdXMAAAABaQEAAAAGY2FuY2VsAAAAAQAAAAdhY2NvdW50BAAAAAt1c2VyQWRkcmVzcwkBAAAAEUBleHRyTmF0aXZlKDEwNjIpAAAAAQUAAAAHYWNjb3VudAQAAAAWdXNlcldhdmVzTG9ja2VkQmFsYW5jZQkBAAAAFWdldFdhdmVzTG9ja2VkQmFsYW5jZQAAAAEFAAAAB2FjY291bnQEAAAAGXVzZXJOZXV0cmlub0xvY2tlZEJhbGFuY2UJAQAAABhnZXROZXV0cmlub0xvY2tlZEJhbGFuY2UAAAABBQAAAAdhY2NvdW50AwkBAAAAAiE9AAAAAgkABCUAAAABCAUAAAABaQAAAAZjYWxsZXICAAAAIzNQTEd5UGdBOUVSd2lIZkxra3ZyVlNkc1YyNnFnZHIzYXFuCQAAAgAAAAECAAAAEnBlcm1pc3Npb25zIGRlbmllZAMDCQAAAAAAAAIFAAAAFnVzZXJXYXZlc0xvY2tlZEJhbGFuY2UAAAAAAAAAAAAJAAAAAAAAAgUAAAAZdXNlck5ldXRyaW5vTG9ja2VkQmFsYW5jZQAAAAAAAAAAAAcJAAACAAAAAQIAAAARbm90aGluZyB0byBjYW5jZWwJAARMAAAAAgkBAAAADEludGVnZXJFbnRyeQAAAAIJAQAAABhnZXRXYXZlc0xvY2tlZEJhbGFuY2VLZXkAAAABBQAAAAdhY2NvdW50AAAAAAAAAAAACQAETAAAAAIJAQAAAAxJbnRlZ2VyRW50cnkAAAACCQEAAAAbZ2V0TmV1dHJpbm9Mb2NrZWRCYWxhbmNlS2V5AAAAAQUAAAAHYWNjb3VudAAAAAAAAAAAAAkABEwAAAACCQEAAAAMSW50ZWdlckVudHJ5AAAAAgUAAAAVV2F2ZXNMb2NrZWRCYWxhbmNlS2V5CQAAZQAAAAIFAAAAEndhdmVzTG9ja2VkQmFsYW5jZQUAAAAWdXNlcldhdmVzTG9ja2VkQmFsYW5jZQkABEwAAAACCQEAAAAMSW50ZWdlckVudHJ5AAAAAgUAAAAYTmV1dHJpbm9Mb2NrZWRCYWxhbmNlS2V5CQAAZQAAAAIFAAAAFW5ldXRyaW5vTG9ja2VkQmFsYW5jZQUAAAAZdXNlck5ldXRyaW5vTG9ja2VkQmFsYW5jZQkABEwAAAACCQEAAAAOU2NyaXB0VHJhbnNmZXIAAAADBQAAAAt1c2VyQWRkcmVzcwUAAAAWdXNlcldhdmVzTG9ja2VkQmFsYW5jZQUAAAAEdW5pdAkABEwAAAACCQEAAAAOU2NyaXB0VHJhbnNmZXIAAAADBQAAAAt1c2VyQWRkcmVzcwUAAAAZdXNlck5ldXRyaW5vTG9ja2VkQmFsYW5jZQUAAAAPbmV1dHJpbm9Bc3NldElkBQAAAANuaWwAAAABAAAAAnR4AQAAAAZ2ZXJpZnkAAAAABAAAAAJpZAkAAlgAAAABCAUAAAACdHgAAAACaWQEAAAABWNvdW50CQAAZAAAAAIJAABkAAAAAgkAAGQAAAACAwkAAfQAAAADCAUAAAACdHgAAAAJYm9keUJ5dGVzCQABkQAAAAIIBQAAAAJ0eAAAAAZwcm9vZnMAAAAAAAAAAAAJAAJZAAAAAQkAAZEAAAACBQAAABBwdWJLZXlBZG1pbnNMaXN0AAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAwkAAfQAAAADCAUAAAACdHgAAAAJYm9keUJ5dGVzCQABkQAAAAIIBQAAAAJ0eAAAAAZwcm9vZnMAAAAAAAAAAAEJAAJZAAAAAQkAAZEAAAACBQAAABBwdWJLZXlBZG1pbnNMaXN0AAAAAAAAAAABAAAAAAAAAAABAAAAAAAAAAAAAwkAAfQAAAADCAUAAAACdHgAAAAJYm9keUJ5dGVzCQABkQAAAAIIBQAAAAJ0eAAAAAZwcm9vZnMAAAAAAAAAAAIJAAJZAAAAAQkAAZEAAAACBQAAABBwdWJLZXlBZG1pbnNMaXN0AAAAAAAAAAACAAAAAAAAAAABAAAAAAAAAAAAAwkAAfQAAAADCAUAAAACdHgAAAAJYm9keUJ5dGVzCQABkQAAAAIIBQAAAAJ0eAAAAAZwcm9vZnMAAAAAAAAAAAMJAAJZAAAAAQkAAZEAAAACBQAAABBwdWJLZXlBZG1pbnNMaXN0AAAAAAAAAAADAAAAAAAAAAACAAAAAAAAAAAABAAAAAckbWF0Y2gwBQAAAAJ0eAMDCQAAAQAAAAIFAAAAByRtYXRjaDACAAAAFkxlYXNlQ2FuY2VsVHJhbnNhY3Rpb24GCQAAAQAAAAIFAAAAByRtYXRjaDACAAAAEExlYXNlVHJhbnNhY3Rpb24EAAAACWxlYXNpbmdUeAUAAAAHJG1hdGNoMAkAAfQAAAADCAUAAAAJbGVhc2luZ1R4AAAACWJvZHlCeXRlcwkAAZEAAAACCAUAAAAJbGVhc2luZ1R4AAAABnByb29mcwAAAAAAAAAAAAUAAAAYbm9kZU9yYWNsZVByb3ZpZGVyUHViS2V5AwkAAAEAAAACBQAAAAckbWF0Y2gwAgAAABVTcG9uc29yRmVlVHJhbnNhY3Rpb24EAAAACXNwb25zb3JUeAUAAAAHJG1hdGNoMAMJAQAAABtjaGVja0lzVmFsaWRNaW5TcG9uc29yZWRGZWUAAAABBQAAAAlzcG9uc29yVHgJAABnAAAAAgUAAAAFY291bnQAAAAAAAAAAAMHCQAAZwAAAAIFAAAABWNvdW50AAAAAAAAAAADEbCC3Q==", "chainId": 87, "height": 2675360, "applicationStatus": "succeeded", "spentComplexity": 0 } View: original | compacted Prev: E8WtRULqzDnb95xYZmy31gkPnd7x9tnJDnb1Fjj9tdG1 Next: DXH36JkLXLUENabfWVwz39Tui5aChR58xuyueYXJWWkP Diff:
OldNewDifferences
2121
2222 let pubKeyAdminsList = ["GJdLSaLiv5K7xuejac8mcRcHoyo3dPrESrvktG3a6MAR", "FWVffYr2ALmHMejZm3WqeLz6Sdym3gLFGtJn4KTwyU5x", "3Wh2LaWcb5gg7K2pPcW3Ep6EAuRBzYkAgrdpt43jTDFa", "5WRXFSjwcTbNfKcJs8ZqXmSSWYsSVJUtMvMqZj5hH4Nc"]
2323
24-let SENDTXEXPIRE = 30
25-
26-let LISTSPLITSYMBOL = "_"
27-
28-let LISTDATASYMBOL = "+"
24+let SEP = "__"
2925
3026 let WAVELET = 100000000
3127
3834 let IdxNetAmount = 0
3935
4036 let IdxFeeAmount = 1
37+
38+let IdxGrossAmount = 2
4139
4240 let NeutrinoAssetIdKey = "neutrino_asset_id"
4341
6765
6866 let FeesManagerAddressKey = "fees_manager_address"
6967
70-let RPDBalanceKey = "rpd_balance"
71-
72-func getRPDContractBalanceKey (assetId) = ((RPDBalanceKey + "_") + toBase58String(assetId))
73-
68+let RsaRandPublic58Key = "rand_rsa_public"
7469
7570 let PriceKey = "price"
7671
8479 func getHeightPriceByIndexKey (index) = ((PriceIndexKey + "_") + toString(index))
8580
8681
87-let BalanceLockedkKey = "balance_lock_"
88-
89-let WavesLockedBalanceKey = (BalanceLockedkKey + "waves")
90-
91-let NeutrinoLockedBalanceKey = (BalanceLockedkKey + "neutrino")
92-
93-func getRPDSnapshotContractBalanceKey (count,assetId) = ((((RPDBalanceKey + "_") + toBase58String(assetId)) + "_") + toString(count))
94-
95-
96-func getCancelLeaseTxReserveFeeKey (hash) = (("cancel_lease_tx_reserve_fee" + "_") + hash)
97-
98-
99-func getWavesLockedBalanceKey (owner) = ((WavesLockedBalanceKey + "_") + owner)
100-
101-
102-func getNeutrinoLockedBalanceKey (owner) = ((NeutrinoLockedBalanceKey + "_") + owner)
103-
104-
10582 func getBalanceUnlockBlockKey (owner) = ("balance_unlock_block_" + owner)
10683
10784
108-func getRPDProfitKey (count) = (("rpd_profit" + "_") + toString(count))
85+func minSwapAmountKEY (swapType) = (("min_" + swapType) + "_swap_amount")
86+
87+
88+func totalLockedKEY (swapType) = ("balance_lock_" + swapType)
89+
90+
91+func totalLockedByUserKEY (swapType,owner) = makeString(["balance_lock", swapType, owner], "_")
92+
93+
94+func balanceLockIntervalKEY (swapType) = (("balance_" + swapType) + "_lock_interval")
95+
96+
97+func minBalanceLockIntervalKEY (swapType) = (("balance_" + swapType) + "_lock_interval_minimum")
98+
99+
100+func outFeePartKEY (swapType) = (swapType + "Out_swap_feePart")
101+
102+
103+func minSwapAmountREAD (swapType) = valueOrElse(getInteger(this, minSwapAmountKEY(swapType)), 0)
104+
105+
106+func totalLockedREAD (swapType) = valueOrElse(getInteger(this, totalLockedKEY(swapType)), 0)
107+
108+
109+func totalLockedByUserREAD (swapType,owner) = valueOrElse(getInteger(this, totalLockedByUserKEY(swapType, owner)), 0)
110+
111+
112+func balanceLockIntervalREAD (swapType) = valueOrElse(getInteger(this, balanceLockIntervalKEY(swapType)), 1440)
113+
114+
115+func minBalanceLockIntervalREAD (swapType) = valueOrElse(getInteger(this, minBalanceLockIntervalKEY(swapType)), 60)
116+
117+
118+func feeManagerAddressREAD () = valueOrErrorMessage(addressFromString(valueOrErrorMessage(getString(this, FeesManagerAddressKey), (FeesManagerAddressKey + " is not specified"))), (FeesManagerAddressKey + " invalid address format"))
109119
110120
111121 func convertNeutrinoToWaves (amount,price) = fraction(fraction(amount, PRICELET, price), WAVELET, PAULI)
120130 func convertJsonArrayToList (jsonArray) = split(jsonArray, ",")
121131
122132
133+func minSwapAmountFAIL (swapType,minSwapAmount) = throw(((("The specified amount in " + swapType) + " swap is less than the required minimum of ") + toString(minSwapAmount)))
134+
135+
136+func emergencyShutdownFAIL () = throw("contract is blocked by EMERGENCY SHUTDOWN actions untill reactivation by emergency oracles")
137+
138+
139+func priceIndexFAIL (index,priceIndex,indexHeight,unlockHeight,prevIndexHeight) = throw(((((((((("invalid price history index: index=" + toString(index)) + " priceIndex=") + toString(priceIndex)) + " indexHeight=") + toString(indexHeight)) + " unlockHeight=") + toString(unlockHeight)) + " prevIndexHeight=") + toString(prevIndexHeight)))
140+
141+
123142 let liquidationContract = getStringByKey(LiquidationContractKey)
124143
125-let neutrinoAssetIdString = getStringByKey(NeutrinoAssetIdKey)
126-
127-let neutrinoAssetId = fromBase58String(neutrinoAssetIdString)
144+let neutrinoAssetId = fromBase58String(getStringByKey(NeutrinoAssetIdKey))
128145
129146 let auctionContract = getStringByKey(AuctionContractKey)
130147
138155
139156 let nodeOracleProviderPubKey = fromBase58String(getStringByKey(NodeOracleProviderPubKeyKey))
140157
141-let balanceWavesLockInterval = getNumberByKey(BalanceWavesLockIntervalKey)
142-
143-let balanceNeutrinoLockInterval = getNumberByKey(BalanceNeutrinoLockIntervalKey)
144-
145-let minWavesSwapAmount = getNumberByKey(MinWavesSwapAmountKey)
146-
147-let minNeutrinoSwapAmount = getNumberByKey(MinNeutrinoSwapAmountKey)
148-
149158 let bondAssetId = fromBase58String("6nSpVyNH7yM69eg446wrQR94ipbbcmZMU1ENPwanC97g")
150159
151160 let deprecatedBondAssetId = fromBase58String("975akZBfnMj513U7MZaHKzQrmsEx5aE3wdWKTrHBhbjF")
161+
162+let rsaPub = fromBase64String(valueOrErrorMessage(getString(this, RsaRandPublic58Key), "RSA public key has not been specified"))
152163
153164 let neutrinoContract = this
154165
155166 let currentPrice = getNumberByAddressAndKey(controlContract, PriceKey)
156167
157-let neutrinoLockedBalance = getNumberByKey(NeutrinoLockedBalanceKey)
168+let neutrinoLockedBalance = totalLockedREAD("neutrino")
158169
159-let wavesLockedBalance = getNumberByKey(WavesLockedBalanceKey)
170+let wavesLockedBalance = totalLockedREAD("waves")
160171
161172 let reserve = (wavesBalance(neutrinoContract).regular - wavesLockedBalance)
162173
181192 }
182193
183194
184-func getRPDContractBalance (assetId) = getNumberByAddressAndKey(rpdContract, getRPDContractBalanceKey(assetId))
185-
186-
187195 func getPriceHistory (block) = getNumberByAddressAndKey(controlContract, getPriceHistoryKey(block))
188196
189197
190198 func getHeightPriceByIndex (index) = getNumberByAddressAndKey(controlContract, getHeightPriceByIndexKey(index))
191199
192200
193-func getCancelLeaseTxReserveFee (hash) = getNumberByKey(getCancelLeaseTxReserveFeeKey(hash))
201+let sIdxSwapType = 1
202+
203+let sIdxStatus = 2
204+
205+let sIdxInAmount = 3
206+
207+let sIdxPrice = 4
208+
209+let sIdxOutNetAmount = 5
210+
211+let sIdxOutFeeAmount = 6
212+
213+let sIdxStartHeight = 7
214+
215+let sIdxStartTimestamp = 8
216+
217+let sIdxEndHeight = 9
218+
219+let sIdxEndTimestamp = 10
220+
221+let sIdxSelfUnlockHeight = 11
222+
223+let sIdxRandUnlockHeight = 12
224+
225+func swapKEY (userAddress,txId) = makeString(["%s%s", userAddress, txId], SEP)
226+
227+
228+func strSwapDATA (swapType,status,inAmount,price,outNetAmount,outFeeAmount,startHeight,startTimestamp,endHeight,endTimestamp,selfUnlockHeight,randUnlockHeight) = makeString(["%s%s%d%d%d%d%d%d%d%d%d%d", swapType, status, inAmount, price, outNetAmount, outFeeAmount, startHeight, startTimestamp, endHeight, endTimestamp, selfUnlockHeight, randUnlockHeight], SEP)
229+
230+
231+func pendingSwapDATA (swapType,inAssetAmount,selfUnlockHeight) = strSwapDATA(swapType, "PENDING", toString(inAssetAmount), "0", "0", "0", toString(height), toString(lastBlock.timestamp), "0", "0", toString(selfUnlockHeight), "0")
232+
233+
234+func finishSwapDATA (dataArray,price,outNetAmount,outFeeAmount,randUnlockHeight) = strSwapDATA(dataArray[sIdxSwapType], "FINISHED", dataArray[sIdxInAmount], toString(price), toString(outNetAmount), toString(outFeeAmount), dataArray[sIdxStartHeight], dataArray[sIdxStartTimestamp], toString(height), toString(lastBlock.timestamp), dataArray[sIdxSelfUnlockHeight], toString(randUnlockHeight))
235+
236+
237+func swapDataFailOrREAD (userAddress,swapTxId) = {
238+ let swapKey = swapKEY(userAddress, swapTxId)
239+ split(valueOrErrorMessage(getString(this, swapKey), ("no swap data for " + swapKey)), SEP)
240+ }
241+
242+
243+func applyFees (amountGross,feePart) = {
244+ let feeAmount = fraction(amountGross, feePart, PAULI)
245+[(amountGross - feeAmount), feeAmount, amountGross]
246+ }
247+
248+
249+func randUnlockHeightOrFail (txId,rsaSig,swapType,startHeight) = {
250+ let isRsaValid = rsaVerify_16Kb(SHA256, toBytes(txId), rsaSig, rsaPub)
251+ if (!(isRsaValid))
252+ then throw("invalid RSA signature")
253+ else {
254+ let maxBalanceLockInterval = balanceLockIntervalREAD(swapType)
255+ let minBalanceLockInterval = minBalanceLockIntervalREAD(swapType)
256+ let rand = (toInt(sha256_16Kb(rsaSig)) % (maxBalanceLockInterval - minBalanceLockInterval))
257+ let randLockInterval = (minBalanceLockInterval + (if ((0 > rand))
258+ then -(rand)
259+ else rand))
260+ (startHeight + randLockInterval)
261+ }
262+ }
263+
264+
265+let BalanceLockedkKey = "balance_lock_"
266+
267+let WavesLockedBalanceKey = (BalanceLockedkKey + "waves")
268+
269+let NeutrinoLockedBalanceKey = (BalanceLockedkKey + "neutrino")
270+
271+func getWavesLockedBalanceKey (owner) = ((WavesLockedBalanceKey + "_") + owner)
272+
273+
274+func getNeutrinoLockedBalanceKey (owner) = ((NeutrinoLockedBalanceKey + "_") + owner)
194275
195276
196277 func getWavesLockedBalance (owner) = getNumberByKey(getWavesLockedBalanceKey(owner))
199280 func getNeutrinoLockedBalance (owner) = getNumberByKey(getNeutrinoLockedBalanceKey(owner))
200281
201282
202-func getUnlockBalanceBlock (owner) = getNumberByKey(getBalanceUnlockBlockKey(owner))
283+func commonSwap (swapType,i) = {
284+ let pmt = value(i.payments[0])
285+ let account = toString(i.caller)
286+ let txId58 = toBase58String(i.transactionId)
287+ let minSwapAmount = minSwapAmountREAD(swapType)
288+ let totalLocked = totalLockedREAD(swapType)
289+ let totalLockedByUser = totalLockedByUserREAD(swapType, account)
290+ let balanceLockMaxInterval = balanceLockIntervalREAD(swapType)
291+ let selfUnlockHeight = (height + balanceLockMaxInterval)
292+ if ((minSwapAmount > pmt.amount))
293+ then minSwapAmountFAIL(swapType, minSwapAmount)
294+ else if (isBlocked)
295+ then emergencyShutdownFAIL()
296+ else [IntegerEntry(totalLockedByUserKEY(swapType, account), (totalLockedByUser + pmt.amount)), IntegerEntry(getBalanceUnlockBlockKey(account), (height + balanceLockMaxInterval)), IntegerEntry(totalLockedKEY(swapType), (totalLocked + pmt.amount)), StringEntry(swapKEY(account, txId58), pendingSwapDATA(swapType, pmt.amount, selfUnlockHeight))]
297+ }
203298
204299
205-func getRPDProfit (count) = getNumberByKey(getRPDProfitKey(count))
206-
207-
208-func applyFees (amountGross,feePart) = {
209- let feeAmount = fraction(amountGross, feePart, PAULI)
210-[(amountGross - feeAmount), feeAmount]
300+func commonWithdraw (account,index,swapTxId,rsaSigOrUnit,i) = {
301+ let userAddress = addressFromStringValue(account)
302+ let feeManagerAddress = feeManagerAddressREAD()
303+ let dataArray = swapDataFailOrREAD(account, swapTxId)
304+ let selfUnlockHeight = parseIntValue(dataArray[sIdxSelfUnlockHeight])
305+ let swapType = dataArray[sIdxSwapType]
306+ let inAmount = parseIntValue(dataArray[sIdxInAmount])
307+ let swapStatus = dataArray[sIdxStatus]
308+ let startHeight = parseIntValue(dataArray[sIdxStartHeight])
309+ let outFeePart = valueOrElse(getInteger(this, outFeePartKEY(swapType)), DEFAULTSWAPFEE)
310+ let totalLocked = totalLockedREAD(swapType)
311+ let totalLockedByUser = totalLockedByUserREAD(swapType, account)
312+ let unlockHeight = match rsaSigOrUnit {
313+ case rsaSig: ByteVector =>
314+ randUnlockHeightOrFail(swapTxId, rsaSig, swapType, startHeight)
315+ case _: Unit =>
316+ selfUnlockHeight
317+ case _ =>
318+ throw("Match error")
319+ }
320+ let indexHeight = getHeightPriceByIndex(index)
321+ let prevIndexHeight = getHeightPriceByIndex((index - 1))
322+ let priceByIndex = getPriceHistory(indexHeight)
323+ let outAmountGrossTuple = if ((swapType == "waves"))
324+ then $Tuple2(convertWavesToNeutrino(inAmount, priceByIndex), neutrinoAssetId)
325+ else if ((swapType == "neutrino"))
326+ then $Tuple2(convertNeutrinoToWaves(inAmount, priceByIndex), unit)
327+ else throw(("Unsupported swap type " + swapType))
328+ let payoutsArray = applyFees(outAmountGrossTuple._1, outFeePart)
329+ let outNetAmount = payoutsArray[IdxNetAmount]
330+ let outFeeAmount = payoutsArray[IdxFeeAmount]
331+ if (isBlocked)
332+ then emergencyShutdownFAIL()
333+ else if ((swapStatus != "PENDING"))
334+ then throw("swap has been already processed")
335+ else if ((unlockHeight > height))
336+ then throw((("please wait for: " + toString(unlockHeight)) + " block height to withdraw funds"))
337+ else if (if (if ((index > priceIndex))
338+ then true
339+ else (unlockHeight > indexHeight))
340+ then true
341+ else if ((prevIndexHeight != 0))
342+ then (prevIndexHeight >= unlockHeight)
343+ else false)
344+ then priceIndexFAIL(index, priceIndex, indexHeight, unlockHeight, prevIndexHeight)
345+ else if ((0 >= payoutsArray[IdxGrossAmount]))
346+ then throw("balance equals zero")
347+ else if (if ((0 > outFeePart))
348+ then true
349+ else (outFeePart >= PAULI))
350+ then throw(((("invalid outFeePart config for " + swapType) + " swap: outFeePart=") + toString(outFeePart)))
351+ else [IntegerEntry(totalLockedByUserKEY(swapType, account), (totalLockedByUser - inAmount)), IntegerEntry(totalLockedKEY(swapType), (totalLocked - inAmount)), ScriptTransfer(userAddress, outNetAmount, outAmountGrossTuple._2), ScriptTransfer(feeManagerAddress, outFeeAmount, outAmountGrossTuple._2), StringEntry(swapKEY(account, swapTxId), finishSwapDATA(dataArray, priceByIndex, outNetAmount, outFeeAmount, unlockHeight))]
211352 }
212353
213354
214355 @Callable(i)
215356 func swapWavesToNeutrino () = {
216357 let pmt = value(i.payments[0])
217- let account = toString(i.caller)
218- if ((minWavesSwapAmount > pmt.amount))
219- then throw((("The specified Waves amount is less than the required minimum of " + toString(minWavesSwapAmount)) + " wavelets."))
220- else if (isDefined(pmt.assetId))
221- then throw("Only Waves token is allowed for swapping.")
222- else if (isBlocked)
223- then throw("contract is blocked by EMERGENCY SHUTDOWN actions untill reactivation by emergency oracles")
224- else if ((getUnlockBalanceBlock(account) > height))
225- then throw((("await " + toString((getUnlockBalanceBlock(account) - height))) + " blocks"))
226- else if (if ((getNeutrinoLockedBalance(account) != 0))
227- then true
228- else (getWavesLockedBalance(account) != 0))
229- then throw("please withdraw locked funds first")
230- else [IntegerEntry(getWavesLockedBalanceKey(account), pmt.amount), IntegerEntry(getBalanceUnlockBlockKey(account), (height + balanceWavesLockInterval)), IntegerEntry(WavesLockedBalanceKey, (wavesLockedBalance + pmt.amount))]
358+ if (isDefined(pmt.assetId))
359+ then throw("Only Waves token is allowed for swapping.")
360+ else commonSwap("waves", i)
231361 }
232362
233363
235365 @Callable(i)
236366 func swapNeutrinoToWaves () = {
237367 let pmt = value(i.payments[0])
238- let account = toString(i.caller)
239- if ((minNeutrinoSwapAmount > pmt.amount))
240- then throw((("The specified Neutrino amount is less than the required minimum of " + toString(minNeutrinoSwapAmount)) + " Neutrino cents."))
241- else if (isBlocked)
242- then throw("The contract is blocked by EMERGENCY SHUTDOWN. Please wait for reactivation by emergency oracles.")
243- else if ((pmt.assetId != neutrinoAssetId))
244- then throw("Only appropriate Neutrino tokens are allowed for swapping.")
245- else if ((getUnlockBalanceBlock(account) > height))
246- then throw((("await " + toString((getUnlockBalanceBlock(account) - height))) + " blocks"))
247- else if (if ((getNeutrinoLockedBalance(account) != 0))
248- then true
249- else (getWavesLockedBalance(account) != 0))
250- then throw("please withdraw locked funds first")
251- else [IntegerEntry(getNeutrinoLockedBalanceKey(account), pmt.amount), IntegerEntry(getBalanceUnlockBlockKey(account), (height + balanceNeutrinoLockInterval)), IntegerEntry(NeutrinoLockedBalanceKey, (neutrinoLockedBalance + pmt.amount))]
368+ if ((pmt.assetId != neutrinoAssetId))
369+ then throw("Only appropriate Neutrino tokens are allowed for swapping.")
370+ else commonSwap("neutrino", i)
252371 }
253372
254373
255374
256375 @Callable(i)
257-func withdraw (account,index) = {
258- let userAddress = addressFromStringValue(account)
259- let feeManagerAddress = valueOrErrorMessage(addressFromString(valueOrErrorMessage(getString(this, FeesManagerAddressKey), (FeesManagerAddressKey + " is not specified"))), (FeesManagerAddressKey + " invalid address format"))
260- let unlockHeight = getUnlockBalanceBlock(account)
261- let userWavesLockedBalance = getWavesLockedBalance(account)
262- let userNeutrinoLockedBalance = getNeutrinoLockedBalance(account)
263- let indexHeight = getHeightPriceByIndex(index)
264- let prevIndexHeight = getHeightPriceByIndex((index - 1))
265- let priceByIndex = getPriceHistory(indexHeight)
266- let neutrinoAmountGross = convertWavesToNeutrino(userWavesLockedBalance, priceByIndex)
267- let wavesAmountGross = convertNeutrinoToWaves(userNeutrinoLockedBalance, priceByIndex)
268- let neutrinoOutFeePart = valueOrElse(getInteger(this, NeutrinoOutFeePartKey), DEFAULTSWAPFEE)
269- let wavesOutFeePart = valueOrElse(getInteger(this, WavesOutFeePartKey), DEFAULTSWAPFEE)
270- let neutrinoPayoutsArray = applyFees(neutrinoAmountGross, neutrinoOutFeePart)
271- let wavesPayoutsArray = applyFees(wavesAmountGross, wavesOutFeePart)
272- if (isBlocked)
273- then throw("contract is blocked by EMERGENCY SHUTDOWN actions untill reactivation by emergency oracles")
274- else if ((unlockHeight > height))
275- then throw((("please wait for: " + toString(unlockHeight)) + " block height to withdraw WAVES funds"))
276- else if (if (if ((index > priceIndex))
277- then true
278- else (unlockHeight > indexHeight))
279- then true
280- else if ((prevIndexHeight != 0))
281- then (prevIndexHeight >= unlockHeight)
282- else false)
283- then throw(((((((((("invalid price history index: index=" + toString(index)) + " priceIndex=") + toString(priceIndex)) + " indexHeight=") + toString(indexHeight)) + " unlockHeight=") + toString(unlockHeight)) + " prevIndexHeight=") + toString(prevIndexHeight)))
284- else if (if ((0 >= neutrinoAmountGross))
285- then (0 >= wavesAmountGross)
286- else false)
287- then throw("balance equals zero")
288- else if (if (if (if ((0 > neutrinoOutFeePart))
289- then true
290- else (neutrinoOutFeePart >= PAULI))
291- then true
292- else (0 > wavesOutFeePart))
293- then true
294- else (wavesOutFeePart >= PAULI))
295- then throw(((("invalid fees data: neutrinoOutFeePart=" + toString(neutrinoOutFeePart)) + " wavesOutFeePart=") + toString(wavesOutFeePart)))
296- else [IntegerEntry(getWavesLockedBalanceKey(account), 0), IntegerEntry(getNeutrinoLockedBalanceKey(account), 0), IntegerEntry(WavesLockedBalanceKey, (wavesLockedBalance - userWavesLockedBalance)), IntegerEntry(NeutrinoLockedBalanceKey, (neutrinoLockedBalance - userNeutrinoLockedBalance)), ScriptTransfer(userAddress, wavesPayoutsArray[IdxNetAmount], unit), ScriptTransfer(feeManagerAddress, wavesPayoutsArray[IdxFeeAmount], unit), ScriptTransfer(userAddress, neutrinoPayoutsArray[IdxNetAmount], neutrinoAssetId), ScriptTransfer(feeManagerAddress, neutrinoPayoutsArray[IdxFeeAmount], neutrinoAssetId)]
297- }
376+func withdraw (account,index,swapTxId) = commonWithdraw(account, index, swapTxId, unit, i)
377+
378+
379+
380+@Callable(i)
381+func withdrawRand (account,index,swapTxId,rsaSig) = commonWithdraw(account, index, swapTxId, rsaSig, i)
298382
299383
300384
309393 else if ((surplusWithLiquidation >= (1 * PAULI)))
310394 then [ScriptTransfer(addressFromStringValue(liquidationContract), surplusWithLiquidation, neutrinoAssetId)]
311395 else throw(((((((("bond were generated or do not need it. Deficit:" + toString(auctionNBAmount)) + "|") + toString(0)) + ". Surplus:") + toString(surplusWithLiquidation)) + "|") + toString(surplus)))
396+ }
397+
398+
399+
400+@Callable(i)
401+func cancel (account) = {
402+ let userAddress = addressFromStringValue(account)
403+ let userWavesLockedBalance = getWavesLockedBalance(account)
404+ let userNeutrinoLockedBalance = getNeutrinoLockedBalance(account)
405+ if ((toString(i.caller) != "3PLGyPgA9ERwiHfLkkvrVSdsV26qgdr3aqn"))
406+ then throw("permissions denied")
407+ else if (if ((userWavesLockedBalance == 0))
408+ then (userNeutrinoLockedBalance == 0)
409+ else false)
410+ then throw("nothing to cancel")
411+ else [IntegerEntry(getWavesLockedBalanceKey(account), 0), IntegerEntry(getNeutrinoLockedBalanceKey(account), 0), IntegerEntry(WavesLockedBalanceKey, (wavesLockedBalance - userWavesLockedBalance)), IntegerEntry(NeutrinoLockedBalanceKey, (neutrinoLockedBalance - userNeutrinoLockedBalance)), ScriptTransfer(userAddress, userWavesLockedBalance, unit), ScriptTransfer(userAddress, userNeutrinoLockedBalance, neutrinoAssetId)]
312412 }
313413
314414
Full:
OldNewDifferences
11 {-# STDLIB_VERSION 4 #-}
22 {-# SCRIPT_TYPE ACCOUNT #-}
33 {-# CONTENT_TYPE DAPP #-}
44 func getNumberByKey (key) = valueOrElse(getInteger(this, key), 0)
55
66
77 func getStringByKey (key) = valueOrElse(getString(this, key), "")
88
99
1010 func getBoolByKey (key) = valueOrElse(getBoolean(this, key), false)
1111
1212
1313 func getNumberByAddressAndKey (address,key) = valueOrElse(getInteger(addressFromStringValue(address), key), 0)
1414
1515
1616 func getStringByAddressAndKey (address,key) = valueOrElse(getString(addressFromStringValue(address), key), "")
1717
1818
1919 func getBoolByAddressAndKey (address,key) = valueOrElse(getBoolean(addressFromStringValue(address), key), false)
2020
2121
2222 let pubKeyAdminsList = ["GJdLSaLiv5K7xuejac8mcRcHoyo3dPrESrvktG3a6MAR", "FWVffYr2ALmHMejZm3WqeLz6Sdym3gLFGtJn4KTwyU5x", "3Wh2LaWcb5gg7K2pPcW3Ep6EAuRBzYkAgrdpt43jTDFa", "5WRXFSjwcTbNfKcJs8ZqXmSSWYsSVJUtMvMqZj5hH4Nc"]
2323
24-let SENDTXEXPIRE = 30
25-
26-let LISTSPLITSYMBOL = "_"
27-
28-let LISTDATASYMBOL = "+"
24+let SEP = "__"
2925
3026 let WAVELET = 100000000
3127
3228 let PAULI = 1000000
3329
3430 let PRICELET = 1000000
3531
3632 let DEFAULTSWAPFEE = 20000
3733
3834 let IdxNetAmount = 0
3935
4036 let IdxFeeAmount = 1
37+
38+let IdxGrossAmount = 2
4139
4240 let NeutrinoAssetIdKey = "neutrino_asset_id"
4341
4442 let BondAssetIdKey = "bond_asset_id"
4543
4644 let AuctionContractKey = "auction_contract"
4745
4846 let LiquidationContractKey = "liquidation_contract"
4947
5048 let RPDContractKey = "rpd_contract"
5149
5250 let ContolContractKey = "control_contract"
5351
5452 let BalanceWavesLockIntervalKey = "balance_waves_lock_interval"
5553
5654 let BalanceNeutrinoLockIntervalKey = "balance_neutrino_lock_interval"
5755
5856 let MinWavesSwapAmountKey = "min_waves_swap_amount"
5957
6058 let MinNeutrinoSwapAmountKey = "min_neutrino_swap_amount"
6159
6260 let NodeOracleProviderPubKeyKey = "node_oracle_provider"
6361
6462 let NeutrinoOutFeePartKey = "neutrinoOut_swap_feePart"
6563
6664 let WavesOutFeePartKey = "wavesOut_swap_feePart"
6765
6866 let FeesManagerAddressKey = "fees_manager_address"
6967
70-let RPDBalanceKey = "rpd_balance"
71-
72-func getRPDContractBalanceKey (assetId) = ((RPDBalanceKey + "_") + toBase58String(assetId))
73-
68+let RsaRandPublic58Key = "rand_rsa_public"
7469
7570 let PriceKey = "price"
7671
7772 let PriceIndexKey = "price_index"
7873
7974 let IsBlockedKey = "is_blocked"
8075
8176 func getPriceHistoryKey (block) = ((PriceKey + "_") + toString(block))
8277
8378
8479 func getHeightPriceByIndexKey (index) = ((PriceIndexKey + "_") + toString(index))
8580
8681
87-let BalanceLockedkKey = "balance_lock_"
88-
89-let WavesLockedBalanceKey = (BalanceLockedkKey + "waves")
90-
91-let NeutrinoLockedBalanceKey = (BalanceLockedkKey + "neutrino")
92-
93-func getRPDSnapshotContractBalanceKey (count,assetId) = ((((RPDBalanceKey + "_") + toBase58String(assetId)) + "_") + toString(count))
94-
95-
96-func getCancelLeaseTxReserveFeeKey (hash) = (("cancel_lease_tx_reserve_fee" + "_") + hash)
97-
98-
99-func getWavesLockedBalanceKey (owner) = ((WavesLockedBalanceKey + "_") + owner)
100-
101-
102-func getNeutrinoLockedBalanceKey (owner) = ((NeutrinoLockedBalanceKey + "_") + owner)
103-
104-
10582 func getBalanceUnlockBlockKey (owner) = ("balance_unlock_block_" + owner)
10683
10784
108-func getRPDProfitKey (count) = (("rpd_profit" + "_") + toString(count))
85+func minSwapAmountKEY (swapType) = (("min_" + swapType) + "_swap_amount")
86+
87+
88+func totalLockedKEY (swapType) = ("balance_lock_" + swapType)
89+
90+
91+func totalLockedByUserKEY (swapType,owner) = makeString(["balance_lock", swapType, owner], "_")
92+
93+
94+func balanceLockIntervalKEY (swapType) = (("balance_" + swapType) + "_lock_interval")
95+
96+
97+func minBalanceLockIntervalKEY (swapType) = (("balance_" + swapType) + "_lock_interval_minimum")
98+
99+
100+func outFeePartKEY (swapType) = (swapType + "Out_swap_feePart")
101+
102+
103+func minSwapAmountREAD (swapType) = valueOrElse(getInteger(this, minSwapAmountKEY(swapType)), 0)
104+
105+
106+func totalLockedREAD (swapType) = valueOrElse(getInteger(this, totalLockedKEY(swapType)), 0)
107+
108+
109+func totalLockedByUserREAD (swapType,owner) = valueOrElse(getInteger(this, totalLockedByUserKEY(swapType, owner)), 0)
110+
111+
112+func balanceLockIntervalREAD (swapType) = valueOrElse(getInteger(this, balanceLockIntervalKEY(swapType)), 1440)
113+
114+
115+func minBalanceLockIntervalREAD (swapType) = valueOrElse(getInteger(this, minBalanceLockIntervalKEY(swapType)), 60)
116+
117+
118+func feeManagerAddressREAD () = valueOrErrorMessage(addressFromString(valueOrErrorMessage(getString(this, FeesManagerAddressKey), (FeesManagerAddressKey + " is not specified"))), (FeesManagerAddressKey + " invalid address format"))
109119
110120
111121 func convertNeutrinoToWaves (amount,price) = fraction(fraction(amount, PRICELET, price), WAVELET, PAULI)
112122
113123
114124 func convertWavesToNeutrino (amount,price) = fraction(fraction(amount, price, PRICELET), PAULI, WAVELET)
115125
116126
117127 func convertWavesToBond (amount,price) = convertWavesToNeutrino(amount, price)
118128
119129
120130 func convertJsonArrayToList (jsonArray) = split(jsonArray, ",")
121131
122132
133+func minSwapAmountFAIL (swapType,minSwapAmount) = throw(((("The specified amount in " + swapType) + " swap is less than the required minimum of ") + toString(minSwapAmount)))
134+
135+
136+func emergencyShutdownFAIL () = throw("contract is blocked by EMERGENCY SHUTDOWN actions untill reactivation by emergency oracles")
137+
138+
139+func priceIndexFAIL (index,priceIndex,indexHeight,unlockHeight,prevIndexHeight) = throw(((((((((("invalid price history index: index=" + toString(index)) + " priceIndex=") + toString(priceIndex)) + " indexHeight=") + toString(indexHeight)) + " unlockHeight=") + toString(unlockHeight)) + " prevIndexHeight=") + toString(prevIndexHeight)))
140+
141+
123142 let liquidationContract = getStringByKey(LiquidationContractKey)
124143
125-let neutrinoAssetIdString = getStringByKey(NeutrinoAssetIdKey)
126-
127-let neutrinoAssetId = fromBase58String(neutrinoAssetIdString)
144+let neutrinoAssetId = fromBase58String(getStringByKey(NeutrinoAssetIdKey))
128145
129146 let auctionContract = getStringByKey(AuctionContractKey)
130147
131148 let rpdContract = getStringByKey(RPDContractKey)
132149
133150 let controlContract = getStringByKey(ContolContractKey)
134151
135152 let priceIndex = getNumberByAddressAndKey(controlContract, PriceIndexKey)
136153
137154 let isBlocked = getBoolByAddressAndKey(controlContract, IsBlockedKey)
138155
139156 let nodeOracleProviderPubKey = fromBase58String(getStringByKey(NodeOracleProviderPubKeyKey))
140157
141-let balanceWavesLockInterval = getNumberByKey(BalanceWavesLockIntervalKey)
142-
143-let balanceNeutrinoLockInterval = getNumberByKey(BalanceNeutrinoLockIntervalKey)
144-
145-let minWavesSwapAmount = getNumberByKey(MinWavesSwapAmountKey)
146-
147-let minNeutrinoSwapAmount = getNumberByKey(MinNeutrinoSwapAmountKey)
148-
149158 let bondAssetId = fromBase58String("6nSpVyNH7yM69eg446wrQR94ipbbcmZMU1ENPwanC97g")
150159
151160 let deprecatedBondAssetId = fromBase58String("975akZBfnMj513U7MZaHKzQrmsEx5aE3wdWKTrHBhbjF")
161+
162+let rsaPub = fromBase64String(valueOrErrorMessage(getString(this, RsaRandPublic58Key), "RSA public key has not been specified"))
152163
153164 let neutrinoContract = this
154165
155166 let currentPrice = getNumberByAddressAndKey(controlContract, PriceKey)
156167
157-let neutrinoLockedBalance = getNumberByKey(NeutrinoLockedBalanceKey)
168+let neutrinoLockedBalance = totalLockedREAD("neutrino")
158169
159-let wavesLockedBalance = getNumberByKey(WavesLockedBalanceKey)
170+let wavesLockedBalance = totalLockedREAD("waves")
160171
161172 let reserve = (wavesBalance(neutrinoContract).regular - wavesLockedBalance)
162173
163174 let neutrinoSupply = (((neutrinoLockedBalance + value(assetInfo(neutrinoAssetId)).quantity) - assetBalance(neutrinoContract, neutrinoAssetId)) - assetBalance(addressFromStringValue(liquidationContract), neutrinoAssetId))
164175
165176 let surplus = (convertWavesToNeutrino(reserve, currentPrice) - neutrinoSupply)
166177
167178 let deficit = (neutrinoSupply - convertWavesToNeutrino(reserve, currentPrice))
168179
169180 func checkIsValidMinSponsoredFee (tx) = {
170181 let MINTRANSFERFEE = 100000
171182 let SponsoredFeeUpperBound = 1000
172183 let realNeutrinoFee = convertWavesToNeutrino(MINTRANSFERFEE, currentPrice)
173184 let minNeutrinoFee = (realNeutrinoFee * 2)
174185 let maxNeutrinoFee = fraction(realNeutrinoFee, SponsoredFeeUpperBound, 100)
175186 let inputFee = value(tx.minSponsoredAssetFee)
176187 if (if ((inputFee >= minNeutrinoFee))
177188 then (maxNeutrinoFee >= inputFee)
178189 else false)
179190 then (tx.assetId == neutrinoAssetId)
180191 else false
181192 }
182193
183194
184-func getRPDContractBalance (assetId) = getNumberByAddressAndKey(rpdContract, getRPDContractBalanceKey(assetId))
185-
186-
187195 func getPriceHistory (block) = getNumberByAddressAndKey(controlContract, getPriceHistoryKey(block))
188196
189197
190198 func getHeightPriceByIndex (index) = getNumberByAddressAndKey(controlContract, getHeightPriceByIndexKey(index))
191199
192200
193-func getCancelLeaseTxReserveFee (hash) = getNumberByKey(getCancelLeaseTxReserveFeeKey(hash))
201+let sIdxSwapType = 1
202+
203+let sIdxStatus = 2
204+
205+let sIdxInAmount = 3
206+
207+let sIdxPrice = 4
208+
209+let sIdxOutNetAmount = 5
210+
211+let sIdxOutFeeAmount = 6
212+
213+let sIdxStartHeight = 7
214+
215+let sIdxStartTimestamp = 8
216+
217+let sIdxEndHeight = 9
218+
219+let sIdxEndTimestamp = 10
220+
221+let sIdxSelfUnlockHeight = 11
222+
223+let sIdxRandUnlockHeight = 12
224+
225+func swapKEY (userAddress,txId) = makeString(["%s%s", userAddress, txId], SEP)
226+
227+
228+func strSwapDATA (swapType,status,inAmount,price,outNetAmount,outFeeAmount,startHeight,startTimestamp,endHeight,endTimestamp,selfUnlockHeight,randUnlockHeight) = makeString(["%s%s%d%d%d%d%d%d%d%d%d%d", swapType, status, inAmount, price, outNetAmount, outFeeAmount, startHeight, startTimestamp, endHeight, endTimestamp, selfUnlockHeight, randUnlockHeight], SEP)
229+
230+
231+func pendingSwapDATA (swapType,inAssetAmount,selfUnlockHeight) = strSwapDATA(swapType, "PENDING", toString(inAssetAmount), "0", "0", "0", toString(height), toString(lastBlock.timestamp), "0", "0", toString(selfUnlockHeight), "0")
232+
233+
234+func finishSwapDATA (dataArray,price,outNetAmount,outFeeAmount,randUnlockHeight) = strSwapDATA(dataArray[sIdxSwapType], "FINISHED", dataArray[sIdxInAmount], toString(price), toString(outNetAmount), toString(outFeeAmount), dataArray[sIdxStartHeight], dataArray[sIdxStartTimestamp], toString(height), toString(lastBlock.timestamp), dataArray[sIdxSelfUnlockHeight], toString(randUnlockHeight))
235+
236+
237+func swapDataFailOrREAD (userAddress,swapTxId) = {
238+ let swapKey = swapKEY(userAddress, swapTxId)
239+ split(valueOrErrorMessage(getString(this, swapKey), ("no swap data for " + swapKey)), SEP)
240+ }
241+
242+
243+func applyFees (amountGross,feePart) = {
244+ let feeAmount = fraction(amountGross, feePart, PAULI)
245+[(amountGross - feeAmount), feeAmount, amountGross]
246+ }
247+
248+
249+func randUnlockHeightOrFail (txId,rsaSig,swapType,startHeight) = {
250+ let isRsaValid = rsaVerify_16Kb(SHA256, toBytes(txId), rsaSig, rsaPub)
251+ if (!(isRsaValid))
252+ then throw("invalid RSA signature")
253+ else {
254+ let maxBalanceLockInterval = balanceLockIntervalREAD(swapType)
255+ let minBalanceLockInterval = minBalanceLockIntervalREAD(swapType)
256+ let rand = (toInt(sha256_16Kb(rsaSig)) % (maxBalanceLockInterval - minBalanceLockInterval))
257+ let randLockInterval = (minBalanceLockInterval + (if ((0 > rand))
258+ then -(rand)
259+ else rand))
260+ (startHeight + randLockInterval)
261+ }
262+ }
263+
264+
265+let BalanceLockedkKey = "balance_lock_"
266+
267+let WavesLockedBalanceKey = (BalanceLockedkKey + "waves")
268+
269+let NeutrinoLockedBalanceKey = (BalanceLockedkKey + "neutrino")
270+
271+func getWavesLockedBalanceKey (owner) = ((WavesLockedBalanceKey + "_") + owner)
272+
273+
274+func getNeutrinoLockedBalanceKey (owner) = ((NeutrinoLockedBalanceKey + "_") + owner)
194275
195276
196277 func getWavesLockedBalance (owner) = getNumberByKey(getWavesLockedBalanceKey(owner))
197278
198279
199280 func getNeutrinoLockedBalance (owner) = getNumberByKey(getNeutrinoLockedBalanceKey(owner))
200281
201282
202-func getUnlockBalanceBlock (owner) = getNumberByKey(getBalanceUnlockBlockKey(owner))
283+func commonSwap (swapType,i) = {
284+ let pmt = value(i.payments[0])
285+ let account = toString(i.caller)
286+ let txId58 = toBase58String(i.transactionId)
287+ let minSwapAmount = minSwapAmountREAD(swapType)
288+ let totalLocked = totalLockedREAD(swapType)
289+ let totalLockedByUser = totalLockedByUserREAD(swapType, account)
290+ let balanceLockMaxInterval = balanceLockIntervalREAD(swapType)
291+ let selfUnlockHeight = (height + balanceLockMaxInterval)
292+ if ((minSwapAmount > pmt.amount))
293+ then minSwapAmountFAIL(swapType, minSwapAmount)
294+ else if (isBlocked)
295+ then emergencyShutdownFAIL()
296+ else [IntegerEntry(totalLockedByUserKEY(swapType, account), (totalLockedByUser + pmt.amount)), IntegerEntry(getBalanceUnlockBlockKey(account), (height + balanceLockMaxInterval)), IntegerEntry(totalLockedKEY(swapType), (totalLocked + pmt.amount)), StringEntry(swapKEY(account, txId58), pendingSwapDATA(swapType, pmt.amount, selfUnlockHeight))]
297+ }
203298
204299
205-func getRPDProfit (count) = getNumberByKey(getRPDProfitKey(count))
206-
207-
208-func applyFees (amountGross,feePart) = {
209- let feeAmount = fraction(amountGross, feePart, PAULI)
210-[(amountGross - feeAmount), feeAmount]
300+func commonWithdraw (account,index,swapTxId,rsaSigOrUnit,i) = {
301+ let userAddress = addressFromStringValue(account)
302+ let feeManagerAddress = feeManagerAddressREAD()
303+ let dataArray = swapDataFailOrREAD(account, swapTxId)
304+ let selfUnlockHeight = parseIntValue(dataArray[sIdxSelfUnlockHeight])
305+ let swapType = dataArray[sIdxSwapType]
306+ let inAmount = parseIntValue(dataArray[sIdxInAmount])
307+ let swapStatus = dataArray[sIdxStatus]
308+ let startHeight = parseIntValue(dataArray[sIdxStartHeight])
309+ let outFeePart = valueOrElse(getInteger(this, outFeePartKEY(swapType)), DEFAULTSWAPFEE)
310+ let totalLocked = totalLockedREAD(swapType)
311+ let totalLockedByUser = totalLockedByUserREAD(swapType, account)
312+ let unlockHeight = match rsaSigOrUnit {
313+ case rsaSig: ByteVector =>
314+ randUnlockHeightOrFail(swapTxId, rsaSig, swapType, startHeight)
315+ case _: Unit =>
316+ selfUnlockHeight
317+ case _ =>
318+ throw("Match error")
319+ }
320+ let indexHeight = getHeightPriceByIndex(index)
321+ let prevIndexHeight = getHeightPriceByIndex((index - 1))
322+ let priceByIndex = getPriceHistory(indexHeight)
323+ let outAmountGrossTuple = if ((swapType == "waves"))
324+ then $Tuple2(convertWavesToNeutrino(inAmount, priceByIndex), neutrinoAssetId)
325+ else if ((swapType == "neutrino"))
326+ then $Tuple2(convertNeutrinoToWaves(inAmount, priceByIndex), unit)
327+ else throw(("Unsupported swap type " + swapType))
328+ let payoutsArray = applyFees(outAmountGrossTuple._1, outFeePart)
329+ let outNetAmount = payoutsArray[IdxNetAmount]
330+ let outFeeAmount = payoutsArray[IdxFeeAmount]
331+ if (isBlocked)
332+ then emergencyShutdownFAIL()
333+ else if ((swapStatus != "PENDING"))
334+ then throw("swap has been already processed")
335+ else if ((unlockHeight > height))
336+ then throw((("please wait for: " + toString(unlockHeight)) + " block height to withdraw funds"))
337+ else if (if (if ((index > priceIndex))
338+ then true
339+ else (unlockHeight > indexHeight))
340+ then true
341+ else if ((prevIndexHeight != 0))
342+ then (prevIndexHeight >= unlockHeight)
343+ else false)
344+ then priceIndexFAIL(index, priceIndex, indexHeight, unlockHeight, prevIndexHeight)
345+ else if ((0 >= payoutsArray[IdxGrossAmount]))
346+ then throw("balance equals zero")
347+ else if (if ((0 > outFeePart))
348+ then true
349+ else (outFeePart >= PAULI))
350+ then throw(((("invalid outFeePart config for " + swapType) + " swap: outFeePart=") + toString(outFeePart)))
351+ else [IntegerEntry(totalLockedByUserKEY(swapType, account), (totalLockedByUser - inAmount)), IntegerEntry(totalLockedKEY(swapType), (totalLocked - inAmount)), ScriptTransfer(userAddress, outNetAmount, outAmountGrossTuple._2), ScriptTransfer(feeManagerAddress, outFeeAmount, outAmountGrossTuple._2), StringEntry(swapKEY(account, swapTxId), finishSwapDATA(dataArray, priceByIndex, outNetAmount, outFeeAmount, unlockHeight))]
211352 }
212353
213354
214355 @Callable(i)
215356 func swapWavesToNeutrino () = {
216357 let pmt = value(i.payments[0])
217- let account = toString(i.caller)
218- if ((minWavesSwapAmount > pmt.amount))
219- then throw((("The specified Waves amount is less than the required minimum of " + toString(minWavesSwapAmount)) + " wavelets."))
220- else if (isDefined(pmt.assetId))
221- then throw("Only Waves token is allowed for swapping.")
222- else if (isBlocked)
223- then throw("contract is blocked by EMERGENCY SHUTDOWN actions untill reactivation by emergency oracles")
224- else if ((getUnlockBalanceBlock(account) > height))
225- then throw((("await " + toString((getUnlockBalanceBlock(account) - height))) + " blocks"))
226- else if (if ((getNeutrinoLockedBalance(account) != 0))
227- then true
228- else (getWavesLockedBalance(account) != 0))
229- then throw("please withdraw locked funds first")
230- else [IntegerEntry(getWavesLockedBalanceKey(account), pmt.amount), IntegerEntry(getBalanceUnlockBlockKey(account), (height + balanceWavesLockInterval)), IntegerEntry(WavesLockedBalanceKey, (wavesLockedBalance + pmt.amount))]
358+ if (isDefined(pmt.assetId))
359+ then throw("Only Waves token is allowed for swapping.")
360+ else commonSwap("waves", i)
231361 }
232362
233363
234364
235365 @Callable(i)
236366 func swapNeutrinoToWaves () = {
237367 let pmt = value(i.payments[0])
238- let account = toString(i.caller)
239- if ((minNeutrinoSwapAmount > pmt.amount))
240- then throw((("The specified Neutrino amount is less than the required minimum of " + toString(minNeutrinoSwapAmount)) + " Neutrino cents."))
241- else if (isBlocked)
242- then throw("The contract is blocked by EMERGENCY SHUTDOWN. Please wait for reactivation by emergency oracles.")
243- else if ((pmt.assetId != neutrinoAssetId))
244- then throw("Only appropriate Neutrino tokens are allowed for swapping.")
245- else if ((getUnlockBalanceBlock(account) > height))
246- then throw((("await " + toString((getUnlockBalanceBlock(account) - height))) + " blocks"))
247- else if (if ((getNeutrinoLockedBalance(account) != 0))
248- then true
249- else (getWavesLockedBalance(account) != 0))
250- then throw("please withdraw locked funds first")
251- else [IntegerEntry(getNeutrinoLockedBalanceKey(account), pmt.amount), IntegerEntry(getBalanceUnlockBlockKey(account), (height + balanceNeutrinoLockInterval)), IntegerEntry(NeutrinoLockedBalanceKey, (neutrinoLockedBalance + pmt.amount))]
368+ if ((pmt.assetId != neutrinoAssetId))
369+ then throw("Only appropriate Neutrino tokens are allowed for swapping.")
370+ else commonSwap("neutrino", i)
252371 }
253372
254373
255374
256375 @Callable(i)
257-func withdraw (account,index) = {
258- let userAddress = addressFromStringValue(account)
259- let feeManagerAddress = valueOrErrorMessage(addressFromString(valueOrErrorMessage(getString(this, FeesManagerAddressKey), (FeesManagerAddressKey + " is not specified"))), (FeesManagerAddressKey + " invalid address format"))
260- let unlockHeight = getUnlockBalanceBlock(account)
261- let userWavesLockedBalance = getWavesLockedBalance(account)
262- let userNeutrinoLockedBalance = getNeutrinoLockedBalance(account)
263- let indexHeight = getHeightPriceByIndex(index)
264- let prevIndexHeight = getHeightPriceByIndex((index - 1))
265- let priceByIndex = getPriceHistory(indexHeight)
266- let neutrinoAmountGross = convertWavesToNeutrino(userWavesLockedBalance, priceByIndex)
267- let wavesAmountGross = convertNeutrinoToWaves(userNeutrinoLockedBalance, priceByIndex)
268- let neutrinoOutFeePart = valueOrElse(getInteger(this, NeutrinoOutFeePartKey), DEFAULTSWAPFEE)
269- let wavesOutFeePart = valueOrElse(getInteger(this, WavesOutFeePartKey), DEFAULTSWAPFEE)
270- let neutrinoPayoutsArray = applyFees(neutrinoAmountGross, neutrinoOutFeePart)
271- let wavesPayoutsArray = applyFees(wavesAmountGross, wavesOutFeePart)
272- if (isBlocked)
273- then throw("contract is blocked by EMERGENCY SHUTDOWN actions untill reactivation by emergency oracles")
274- else if ((unlockHeight > height))
275- then throw((("please wait for: " + toString(unlockHeight)) + " block height to withdraw WAVES funds"))
276- else if (if (if ((index > priceIndex))
277- then true
278- else (unlockHeight > indexHeight))
279- then true
280- else if ((prevIndexHeight != 0))
281- then (prevIndexHeight >= unlockHeight)
282- else false)
283- then throw(((((((((("invalid price history index: index=" + toString(index)) + " priceIndex=") + toString(priceIndex)) + " indexHeight=") + toString(indexHeight)) + " unlockHeight=") + toString(unlockHeight)) + " prevIndexHeight=") + toString(prevIndexHeight)))
284- else if (if ((0 >= neutrinoAmountGross))
285- then (0 >= wavesAmountGross)
286- else false)
287- then throw("balance equals zero")
288- else if (if (if (if ((0 > neutrinoOutFeePart))
289- then true
290- else (neutrinoOutFeePart >= PAULI))
291- then true
292- else (0 > wavesOutFeePart))
293- then true
294- else (wavesOutFeePart >= PAULI))
295- then throw(((("invalid fees data: neutrinoOutFeePart=" + toString(neutrinoOutFeePart)) + " wavesOutFeePart=") + toString(wavesOutFeePart)))
296- else [IntegerEntry(getWavesLockedBalanceKey(account), 0), IntegerEntry(getNeutrinoLockedBalanceKey(account), 0), IntegerEntry(WavesLockedBalanceKey, (wavesLockedBalance - userWavesLockedBalance)), IntegerEntry(NeutrinoLockedBalanceKey, (neutrinoLockedBalance - userNeutrinoLockedBalance)), ScriptTransfer(userAddress, wavesPayoutsArray[IdxNetAmount], unit), ScriptTransfer(feeManagerAddress, wavesPayoutsArray[IdxFeeAmount], unit), ScriptTransfer(userAddress, neutrinoPayoutsArray[IdxNetAmount], neutrinoAssetId), ScriptTransfer(feeManagerAddress, neutrinoPayoutsArray[IdxFeeAmount], neutrinoAssetId)]
297- }
376+func withdraw (account,index,swapTxId) = commonWithdraw(account, index, swapTxId, unit, i)
377+
378+
379+
380+@Callable(i)
381+func withdrawRand (account,index,swapTxId,rsaSig) = commonWithdraw(account, index, swapTxId, rsaSig, i)
298382
299383
300384
301385 @Callable(i)
302386 func transferToAuction () = {
303387 let auctionNBAmount = (neutrinoSupply - assetBalance(addressFromStringValue(auctionContract), bondAssetId))
304388 let surplusWithLiquidation = (surplus - assetBalance(addressFromStringValue(liquidationContract), neutrinoAssetId))
305389 if (isBlocked)
306390 then throw("contract is blocked by EMERGENCY SHUTDOWN actions untill reactivation by emergency oracles")
307391 else if ((auctionNBAmount > (1 * PAULI)))
308392 then [ScriptTransfer(addressFromStringValue(auctionContract), auctionNBAmount, bondAssetId)]
309393 else if ((surplusWithLiquidation >= (1 * PAULI)))
310394 then [ScriptTransfer(addressFromStringValue(liquidationContract), surplusWithLiquidation, neutrinoAssetId)]
311395 else throw(((((((("bond were generated or do not need it. Deficit:" + toString(auctionNBAmount)) + "|") + toString(0)) + ". Surplus:") + toString(surplusWithLiquidation)) + "|") + toString(surplus)))
396+ }
397+
398+
399+
400+@Callable(i)
401+func cancel (account) = {
402+ let userAddress = addressFromStringValue(account)
403+ let userWavesLockedBalance = getWavesLockedBalance(account)
404+ let userNeutrinoLockedBalance = getNeutrinoLockedBalance(account)
405+ if ((toString(i.caller) != "3PLGyPgA9ERwiHfLkkvrVSdsV26qgdr3aqn"))
406+ then throw("permissions denied")
407+ else if (if ((userWavesLockedBalance == 0))
408+ then (userNeutrinoLockedBalance == 0)
409+ else false)
410+ then throw("nothing to cancel")
411+ else [IntegerEntry(getWavesLockedBalanceKey(account), 0), IntegerEntry(getNeutrinoLockedBalanceKey(account), 0), IntegerEntry(WavesLockedBalanceKey, (wavesLockedBalance - userWavesLockedBalance)), IntegerEntry(NeutrinoLockedBalanceKey, (neutrinoLockedBalance - userNeutrinoLockedBalance)), ScriptTransfer(userAddress, userWavesLockedBalance, unit), ScriptTransfer(userAddress, userNeutrinoLockedBalance, neutrinoAssetId)]
312412 }
313413
314414
315415 @Verifier(tx)
316416 func verify () = {
317417 let id = toBase58String(tx.id)
318418 let count = ((((if (sigVerify(tx.bodyBytes, tx.proofs[0], fromBase58String(pubKeyAdminsList[0])))
319419 then 1
320420 else 0) + (if (sigVerify(tx.bodyBytes, tx.proofs[1], fromBase58String(pubKeyAdminsList[1])))
321421 then 1
322422 else 0)) + (if (sigVerify(tx.bodyBytes, tx.proofs[2], fromBase58String(pubKeyAdminsList[2])))
323423 then 1
324424 else 0)) + (if (sigVerify(tx.bodyBytes, tx.proofs[3], fromBase58String(pubKeyAdminsList[3])))
325425 then 2
326426 else 0))
327427 match tx {
328428 case leasingTx: LeaseCancelTransaction|LeaseTransaction =>
329429 sigVerify(leasingTx.bodyBytes, leasingTx.proofs[0], nodeOracleProviderPubKey)
330430 case sponsorTx: SponsorFeeTransaction =>
331431 if (checkIsValidMinSponsoredFee(sponsorTx))
332432 then (count >= 3)
333433 else false
334434 case _ =>
335435 (count >= 3)
336436 }
337437 }
338438

github/deemru/w8io/786bc32 
78.22 ms