tx · LyLp1Ltwsnp3VJC9FU6z4rUNxz8iD1MeWafoPCNHtMx

3MzGPAZokuNfqjMuP7yDXFATZjhjtEP5UCa:  -0.03700000 Waves

2023.01.09 11:38 [2397174] smart account 3MzGPAZokuNfqjMuP7yDXFATZjhjtEP5UCa > SELF 0.00000000 Waves

{ "type": 13, "id": "LyLp1Ltwsnp3VJC9FU6z4rUNxz8iD1MeWafoPCNHtMx", "fee": 3700000, "feeAssetId": null, "timestamp": 1673253532581, "version": 2, "chainId": 84, "sender": "3MzGPAZokuNfqjMuP7yDXFATZjhjtEP5UCa", "senderPublicKey": "4EBKd2zSCvpiSLeyovT5FUuMvGpi6oxdBAbvQybSYi6p", "proofs": [ "3zkGbkjpr8ZgnBk3Coh2Pits3mPctNmjTyb5u3fnCtDiJmTw7ngzLtNZYq15DN3KQBr9csNTKA6FhUo7ySp9jtas" ], "script": "base64:BgJLCAISBAoCCAgSAwoBCBIAEgMKAQgSCgoICAEBAQEBAQgSCwoJCAEBAQgBAQEBEg0KCwgIAQEBAQEBCAgBEgMKAQESAwoBARIDCgEBOwAUa19jb29yZGluYXRvckFkZHJlc3MCFGtfY29vcmRpbmF0b3JBZGRyZXNzABJrX2FkbWluX3B1YmxpY19rZXkCEmtfYWRtaW5fcHVibGljX2tleQANa19xdW90ZV9hc3NldAINa19xdW90ZV9hc3NldAAFa19hbW0CBWtfYW1tABFrX21hbmFnZXJfYWRkcmVzcwIRa19tYW5hZ2VyX2FkZHJlc3MAEmtfcG9zaXRpb25TZXF1ZW5jZQISa19wb3NpdGlvblNlcXVlbmNlAA5rX3Bvc2l0aW9uU2l6ZQIOa19wb3NpdGlvblNpemUABWtfZmVlAgVrX2ZlZQAQa19leGVjdXRlZE9yZGVycwIQa19leGVjdXRlZE9yZGVycwAQa19jYW5jZWxlZE9yZGVycwIQa19jYW5jZWxlZE9yZGVycwAHa19vcmRlcgIHa19vcmRlcgANa19sYXN0T3JkZXJJZAINa19sYXN0T3JkZXJJZAAQa190cmFkZXJPcmRlckNudAIQa190cmFkZXJPcmRlckNudAAQa190cmFkZXJPcmRlcklkcwIQa190cmFkZXJPcmRlcklkcwAIa19zZW5kZXICCGtfc2VuZGVyAA1rX2luaXRpYWxpemVkAg1rX2luaXRpYWxpemVkAARTVE9QAAEABFRBS0UAAgAFTElNSVQAAwAETE9ORwABAAVTSE9SVAACAAhJTkNSRUFTRQABAAhERUNSRUFTRQACABlNQVhfVFJBREVSX09SREVSU19QRVJfQU1NAAUABFRJTUUIBQlsYXN0QmxvY2sJdGltZXN0YW1wAAxERUNJTUFMX1VOSVQJAGgCAAEJAGgCCQBoAgkAaAIJAGgCCQBoAgAKAAoACgAKAAoACgAMU1BSRUFEX0xJTUlUCQBpAgUMREVDSU1BTF9VTklUAMgBAQNhYnMBAl94AwkAZgIFAl94AAAFAl94CQEBLQEFAl94AQRkaXZkAgJfeAJfeQkAbgQFAl94BQxERUNJTUFMX1VOSVQFAl95BQhIQUxGRVZFTgEEbXVsZAICX3gCX3kJAG4EBQJfeAUCX3kFDERFQ0lNQUxfVU5JVAUISEFMRkVWRU4BBG1pbnYCAl94Al95AwkAZgIFAl94BQJfeQUCX3kFAl94AQ50b0NvbXBvc2l0ZUtleQIEX2tleQhfYWRkcmVzcwkArAICCQCsAgIFBF9rZXkCAV8FCF9hZGRyZXNzARBleGVjdXRlZE9yZGVyS2V5AQhfb3JkZXJJZAkArAICCQCsAgIFEGtfZXhlY3V0ZWRPcmRlcnMCAV8JAKQDAQUIX29yZGVySWQBEGNhbmNlbGVkT3JkZXJLZXkBCF9vcmRlcklkCQCsAgIJAKwCAgUQa19jYW5jZWxlZE9yZGVycwIBXwkApAMBBQhfb3JkZXJJZAEIb3JkZXJLZXkBCF9vcmRlcklkCQEOdG9Db21wb3NpdGVLZXkCBQdrX29yZGVyCQCkAwEFCF9vcmRlcklkARN0cmFkZXJPcmRlckNvdW50S2V5AgRfYW1tB190cmFkZXIJAKwCAgkArAICCQCsAgIJAKwCAgUQa190cmFkZXJPcmRlckNudAIBXwUEX2FtbQIBXwUHX3RyYWRlcgERdHJhZGVyT3JkZXJJZHNLZXkCBF9hbW0HX3RyYWRlcgkArAICCQCsAgIJAKwCAgkArAICBRBrX3RyYWRlck9yZGVySWRzAgFfBQRfYW1tAgFfBQdfdHJhZGVyAQtjb29yZGluYXRvcgAJARN2YWx1ZU9yRXJyb3JNZXNzYWdlAgkApggBCQERQGV4dHJOYXRpdmUoMTA1MykCBQR0aGlzBRRrX2Nvb3JkaW5hdG9yQWRkcmVzcwITQ29vcmRpbmF0b3Igbm90IHNldAEKcXVvdGVBc3NldAAJANkEAQkBEUBleHRyTmF0aXZlKDEwNTMpAgkBC2Nvb3JkaW5hdG9yAAUNa19xdW90ZV9hc3NldAEObWFuYWdlckFkZHJlc3MACQETdmFsdWVPckVycm9yTWVzc2FnZQIJAKYIAQkBEUBleHRyTmF0aXZlKDEwNTMpAgkBC2Nvb3JkaW5hdG9yAAURa19tYW5hZ2VyX2FkZHJlc3MCD01hbmFnZXIgbm90IHNldAELaXNXaGl0ZWxpc3QBCF9hZGRyZXNzCQELdmFsdWVPckVsc2UCCQCbCAIJAQtjb29yZGluYXRvcgAJAQ50b0NvbXBvc2l0ZUtleQIFBWtfYW1tBQhfYWRkcmVzcwcBDmFkbWluUHVibGljS2V5AAkA2QQBCQERQGV4dHJOYXRpdmUoMTA1MykCCQELY29vcmRpbmF0b3IABRJrX2FkbWluX3B1YmxpY19rZXkBC2luaXRpYWxpemVkAAkBC3ZhbHVlT3JFbHNlAgkAmwgCBQR0aGlzBQ1rX2luaXRpYWxpemVkBwEHaXNWYWxpZAEIX29yZGVySWQDCQELdmFsdWVPckVsc2UCCQCbCAIFBHRoaXMJARBleGVjdXRlZE9yZGVyS2V5AQUIX29yZGVySWQHCQACAQkArAICAhhPcmRlciBhbHJlYWR5IGV4ZWN1dGVkOiAJAKQDAQUIX29yZGVySWQDCQELdmFsdWVPckVsc2UCCQCbCAIFBHRoaXMJARBjYW5jZWxlZE9yZGVyS2V5AQUIX29yZGVySWQHCQACAQkArAICAhlPcmRlciBhbHJlYWR5IGNhbmNlbGxlZDogCQCkAwEFCF9vcmRlcklkBgEOY3VycmVudE9yZGVySWQACQELdmFsdWVPckVsc2UCCQCaCAIFBHRoaXMFDWtfbGFzdE9yZGVySWQAAAETZ2V0VHJhZGVyT3JkZXJDb3VudAIEX2FtbQdfdHJhZGVyBANrZXkJARN0cmFkZXJPcmRlckNvdW50S2V5AgUEX2FtbQUHX3RyYWRlcgkBC3ZhbHVlT3JFbHNlAgkAmggCBQR0aGlzBQNrZXkAAAESdHJhZGVyQW1tT3JkZXJzSWRzAgRfYW1tB190cmFkZXIEA2tleQkBEXRyYWRlck9yZGVySWRzS2V5AgUEX2FtbQUHX3RyYWRlcgQDdmFsCQELdmFsdWVPckVsc2UCCQCdCAIFBHRoaXMFA2tleQIAAwkAAAIFA3ZhbAIABQNuaWwJALUJAgUDdmFsAgEsAQhnZXRPcmRlcgEIX29yZGVySWQECG9yZGVyU3RyCQETdmFsdWVPckVycm9yTWVzc2FnZQIJAJ0IAgUEdGhpcwkBCG9yZGVyS2V5AQUIX29yZGVySWQJAKwCAgISSW52YWxpZCBvcmRlciBpZDogCQCkAwEFCF9vcmRlcklkBA1vcmRlclBhcnRMaXN0CQC1CQIFCG9yZGVyU3RyAgEsBANhbW0JAJEDAgUNb3JkZXJQYXJ0TGlzdAAABAZ0cmFkZXIJAJEDAgUNb3JkZXJQYXJ0TGlzdAABBAhhbW91bnRJbgkBE3ZhbHVlT3JFcnJvck1lc3NhZ2UCCQC2CQEJAJEDAgUNb3JkZXJQYXJ0TGlzdAACAhBJbnZhbGlkIGFtb3VudEluBAhsZXZlcmFnZQkBE3ZhbHVlT3JFcnJvck1lc3NhZ2UCCQC2CQEJAJEDAgUNb3JkZXJQYXJ0TGlzdAADAhBJbnZhbGlkIGxldmVyYWdlBAR0eXBlCQETdmFsdWVPckVycm9yTWVzc2FnZQIJALYJAQkAkQMCBQ1vcmRlclBhcnRMaXN0AAQCDEludmFsaWQgdHlwZQQMdHJpZ2dlclByaWNlCQETdmFsdWVPckVycm9yTWVzc2FnZQIJALYJAQkAkQMCBQ1vcmRlclBhcnRMaXN0AAUCFEludmFsaWQgdHJpZ2dlclByaWNlBAtwYXltZW50VXNkbgkBE3ZhbHVlT3JFcnJvck1lc3NhZ2UCCQC2CQEJAJEDAgUNb3JkZXJQYXJ0TGlzdAAGAhNJbnZhbGlkIHBheW1lbnRVc2RuBARzaWRlCQETdmFsdWVPckVycm9yTWVzc2FnZQIJALYJAQkAkQMCBQ1vcmRlclBhcnRMaXN0AAcCDEludmFsaWQgc2lkZQQHcmVmTGluawkAkQMCBQ1vcmRlclBhcnRMaXN0AAgECnBvc2l0aW9uSWQJARN2YWx1ZU9yRXJyb3JNZXNzYWdlAgkAtgkBCQCRAwIFDW9yZGVyUGFydExpc3QACQISSW52YWxpZCBwb3NpdGlvbklkBApsaW1pdFByaWNlCQETdmFsdWVPckVycm9yTWVzc2FnZQIJALYJAQkAkQMCBQ1vcmRlclBhcnRMaXN0AAoCEkludmFsaWQgbGltaXRQcmljZQkAnQoLBQNhbW0FBnRyYWRlcgUIYW1vdW50SW4FCGxldmVyYWdlBQR0eXBlBQx0cmlnZ2VyUHJpY2UFC3BheW1lbnRVc2RuBQRzaWRlBQdyZWZMaW5rBQpwb3NpdGlvbklkBQpsaW1pdFByaWNlAQ5nZXRNYXJrZXRQcmljZQEEX2FtbQQBcwkA/AcECQERQGV4dHJOYXRpdmUoMTA2MikBBQRfYW1tAhBjb21wdXRlU3BvdFByaWNlBQNuaWwFA25pbAMJAAACBQFzBQFzBANyZXMEByRtYXRjaDAFAXMDCQABAgUHJG1hdGNoMAIDSW50BAF0BQckbWF0Y2gwBQF0CQACAQIfSW52YWxpZCBjb21wdXRlU3BvdFByaWNlIHJlc3VsdAkBBXZhbHVlAQUDcmVzCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuAQZnZXRGZWUCBF9hbW0HX3RyYWRlcgQBcwkA/AcECQERQGV4dHJOYXRpdmUoMTA2MikBBQRfYW1tAh9jb21wdXRlRmVlRm9yVHJhZGVyV2l0aEFydGlmYWN0CQDMCAIFB190cmFkZXIJAMwIAgIABQNuaWwFA25pbAMJAAACBQFzBQFzBANyZXMEByRtYXRjaDAFAXMDCQABAgUHJG1hdGNoMAIOKEludCwgQm9vbGVhbikEAXQFByRtYXRjaDAIBQF0Al8xCQACAQIuSW52YWxpZCBjb21wdXRlRmVlRm9yVHJhZGVyV2l0aEFydGlmYWN0IHJlc3VsdAkBBXZhbHVlAQUDcmVzCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuAQ9nZXRQb3NpdGlvblNpemUCBF9hbW0HX3RyYWRlcgQDYW1tCQERQGV4dHJOYXRpdmUoMTA2MikBBQRfYW1tBAdzaXplS2V5CQEOdG9Db21wb3NpdGVLZXkCBQ5rX3Bvc2l0aW9uU2l6ZQUHX3RyYWRlcgkBC3ZhbHVlT3JFbHNlAgkAmggCBQNhbW0FB3NpemVLZXkAAAENZ2V0UG9zaXRpb25JZAIEX2FtbQdfdHJhZGVyBANhbW0JARFAZXh0ck5hdGl2ZSgxMDYyKQEFBF9hbW0EBnNlcUtleQkBDnRvQ29tcG9zaXRlS2V5AgUSa19wb3NpdGlvblNlcXVlbmNlBQdfdHJhZGVyCQELdmFsdWVPckVsc2UCCQCaCAIFA2FtbQUGc2VxS2V5AAABCWdldFNwcmVhZAEGX3ByaWNlCQEEbXVsZAIFBl9wcmljZQUMU1BSRUFEX0xJTUlUAQlzYXZlT3JkZXIMCF9vcmRlcklkBF9hbW0HX3RyYWRlcglfYW1vdW50SW4JX2xldmVyYWdlBV90eXBlDV90cmlnZ2VyUHJpY2UMX3BheW1lbnRVc2RuBV9zaWRlCF9yZWZMaW5rC19wb3NpdGlvbklkC19saW1pdFByaWNlBAhvcmRlclN0cgkAuQkCCQDMCAIFBF9hbW0JAMwIAgUHX3RyYWRlcgkAzAgCCQCkAwEFCV9hbW91bnRJbgkAzAgCCQCkAwEFCV9sZXZlcmFnZQkAzAgCCQCkAwEFBV90eXBlCQDMCAIJAKQDAQUNX3RyaWdnZXJQcmljZQkAzAgCCQCkAwEFDF9wYXltZW50VXNkbgkAzAgCCQCkAwEFBV9zaWRlCQDMCAIFCF9yZWZMaW5rCQDMCAIJAKQDAQULX3Bvc2l0aW9uSWQJAMwIAgkApAMBBQtfbGltaXRQcmljZQUDbmlsAgEsCQDMCAIJAQtTdHJpbmdFbnRyeQIJAQhvcmRlcktleQEFCF9vcmRlcklkBQhvcmRlclN0cgUDbmlsARBhZGRSZW1vdmVPcmRlcklkBAhfb3JkZXJJZARfYW1tB190cmFkZXIEX2FkZAQIb3JkZXJJZHMJARJ0cmFkZXJBbW1PcmRlcnNJZHMCBQRfYW1tBQdfdHJhZGVyBAtvcmRlcklkc05ldwMFBF9hZGQJAM0IAgUIb3JkZXJJZHMJAKQDAQUIX29yZGVySWQJANEIAgUIb3JkZXJJZHMJARN2YWx1ZU9yRXJyb3JNZXNzYWdlAgkAzwgCBQhvcmRlcklkcwkApAMBBQhfb3JkZXJJZAkArAICAhJObyBvcmRlciB3aXRoIGlkOiAJAKQDAQUIX29yZGVySWQEDm9yZGVySWRzTmV3U3RyCQC5CQIFC29yZGVySWRzTmV3AgEsCQDMCAIJAQtTdHJpbmdFbnRyeQIJARF0cmFkZXJPcmRlcklkc0tleQIFBF9hbW0FB190cmFkZXIFDm9yZGVySWRzTmV3U3RyBQNuaWwBFnVwZGF0ZVRyYWRlck9yZGVyQ291bnQDBF9hbW0HX3RyYWRlcgZfY291bnQJAMwIAgkBDEludGVnZXJFbnRyeQIJARN0cmFkZXJPcmRlckNvdW50S2V5AgUEX2FtbQUHX3RyYWRlcgUGX2NvdW50BQNuaWwBEXVwZGF0ZUxhc3RPcmRlcklkAQxfbGFzdE9yZGVySWQJAMwIAgkBDEludGVnZXJFbnRyeQIFDWtfbGFzdE9yZGVySWQFDF9sYXN0T3JkZXJJZAUDbmlsARBtYXJrRXhlY3V0ZU9yZGVyAQhfb3JkZXJJZAkAzAgCCQEMQm9vbGVhbkVudHJ5AgkBDnRvQ29tcG9zaXRlS2V5AgUQa19leGVjdXRlZE9yZGVycwkApAMBBQhfb3JkZXJJZAYFA25pbAEPbWFya0NhbmNlbE9yZGVyAQhfb3JkZXJJZAkAzAgCCQEMQm9vbGVhbkVudHJ5AgkBDnRvQ29tcG9zaXRlS2V5AgUQa19jYW5jZWxlZE9yZGVycwkApAMBBQhfb3JkZXJJZAYFA25pbAoBaQESY2xlYW5VcFN0YWxlT3JkZXJzAgRfYW1tB190cmFkZXIEBm9yZGVycwkBEnRyYWRlckFtbU9yZGVyc0lkcwIFBF9hbW0FB190cmFkZXIECm9yZGVyQ291bnQJARNnZXRUcmFkZXJPcmRlckNvdW50AgUEX2FtbQUHX3RyYWRlcgoBCmNsZWFuVXBPbmUCBF9hY2MIX29yZGVySWQECm9yZGVySWRJbnQJARN2YWx1ZU9yRXJyb3JNZXNzYWdlAgkAtgkBBQhfb3JkZXJJZAIQSW52YWxpZCBvcmRlciBpZAQLJHQwODM4Mzg1NzgJAQhnZXRPcmRlcgEFCm9yZGVySWRJbnQEA194MQgFCyR0MDgzODM4NTc4Al8xBANfeDIIBQskdDA4MzgzODU3OAJfMgQDX3gzCAULJHQwODM4Mzg1NzgCXzMEA194NAgFCyR0MDgzODM4NTc4Al80BAVfdHlwZQgFCyR0MDgzODM4NTc4Al81BANfeDUIBQskdDA4MzgzODU3OAJfNgQDX3g2CAULJHQwODM4Mzg1NzgCXzcEA194NwgFCyR0MDgzODM4NTc4Al84BANfeDgIBQskdDA4MzgzODU3OAJfOQQLX3Bvc2l0aW9uSWQIBQskdDA4MzgzODU3OANfMTAEA194OQgFCyR0MDgzODM4NTc4A18xMQQMcG9zaXRpb25TaXplCQEPZ2V0UG9zaXRpb25TaXplAgUEX2FtbQUHX3RyYWRlcgQRY3VycmVudFBvc2l0aW9uSWQDCQECIT0CBQxwb3NpdGlvblNpemUAAAkBDWdldFBvc2l0aW9uSWQCBQRfYW1tBQdfdHJhZGVyAAADAwMJAAACBQVfdHlwZQUEU1RPUAYJAAACBQVfdHlwZQUEVEFLRQkBAiE9AgURY3VycmVudFBvc2l0aW9uSWQFC19wb3NpdGlvbklkBwQGY2hhbmdlCQDOCAIJAQ9tYXJrQ2FuY2VsT3JkZXIBBQpvcmRlcklkSW50CQEQYWRkUmVtb3ZlT3JkZXJJZAQFCm9yZGVySWRJbnQFBF9hbW0FB190cmFkZXIHCQCUCgIJAGQCCAUEX2FjYwJfMQABCQDOCAIIBQRfYWNjAl8yBQZjaGFuZ2UFBF9hY2MEBnJlc3VsdAoAAiRsBQZvcmRlcnMKAAIkcwkAkAMBBQIkbAoABSRhY2MwCQCUCgIAAAUDbmlsCgEFJGYwXzECAiRhAiRpAwkAZwIFAiRpBQIkcwUCJGEJAQpjbGVhblVwT25lAgUCJGEJAJEDAgUCJGwFAiRpCgEFJGYwXzICAiRhAiRpAwkAZwIFAiRpBQIkcwUCJGEJAAIBAhNMaXN0IHNpemUgZXhjZWVkcyA1CQEFJGYwXzICCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECBQUkYWNjMAAAAAEAAgADAAQABQkAzggCCAUGcmVzdWx0Al8yCQEWdXBkYXRlVHJhZGVyT3JkZXJDb3VudAMFBF9hbW0FB190cmFkZXIJAGUCBQpvcmRlckNvdW50CAUGcmVzdWx0Al8xAWkBCnNldENvbnRleHQBB19zZW5kZXIDCQECIT0CCAUBaQZjYWxsZXIFBHRoaXMJAAIBAg5Pbmx5IHNlbGYtY2FsbAkAzAgCCQELU3RyaW5nRW50cnkCBQhrX3NlbmRlcgUHX3NlbmRlcgUDbmlsAWkBDHJlc2V0Q29udGV4dAADCQECIT0CCAUBaQZjYWxsZXIFBHRoaXMJAAIBAg5Pbmx5IHNlbGYtY2FsbAkAzAgCCQELRGVsZXRlRW50cnkBBQhrX3NlbmRlcgUDbmlsAWkBCmluaXRpYWxpemUBDF9jb29yZGluYXRvcgMJAQtpbml0aWFsaXplZAAJAAIBAhNBbHJlYWR5IGluaXRpYWxpemVkCQDMCAIJAQtTdHJpbmdFbnRyeQIFFGtfY29vcmRpbmF0b3JBZGRyZXNzBQxfY29vcmRpbmF0b3IJAMwIAgkBDEJvb2xlYW5FbnRyeQIFDWtfaW5pdGlhbGl6ZWQGBQNuaWwBaQELY3JlYXRlT3JkZXIIBF9hbW0FX3R5cGUNX3RyaWdnZXJQcmljZQtfbGltaXRQcmljZQlfYW1vdW50SW4JX2xldmVyYWdlBV9zaWRlCF9yZWZMaW5rBAdfdHJhZGVyCQClCAEIBQFpBmNhbGxlcgQHY2xlYW5VcAkA/AcEBQR0aGlzAhJjbGVhblVwU3RhbGVPcmRlcnMJAMwIAgUEX2FtbQkAzAgCBQdfdHJhZGVyBQNuaWwFA25pbAMJAAACBQdjbGVhblVwBQdjbGVhblVwAwkAZgIJAJADAQgFAWkIcGF5bWVudHMAAQkAAgECNUludmFsaWQgY3JlYXRlT3JkZXIgcGFyYW1ldGVyczogaW52YWxpZCBwYXltZW50IGNvdW50BA0kdDAxMDI4NzEwNTM5AwkAAAIJAJADAQgFAWkIcGF5bWVudHMAAQkAlAoCCQDYBAEJARN2YWx1ZU9yRXJyb3JNZXNzYWdlAggJAJEDAggFAWkIcGF5bWVudHMAAAdhc3NldElkAhBJbnZhbGlkIGFzc2V0IGlkCAkAkQMCCAUBaQhwYXltZW50cwAABmFtb3VudAkAlAoCAgAAAAQOcGF5bWVudEFzc2V0SWQIBQ0kdDAxMDI4NzEwNTM5Al8xBA1wYXltZW50QW1vdW50CAUNJHQwMTAyODcxMDUzOQJfMgQGZG9DYWxsCQD8BwQFBHRoaXMCE2ludGVybmFsQ3JlYXRlT3JkZXIJAMwIAgUHX3RyYWRlcgkAzAgCBQRfYW1tCQDMCAIFBV90eXBlCQDMCAIFDV90cmlnZ2VyUHJpY2UJAMwIAgULX2xpbWl0UHJpY2UJAMwIAgUJX2Ftb3VudEluCQDMCAIFCV9sZXZlcmFnZQkAzAgCBQVfc2lkZQkAzAgCBQhfcmVmTGluawkAzAgCBQ5wYXltZW50QXNzZXRJZAkAzAgCBQ1wYXltZW50QW1vdW50BQNuaWwFA25pbAMJAAACBQZkb0NhbGwFBmRvQ2FsbAUDbmlsCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuAWkBHGluY3JlYXNlUG9zaXRpb25XaXRoU3RvcExvc3MJBF9hbW0KX2RpcmVjdGlvbglfbGV2ZXJhZ2UTX21pbkJhc2VBc3NldEFtb3VudAhfcmVmTGluaxFfc3RvcFRyaWdnZXJQcmljZQ9fc3RvcExpbWl0UHJpY2URX3Rha2VUcmlnZ2VyUHJpY2UPX3Rha2VMaW1pdFByaWNlBAdfdHJhZGVyCQClCAEIBQFpBmNhbGxlcgQHY2xlYW5VcAkA/AcEBQR0aGlzAhJjbGVhblVwU3RhbGVPcmRlcnMJAMwIAgUEX2FtbQkAzAgCBQdfdHJhZGVyBQNuaWwFA25pbAMJAAACBQdjbGVhblVwBQdjbGVhblVwAwMJAQEhAQkBC2luaXRpYWxpemVkAAYJAQEhAQkBC2lzV2hpdGVsaXN0AQUEX2FtbQkAAgECL0ludmFsaWQgaW5jcmVhc2VQb3NpdGlvbldpdGhTdG9wTG9zcyBwYXJhbWV0ZXJzBAxwb3NpdGlvblNpemUJAQ9nZXRQb3NpdGlvblNpemUCBQRfYW1tBQdfdHJhZGVyAwkBAiE9AgUMcG9zaXRpb25TaXplAAAJAAIBAkJJbnZhbGlkIGluY3JlYXNlUG9zaXRpb25XaXRoU3RvcExvc3MgcGFyYW1ldGVyczogb25seSBuZXcgcG9zaXRpb24EDGRvU2V0Q29udGV4dAkA/AcEBQR0aGlzAgpzZXRDb250ZXh0CQDMCAIFB190cmFkZXIFA25pbAUDbmlsAwkAAAIFDGRvU2V0Q29udGV4dAUMZG9TZXRDb250ZXh0BA9kb0Nsb3NlUG9zaXRpb24JAPwHBAkBEUBleHRyTmF0aXZlKDEwNjIpAQUEX2FtbQIQaW5jcmVhc2VQb3NpdGlvbgkAzAgCBQpfZGlyZWN0aW9uCQDMCAIFCV9sZXZlcmFnZQkAzAgCBRNfbWluQmFzZUFzc2V0QW1vdW50CQDMCAIFCF9yZWZMaW5rBQNuaWwIBQFpCHBheW1lbnRzAwkAAAIFD2RvQ2xvc2VQb3NpdGlvbgUPZG9DbG9zZVBvc2l0aW9uBA5kb1Jlc2V0Q29udGV4dAkA/AcEBQR0aGlzAgxyZXNldENvbnRleHQFA25pbAUDbmlsAwkAAAIFDmRvUmVzZXRDb250ZXh0BQ5kb1Jlc2V0Q29udGV4dAQSb3BlbmVkUG9zaXRpb25TaXplCQEPZ2V0UG9zaXRpb25TaXplAgUEX2FtbQUHX3RyYWRlcgMJAAACBRJvcGVuZWRQb3NpdGlvblNpemUFEm9wZW5lZFBvc2l0aW9uU2l6ZQQIYW1vdW50SW4JAQNhYnMBBRJvcGVuZWRQb3NpdGlvblNpemUEDHN0b3BMb3NzU2lkZQMJAGYCAAAFEm9wZW5lZFBvc2l0aW9uU2l6ZQUETE9ORwUFU0hPUlQEEWRvQ3JlYXRlU3RvcE9yZGVyAwkAZgIFEV9zdG9wVHJpZ2dlclByaWNlAAAEEWRvQ3JlYXRlU3RvcE9yZGVyCQD8BwQFBHRoaXMCE2ludGVybmFsQ3JlYXRlT3JkZXIJAMwIAgUHX3RyYWRlcgkAzAgCBQRfYW1tCQDMCAIFBFNUT1AJAMwIAgURX3N0b3BUcmlnZ2VyUHJpY2UJAMwIAgUPX3N0b3BMaW1pdFByaWNlCQDMCAIFCGFtb3VudEluCQDMCAIAAAkAzAgCBQxzdG9wTG9zc1NpZGUJAMwIAgUIX3JlZkxpbmsJAMwIAgIACQDMCAIAAAUDbmlsBQNuaWwDCQAAAgURZG9DcmVhdGVTdG9wT3JkZXIFEWRvQ3JlYXRlU3RvcE9yZGVyBQNuaWwJAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4FA25pbAMJAAACBRFkb0NyZWF0ZVN0b3BPcmRlcgURZG9DcmVhdGVTdG9wT3JkZXIEEWRvQ3JlYXRlVGFrZU9yZGVyAwkAZgIFEV90YWtlVHJpZ2dlclByaWNlAAAEEWRvQ3JlYXRlVGFrZU9yZGVyCQD8BwQFBHRoaXMCE2ludGVybmFsQ3JlYXRlT3JkZXIJAMwIAgUHX3RyYWRlcgkAzAgCBQRfYW1tCQDMCAIFBFRBS0UJAMwIAgURX3Rha2VUcmlnZ2VyUHJpY2UJAMwIAgUPX3Rha2VMaW1pdFByaWNlCQDMCAIFCGFtb3VudEluCQDMCAIAAAkAzAgCBQxzdG9wTG9zc1NpZGUJAMwIAgUIX3JlZkxpbmsJAMwIAgIACQDMCAIAAAUDbmlsBQNuaWwDCQAAAgURZG9DcmVhdGVUYWtlT3JkZXIFEWRvQ3JlYXRlVGFrZU9yZGVyBQNuaWwJAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4FA25pbAMJAAACBRFkb0NyZWF0ZVRha2VPcmRlcgURZG9DcmVhdGVUYWtlT3JkZXIFA25pbAkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgFpARNpbnRlcm5hbENyZWF0ZU9yZGVyCwdfdHJhZGVyBF9hbW0FX3R5cGUNX3RyaWdnZXJQcmljZQtfbGltaXRQcmljZQlfYW1vdW50SW4JX2xldmVyYWdlBV9zaWRlCF9yZWZMaW5rD19wYXltZW50QXNzZXRJZA5fcGF5bWVudEFtb3VudAMDAwMDAwMDAwkBASEBCQELaW5pdGlhbGl6ZWQABgkBASEBCQELaXNXaGl0ZWxpc3QBBQRfYW1tBgkAZwIAAAUNX3RyaWdnZXJQcmljZQYJAGYCAAAFC19saW1pdFByaWNlBgkAZwIAAAUJX2Ftb3VudEluBgkAZgIAAAUJX2xldmVyYWdlBgkBASEBAwkAAAIFBV9zaWRlBQRMT05HBgkAAAIFBV9zaWRlBQVTSE9SVAYJAQEhAQMDCQAAAgUFX3R5cGUFBFNUT1AGCQAAAgUFX3R5cGUFBFRBS0UGCQAAAgUFX3R5cGUFBUxJTUlUBgkBASEBCQAAAggFAWkGY2FsbGVyBQR0aGlzCQACAQIeSW52YWxpZCBjcmVhdGVPcmRlciBwYXJhbWV0ZXJzBAdvcmRlcklkCQBkAgkBDmN1cnJlbnRPcmRlcklkAAABBBNuZXdUcmFkZXJPcmRlckNvdW50CQBkAgkBE2dldFRyYWRlck9yZGVyQ291bnQCBQRfYW1tBQdfdHJhZGVyAAEEDHBvc2l0aW9uU2l6ZQkBD2dldFBvc2l0aW9uU2l6ZQIFBF9hbW0FB190cmFkZXIECl9kaXJlY3Rpb24DAwMJAAACBQxwb3NpdGlvblNpemUAAAYDCQBmAgUMcG9zaXRpb25TaXplAAAJAAACBQVfc2lkZQUETE9ORwcGAwkAZgIAAAUMcG9zaXRpb25TaXplCQAAAgUFX3NpZGUFBVNIT1JUBwUISU5DUkVBU0UFCERFQ1JFQVNFAwMJAAACBQxwb3NpdGlvblNpemUAAAMJAAACBQVfdHlwZQUEU1RPUAYJAAACBQVfdHlwZQUEVEFLRQcJAAIBAitDYW4gbm90IGNyZWF0ZSBTVE9QL1RBS0Ugb3JkZXI6IG5vIHBvc2l0aW9uBAt1c2RuUGF5bWVudAMJAAACBQpfZGlyZWN0aW9uBQhJTkNSRUFTRQMDCQECIT0CBQ9fcGF5bWVudEFzc2V0SWQJANgEAQkBCnF1b3RlQXNzZXQABgkBAiE9AgUOX3BheW1lbnRBbW91bnQFCV9hbW91bnRJbgkAAgECNEludmFsaWQgY3JlYXRlTGltaXRPcmRlciBwYXJhbWV0ZXJzOiBpbnZhbGlkIHBheW1lbnQEBXN0YWtlCQD8BwQJAQ5tYW5hZ2VyQWRkcmVzcwACB2RlcG9zaXQFA25pbAkAzAgCCQEPQXR0YWNoZWRQYXltZW50AgkBCnF1b3RlQXNzZXQABQ5fcGF5bWVudEFtb3VudAUDbmlsAwkAAAIFBXN0YWtlBQVzdGFrZQUOX3BheW1lbnRBbW91bnQJAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4AAAMJAAACBQt1c2RuUGF5bWVudAULdXNkblBheW1lbnQECnBvc2l0aW9uSWQDCQECIT0CBQxwb3NpdGlvblNpemUAAAkBDWdldFBvc2l0aW9uSWQCBQRfYW1tBQdfdHJhZGVyAAADCQBmAgUTbmV3VHJhZGVyT3JkZXJDb3VudAUZTUFYX1RSQURFUl9PUkRFUlNfUEVSX0FNTQkAAgECMEludmFsaWQgY3JlYXRlTGltaXRPcmRlciBwYXJhbWV0ZXJzOiBvcmRlciBjb3VudAkAzggCCQDOCAIJAM4IAgkBCXNhdmVPcmRlcgwFB29yZGVySWQFBF9hbW0FB190cmFkZXIFCV9hbW91bnRJbgUJX2xldmVyYWdlBQVfdHlwZQUNX3RyaWdnZXJQcmljZQULdXNkblBheW1lbnQFBV9zaWRlBQhfcmVmTGluawUKcG9zaXRpb25JZAULX2xpbWl0UHJpY2UJARBhZGRSZW1vdmVPcmRlcklkBAUHb3JkZXJJZAUEX2FtbQUHX3RyYWRlcgYJARZ1cGRhdGVUcmFkZXJPcmRlckNvdW50AwUEX2FtbQUHX3RyYWRlcgUTbmV3VHJhZGVyT3JkZXJDb3VudAkBEXVwZGF0ZUxhc3RPcmRlcklkAQUHb3JkZXJJZAkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgFpAQtjYW5jZWxPcmRlcgEIX29yZGVySWQEDSR0MDE1OTA4MTYxMjcJAQhnZXRPcmRlcgEFCF9vcmRlcklkBARfYW1tCAUNJHQwMTU5MDgxNjEyNwJfMQQHX3RyYWRlcggFDSR0MDE1OTA4MTYxMjcCXzIECV9hbW91bnRJbggFDSR0MDE1OTA4MTYxMjcCXzMECV9sZXZlcmFnZQgFDSR0MDE1OTA4MTYxMjcCXzQEBV90eXBlCAUNJHQwMTU5MDgxNjEyNwJfNQQNX3RyaWdnZXJQcmljZQgFDSR0MDE1OTA4MTYxMjcCXzYEC19hbW91bnRVc2RuCAUNJHQwMTU5MDgxNjEyNwJfNwQFX3NpZGUIBQ0kdDAxNTkwODE2MTI3Al84BAhfcmVmTGluawgFDSR0MDE1OTA4MTYxMjcCXzkEC19wb3NpdGlvbklkCAUNJHQwMTU5MDgxNjEyNwNfMTAEC19saW1pdFByaWNlCAUNJHQwMTU5MDgxNjEyNwNfMTEDAwMJAQEhAQkBC2luaXRpYWxpemVkAAYJAQEhAQkBB2lzVmFsaWQBBQhfb3JkZXJJZAYJAQEhAQkAAAIJAKUIAQgFAWkGY2FsbGVyBQdfdHJhZGVyCQACAQIeSW52YWxpZCBjYW5jZWxPcmRlciBwYXJhbWV0ZXJzBAdjbGVhblVwCQD8BwQFBHRoaXMCEmNsZWFuVXBTdGFsZU9yZGVycwkAzAgCBQRfYW1tCQDMCAIFB190cmFkZXIFA25pbAUDbmlsAwkAAAIFB2NsZWFuVXAFB2NsZWFuVXAEE25ld1RyYWRlck9yZGVyQ291bnQJAGUCCQETZ2V0VHJhZGVyT3JkZXJDb3VudAIFBF9hbW0FB190cmFkZXIAAQQId2l0aGRyYXcDCQBmAgULX2Ftb3VudFVzZG4AAAQHdW5zdGFrZQkA/AcECQEObWFuYWdlckFkZHJlc3MAAgh3aXRoZHJhdwkAzAgCCQDYBAEJAQpxdW90ZUFzc2V0AAkAzAgCBQtfYW1vdW50VXNkbgUDbmlsBQNuaWwDCQAAAgUHdW5zdGFrZQUHdW5zdGFrZQUDbmlsCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuBQNuaWwDCQAAAgUId2l0aGRyYXcFCHdpdGhkcmF3CQDOCAIJAM4IAgkAzggCCQEPbWFya0NhbmNlbE9yZGVyAQUIX29yZGVySWQJARBhZGRSZW1vdmVPcmRlcklkBAUIX29yZGVySWQFBF9hbW0FB190cmFkZXIHCQEWdXBkYXRlVHJhZGVyT3JkZXJDb3VudAMFBF9hbW0FB190cmFkZXIFE25ld1RyYWRlck9yZGVyQ291bnQDCQBmAgULX2Ftb3VudFVzZG4AAAkAzAgCCQEOU2NyaXB0VHJhbnNmZXIDCAUBaQZjYWxsZXIFC19hbW91bnRVc2RuCQEKcXVvdGVBc3NldAAFA25pbAUDbmlsCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuAWkBDGV4ZWN1dGVPcmRlcgEIX29yZGVySWQEDSR0MDE3MDk4MTczMTcJAQhnZXRPcmRlcgEFCF9vcmRlcklkBARfYW1tCAUNJHQwMTcwOTgxNzMxNwJfMQQHX3RyYWRlcggFDSR0MDE3MDk4MTczMTcCXzIECV9hbW91bnRJbggFDSR0MDE3MDk4MTczMTcCXzMECV9sZXZlcmFnZQgFDSR0MDE3MDk4MTczMTcCXzQEBV90eXBlCAUNJHQwMTcwOTgxNzMxNwJfNQQNX3RyaWdnZXJQcmljZQgFDSR0MDE3MDk4MTczMTcCXzYEC19hbW91bnRVc2RuCAUNJHQwMTcwOTgxNzMxNwJfNwQFX3NpZGUIBQ0kdDAxNzA5ODE3MzE3Al84BAhfcmVmTGluawgFDSR0MDE3MDk4MTczMTcCXzkEC19wb3NpdGlvbklkCAUNJHQwMTcwOTgxNzMxNwNfMTAEC19saW1pdFByaWNlCAUNJHQwMTcwOTgxNzMxNwNfMTEEB2NsZWFuVXAJAPwHBAUEdGhpcwISY2xlYW5VcFN0YWxlT3JkZXJzCQDMCAIFBF9hbW0JAMwIAgUHX3RyYWRlcgUDbmlsBQNuaWwDCQAAAgUHY2xlYW5VcAUHY2xlYW5VcAMDCQEBIQEJAQtpbml0aWFsaXplZAAGCQEBIQEJAQdpc1ZhbGlkAQUIX29yZGVySWQJAAIBAh9JbnZhbGlkIGV4ZWN1dGVPcmRlciBwYXJhbWV0ZXJzBAxwb3NpdGlvblNpemUJAQ9nZXRQb3NpdGlvblNpemUCBQRfYW1tBQdfdHJhZGVyBBFjdXJyZW50UG9zaXRpb25JZAMJAQIhPQIFDHBvc2l0aW9uU2l6ZQAACQENZ2V0UG9zaXRpb25JZAIFBF9hbW0FB190cmFkZXIAAAQNJHQwMTc3MjEyMTU0MgMJAAACBQVfdHlwZQUEU1RPUAQSX3Bvc2l0aW9uRGlyZWN0aW9uAwkAZgIFDHBvc2l0aW9uU2l6ZQAABQRMT05HAwkAZgIAAAUMcG9zaXRpb25TaXplBQVTSE9SVAkAAgECLENhbiBub3QgZXhlY3V0ZSBTVE9QIG9yZGVyOiBubyBvcGVuIHBvc2l0aW9uBAttYXJrZXRQcmljZQkBDmdldE1hcmtldFByaWNlAQUEX2FtbQQMaXNFeGVjdXRhYmxlAwkAAAIFBV9zaWRlBRJfcG9zaXRpb25EaXJlY3Rpb24JAAIBAidDYW4gbm90IGV4ZWN1dGUgU1RPUCBvcmRlcjogcmVkdWNlIG9ubHkDCQECIT0CBRFjdXJyZW50UG9zaXRpb25JZAULX3Bvc2l0aW9uSWQJAAIBAitDYW4gbm90IGV4ZWN1dGUgU1RPUCBvcmRlcjogcG9zaXRpb24gY2xvc2VkAwkAAAIFEl9wb3NpdGlvbkRpcmVjdGlvbgUETE9ORwkAZwIFDV90cmlnZ2VyUHJpY2UFC21hcmtldFByaWNlCQBnAgULbWFya2V0UHJpY2UFDV90cmlnZ2VyUHJpY2UDBQxpc0V4ZWN1dGFibGUJAJUKAwINY2xvc2VQb3NpdGlvbgkAzAgCCQEEbWludgIFCV9hbW91bnRJbgkBA2FicwEFDHBvc2l0aW9uU2l6ZQkAzAgCCQEEbXVsZAIFC19saW1pdFByaWNlCQEDYWJzAQUMcG9zaXRpb25TaXplBQNuaWwFA25pbAkAAgECMUNhbiBub3QgZXhlY3V0ZSBTVE9QIG9yZGVyOiB0cmlnZ2VyUHJpY2UgbWlzbWF0Y2gDCQAAAgUFX3R5cGUFBFRBS0UEEl9wb3NpdGlvbkRpcmVjdGlvbgMJAGYCBQxwb3NpdGlvblNpemUAAAUETE9ORwMJAGYCAAAFDHBvc2l0aW9uU2l6ZQUFU0hPUlQJAAIBAixDYW4gbm90IGV4ZWN1dGUgU1RPUCBvcmRlcjogbm8gb3BlbiBwb3NpdGlvbgQLbWFya2V0UHJpY2UJAQ5nZXRNYXJrZXRQcmljZQEFBF9hbW0EDGlzRXhlY3V0YWJsZQMJAAACBQVfc2lkZQUSX3Bvc2l0aW9uRGlyZWN0aW9uCQACAQInQ2FuIG5vdCBleGVjdXRlIFRBS0Ugb3JkZXI6IHJlZHVjZSBvbmx5AwkBAiE9AgURY3VycmVudFBvc2l0aW9uSWQFC19wb3NpdGlvbklkCQACAQkArAICCQCsAgIJAKwCAgIsQ2FuIG5vdCBleGVjdXRlIFRBS0Ugb3JkZXI6IHBvc2l0aW9uIGNsb3NlZCAJAKQDAQURY3VycmVudFBvc2l0aW9uSWQCAiE9CQCkAwEFC19wb3NpdGlvbklkAwkAAAIFEl9wb3NpdGlvbkRpcmVjdGlvbgUETE9ORwkAZwIFC21hcmtldFByaWNlBQ1fdHJpZ2dlclByaWNlCQBnAgUNX3RyaWdnZXJQcmljZQULbWFya2V0UHJpY2UDBQxpc0V4ZWN1dGFibGUJAJUKAwINY2xvc2VQb3NpdGlvbgkAzAgCCQEEbWludgIFCV9hbW91bnRJbgkBA2FicwEFDHBvc2l0aW9uU2l6ZQkAzAgCCQEEbXVsZAIFC19saW1pdFByaWNlCQEDYWJzAQUMcG9zaXRpb25TaXplBQNuaWwFA25pbAkAAgECMUNhbiBub3QgZXhlY3V0ZSBUQUtFIG9yZGVyOiB0cmlnZ2VyUHJpY2UgbWlzbWF0Y2gDCQAAAgUFX3R5cGUFBUxJTUlUBAttYXJrZXRQcmljZQkBDmdldE1hcmtldFByaWNlAQUEX2FtbQQGc3ByZWFkAwkAAAIFC19saW1pdFByaWNlAAAJAQlnZXRTcHJlYWQBBQ1fdHJpZ2dlclByaWNlCQEDYWJzAQkAZQIFDV90cmlnZ2VyUHJpY2UFC19saW1pdFByaWNlBAxpc0V4ZWN1dGFibGUDCQBnAgULbWFya2V0UHJpY2UJAGUCBQ1fdHJpZ2dlclByaWNlBQZzcHJlYWQJAGcCCQBkAgUNX3RyaWdnZXJQcmljZQUGc3ByZWFkBQttYXJrZXRQcmljZQcDBQxpc0V4ZWN1dGFibGUEEl9wb3NpdGlvbkRpcmVjdGlvbgMJAGYCBQxwb3NpdGlvblNpemUAAAUETE9ORwMJAGYCAAAFDHBvc2l0aW9uU2l6ZQUFU0hPUlQA////////////AQQJZGlyZWN0aW9uAwkAAAIFDHBvc2l0aW9uU2l6ZQAABQhJTkNSRUFTRQMJAAACBRJfcG9zaXRpb25EaXJlY3Rpb24FBV9zaWRlBQhJTkNSRUFTRQUIREVDUkVBU0UDCQAAAgUJZGlyZWN0aW9uBQhJTkNSRUFTRQQPYW1vdW50SW5XaXRoRmVlCQBlAgULX2Ftb3VudFVzZG4JAQRtdWxkAgULX2Ftb3VudFVzZG4JAQZnZXRGZWUCBQRfYW1tBQdfdHJhZGVyCQCVCgMCEGluY3JlYXNlUG9zaXRpb24JAMwIAgUFX3NpZGUJAMwIAgUJX2xldmVyYWdlCQDMCAIDCQAAAgULX2xpbWl0UHJpY2UAAAAACQEEZGl2ZAIFD2Ftb3VudEluV2l0aEZlZQULX2xpbWl0UHJpY2UJAMwIAgUIX3JlZkxpbmsFA25pbAkAzAgCCQEPQXR0YWNoZWRQYXltZW50AgkBCnF1b3RlQXNzZXQABQtfYW1vdW50VXNkbgUDbmlsCQCVCgMCDWNsb3NlUG9zaXRpb24JAMwIAgUJX2Ftb3VudEluCQDMCAIJAQRtdWxkAgUJX2Ftb3VudEluBQtfbGltaXRQcmljZQUDbmlsBQNuaWwJAAIBAjJDYW4gbm90IGV4ZWN1dGUgTElNSVQgb3JkZXI6IHRyaWdnZXJQcmljZSBtaXNtYXRjaAkAAgEJAKwCAgIUSW52YWxpZCBvcmRlciB0eXBlOiAJAKQDAQUFX3R5cGUEBm1ldGhvZAgFDSR0MDE3NzIxMjE1NDICXzEEBGFyZ3MIBQ0kdDAxNzcyMTIxNTQyAl8yBAhwYXltZW50cwgFDSR0MDE3NzIxMjE1NDICXzMECHdpdGhkcmF3AwkAAAIJAJADAQUIcGF5bWVudHMAAQQHdW5zdGFrZQkA/AcECQEObWFuYWdlckFkZHJlc3MAAgh3aXRoZHJhdwkAzAgCCQDYBAEJAQpxdW90ZUFzc2V0AAkAzAgCCAkAkQMCBQhwYXltZW50cwAABmFtb3VudAUDbmlsBQNuaWwDCQAAAgUHdW5zdGFrZQUHdW5zdGFrZQUDbmlsCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuBQNuaWwDCQAAAgUId2l0aGRyYXcFCHdpdGhkcmF3BAxkb1NldENvbnRleHQJAPwHBAUEdGhpcwIKc2V0Q29udGV4dAkAzAgCBQdfdHJhZGVyBQNuaWwFA25pbAMJAAACBQxkb1NldENvbnRleHQFDGRvU2V0Q29udGV4dAQPZG9DbG9zZVBvc2l0aW9uCQD8BwQJARFAZXh0ck5hdGl2ZSgxMDYyKQEFBF9hbW0FBm1ldGhvZAUEYXJncwUIcGF5bWVudHMDCQAAAgUPZG9DbG9zZVBvc2l0aW9uBQ9kb0Nsb3NlUG9zaXRpb24EDmRvUmVzZXRDb250ZXh0CQD8BwQFBHRoaXMCDHJlc2V0Q29udGV4dAUDbmlsBQNuaWwDCQAAAgUOZG9SZXNldENvbnRleHQFDmRvUmVzZXRDb250ZXh0BBNuZXdUcmFkZXJPcmRlckNvdW50CQBlAgkBE2dldFRyYWRlck9yZGVyQ291bnQCBQRfYW1tBQdfdHJhZGVyAAEJAM4IAgkAzggCCQEWdXBkYXRlVHJhZGVyT3JkZXJDb3VudAMFBF9hbW0FB190cmFkZXIFE25ld1RyYWRlck9yZGVyQ291bnQJARBhZGRSZW1vdmVPcmRlcklkBAUIX29yZGVySWQFBF9hbW0FB190cmFkZXIHCQEQbWFya0V4ZWN1dGVPcmRlcgEFCF9vcmRlcklkCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuAWkBFHZpZXdfY2FuRXhlY3V0ZU9yZGVyAQhfb3JkZXJJZAQBcwkA/AcEBQR0aGlzAgxleGVjdXRlT3JkZXIJAMwIAgUIX29yZGVySWQFA25pbAUDbmlsAwkAAAIFAXMFAXMJAAIBAgdTdWNjZXNzCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuAQJ0eAEGdmVyaWZ5AAkA9AMDCAUCdHgJYm9keUJ5dGVzCQCRAwIIBQJ0eAZwcm9vZnMAAAkBDmFkbWluUHVibGljS2V5ABy9SNs=", "height": 2397174, "applicationStatus": "succeeded", "spentComplexity": 0 } View: original | compacted Prev: 67rL8AVvC7VQ37dptjZm8ZXwqYraFf8gfxETRdmkMAq2 Next: JAco3pQugD5qhd1Mc8q5S4gFRzpcvjbSBKy3PhxfXvcR Diff:
OldNewDifferences
55
66 let k_admin_public_key = "k_admin_public_key"
77
8-let k_quoteAssetReserve = "k_qtAstR"
8+let k_quote_asset = "k_quote_asset"
99
10-let k_baseAssetReserve = "k_bsAstR"
10+let k_amm = "k_amm"
1111
12-let k_positionClosedDate = "k_positionClosedDate"
12+let k_manager_address = "k_manager_address"
13+
14+let k_positionSequence = "k_positionSequence"
15+
16+let k_positionSize = "k_positionSize"
17+
18+let k_fee = "k_fee"
1319
1420 let k_executedOrders = "k_executedOrders"
21+
22+let k_canceledOrders = "k_canceledOrders"
23+
24+let k_order = "k_order"
25+
26+let k_lastOrderId = "k_lastOrderId"
27+
28+let k_traderOrderCnt = "k_traderOrderCnt"
29+
30+let k_traderOrderIds = "k_traderOrderIds"
1531
1632 let k_sender = "k_sender"
1733
1834 let k_initialized = "k_initialized"
1935
20-let TAKE_PROFIT = 1
36+let STOP = 1
2137
22-let STOP_LOSS = 2
38+let TAKE = 2
39+
40+let LIMIT = 3
41+
42+let LONG = 1
43+
44+let SHORT = 2
45+
46+let INCREASE = 1
47+
48+let DECREASE = 2
49+
50+let MAX_TRADER_ORDERS_PER_AMM = 5
2351
2452 let TIME = lastBlock.timestamp
2553
2654 let DECIMAL_UNIT = (1 * (((((10 * 10) * 10) * 10) * 10) * 10))
55+
56+let SPREAD_LIMIT = (DECIMAL_UNIT / 200)
57+
58+func abs (_x) = if ((_x > 0))
59+ then _x
60+ else -(_x)
61+
2762
2863 func divd (_x,_y) = fraction(_x, DECIMAL_UNIT, _y, HALFEVEN)
2964
3166 func muld (_x,_y) = fraction(_x, _y, DECIMAL_UNIT, HALFEVEN)
3267
3368
34-func executedOrderKey (_orderId) = ((k_executedOrders + "_") + _orderId)
69+func minv (_x,_y) = if ((_x > _y))
70+ then _y
71+ else _x
72+
73+
74+func toCompositeKey (_key,_address) = ((_key + "_") + _address)
75+
76+
77+func executedOrderKey (_orderId) = ((k_executedOrders + "_") + toString(_orderId))
78+
79+
80+func canceledOrderKey (_orderId) = ((k_canceledOrders + "_") + toString(_orderId))
81+
82+
83+func orderKey (_orderId) = toCompositeKey(k_order, toString(_orderId))
84+
85+
86+func traderOrderCountKey (_amm,_trader) = ((((k_traderOrderCnt + "_") + _amm) + "_") + _trader)
87+
88+
89+func traderOrderIdsKey (_amm,_trader) = ((((k_traderOrderIds + "_") + _amm) + "_") + _trader)
3590
3691
3792 func coordinator () = valueOrErrorMessage(addressFromString(getStringValue(this, k_coordinatorAddress)), "Coordinator not set")
93+
94+
95+func quoteAsset () = fromBase58String(getStringValue(coordinator(), k_quote_asset))
96+
97+
98+func managerAddress () = valueOrErrorMessage(addressFromString(getStringValue(coordinator(), k_manager_address)), "Manager not set")
99+
100+
101+func isWhitelist (_address) = valueOrElse(getBoolean(coordinator(), toCompositeKey(k_amm, _address)), false)
38102
39103
40104 func adminPublicKey () = fromBase58String(getStringValue(coordinator(), k_admin_public_key))
43107 func initialized () = valueOrElse(getBoolean(this, k_initialized), false)
44108
45109
46-func requireValidOrderSignature (_prefix,_order,_signature,_senderPublicKey) = {
47- let message = toBytes((_prefix + _order))
48- let sig = fromBase58String(_signature)
49- let pub = fromBase58String(_senderPublicKey)
50- if (sigVerify(message, sig, pub))
51- then unit
52- else throw("Invalid order signature")
110+func isValid (_orderId) = if (valueOrElse(getBoolean(this, executedOrderKey(_orderId)), false))
111+ then throw(("Order already executed: " + toString(_orderId)))
112+ else if (valueOrElse(getBoolean(this, canceledOrderKey(_orderId)), false))
113+ then throw(("Order already cancelled: " + toString(_orderId)))
114+ else true
115+
116+
117+func currentOrderId () = valueOrElse(getInteger(this, k_lastOrderId), 0)
118+
119+
120+func getTraderOrderCount (_amm,_trader) = {
121+ let key = traderOrderCountKey(_amm, _trader)
122+ valueOrElse(getInteger(this, key), 0)
53123 }
54124
55125
56-func requireNotExecuted (_orderId) = if (valueOrElse(getBoolean(this, executedOrderKey(_orderId)), false))
57- then throw(("Order already executed: " + _orderId))
58- else unit
126+func traderAmmOrdersIds (_amm,_trader) = {
127+ let key = traderOrderIdsKey(_amm, _trader)
128+ let val = valueOrElse(getString(this, key), "")
129+ if ((val == ""))
130+ then nil
131+ else split(val, ",")
132+ }
133+
134+
135+func getOrder (_orderId) = {
136+ let orderStr = valueOrErrorMessage(getString(this, orderKey(_orderId)), ("Invalid order id: " + toString(_orderId)))
137+ let orderPartList = split(orderStr, ",")
138+ let amm = orderPartList[0]
139+ let trader = orderPartList[1]
140+ let amountIn = valueOrErrorMessage(parseInt(orderPartList[2]), "Invalid amountIn")
141+ let leverage = valueOrErrorMessage(parseInt(orderPartList[3]), "Invalid leverage")
142+ let type = valueOrErrorMessage(parseInt(orderPartList[4]), "Invalid type")
143+ let triggerPrice = valueOrErrorMessage(parseInt(orderPartList[5]), "Invalid triggerPrice")
144+ let paymentUsdn = valueOrErrorMessage(parseInt(orderPartList[6]), "Invalid paymentUsdn")
145+ let side = valueOrErrorMessage(parseInt(orderPartList[7]), "Invalid side")
146+ let refLink = orderPartList[8]
147+ let positionId = valueOrErrorMessage(parseInt(orderPartList[9]), "Invalid positionId")
148+ let limitPrice = valueOrErrorMessage(parseInt(orderPartList[10]), "Invalid limitPrice")
149+ $Tuple11(amm, trader, amountIn, leverage, type, triggerPrice, paymentUsdn, side, refLink, positionId, limitPrice)
150+ }
151+
152+
153+func getMarketPrice (_amm) = {
154+ let s = invoke(addressFromStringValue(_amm), "computeSpotPrice", nil, nil)
155+ if ((s == s))
156+ then {
157+ let res = match s {
158+ case t: Int =>
159+ t
160+ case _ =>
161+ throw("Invalid computeSpotPrice result")
162+ }
163+ value(res)
164+ }
165+ else throw("Strict value is not equal to itself.")
166+ }
167+
168+
169+func getFee (_amm,_trader) = {
170+ let s = invoke(addressFromStringValue(_amm), "computeFeeForTraderWithArtifact", [_trader, ""], nil)
171+ if ((s == s))
172+ then {
173+ let res = match s {
174+ case t: (Int, Boolean) =>
175+ t._1
176+ case _ =>
177+ throw("Invalid computeFeeForTraderWithArtifact result")
178+ }
179+ value(res)
180+ }
181+ else throw("Strict value is not equal to itself.")
182+ }
183+
184+
185+func getPositionSize (_amm,_trader) = {
186+ let amm = addressFromStringValue(_amm)
187+ let sizeKey = toCompositeKey(k_positionSize, _trader)
188+ valueOrElse(getInteger(amm, sizeKey), 0)
189+ }
190+
191+
192+func getPositionId (_amm,_trader) = {
193+ let amm = addressFromStringValue(_amm)
194+ let seqKey = toCompositeKey(k_positionSequence, _trader)
195+ valueOrElse(getInteger(amm, seqKey), 0)
196+ }
197+
198+
199+func getSpread (_price) = muld(_price, SPREAD_LIMIT)
200+
201+
202+func saveOrder (_orderId,_amm,_trader,_amountIn,_leverage,_type,_triggerPrice,_paymentUsdn,_side,_refLink,_positionId,_limitPrice) = {
203+ let orderStr = makeString([_amm, _trader, toString(_amountIn), toString(_leverage), toString(_type), toString(_triggerPrice), toString(_paymentUsdn), toString(_side), _refLink, toString(_positionId), toString(_limitPrice)], ",")
204+[StringEntry(orderKey(_orderId), orderStr)]
205+ }
206+
207+
208+func addRemoveOrderId (_orderId,_amm,_trader,_add) = {
209+ let orderIds = traderAmmOrdersIds(_amm, _trader)
210+ let orderIdsNew = if (_add)
211+ then (orderIds :+ toString(_orderId))
212+ else removeByIndex(orderIds, valueOrErrorMessage(indexOf(orderIds, toString(_orderId)), ("No order with id: " + toString(_orderId))))
213+ let orderIdsNewStr = makeString(orderIdsNew, ",")
214+[StringEntry(traderOrderIdsKey(_amm, _trader), orderIdsNewStr)]
215+ }
216+
217+
218+func updateTraderOrderCount (_amm,_trader,_count) = [IntegerEntry(traderOrderCountKey(_amm, _trader), _count)]
219+
220+
221+func updateLastOrderId (_lastOrderId) = [IntegerEntry(k_lastOrderId, _lastOrderId)]
222+
223+
224+func markExecuteOrder (_orderId) = [BooleanEntry(toCompositeKey(k_executedOrders, toString(_orderId)), true)]
225+
226+
227+func markCancelOrder (_orderId) = [BooleanEntry(toCompositeKey(k_canceledOrders, toString(_orderId)), true)]
228+
229+
230+@Callable(i)
231+func cleanUpStaleOrders (_amm,_trader) = {
232+ let orders = traderAmmOrdersIds(_amm, _trader)
233+ let orderCount = getTraderOrderCount(_amm, _trader)
234+ func cleanUpOne (_acc,_orderId) = {
235+ let orderIdInt = valueOrErrorMessage(parseInt(_orderId), "Invalid order id")
236+ let $t083838578 = getOrder(orderIdInt)
237+ let _x1 = $t083838578._1
238+ let _x2 = $t083838578._2
239+ let _x3 = $t083838578._3
240+ let _x4 = $t083838578._4
241+ let _type = $t083838578._5
242+ let _x5 = $t083838578._6
243+ let _x6 = $t083838578._7
244+ let _x7 = $t083838578._8
245+ let _x8 = $t083838578._9
246+ let _positionId = $t083838578._10
247+ let _x9 = $t083838578._11
248+ let positionSize = getPositionSize(_amm, _trader)
249+ let currentPositionId = if ((positionSize != 0))
250+ then getPositionId(_amm, _trader)
251+ else 0
252+ if (if (if ((_type == STOP))
253+ then true
254+ else (_type == TAKE))
255+ then (currentPositionId != _positionId)
256+ else false)
257+ then {
258+ let change = (markCancelOrder(orderIdInt) ++ addRemoveOrderId(orderIdInt, _amm, _trader, false))
259+ $Tuple2((_acc._1 + 1), (_acc._2 ++ change))
260+ }
261+ else _acc
262+ }
263+
264+ let result = {
265+ let $l = orders
266+ let $s = size($l)
267+ let $acc0 = $Tuple2(0, nil)
268+ func $f0_1 ($a,$i) = if (($i >= $s))
269+ then $a
270+ else cleanUpOne($a, $l[$i])
271+
272+ func $f0_2 ($a,$i) = if (($i >= $s))
273+ then $a
274+ else throw("List size exceeds 5")
275+
276+ $f0_2($f0_1($f0_1($f0_1($f0_1($f0_1($acc0, 0), 1), 2), 3), 4), 5)
277+ }
278+ (result._2 ++ updateTraderOrderCount(_amm, _trader, (orderCount - result._1)))
279+ }
280+
59281
60282
61283 @Callable(i)
80302
81303
82304 @Callable(i)
83-func executeOrder (_prefix,_order,_signature) = {
84- let orderParts = split(_order, ",")
85- let orderType = valueOrErrorMessage(parseInt(orderParts[0]), "Invalid order data [type]")
86- let orderId = toBase58String(sha256(toBytes(_order)))
87- let validateNotExecuted = requireNotExecuted(orderId)
88- if ((validateNotExecuted == validateNotExecuted))
89- then if (if ((orderType == TAKE_PROFIT))
305+func createOrder (_amm,_type,_triggerPrice,_limitPrice,_amountIn,_leverage,_side,_refLink) = {
306+ let _trader = toString(i.caller)
307+ let cleanUp = invoke(this, "cleanUpStaleOrders", [_amm, _trader], nil)
308+ if ((cleanUp == cleanUp))
309+ then if ((size(i.payments) > 1))
310+ then throw("Invalid createOrder parameters: invalid payment count")
311+ else {
312+ let $t01028710539 = if ((size(i.payments) == 1))
313+ then $Tuple2(toBase58String(valueOrErrorMessage(i.payments[0].assetId, "Invalid asset id")), i.payments[0].amount)
314+ else $Tuple2("", 0)
315+ let paymentAssetId = $t01028710539._1
316+ let paymentAmount = $t01028710539._2
317+ let doCall = invoke(this, "internalCreateOrder", [_trader, _amm, _type, _triggerPrice, _limitPrice, _amountIn, _leverage, _side, _refLink, paymentAssetId, paymentAmount], nil)
318+ if ((doCall == doCall))
319+ then nil
320+ else throw("Strict value is not equal to itself.")
321+ }
322+ else throw("Strict value is not equal to itself.")
323+ }
324+
325+
326+
327+@Callable(i)
328+func increasePositionWithStopLoss (_amm,_direction,_leverage,_minBaseAssetAmount,_refLink,_stopTriggerPrice,_stopLimitPrice,_takeTriggerPrice,_takeLimitPrice) = {
329+ let _trader = toString(i.caller)
330+ let cleanUp = invoke(this, "cleanUpStaleOrders", [_amm, _trader], nil)
331+ if ((cleanUp == cleanUp))
332+ then if (if (!(initialized()))
90333 then true
91- else (orderType == STOP_LOSS))
92- then {
93- let amm = orderParts[1]
94- let senderPublicKey = orderParts[2]
95- let price = valueOrErrorMessage(parseInt(orderParts[3]), "Invalid order data [price]")
96- let timestamp = valueOrErrorMessage(parseInt(orderParts[4]), "Invalid order data [timestamp]")
97- let validDue = valueOrErrorMessage(parseInt(orderParts[5]), "Invalid order data [validDue]")
98- let validateSignature = requireValidOrderSignature(_prefix, _order, _signature, senderPublicKey)
99- if ((validateSignature == validateSignature))
334+ else !(isWhitelist(_amm)))
335+ then throw("Invalid increasePositionWithStopLoss parameters")
336+ else {
337+ let positionSize = getPositionSize(_amm, _trader)
338+ if ((positionSize != 0))
339+ then throw("Invalid increasePositionWithStopLoss parameters: only new position")
340+ else {
341+ let doSetContext = invoke(this, "setContext", [_trader], nil)
342+ if ((doSetContext == doSetContext))
343+ then {
344+ let doClosePosition = invoke(addressFromStringValue(_amm), "increasePosition", [_direction, _leverage, _minBaseAssetAmount, _refLink], i.payments)
345+ if ((doClosePosition == doClosePosition))
346+ then {
347+ let doResetContext = invoke(this, "resetContext", nil, nil)
348+ if ((doResetContext == doResetContext))
349+ then {
350+ let openedPositionSize = getPositionSize(_amm, _trader)
351+ if ((openedPositionSize == openedPositionSize))
352+ then {
353+ let amountIn = abs(openedPositionSize)
354+ let stopLossSide = if ((0 > openedPositionSize))
355+ then LONG
356+ else SHORT
357+ let doCreateStopOrder = if ((_stopTriggerPrice > 0))
358+ then {
359+ let doCreateStopOrder = invoke(this, "internalCreateOrder", [_trader, _amm, STOP, _stopTriggerPrice, _stopLimitPrice, amountIn, 0, stopLossSide, _refLink, "", 0], nil)
360+ if ((doCreateStopOrder == doCreateStopOrder))
361+ then nil
362+ else throw("Strict value is not equal to itself.")
363+ }
364+ else nil
365+ if ((doCreateStopOrder == doCreateStopOrder))
366+ then {
367+ let doCreateTakeOrder = if ((_takeTriggerPrice > 0))
368+ then {
369+ let doCreateTakeOrder = invoke(this, "internalCreateOrder", [_trader, _amm, TAKE, _takeTriggerPrice, _takeLimitPrice, amountIn, 0, stopLossSide, _refLink, "", 0], nil)
370+ if ((doCreateTakeOrder == doCreateTakeOrder))
371+ then nil
372+ else throw("Strict value is not equal to itself.")
373+ }
374+ else nil
375+ if ((doCreateTakeOrder == doCreateTakeOrder))
376+ then nil
377+ else throw("Strict value is not equal to itself.")
378+ }
379+ else throw("Strict value is not equal to itself.")
380+ }
381+ else throw("Strict value is not equal to itself.")
382+ }
383+ else throw("Strict value is not equal to itself.")
384+ }
385+ else throw("Strict value is not equal to itself.")
386+ }
387+ else throw("Strict value is not equal to itself.")
388+ }
389+ }
390+ else throw("Strict value is not equal to itself.")
391+ }
392+
393+
394+
395+@Callable(i)
396+func internalCreateOrder (_trader,_amm,_type,_triggerPrice,_limitPrice,_amountIn,_leverage,_side,_refLink,_paymentAssetId,_paymentAmount) = if (if (if (if (if (if (if (if (if (!(initialized()))
397+ then true
398+ else !(isWhitelist(_amm)))
399+ then true
400+ else (0 >= _triggerPrice))
401+ then true
402+ else (0 > _limitPrice))
403+ then true
404+ else (0 >= _amountIn))
405+ then true
406+ else (0 > _leverage))
407+ then true
408+ else !(if ((_side == LONG))
409+ then true
410+ else (_side == SHORT)))
411+ then true
412+ else !(if (if ((_type == STOP))
413+ then true
414+ else (_type == TAKE))
415+ then true
416+ else (_type == LIMIT)))
417+ then true
418+ else !((i.caller == this)))
419+ then throw("Invalid createOrder parameters")
420+ else {
421+ let orderId = (currentOrderId() + 1)
422+ let newTraderOrderCount = (getTraderOrderCount(_amm, _trader) + 1)
423+ let positionSize = getPositionSize(_amm, _trader)
424+ let _direction = if (if (if ((positionSize == 0))
425+ then true
426+ else if ((positionSize > 0))
427+ then (_side == LONG)
428+ else false)
429+ then true
430+ else if ((0 > positionSize))
431+ then (_side == SHORT)
432+ else false)
433+ then INCREASE
434+ else DECREASE
435+ if (if ((positionSize == 0))
436+ then if ((_type == STOP))
437+ then true
438+ else (_type == TAKE)
439+ else false)
440+ then throw("Can not create STOP/TAKE order: no position")
441+ else {
442+ let usdnPayment = if ((_direction == INCREASE))
443+ then if (if ((_paymentAssetId != toBase58String(quoteAsset())))
444+ then true
445+ else (_paymentAmount != _amountIn))
446+ then throw("Invalid createLimitOrder parameters: invalid payment")
447+ else {
448+ let stake = invoke(managerAddress(), "deposit", nil, [AttachedPayment(quoteAsset(), _paymentAmount)])
449+ if ((stake == stake))
450+ then _paymentAmount
451+ else throw("Strict value is not equal to itself.")
452+ }
453+ else 0
454+ if ((usdnPayment == usdnPayment))
100455 then {
101- let ammAddress = valueOrErrorMessage(addressFromString(amm), "Invalid order data [amm]")
102- let quoteAssetReserve = getIntegerValue(ammAddress, k_quoteAssetReserve)
103- let baseAssetReserve = getIntegerValue(ammAddress, k_baseAssetReserve)
104- let ammPrice = divd(quoteAssetReserve, baseAssetReserve)
105- let priceMatch = if ((orderType == TAKE_PROFIT))
106- then (ammPrice >= price)
107- else (price >= ammPrice)
108- let validatePrice = if (!(priceMatch))
109- then throw(((("Can not execute order [price]: AMM Price=" + toString(ammPrice)) + " Order Price=") + toString(price)))
110- else unit
111- if ((validatePrice == validatePrice))
456+ let positionId = if ((positionSize != 0))
457+ then getPositionId(_amm, _trader)
458+ else 0
459+ if ((newTraderOrderCount > MAX_TRADER_ORDERS_PER_AMM))
460+ then throw("Invalid createLimitOrder parameters: order count")
461+ else (((saveOrder(orderId, _amm, _trader, _amountIn, _leverage, _type, _triggerPrice, usdnPayment, _side, _refLink, positionId, _limitPrice) ++ addRemoveOrderId(orderId, _amm, _trader, true)) ++ updateTraderOrderCount(_amm, _trader, newTraderOrderCount)) ++ updateLastOrderId(orderId))
462+ }
463+ else throw("Strict value is not equal to itself.")
464+ }
465+ }
466+
467+
468+
469+@Callable(i)
470+func cancelOrder (_orderId) = {
471+ let $t01590816127 = getOrder(_orderId)
472+ let _amm = $t01590816127._1
473+ let _trader = $t01590816127._2
474+ let _amountIn = $t01590816127._3
475+ let _leverage = $t01590816127._4
476+ let _type = $t01590816127._5
477+ let _triggerPrice = $t01590816127._6
478+ let _amountUsdn = $t01590816127._7
479+ let _side = $t01590816127._8
480+ let _refLink = $t01590816127._9
481+ let _positionId = $t01590816127._10
482+ let _limitPrice = $t01590816127._11
483+ if (if (if (!(initialized()))
484+ then true
485+ else !(isValid(_orderId)))
486+ then true
487+ else !((toString(i.caller) == _trader)))
488+ then throw("Invalid cancelOrder parameters")
489+ else {
490+ let cleanUp = invoke(this, "cleanUpStaleOrders", [_amm, _trader], nil)
491+ if ((cleanUp == cleanUp))
492+ then {
493+ let newTraderOrderCount = (getTraderOrderCount(_amm, _trader) - 1)
494+ let withdraw = if ((_amountUsdn > 0))
495+ then {
496+ let unstake = invoke(managerAddress(), "withdraw", [toBase58String(quoteAsset()), _amountUsdn], nil)
497+ if ((unstake == unstake))
498+ then nil
499+ else throw("Strict value is not equal to itself.")
500+ }
501+ else nil
502+ if ((withdraw == withdraw))
503+ then (((markCancelOrder(_orderId) ++ addRemoveOrderId(_orderId, _amm, _trader, false)) ++ updateTraderOrderCount(_amm, _trader, newTraderOrderCount)) ++ (if ((_amountUsdn > 0))
504+ then [ScriptTransfer(i.caller, _amountUsdn, quoteAsset())]
505+ else nil))
506+ else throw("Strict value is not equal to itself.")
507+ }
508+ else throw("Strict value is not equal to itself.")
509+ }
510+ }
511+
512+
513+
514+@Callable(i)
515+func executeOrder (_orderId) = {
516+ let $t01709817317 = getOrder(_orderId)
517+ let _amm = $t01709817317._1
518+ let _trader = $t01709817317._2
519+ let _amountIn = $t01709817317._3
520+ let _leverage = $t01709817317._4
521+ let _type = $t01709817317._5
522+ let _triggerPrice = $t01709817317._6
523+ let _amountUsdn = $t01709817317._7
524+ let _side = $t01709817317._8
525+ let _refLink = $t01709817317._9
526+ let _positionId = $t01709817317._10
527+ let _limitPrice = $t01709817317._11
528+ let cleanUp = invoke(this, "cleanUpStaleOrders", [_amm, _trader], nil)
529+ if ((cleanUp == cleanUp))
530+ then if (if (!(initialized()))
531+ then true
532+ else !(isValid(_orderId)))
533+ then throw("Invalid executeOrder parameters")
534+ else {
535+ let positionSize = getPositionSize(_amm, _trader)
536+ let currentPositionId = if ((positionSize != 0))
537+ then getPositionId(_amm, _trader)
538+ else 0
539+ let $t01772121542 = if ((_type == STOP))
540+ then {
541+ let _positionDirection = if ((positionSize > 0))
542+ then LONG
543+ else if ((0 > positionSize))
544+ then SHORT
545+ else throw("Can not execute STOP order: no open position")
546+ let marketPrice = getMarketPrice(_amm)
547+ let isExecutable = if ((_side == _positionDirection))
548+ then throw("Can not execute STOP order: reduce only")
549+ else if ((currentPositionId != _positionId))
550+ then throw("Can not execute STOP order: position closed")
551+ else if ((_positionDirection == LONG))
552+ then (_triggerPrice >= marketPrice)
553+ else (marketPrice >= _triggerPrice)
554+ if (isExecutable)
555+ then $Tuple3("closePosition", [minv(_amountIn, abs(positionSize)), muld(_limitPrice, abs(positionSize))], nil)
556+ else throw("Can not execute STOP order: triggerPrice mismatch")
557+ }
558+ else if ((_type == TAKE))
559+ then {
560+ let _positionDirection = if ((positionSize > 0))
561+ then LONG
562+ else if ((0 > positionSize))
563+ then SHORT
564+ else throw("Can not execute STOP order: no open position")
565+ let marketPrice = getMarketPrice(_amm)
566+ let isExecutable = if ((_side == _positionDirection))
567+ then throw("Can not execute TAKE order: reduce only")
568+ else if ((currentPositionId != _positionId))
569+ then throw(((("Can not execute TAKE order: position closed " + toString(currentPositionId)) + "!=") + toString(_positionId)))
570+ else if ((_positionDirection == LONG))
571+ then (marketPrice >= _triggerPrice)
572+ else (_triggerPrice >= marketPrice)
573+ if (isExecutable)
574+ then $Tuple3("closePosition", [minv(_amountIn, abs(positionSize)), muld(_limitPrice, abs(positionSize))], nil)
575+ else throw("Can not execute TAKE order: triggerPrice mismatch")
576+ }
577+ else if ((_type == LIMIT))
112578 then {
113- let dueMatch = if ((validDue == 0))
114- then true
115- else (validDue >= TIME)
116- let validateDue = if (!(dueMatch))
117- then throw(((("Can not execute order [due]: Due=" + toString(validDue)) + " Time=") + toString(TIME)))
118- else unit
119- if ((validateDue == validateDue))
579+ let marketPrice = getMarketPrice(_amm)
580+ let spread = if ((_limitPrice == 0))
581+ then getSpread(_triggerPrice)
582+ else abs((_triggerPrice - _limitPrice))
583+ let isExecutable = if ((marketPrice >= (_triggerPrice - spread)))
584+ then ((_triggerPrice + spread) >= marketPrice)
585+ else false
586+ if (isExecutable)
120587 then {
121- let traderAddress = toString(addressFromPublicKey(fromBase58String(senderPublicKey)))
122- let positionWasClosed = valueOrElse(getInteger(ammAddress, ((k_positionClosedDate + "_") + traderAddress)), 0)
123- let positionMatch = (timestamp >= positionWasClosed)
124- let validatePosition = if (!(positionMatch))
125- then throw(((("Can not execute order [position closed]: Order Created=" + toString(timestamp)) + " Position Closed=") + toString(positionWasClosed)))
126- else unit
127- if ((validatePosition == validatePosition))
128- then if (if (if (priceMatch)
129- then dueMatch
130- else false)
131- then positionMatch
132- else false)
133- then {
134- let doSetContext = invoke(this, "setContext", [traderAddress], nil)
135- if ((doSetContext == doSetContext))
136- then {
137- let doClosePosition = invoke(ammAddress, "closePosition", nil, nil)
138- if ((doClosePosition == doClosePosition))
139- then {
140- let doResetContext = invoke(this, "resetContext", nil, nil)
141- if ((doResetContext == doResetContext))
142- then [BooleanEntry(executedOrderKey(orderId), true)]
143- else throw("Strict value is not equal to itself.")
144- }
145- else throw("Strict value is not equal to itself.")
146- }
147- else throw("Strict value is not equal to itself.")
148- }
149- else throw("Invalid order execution timing")
588+ let _positionDirection = if ((positionSize > 0))
589+ then LONG
590+ else if ((0 > positionSize))
591+ then SHORT
592+ else -1
593+ let direction = if ((positionSize == 0))
594+ then INCREASE
595+ else if ((_positionDirection == _side))
596+ then INCREASE
597+ else DECREASE
598+ if ((direction == INCREASE))
599+ then {
600+ let amountInWithFee = (_amountUsdn - muld(_amountUsdn, getFee(_amm, _trader)))
601+ $Tuple3("increasePosition", [_side, _leverage, if ((_limitPrice == 0))
602+ then 0
603+ else divd(amountInWithFee, _limitPrice), _refLink], [AttachedPayment(quoteAsset(), _amountUsdn)])
604+ }
605+ else $Tuple3("closePosition", [_amountIn, muld(_amountIn, _limitPrice)], nil)
606+ }
607+ else throw("Can not execute LIMIT order: triggerPrice mismatch")
608+ }
609+ else throw(("Invalid order type: " + toString(_type)))
610+ let method = $t01772121542._1
611+ let args = $t01772121542._2
612+ let payments = $t01772121542._3
613+ let withdraw = if ((size(payments) == 1))
614+ then {
615+ let unstake = invoke(managerAddress(), "withdraw", [toBase58String(quoteAsset()), payments[0].amount], nil)
616+ if ((unstake == unstake))
617+ then nil
618+ else throw("Strict value is not equal to itself.")
619+ }
620+ else nil
621+ if ((withdraw == withdraw))
622+ then {
623+ let doSetContext = invoke(this, "setContext", [_trader], nil)
624+ if ((doSetContext == doSetContext))
625+ then {
626+ let doClosePosition = invoke(addressFromStringValue(_amm), method, args, payments)
627+ if ((doClosePosition == doClosePosition))
628+ then {
629+ let doResetContext = invoke(this, "resetContext", nil, nil)
630+ if ((doResetContext == doResetContext))
631+ then {
632+ let newTraderOrderCount = (getTraderOrderCount(_amm, _trader) - 1)
633+ ((updateTraderOrderCount(_amm, _trader, newTraderOrderCount) ++ addRemoveOrderId(_orderId, _amm, _trader, false)) ++ markExecuteOrder(_orderId))
634+ }
150635 else throw("Strict value is not equal to itself.")
151636 }
152637 else throw("Strict value is not equal to itself.")
155640 }
156641 else throw("Strict value is not equal to itself.")
157642 }
158- else throw(("Invalid order type: " + toString(orderType)))
643+ else throw("Strict value is not equal to itself.")
644+ }
645+
646+
647+
648+@Callable(i)
649+func view_canExecuteOrder (_orderId) = {
650+ let s = invoke(this, "executeOrder", [_orderId], nil)
651+ if ((s == s))
652+ then throw("Success")
159653 else throw("Strict value is not equal to itself.")
160654 }
161655
Full:
OldNewDifferences
11 {-# STDLIB_VERSION 6 #-}
22 {-# SCRIPT_TYPE ACCOUNT #-}
33 {-# CONTENT_TYPE DAPP #-}
44 let k_coordinatorAddress = "k_coordinatorAddress"
55
66 let k_admin_public_key = "k_admin_public_key"
77
8-let k_quoteAssetReserve = "k_qtAstR"
8+let k_quote_asset = "k_quote_asset"
99
10-let k_baseAssetReserve = "k_bsAstR"
10+let k_amm = "k_amm"
1111
12-let k_positionClosedDate = "k_positionClosedDate"
12+let k_manager_address = "k_manager_address"
13+
14+let k_positionSequence = "k_positionSequence"
15+
16+let k_positionSize = "k_positionSize"
17+
18+let k_fee = "k_fee"
1319
1420 let k_executedOrders = "k_executedOrders"
21+
22+let k_canceledOrders = "k_canceledOrders"
23+
24+let k_order = "k_order"
25+
26+let k_lastOrderId = "k_lastOrderId"
27+
28+let k_traderOrderCnt = "k_traderOrderCnt"
29+
30+let k_traderOrderIds = "k_traderOrderIds"
1531
1632 let k_sender = "k_sender"
1733
1834 let k_initialized = "k_initialized"
1935
20-let TAKE_PROFIT = 1
36+let STOP = 1
2137
22-let STOP_LOSS = 2
38+let TAKE = 2
39+
40+let LIMIT = 3
41+
42+let LONG = 1
43+
44+let SHORT = 2
45+
46+let INCREASE = 1
47+
48+let DECREASE = 2
49+
50+let MAX_TRADER_ORDERS_PER_AMM = 5
2351
2452 let TIME = lastBlock.timestamp
2553
2654 let DECIMAL_UNIT = (1 * (((((10 * 10) * 10) * 10) * 10) * 10))
55+
56+let SPREAD_LIMIT = (DECIMAL_UNIT / 200)
57+
58+func abs (_x) = if ((_x > 0))
59+ then _x
60+ else -(_x)
61+
2762
2863 func divd (_x,_y) = fraction(_x, DECIMAL_UNIT, _y, HALFEVEN)
2964
3065
3166 func muld (_x,_y) = fraction(_x, _y, DECIMAL_UNIT, HALFEVEN)
3267
3368
34-func executedOrderKey (_orderId) = ((k_executedOrders + "_") + _orderId)
69+func minv (_x,_y) = if ((_x > _y))
70+ then _y
71+ else _x
72+
73+
74+func toCompositeKey (_key,_address) = ((_key + "_") + _address)
75+
76+
77+func executedOrderKey (_orderId) = ((k_executedOrders + "_") + toString(_orderId))
78+
79+
80+func canceledOrderKey (_orderId) = ((k_canceledOrders + "_") + toString(_orderId))
81+
82+
83+func orderKey (_orderId) = toCompositeKey(k_order, toString(_orderId))
84+
85+
86+func traderOrderCountKey (_amm,_trader) = ((((k_traderOrderCnt + "_") + _amm) + "_") + _trader)
87+
88+
89+func traderOrderIdsKey (_amm,_trader) = ((((k_traderOrderIds + "_") + _amm) + "_") + _trader)
3590
3691
3792 func coordinator () = valueOrErrorMessage(addressFromString(getStringValue(this, k_coordinatorAddress)), "Coordinator not set")
93+
94+
95+func quoteAsset () = fromBase58String(getStringValue(coordinator(), k_quote_asset))
96+
97+
98+func managerAddress () = valueOrErrorMessage(addressFromString(getStringValue(coordinator(), k_manager_address)), "Manager not set")
99+
100+
101+func isWhitelist (_address) = valueOrElse(getBoolean(coordinator(), toCompositeKey(k_amm, _address)), false)
38102
39103
40104 func adminPublicKey () = fromBase58String(getStringValue(coordinator(), k_admin_public_key))
41105
42106
43107 func initialized () = valueOrElse(getBoolean(this, k_initialized), false)
44108
45109
46-func requireValidOrderSignature (_prefix,_order,_signature,_senderPublicKey) = {
47- let message = toBytes((_prefix + _order))
48- let sig = fromBase58String(_signature)
49- let pub = fromBase58String(_senderPublicKey)
50- if (sigVerify(message, sig, pub))
51- then unit
52- else throw("Invalid order signature")
110+func isValid (_orderId) = if (valueOrElse(getBoolean(this, executedOrderKey(_orderId)), false))
111+ then throw(("Order already executed: " + toString(_orderId)))
112+ else if (valueOrElse(getBoolean(this, canceledOrderKey(_orderId)), false))
113+ then throw(("Order already cancelled: " + toString(_orderId)))
114+ else true
115+
116+
117+func currentOrderId () = valueOrElse(getInteger(this, k_lastOrderId), 0)
118+
119+
120+func getTraderOrderCount (_amm,_trader) = {
121+ let key = traderOrderCountKey(_amm, _trader)
122+ valueOrElse(getInteger(this, key), 0)
53123 }
54124
55125
56-func requireNotExecuted (_orderId) = if (valueOrElse(getBoolean(this, executedOrderKey(_orderId)), false))
57- then throw(("Order already executed: " + _orderId))
58- else unit
126+func traderAmmOrdersIds (_amm,_trader) = {
127+ let key = traderOrderIdsKey(_amm, _trader)
128+ let val = valueOrElse(getString(this, key), "")
129+ if ((val == ""))
130+ then nil
131+ else split(val, ",")
132+ }
133+
134+
135+func getOrder (_orderId) = {
136+ let orderStr = valueOrErrorMessage(getString(this, orderKey(_orderId)), ("Invalid order id: " + toString(_orderId)))
137+ let orderPartList = split(orderStr, ",")
138+ let amm = orderPartList[0]
139+ let trader = orderPartList[1]
140+ let amountIn = valueOrErrorMessage(parseInt(orderPartList[2]), "Invalid amountIn")
141+ let leverage = valueOrErrorMessage(parseInt(orderPartList[3]), "Invalid leverage")
142+ let type = valueOrErrorMessage(parseInt(orderPartList[4]), "Invalid type")
143+ let triggerPrice = valueOrErrorMessage(parseInt(orderPartList[5]), "Invalid triggerPrice")
144+ let paymentUsdn = valueOrErrorMessage(parseInt(orderPartList[6]), "Invalid paymentUsdn")
145+ let side = valueOrErrorMessage(parseInt(orderPartList[7]), "Invalid side")
146+ let refLink = orderPartList[8]
147+ let positionId = valueOrErrorMessage(parseInt(orderPartList[9]), "Invalid positionId")
148+ let limitPrice = valueOrErrorMessage(parseInt(orderPartList[10]), "Invalid limitPrice")
149+ $Tuple11(amm, trader, amountIn, leverage, type, triggerPrice, paymentUsdn, side, refLink, positionId, limitPrice)
150+ }
151+
152+
153+func getMarketPrice (_amm) = {
154+ let s = invoke(addressFromStringValue(_amm), "computeSpotPrice", nil, nil)
155+ if ((s == s))
156+ then {
157+ let res = match s {
158+ case t: Int =>
159+ t
160+ case _ =>
161+ throw("Invalid computeSpotPrice result")
162+ }
163+ value(res)
164+ }
165+ else throw("Strict value is not equal to itself.")
166+ }
167+
168+
169+func getFee (_amm,_trader) = {
170+ let s = invoke(addressFromStringValue(_amm), "computeFeeForTraderWithArtifact", [_trader, ""], nil)
171+ if ((s == s))
172+ then {
173+ let res = match s {
174+ case t: (Int, Boolean) =>
175+ t._1
176+ case _ =>
177+ throw("Invalid computeFeeForTraderWithArtifact result")
178+ }
179+ value(res)
180+ }
181+ else throw("Strict value is not equal to itself.")
182+ }
183+
184+
185+func getPositionSize (_amm,_trader) = {
186+ let amm = addressFromStringValue(_amm)
187+ let sizeKey = toCompositeKey(k_positionSize, _trader)
188+ valueOrElse(getInteger(amm, sizeKey), 0)
189+ }
190+
191+
192+func getPositionId (_amm,_trader) = {
193+ let amm = addressFromStringValue(_amm)
194+ let seqKey = toCompositeKey(k_positionSequence, _trader)
195+ valueOrElse(getInteger(amm, seqKey), 0)
196+ }
197+
198+
199+func getSpread (_price) = muld(_price, SPREAD_LIMIT)
200+
201+
202+func saveOrder (_orderId,_amm,_trader,_amountIn,_leverage,_type,_triggerPrice,_paymentUsdn,_side,_refLink,_positionId,_limitPrice) = {
203+ let orderStr = makeString([_amm, _trader, toString(_amountIn), toString(_leverage), toString(_type), toString(_triggerPrice), toString(_paymentUsdn), toString(_side), _refLink, toString(_positionId), toString(_limitPrice)], ",")
204+[StringEntry(orderKey(_orderId), orderStr)]
205+ }
206+
207+
208+func addRemoveOrderId (_orderId,_amm,_trader,_add) = {
209+ let orderIds = traderAmmOrdersIds(_amm, _trader)
210+ let orderIdsNew = if (_add)
211+ then (orderIds :+ toString(_orderId))
212+ else removeByIndex(orderIds, valueOrErrorMessage(indexOf(orderIds, toString(_orderId)), ("No order with id: " + toString(_orderId))))
213+ let orderIdsNewStr = makeString(orderIdsNew, ",")
214+[StringEntry(traderOrderIdsKey(_amm, _trader), orderIdsNewStr)]
215+ }
216+
217+
218+func updateTraderOrderCount (_amm,_trader,_count) = [IntegerEntry(traderOrderCountKey(_amm, _trader), _count)]
219+
220+
221+func updateLastOrderId (_lastOrderId) = [IntegerEntry(k_lastOrderId, _lastOrderId)]
222+
223+
224+func markExecuteOrder (_orderId) = [BooleanEntry(toCompositeKey(k_executedOrders, toString(_orderId)), true)]
225+
226+
227+func markCancelOrder (_orderId) = [BooleanEntry(toCompositeKey(k_canceledOrders, toString(_orderId)), true)]
228+
229+
230+@Callable(i)
231+func cleanUpStaleOrders (_amm,_trader) = {
232+ let orders = traderAmmOrdersIds(_amm, _trader)
233+ let orderCount = getTraderOrderCount(_amm, _trader)
234+ func cleanUpOne (_acc,_orderId) = {
235+ let orderIdInt = valueOrErrorMessage(parseInt(_orderId), "Invalid order id")
236+ let $t083838578 = getOrder(orderIdInt)
237+ let _x1 = $t083838578._1
238+ let _x2 = $t083838578._2
239+ let _x3 = $t083838578._3
240+ let _x4 = $t083838578._4
241+ let _type = $t083838578._5
242+ let _x5 = $t083838578._6
243+ let _x6 = $t083838578._7
244+ let _x7 = $t083838578._8
245+ let _x8 = $t083838578._9
246+ let _positionId = $t083838578._10
247+ let _x9 = $t083838578._11
248+ let positionSize = getPositionSize(_amm, _trader)
249+ let currentPositionId = if ((positionSize != 0))
250+ then getPositionId(_amm, _trader)
251+ else 0
252+ if (if (if ((_type == STOP))
253+ then true
254+ else (_type == TAKE))
255+ then (currentPositionId != _positionId)
256+ else false)
257+ then {
258+ let change = (markCancelOrder(orderIdInt) ++ addRemoveOrderId(orderIdInt, _amm, _trader, false))
259+ $Tuple2((_acc._1 + 1), (_acc._2 ++ change))
260+ }
261+ else _acc
262+ }
263+
264+ let result = {
265+ let $l = orders
266+ let $s = size($l)
267+ let $acc0 = $Tuple2(0, nil)
268+ func $f0_1 ($a,$i) = if (($i >= $s))
269+ then $a
270+ else cleanUpOne($a, $l[$i])
271+
272+ func $f0_2 ($a,$i) = if (($i >= $s))
273+ then $a
274+ else throw("List size exceeds 5")
275+
276+ $f0_2($f0_1($f0_1($f0_1($f0_1($f0_1($acc0, 0), 1), 2), 3), 4), 5)
277+ }
278+ (result._2 ++ updateTraderOrderCount(_amm, _trader, (orderCount - result._1)))
279+ }
280+
59281
60282
61283 @Callable(i)
62284 func setContext (_sender) = if ((i.caller != this))
63285 then throw("Only self-call")
64286 else [StringEntry(k_sender, _sender)]
65287
66288
67289
68290 @Callable(i)
69291 func resetContext () = if ((i.caller != this))
70292 then throw("Only self-call")
71293 else [DeleteEntry(k_sender)]
72294
73295
74296
75297 @Callable(i)
76298 func initialize (_coordinator) = if (initialized())
77299 then throw("Already initialized")
78300 else [StringEntry(k_coordinatorAddress, _coordinator), BooleanEntry(k_initialized, true)]
79301
80302
81303
82304 @Callable(i)
83-func executeOrder (_prefix,_order,_signature) = {
84- let orderParts = split(_order, ",")
85- let orderType = valueOrErrorMessage(parseInt(orderParts[0]), "Invalid order data [type]")
86- let orderId = toBase58String(sha256(toBytes(_order)))
87- let validateNotExecuted = requireNotExecuted(orderId)
88- if ((validateNotExecuted == validateNotExecuted))
89- then if (if ((orderType == TAKE_PROFIT))
305+func createOrder (_amm,_type,_triggerPrice,_limitPrice,_amountIn,_leverage,_side,_refLink) = {
306+ let _trader = toString(i.caller)
307+ let cleanUp = invoke(this, "cleanUpStaleOrders", [_amm, _trader], nil)
308+ if ((cleanUp == cleanUp))
309+ then if ((size(i.payments) > 1))
310+ then throw("Invalid createOrder parameters: invalid payment count")
311+ else {
312+ let $t01028710539 = if ((size(i.payments) == 1))
313+ then $Tuple2(toBase58String(valueOrErrorMessage(i.payments[0].assetId, "Invalid asset id")), i.payments[0].amount)
314+ else $Tuple2("", 0)
315+ let paymentAssetId = $t01028710539._1
316+ let paymentAmount = $t01028710539._2
317+ let doCall = invoke(this, "internalCreateOrder", [_trader, _amm, _type, _triggerPrice, _limitPrice, _amountIn, _leverage, _side, _refLink, paymentAssetId, paymentAmount], nil)
318+ if ((doCall == doCall))
319+ then nil
320+ else throw("Strict value is not equal to itself.")
321+ }
322+ else throw("Strict value is not equal to itself.")
323+ }
324+
325+
326+
327+@Callable(i)
328+func increasePositionWithStopLoss (_amm,_direction,_leverage,_minBaseAssetAmount,_refLink,_stopTriggerPrice,_stopLimitPrice,_takeTriggerPrice,_takeLimitPrice) = {
329+ let _trader = toString(i.caller)
330+ let cleanUp = invoke(this, "cleanUpStaleOrders", [_amm, _trader], nil)
331+ if ((cleanUp == cleanUp))
332+ then if (if (!(initialized()))
90333 then true
91- else (orderType == STOP_LOSS))
92- then {
93- let amm = orderParts[1]
94- let senderPublicKey = orderParts[2]
95- let price = valueOrErrorMessage(parseInt(orderParts[3]), "Invalid order data [price]")
96- let timestamp = valueOrErrorMessage(parseInt(orderParts[4]), "Invalid order data [timestamp]")
97- let validDue = valueOrErrorMessage(parseInt(orderParts[5]), "Invalid order data [validDue]")
98- let validateSignature = requireValidOrderSignature(_prefix, _order, _signature, senderPublicKey)
99- if ((validateSignature == validateSignature))
334+ else !(isWhitelist(_amm)))
335+ then throw("Invalid increasePositionWithStopLoss parameters")
336+ else {
337+ let positionSize = getPositionSize(_amm, _trader)
338+ if ((positionSize != 0))
339+ then throw("Invalid increasePositionWithStopLoss parameters: only new position")
340+ else {
341+ let doSetContext = invoke(this, "setContext", [_trader], nil)
342+ if ((doSetContext == doSetContext))
343+ then {
344+ let doClosePosition = invoke(addressFromStringValue(_amm), "increasePosition", [_direction, _leverage, _minBaseAssetAmount, _refLink], i.payments)
345+ if ((doClosePosition == doClosePosition))
346+ then {
347+ let doResetContext = invoke(this, "resetContext", nil, nil)
348+ if ((doResetContext == doResetContext))
349+ then {
350+ let openedPositionSize = getPositionSize(_amm, _trader)
351+ if ((openedPositionSize == openedPositionSize))
352+ then {
353+ let amountIn = abs(openedPositionSize)
354+ let stopLossSide = if ((0 > openedPositionSize))
355+ then LONG
356+ else SHORT
357+ let doCreateStopOrder = if ((_stopTriggerPrice > 0))
358+ then {
359+ let doCreateStopOrder = invoke(this, "internalCreateOrder", [_trader, _amm, STOP, _stopTriggerPrice, _stopLimitPrice, amountIn, 0, stopLossSide, _refLink, "", 0], nil)
360+ if ((doCreateStopOrder == doCreateStopOrder))
361+ then nil
362+ else throw("Strict value is not equal to itself.")
363+ }
364+ else nil
365+ if ((doCreateStopOrder == doCreateStopOrder))
366+ then {
367+ let doCreateTakeOrder = if ((_takeTriggerPrice > 0))
368+ then {
369+ let doCreateTakeOrder = invoke(this, "internalCreateOrder", [_trader, _amm, TAKE, _takeTriggerPrice, _takeLimitPrice, amountIn, 0, stopLossSide, _refLink, "", 0], nil)
370+ if ((doCreateTakeOrder == doCreateTakeOrder))
371+ then nil
372+ else throw("Strict value is not equal to itself.")
373+ }
374+ else nil
375+ if ((doCreateTakeOrder == doCreateTakeOrder))
376+ then nil
377+ else throw("Strict value is not equal to itself.")
378+ }
379+ else throw("Strict value is not equal to itself.")
380+ }
381+ else throw("Strict value is not equal to itself.")
382+ }
383+ else throw("Strict value is not equal to itself.")
384+ }
385+ else throw("Strict value is not equal to itself.")
386+ }
387+ else throw("Strict value is not equal to itself.")
388+ }
389+ }
390+ else throw("Strict value is not equal to itself.")
391+ }
392+
393+
394+
395+@Callable(i)
396+func internalCreateOrder (_trader,_amm,_type,_triggerPrice,_limitPrice,_amountIn,_leverage,_side,_refLink,_paymentAssetId,_paymentAmount) = if (if (if (if (if (if (if (if (if (!(initialized()))
397+ then true
398+ else !(isWhitelist(_amm)))
399+ then true
400+ else (0 >= _triggerPrice))
401+ then true
402+ else (0 > _limitPrice))
403+ then true
404+ else (0 >= _amountIn))
405+ then true
406+ else (0 > _leverage))
407+ then true
408+ else !(if ((_side == LONG))
409+ then true
410+ else (_side == SHORT)))
411+ then true
412+ else !(if (if ((_type == STOP))
413+ then true
414+ else (_type == TAKE))
415+ then true
416+ else (_type == LIMIT)))
417+ then true
418+ else !((i.caller == this)))
419+ then throw("Invalid createOrder parameters")
420+ else {
421+ let orderId = (currentOrderId() + 1)
422+ let newTraderOrderCount = (getTraderOrderCount(_amm, _trader) + 1)
423+ let positionSize = getPositionSize(_amm, _trader)
424+ let _direction = if (if (if ((positionSize == 0))
425+ then true
426+ else if ((positionSize > 0))
427+ then (_side == LONG)
428+ else false)
429+ then true
430+ else if ((0 > positionSize))
431+ then (_side == SHORT)
432+ else false)
433+ then INCREASE
434+ else DECREASE
435+ if (if ((positionSize == 0))
436+ then if ((_type == STOP))
437+ then true
438+ else (_type == TAKE)
439+ else false)
440+ then throw("Can not create STOP/TAKE order: no position")
441+ else {
442+ let usdnPayment = if ((_direction == INCREASE))
443+ then if (if ((_paymentAssetId != toBase58String(quoteAsset())))
444+ then true
445+ else (_paymentAmount != _amountIn))
446+ then throw("Invalid createLimitOrder parameters: invalid payment")
447+ else {
448+ let stake = invoke(managerAddress(), "deposit", nil, [AttachedPayment(quoteAsset(), _paymentAmount)])
449+ if ((stake == stake))
450+ then _paymentAmount
451+ else throw("Strict value is not equal to itself.")
452+ }
453+ else 0
454+ if ((usdnPayment == usdnPayment))
100455 then {
101- let ammAddress = valueOrErrorMessage(addressFromString(amm), "Invalid order data [amm]")
102- let quoteAssetReserve = getIntegerValue(ammAddress, k_quoteAssetReserve)
103- let baseAssetReserve = getIntegerValue(ammAddress, k_baseAssetReserve)
104- let ammPrice = divd(quoteAssetReserve, baseAssetReserve)
105- let priceMatch = if ((orderType == TAKE_PROFIT))
106- then (ammPrice >= price)
107- else (price >= ammPrice)
108- let validatePrice = if (!(priceMatch))
109- then throw(((("Can not execute order [price]: AMM Price=" + toString(ammPrice)) + " Order Price=") + toString(price)))
110- else unit
111- if ((validatePrice == validatePrice))
456+ let positionId = if ((positionSize != 0))
457+ then getPositionId(_amm, _trader)
458+ else 0
459+ if ((newTraderOrderCount > MAX_TRADER_ORDERS_PER_AMM))
460+ then throw("Invalid createLimitOrder parameters: order count")
461+ else (((saveOrder(orderId, _amm, _trader, _amountIn, _leverage, _type, _triggerPrice, usdnPayment, _side, _refLink, positionId, _limitPrice) ++ addRemoveOrderId(orderId, _amm, _trader, true)) ++ updateTraderOrderCount(_amm, _trader, newTraderOrderCount)) ++ updateLastOrderId(orderId))
462+ }
463+ else throw("Strict value is not equal to itself.")
464+ }
465+ }
466+
467+
468+
469+@Callable(i)
470+func cancelOrder (_orderId) = {
471+ let $t01590816127 = getOrder(_orderId)
472+ let _amm = $t01590816127._1
473+ let _trader = $t01590816127._2
474+ let _amountIn = $t01590816127._3
475+ let _leverage = $t01590816127._4
476+ let _type = $t01590816127._5
477+ let _triggerPrice = $t01590816127._6
478+ let _amountUsdn = $t01590816127._7
479+ let _side = $t01590816127._8
480+ let _refLink = $t01590816127._9
481+ let _positionId = $t01590816127._10
482+ let _limitPrice = $t01590816127._11
483+ if (if (if (!(initialized()))
484+ then true
485+ else !(isValid(_orderId)))
486+ then true
487+ else !((toString(i.caller) == _trader)))
488+ then throw("Invalid cancelOrder parameters")
489+ else {
490+ let cleanUp = invoke(this, "cleanUpStaleOrders", [_amm, _trader], nil)
491+ if ((cleanUp == cleanUp))
492+ then {
493+ let newTraderOrderCount = (getTraderOrderCount(_amm, _trader) - 1)
494+ let withdraw = if ((_amountUsdn > 0))
495+ then {
496+ let unstake = invoke(managerAddress(), "withdraw", [toBase58String(quoteAsset()), _amountUsdn], nil)
497+ if ((unstake == unstake))
498+ then nil
499+ else throw("Strict value is not equal to itself.")
500+ }
501+ else nil
502+ if ((withdraw == withdraw))
503+ then (((markCancelOrder(_orderId) ++ addRemoveOrderId(_orderId, _amm, _trader, false)) ++ updateTraderOrderCount(_amm, _trader, newTraderOrderCount)) ++ (if ((_amountUsdn > 0))
504+ then [ScriptTransfer(i.caller, _amountUsdn, quoteAsset())]
505+ else nil))
506+ else throw("Strict value is not equal to itself.")
507+ }
508+ else throw("Strict value is not equal to itself.")
509+ }
510+ }
511+
512+
513+
514+@Callable(i)
515+func executeOrder (_orderId) = {
516+ let $t01709817317 = getOrder(_orderId)
517+ let _amm = $t01709817317._1
518+ let _trader = $t01709817317._2
519+ let _amountIn = $t01709817317._3
520+ let _leverage = $t01709817317._4
521+ let _type = $t01709817317._5
522+ let _triggerPrice = $t01709817317._6
523+ let _amountUsdn = $t01709817317._7
524+ let _side = $t01709817317._8
525+ let _refLink = $t01709817317._9
526+ let _positionId = $t01709817317._10
527+ let _limitPrice = $t01709817317._11
528+ let cleanUp = invoke(this, "cleanUpStaleOrders", [_amm, _trader], nil)
529+ if ((cleanUp == cleanUp))
530+ then if (if (!(initialized()))
531+ then true
532+ else !(isValid(_orderId)))
533+ then throw("Invalid executeOrder parameters")
534+ else {
535+ let positionSize = getPositionSize(_amm, _trader)
536+ let currentPositionId = if ((positionSize != 0))
537+ then getPositionId(_amm, _trader)
538+ else 0
539+ let $t01772121542 = if ((_type == STOP))
540+ then {
541+ let _positionDirection = if ((positionSize > 0))
542+ then LONG
543+ else if ((0 > positionSize))
544+ then SHORT
545+ else throw("Can not execute STOP order: no open position")
546+ let marketPrice = getMarketPrice(_amm)
547+ let isExecutable = if ((_side == _positionDirection))
548+ then throw("Can not execute STOP order: reduce only")
549+ else if ((currentPositionId != _positionId))
550+ then throw("Can not execute STOP order: position closed")
551+ else if ((_positionDirection == LONG))
552+ then (_triggerPrice >= marketPrice)
553+ else (marketPrice >= _triggerPrice)
554+ if (isExecutable)
555+ then $Tuple3("closePosition", [minv(_amountIn, abs(positionSize)), muld(_limitPrice, abs(positionSize))], nil)
556+ else throw("Can not execute STOP order: triggerPrice mismatch")
557+ }
558+ else if ((_type == TAKE))
559+ then {
560+ let _positionDirection = if ((positionSize > 0))
561+ then LONG
562+ else if ((0 > positionSize))
563+ then SHORT
564+ else throw("Can not execute STOP order: no open position")
565+ let marketPrice = getMarketPrice(_amm)
566+ let isExecutable = if ((_side == _positionDirection))
567+ then throw("Can not execute TAKE order: reduce only")
568+ else if ((currentPositionId != _positionId))
569+ then throw(((("Can not execute TAKE order: position closed " + toString(currentPositionId)) + "!=") + toString(_positionId)))
570+ else if ((_positionDirection == LONG))
571+ then (marketPrice >= _triggerPrice)
572+ else (_triggerPrice >= marketPrice)
573+ if (isExecutable)
574+ then $Tuple3("closePosition", [minv(_amountIn, abs(positionSize)), muld(_limitPrice, abs(positionSize))], nil)
575+ else throw("Can not execute TAKE order: triggerPrice mismatch")
576+ }
577+ else if ((_type == LIMIT))
112578 then {
113- let dueMatch = if ((validDue == 0))
114- then true
115- else (validDue >= TIME)
116- let validateDue = if (!(dueMatch))
117- then throw(((("Can not execute order [due]: Due=" + toString(validDue)) + " Time=") + toString(TIME)))
118- else unit
119- if ((validateDue == validateDue))
579+ let marketPrice = getMarketPrice(_amm)
580+ let spread = if ((_limitPrice == 0))
581+ then getSpread(_triggerPrice)
582+ else abs((_triggerPrice - _limitPrice))
583+ let isExecutable = if ((marketPrice >= (_triggerPrice - spread)))
584+ then ((_triggerPrice + spread) >= marketPrice)
585+ else false
586+ if (isExecutable)
120587 then {
121- let traderAddress = toString(addressFromPublicKey(fromBase58String(senderPublicKey)))
122- let positionWasClosed = valueOrElse(getInteger(ammAddress, ((k_positionClosedDate + "_") + traderAddress)), 0)
123- let positionMatch = (timestamp >= positionWasClosed)
124- let validatePosition = if (!(positionMatch))
125- then throw(((("Can not execute order [position closed]: Order Created=" + toString(timestamp)) + " Position Closed=") + toString(positionWasClosed)))
126- else unit
127- if ((validatePosition == validatePosition))
128- then if (if (if (priceMatch)
129- then dueMatch
130- else false)
131- then positionMatch
132- else false)
133- then {
134- let doSetContext = invoke(this, "setContext", [traderAddress], nil)
135- if ((doSetContext == doSetContext))
136- then {
137- let doClosePosition = invoke(ammAddress, "closePosition", nil, nil)
138- if ((doClosePosition == doClosePosition))
139- then {
140- let doResetContext = invoke(this, "resetContext", nil, nil)
141- if ((doResetContext == doResetContext))
142- then [BooleanEntry(executedOrderKey(orderId), true)]
143- else throw("Strict value is not equal to itself.")
144- }
145- else throw("Strict value is not equal to itself.")
146- }
147- else throw("Strict value is not equal to itself.")
148- }
149- else throw("Invalid order execution timing")
588+ let _positionDirection = if ((positionSize > 0))
589+ then LONG
590+ else if ((0 > positionSize))
591+ then SHORT
592+ else -1
593+ let direction = if ((positionSize == 0))
594+ then INCREASE
595+ else if ((_positionDirection == _side))
596+ then INCREASE
597+ else DECREASE
598+ if ((direction == INCREASE))
599+ then {
600+ let amountInWithFee = (_amountUsdn - muld(_amountUsdn, getFee(_amm, _trader)))
601+ $Tuple3("increasePosition", [_side, _leverage, if ((_limitPrice == 0))
602+ then 0
603+ else divd(amountInWithFee, _limitPrice), _refLink], [AttachedPayment(quoteAsset(), _amountUsdn)])
604+ }
605+ else $Tuple3("closePosition", [_amountIn, muld(_amountIn, _limitPrice)], nil)
606+ }
607+ else throw("Can not execute LIMIT order: triggerPrice mismatch")
608+ }
609+ else throw(("Invalid order type: " + toString(_type)))
610+ let method = $t01772121542._1
611+ let args = $t01772121542._2
612+ let payments = $t01772121542._3
613+ let withdraw = if ((size(payments) == 1))
614+ then {
615+ let unstake = invoke(managerAddress(), "withdraw", [toBase58String(quoteAsset()), payments[0].amount], nil)
616+ if ((unstake == unstake))
617+ then nil
618+ else throw("Strict value is not equal to itself.")
619+ }
620+ else nil
621+ if ((withdraw == withdraw))
622+ then {
623+ let doSetContext = invoke(this, "setContext", [_trader], nil)
624+ if ((doSetContext == doSetContext))
625+ then {
626+ let doClosePosition = invoke(addressFromStringValue(_amm), method, args, payments)
627+ if ((doClosePosition == doClosePosition))
628+ then {
629+ let doResetContext = invoke(this, "resetContext", nil, nil)
630+ if ((doResetContext == doResetContext))
631+ then {
632+ let newTraderOrderCount = (getTraderOrderCount(_amm, _trader) - 1)
633+ ((updateTraderOrderCount(_amm, _trader, newTraderOrderCount) ++ addRemoveOrderId(_orderId, _amm, _trader, false)) ++ markExecuteOrder(_orderId))
634+ }
150635 else throw("Strict value is not equal to itself.")
151636 }
152637 else throw("Strict value is not equal to itself.")
153638 }
154639 else throw("Strict value is not equal to itself.")
155640 }
156641 else throw("Strict value is not equal to itself.")
157642 }
158- else throw(("Invalid order type: " + toString(orderType)))
643+ else throw("Strict value is not equal to itself.")
644+ }
645+
646+
647+
648+@Callable(i)
649+func view_canExecuteOrder (_orderId) = {
650+ let s = invoke(this, "executeOrder", [_orderId], nil)
651+ if ((s == s))
652+ then throw("Success")
159653 else throw("Strict value is not equal to itself.")
160654 }
161655
162656
163657 @Verifier(tx)
164658 func verify () = sigVerify(tx.bodyBytes, tx.proofs[0], adminPublicKey())
165659

github/deemru/w8io/873ac7e 
59.77 ms