tx · CP3B6voJ9BFaP5Jh1n1vU3yEQotepaAo51KovJSysVj

3N3UHNYQ8xwe2WWLKgXX628JyUtF6kYK4wS:  -0.01400000 Waves

2020.11.03 11:10 [1248857] smart account 3N3UHNYQ8xwe2WWLKgXX628JyUtF6kYK4wS > SELF 0.00000000 Waves

{ "type": 13, "id": "CP3B6voJ9BFaP5Jh1n1vU3yEQotepaAo51KovJSysVj", "fee": 1400000, "feeAssetId": null, "timestamp": 1604391035133, "version": 1, "sender": "3N3UHNYQ8xwe2WWLKgXX628JyUtF6kYK4wS", "senderPublicKey": "Hxyif8HnnHh34SZ7PZewDcNUgrZfHNJsQvLNCcPbCW27", "proofs": [ "5tZNBLcY8WDP36hN1iTTqNaz8kfXvLvHFbxfN7s6Xsdmdh6Hh1YYcUbb7uAu3CgvA3VaSkMCmhL8BerLEj3QGAw7" ], "script": "base64:AAIEAAAAAAAAADwIAhIKCggICAEIAQEICBIRCg8ICAEIAQgBAQEBAQEBAQESAwoBCBIDCgEIEgQKAggBEgQKAggIEgMKAQgAAABpAQAAABZjb252ZXJ0SnNvbkFycmF5VG9MaXN0AAAAAgAAAAlqc29uQXJyYXkAAAALc3BsaXRTeW1ib2wJAAS1AAAAAgUAAAAJanNvbkFycmF5BQAAAAtzcGxpdFN5bWJvbAEAAAAYZ2V0U3RyaW5nQnlBZGRyZXNzQW5kS2V5AAAAAgAAAAdhZGRyZXNzAAAAA2tleQkBAAAAC3ZhbHVlT3JFbHNlAAAAAgkABB0AAAACBQAAAAdhZGRyZXNzBQAAAANrZXkCAAAAAAEAAAAMZ2V0Qm9vbEJ5S2V5AAAAAQAAAANrZXkJAQAAAAt2YWx1ZU9yRWxzZQAAAAIJAAQbAAAAAgUAAAAEdGhpcwUAAAADa2V5BwAAAAADU0VQAgAAAAJfXwAAAAAMa2V5QXNzZXRDb2RlAAAAAAAAAAABAAAAAAprZXlBc3NldElkAAAAAAAAAAACAAAAAA5rZXlBc3NldFN0YXR1cwAAAAAAAAAAAwAAAAAVa2V5QXNzZXRQcmljZURlY2ltYWxzAAAAAAAAAAAEAAAAABNrZXlBc3NldEJha2luZ0Fzc2V0AAAAAAAAAAAFAAAAABtrZXlPdmVyY29sbGF0ZXJhbFBlcmNlbnRhZ2UAAAAAAAAAAAYAAAAACmtleU1pblBvb2wAAAAAAAAAAAcAAAAAFWtleVByaWNlT3JhY2xlQWRkcmVzcwAAAAAAAAAACAAAAAAQa2V5TWluQnV5UGF5bWVudAAAAAAAAAAACQAAAAARa2V5TWluU2VsbFBheW1lbnQAAAAAAAAAAAoAAAAAEmtleUJ1eUxvY2tJbnRlcnZhbAAAAAAAAAAACwAAAAATa2V5U2VsbExvY2tJbnRlcnZhbAAAAAAAAAAADAAAAAAJa2V5QnV5RmVlAAAAAAAAAAANAAAAAAprZXlTZWxsRmVlAAAAAAAAAAAOAAAAABhrZXlQb29sUmVkZW1wdGlvblRpbWVvdXQAAAAAAAAAAA8AAAAAIWtleVdlZWtlbmRzUHJpY2VDaGFuZ2VDb2VmZmljaWVudAAAAAAAAAAAEAAAAAAOYWRtaW5QdWJsaWNLZXkCAAAALEcxRWJ1NUFNSGFUSkZFYlpOa1pRY0pGVVE5S2ZpWVV3dXB5TEc4NTdEY0ZyAAAAABpuZXV0cmlub0NvbnRyYWN0QWRkcmVzc0tleQIAAAArJXMlc19fY29tbW9uQ29uZmlnX19uZXV0cmlub0NvbnRyYWN0QWRkcmVzcwAAAAAZbmV1dHJpbm9TdGFraW5nQWRkcmVzc0tleQIAAAAqJXMlc19fY29tbW9uQ29uZmlnX19uZXV0cmlub1N0YWtpbmdBZGRyZXNzAAAAABttaW5pbXVuUG9vbEZ1bmRpbmdBbW91bnRLZXkCAAAALCVzJXNfX2NvbW1vbkNvbmZpZ19fbWluaW11bVBvb2xGdW5kaW5nQW1vdW50AAAAABBpc0luaXRpYWxpemVkS2V5AgAAAB8lcyVzX19jb21tb25Db25maWdfX2luaXRpYWxpemVkAAAAABRjdXJyZW50QXNzZXRJbmRleEtleQIAAAAvJXMlc19fY29tbW9uQ29uZmlnX19wcmVkZWZpbmVkQXNzZXRzQWN0aXZlSW5kZXgAAAAAFGFzc2V0c0FkZHJlc3NMaXN0S2V5AgAAACglcyVzX19jb21tb25Db25maWdfX3ByZWRlZmluZWRBc3NldHNMaXN0AAAAAA5kZWJ0VG9rZW5JZEtleQIAAAAfJXMlc19fY29tbW9uQ29uZmlnX19kZWJ0QXNzZXRJZAAAAAAZZGVidFRva2VuRXRhbG9uQmFsYW5jZUtleQIAAAAqJXMlc19fY29tbW9uQ29uZmlnX19kZWJ0QXNzZXRFdGFsb25CYWxhbmNlAAAAABVkZWZvU3Rha2luZ0FkZHJlc3NLZXkCAAAAJiVzJXNfX2NvbW1vbkNvbmZpZ19fZGVmb1N0YWtpbmdBZGRyZXNzAAAAABpkZWZvU3Rha2luZ1BhY2VtYWtlclB1YktleQIAAAArJXMlc19fY29tbW9uQ29uZmlnX19kZWZvU3Rha2luZ1BhY2VtYWtlclB1YgAAAAAWbWluUG9vbEludmVzdEFtb3VudEtleQIAAAAnJXMlc19fY29tbW9uQ29uZmlnX19taW5Qb29sSW52ZXN0QW1vdW50AAAAABJuZXV0cmlub0Fzc2V0SWRLZXkCAAAAEW5ldXRyaW5vX2Fzc2V0X2lkAAAAAA5uc2J0QXNzZXRJZEtleQIAAAANYm9uZF9hc3NldF9pZAAAAAAObmV1dHJpbm9ScGRLZXkCAAAADHJwZF9jb250cmFjdAAAAAAOZGVmb0Fzc2V0SWRLZXkCAAAADyVzX19kZWZvQXNzZXRJZAAAAAAVc2V0QXNzZXRTY3JpcHRUeElkS2V5AgAAABMlc19fYXNzZXRTY3JpcHRUeElkAQAAABFnZXRBc3NldENvbmZpZ0tleQAAAAEAAAAMYXNzZXRBZGRyZXNzCQABLAAAAAIJAAEsAAAAAgIAAAATJXMlcyVzX19kZWZvQXNzZXRfXwUAAAAMYXNzZXRBZGRyZXNzAgAAAAhfX2NvbmZpZwEAAAAQZ2V0QXNzZXRQcmljZUtleQAAAAEAAAAJYXNzZXRDb2RlCQABLAAAAAICAAAAEiVzJXNfX2Fzc2V0UHJpY2VfXwUAAAAJYXNzZXRDb2RlAQAAABVnZXRBc3NldEluaXRIZWlnaHRLZXkAAAABAAAADGFzc2V0QWRkcmVzcwkAASwAAAACCQABLAAAAAICAAAAEyVzJXMlc19fZGVmb0Fzc2V0X18FAAAADGFzc2V0QWRkcmVzcwIAAAAMX19pbml0SGVpZ2h0AQAAABlnZXRBc3NldEFjdGl2YXRlSGVpZ2h0S2V5AAAAAQAAAAxhc3NldEFkZHJlc3MJAAEsAAAAAgkAASwAAAACAgAAABMlcyVzJXNfX2RlZm9Bc3NldF9fBQAAAAxhc3NldEFkZHJlc3MCAAAAEF9fYWN0aXZhdGVIZWlnaHQBAAAAHWdldEFzc2V0Q3VycnJlbnRQb29sQW1vdW50S2V5AAAAAQAAAAxhc3NldEFkZHJlc3MJAAEsAAAAAgkAASwAAAACAgAAABMlcyVzJXNfX2RlZm9Bc3NldF9fBQAAAAxhc3NldEFkZHJlc3MCAAAADV9fY3VycmVudFBvb2wBAAAAGGdldEFzc2V0TWF4UG9vbEFtb3VudEtleQAAAAEAAAAMYXNzZXRBZGRyZXNzCQABLAAAAAIJAAEsAAAAAgIAAAATJXMlcyVzX19kZWZvQXNzZXRfXwUAAAAMYXNzZXRBZGRyZXNzAgAAAAlfX21heFBvb2wBAAAAD2dldEFzc2V0TWV0YUtleQAAAAEAAAAMYXNzZXRBZGRyZXNzCQABLAAAAAIJAAEsAAAAAgIAAAATJXMlcyVzX19kZWZvQXNzZXRfXwUAAAAMYXNzZXRBZGRyZXNzAgAAAAZfX21ldGEBAAAAFWdldEFzc2V0UG9vbE1ha2Vyc0tleQAAAAEAAAAMYXNzZXRBZGRyZXNzCQABLAAAAAIJAAEsAAAAAgIAAAATJXMlcyVzX19kZWZvQXNzZXRfXwUAAAAMYXNzZXRBZGRyZXNzAgAAAAxfX3Bvb2xNYWtlcnMBAAAAGGdldEFzc2V0QWRkcmVzc0J5Q29kZUtleQAAAAEAAAAJYXNzZXRDb2RlCQABLAAAAAIJAAEsAAAAAgIAAAATJXMlcyVzX19kZWZvQXNzZXRfXwUAAAAJYXNzZXRDb2RlAgAAABRfX2FkZHJlc3NCeUFzc2V0Q29kZQEAAAAbZ2V0QXNzZXRBZGRyZXNzQnlBc3NldElkS2V5AAAAAQAAAAdhc3NldElkCQABLAAAAAIJAAEsAAAAAgIAAAATJXMlcyVzX19kZWZvQXNzZXRfXwUAAAAHYXNzZXRJZAIAAAASX19hZGRyZXNzQnlBc3NldElkAQAAACJnZXRQb29sTWFrZXJQYXJ0aWNpcGF0aW9uQW1vdW50S2V5AAAAAgAAAAxhc3NldEFkZHJlc3MAAAAQcG9vbE1ha2VyQWRkcmVzcwkABLkAAAACCQAETAAAAAICAAAADiVzJXMlcyVzX19wb29sCQAETAAAAAIFAAAADGFzc2V0QWRkcmVzcwkABEwAAAACBQAAABBwb29sTWFrZXJBZGRyZXNzCQAETAAAAAICAAAABmFtb3VudAUAAAADbmlsBQAAAANTRVABAAAAJWdldFBvb2xNYWtlclBhcnRpY2lwYXRpb25NYXhBbW91bnRLZXkAAAACAAAADGFzc2V0QWRkcmVzcwAAABBwb29sTWFrZXJBZGRyZXNzCQAEuQAAAAIJAARMAAAAAgIAAAAOJXMlcyVzJXNfX3Bvb2wJAARMAAAAAgUAAAAMYXNzZXRBZGRyZXNzCQAETAAAAAIFAAAAEHBvb2xNYWtlckFkZHJlc3MJAARMAAAAAgIAAAAJbWF4QW1vdW50BQAAAANuaWwFAAAAA1NFUAEAAAAbZ2V0UG9vbE1ha2VyTG9ja2VkQW1vdW50S2V5AAAAAgAAAAxhc3NldEFkZHJlc3MAAAAQcG9vbE1ha2VyQWRkcmVzcwkABLkAAAACCQAETAAAAAICAAAADiVzJXMlcyVzX19wb29sCQAETAAAAAIFAAAADGFzc2V0QWRkcmVzcwkABEwAAAACBQAAABBwb29sTWFrZXJBZGRyZXNzCQAETAAAAAICAAAADGxvY2tlZEFtb3VudAUAAAADbmlsBQAAAANTRVABAAAAG2dldFBvb2xNYWtlclVubG9ja0hlaWdodEtleQAAAAIAAAAMYXNzZXRBZGRyZXNzAAAAEHBvb2xNYWtlckFkZHJlc3MJAAS5AAAAAgkABEwAAAACAgAAAA4lcyVzJXMlc19fcG9vbAkABEwAAAACBQAAAAxhc3NldEFkZHJlc3MJAARMAAAAAgUAAAAQcG9vbE1ha2VyQWRkcmVzcwkABEwAAAACAgAAAAx1bmxvY2tIZWlnaHQFAAAAA25pbAUAAAADU0VQAQAAAB9nZXRQb29sTWFrZXJMaXF1aWRpdHlSZXF1ZXN0S2V5AAAAAwAAAAxhc3NldEFkZHJlc3MAAAAQcG9vbE1ha2VyQWRkcmVzcwAAAAFoCQAEuQAAAAIJAARMAAAAAgIAAAAQJXMlcyVzJWQlc19fcG9vbAkABEwAAAACBQAAAAxhc3NldEFkZHJlc3MJAARMAAAAAgUAAAAQcG9vbE1ha2VyQWRkcmVzcwkABEwAAAACCQABpAAAAAEFAAAAAWgJAARMAAAAAgIAAAAQbGlxdWlkaXR5UmVxdWVzdAUAAAADbmlsBQAAAANTRVABAAAAG2dldFBvb2xUb3RhbExvY2tlZEFtb3VudEtleQAAAAEAAAAMYXNzZXRBZGRyZXNzCQAEuQAAAAIJAARMAAAAAgIAAAAMJXMlcyVzX19wb29sCQAETAAAAAIFAAAADGFzc2V0QWRkcmVzcwkABEwAAAACAgAAABF0b3RhbExvY2tlZEFtb3VudAUAAAADbmlsBQAAAANTRVABAAAAE2dldEFzc2V0Q29uZmlnQXJyYXkAAAABAAAADGFzc2V0QWRkcmVzcwkBAAAAFmNvbnZlcnRKc29uQXJyYXlUb0xpc3QAAAACCQEAAAATdmFsdWVPckVycm9yTWVzc2FnZQAAAAIJAQAAABhnZXRTdHJpbmdCeUFkZHJlc3NBbmRLZXkAAAACBQAAAAR0aGlzCQEAAAARZ2V0QXNzZXRDb25maWdLZXkAAAABBQAAAAxhc3NldEFkZHJlc3MJAAEsAAAAAgkAASwAAAACAgAAAA5ObyBERUZPIEFzc2V0IAUAAAAMYXNzZXRBZGRyZXNzAgAAAA5jb25maWcgcHJlc2VudAIAAAACX18AAAAADW11bHRpcGxpY2F0b3IAAAAAAAAPQkAAAAAAEG5ldXRyaW5vQ29udHJhY3QJAQAAABFAZXh0ck5hdGl2ZSgxMDYyKQAAAAEJAQAAABN2YWx1ZU9yRXJyb3JNZXNzYWdlAAAAAgkABB0AAAACBQAAAAR0aGlzBQAAABpuZXV0cmlub0NvbnRyYWN0QWRkcmVzc0tleQIAAAAfTm8gbmV1dHJpbm8gY29udHJhY3Qgc3BlY2lmaWVkLgAAAAASbmV1dHJpbm9Bc3NldElkU3RyCQEAAAATdmFsdWVPckVycm9yTWVzc2FnZQAAAAIJAAQdAAAAAgUAAAAQbmV1dHJpbm9Db250cmFjdAUAAAASbmV1dHJpbm9Bc3NldElkS2V5AgAAABdObyBVU0ROIGFzc2V0IGlkIGZvdW5kLgAAAAAPbmV1dHJpbm9Bc3NldElkCQACWQAAAAEFAAAAEm5ldXRyaW5vQXNzZXRJZFN0cgAAAAALbnNidEFzc2V0SWQJAAJZAAAAAQkBAAAAE3ZhbHVlT3JFcnJvck1lc3NhZ2UAAAACCQAEHQAAAAIFAAAAEG5ldXRyaW5vQ29udHJhY3QFAAAADm5zYnRBc3NldElkS2V5AgAAABZObyBOU0JUIGFzc2V0IGlkIGZvdW5kAAAAABJtaW5pbXVtUG9vbEZ1bmRpbmcJAQAAABN2YWx1ZU9yRXJyb3JNZXNzYWdlAAAAAgkABBoAAAACBQAAAAR0aGlzBQAAABttaW5pbXVuUG9vbEZ1bmRpbmdBbW91bnRLZXkCAAAALU1pbmltdW0gcG9vbCBmdW5kaW5nIGFtb3VudCBpcyBub3Qgc3BlY2lmaWVkLgAAAAATbWluUG9vbEludmVzdEFtb3VudAkBAAAAC3ZhbHVlT3JFbHNlAAAAAgkABBoAAAACBQAAAAR0aGlzBQAAABZtaW5Qb29sSW52ZXN0QW1vdW50S2V5CQAAaAAAAAIAAAAAAAAAAAoFAAAADW11bHRpcGxpY2F0b3IAAAAADUFTU0V0UFJPUE9TRUQCAAAACFBST1BPU0VEAAAAAApBU1NFdFJFQURZAgAAAAVSRUFEWQAAAAALQVNTRXRJU1NVRUQCAAAABklTU1VFRAAAAAALYXNzZXRTdGF0dXMJAAUVAAAAAwUAAAANQVNTRXRQUk9QT1NFRAUAAAAKQVNTRXRSRUFEWQUAAAALQVNTRXRJU1NVRUQAAAAAGHByZWRlZmluZWRBc3NldENvbnRyYWN0cwkBAAAAE3ZhbHVlT3JFcnJvck1lc3NhZ2UAAAACCQAEHQAAAAIFAAAABHRoaXMFAAAAFGFzc2V0c0FkZHJlc3NMaXN0S2V5AgAAAC1ObyBwb29sIG9mIHByZWRlcGxveWVkIGFzc2V0IGNvbnRyYWN0cyBmb3VuZC4AAAAAFHByZWRlZmluZWRBc3NldHNMaXN0CQEAAAAWY29udmVydEpzb25BcnJheVRvTGlzdAAAAAIFAAAAGHByZWRlZmluZWRBc3NldENvbnRyYWN0cwIAAAABLAAAAAARY3VycmVudEFzc2V0SW5kZXgJAQAAAAt2YWx1ZU9yRWxzZQAAAAIJAAQaAAAAAgUAAAAEdGhpcwUAAAAUY3VycmVudEFzc2V0SW5kZXhLZXkAAAAAAAAAAAAAAAAACWRlYnRUb2tlbgkAAlkAAAABCQEAAAATdmFsdWVPckVycm9yTWVzc2FnZQAAAAIJAAQdAAAAAgUAAAAEdGhpcwUAAAAOZGVidFRva2VuSWRLZXkCAAAAE05vIGRlYnQgdG9rZW4gZm91bmQAAAAAFmRlYnRUb2tlblJlaXNzdWVBbW91bnQJAQAAAAt2YWx1ZU9yRWxzZQAAAAIJAAQaAAAAAgUAAAAEdGhpcwUAAAAZZGVidFRva2VuRXRhbG9uQmFsYW5jZUtleQAAAAAAAAAAAAAAAAATbmV1dHJpbm9ScGRDb250cmFjdAkBAAAAEUBleHRyTmF0aXZlKDEwNjIpAAAAAQkBAAAAEUBleHRyTmF0aXZlKDEwNTMpAAAAAgUAAAAQbmV1dHJpbm9Db250cmFjdAUAAAAObmV1dHJpbm9ScGRLZXkBAAAAFWdldE5ldXRyaW5vUnBkQmFsYW5jZQAAAAEAAAALdXNlckFkZHJlc3MEAAAAFnVzZXJOZXV0cmlub1JwZEJhbGFuY2UJAAS5AAAAAgkABEwAAAACAgAAAAtycGRfYmFsYW5jZQkABEwAAAACBQAAABJuZXV0cmlub0Fzc2V0SWRTdHIJAARMAAAAAgUAAAALdXNlckFkZHJlc3MFAAAAA25pbAIAAAABXwkBAAAAC3ZhbHVlT3JFbHNlAAAAAgkABBoAAAACBQAAABNuZXV0cmlub1JwZENvbnRyYWN0BQAAABZ1c2VyTmV1dHJpbm9ScGRCYWxhbmNlAAAAAAAAAAAAAQAAABVnZXRBc3NldEFkZHJlc3NCeUNvZGUAAAABAAAAC2Fzc2V0U3ltYm9sCQEAAAALdmFsdWVPckVsc2UAAAACCQAEHQAAAAIFAAAABHRoaXMJAQAAABhnZXRBc3NldEFkZHJlc3NCeUNvZGVLZXkAAAABBQAAAAthc3NldFN5bWJvbAIAAAAAAQAAABtnZXRBc3NldEFkZHJlc3NCeUNvZGVPckZhaWwAAAABAAAAC2Fzc2V0U3ltYm9sCQEAAAATdmFsdWVPckVycm9yTWVzc2FnZQAAAAIJAAQdAAAAAgUAAAAEdGhpcwkBAAAAGGdldEFzc2V0QWRkcmVzc0J5Q29kZUtleQAAAAEFAAAAC2Fzc2V0U3ltYm9sCQABLAAAAAIJAAEsAAAAAgIAAAAdQ2Fubm90IGZpbmQgYXNzZXQgYWRkcmVzcyBieSAFAAAAC2Fzc2V0U3ltYm9sAgAAAAUgY29kZQEAAAAMZ2V0QXNzZXRDb2RlAAAAAQAAAAthc3NldENvbmZpZwkAAZEAAAACBQAAAAthc3NldENvbmZpZwUAAAAMa2V5QXNzZXRDb2RlAQAAAApnZXRBc3NldElEAAAAAQAAAAthc3NldENvbmZpZwkAAZEAAAACBQAAAAthc3NldENvbmZpZwUAAAAKa2V5QXNzZXRJZAEAAAAOZ2V0QXNzZXRTdGF0dXMAAAABAAAAC2Fzc2V0Q29uZmlnCQABkQAAAAIFAAAAC2Fzc2V0Q29uZmlnBQAAAA5rZXlBc3NldFN0YXR1cwEAAAAVZ2V0QXNzZXRQcmljZURlY2ltYWxzAAAAAQAAAAthc3NldENvbmZpZwkBAAAADXBhcnNlSW50VmFsdWUAAAABCQABkQAAAAIFAAAAC2Fzc2V0Q29uZmlnBQAAABVrZXlBc3NldFByaWNlRGVjaW1hbHMBAAAAFWdldEFzc2V0QmFraW5nQXNzZXRJZAAAAAEAAAALYXNzZXRDb25maWcJAAGRAAAAAgUAAAALYXNzZXRDb25maWcFAAAAE2tleUFzc2V0QmFraW5nQXNzZXQBAAAAHWdldEFzc2V0T3ZlckNvbGxhdGVyYWxQZXJjZW50AAAAAQAAAAthc3NldENvbmZpZwkBAAAADXBhcnNlSW50VmFsdWUAAAABCQABkQAAAAIFAAAAC2Fzc2V0Q29uZmlnBQAAABtrZXlPdmVyY29sbGF0ZXJhbFBlcmNlbnRhZ2UBAAAAHWdldEFzc2V0TWluUmVxdWlyZWRQb29sQW1vdW50AAAAAQAAAAthc3NldENvbmZpZwkBAAAADXBhcnNlSW50VmFsdWUAAAABCQABkQAAAAIFAAAAC2Fzc2V0Q29uZmlnBQAAAAprZXlNaW5Qb29sAQAAABpnZXRBc3NldFByaWNlT3JhY2xlQWRkcmVzcwAAAAEAAAALYXNzZXRDb25maWcJAAGRAAAAAgUAAAALYXNzZXRDb25maWcFAAAAFWtleVByaWNlT3JhY2xlQWRkcmVzcwEAAAAYZ2V0TWluQnV5UGF5bWVudFBlckFzc2V0AAAAAQAAAAthc3NldENvbmZpZwkBAAAADXBhcnNlSW50VmFsdWUAAAABCQABkQAAAAIFAAAAC2Fzc2V0Q29uZmlnBQAAABBrZXlNaW5CdXlQYXltZW50AQAAABlnZXRNaW5TZWxsUGF5bWVudFBlckFzc2V0AAAAAQAAAAthc3NldENvbmZpZwkBAAAADXBhcnNlSW50VmFsdWUAAAABCQABkQAAAAIFAAAAC2Fzc2V0Q29uZmlnBQAAABFrZXlNaW5TZWxsUGF5bWVudAEAAAAaZ2V0QnV5TG9ja0ludGVydmFsUGVyQXNzZXQAAAABAAAAC2Fzc2V0Q29uZmlnCQEAAAANcGFyc2VJbnRWYWx1ZQAAAAEJAAGRAAAAAgUAAAALYXNzZXRDb25maWcFAAAAEmtleUJ1eUxvY2tJbnRlcnZhbAEAAAAbZ2V0U2VsbExvY2tJbnRlcnZhbFBlckFzc2V0AAAAAQAAAAthc3NldENvbmZpZwkBAAAADXBhcnNlSW50VmFsdWUAAAABCQABkQAAAAIFAAAAC2Fzc2V0Q29uZmlnBQAAABNrZXlTZWxsTG9ja0ludGVydmFsAQAAABFnZXRCdXlGZWVQZXJBc3NldAAAAAEAAAALYXNzZXRDb25maWcJAQAAAA1wYXJzZUludFZhbHVlAAAAAQkAAZEAAAACBQAAAAthc3NldENvbmZpZwUAAAAJa2V5QnV5RmVlAQAAABJnZXRTZWxsRmVlUGVyQXNzZXQAAAABAAAAC2Fzc2V0Q29uZmlnCQEAAAANcGFyc2VJbnRWYWx1ZQAAAAEJAAGRAAAAAgUAAAALYXNzZXRDb25maWcFAAAACmtleVNlbGxGZWUBAAAAGmdldFJlZGVtcHRpb25UaW1lb3V0QmxvY2tzAAAAAQAAAAthc3NldENvbmZpZwkBAAAADXBhcnNlSW50VmFsdWUAAAABCQABkQAAAAIFAAAAC2Fzc2V0Q29uZmlnBQAAABhrZXlQb29sUmVkZW1wdGlvblRpbWVvdXQBAAAAIWdldFdlZWtlbmRzUHJpY2VDaGFuZ2VDb2VmZmljaWVudAAAAAEAAAALYXNzZXRDb25maWcJAQAAAA1wYXJzZUludFZhbHVlAAAAAQkAAZEAAAACBQAAAAthc3NldENvbmZpZwUAAAAha2V5V2Vla2VuZHNQcmljZUNoYW5nZUNvZWZmaWNpZW50AQAAABJnZXRBc3NldFBvb2xNYWtlcnMAAAABAAAADGFzc2V0QWRkcmVzcwkBAAAAC3ZhbHVlT3JFbHNlAAAAAgkABB0AAAACBQAAAAR0aGlzCQEAAAAVZ2V0QXNzZXRQb29sTWFrZXJzS2V5AAAAAQUAAAAMYXNzZXRBZGRyZXNzAgAAAAABAAAAEmdldEFzc2V0SW5pdEhlaWdodAAAAAEAAAAMYXNzZXRBZGRyZXNzCQEAAAALdmFsdWVPckVsc2UAAAACCQAEGgAAAAIFAAAABHRoaXMJAQAAABVnZXRBc3NldEluaXRIZWlnaHRLZXkAAAABBQAAAAxhc3NldEFkZHJlc3MAAAAAAAAAAAABAAAAFmdldEFzc2V0QWN0aXZhdGVIZWlnaHQAAAABAAAADGFzc2V0QWRkcmVzcwkBAAAAC3ZhbHVlT3JFbHNlAAAAAgkABBoAAAACBQAAAAR0aGlzCQEAAAAZZ2V0QXNzZXRBY3RpdmF0ZUhlaWdodEtleQAAAAEFAAAADGFzc2V0QWRkcmVzcwAAAAAAAAAAAAEAAAAZZ2V0QXNzZXRDdXJyZW50UG9vbEFtb3VudAAAAAEAAAAMYXNzZXRBZGRyZXNzCQEAAAALdmFsdWVPckVsc2UAAAACCQAEGgAAAAIFAAAABHRoaXMJAQAAAB1nZXRBc3NldEN1cnJyZW50UG9vbEFtb3VudEtleQAAAAEFAAAADGFzc2V0QWRkcmVzcwAAAAAAAAAAAAEAAAAVZ2V0QXNzZXRNYXhQb29sQW1vdW50AAAAAQAAAAxhc3NldEFkZHJlc3MJAQAAAAt2YWx1ZU9yRWxzZQAAAAIJAAQaAAAAAgUAAAAEdGhpcwkBAAAAGGdldEFzc2V0TWF4UG9vbEFtb3VudEtleQAAAAEFAAAADGFzc2V0QWRkcmVzcwAAAAAAAAAAAAEAAAAMZ2V0QXNzZXRNZXRhAAAAAQAAAAxhc3NldEFkZHJlc3MJAQAAAAt2YWx1ZU9yRWxzZQAAAAIJAAQdAAAAAgUAAAAEdGhpcwkBAAAAD2dldEFzc2V0TWV0YUtleQAAAAEFAAAADGFzc2V0QWRkcmVzcwIAAAAAAQAAAB9nZXRQb29sTWFrZXJQYXJ0aWNpcGF0aW9uQW1vdW50AAAAAgAAAAxhc3NldEFkZHJlc3MAAAAQcG9vbE1ha2VyQWRkcmVzcwkBAAAAC3ZhbHVlT3JFbHNlAAAAAgkABBoAAAACBQAAAAR0aGlzCQEAAAAiZ2V0UG9vbE1ha2VyUGFydGljaXBhdGlvbkFtb3VudEtleQAAAAIFAAAADGFzc2V0QWRkcmVzcwUAAAAQcG9vbE1ha2VyQWRkcmVzcwAAAAAAAAAAAAEAAAAiZ2V0UG9vbE1ha2VyUGFydGljaXBhdGlvbk1heEFtb3VudAAAAAIAAAAMYXNzZXRBZGRyZXNzAAAAEHBvb2xNYWtlckFkZHJlc3MJAQAAAAt2YWx1ZU9yRWxzZQAAAAIJAAQaAAAAAgUAAAAEdGhpcwkBAAAAJWdldFBvb2xNYWtlclBhcnRpY2lwYXRpb25NYXhBbW91bnRLZXkAAAACBQAAAAxhc3NldEFkZHJlc3MFAAAAEHBvb2xNYWtlckFkZHJlc3MAAAAAAAAAAAABAAAAGGdldFBvb2xNYWtlckxvY2tlZEFtb3VudAAAAAIAAAAMYXNzZXRBZGRyZXNzAAAAEHBvb2xNYWtlckFkZHJlc3MJAQAAAAt2YWx1ZU9yRWxzZQAAAAIJAAQaAAAAAgUAAAAEdGhpcwkBAAAAG2dldFBvb2xNYWtlckxvY2tlZEFtb3VudEtleQAAAAIFAAAADGFzc2V0QWRkcmVzcwUAAAAQcG9vbE1ha2VyQWRkcmVzcwAAAAAAAAAAAAEAAAAYZ2V0UG9vbE1ha2VyVW5sb2NrSGVpZ2h0AAAAAgAAAAxhc3NldEFkZHJlc3MAAAAQcG9vbE1ha2VyQWRkcmVzcwkBAAAAC3ZhbHVlT3JFbHNlAAAAAgkABBoAAAACBQAAAAR0aGlzCQEAAAAbZ2V0UG9vbE1ha2VyVW5sb2NrSGVpZ2h0S2V5AAAAAgUAAAAMYXNzZXRBZGRyZXNzBQAAABBwb29sTWFrZXJBZGRyZXNzAAAAAAAAAAAAAQAAAA1nZXRBc3NldFByaWNlAAAAAgAAABJwcmljZU9yYWNsZUFkZHJlc3MAAAAJYXNzZXRDb2RlCQEAAAATdmFsdWVPckVycm9yTWVzc2FnZQAAAAIJAAQaAAAAAgUAAAAScHJpY2VPcmFjbGVBZGRyZXNzCQEAAAAQZ2V0QXNzZXRQcmljZUtleQAAAAEFAAAACWFzc2V0Q29kZQIAAAAUTm8gQXNzZXQgcHJpY2UgZm91bmQBAAAAGGdldFBvb2xUb3RhbExvY2tlZEFtb3VudAAAAAEAAAAMYXNzZXRBZGRyZXNzCQEAAAALdmFsdWVPckVsc2UAAAACCQAEGgAAAAIFAAAABHRoaXMJAQAAABtnZXRQb29sVG90YWxMb2NrZWRBbW91bnRLZXkAAAABBQAAAAxhc3NldEFkZHJlc3MAAAAAAAAAAAABAAAAH2NvbXBvc2VBc3NldENvbmZpZ3VyYXRpb25TdHJpbmcAAAAQAAAACWFzc2V0Q29kZQAAAAdhc3NldElkAAAAC2Fzc2V0U3RhdHVzAAAADXByaWNlRGVjaW1hbHMAAAAOYmFja2luZ0Fzc2V0SWQAAAAVb3ZlckNvbGxhdGVyYWxQZXJjZW50AAAAB21pblBvb2wAAAAScHJpY2VPcmFjbGVBZGRyZXNzAAAADW1pbkJ1eVBheW1lbnQAAAAObWluU2VsbFBheW1lbnQAAAAPYnV5TG9ja0ludGVydmFsAAAAEHNlbGxMb2NrSW50ZXJ2YWwAAAAGYnV5RmVlAAAAB3NlbGxGZWUAAAAVcG9vbFJlZGVtcHRpb25UaW1lb3V0AAAAHndlZWtlbmRzUHJpY2VDaGFuZ2VDb2VmZmljaWVudAkABLkAAAACCQAETAAAAAICAAAAHiVzJXMlcyVkJXMlZCVkJXMlZCVkJWQlZCVkJWQlZAkABEwAAAACBQAAAAlhc3NldENvZGUJAARMAAAAAgUAAAAHYXNzZXRJZAkABEwAAAACBQAAAAthc3NldFN0YXR1cwkABEwAAAACCQABpAAAAAEFAAAADXByaWNlRGVjaW1hbHMJAARMAAAAAgUAAAAOYmFja2luZ0Fzc2V0SWQJAARMAAAAAgkAAaQAAAABBQAAABVvdmVyQ29sbGF0ZXJhbFBlcmNlbnQJAARMAAAAAgkAAaQAAAABBQAAAAdtaW5Qb29sCQAETAAAAAIFAAAAEnByaWNlT3JhY2xlQWRkcmVzcwkABEwAAAACCQABpAAAAAEFAAAADW1pbkJ1eVBheW1lbnQJAARMAAAAAgkAAaQAAAABBQAAAA5taW5TZWxsUGF5bWVudAkABEwAAAACCQABpAAAAAEFAAAAD2J1eUxvY2tJbnRlcnZhbAkABEwAAAACCQABpAAAAAEFAAAAEHNlbGxMb2NrSW50ZXJ2YWwJAARMAAAAAgkAAaQAAAABBQAAAAZidXlGZWUJAARMAAAAAgkAAaQAAAABBQAAAAdzZWxsRmVlCQAETAAAAAIJAAGkAAAAAQUAAAAVcG9vbFJlZGVtcHRpb25UaW1lb3V0CQAETAAAAAIJAAGkAAAAAQUAAAAed2Vla2VuZHNQcmljZUNoYW5nZUNvZWZmaWNpZW50BQAAAANuaWwCAAAAAl9fAQAAABR1cGRhdGVBc3NldENmZ1N0YXR1cwAAAAIAAAAJbmV3U3RhdHVzAAAAFmFzc2V0Q29uZmlndXJhdGlvbkxpc3QEAAAABWFDb2RlCQEAAAAMZ2V0QXNzZXRDb2RlAAAAAQUAAAAWYXNzZXRDb25maWd1cmF0aW9uTGlzdAQAAAADYUlECQEAAAAKZ2V0QXNzZXRJRAAAAAEFAAAAFmFzc2V0Q29uZmlndXJhdGlvbkxpc3QEAAAADmFQcmljZURlY2ltYWxzCQEAAAAVZ2V0QXNzZXRQcmljZURlY2ltYWxzAAAAAQUAAAAWYXNzZXRDb25maWd1cmF0aW9uTGlzdAQAAAAOYUJha2luZ0Fzc2V0SWQJAQAAABVnZXRBc3NldEJha2luZ0Fzc2V0SWQAAAABBQAAABZhc3NldENvbmZpZ3VyYXRpb25MaXN0BAAAABZhT3ZlckNvbGxhdGVyYWxQZXJjZW50CQEAAAAdZ2V0QXNzZXRPdmVyQ29sbGF0ZXJhbFBlcmNlbnQAAAABBQAAABZhc3NldENvbmZpZ3VyYXRpb25MaXN0BAAAABBhTWluUmVxdWlyZWRQb29sCQEAAAAdZ2V0QXNzZXRNaW5SZXF1aXJlZFBvb2xBbW91bnQAAAABBQAAABZhc3NldENvbmZpZ3VyYXRpb25MaXN0BAAAABNhUHJpY2VPcmFjbGVBZGRyZXNzCQEAAAAaZ2V0QXNzZXRQcmljZU9yYWNsZUFkZHJlc3MAAAABBQAAABZhc3NldENvbmZpZ3VyYXRpb25MaXN0BAAAAA5hTWluQnV5UGF5bWVudAkBAAAAGGdldE1pbkJ1eVBheW1lbnRQZXJBc3NldAAAAAEFAAAAFmFzc2V0Q29uZmlndXJhdGlvbkxpc3QEAAAAD2FNaW5TZWxsUGF5bWVudAkBAAAAGWdldE1pblNlbGxQYXltZW50UGVyQXNzZXQAAAABBQAAABZhc3NldENvbmZpZ3VyYXRpb25MaXN0BAAAABBhQnV5TG9ja0ludGVydmFsCQEAAAAaZ2V0QnV5TG9ja0ludGVydmFsUGVyQXNzZXQAAAABBQAAABZhc3NldENvbmZpZ3VyYXRpb25MaXN0BAAAABFhU2VsbExvY2tJbnRlcnZhbAkBAAAAG2dldFNlbGxMb2NrSW50ZXJ2YWxQZXJBc3NldAAAAAEFAAAAFmFzc2V0Q29uZmlndXJhdGlvbkxpc3QEAAAAB2FCdXlGZWUJAQAAABFnZXRCdXlGZWVQZXJBc3NldAAAAAEFAAAAFmFzc2V0Q29uZmlndXJhdGlvbkxpc3QEAAAACGFTZWxsRmVlCQEAAAASZ2V0U2VsbEZlZVBlckFzc2V0AAAAAQUAAAAWYXNzZXRDb25maWd1cmF0aW9uTGlzdAQAAAAYYVJlZGVtcHRpb25UaW1lb3V0QmxvY2tzCQEAAAAaZ2V0UmVkZW1wdGlvblRpbWVvdXRCbG9ja3MAAAABBQAAABZhc3NldENvbmZpZ3VyYXRpb25MaXN0BAAAAB53ZWVrZW5kc1ByaWNlQ2hhbmdlQ29lZmZpY2llbnQJAQAAACFnZXRXZWVrZW5kc1ByaWNlQ2hhbmdlQ29lZmZpY2llbnQAAAABBQAAABZhc3NldENvbmZpZ3VyYXRpb25MaXN0CQEAAAAfY29tcG9zZUFzc2V0Q29uZmlndXJhdGlvblN0cmluZwAAABAFAAAABWFDb2RlBQAAAANhSUQFAAAACW5ld1N0YXR1cwUAAAAOYVByaWNlRGVjaW1hbHMFAAAADmFCYWtpbmdBc3NldElkBQAAABZhT3ZlckNvbGxhdGVyYWxQZXJjZW50BQAAABBhTWluUmVxdWlyZWRQb29sBQAAABNhUHJpY2VPcmFjbGVBZGRyZXNzBQAAAA5hTWluQnV5UGF5bWVudAUAAAAPYU1pblNlbGxQYXltZW50BQAAABBhQnV5TG9ja0ludGVydmFsBQAAABFhU2VsbExvY2tJbnRlcnZhbAUAAAAHYUJ1eUZlZQUAAAAIYVNlbGxGZWUFAAAAGGFSZWRlbXB0aW9uVGltZW91dEJsb2NrcwUAAAAed2Vla2VuZHNQcmljZUNoYW5nZUNvZWZmaWNpZW50AQAAABljYWxjQWxsb3dlZEFtb3VudFRvUmVkZWVtAAAAAgAAAAlhc3NldENvZGUAAAAQcG9vbE1ha2VyQWRkcmVzcwQAAAAPYXNzZXRBZGRyZXNzU3RyCQEAAAAbZ2V0QXNzZXRBZGRyZXNzQnlDb2RlT3JGYWlsAAAAAQUAAAAJYXNzZXRDb2RlBAAAAAxhc3NldEFkZHJlc3MJAQAAABFAZXh0ck5hdGl2ZSgxMDYyKQAAAAEFAAAAD2Fzc2V0QWRkcmVzc1N0cgQAAAAWYXNzZXRDb25maWd1cmF0aW9uTGlzdAkBAAAAE2dldEFzc2V0Q29uZmlnQXJyYXkAAAABBQAAAA9hc3NldEFkZHJlc3NTdHIEAAAACmFzc2V0SWRTdHIJAQAAAApnZXRBc3NldElEAAAAAQUAAAAWYXNzZXRDb25maWd1cmF0aW9uTGlzdAQAAAAHYXNzZXRJZAkAAlkAAAABBQAAAAphc3NldElkU3RyBAAAAA5wb29sQWN0dWFsU2l6ZQkBAAAAC3ZhbHVlT3JFbHNlAAAAAgkABBoAAAACBQAAAAR0aGlzCQEAAAAdZ2V0QXNzZXRDdXJycmVudFBvb2xBbW91bnRLZXkAAAABBQAAAA9hc3NldEFkZHJlc3NTdHIAAAAAAAAAAAAEAAAAC3Bvb2xNYXhTaXplCQEAAAALdmFsdWVPckVsc2UAAAACCQAEGgAAAAIFAAAABHRoaXMJAQAAABhnZXRBc3NldE1heFBvb2xBbW91bnRLZXkAAAABBQAAAA9hc3NldEFkZHJlc3NTdHIAAAAAAAAAAAAEAAAAGHBvb2xUb3RhbExvY2tlZEFtb3VudEtleQkBAAAAG2dldFBvb2xUb3RhbExvY2tlZEFtb3VudEtleQAAAAEFAAAAD2Fzc2V0QWRkcmVzc1N0cgQAAAAPcG9vbFRvdGFsTG9ja2VkCQEAAAALdmFsdWVPckVsc2UAAAACCQAEGgAAAAIFAAAABHRoaXMFAAAAGHBvb2xUb3RhbExvY2tlZEFtb3VudEtleQAAAAAAAAAAAAQAAAAVYXNzZXRVc2RuU3Rha2VkQW1vdW50CQEAAAAVZ2V0TmV1dHJpbm9ScGRCYWxhbmNlAAAAAQUAAAAPYXNzZXRBZGRyZXNzU3RyBAAAABZhc3NldFVzZG5CYWxhbmNlQW1vdW50CQAD8AAAAAIFAAAADGFzc2V0QWRkcmVzcwUAAAAPbmV1dHJpbm9Bc3NldElkBAAAABRhc3NldFRvdGFsVXNkbkFtb3VudAkAAGUAAAACCQAAZAAAAAIFAAAAFWFzc2V0VXNkblN0YWtlZEFtb3VudAUAAAAWYXNzZXRVc2RuQmFsYW5jZUFtb3VudAUAAAAPcG9vbFRvdGFsTG9ja2VkBAAAAAt1Y29sbGF0ZXJhbAkAAGUAAAACBQAAABRhc3NldFRvdGFsVXNkbkFtb3VudAUAAAAOcG9vbEFjdHVhbFNpemUEAAAAEGRlZm9Bc3NldEJhbGFuY2UJAAPwAAAAAgUAAAAMYXNzZXRBZGRyZXNzBQAAAAdhc3NldElkBAAAABJhc3NldEVtaXNzaW9uRGlydHkICQEAAAATdmFsdWVPckVycm9yTWVzc2FnZQAAAAIJAAPsAAAAAQUAAAAHYXNzZXRJZAkAASwAAAACAgAAABhDYW5ub3QgZmluZCBhc3NldCBieSBpZCAFAAAACmFzc2V0SWRTdHIAAAAIcXVhbnRpdHkEAAAADWFzc2V0RW1pc3Npb24JAABlAAAAAgUAAAASYXNzZXRFbWlzc2lvbkRpcnR5BQAAABBkZWZvQXNzZXRCYWxhbmNlBAAAABJwcmljZU9yYWNsZUFkZHJlc3MJAQAAABFAZXh0ck5hdGl2ZSgxMDYyKQAAAAEJAQAAABpnZXRBc3NldFByaWNlT3JhY2xlQWRkcmVzcwAAAAEFAAAAFmFzc2V0Q29uZmlndXJhdGlvbkxpc3QEAAAADGN1cnJlbnRQcmljZQkBAAAADWdldEFzc2V0UHJpY2UAAAACBQAAABJwcmljZU9yYWNsZUFkZHJlc3MFAAAACWFzc2V0Q29kZQQAAAAbcHJpY2VXaXRoT3ZlcmNvbGxhdGVyYWxSYXRlCQAAaQAAAAIJAABoAAAAAgkAAGQAAAACBQAAAA1tdWx0aXBsaWNhdG9yCQEAAAAdZ2V0QXNzZXRPdmVyQ29sbGF0ZXJhbFBlcmNlbnQAAAABBQAAABZhc3NldENvbmZpZ3VyYXRpb25MaXN0BQAAAAxjdXJyZW50UHJpY2UFAAAADW11bHRpcGxpY2F0b3IEAAAAGXVzZG5OZWVkZWRUb0NvdmVyRW1pc3Npb24JAABoAAAAAgUAAAAbcHJpY2VXaXRoT3ZlcmNvbGxhdGVyYWxSYXRlBQAAAA1hc3NldEVtaXNzaW9uBAAAABJwb29sTWFrZXJNYXhBbW91bnQJAQAAACJnZXRQb29sTWFrZXJQYXJ0aWNpcGF0aW9uTWF4QW1vdW50AAAAAgUAAAAPYXNzZXRBZGRyZXNzU3RyBQAAABBwb29sTWFrZXJBZGRyZXNzBAAAAB9wb29sTWFrZXJQYXJ0aWNpcGF0aW9uQW1vdW50S2V5CQEAAAAiZ2V0UG9vbE1ha2VyUGFydGljaXBhdGlvbkFtb3VudEtleQAAAAIFAAAAD2Fzc2V0QWRkcmVzc1N0cgUAAAAQcG9vbE1ha2VyQWRkcmVzcwQAAAAVcG9vbE1ha2VyQWN0dWFsQW1vdW50CQEAAAALdmFsdWVPckVsc2UAAAACCQAEGgAAAAIFAAAABHRoaXMFAAAAH3Bvb2xNYWtlclBhcnRpY2lwYXRpb25BbW91bnRLZXkAAAAAAAAAAAAEAAAAFXBvb2xNYWtlckxvY2tlZEFtb3VudAkBAAAAGGdldFBvb2xNYWtlckxvY2tlZEFtb3VudAAAAAIFAAAAD2Fzc2V0QWRkcmVzc1N0cgUAAAAQcG9vbE1ha2VyQWRkcmVzcwQAAAAHc3VycGx1cwkAAGUAAAACBQAAAAt1Y29sbGF0ZXJhbAUAAAAZdXNkbk5lZWRlZFRvQ292ZXJFbWlzc2lvbgQAAAAQcGVyY2VudFZpYUFjdHVhbAkAAGsAAAADBQAAABVwb29sTWFrZXJBY3R1YWxBbW91bnQFAAAADW11bHRpcGxpY2F0b3IFAAAADnBvb2xBY3R1YWxTaXplBAAAAA1wZXJjZW50VmlhTWF4CQAAawAAAAMFAAAAEnBvb2xNYWtlck1heEFtb3VudAUAAAANbXVsdGlwbGljYXRvcgUAAAALcG9vbE1heFNpemUEAAAACmZyb3plblBhcnQDCQAAZwAAAAIFAAAAB3N1cnBsdXMAAAAAAAAAAAAAAAAAAAAAAAAJAAGWAAAAAQkABEwAAAACBQAAABBwZXJjZW50VmlhQWN0dWFsCQAETAAAAAIFAAAADXBlcmNlbnRWaWFNYXgFAAAAA25pbAQAAAAVYWxsb3dlZEFtb3VudFRvUmVkZWVtCQAAawAAAAMJAABlAAAAAgUAAAANbXVsdGlwbGljYXRvcgUAAAAKZnJvemVuUGFydAUAAAAVcG9vbE1ha2VyQWN0dWFsQW1vdW50BQAAAA1tdWx0aXBsaWNhdG9yBAAAAA53aXRoZHJhd0hlaWdodAkAAGQAAAACBQAAAAZoZWlnaHQJAQAAABpnZXRSZWRlbXB0aW9uVGltZW91dEJsb2NrcwAAAAEFAAAAFmFzc2V0Q29uZmlndXJhdGlvbkxpc3QJAAUaAAAACAUAAAAPYXNzZXRBZGRyZXNzU3RyBQAAABVhbGxvd2VkQW1vdW50VG9SZWRlZW0FAAAAD3Bvb2xUb3RhbExvY2tlZAUAAAAOcG9vbEFjdHVhbFNpemUFAAAAFXBvb2xNYWtlckFjdHVhbEFtb3VudAUAAAAOd2l0aGRyYXdIZWlnaHQFAAAAFXBvb2xNYWtlckxvY2tlZEFtb3VudAkABLkAAAACCQAETAAAAAIFAAAAD2Fzc2V0QWRkcmVzc1N0cgkABEwAAAACBQAAAAphc3NldElkU3RyCQAETAAAAAIFAAAAEHBvb2xNYWtlckFkZHJlc3MJAARMAAAAAgkAAaQAAAABBQAAABVhc3NldFVzZG5TdGFrZWRBbW91bnQJAARMAAAAAgkAAaQAAAABBQAAABZhc3NldFVzZG5CYWxhbmNlQW1vdW50CQAETAAAAAIJAAGkAAAAAQUAAAAUYXNzZXRUb3RhbFVzZG5BbW91bnQJAARMAAAAAgkAAaQAAAABBQAAAA5wb29sQWN0dWFsU2l6ZQkABEwAAAACCQABpAAAAAEFAAAAC3Bvb2xNYXhTaXplCQAETAAAAAIJAAGkAAAAAQUAAAALdWNvbGxhdGVyYWwJAARMAAAAAgkAAaQAAAABBQAAAA1hc3NldEVtaXNzaW9uCQAETAAAAAIJAAGkAAAAAQUAAAAMY3VycmVudFByaWNlCQAETAAAAAIJAAGkAAAAAQUAAAAbcHJpY2VXaXRoT3ZlcmNvbGxhdGVyYWxSYXRlCQAETAAAAAIJAAGkAAAAAQUAAAAScG9vbE1ha2VyTWF4QW1vdW50CQAETAAAAAIJAAGkAAAAAQUAAAAVcG9vbE1ha2VyQWN0dWFsQW1vdW50CQAETAAAAAIJAAGkAAAAAQUAAAAVcG9vbE1ha2VyTG9ja2VkQW1vdW50CQAETAAAAAIJAAGkAAAAAQUAAAAHc3VycGx1cwkABEwAAAACCQABpAAAAAEFAAAAEHBlcmNlbnRWaWFBY3R1YWwJAARMAAAAAgkAAaQAAAABBQAAAA1wZXJjZW50VmlhTWF4CQAETAAAAAIJAAGkAAAAAQUAAAAKZnJvemVuUGFydAkABEwAAAACCQABpAAAAAEFAAAAFWFsbG93ZWRBbW91bnRUb1JlZGVlbQUAAAADbmlsBQAAAANTRVAAAAAHAAAAAWkBAAAABGluaXQAAAAIAAAAF25ldXRyaW5vQ29udHJhY3RBZGRyZXNzAAAAFm5ldXRyaW5vU3Rha2luZ0FkZHJlc3MAAAAYbWluaW11blBvb2xGdW5kaW5nQW1vdW50AAAAE3ByZWRlZmluZWRBc3NldExpc3QAAAARY3VycmVudEFzc2V0SW5kZXgAAAASZGVidEV0aGFsb25CYWxhbmNlAAAAEmRlZm9TdGFraW5nQWRkcmVzcwAAABdkZWZvU3Rha2luZ1BhY2VtYWtlclB1YgMJAQAAAAIhPQAAAAIJAAJYAAAAAQgFAAAAAWkAAAAPY2FsbGVyUHVibGljS2V5BQAAAA5hZG1pblB1YmxpY0tleQkAAAIAAAABAgAAACpPbmx5IEFkbWluIGNhbiBwZXJmb3JtIGNvbnRyYWN0IG9wZXJhdGlvbi4DCQAAAAAAAAIJAQAAAAxnZXRCb29sQnlLZXkAAAABBQAAABBpc0luaXRpYWxpemVkS2V5BgkAAAIAAAABAgAAACFDb250cmFjdCB3YXMgYWxyZWFkeSBpbml0aWFsaXplZC4EAAAACWlzc3VlRGVidAkABEIAAAAFAgAAAAlERUZPX0RFQlQCAAAAEERFRk8gRGVidCBUb2tlbi4AAAAAAAAAAAEAAAAAAAAAAAYGBAAAAAtkZWJ0VG9rZW5JZAkABDgAAAABBQAAAAlpc3N1ZURlYnQJAARMAAAAAgkBAAAAC1N0cmluZ0VudHJ5AAAAAgUAAAAabmV1dHJpbm9Db250cmFjdEFkZHJlc3NLZXkFAAAAF25ldXRyaW5vQ29udHJhY3RBZGRyZXNzCQAETAAAAAIJAQAAAAtTdHJpbmdFbnRyeQAAAAIFAAAAGW5ldXRyaW5vU3Rha2luZ0FkZHJlc3NLZXkFAAAAFm5ldXRyaW5vU3Rha2luZ0FkZHJlc3MJAARMAAAAAgkBAAAADEludGVnZXJFbnRyeQAAAAIFAAAAG21pbmltdW5Qb29sRnVuZGluZ0Ftb3VudEtleQUAAAAYbWluaW11blBvb2xGdW5kaW5nQW1vdW50CQAETAAAAAIJAQAAAAtTdHJpbmdFbnRyeQAAAAIFAAAAFGFzc2V0c0FkZHJlc3NMaXN0S2V5BQAAABNwcmVkZWZpbmVkQXNzZXRMaXN0CQAETAAAAAIJAQAAAAxJbnRlZ2VyRW50cnkAAAACBQAAABRjdXJyZW50QXNzZXRJbmRleEtleQUAAAARY3VycmVudEFzc2V0SW5kZXgJAARMAAAAAgkBAAAADEludGVnZXJFbnRyeQAAAAIFAAAAGWRlYnRUb2tlbkV0YWxvbkJhbGFuY2VLZXkFAAAAEmRlYnRFdGhhbG9uQmFsYW5jZQkABEwAAAACCQEAAAALU3RyaW5nRW50cnkAAAACBQAAAA5kZWJ0VG9rZW5JZEtleQkAAlgAAAABBQAAAAtkZWJ0VG9rZW5JZAkABEwAAAACCQEAAAALU3RyaW5nRW50cnkAAAACBQAAABVkZWZvU3Rha2luZ0FkZHJlc3NLZXkFAAAAEmRlZm9TdGFraW5nQWRkcmVzcwkABEwAAAACCQEAAAALU3RyaW5nRW50cnkAAAACBQAAABpkZWZvU3Rha2luZ1BhY2VtYWtlclB1YktleQUAAAAXZGVmb1N0YWtpbmdQYWNlbWFrZXJQdWIJAARMAAAAAgkBAAAADEJvb2xlYW5FbnRyeQAAAAIFAAAAEGlzSW5pdGlhbGl6ZWRLZXkGCQAETAAAAAIFAAAACWlzc3VlRGVidAkABEwAAAACCQEAAAAEQnVybgAAAAIFAAAAC2RlYnRUb2tlbklkAAAAAAAAAAABBQAAAANuaWwAAAABaQEAAAAMcHJvcG9zZUFzc2V0AAAADwAAAAlhc3NldENvZGUAAAAEbWV0YQAAAAdtaW5Qb29sAAAAEnByaWNlT3JhY2xlQWRkcmVzcwAAABVvdmVyQ29sbGF0ZXJhbFBlcmNlbnQAAAAOYmFja2luZ0Fzc2V0SWQAAAANcHJpY2VEZWNpbWFscwAAAA1taW5CdXlQYXltZW50AAAADm1pblNlbGxQYXltZW50AAAAD2J1eUxvY2tJbnRlcnZhbAAAABBzZWxsTG9ja0ludGVydmFsAAAABmJ1eUZlZQAAAAdzZWxsRmVlAAAAFXBvb2xSZWRlbXB0aW9uVGltZW91dAAAAB53ZWVrZW5kc1ByaWNlQ2hhbmdlQ29lZmZpY2llbnQDCQEAAAACIT0AAAACCQACWAAAAAEIBQAAAAFpAAAAD2NhbGxlclB1YmxpY0tleQUAAAAOYWRtaW5QdWJsaWNLZXkJAAACAAAAAQIAAAAqT25seSBBZG1pbiBjYW4gcGVyZm9ybSBjb250cmFjdCBvcGVyYXRpb24uAwkAAAAAAAACCQEAAAAMZ2V0Qm9vbEJ5S2V5AAAAAQUAAAAQaXNJbml0aWFsaXplZEtleQcJAAACAAAAAQIAAAAaQ29udHJhY3QgaXMgbm90IHlldCByZWFkeS4DCQEAAAACIT0AAAACCQACWQAAAAEFAAAADmJhY2tpbmdBc3NldElkBQAAAA9uZXV0cmlub0Fzc2V0SWQJAAACAAAAAQIAAAAjQmFraW5nIGFzc2V0IGlkIGNvdWxkIGJlIFVTRE4gb25seS4DCQEAAAACIT0AAAACCQEAAAAVZ2V0QXNzZXRBZGRyZXNzQnlDb2RlAAAAAQUAAAAJYXNzZXRDb2RlAgAAAAAJAAACAAAAAQkAASwAAAACCQABLAAAAAICAAAAF0Fzc2V0IHdpdGggcGFzc2VkIGNvZGUgBQAAAAlhc3NldENvZGUCAAAAGyBoYXMgYWxyZWFkeSBiZWVuIHByb3Bvc2VkLgMJAABmAAAAAgUAAAASbWluaW11bVBvb2xGdW5kaW5nBQAAAAdtaW5Qb29sCQAAAgAAAAEJAAEsAAAAAgIAAAAzTWluaW11bSBwb29sIGFtb3VudCBzaG91bGQgYmUgZ3JlYXRlciBvciBlcXVhbHMgdG8gCQABpAAAAAEFAAAAEm1pbmltdW1Qb29sRnVuZGluZwMDAwMDAwMDCQAAZgAAAAIAAAAAAAAAAAAFAAAAFW92ZXJDb2xsYXRlcmFsUGVyY2VudAYJAABmAAAAAgAAAAAAAAAAAAUAAAANcHJpY2VEZWNpbWFscwYJAABmAAAAAgAAAAAAAAAAAAUAAAANbWluQnV5UGF5bWVudAYJAABmAAAAAgAAAAAAAAAAAAUAAAAObWluU2VsbFBheW1lbnQGCQAAZgAAAAIAAAAAAAAAAAAFAAAAD2J1eUxvY2tJbnRlcnZhbAYJAABmAAAAAgAAAAAAAAAAAAUAAAAQc2VsbExvY2tJbnRlcnZhbAYJAABmAAAAAgAAAAAAAAAAAAUAAAAGYnV5RmVlBgkAAGYAAAACAAAAAAAAAAAABQAAAAdzZWxsRmVlCQAAAgAAAAECAAAAKE9uZSBvZiB0aGUgcGFzc2VkIHBhcmFtZXRlcnMgaXMgaW52YWxpZC4EAAAAF3ByaWNlT3JhY2xlQWRkcmVzc1ZhbGlkCQEAAAATdmFsdWVPckVycm9yTWVzc2FnZQAAAAIJAQAAABFAZXh0ck5hdGl2ZSgxMDYyKQAAAAEFAAAAEnByaWNlT3JhY2xlQWRkcmVzcwIAAAApUGFzc2VkIFByaWNlIE9yYWNsZSBBZGRyZXNzIGlzIG5vdCB2YWxpZC4EAAAADGFzc2V0QWRkcmVzcwkAAZEAAAACBQAAABRwcmVkZWZpbmVkQXNzZXRzTGlzdAUAAAARY3VycmVudEFzc2V0SW5kZXgEAAAAC2RlZm9Bc3NldElkCQEAAAATdmFsdWVPckVycm9yTWVzc2FnZQAAAAIJAAQdAAAAAgkBAAAAE3ZhbHVlT3JFcnJvck1lc3NhZ2UAAAACCQAEJgAAAAEFAAAADGFzc2V0QWRkcmVzcwIAAAAABQAAAA5kZWZvQXNzZXRJZEtleQIAAAAXTm8gZGVmbyBhc3NldCBJRCBmb3VuZC4JAARMAAAAAgkBAAAAC1N0cmluZ0VudHJ5AAAAAgkBAAAAEWdldEFzc2V0Q29uZmlnS2V5AAAAAQUAAAAMYXNzZXRBZGRyZXNzCQEAAAAfY29tcG9zZUFzc2V0Q29uZmlndXJhdGlvblN0cmluZwAAABAFAAAACWFzc2V0Q29kZQUAAAALZGVmb0Fzc2V0SWQIBQAAAAthc3NldFN0YXR1cwAAAAJfMQUAAAANcHJpY2VEZWNpbWFscwUAAAAOYmFja2luZ0Fzc2V0SWQFAAAAFW92ZXJDb2xsYXRlcmFsUGVyY2VudAUAAAAHbWluUG9vbAUAAAAScHJpY2VPcmFjbGVBZGRyZXNzBQAAAA1taW5CdXlQYXltZW50BQAAAA5taW5TZWxsUGF5bWVudAUAAAAPYnV5TG9ja0ludGVydmFsBQAAABBzZWxsTG9ja0ludGVydmFsBQAAAAZidXlGZWUFAAAAB3NlbGxGZWUFAAAAFXBvb2xSZWRlbXB0aW9uVGltZW91dAUAAAAed2Vla2VuZHNQcmljZUNoYW5nZUNvZWZmaWNpZW50CQAETAAAAAIJAQAAAAtTdHJpbmdFbnRyeQAAAAIJAQAAABhnZXRBc3NldEFkZHJlc3NCeUNvZGVLZXkAAAABBQAAAAlhc3NldENvZGUFAAAADGFzc2V0QWRkcmVzcwkABEwAAAACCQEAAAALU3RyaW5nRW50cnkAAAACCQEAAAAPZ2V0QXNzZXRNZXRhS2V5AAAAAQUAAAAMYXNzZXRBZGRyZXNzBQAAAARtZXRhCQAETAAAAAIJAQAAAAxJbnRlZ2VyRW50cnkAAAACCQEAAAAVZ2V0QXNzZXRJbml0SGVpZ2h0S2V5AAAAAQUAAAAMYXNzZXRBZGRyZXNzBQAAAAZoZWlnaHQJAARMAAAAAgkBAAAADEludGVnZXJFbnRyeQAAAAIFAAAAFGN1cnJlbnRBc3NldEluZGV4S2V5CQAAZAAAAAIFAAAAEWN1cnJlbnRBc3NldEluZGV4AAAAAAAAAAABCQAETAAAAAIJAQAAAAtTdHJpbmdFbnRyeQAAAAIJAQAAABtnZXRBc3NldEFkZHJlc3NCeUFzc2V0SWRLZXkAAAABBQAAAAtkZWZvQXNzZXRJZAUAAAAMYXNzZXRBZGRyZXNzBQAAAANuaWwAAAABaQEAAAARZnVuZExpcXVpZGl0eVBvb2wAAAABAAAACWFzc2V0Q29kZQQAAAAPYXNzZXRBZGRyZXNzU3RyCQEAAAAbZ2V0QXNzZXRBZGRyZXNzQnlDb2RlT3JGYWlsAAAAAQUAAAAJYXNzZXRDb2RlBAAAABZhc3NldENvbmZpZ3VyYXRpb25MaXN0CQEAAAATZ2V0QXNzZXRDb25maWdBcnJheQAAAAEFAAAAD2Fzc2V0QWRkcmVzc1N0cgQAAAAPYXR0YWNoZWRQYXltZW50CQEAAAAFdmFsdWUAAAABCQABkQAAAAIIBQAAAAFpAAAACHBheW1lbnRzAAAAAAAAAAAABAAAABBwb29sTWFrZXJBZGRyZXNzCQAEJQAAAAEIBQAAAAFpAAAABmNhbGxlcgMJAAAAAAAAAgkBAAAADGdldEJvb2xCeUtleQAAAAEFAAAAEGlzSW5pdGlhbGl6ZWRLZXkHCQAAAgAAAAECAAAAGkNvbnRyYWN0IGlzIG5vdCB5ZXQgcmVhZHkuAwkBAAAAAiE9AAAAAggFAAAAD2F0dGFjaGVkUGF5bWVudAAAAAdhc3NldElkBQAAAA9uZXV0cmlub0Fzc2V0SWQJAAACAAAAAQIAAAAuTGlxdWlkaXR5IHBvb2wgY291bGQgYmUgZnVuZGVkIHdpdGggVVNETiBvbmx5LgMJAABmAAAAAgUAAAATbWluUG9vbEludmVzdEFtb3VudAgFAAAAD2F0dGFjaGVkUGF5bWVudAAAAAZhbW91bnQJAAACAAAAAQkAASwAAAACAgAAADFBdHRhY2hlZCBwYXltZW50IGFtb3VudCBpcyBsZXNzIHRoYSBtaW4gYWxsb3dlZDogCQABpAAAAAEFAAAAE21pblBvb2xJbnZlc3RBbW91bnQEAAAAFG5ld0N1cnJlbnRQb29sQW1vdW50CQAAZAAAAAIIBQAAAA9hdHRhY2hlZFBheW1lbnQAAAAGYW1vdW50CQEAAAAZZ2V0QXNzZXRDdXJyZW50UG9vbEFtb3VudAAAAAEFAAAAD2Fzc2V0QWRkcmVzc1N0cgQAAAANbWF4UG9vbEFtb3VudAkBAAAAFWdldEFzc2V0TWF4UG9vbEFtb3VudAAAAAEFAAAAD2Fzc2V0QWRkcmVzc1N0cgQAAAAXY3VycmVudFBvb2xNYWtlckJhbGFuY2UJAQAAAB9nZXRQb29sTWFrZXJQYXJ0aWNpcGF0aW9uQW1vdW50AAAAAgUAAAAPYXNzZXRBZGRyZXNzU3RyBQAAABBwb29sTWFrZXJBZGRyZXNzBAAAABNtYXhQb29sTWFrZXJCYWxhbmNlCQEAAAAiZ2V0UG9vbE1ha2VyUGFydGljaXBhdGlvbk1heEFtb3VudAAAAAIFAAAAD2Fzc2V0QWRkcmVzc1N0cgUAAAAQcG9vbE1ha2VyQWRkcmVzcwQAAAASY3VycmVudEFzc2V0U3RhdHVzCQEAAAAOZ2V0QXNzZXRTdGF0dXMAAAABBQAAABZhc3NldENvbmZpZ3VyYXRpb25MaXN0BAAAAA5uZXdBc3NldFN0YXR1cwMDCQAAAAAAAAIFAAAAEmN1cnJlbnRBc3NldFN0YXR1cwUAAAANQVNTRXRQUk9QT1NFRAkAAGcAAAACBQAAABRuZXdDdXJyZW50UG9vbEFtb3VudAkBAAAAHWdldEFzc2V0TWluUmVxdWlyZWRQb29sQW1vdW50AAAAAQUAAAAWYXNzZXRDb25maWd1cmF0aW9uTGlzdAcFAAAACkFTU0V0UkVBRFkFAAAAEmN1cnJlbnRBc3NldFN0YXR1cwQAAAATbmV3UG9vbE1ha2VyQmFsYW5jZQkAAGQAAAACBQAAABdjdXJyZW50UG9vbE1ha2VyQmFsYW5jZQgFAAAAD2F0dGFjaGVkUGF5bWVudAAAAAZhbW91bnQEAAAAGWltbWVkaWF0ZVRyYW5zZmVySWZJc3N1ZWQDCQAAAAAAAAIFAAAAEmN1cnJlbnRBc3NldFN0YXR1cwUAAAALQVNTRXRJU1NVRUQJAARMAAAAAgkBAAAADlNjcmlwdFRyYW5zZmVyAAAAAwkBAAAAEUBleHRyTmF0aXZlKDEwNjIpAAAAAQUAAAAPYXNzZXRBZGRyZXNzU3RyCAUAAAAPYXR0YWNoZWRQYXltZW50AAAABmFtb3VudAUAAAAPbmV1dHJpbm9Bc3NldElkBQAAAANuaWwFAAAAA25pbAkABE4AAAACCQAETAAAAAIJAQAAAAxJbnRlZ2VyRW50cnkAAAACCQEAAAAiZ2V0UG9vbE1ha2VyUGFydGljaXBhdGlvbkFtb3VudEtleQAAAAIFAAAAD2Fzc2V0QWRkcmVzc1N0cgUAAAAQcG9vbE1ha2VyQWRkcmVzcwUAAAATbmV3UG9vbE1ha2VyQmFsYW5jZQkABEwAAAACCQEAAAAMSW50ZWdlckVudHJ5AAAAAgkBAAAAJWdldFBvb2xNYWtlclBhcnRpY2lwYXRpb25NYXhBbW91bnRLZXkAAAACBQAAAA9hc3NldEFkZHJlc3NTdHIFAAAAEHBvb2xNYWtlckFkZHJlc3MJAAGWAAAAAQkABEwAAAACBQAAABNuZXdQb29sTWFrZXJCYWxhbmNlCQAETAAAAAIFAAAAE21heFBvb2xNYWtlckJhbGFuY2UFAAAAA25pbAkABEwAAAACCQEAAAAMSW50ZWdlckVudHJ5AAAAAgkBAAAAHWdldEFzc2V0Q3VycnJlbnRQb29sQW1vdW50S2V5AAAAAQUAAAAPYXNzZXRBZGRyZXNzU3RyBQAAABRuZXdDdXJyZW50UG9vbEFtb3VudAkABEwAAAACCQEAAAAMSW50ZWdlckVudHJ5AAAAAgkBAAAAGGdldEFzc2V0TWF4UG9vbEFtb3VudEtleQAAAAEFAAAAD2Fzc2V0QWRkcmVzc1N0cgkAAZYAAAABCQAETAAAAAIFAAAADW1heFBvb2xBbW91bnQJAARMAAAAAgUAAAAUbmV3Q3VycmVudFBvb2xBbW91bnQFAAAAA25pbAkABEwAAAACCQEAAAALU3RyaW5nRW50cnkAAAACCQEAAAARZ2V0QXNzZXRDb25maWdLZXkAAAABBQAAAA9hc3NldEFkZHJlc3NTdHIJAQAAABR1cGRhdGVBc3NldENmZ1N0YXR1cwAAAAIFAAAADm5ld0Fzc2V0U3RhdHVzBQAAABZhc3NldENvbmZpZ3VyYXRpb25MaXN0BQAAAANuaWwFAAAAGWltbWVkaWF0ZVRyYW5zZmVySWZJc3N1ZWQAAAABaQEAAAANYWN0aXZhdGVBc3NldAAAAAEAAAAJYXNzZXRDb2RlBAAAAAxhc3NldEFkZHJlc3MJAQAAABVnZXRBc3NldEFkZHJlc3NCeUNvZGUAAAABBQAAAAlhc3NldENvZGUEAAAAFmFzc2V0Q29uZmlndXJhdGlvbkxpc3QJAQAAABNnZXRBc3NldENvbmZpZ0FycmF5AAAAAQUAAAAMYXNzZXRBZGRyZXNzBAAAABZhc3NldEN1cnJlbnRQb29sQW1vdW50CQEAAAAZZ2V0QXNzZXRDdXJyZW50UG9vbEFtb3VudAAAAAEFAAAADGFzc2V0QWRkcmVzcwQAAAAOYUN1cnJlbnRTdGF0dXMJAQAAAA5nZXRBc3NldFN0YXR1cwAAAAEFAAAAFmFzc2V0Q29uZmlndXJhdGlvbkxpc3QDCQEAAAACIT0AAAACCQACWAAAAAEIBQAAAAFpAAAAD2NhbGxlclB1YmxpY0tleQUAAAAOYWRtaW5QdWJsaWNLZXkJAAACAAAAAQIAAAAqT25seSBBZG1pbiBjYW4gcGVyZm9ybSBjb250cmFjdCBvcGVyYXRpb24uAwkAAAAAAAACCQEAAAAMZ2V0Qm9vbEJ5S2V5AAAAAQUAAAAQaXNJbml0aWFsaXplZEtleQcJAAACAAAAAQIAAAAaQ29udHJhY3QgaXMgbm90IHlldCByZWFkeS4DCQEAAAACIT0AAAACBQAAAA5hQ3VycmVudFN0YXR1cwgFAAAAC2Fzc2V0U3RhdHVzAAAAAl8yCQAAAgAAAAEJAAEsAAAAAgkAASwAAAACAgAAADBBc3NldCBpcyBub3QgaW4gUkVBRFkgc3RhdHVzLiBDdXJyZW50IHN0YXR1cyBpcyAFAAAADmFDdXJyZW50U3RhdHVzAgAAAAEuAwkAAAAAAAACBQAAAAxhc3NldEFkZHJlc3MCAAAAAAkAAAIAAAABCQABLAAAAAIJAAEsAAAAAgIAAAASQ2Fubm90IGZpbmQgYXNzZXQgBQAAAAlhc3NldENvZGUCAAAAAS4DCQAAZgAAAAIFAAAAEm1pbmltdW1Qb29sRnVuZGluZwUAAAAWYXNzZXRDdXJyZW50UG9vbEFtb3VudAkAAAIAAAABCQABLAAAAAIJAAEsAAAAAgkAASwAAAACCQABLAAAAAICAAAALUNhbm5vdCBpc3N1ZSBhc3NldCBhcyBsaXF1aWRpdHkgcG9vbCBhbW91bnQgKAkAAaQAAAABBQAAABZhc3NldEN1cnJlbnRQb29sQW1vdW50AgAAABkpIGlzIGxlc3MgdGhhbiByZXF1aXJlZCAoCQABpAAAAAEFAAAAEm1pbmltdW1Qb29sRnVuZGluZwIAAAABKQQAAAAJaXNzdWVEZWJ0CQEAAAAHUmVpc3N1ZQAAAAMFAAAACWRlYnRUb2tlbgUAAAAWZGVidFRva2VuUmVpc3N1ZUFtb3VudAYJAARMAAAAAgkBAAAADEludGVnZXJFbnRyeQAAAAIJAQAAABlnZXRBc3NldEFjdGl2YXRlSGVpZ2h0S2V5AAAAAQUAAAAMYXNzZXRBZGRyZXNzBQAAAAZoZWlnaHQJAARMAAAAAgkBAAAAC1N0cmluZ0VudHJ5AAAAAgkBAAAAEWdldEFzc2V0Q29uZmlnS2V5AAAAAQUAAAAMYXNzZXRBZGRyZXNzCQEAAAAUdXBkYXRlQXNzZXRDZmdTdGF0dXMAAAACCAUAAAALYXNzZXRTdGF0dXMAAAACXzMFAAAAFmFzc2V0Q29uZmlndXJhdGlvbkxpc3QJAARMAAAAAgUAAAAJaXNzdWVEZWJ0CQAETAAAAAIJAQAAAA5TY3JpcHRUcmFuc2ZlcgAAAAMJAQAAABFAZXh0ck5hdGl2ZSgxMDYyKQAAAAEFAAAADGFzc2V0QWRkcmVzcwUAAAAWYXNzZXRDdXJyZW50UG9vbEFtb3VudAUAAAAPbmV1dHJpbm9Bc3NldElkCQAETAAAAAIJAQAAAA5TY3JpcHRUcmFuc2ZlcgAAAAMJAQAAABFAZXh0ck5hdGl2ZSgxMDYyKQAAAAEFAAAADGFzc2V0QWRkcmVzcwUAAAAWZGVidFRva2VuUmVpc3N1ZUFtb3VudAUAAAAJZGVidFRva2VuBQAAAANuaWwAAAABaQEAAAAOcmVkZWVtRnJvbVBvb2wAAAACAAAACWFzc2V0Q29kZQAAAAZhbW91bnQEAAAADWNhbGxlckFkZHJlc3MJAAQlAAAAAQgFAAAAAWkAAAAGY2FsbGVyBAAAAAtyZWRlZW1UdXBsZQkBAAAAGWNhbGNBbGxvd2VkQW1vdW50VG9SZWRlZW0AAAACBQAAAAlhc3NldENvZGUFAAAADWNhbGxlckFkZHJlc3MEAAAAD2Fzc2V0QWRkcmVzc1N0cggFAAAAC3JlZGVlbVR1cGxlAAAAAl8xBAAAABVhbGxvd2VkQW1vdW50VG9SZWRlZW0IBQAAAAtyZWRlZW1UdXBsZQAAAAJfMgQAAAAPcG9vbFRvdGFsTG9ja2VkCAUAAAALcmVkZWVtVHVwbGUAAAACXzMEAAAADnBvb2xBY3R1YWxTaXplCAUAAAALcmVkZWVtVHVwbGUAAAACXzQEAAAAFXBvb2xNYWtlckFjdHVhbEFtb3VudAgFAAAAC3JlZGVlbVR1cGxlAAAAAl81BAAAAA53aXRoZHJhd0hlaWdodAgFAAAAC3JlZGVlbVR1cGxlAAAAAl82BAAAABVwb29sTWFrZXJMb2NrZWRBbW91bnQIBQAAAAtyZWRlZW1UdXBsZQAAAAJfNwQAAAAJdHJhY2VEYXRhCAUAAAALcmVkZWVtVHVwbGUAAAACXzgEAAAAHHBvb2xNYWtlckxpcXVpZGl0eVJlcXVlc3RLZXkJAQAAAB9nZXRQb29sTWFrZXJMaXF1aWRpdHlSZXF1ZXN0S2V5AAAAAwUAAAAPYXNzZXRBZGRyZXNzU3RyBQAAAA1jYWxsZXJBZGRyZXNzBQAAAA53aXRoZHJhd0hlaWdodAMJAAAAAAAAAgkBAAAADGdldEJvb2xCeUtleQAAAAEFAAAAEGlzSW5pdGlhbGl6ZWRLZXkHCQAAAgAAAAECAAAAGkNvbnRyYWN0IGlzIG5vdCB5ZXQgcmVhZHkuAwMDCQAAZgAAAAIAAAAAAAAAAAAFAAAABmFtb3VudAYJAABnAAAAAgAAAAAAAAAAAAUAAAAVcG9vbE1ha2VyQWN0dWFsQW1vdW50BgkAAGYAAAACBQAAAAZhbW91bnQFAAAAFWFsbG93ZWRBbW91bnRUb1JlZGVlbQkAAAIAAAABCQABLAAAAAIJAAEsAAAAAgIAAAAiQ2Fubm90IGZpbmQgcG9vbCBtYWtlciBieSBhZGRyZXNzIAUAAAANY2FsbGVyQWRkcmVzcwIAAAAhIG9yIGFtb3VudCBwYXNzZWQgaXMgbm90IGNvcnJlY3QuAwkAAGYAAAACCQAAaAAAAAIAAAAAAAAAAAEFAAAADW11bHRpcGxpY2F0b3IFAAAABmFtb3VudAkAAAIAAAABAgAAACZSZWRlZW0gYW1vdW50IGlzIGxlc3MgdGhlbiBtaW4gYWxsb3dlZAMJAABmAAAAAgUAAAAVcG9vbE1ha2VyTG9ja2VkQW1vdW50AAAAAAAAAAAACQAAAgAAAAECAAAARlBvb2wgbWFrZXIgYWxyZWFkeSBoYXMgaW5pdGlhdGVkIHJlZGVtcHRpb24gdGhhdCBpcyBub3Qgd2l0aGRyYXduIHlldC4DCQEAAAAJaXNEZWZpbmVkAAAAAQkABBoAAAACBQAAAAR0aGlzBQAAABxwb29sTWFrZXJMaXF1aWRpdHlSZXF1ZXN0S2V5CQAAAgAAAAEJAAEsAAAAAgkAASwAAAACAgAAADBQb29sIG1ha2VyIGhhcyBhbHJlYWR5IGluaXRpYXRlZCByZWRlbXB0aW9uIGZvciAJAAGkAAAAAQUAAAAGaGVpZ2h0AgAAAAcgaGVpZ2h0CQAETAAAAAIJAQAAAAxJbnRlZ2VyRW50cnkAAAACCQEAAAAbZ2V0UG9vbFRvdGFsTG9ja2VkQW1vdW50S2V5AAAAAQUAAAAPYXNzZXRBZGRyZXNzU3RyCQAAZAAAAAIFAAAAD3Bvb2xUb3RhbExvY2tlZAUAAAAGYW1vdW50CQAETAAAAAIJAQAAAAxJbnRlZ2VyRW50cnkAAAACCQEAAAAdZ2V0QXNzZXRDdXJycmVudFBvb2xBbW91bnRLZXkAAAABBQAAAA9hc3NldEFkZHJlc3NTdHIJAABlAAAAAgUAAAAOcG9vbEFjdHVhbFNpemUFAAAABmFtb3VudAkABEwAAAACCQEAAAAMSW50ZWdlckVudHJ5AAAAAgkBAAAAImdldFBvb2xNYWtlclBhcnRpY2lwYXRpb25BbW91bnRLZXkAAAACBQAAAA9hc3NldEFkZHJlc3NTdHIFAAAADWNhbGxlckFkZHJlc3MJAABlAAAAAgUAAAAVcG9vbE1ha2VyQWN0dWFsQW1vdW50BQAAAAZhbW91bnQJAARMAAAAAgkBAAAADEludGVnZXJFbnRyeQAAAAIFAAAAHHBvb2xNYWtlckxpcXVpZGl0eVJlcXVlc3RLZXkFAAAABmFtb3VudAkABEwAAAACCQEAAAAMSW50ZWdlckVudHJ5AAAAAgkBAAAAG2dldFBvb2xNYWtlckxvY2tlZEFtb3VudEtleQAAAAIFAAAAD2Fzc2V0QWRkcmVzc1N0cgUAAAANY2FsbGVyQWRkcmVzcwUAAAAGYW1vdW50CQAETAAAAAIJAQAAAAxJbnRlZ2VyRW50cnkAAAACCQEAAAAbZ2V0UG9vbE1ha2VyVW5sb2NrSGVpZ2h0S2V5AAAAAgUAAAAPYXNzZXRBZGRyZXNzU3RyBQAAAA1jYWxsZXJBZGRyZXNzBQAAAA53aXRoZHJhd0hlaWdodAkABEwAAAACCQEAAAALU3RyaW5nRW50cnkAAAACAgAAABklcyVzX3RyYWNlX3JlZGVlbUZyb21Qb29sBQAAAAl0cmFjZURhdGEFAAAAA25pbAAAAAFpAQAAABtnZXRNYXhBbGxvd2VkQW1vdW50VG9SZWRlZW0AAAACAAAACWFzc2V0Q29kZQAAABBwb29sTWFrZXJBZGRyZXNzBAAAAAtyZWRlZW1UdXBsZQkBAAAAGWNhbGNBbGxvd2VkQW1vdW50VG9SZWRlZW0AAAACBQAAAAlhc3NldENvZGUFAAAAEHBvb2xNYWtlckFkZHJlc3MEAAAAD2Fzc2V0QWRkcmVzc1N0cggFAAAAC3JlZGVlbVR1cGxlAAAAAl8xBAAAABVhbGxvd2VkQW1vdW50VG9SZWRlZW0IBQAAAAtyZWRlZW1UdXBsZQAAAAJfMgQAAAAPcG9vbFRvdGFsTG9ja2VkCAUAAAALcmVkZWVtVHVwbGUAAAACXzMEAAAADnBvb2xBY3R1YWxTaXplCAUAAAALcmVkZWVtVHVwbGUAAAACXzQEAAAAFXBvb2xNYWtlckFjdHVhbEFtb3VudAgFAAAAC3JlZGVlbVR1cGxlAAAAAl81BAAAAA53aXRoZHJhd0hlaWdodAgFAAAAC3JlZGVlbVR1cGxlAAAAAl82BAAAABVwb29sTWFrZXJMb2NrZWRBbW91bnQIBQAAAAtyZWRlZW1UdXBsZQAAAAJfNwkAAAIAAAABCQABLAAAAAIJAAEsAAAAAgkAASwAAAACCQABLAAAAAIJAAEsAAAAAgkAASwAAAACCQABLAAAAAICAAAAAXsCAAAAF2FsbG93ZWRBbW91bnRUb1JlZGVlbTogCQABpAAAAAEFAAAAFWFsbG93ZWRBbW91bnRUb1JlZGVlbQIAAAANLCBhc3NldENvZGU6IAUAAAAJYXNzZXRDb2RlAgAAAA0sIHBvb2xNYWtlcjogBQAAABBwb29sTWFrZXJBZGRyZXNzAgAAAAF9AAAAAWkBAAAACHdpdGhkcmF3AAAAAQAAAAlhc3NldENvZGUEAAAADWNhbGxlckFkZHJlc3MJAAQlAAAAAQgFAAAAAWkAAAAGY2FsbGVyBAAAAA9hc3NldEFkZHJlc3NTdHIJAQAAABtnZXRBc3NldEFkZHJlc3NCeUNvZGVPckZhaWwAAAABBQAAAAlhc3NldENvZGUEAAAADGFzc2V0QWRkcmVzcwkBAAAAEUBleHRyTmF0aXZlKDEwNjIpAAAAAQUAAAAPYXNzZXRBZGRyZXNzU3RyBAAAABhwb29sTWFrZXJMb2NrZWRBbW91bnRLZXkJAQAAABtnZXRQb29sTWFrZXJMb2NrZWRBbW91bnRLZXkAAAACBQAAAA9hc3NldEFkZHJlc3NTdHIFAAAADWNhbGxlckFkZHJlc3MEAAAAGXJlZ2lzdGVyZWRBbW91bnQ0V2l0aGRyYXcJAQAAAAt2YWx1ZU9yRWxzZQAAAAIJAAQaAAAAAgUAAAAEdGhpcwUAAAAYcG9vbE1ha2VyTG9ja2VkQW1vdW50S2V5AAAAAAAAAAAABAAAABhwb29sTWFrZXJVbmxvY2tIZWlnaHRLZXkJAQAAABtnZXRQb29sTWFrZXJVbmxvY2tIZWlnaHRLZXkAAAACBQAAAA9hc3NldEFkZHJlc3NTdHIFAAAADWNhbGxlckFkZHJlc3MEAAAADndpdGhkcmF3SGVpZ2h0CQEAAAALdmFsdWVPckVsc2UAAAACCQAEGgAAAAIFAAAABHRoaXMFAAAAGHBvb2xNYWtlclVubG9ja0hlaWdodEtleQAAAAAAAAAAAAQAAAAYcG9vbFRvdGFsTG9ja2VkQW1vdW50S2V5CQEAAAAbZ2V0UG9vbFRvdGFsTG9ja2VkQW1vdW50S2V5AAAAAQUAAAAPYXNzZXRBZGRyZXNzU3RyBAAAAA9wb29sVG90YWxMb2NrZWQJAQAAAAt2YWx1ZU9yRWxzZQAAAAIJAAQaAAAAAgUAAAAEdGhpcwUAAAAYcG9vbFRvdGFsTG9ja2VkQW1vdW50S2V5AAAAAAAAAAAABAAAABxwb29sTWFrZXJMaXF1aWRpdHlSZXF1ZXN0S2V5CQEAAAAfZ2V0UG9vbE1ha2VyTGlxdWlkaXR5UmVxdWVzdEtleQAAAAMFAAAAD2Fzc2V0QWRkcmVzc1N0cgUAAAANY2FsbGVyQWRkcmVzcwUAAAAOd2l0aGRyYXdIZWlnaHQDCQAAAAAAAAIJAQAAAAxnZXRCb29sQnlLZXkAAAABBQAAABBpc0luaXRpYWxpemVkS2V5BwkAAAIAAAABAgAAABpDb250cmFjdCBpcyBub3QgeWV0IHJlYWR5LgMJAQAAAAEhAAAAAQkBAAAACWlzRGVmaW5lZAAAAAEJAAQaAAAAAgUAAAAMYXNzZXRBZGRyZXNzBQAAABxwb29sTWFrZXJMaXF1aWRpdHlSZXF1ZXN0S2V5CQAAAgAAAAECAAAAM05lZWQgdG8gdHJhbnNmZXIgbGlxdWlkaXR5IGZyb20gYXNzZXQgYWNjb3VudCBmaXJzdAMJAABnAAAAAgAAAAAAAAAAAAUAAAAZcmVnaXN0ZXJlZEFtb3VudDRXaXRoZHJhdwkAAAIAAAABCQABLAAAAAIJAAEsAAAAAgIAAAAiQ2Fubm90IGZpbmQgcG9vbCBtYWtlciBieSBhZGRyZXNzIAUAAAANY2FsbGVyQWRkcmVzcwIAAAAVIG9yIG5vIGxvY2tlZCBhbW91bnQuAwkAAGYAAAACBQAAAAZoZWlnaHQFAAAADndpdGhkcmF3SGVpZ2h0CQAAAgAAAAEJAAEsAAAAAgkAASwAAAACAgAAAAxQbGVhc2Ugd2FpdCAJAAGkAAAAAQUAAAAOd2l0aGRyYXdIZWlnaHQCAAAAESBiZWZvcmUgd2l0aGRyYXcuCQAETAAAAAIJAQAAAAxJbnRlZ2VyRW50cnkAAAACBQAAABhwb29sTWFrZXJMb2NrZWRBbW91bnRLZXkAAAAAAAAAAAAJAARMAAAAAgkBAAAADEludGVnZXJFbnRyeQAAAAIFAAAAGHBvb2xUb3RhbExvY2tlZEFtb3VudEtleQkAAGUAAAACBQAAAA9wb29sVG90YWxMb2NrZWQFAAAAGXJlZ2lzdGVyZWRBbW91bnQ0V2l0aGRyYXcJAARMAAAAAgkBAAAADEludGVnZXJFbnRyeQAAAAIFAAAAGHBvb2xNYWtlclVubG9ja0hlaWdodEtleQAAAAAAAAAAAAkABEwAAAACCQEAAAAOU2NyaXB0VHJhbnNmZXIAAAADCQEAAAARQGV4dHJOYXRpdmUoMTA2MikAAAABBQAAAA1jYWxsZXJBZGRyZXNzBQAAABlyZWdpc3RlcmVkQW1vdW50NFdpdGhkcmF3BQAAAA9uZXV0cmlub0Fzc2V0SWQFAAAAA25pbAAAAABZPDXA", "chainId": 84, "height": 1248857, "applicationStatus": "succeeded", "spentComplexity": 0 } View: original | compacted Prev: 7PcvzfQ9UnNNZNHjvmxiZshdndexZF2Vt1CERk4o8Vq8 Next: 9MqbrqgMMp8aMDgXAZuv5wYQrBUcLtSd7CJFELKmgLHw Diff:
OldNewDifferences
99
1010 func getBoolByKey (key) = valueOrElse(getBoolean(this, key), false)
1111
12+
13+let SEP = "__"
1214
1315 let keyAssetCode = 1
1416
6466
6567 let defoStakingPacemakerPubKey = "%s%s__commonConfig__defoStakingPacemakerPub"
6668
69+let minPoolInvestAmountKey = "%s%s__commonConfig__minPoolInvestAmount"
70+
6771 let neutrinoAssetIdKey = "neutrino_asset_id"
6872
6973 let nsbtAssetIdKey = "bond_asset_id"
74+
75+let neutrinoRpdKey = "rpd_contract"
7076
7177 let defoAssetIdKey = "%s__defoAssetId"
7278
102108 func getAssetAddressByAssetIdKey (assetId) = (("%s%s%s__defoAsset__" + assetId) + "__addressByAssetId")
103109
104110
105-func getPoolMakerParticipationAmountKey (assetAddress,poolMakerAddress) = (((("%s%s%s%s__pool__" + assetAddress) + "__") + poolMakerAddress) + "__amount")
111+func getPoolMakerParticipationAmountKey (assetAddress,poolMakerAddress) = makeString(["%s%s%s%s__pool", assetAddress, poolMakerAddress, "amount"], SEP)
106112
107113
108-func getPoolMakerParticipationMaxAmountKey (assetAddress,poolMakerAddress) = (((("%s%s%s%s__pool__" + assetAddress) + "__") + poolMakerAddress) + "__maxAmount")
114+func getPoolMakerParticipationMaxAmountKey (assetAddress,poolMakerAddress) = makeString(["%s%s%s%s__pool", assetAddress, poolMakerAddress, "maxAmount"], SEP)
109115
110116
111-func getPoolMakerLockedAmountKey (assetAddress,poolMakerAddress) = (((("%s%s%s%s__pool__" + assetAddress) + "__") + poolMakerAddress) + "__lockedAmount")
117+func getPoolMakerLockedAmountKey (assetAddress,poolMakerAddress) = makeString(["%s%s%s%s__pool", assetAddress, poolMakerAddress, "lockedAmount"], SEP)
112118
113119
114-func getPoolMakerUnlockHeightKey (assetAddress,poolMakerAddress) = (((("%s%s%s%s__pool__" + assetAddress) + "__") + poolMakerAddress) + "__unlockHeight")
120+func getPoolMakerUnlockHeightKey (assetAddress,poolMakerAddress) = makeString(["%s%s%s%s__pool", assetAddress, poolMakerAddress, "unlockHeight"], SEP)
121+
122+
123+func getPoolMakerLiquidityRequestKey (assetAddress,poolMakerAddress,h) = makeString(["%s%s%s%d%s__pool", assetAddress, poolMakerAddress, toString(h), "liquidityRequest"], SEP)
124+
125+
126+func getPoolTotalLockedAmountKey (assetAddress) = makeString(["%s%s%s__pool", assetAddress, "totalLockedAmount"], SEP)
115127
116128
117129 func getAssetConfigArray (assetAddress) = convertJsonArrayToList(valueOrErrorMessage(getStringByAddressAndKey(this, getAssetConfigKey(assetAddress)), (("No DEFO Asset " + assetAddress) + "config present")), "__")
118130
119131
132+let multiplicator = 1000000
133+
120134 let neutrinoContract = addressFromStringValue(valueOrErrorMessage(getString(this, neutrinoContractAddressKey), "No neutrino contract specified."))
121135
122-let neutrinoAssetId = fromBase58String(valueOrErrorMessage(getString(neutrinoContract, neutrinoAssetIdKey), "No USDN asset id found."))
136+let neutrinoAssetIdStr = valueOrErrorMessage(getString(neutrinoContract, neutrinoAssetIdKey), "No USDN asset id found.")
137+
138+let neutrinoAssetId = fromBase58String(neutrinoAssetIdStr)
123139
124140 let nsbtAssetId = fromBase58String(valueOrErrorMessage(getString(neutrinoContract, nsbtAssetIdKey), "No NSBT asset id found"))
125141
126142 let minimumPoolFunding = valueOrErrorMessage(getInteger(this, minimunPoolFundingAmountKey), "Minimum pool funding amount is not specified.")
127143
128-let assetStatus = $Tuple3("PROPOSED", "READY", "ISSUED")
144+let minPoolInvestAmount = valueOrElse(getInteger(this, minPoolInvestAmountKey), (10 * multiplicator))
145+
146+let ASSEtPROPOSED = "PROPOSED"
147+
148+let ASSEtREADY = "READY"
149+
150+let ASSEtISSUED = "ISSUED"
151+
152+let assetStatus = $Tuple3(ASSEtPROPOSED, ASSEtREADY, ASSEtISSUED)
129153
130154 let predefinedAssetContracts = valueOrErrorMessage(getString(this, assetsAddressListKey), "No pool of predeployed asset contracts found.")
131155
135159
136160 let debtToken = fromBase58String(valueOrErrorMessage(getString(this, debtTokenIdKey), "No debt token found"))
137161
138-let multiplicator = 1000000
139-
140162 let debtTokenReissueAmount = valueOrElse(getInteger(this, debtTokenEtalonBalanceKey), 0)
141163
164+let neutrinoRpdContract = addressFromStringValue(getStringValue(neutrinoContract, neutrinoRpdKey))
165+
166+func getNeutrinoRpdBalance (userAddress) = {
167+ let userNeutrinoRpdBalance = makeString(["rpd_balance", neutrinoAssetIdStr, userAddress], "_")
168+ valueOrElse(getInteger(neutrinoRpdContract, userNeutrinoRpdBalance), 0)
169+ }
170+
171+
142172 func getAssetAddressByCode (assetSymbol) = valueOrElse(getString(this, getAssetAddressByCodeKey(assetSymbol)), "")
173+
174+
175+func getAssetAddressByCodeOrFail (assetSymbol) = valueOrErrorMessage(getString(this, getAssetAddressByCodeKey(assetSymbol)), (("Cannot find asset address by " + assetSymbol) + " code"))
143176
144177
145178 func getAssetCode (assetConfig) = assetConfig[keyAssetCode]
223256 func getAssetPrice (priceOracleAddress,assetCode) = valueOrErrorMessage(getInteger(priceOracleAddress, getAssetPriceKey(assetCode)), "No Asset price found")
224257
225258
259+func getPoolTotalLockedAmount (assetAddress) = valueOrElse(getInteger(this, getPoolTotalLockedAmountKey(assetAddress)), 0)
260+
261+
226262 func composeAssetConfigurationString (assetCode,assetId,assetStatus,priceDecimals,backingAssetId,overCollateralPercent,minPool,priceOracleAddress,minBuyPayment,minSellPayment,buyLockInterval,sellLockInterval,buyFee,sellFee,poolRedemptionTimeout,weekendsPriceChangeCoefficient) = makeString(["%s%s%s%d%s%d%d%s%d%d%d%d%d%d%d", assetCode, assetId, assetStatus, toString(priceDecimals), backingAssetId, toString(overCollateralPercent), toString(minPool), priceOracleAddress, toString(minBuyPayment), toString(minSellPayment), toString(buyLockInterval), toString(sellLockInterval), toString(buyFee), toString(sellFee), toString(poolRedemptionTimeout), toString(weekendsPriceChangeCoefficient)], "__")
227263
228264
243279 let aRedemptionTimeoutBlocks = getRedemptionTimeoutBlocks(assetConfigurationList)
244280 let weekendsPriceChangeCoefficient = getWeekendsPriceChangeCoefficient(assetConfigurationList)
245281 composeAssetConfigurationString(aCode, aID, newStatus, aPriceDecimals, aBakingAssetId, aOverCollateralPercent, aMinRequiredPool, aPriceOracleAddress, aMinBuyPayment, aMinSellPayment, aBuyLockInterval, aSellLockInterval, aBuyFee, aSellFee, aRedemptionTimeoutBlocks, weekendsPriceChangeCoefficient)
282+ }
283+
284+
285+func calcAllowedAmountToRedeem (assetCode,poolMakerAddress) = {
286+ let assetAddressStr = getAssetAddressByCodeOrFail(assetCode)
287+ let assetAddress = addressFromStringValue(assetAddressStr)
288+ let assetConfigurationList = getAssetConfigArray(assetAddressStr)
289+ let assetIdStr = getAssetID(assetConfigurationList)
290+ let assetId = fromBase58String(assetIdStr)
291+ let poolActualSize = valueOrElse(getInteger(this, getAssetCurrrentPoolAmountKey(assetAddressStr)), 0)
292+ let poolMaxSize = valueOrElse(getInteger(this, getAssetMaxPoolAmountKey(assetAddressStr)), 0)
293+ let poolTotalLockedAmountKey = getPoolTotalLockedAmountKey(assetAddressStr)
294+ let poolTotalLocked = valueOrElse(getInteger(this, poolTotalLockedAmountKey), 0)
295+ let assetUsdnStakedAmount = getNeutrinoRpdBalance(assetAddressStr)
296+ let assetUsdnBalanceAmount = assetBalance(assetAddress, neutrinoAssetId)
297+ let assetTotalUsdnAmount = ((assetUsdnStakedAmount + assetUsdnBalanceAmount) - poolTotalLocked)
298+ let ucollateral = (assetTotalUsdnAmount - poolActualSize)
299+ let defoAssetBalance = assetBalance(assetAddress, assetId)
300+ let assetEmissionDirty = valueOrErrorMessage(assetInfo(assetId), ("Cannot find asset by id " + assetIdStr)).quantity
301+ let assetEmission = (assetEmissionDirty - defoAssetBalance)
302+ let priceOracleAddress = addressFromStringValue(getAssetPriceOracleAddress(assetConfigurationList))
303+ let currentPrice = getAssetPrice(priceOracleAddress, assetCode)
304+ let priceWithOvercollateralRate = (((multiplicator + getAssetOverCollateralPercent(assetConfigurationList)) * currentPrice) / multiplicator)
305+ let usdnNeededToCoverEmission = (priceWithOvercollateralRate * assetEmission)
306+ let poolMakerMaxAmount = getPoolMakerParticipationMaxAmount(assetAddressStr, poolMakerAddress)
307+ let poolMakerParticipationAmountKey = getPoolMakerParticipationAmountKey(assetAddressStr, poolMakerAddress)
308+ let poolMakerActualAmount = valueOrElse(getInteger(this, poolMakerParticipationAmountKey), 0)
309+ let poolMakerLockedAmount = getPoolMakerLockedAmount(assetAddressStr, poolMakerAddress)
310+ let surplus = (ucollateral - usdnNeededToCoverEmission)
311+ let percentViaActual = fraction(poolMakerActualAmount, multiplicator, poolActualSize)
312+ let percentViaMax = fraction(poolMakerMaxAmount, multiplicator, poolMaxSize)
313+ let frozenPart = if ((surplus >= 0))
314+ then 0
315+ else max([percentViaActual, percentViaMax])
316+ let allowedAmountToRedeem = fraction((multiplicator - frozenPart), poolMakerActualAmount, multiplicator)
317+ let withdrawHeight = (height + getRedemptionTimeoutBlocks(assetConfigurationList))
318+ $Tuple8(assetAddressStr, allowedAmountToRedeem, poolTotalLocked, poolActualSize, poolMakerActualAmount, withdrawHeight, poolMakerLockedAmount, makeString([assetAddressStr, assetIdStr, poolMakerAddress, toString(assetUsdnStakedAmount), toString(assetUsdnBalanceAmount), toString(assetTotalUsdnAmount), toString(poolActualSize), toString(poolMaxSize), toString(ucollateral), toString(assetEmission), toString(currentPrice), toString(priceWithOvercollateralRate), toString(poolMakerMaxAmount), toString(poolMakerActualAmount), toString(poolMakerLockedAmount), toString(surplus), toString(percentViaActual), toString(percentViaMax), toString(frozenPart), toString(allowedAmountToRedeem)], SEP))
246319 }
247320
248321
297370
298371 @Callable(i)
299372 func fundLiquidityPool (assetCode) = {
300- let assetAddress = getAssetAddressByCode(assetCode)
301- let assetConfigurationList = getAssetConfigArray(assetAddress)
373+ let assetAddressStr = getAssetAddressByCodeOrFail(assetCode)
374+ let assetConfigurationList = getAssetConfigArray(assetAddressStr)
302375 let attachedPayment = value(i.payments[0])
303- if ((toBase58String(i.callerPublicKey) != adminPublicKey))
304- then throw("Only Admin can perform contract operation.")
305- else if ((getBoolByKey(isInitializedKey) == false))
306- then throw("Contract is not yet ready.")
307- else if ((attachedPayment.assetId != neutrinoAssetId))
308- then throw("Liquidity pool could be funded with USDN only.")
309- else if ((attachedPayment.amount == 0))
310- then throw("Incorrect attached payment amount.")
311- else if ((assetAddress == ""))
312- then throw((("Cannot find liquidity pool for asset " + assetCode) + "."))
313- else {
314- let finalPoolAmount = (attachedPayment.amount + getAssetCurrentPoolAmount(assetAddress))
315- let maxPoolAmount = getAssetMaxPoolAmount(assetAddress)
316- let poolMakerAddress = toString(i.caller)
317- let currentPoolMakerBalance = getPoolMakerParticipationAmount(assetAddress, poolMakerAddress)
318- let maxPoolMakerBalance = getPoolMakerParticipationMaxAmount(assetAddress, poolMakerAddress)
319- let currentPoolStatus = getAssetStatus(assetConfigurationList)
320- let finalPoolStatus = if (if ((finalPoolAmount >= getAssetMinRequiredPoolAmount(assetConfigurationList)))
321- then (currentPoolStatus == assetStatus._1)
322- else false)
323- then assetStatus._2
324- else currentPoolStatus
325- let currentPoolMakers = getAssetPoolMakers(assetAddress)
326- let newPoolMakers = if ((currentPoolMakers == ""))
327- then poolMakerAddress
328- else if (contains(currentPoolMakers, poolMakerAddress))
329- then currentPoolMakers
330- else ((currentPoolMakers + ",") + toString(i.caller))
331- let newPoolMakerBalance = (currentPoolMakerBalance + i.payments[0].amount)
332- let result = [IntegerEntry(getPoolMakerParticipationAmountKey(assetAddress, toString(i.caller)), newPoolMakerBalance), IntegerEntry(getPoolMakerParticipationMaxAmountKey(assetAddress, toString(i.caller)), if ((newPoolMakerBalance > maxPoolMakerBalance))
333- then newPoolMakerBalance
334- else maxPoolMakerBalance), IntegerEntry(getAssetCurrrentPoolAmountKey(assetAddress), finalPoolAmount), IntegerEntry(getAssetMaxPoolAmountKey(assetAddress), if ((finalPoolAmount > maxPoolAmount))
335- then finalPoolAmount
336- else maxPoolAmount), StringEntry(getAssetPoolMakersKey(assetAddress), newPoolMakers), StringEntry(getAssetConfigKey(assetAddress), updateAssetCfgStatus(finalPoolStatus, assetConfigurationList))]
337- if ((currentPoolStatus == assetStatus._3))
338- then (result :+ ScriptTransfer(addressFromStringValue(assetAddress), attachedPayment.amount, neutrinoAssetId))
339- else result
340- }
376+ let poolMakerAddress = toString(i.caller)
377+ if ((getBoolByKey(isInitializedKey) == false))
378+ then throw("Contract is not yet ready.")
379+ else if ((attachedPayment.assetId != neutrinoAssetId))
380+ then throw("Liquidity pool could be funded with USDN only.")
381+ else if ((minPoolInvestAmount > attachedPayment.amount))
382+ then throw(("Attached payment amount is less tha min allowed: " + toString(minPoolInvestAmount)))
383+ else {
384+ let newCurrentPoolAmount = (attachedPayment.amount + getAssetCurrentPoolAmount(assetAddressStr))
385+ let maxPoolAmount = getAssetMaxPoolAmount(assetAddressStr)
386+ let currentPoolMakerBalance = getPoolMakerParticipationAmount(assetAddressStr, poolMakerAddress)
387+ let maxPoolMakerBalance = getPoolMakerParticipationMaxAmount(assetAddressStr, poolMakerAddress)
388+ let currentAssetStatus = getAssetStatus(assetConfigurationList)
389+ let newAssetStatus = if (if ((currentAssetStatus == ASSEtPROPOSED))
390+ then (newCurrentPoolAmount >= getAssetMinRequiredPoolAmount(assetConfigurationList))
391+ else false)
392+ then ASSEtREADY
393+ else currentAssetStatus
394+ let newPoolMakerBalance = (currentPoolMakerBalance + attachedPayment.amount)
395+ let immediateTransferIfIssued = if ((currentAssetStatus == ASSEtISSUED))
396+ then [ScriptTransfer(addressFromStringValue(assetAddressStr), attachedPayment.amount, neutrinoAssetId)]
397+ else nil
398+ ([IntegerEntry(getPoolMakerParticipationAmountKey(assetAddressStr, poolMakerAddress), newPoolMakerBalance), IntegerEntry(getPoolMakerParticipationMaxAmountKey(assetAddressStr, poolMakerAddress), max([newPoolMakerBalance, maxPoolMakerBalance])), IntegerEntry(getAssetCurrrentPoolAmountKey(assetAddressStr), newCurrentPoolAmount), IntegerEntry(getAssetMaxPoolAmountKey(assetAddressStr), max([maxPoolAmount, newCurrentPoolAmount])), StringEntry(getAssetConfigKey(assetAddressStr), updateAssetCfgStatus(newAssetStatus, assetConfigurationList))] ++ immediateTransferIfIssued)
399+ }
341400 }
342401
343402
368427
369428 @Callable(i)
370429 func redeemFromPool (assetCode,amount) = {
371- let assetAddress = getAssetAddressByCode(assetCode)
372- let assetConfigurationList = getAssetConfigArray(assetAddress)
373430 let callerAddress = toString(i.caller)
374- let totalAmountInPoolByCaller = getPoolMakerParticipationAmount(assetAddress, callerAddress)
375- let lockedAmountByPoolMaker = getPoolMakerLockedAmount(assetAddress, callerAddress)
376- if ((toBase58String(i.callerPublicKey) != adminPublicKey))
377- then throw("Only Admin can perform contract operation.")
378- else if ((getBoolByKey(isInitializedKey) == false))
379- then throw("Contract is not yet ready.")
380- else if ((assetAddress == ""))
381- then throw((("Cannot find asset " + assetCode) + "."))
382- else if (if (if ((0 > amount))
383- then true
384- else (totalAmountInPoolByCaller == 0))
385- then true
386- else (amount > totalAmountInPoolByCaller))
387- then throw((("Cannot find pool maker by address " + callerAddress) + " or amount passed is not correct."))
388- else if ((lockedAmountByPoolMaker > 0))
389- then throw("Pool maker already has initiated redemption that is not withdrawn yet.")
390- else [IntegerEntry(getPoolMakerLockedAmountKey(assetAddress, callerAddress), amount), IntegerEntry(getPoolMakerUnlockHeightKey(assetAddress, callerAddress), (height + getRedemptionTimeoutBlocks(assetConfigurationList)))]
431+ let redeemTuple = calcAllowedAmountToRedeem(assetCode, callerAddress)
432+ let assetAddressStr = redeemTuple._1
433+ let allowedAmountToRedeem = redeemTuple._2
434+ let poolTotalLocked = redeemTuple._3
435+ let poolActualSize = redeemTuple._4
436+ let poolMakerActualAmount = redeemTuple._5
437+ let withdrawHeight = redeemTuple._6
438+ let poolMakerLockedAmount = redeemTuple._7
439+ let traceData = redeemTuple._8
440+ let poolMakerLiquidityRequestKey = getPoolMakerLiquidityRequestKey(assetAddressStr, callerAddress, withdrawHeight)
441+ if ((getBoolByKey(isInitializedKey) == false))
442+ then throw("Contract is not yet ready.")
443+ else if (if (if ((0 > amount))
444+ then true
445+ else (0 >= poolMakerActualAmount))
446+ then true
447+ else (amount > allowedAmountToRedeem))
448+ then throw((("Cannot find pool maker by address " + callerAddress) + " or amount passed is not correct."))
449+ else if (((1 * multiplicator) > amount))
450+ then throw("Redeem amount is less then min allowed")
451+ else if ((poolMakerLockedAmount > 0))
452+ then throw("Pool maker already has initiated redemption that is not withdrawn yet.")
453+ else if (isDefined(getInteger(this, poolMakerLiquidityRequestKey)))
454+ then throw((("Pool maker has already initiated redemption for " + toString(height)) + " height"))
455+ else [IntegerEntry(getPoolTotalLockedAmountKey(assetAddressStr), (poolTotalLocked + amount)), IntegerEntry(getAssetCurrrentPoolAmountKey(assetAddressStr), (poolActualSize - amount)), IntegerEntry(getPoolMakerParticipationAmountKey(assetAddressStr, callerAddress), (poolMakerActualAmount - amount)), IntegerEntry(poolMakerLiquidityRequestKey, amount), IntegerEntry(getPoolMakerLockedAmountKey(assetAddressStr, callerAddress), amount), IntegerEntry(getPoolMakerUnlockHeightKey(assetAddressStr, callerAddress), withdrawHeight), StringEntry("%s%s_trace_redeemFromPool", traceData)]
391456 }
392457
393458
394459
395460 @Callable(i)
396-func getMaxAllowedAmountToRedeem (assetCode,poolMaker) = throw((((("{allowedAmountToRedeem: 1000, assetCode: " + assetCode) + ", poolMaker: ") + poolMaker) + "}"))
461+func getMaxAllowedAmountToRedeem (assetCode,poolMakerAddress) = {
462+ let redeemTuple = calcAllowedAmountToRedeem(assetCode, poolMakerAddress)
463+ let assetAddressStr = redeemTuple._1
464+ let allowedAmountToRedeem = redeemTuple._2
465+ let poolTotalLocked = redeemTuple._3
466+ let poolActualSize = redeemTuple._4
467+ let poolMakerActualAmount = redeemTuple._5
468+ let withdrawHeight = redeemTuple._6
469+ let poolMakerLockedAmount = redeemTuple._7
470+ throw(((((((("{" + "allowedAmountToRedeem: ") + toString(allowedAmountToRedeem)) + ", assetCode: ") + assetCode) + ", poolMaker: ") + poolMakerAddress) + "}"))
471+ }
397472
398473
399474
400475 @Callable(i)
401476 func withdraw (assetCode) = {
402- let assetConfigurationList = getAssetConfigArray(assetCode)
403- let assetAddress = getAssetAddressByCode(assetCode)
404- let priceOracleAddress = addressFromStringValue(getAssetPriceOracleAddress(assetConfigurationList))
405477 let callerAddress = toString(i.caller)
406- let syntheticAssetInfo = valueOrErrorMessage(assetInfo(fromBase58String(assetAddress)), ("Cannot find asset by id " + assetAddress))
407- let totalUSDNBalanceOnContract = assetBalance(this, neutrinoAssetId)
408- let totalAmountInPoolByPoolMaker = getPoolMakerParticipationAmount(assetAddress, callerAddress)
409- let lockedAmountByPoolMaker = getPoolMakerLockedAmount(assetAddress, callerAddress)
410- let minWithdrawHeight = getPoolMakerUnlockHeight(assetAddress, callerAddress)
411- let poolMaketProportion = (getAssetMaxPoolAmount(assetAddress) / getPoolMakerParticipationMaxAmount(assetAddress, callerAddress))
412- if ((toBase58String(i.callerPublicKey) != adminPublicKey))
413- then throw("Only Admin can perform contract operation.")
414- else if ((getBoolByKey(isInitializedKey) == false))
415- then throw("Contract is not yet ready.")
416- else if ((getBoolByKey(isInitializedKey) == false))
417- then throw("Contract is not yet ready.")
418- else if ((assetAddress == ""))
419- then throw((("Cannot find asset " + assetCode) + "."))
420- else if ((totalAmountInPoolByPoolMaker == 0))
421- then throw((("Cannot find pool maker by address " + callerAddress) + " or no locked amount."))
422- else if ((height > minWithdrawHeight))
423- then throw((("Please wait " + toString(minWithdrawHeight)) + " before withdraw."))
424- else {
425- let assetEmission = syntheticAssetInfo.quantity
426- let currentPrice = getAssetPrice(priceOracleAddress, assetCode)
427- let poolMakerLockedAmount = getPoolMakerLockedAmount(assetAddress, callerAddress)
428- let priceWithOvercollateralRate = (getAssetOverCollateralPercent(assetConfigurationList) * currentPrice)
429- let usdnNeededToCoverEmission = (priceWithOvercollateralRate * assetEmission)
430- if ((totalUSDNBalanceOnContract > usdnNeededToCoverEmission))
431- then {
432- let amoutAvailableToExitFromPool = (totalUSDNBalanceOnContract - usdnNeededToCoverEmission)
433- let amountForWithdraw = (poolMaketProportion * amoutAvailableToExitFromPool)
434- let finalLockedAmount = (getPoolMakerLockedAmount(assetAddress, callerAddress) - amountForWithdraw)
435-[IntegerEntry(getAssetCurrrentPoolAmountKey(assetAddress), (getAssetCurrentPoolAmount(assetAddress) - amountForWithdraw)), IntegerEntry(getPoolMakerParticipationAmountKey(assetAddress, callerAddress), (getPoolMakerParticipationAmount(assetAddress, callerAddress) - amountForWithdraw)), IntegerEntry(getPoolMakerLockedAmountKey(assetAddress, callerAddress), finalLockedAmount), ScriptTransfer(addressFromStringValue(callerAddress), amountForWithdraw, neutrinoAssetId)]
436- }
437- else nil
438- }
478+ let assetAddressStr = getAssetAddressByCodeOrFail(assetCode)
479+ let assetAddress = addressFromStringValue(assetAddressStr)
480+ let poolMakerLockedAmountKey = getPoolMakerLockedAmountKey(assetAddressStr, callerAddress)
481+ let registeredAmount4Withdraw = valueOrElse(getInteger(this, poolMakerLockedAmountKey), 0)
482+ let poolMakerUnlockHeightKey = getPoolMakerUnlockHeightKey(assetAddressStr, callerAddress)
483+ let withdrawHeight = valueOrElse(getInteger(this, poolMakerUnlockHeightKey), 0)
484+ let poolTotalLockedAmountKey = getPoolTotalLockedAmountKey(assetAddressStr)
485+ let poolTotalLocked = valueOrElse(getInteger(this, poolTotalLockedAmountKey), 0)
486+ let poolMakerLiquidityRequestKey = getPoolMakerLiquidityRequestKey(assetAddressStr, callerAddress, withdrawHeight)
487+ if ((getBoolByKey(isInitializedKey) == false))
488+ then throw("Contract is not yet ready.")
489+ else if (!(isDefined(getInteger(assetAddress, poolMakerLiquidityRequestKey))))
490+ then throw("Need to transfer liquidity from asset account first")
491+ else if ((0 >= registeredAmount4Withdraw))
492+ then throw((("Cannot find pool maker by address " + callerAddress) + " or no locked amount."))
493+ else if ((height > withdrawHeight))
494+ then throw((("Please wait " + toString(withdrawHeight)) + " before withdraw."))
495+ else [IntegerEntry(poolMakerLockedAmountKey, 0), IntegerEntry(poolTotalLockedAmountKey, (poolTotalLocked - registeredAmount4Withdraw)), IntegerEntry(poolMakerUnlockHeightKey, 0), ScriptTransfer(addressFromStringValue(callerAddress), registeredAmount4Withdraw, neutrinoAssetId)]
439496 }
440497
441498
Full:
OldNewDifferences
11 {-# STDLIB_VERSION 4 #-}
22 {-# SCRIPT_TYPE ACCOUNT #-}
33 {-# CONTENT_TYPE DAPP #-}
44 func convertJsonArrayToList (jsonArray,splitSymbol) = split(jsonArray, splitSymbol)
55
66
77 func getStringByAddressAndKey (address,key) = valueOrElse(getString(address, key), "")
88
99
1010 func getBoolByKey (key) = valueOrElse(getBoolean(this, key), false)
1111
12+
13+let SEP = "__"
1214
1315 let keyAssetCode = 1
1416
1517 let keyAssetId = 2
1618
1719 let keyAssetStatus = 3
1820
1921 let keyAssetPriceDecimals = 4
2022
2123 let keyAssetBakingAsset = 5
2224
2325 let keyOvercollateralPercentage = 6
2426
2527 let keyMinPool = 7
2628
2729 let keyPriceOracleAddress = 8
2830
2931 let keyMinBuyPayment = 9
3032
3133 let keyMinSellPayment = 10
3234
3335 let keyBuyLockInterval = 11
3436
3537 let keySellLockInterval = 12
3638
3739 let keyBuyFee = 13
3840
3941 let keySellFee = 14
4042
4143 let keyPoolRedemptionTimeout = 15
4244
4345 let keyWeekendsPriceChangeCoefficient = 16
4446
4547 let adminPublicKey = "G1Ebu5AMHaTJFEbZNkZQcJFUQ9KfiYUwupyLG857DcFr"
4648
4749 let neutrinoContractAddressKey = "%s%s__commonConfig__neutrinoContractAddress"
4850
4951 let neutrinoStakingAddressKey = "%s%s__commonConfig__neutrinoStakingAddress"
5052
5153 let minimunPoolFundingAmountKey = "%s%s__commonConfig__minimumPoolFundingAmount"
5254
5355 let isInitializedKey = "%s%s__commonConfig__initialized"
5456
5557 let currentAssetIndexKey = "%s%s__commonConfig__predefinedAssetsActiveIndex"
5658
5759 let assetsAddressListKey = "%s%s__commonConfig__predefinedAssetsList"
5860
5961 let debtTokenIdKey = "%s%s__commonConfig__debtAssetId"
6062
6163 let debtTokenEtalonBalanceKey = "%s%s__commonConfig__debtAssetEtalonBalance"
6264
6365 let defoStakingAddressKey = "%s%s__commonConfig__defoStakingAddress"
6466
6567 let defoStakingPacemakerPubKey = "%s%s__commonConfig__defoStakingPacemakerPub"
6668
69+let minPoolInvestAmountKey = "%s%s__commonConfig__minPoolInvestAmount"
70+
6771 let neutrinoAssetIdKey = "neutrino_asset_id"
6872
6973 let nsbtAssetIdKey = "bond_asset_id"
74+
75+let neutrinoRpdKey = "rpd_contract"
7076
7177 let defoAssetIdKey = "%s__defoAssetId"
7278
7379 let setAssetScriptTxIdKey = "%s__assetScriptTxId"
7480
7581 func getAssetConfigKey (assetAddress) = (("%s%s%s__defoAsset__" + assetAddress) + "__config")
7682
7783
7884 func getAssetPriceKey (assetCode) = ("%s%s__assetPrice__" + assetCode)
7985
8086
8187 func getAssetInitHeightKey (assetAddress) = (("%s%s%s__defoAsset__" + assetAddress) + "__initHeight")
8288
8389
8490 func getAssetActivateHeightKey (assetAddress) = (("%s%s%s__defoAsset__" + assetAddress) + "__activateHeight")
8591
8692
8793 func getAssetCurrrentPoolAmountKey (assetAddress) = (("%s%s%s__defoAsset__" + assetAddress) + "__currentPool")
8894
8995
9096 func getAssetMaxPoolAmountKey (assetAddress) = (("%s%s%s__defoAsset__" + assetAddress) + "__maxPool")
9197
9298
9399 func getAssetMetaKey (assetAddress) = (("%s%s%s__defoAsset__" + assetAddress) + "__meta")
94100
95101
96102 func getAssetPoolMakersKey (assetAddress) = (("%s%s%s__defoAsset__" + assetAddress) + "__poolMakers")
97103
98104
99105 func getAssetAddressByCodeKey (assetCode) = (("%s%s%s__defoAsset__" + assetCode) + "__addressByAssetCode")
100106
101107
102108 func getAssetAddressByAssetIdKey (assetId) = (("%s%s%s__defoAsset__" + assetId) + "__addressByAssetId")
103109
104110
105-func getPoolMakerParticipationAmountKey (assetAddress,poolMakerAddress) = (((("%s%s%s%s__pool__" + assetAddress) + "__") + poolMakerAddress) + "__amount")
111+func getPoolMakerParticipationAmountKey (assetAddress,poolMakerAddress) = makeString(["%s%s%s%s__pool", assetAddress, poolMakerAddress, "amount"], SEP)
106112
107113
108-func getPoolMakerParticipationMaxAmountKey (assetAddress,poolMakerAddress) = (((("%s%s%s%s__pool__" + assetAddress) + "__") + poolMakerAddress) + "__maxAmount")
114+func getPoolMakerParticipationMaxAmountKey (assetAddress,poolMakerAddress) = makeString(["%s%s%s%s__pool", assetAddress, poolMakerAddress, "maxAmount"], SEP)
109115
110116
111-func getPoolMakerLockedAmountKey (assetAddress,poolMakerAddress) = (((("%s%s%s%s__pool__" + assetAddress) + "__") + poolMakerAddress) + "__lockedAmount")
117+func getPoolMakerLockedAmountKey (assetAddress,poolMakerAddress) = makeString(["%s%s%s%s__pool", assetAddress, poolMakerAddress, "lockedAmount"], SEP)
112118
113119
114-func getPoolMakerUnlockHeightKey (assetAddress,poolMakerAddress) = (((("%s%s%s%s__pool__" + assetAddress) + "__") + poolMakerAddress) + "__unlockHeight")
120+func getPoolMakerUnlockHeightKey (assetAddress,poolMakerAddress) = makeString(["%s%s%s%s__pool", assetAddress, poolMakerAddress, "unlockHeight"], SEP)
121+
122+
123+func getPoolMakerLiquidityRequestKey (assetAddress,poolMakerAddress,h) = makeString(["%s%s%s%d%s__pool", assetAddress, poolMakerAddress, toString(h), "liquidityRequest"], SEP)
124+
125+
126+func getPoolTotalLockedAmountKey (assetAddress) = makeString(["%s%s%s__pool", assetAddress, "totalLockedAmount"], SEP)
115127
116128
117129 func getAssetConfigArray (assetAddress) = convertJsonArrayToList(valueOrErrorMessage(getStringByAddressAndKey(this, getAssetConfigKey(assetAddress)), (("No DEFO Asset " + assetAddress) + "config present")), "__")
118130
119131
132+let multiplicator = 1000000
133+
120134 let neutrinoContract = addressFromStringValue(valueOrErrorMessage(getString(this, neutrinoContractAddressKey), "No neutrino contract specified."))
121135
122-let neutrinoAssetId = fromBase58String(valueOrErrorMessage(getString(neutrinoContract, neutrinoAssetIdKey), "No USDN asset id found."))
136+let neutrinoAssetIdStr = valueOrErrorMessage(getString(neutrinoContract, neutrinoAssetIdKey), "No USDN asset id found.")
137+
138+let neutrinoAssetId = fromBase58String(neutrinoAssetIdStr)
123139
124140 let nsbtAssetId = fromBase58String(valueOrErrorMessage(getString(neutrinoContract, nsbtAssetIdKey), "No NSBT asset id found"))
125141
126142 let minimumPoolFunding = valueOrErrorMessage(getInteger(this, minimunPoolFundingAmountKey), "Minimum pool funding amount is not specified.")
127143
128-let assetStatus = $Tuple3("PROPOSED", "READY", "ISSUED")
144+let minPoolInvestAmount = valueOrElse(getInteger(this, minPoolInvestAmountKey), (10 * multiplicator))
145+
146+let ASSEtPROPOSED = "PROPOSED"
147+
148+let ASSEtREADY = "READY"
149+
150+let ASSEtISSUED = "ISSUED"
151+
152+let assetStatus = $Tuple3(ASSEtPROPOSED, ASSEtREADY, ASSEtISSUED)
129153
130154 let predefinedAssetContracts = valueOrErrorMessage(getString(this, assetsAddressListKey), "No pool of predeployed asset contracts found.")
131155
132156 let predefinedAssetsList = convertJsonArrayToList(predefinedAssetContracts, ",")
133157
134158 let currentAssetIndex = valueOrElse(getInteger(this, currentAssetIndexKey), 0)
135159
136160 let debtToken = fromBase58String(valueOrErrorMessage(getString(this, debtTokenIdKey), "No debt token found"))
137161
138-let multiplicator = 1000000
139-
140162 let debtTokenReissueAmount = valueOrElse(getInteger(this, debtTokenEtalonBalanceKey), 0)
141163
164+let neutrinoRpdContract = addressFromStringValue(getStringValue(neutrinoContract, neutrinoRpdKey))
165+
166+func getNeutrinoRpdBalance (userAddress) = {
167+ let userNeutrinoRpdBalance = makeString(["rpd_balance", neutrinoAssetIdStr, userAddress], "_")
168+ valueOrElse(getInteger(neutrinoRpdContract, userNeutrinoRpdBalance), 0)
169+ }
170+
171+
142172 func getAssetAddressByCode (assetSymbol) = valueOrElse(getString(this, getAssetAddressByCodeKey(assetSymbol)), "")
173+
174+
175+func getAssetAddressByCodeOrFail (assetSymbol) = valueOrErrorMessage(getString(this, getAssetAddressByCodeKey(assetSymbol)), (("Cannot find asset address by " + assetSymbol) + " code"))
143176
144177
145178 func getAssetCode (assetConfig) = assetConfig[keyAssetCode]
146179
147180
148181 func getAssetID (assetConfig) = assetConfig[keyAssetId]
149182
150183
151184 func getAssetStatus (assetConfig) = assetConfig[keyAssetStatus]
152185
153186
154187 func getAssetPriceDecimals (assetConfig) = parseIntValue(assetConfig[keyAssetPriceDecimals])
155188
156189
157190 func getAssetBakingAssetId (assetConfig) = assetConfig[keyAssetBakingAsset]
158191
159192
160193 func getAssetOverCollateralPercent (assetConfig) = parseIntValue(assetConfig[keyOvercollateralPercentage])
161194
162195
163196 func getAssetMinRequiredPoolAmount (assetConfig) = parseIntValue(assetConfig[keyMinPool])
164197
165198
166199 func getAssetPriceOracleAddress (assetConfig) = assetConfig[keyPriceOracleAddress]
167200
168201
169202 func getMinBuyPaymentPerAsset (assetConfig) = parseIntValue(assetConfig[keyMinBuyPayment])
170203
171204
172205 func getMinSellPaymentPerAsset (assetConfig) = parseIntValue(assetConfig[keyMinSellPayment])
173206
174207
175208 func getBuyLockIntervalPerAsset (assetConfig) = parseIntValue(assetConfig[keyBuyLockInterval])
176209
177210
178211 func getSellLockIntervalPerAsset (assetConfig) = parseIntValue(assetConfig[keySellLockInterval])
179212
180213
181214 func getBuyFeePerAsset (assetConfig) = parseIntValue(assetConfig[keyBuyFee])
182215
183216
184217 func getSellFeePerAsset (assetConfig) = parseIntValue(assetConfig[keySellFee])
185218
186219
187220 func getRedemptionTimeoutBlocks (assetConfig) = parseIntValue(assetConfig[keyPoolRedemptionTimeout])
188221
189222
190223 func getWeekendsPriceChangeCoefficient (assetConfig) = parseIntValue(assetConfig[keyWeekendsPriceChangeCoefficient])
191224
192225
193226 func getAssetPoolMakers (assetAddress) = valueOrElse(getString(this, getAssetPoolMakersKey(assetAddress)), "")
194227
195228
196229 func getAssetInitHeight (assetAddress) = valueOrElse(getInteger(this, getAssetInitHeightKey(assetAddress)), 0)
197230
198231
199232 func getAssetActivateHeight (assetAddress) = valueOrElse(getInteger(this, getAssetActivateHeightKey(assetAddress)), 0)
200233
201234
202235 func getAssetCurrentPoolAmount (assetAddress) = valueOrElse(getInteger(this, getAssetCurrrentPoolAmountKey(assetAddress)), 0)
203236
204237
205238 func getAssetMaxPoolAmount (assetAddress) = valueOrElse(getInteger(this, getAssetMaxPoolAmountKey(assetAddress)), 0)
206239
207240
208241 func getAssetMeta (assetAddress) = valueOrElse(getString(this, getAssetMetaKey(assetAddress)), "")
209242
210243
211244 func getPoolMakerParticipationAmount (assetAddress,poolMakerAddress) = valueOrElse(getInteger(this, getPoolMakerParticipationAmountKey(assetAddress, poolMakerAddress)), 0)
212245
213246
214247 func getPoolMakerParticipationMaxAmount (assetAddress,poolMakerAddress) = valueOrElse(getInteger(this, getPoolMakerParticipationMaxAmountKey(assetAddress, poolMakerAddress)), 0)
215248
216249
217250 func getPoolMakerLockedAmount (assetAddress,poolMakerAddress) = valueOrElse(getInteger(this, getPoolMakerLockedAmountKey(assetAddress, poolMakerAddress)), 0)
218251
219252
220253 func getPoolMakerUnlockHeight (assetAddress,poolMakerAddress) = valueOrElse(getInteger(this, getPoolMakerUnlockHeightKey(assetAddress, poolMakerAddress)), 0)
221254
222255
223256 func getAssetPrice (priceOracleAddress,assetCode) = valueOrErrorMessage(getInteger(priceOracleAddress, getAssetPriceKey(assetCode)), "No Asset price found")
224257
225258
259+func getPoolTotalLockedAmount (assetAddress) = valueOrElse(getInteger(this, getPoolTotalLockedAmountKey(assetAddress)), 0)
260+
261+
226262 func composeAssetConfigurationString (assetCode,assetId,assetStatus,priceDecimals,backingAssetId,overCollateralPercent,minPool,priceOracleAddress,minBuyPayment,minSellPayment,buyLockInterval,sellLockInterval,buyFee,sellFee,poolRedemptionTimeout,weekendsPriceChangeCoefficient) = makeString(["%s%s%s%d%s%d%d%s%d%d%d%d%d%d%d", assetCode, assetId, assetStatus, toString(priceDecimals), backingAssetId, toString(overCollateralPercent), toString(minPool), priceOracleAddress, toString(minBuyPayment), toString(minSellPayment), toString(buyLockInterval), toString(sellLockInterval), toString(buyFee), toString(sellFee), toString(poolRedemptionTimeout), toString(weekendsPriceChangeCoefficient)], "__")
227263
228264
229265 func updateAssetCfgStatus (newStatus,assetConfigurationList) = {
230266 let aCode = getAssetCode(assetConfigurationList)
231267 let aID = getAssetID(assetConfigurationList)
232268 let aPriceDecimals = getAssetPriceDecimals(assetConfigurationList)
233269 let aBakingAssetId = getAssetBakingAssetId(assetConfigurationList)
234270 let aOverCollateralPercent = getAssetOverCollateralPercent(assetConfigurationList)
235271 let aMinRequiredPool = getAssetMinRequiredPoolAmount(assetConfigurationList)
236272 let aPriceOracleAddress = getAssetPriceOracleAddress(assetConfigurationList)
237273 let aMinBuyPayment = getMinBuyPaymentPerAsset(assetConfigurationList)
238274 let aMinSellPayment = getMinSellPaymentPerAsset(assetConfigurationList)
239275 let aBuyLockInterval = getBuyLockIntervalPerAsset(assetConfigurationList)
240276 let aSellLockInterval = getSellLockIntervalPerAsset(assetConfigurationList)
241277 let aBuyFee = getBuyFeePerAsset(assetConfigurationList)
242278 let aSellFee = getSellFeePerAsset(assetConfigurationList)
243279 let aRedemptionTimeoutBlocks = getRedemptionTimeoutBlocks(assetConfigurationList)
244280 let weekendsPriceChangeCoefficient = getWeekendsPriceChangeCoefficient(assetConfigurationList)
245281 composeAssetConfigurationString(aCode, aID, newStatus, aPriceDecimals, aBakingAssetId, aOverCollateralPercent, aMinRequiredPool, aPriceOracleAddress, aMinBuyPayment, aMinSellPayment, aBuyLockInterval, aSellLockInterval, aBuyFee, aSellFee, aRedemptionTimeoutBlocks, weekendsPriceChangeCoefficient)
282+ }
283+
284+
285+func calcAllowedAmountToRedeem (assetCode,poolMakerAddress) = {
286+ let assetAddressStr = getAssetAddressByCodeOrFail(assetCode)
287+ let assetAddress = addressFromStringValue(assetAddressStr)
288+ let assetConfigurationList = getAssetConfigArray(assetAddressStr)
289+ let assetIdStr = getAssetID(assetConfigurationList)
290+ let assetId = fromBase58String(assetIdStr)
291+ let poolActualSize = valueOrElse(getInteger(this, getAssetCurrrentPoolAmountKey(assetAddressStr)), 0)
292+ let poolMaxSize = valueOrElse(getInteger(this, getAssetMaxPoolAmountKey(assetAddressStr)), 0)
293+ let poolTotalLockedAmountKey = getPoolTotalLockedAmountKey(assetAddressStr)
294+ let poolTotalLocked = valueOrElse(getInteger(this, poolTotalLockedAmountKey), 0)
295+ let assetUsdnStakedAmount = getNeutrinoRpdBalance(assetAddressStr)
296+ let assetUsdnBalanceAmount = assetBalance(assetAddress, neutrinoAssetId)
297+ let assetTotalUsdnAmount = ((assetUsdnStakedAmount + assetUsdnBalanceAmount) - poolTotalLocked)
298+ let ucollateral = (assetTotalUsdnAmount - poolActualSize)
299+ let defoAssetBalance = assetBalance(assetAddress, assetId)
300+ let assetEmissionDirty = valueOrErrorMessage(assetInfo(assetId), ("Cannot find asset by id " + assetIdStr)).quantity
301+ let assetEmission = (assetEmissionDirty - defoAssetBalance)
302+ let priceOracleAddress = addressFromStringValue(getAssetPriceOracleAddress(assetConfigurationList))
303+ let currentPrice = getAssetPrice(priceOracleAddress, assetCode)
304+ let priceWithOvercollateralRate = (((multiplicator + getAssetOverCollateralPercent(assetConfigurationList)) * currentPrice) / multiplicator)
305+ let usdnNeededToCoverEmission = (priceWithOvercollateralRate * assetEmission)
306+ let poolMakerMaxAmount = getPoolMakerParticipationMaxAmount(assetAddressStr, poolMakerAddress)
307+ let poolMakerParticipationAmountKey = getPoolMakerParticipationAmountKey(assetAddressStr, poolMakerAddress)
308+ let poolMakerActualAmount = valueOrElse(getInteger(this, poolMakerParticipationAmountKey), 0)
309+ let poolMakerLockedAmount = getPoolMakerLockedAmount(assetAddressStr, poolMakerAddress)
310+ let surplus = (ucollateral - usdnNeededToCoverEmission)
311+ let percentViaActual = fraction(poolMakerActualAmount, multiplicator, poolActualSize)
312+ let percentViaMax = fraction(poolMakerMaxAmount, multiplicator, poolMaxSize)
313+ let frozenPart = if ((surplus >= 0))
314+ then 0
315+ else max([percentViaActual, percentViaMax])
316+ let allowedAmountToRedeem = fraction((multiplicator - frozenPart), poolMakerActualAmount, multiplicator)
317+ let withdrawHeight = (height + getRedemptionTimeoutBlocks(assetConfigurationList))
318+ $Tuple8(assetAddressStr, allowedAmountToRedeem, poolTotalLocked, poolActualSize, poolMakerActualAmount, withdrawHeight, poolMakerLockedAmount, makeString([assetAddressStr, assetIdStr, poolMakerAddress, toString(assetUsdnStakedAmount), toString(assetUsdnBalanceAmount), toString(assetTotalUsdnAmount), toString(poolActualSize), toString(poolMaxSize), toString(ucollateral), toString(assetEmission), toString(currentPrice), toString(priceWithOvercollateralRate), toString(poolMakerMaxAmount), toString(poolMakerActualAmount), toString(poolMakerLockedAmount), toString(surplus), toString(percentViaActual), toString(percentViaMax), toString(frozenPart), toString(allowedAmountToRedeem)], SEP))
246319 }
247320
248321
249322 @Callable(i)
250323 func init (neutrinoContractAddress,neutrinoStakingAddress,minimunPoolFundingAmount,predefinedAssetList,currentAssetIndex,debtEthalonBalance,defoStakingAddress,defoStakingPacemakerPub) = if ((toBase58String(i.callerPublicKey) != adminPublicKey))
251324 then throw("Only Admin can perform contract operation.")
252325 else if ((getBoolByKey(isInitializedKey) == true))
253326 then throw("Contract was already initialized.")
254327 else {
255328 let issueDebt = Issue("DEFO_DEBT", "DEFO Debt Token.", 1, 6, true)
256329 let debtTokenId = calculateAssetId(issueDebt)
257330 [StringEntry(neutrinoContractAddressKey, neutrinoContractAddress), StringEntry(neutrinoStakingAddressKey, neutrinoStakingAddress), IntegerEntry(minimunPoolFundingAmountKey, minimunPoolFundingAmount), StringEntry(assetsAddressListKey, predefinedAssetList), IntegerEntry(currentAssetIndexKey, currentAssetIndex), IntegerEntry(debtTokenEtalonBalanceKey, debtEthalonBalance), StringEntry(debtTokenIdKey, toBase58String(debtTokenId)), StringEntry(defoStakingAddressKey, defoStakingAddress), StringEntry(defoStakingPacemakerPubKey, defoStakingPacemakerPub), BooleanEntry(isInitializedKey, true), issueDebt, Burn(debtTokenId, 1)]
258331 }
259332
260333
261334
262335 @Callable(i)
263336 func proposeAsset (assetCode,meta,minPool,priceOracleAddress,overCollateralPercent,backingAssetId,priceDecimals,minBuyPayment,minSellPayment,buyLockInterval,sellLockInterval,buyFee,sellFee,poolRedemptionTimeout,weekendsPriceChangeCoefficient) = if ((toBase58String(i.callerPublicKey) != adminPublicKey))
264337 then throw("Only Admin can perform contract operation.")
265338 else if ((getBoolByKey(isInitializedKey) == false))
266339 then throw("Contract is not yet ready.")
267340 else if ((fromBase58String(backingAssetId) != neutrinoAssetId))
268341 then throw("Baking asset id could be USDN only.")
269342 else if ((getAssetAddressByCode(assetCode) != ""))
270343 then throw((("Asset with passed code " + assetCode) + " has already been proposed."))
271344 else if ((minimumPoolFunding > minPool))
272345 then throw(("Minimum pool amount should be greater or equals to " + toString(minimumPoolFunding)))
273346 else if (if (if (if (if (if (if (if ((0 > overCollateralPercent))
274347 then true
275348 else (0 > priceDecimals))
276349 then true
277350 else (0 > minBuyPayment))
278351 then true
279352 else (0 > minSellPayment))
280353 then true
281354 else (0 > buyLockInterval))
282355 then true
283356 else (0 > sellLockInterval))
284357 then true
285358 else (0 > buyFee))
286359 then true
287360 else (0 > sellFee))
288361 then throw("One of the passed parameters is invalid.")
289362 else {
290363 let priceOracleAddressValid = valueOrErrorMessage(addressFromStringValue(priceOracleAddress), "Passed Price Oracle Address is not valid.")
291364 let assetAddress = predefinedAssetsList[currentAssetIndex]
292365 let defoAssetId = valueOrErrorMessage(getString(valueOrErrorMessage(addressFromString(assetAddress), ""), defoAssetIdKey), "No defo asset ID found.")
293366 [StringEntry(getAssetConfigKey(assetAddress), composeAssetConfigurationString(assetCode, defoAssetId, assetStatus._1, priceDecimals, backingAssetId, overCollateralPercent, minPool, priceOracleAddress, minBuyPayment, minSellPayment, buyLockInterval, sellLockInterval, buyFee, sellFee, poolRedemptionTimeout, weekendsPriceChangeCoefficient)), StringEntry(getAssetAddressByCodeKey(assetCode), assetAddress), StringEntry(getAssetMetaKey(assetAddress), meta), IntegerEntry(getAssetInitHeightKey(assetAddress), height), IntegerEntry(currentAssetIndexKey, (currentAssetIndex + 1)), StringEntry(getAssetAddressByAssetIdKey(defoAssetId), assetAddress)]
294367 }
295368
296369
297370
298371 @Callable(i)
299372 func fundLiquidityPool (assetCode) = {
300- let assetAddress = getAssetAddressByCode(assetCode)
301- let assetConfigurationList = getAssetConfigArray(assetAddress)
373+ let assetAddressStr = getAssetAddressByCodeOrFail(assetCode)
374+ let assetConfigurationList = getAssetConfigArray(assetAddressStr)
302375 let attachedPayment = value(i.payments[0])
303- if ((toBase58String(i.callerPublicKey) != adminPublicKey))
304- then throw("Only Admin can perform contract operation.")
305- else if ((getBoolByKey(isInitializedKey) == false))
306- then throw("Contract is not yet ready.")
307- else if ((attachedPayment.assetId != neutrinoAssetId))
308- then throw("Liquidity pool could be funded with USDN only.")
309- else if ((attachedPayment.amount == 0))
310- then throw("Incorrect attached payment amount.")
311- else if ((assetAddress == ""))
312- then throw((("Cannot find liquidity pool for asset " + assetCode) + "."))
313- else {
314- let finalPoolAmount = (attachedPayment.amount + getAssetCurrentPoolAmount(assetAddress))
315- let maxPoolAmount = getAssetMaxPoolAmount(assetAddress)
316- let poolMakerAddress = toString(i.caller)
317- let currentPoolMakerBalance = getPoolMakerParticipationAmount(assetAddress, poolMakerAddress)
318- let maxPoolMakerBalance = getPoolMakerParticipationMaxAmount(assetAddress, poolMakerAddress)
319- let currentPoolStatus = getAssetStatus(assetConfigurationList)
320- let finalPoolStatus = if (if ((finalPoolAmount >= getAssetMinRequiredPoolAmount(assetConfigurationList)))
321- then (currentPoolStatus == assetStatus._1)
322- else false)
323- then assetStatus._2
324- else currentPoolStatus
325- let currentPoolMakers = getAssetPoolMakers(assetAddress)
326- let newPoolMakers = if ((currentPoolMakers == ""))
327- then poolMakerAddress
328- else if (contains(currentPoolMakers, poolMakerAddress))
329- then currentPoolMakers
330- else ((currentPoolMakers + ",") + toString(i.caller))
331- let newPoolMakerBalance = (currentPoolMakerBalance + i.payments[0].amount)
332- let result = [IntegerEntry(getPoolMakerParticipationAmountKey(assetAddress, toString(i.caller)), newPoolMakerBalance), IntegerEntry(getPoolMakerParticipationMaxAmountKey(assetAddress, toString(i.caller)), if ((newPoolMakerBalance > maxPoolMakerBalance))
333- then newPoolMakerBalance
334- else maxPoolMakerBalance), IntegerEntry(getAssetCurrrentPoolAmountKey(assetAddress), finalPoolAmount), IntegerEntry(getAssetMaxPoolAmountKey(assetAddress), if ((finalPoolAmount > maxPoolAmount))
335- then finalPoolAmount
336- else maxPoolAmount), StringEntry(getAssetPoolMakersKey(assetAddress), newPoolMakers), StringEntry(getAssetConfigKey(assetAddress), updateAssetCfgStatus(finalPoolStatus, assetConfigurationList))]
337- if ((currentPoolStatus == assetStatus._3))
338- then (result :+ ScriptTransfer(addressFromStringValue(assetAddress), attachedPayment.amount, neutrinoAssetId))
339- else result
340- }
376+ let poolMakerAddress = toString(i.caller)
377+ if ((getBoolByKey(isInitializedKey) == false))
378+ then throw("Contract is not yet ready.")
379+ else if ((attachedPayment.assetId != neutrinoAssetId))
380+ then throw("Liquidity pool could be funded with USDN only.")
381+ else if ((minPoolInvestAmount > attachedPayment.amount))
382+ then throw(("Attached payment amount is less tha min allowed: " + toString(minPoolInvestAmount)))
383+ else {
384+ let newCurrentPoolAmount = (attachedPayment.amount + getAssetCurrentPoolAmount(assetAddressStr))
385+ let maxPoolAmount = getAssetMaxPoolAmount(assetAddressStr)
386+ let currentPoolMakerBalance = getPoolMakerParticipationAmount(assetAddressStr, poolMakerAddress)
387+ let maxPoolMakerBalance = getPoolMakerParticipationMaxAmount(assetAddressStr, poolMakerAddress)
388+ let currentAssetStatus = getAssetStatus(assetConfigurationList)
389+ let newAssetStatus = if (if ((currentAssetStatus == ASSEtPROPOSED))
390+ then (newCurrentPoolAmount >= getAssetMinRequiredPoolAmount(assetConfigurationList))
391+ else false)
392+ then ASSEtREADY
393+ else currentAssetStatus
394+ let newPoolMakerBalance = (currentPoolMakerBalance + attachedPayment.amount)
395+ let immediateTransferIfIssued = if ((currentAssetStatus == ASSEtISSUED))
396+ then [ScriptTransfer(addressFromStringValue(assetAddressStr), attachedPayment.amount, neutrinoAssetId)]
397+ else nil
398+ ([IntegerEntry(getPoolMakerParticipationAmountKey(assetAddressStr, poolMakerAddress), newPoolMakerBalance), IntegerEntry(getPoolMakerParticipationMaxAmountKey(assetAddressStr, poolMakerAddress), max([newPoolMakerBalance, maxPoolMakerBalance])), IntegerEntry(getAssetCurrrentPoolAmountKey(assetAddressStr), newCurrentPoolAmount), IntegerEntry(getAssetMaxPoolAmountKey(assetAddressStr), max([maxPoolAmount, newCurrentPoolAmount])), StringEntry(getAssetConfigKey(assetAddressStr), updateAssetCfgStatus(newAssetStatus, assetConfigurationList))] ++ immediateTransferIfIssued)
399+ }
341400 }
342401
343402
344403
345404 @Callable(i)
346405 func activateAsset (assetCode) = {
347406 let assetAddress = getAssetAddressByCode(assetCode)
348407 let assetConfigurationList = getAssetConfigArray(assetAddress)
349408 let assetCurrentPoolAmount = getAssetCurrentPoolAmount(assetAddress)
350409 let aCurrentStatus = getAssetStatus(assetConfigurationList)
351410 if ((toBase58String(i.callerPublicKey) != adminPublicKey))
352411 then throw("Only Admin can perform contract operation.")
353412 else if ((getBoolByKey(isInitializedKey) == false))
354413 then throw("Contract is not yet ready.")
355414 else if ((aCurrentStatus != assetStatus._2))
356415 then throw((("Asset is not in READY status. Current status is " + aCurrentStatus) + "."))
357416 else if ((assetAddress == ""))
358417 then throw((("Cannot find asset " + assetCode) + "."))
359418 else if ((minimumPoolFunding > assetCurrentPoolAmount))
360419 then throw((((("Cannot issue asset as liquidity pool amount (" + toString(assetCurrentPoolAmount)) + ") is less than required (") + toString(minimumPoolFunding)) + ")"))
361420 else {
362421 let issueDebt = Reissue(debtToken, debtTokenReissueAmount, true)
363422 [IntegerEntry(getAssetActivateHeightKey(assetAddress), height), StringEntry(getAssetConfigKey(assetAddress), updateAssetCfgStatus(assetStatus._3, assetConfigurationList)), issueDebt, ScriptTransfer(addressFromStringValue(assetAddress), assetCurrentPoolAmount, neutrinoAssetId), ScriptTransfer(addressFromStringValue(assetAddress), debtTokenReissueAmount, debtToken)]
364423 }
365424 }
366425
367426
368427
369428 @Callable(i)
370429 func redeemFromPool (assetCode,amount) = {
371- let assetAddress = getAssetAddressByCode(assetCode)
372- let assetConfigurationList = getAssetConfigArray(assetAddress)
373430 let callerAddress = toString(i.caller)
374- let totalAmountInPoolByCaller = getPoolMakerParticipationAmount(assetAddress, callerAddress)
375- let lockedAmountByPoolMaker = getPoolMakerLockedAmount(assetAddress, callerAddress)
376- if ((toBase58String(i.callerPublicKey) != adminPublicKey))
377- then throw("Only Admin can perform contract operation.")
378- else if ((getBoolByKey(isInitializedKey) == false))
379- then throw("Contract is not yet ready.")
380- else if ((assetAddress == ""))
381- then throw((("Cannot find asset " + assetCode) + "."))
382- else if (if (if ((0 > amount))
383- then true
384- else (totalAmountInPoolByCaller == 0))
385- then true
386- else (amount > totalAmountInPoolByCaller))
387- then throw((("Cannot find pool maker by address " + callerAddress) + " or amount passed is not correct."))
388- else if ((lockedAmountByPoolMaker > 0))
389- then throw("Pool maker already has initiated redemption that is not withdrawn yet.")
390- else [IntegerEntry(getPoolMakerLockedAmountKey(assetAddress, callerAddress), amount), IntegerEntry(getPoolMakerUnlockHeightKey(assetAddress, callerAddress), (height + getRedemptionTimeoutBlocks(assetConfigurationList)))]
431+ let redeemTuple = calcAllowedAmountToRedeem(assetCode, callerAddress)
432+ let assetAddressStr = redeemTuple._1
433+ let allowedAmountToRedeem = redeemTuple._2
434+ let poolTotalLocked = redeemTuple._3
435+ let poolActualSize = redeemTuple._4
436+ let poolMakerActualAmount = redeemTuple._5
437+ let withdrawHeight = redeemTuple._6
438+ let poolMakerLockedAmount = redeemTuple._7
439+ let traceData = redeemTuple._8
440+ let poolMakerLiquidityRequestKey = getPoolMakerLiquidityRequestKey(assetAddressStr, callerAddress, withdrawHeight)
441+ if ((getBoolByKey(isInitializedKey) == false))
442+ then throw("Contract is not yet ready.")
443+ else if (if (if ((0 > amount))
444+ then true
445+ else (0 >= poolMakerActualAmount))
446+ then true
447+ else (amount > allowedAmountToRedeem))
448+ then throw((("Cannot find pool maker by address " + callerAddress) + " or amount passed is not correct."))
449+ else if (((1 * multiplicator) > amount))
450+ then throw("Redeem amount is less then min allowed")
451+ else if ((poolMakerLockedAmount > 0))
452+ then throw("Pool maker already has initiated redemption that is not withdrawn yet.")
453+ else if (isDefined(getInteger(this, poolMakerLiquidityRequestKey)))
454+ then throw((("Pool maker has already initiated redemption for " + toString(height)) + " height"))
455+ else [IntegerEntry(getPoolTotalLockedAmountKey(assetAddressStr), (poolTotalLocked + amount)), IntegerEntry(getAssetCurrrentPoolAmountKey(assetAddressStr), (poolActualSize - amount)), IntegerEntry(getPoolMakerParticipationAmountKey(assetAddressStr, callerAddress), (poolMakerActualAmount - amount)), IntegerEntry(poolMakerLiquidityRequestKey, amount), IntegerEntry(getPoolMakerLockedAmountKey(assetAddressStr, callerAddress), amount), IntegerEntry(getPoolMakerUnlockHeightKey(assetAddressStr, callerAddress), withdrawHeight), StringEntry("%s%s_trace_redeemFromPool", traceData)]
391456 }
392457
393458
394459
395460 @Callable(i)
396-func getMaxAllowedAmountToRedeem (assetCode,poolMaker) = throw((((("{allowedAmountToRedeem: 1000, assetCode: " + assetCode) + ", poolMaker: ") + poolMaker) + "}"))
461+func getMaxAllowedAmountToRedeem (assetCode,poolMakerAddress) = {
462+ let redeemTuple = calcAllowedAmountToRedeem(assetCode, poolMakerAddress)
463+ let assetAddressStr = redeemTuple._1
464+ let allowedAmountToRedeem = redeemTuple._2
465+ let poolTotalLocked = redeemTuple._3
466+ let poolActualSize = redeemTuple._4
467+ let poolMakerActualAmount = redeemTuple._5
468+ let withdrawHeight = redeemTuple._6
469+ let poolMakerLockedAmount = redeemTuple._7
470+ throw(((((((("{" + "allowedAmountToRedeem: ") + toString(allowedAmountToRedeem)) + ", assetCode: ") + assetCode) + ", poolMaker: ") + poolMakerAddress) + "}"))
471+ }
397472
398473
399474
400475 @Callable(i)
401476 func withdraw (assetCode) = {
402- let assetConfigurationList = getAssetConfigArray(assetCode)
403- let assetAddress = getAssetAddressByCode(assetCode)
404- let priceOracleAddress = addressFromStringValue(getAssetPriceOracleAddress(assetConfigurationList))
405477 let callerAddress = toString(i.caller)
406- let syntheticAssetInfo = valueOrErrorMessage(assetInfo(fromBase58String(assetAddress)), ("Cannot find asset by id " + assetAddress))
407- let totalUSDNBalanceOnContract = assetBalance(this, neutrinoAssetId)
408- let totalAmountInPoolByPoolMaker = getPoolMakerParticipationAmount(assetAddress, callerAddress)
409- let lockedAmountByPoolMaker = getPoolMakerLockedAmount(assetAddress, callerAddress)
410- let minWithdrawHeight = getPoolMakerUnlockHeight(assetAddress, callerAddress)
411- let poolMaketProportion = (getAssetMaxPoolAmount(assetAddress) / getPoolMakerParticipationMaxAmount(assetAddress, callerAddress))
412- if ((toBase58String(i.callerPublicKey) != adminPublicKey))
413- then throw("Only Admin can perform contract operation.")
414- else if ((getBoolByKey(isInitializedKey) == false))
415- then throw("Contract is not yet ready.")
416- else if ((getBoolByKey(isInitializedKey) == false))
417- then throw("Contract is not yet ready.")
418- else if ((assetAddress == ""))
419- then throw((("Cannot find asset " + assetCode) + "."))
420- else if ((totalAmountInPoolByPoolMaker == 0))
421- then throw((("Cannot find pool maker by address " + callerAddress) + " or no locked amount."))
422- else if ((height > minWithdrawHeight))
423- then throw((("Please wait " + toString(minWithdrawHeight)) + " before withdraw."))
424- else {
425- let assetEmission = syntheticAssetInfo.quantity
426- let currentPrice = getAssetPrice(priceOracleAddress, assetCode)
427- let poolMakerLockedAmount = getPoolMakerLockedAmount(assetAddress, callerAddress)
428- let priceWithOvercollateralRate = (getAssetOverCollateralPercent(assetConfigurationList) * currentPrice)
429- let usdnNeededToCoverEmission = (priceWithOvercollateralRate * assetEmission)
430- if ((totalUSDNBalanceOnContract > usdnNeededToCoverEmission))
431- then {
432- let amoutAvailableToExitFromPool = (totalUSDNBalanceOnContract - usdnNeededToCoverEmission)
433- let amountForWithdraw = (poolMaketProportion * amoutAvailableToExitFromPool)
434- let finalLockedAmount = (getPoolMakerLockedAmount(assetAddress, callerAddress) - amountForWithdraw)
435-[IntegerEntry(getAssetCurrrentPoolAmountKey(assetAddress), (getAssetCurrentPoolAmount(assetAddress) - amountForWithdraw)), IntegerEntry(getPoolMakerParticipationAmountKey(assetAddress, callerAddress), (getPoolMakerParticipationAmount(assetAddress, callerAddress) - amountForWithdraw)), IntegerEntry(getPoolMakerLockedAmountKey(assetAddress, callerAddress), finalLockedAmount), ScriptTransfer(addressFromStringValue(callerAddress), amountForWithdraw, neutrinoAssetId)]
436- }
437- else nil
438- }
478+ let assetAddressStr = getAssetAddressByCodeOrFail(assetCode)
479+ let assetAddress = addressFromStringValue(assetAddressStr)
480+ let poolMakerLockedAmountKey = getPoolMakerLockedAmountKey(assetAddressStr, callerAddress)
481+ let registeredAmount4Withdraw = valueOrElse(getInteger(this, poolMakerLockedAmountKey), 0)
482+ let poolMakerUnlockHeightKey = getPoolMakerUnlockHeightKey(assetAddressStr, callerAddress)
483+ let withdrawHeight = valueOrElse(getInteger(this, poolMakerUnlockHeightKey), 0)
484+ let poolTotalLockedAmountKey = getPoolTotalLockedAmountKey(assetAddressStr)
485+ let poolTotalLocked = valueOrElse(getInteger(this, poolTotalLockedAmountKey), 0)
486+ let poolMakerLiquidityRequestKey = getPoolMakerLiquidityRequestKey(assetAddressStr, callerAddress, withdrawHeight)
487+ if ((getBoolByKey(isInitializedKey) == false))
488+ then throw("Contract is not yet ready.")
489+ else if (!(isDefined(getInteger(assetAddress, poolMakerLiquidityRequestKey))))
490+ then throw("Need to transfer liquidity from asset account first")
491+ else if ((0 >= registeredAmount4Withdraw))
492+ then throw((("Cannot find pool maker by address " + callerAddress) + " or no locked amount."))
493+ else if ((height > withdrawHeight))
494+ then throw((("Please wait " + toString(withdrawHeight)) + " before withdraw."))
495+ else [IntegerEntry(poolMakerLockedAmountKey, 0), IntegerEntry(poolTotalLockedAmountKey, (poolTotalLocked - registeredAmount4Withdraw)), IntegerEntry(poolMakerUnlockHeightKey, 0), ScriptTransfer(addressFromStringValue(callerAddress), registeredAmount4Withdraw, neutrinoAssetId)]
439496 }
440497
441498

github/deemru/w8io/3ef1775 
100.20 ms