tx · B8jrxogYe93WYYCD8GcQxAkn5r7KZQpYrC7k2g3xeDTi

3N9be2mwrA52WJho6DiesZkk4351GvpnWuj:  -0.04300000 Waves

2023.02.10 10:54 [2443255] smart account 3N9be2mwrA52WJho6DiesZkk4351GvpnWuj > SELF 0.00000000 Waves

{ "type": 13, "id": "B8jrxogYe93WYYCD8GcQxAkn5r7KZQpYrC7k2g3xeDTi", "fee": 4300000, "feeAssetId": null, "timestamp": 1676015657295, "version": 2, "chainId": 84, "sender": "3N9be2mwrA52WJho6DiesZkk4351GvpnWuj", "senderPublicKey": "6mzmbCza9iqbzxMEELcEA4Xc9NeF4CYpbTtz1zMK3C7x", "proofs": [ "2KTisVyhweqcRNqAh9vTvouVCebkLweFdJGzCzZC5jMh44PDBY9iC11bPm8Hy1UuautrfAREgYHfBZuQfXR3zHFS", "2RMdZjGNAKCgNha1kxPB1Y62zRK9a97HRSqzFZrKWCpLzdadSEGphsyrHNSxxactEwTbqKfSuJiNQRDHef1ZEhBR", "3TJFFXUEjwLhf4tmyjzr29SmzorsgWoPY9VTD6ygcc7o1uotP8JXapMWmu3bCCehfpPwZQCoYmrycznDdGTZoguJ" ], "script": "base64:BgJhCAISDgoMCAgICAgIAQEBAQEBEgUKAwgIARIHCgUYAQEBARIAEgASBQoDCAEIEgMKAQESBAoCAQgSABIFCgMIAQESBAoCAQESBAoCCAESBgoECAEBCBIGCgQICAEBEgASALMBAQ9nZXRTdHJpbmdPckZhaWwCB2FkZHJlc3MDa2V5CQETdmFsdWVPckVycm9yTWVzc2FnZQIJAJ0IAgUHYWRkcmVzcwUDa2V5CQC5CQIJAMwIAgIKbWFuZGF0b3J5IAkAzAgCCQClCAEFB2FkZHJlc3MJAMwIAgIBLgkAzAgCBQNrZXkJAMwIAgIPIGlzIG5vdCBkZWZpbmVkBQNuaWwCAAEFbGNhbGMBAWwJALkIAQUBbAEOZ2V0TnVtYmVyQnlLZXkBA2tleQkBC3ZhbHVlT3JFbHNlAgkAmggCBQR0aGlzBQNrZXkAAAEOZ2V0U3RyaW5nQnlLZXkBA2tleQkBC3ZhbHVlT3JFbHNlAgkAnQgCBQR0aGlzBQNrZXkCAAEMZ2V0Qm9vbEJ5S2V5AQNrZXkJAQt2YWx1ZU9yRWxzZQIJAJsIAgUEdGhpcwUDa2V5BwEYZ2V0TnVtYmVyQnlBZGRyZXNzQW5kS2V5AgdhZGRyZXNzA2tleQkBC3ZhbHVlT3JFbHNlAgkAmggCBQdhZGRyZXNzBQNrZXkAAAEYZ2V0U3RyaW5nQnlBZGRyZXNzQW5kS2V5AgdhZGRyZXNzA2tleQkBC3ZhbHVlT3JFbHNlAgkAnQgCCQERQGV4dHJOYXRpdmUoMTA2MikBBQdhZGRyZXNzBQNrZXkCAAEWZ2V0Qm9vbEJ5QWRkcmVzc0FuZEtleQIHYWRkcmVzcwNrZXkJAQt2YWx1ZU9yRWxzZQIJAJsIAgUHYWRkcmVzcwUDa2V5BwEJYXNBbnlMaXN0AQF2BAckbWF0Y2gwBQF2AwkAAQIFByRtYXRjaDACCUxpc3RbQW55XQQBbAUHJG1hdGNoMAUBbAkAAgECG2ZhaWwgdG8gY2FzdCBpbnRvIExpc3RbQW55XQEIYXNTdHJpbmcBAXYEByRtYXRjaDAFAXYDCQABAgUHJG1hdGNoMAIGU3RyaW5nBAFzBQckbWF0Y2gwBQFzCQACAQIYZmFpbCB0byBjYXN0IGludG8gU3RyaW5nAQVhc0ludAEBdgQHJG1hdGNoMAUBdgMJAAECBQckbWF0Y2gwAgNJbnQEAWkFByRtYXRjaDAFAWkJAAIBAhVmYWlsIHRvIGNhc3QgaW50byBJbnQBB2FzQnl0ZXMBA3ZhbAQHJG1hdGNoMAUDdmFsAwkAAQIFByRtYXRjaDACCkJ5dGVWZWN0b3IEB3ZhbEJ5dGUFByRtYXRjaDAFB3ZhbEJ5dGUJAAIBAhxmYWlsIHRvIGNhc3QgaW50byBCeXRlVmVjdG9yAQlhc1BheW1lbnQBAXYEByRtYXRjaDAFAXYDCQABAgUHJG1hdGNoMAIPQXR0YWNoZWRQYXltZW50BAFwBQckbWF0Y2gwBQFwCQACAQIhZmFpbCB0byBjYXN0IGludG8gQXR0YWNoZWRQYXltZW50ARJhc1N3YXBQYXJhbXNTVFJVQ1QBAXYEByRtYXRjaDAFAXYDCQABAgUHJG1hdGNoMAIjKEludCwgSW50LCBJbnQsIEludCwgSW50LCBJbnQsIEludCkEBnN0cnVjdAUHJG1hdGNoMAUGc3RydWN0CQACAQIdZmFpbCB0byBjYXN0IGludG8gVHVwbGU1IGludHMAA1NFUAICX18AB0xJU1RTRVACAToABU1VTFQ4AIDC1y8ABU1VTFQ2AMCEPQAOREVGQVVMVFNXQVBGRUUAoJwBAAtCUlBST1RFQ1RFRACgjQYAB1dBVkVTSUQBBBOr2TMADElkeE5ldEFtb3VudAAAAAxJZHhGZWVBbW91bnQAAQAOSWR4R3Jvc3NBbW91bnQAAgAZSWR4Q29udHJvbENmZ05ldXRyaW5vRGFwcAABABhJZHhDb250cm9sQ2ZnQXVjdGlvbkRhcHAAAgAUSWR4Q29udHJvbENmZ1JwZERhcHAAAwAVSWR4Q29udHJvbENmZ01hdGhEYXBwAAQAHElkeENvbnRyb2xDZmdMaXF1aWRhdGlvbkRhcHAABQAVSWR4Q29udHJvbENmZ1Jlc3REYXBwAAYAHUlkeENvbnRyb2xDZmdOb2RlUmVnaXN0cnlEYXBwAAcAHElkeENvbnRyb2xDZmdOc2J0U3Rha2luZ0RhcHAACAAZSWR4Q29udHJvbENmZ01lZGlhdG9yRGFwcAAJABxJZHhDb250cm9sQ2ZnU3VyZlN0YWtpbmdEYXBwAAoAIElkeENvbnRyb2xDZmdHbnNidENvbnRyb2xsZXJEYXBwAAsAF0lkeENvbnRyb2xDZmdSZXN0VjJEYXBwAAwAG0lkeENvbnRyb2xDZmdHb3Zlcm5hbmNlRGFwcAANARFrZXlDb250cm9sQWRkcmVzcwACHCVzJXNfX2NvbmZpZ19fY29udHJvbEFkZHJlc3MBDWtleUNvbnRyb2xDZmcAAhElc19fY29udHJvbENvbmZpZwEUcmVhZENvbnRyb2xDZmdPckZhaWwBB2NvbnRyb2wJALwJAgkBD2dldFN0cmluZ09yRmFpbAIFB2NvbnRyb2wJAQ1rZXlDb250cm9sQ2ZnAAUDU0VQARhnZXRDb250cmFjdEFkZHJlc3NPckZhaWwCCmNvbnRyb2xDZmcDaWR4CQETdmFsdWVPckVycm9yTWVzc2FnZQIJAKYIAQkAkQMCBQpjb250cm9sQ2ZnBQNpZHgJAKwCAgItQ29udHJvbCBjZmcgZG9lc24ndCBjb250YWluIGFkZHJlc3MgYXQgaW5kZXggCQCkAwEFA2lkeAAPY29udHJvbENvbnRyYWN0CQERQGV4dHJOYXRpdmUoMTA2MikBCQELdmFsdWVPckVsc2UCCQCdCAIFBHRoaXMJARFrZXlDb250cm9sQWRkcmVzcwACIzNONE5TN2Q0Sm85YTZGMTRMaUZVS0tZVmRVa2tmMmVQNFp4AApjb250cm9sQ2ZnCQEUcmVhZENvbnRyb2xDZmdPckZhaWwBBQ9jb250cm9sQ29udHJhY3QADG1hdGhDb250cmFjdAkBGGdldENvbnRyYWN0QWRkcmVzc09yRmFpbAIFCmNvbnRyb2xDZmcFFUlkeENvbnRyb2xDZmdNYXRoRGFwcAATbnNidFN0YWtpbmdDb250cmFjdAkBGGdldENvbnRyYWN0QWRkcmVzc09yRmFpbAIFCmNvbnRyb2xDZmcFHElkeENvbnRyb2xDZmdOc2J0U3Rha2luZ0RhcHAAE3N1cmZTdGFraW5nQ29udHJhY3QJARhnZXRDb250cmFjdEFkZHJlc3NPckZhaWwCBQpjb250cm9sQ2ZnBRxJZHhDb250cm9sQ2ZnU3VyZlN0YWtpbmdEYXBwABdnbnNidENvbnRyb2xsZXJDb250cmFjdAkBGGdldENvbnRyYWN0QWRkcmVzc09yRmFpbAIFCmNvbnRyb2xDZmcFIElkeENvbnRyb2xDZmdHbnNidENvbnRyb2xsZXJEYXBwAA9hdWN0aW9uQ29udHJhY3QJARhnZXRDb250cmFjdEFkZHJlc3NPckZhaWwCBQpjb250cm9sQ2ZnBRhJZHhDb250cm9sQ2ZnQXVjdGlvbkRhcHAAFG5vZGVSZWdpc3RyeUNvbnRyYWN0CQEYZ2V0Q29udHJhY3RBZGRyZXNzT3JGYWlsAgUKY29udHJvbENmZwUdSWR4Q29udHJvbENmZ05vZGVSZWdpc3RyeURhcHAAC2dvdkNvbnRyYWN0CQEYZ2V0Q29udHJhY3RBZGRyZXNzT3JGYWlsAgUKY29udHJvbENmZwUbSWR4Q29udHJvbENmZ0dvdmVybmFuY2VEYXBwABJOZXV0cmlub0Fzc2V0SWRLZXkCEW5ldXRyaW5vX2Fzc2V0X2lkAA5Cb25kQXNzZXRJZEtleQINYm9uZF9hc3NldF9pZAASQXVjdGlvbkNvbnRyYWN0S2V5AhBhdWN0aW9uX2NvbnRyYWN0ABZOc2J0U3Rha2luZ0NvbnRyYWN0S2V5AhNuc2J0U3Rha2luZ0NvbnRyYWN0ABZMaXF1aWRhdGlvbkNvbnRyYWN0S2V5AhRsaXF1aWRhdGlvbl9jb250cmFjdAAOUlBEQ29udHJhY3RLZXkCDHJwZF9jb250cmFjdAARQ29udG9sQ29udHJhY3RLZXkCEGNvbnRyb2xfY29udHJhY3QAD01hdGhDb250cmFjdEtleQINbWF0aF9jb250cmFjdAAbQmFsYW5jZVdhdmVzTG9ja0ludGVydmFsS2V5AhtiYWxhbmNlX3dhdmVzX2xvY2tfaW50ZXJ2YWwAHkJhbGFuY2VOZXV0cmlub0xvY2tJbnRlcnZhbEtleQIeYmFsYW5jZV9uZXV0cmlub19sb2NrX2ludGVydmFsABVNaW5Ub2tlblN3YXBBbW91bnRLZXkCG21pbl9vdXROZXV0cmlub19zd2FwX2Ftb3VudAAYTWluTmV1dHJpbm9Td2FwQW1vdW50S2V5AhhtaW5fb3V0TXVsdGlfc3dhcF9hbW91bnQAG05vZGVPcmFjbGVQcm92aWRlclB1YktleUtleQIUbm9kZV9vcmFjbGVfcHJvdmlkZXIAFU5ldXRyaW5vT3V0RmVlUGFydEtleQIYbmV1dHJpbm9PdXRfc3dhcF9mZWVQYXJ0ABJXYXZlc091dEZlZVBhcnRLZXkCFXdhdmVzT3V0X3N3YXBfZmVlUGFydAEPa2V5Tm9kZVJlZ2lzdHJ5AQdhZGRyZXNzCQCsAgICBCVzX18FB2FkZHJlc3MACFByaWNlS2V5AgVwcmljZQANUHJpY2VJbmRleEtleQILcHJpY2VfaW5kZXgADElzQmxvY2tlZEtleQIKaXNfYmxvY2tlZAESZ2V0UHJpY2VIaXN0b3J5S2V5AQVibG9jawkArAICCQCsAgIFCFByaWNlS2V5AgFfCQCkAwEFBWJsb2NrARhnZXRIZWlnaHRQcmljZUJ5SW5kZXhLZXkBBWluZGV4CQCsAgIJAKwCAgUNUHJpY2VJbmRleEtleQIBXwkApAMBBQVpbmRleAEVZ2V0U3Rha2luZ05vZGVCeUluZGV4AQNpZHgJAQ5nZXRTdHJpbmdCeUtleQEJALkJAgkAzAgCAgYlcyVkJXMJAMwIAgIFbGVhc2UJAMwIAgkApAMBBQNpZHgJAMwIAgILbm9kZUFkZHJlc3MFA25pbAUDU0VQARxnZXRTdGFraW5nTm9kZUFkZHJlc3NCeUluZGV4AQNpZHgJARFAZXh0ck5hdGl2ZSgxMDYyKQEJARVnZXRTdGFraW5nTm9kZUJ5SW5kZXgBBQNpZHgBH2dldFJlc2VydmVkQW1vdW50Rm9yU3BvbnNvcnNoaXAACQELdmFsdWVPckVsc2UCCQCaCAIFBHRoaXMJALkJAgkAzAgCAgQlcyVzCQDMCAICBWxlYXNlCQDMCAICF3Nwb25zb3JzaGlwV2F2ZXNSZXNlcnZlBQNuaWwFA1NFUAkAaAIA6AcFBU1VTFQ4ARhnZXRCYWxhbmNlVW5sb2NrQmxvY2tLZXkBBW93bmVyCQCsAgICFWJhbGFuY2VfdW5sb2NrX2Jsb2NrXwUFb3duZXIBDWdldExlYXNlSWRLZXkBCW5vZGVJbmRleAkAuQkCCQDMCAICBiVzJWQlcwkAzAgCAgVsZWFzZQkAzAgCCQCkAwEFCW5vZGVJbmRleAkAzAgCAgJpZAUDbmlsBQNTRVABFmdldExlYXNlSWRCeUFkZHJlc3NLZXkBC25vZGVBZGRyZXNzCQC5CQIJAMwIAgIGJXMlcyVzCQDMCAICDmxlYXNlQnlBZGRyZXNzCQDMCAIFC25vZGVBZGRyZXNzCQDMCAICAmlkBQNuaWwFA1NFUAERZ2V0TGVhc2VBbW91bnRLZXkBCW5vZGVJbmRleAkAuQkCCQDMCAICBiVzJWQlcwkAzAgCAgVsZWFzZQkAzAgCCQCkAwEFCW5vZGVJbmRleAkAzAgCAgZhbW91bnQFA25pbAUDU0VQARpnZXRMZWFzZUFtb3VudEJ5QWRkcmVzc0tleQELbm9kZUFkZHJlc3MJALkJAgkAzAgCAgYlcyVzJXMJAMwIAgIObGVhc2VCeUFkZHJlc3MJAMwIAgULbm9kZUFkZHJlc3MJAMwIAgIGYW1vdW50BQNuaWwFA1NFUAEYZ2V0TGVhc2VHcm91cE5vZGVMaXN0S2V5AQhncm91cE51bQkAuQkCCQDMCAICBiVzJWQlcwkAzAgCAgpsZWFzZUdyb3VwCQDMCAIJAKQDAQUIZ3JvdXBOdW0JAMwIAgIIbm9kZUxpc3QFA25pbAUDU0VQARBtaW5Td2FwQW1vdW50S0VZAQhzd2FwVHlwZQkArAICCQCsAgICBG1pbl8FCHN3YXBUeXBlAgxfc3dhcF9hbW91bnQBDnRvdGFsTG9ja2VkS0VZAghzd2FwVHlwZQdhc3NldElkCQC5CQIJAMwIAgIGJXMlcyVzCQDMCAICC2JhbGFuY2VMb2NrCQDMCAIFCHN3YXBUeXBlCQDMCAIFB2Fzc2V0SWQFA25pbAUDU0VQARR0b3RhbExvY2tlZEJ5VXNlcktFWQMIc3dhcFR5cGUFb3duZXIHYXNzZXRJZAkAuQkCCQDMCAICCCVzJXMlcyVzCQDMCAICC2JhbGFuY2VMb2NrCQDMCAIFCHN3YXBUeXBlCQDMCAIFBW93bmVyCQDMCAIFB2Fzc2V0SWQFA25pbAIBXwEWYmFsYW5jZUxvY2tJbnRlcnZhbEtFWQEIc3dhcFR5cGUJAKwCAgkArAICAghiYWxhbmNlXwUIc3dhcFR5cGUCDl9sb2NrX2ludGVydmFsARpub2RlQmFsYW5jZUxvY2tJbnRlcnZhbEtFWQACGmJhbGFuY2Vfbm9kZV9sb2NrX2ludGVydmFsARFzd2Fwc1RpbWVmcmFtZUtFWQACD3N3YXBzX3RpbWVmcmFtZQEOYnJQcm90ZWN0ZWRLRVkAAhdtaW5fQlJfcHJvdGVjdGlvbl9sZXZlbAEPYmFza2V0QXNzZXRzS2V5AAIaJXMlc19fY29tbW9uX19iYXNrZXRBc3NldHMBCmJhc2tldERLZXkAAhUlcyVzX19jb21tb25fX2Jhc2tldEQBCmJhc2tldEtLZXkAAhUlcyVzX19jb21tb25fX2Jhc2tldEsBCmJhc2tldEJLZXkAAhUlcyVzX19jb21tb25fX2Jhc2tldEIBEmJhc2tldExvZ2JEZWx0YUtleQACHSVzJXNfX2NvbW1vbl9fYmFza2V0TG9nYkRlbHRhARZiYXNrZXRXZWlnaHRCeUFzc2V0S2V5AQdhc3NldElkCQCsAgICHyVzJXMlc19fY29tbW9uX193ZWlnaHRCeUFzc2V0X18FB2Fzc2V0SWQBEW1pblN3YXBBbW91bnRSRUFEAQhzd2FwVHlwZQkBC3ZhbHVlT3JFbHNlAgkAmggCBQR0aGlzCQEQbWluU3dhcEFtb3VudEtFWQEFCHN3YXBUeXBlAAABEnN3YXBzVGltZWZyYW1lUkVBRAAJAQt2YWx1ZU9yRWxzZQIJAJoIAgUEdGhpcwkBEXN3YXBzVGltZWZyYW1lS0VZAACgCwEPdG90YWxMb2NrZWRSRUFEAghzd2FwVHlwZQdhc3NldElkCQELdmFsdWVPckVsc2UCCQCaCAIFBHRoaXMJAQ50b3RhbExvY2tlZEtFWQIFCHN3YXBUeXBlBQdhc3NldElkAAABFXRvdGFsTG9ja2VkQnlVc2VyUkVBRAMIc3dhcFR5cGUFb3duZXIHYXNzZXRJZAkBC3ZhbHVlT3JFbHNlAgkAmggCBQR0aGlzCQEUdG90YWxMb2NrZWRCeVVzZXJLRVkDBQhzd2FwVHlwZQUFb3duZXIFB2Fzc2V0SWQAAAEXYmFsYW5jZUxvY2tJbnRlcnZhbFJFQUQBCHN3YXBUeXBlCQELdmFsdWVPckVsc2UCCQCaCAIFBHRoaXMJARZiYWxhbmNlTG9ja0ludGVydmFsS0VZAQUIc3dhcFR5cGUAoAsBG25vZGVCYWxhbmNlTG9ja0ludGVydmFsUkVBRAAJAQt2YWx1ZU9yRWxzZQIJAJoIAgUEdGhpcwkBGm5vZGVCYWxhbmNlTG9ja0ludGVydmFsS0VZAAABARhrZXlTd2FwVXNlclNwZW50SW5QZXJpb2QBC3VzZXJBZGRyZXNzCQC5CQIJAMwIAgIEJXMlcwkAzAgCAhVzd2FwVXNlclNwZW50SW5QZXJpb2QJAMwIAgULdXNlckFkZHJlc3MFA25pbAUDU0VQARVrZXlVc2VyTGFzdFN3YXBIZWlnaHQBC3VzZXJBZGRyZXNzCQC5CQIJAMwIAgIEJXMlcwkAzAgCAhJ1c2VyTGFzdFN3YXBIZWlnaHQJAMwIAgULdXNlckFkZHJlc3MFA25pbAUDU0VQARZjb252ZXJ0TmV1dHJpbm9Ub1dhdmVzAgZhbW91bnQFcHJpY2UJAGsDBQZhbW91bnQFBU1VTFQ4BQVwcmljZQEWY29udmVydFdhdmVzVG9OZXV0cmlubwIGYW1vdW50BXByaWNlCQBrAwUGYW1vdW50BQVwcmljZQUFTVVMVDgBEmNvbnZlcnRXYXZlc1RvQm9uZAIGYW1vdW50BXByaWNlCQEWY29udmVydFdhdmVzVG9OZXV0cmlubwIFBmFtb3VudAUFcHJpY2UBFmNvbnZlcnRKc29uQXJyYXlUb0xpc3QBCWpzb25BcnJheQkAtQkCBQlqc29uQXJyYXkCASwBG2NvbnZlcnRCYXNrZXRJdGVtVG9OZXV0cmlubwQIaW5BbW91bnQHaW5Bc3NldA1iYXNrZXRJbmZvU3RyDW5ldXRyaW5vUHJpY2UECmJhc2tldEluZm8JALUJAgUNYmFza2V0SW5mb1N0cgIBXwoBCGZpbmRJdGVtAgNhY2MEaXRlbQQFYkl0ZW0JALUJAgUEaXRlbQUHTElTVFNFUAMJAAACCQCRAwIFBWJJdGVtAAAFB2luQXNzZXQFBWJJdGVtBQNhY2MEAml0CgACJGwFCmJhc2tldEluZm8KAAIkcwkAkAMBBQIkbAoABSRhY2MwBQNuaWwKAQUkZjBfMQICJGECJGkDCQBnAgUCJGkFAiRzBQIkYQkBCGZpbmRJdGVtAgUCJGEJAJEDAgUCJGwFAiRpCgEFJGYwXzICAiRhAiRpAwkAZwIFAiRpBQIkcwUCJGEJAAIBAhRMaXN0IHNpemUgZXhjZWVkcyAxMAkBBSRmMF8yAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgUFJGFjYzAAAAABAAIAAwAEAAUABgAHAAgACQAKBAVkbG9nNgkBC3ZhbHVlT3JFbHNlAgkAnwgBCQESYmFza2V0TG9nYkRlbHRhS2V5AADeo4P3//////8BBAJkNgkBC3ZhbHVlT3JFbHNlAgkAnwgBCQEKYmFza2V0REtleQAA0IYDBAJiNgkBC3ZhbHVlT3JFbHNlAgkAnwgBCQEKYmFza2V0QktleQAA0KVMBAJ3NgkBC3ZhbHVlT3JFbHNlAgkAnwgBCQEWYmFza2V0V2VpZ2h0QnlBc3NldEtleQEFB2luQXNzZXQAipYKBAJwNgkAZAIJAGUCBQVkbG9nNgUCdzYJAQ1wYXJzZUludFZhbHVlAQkAkQMCBQJpdAABBANwcDYJAGwGBQJiNgAGBQJwNgAGAAYFBkhBTEZVUAQOb3V0QW1vdW50R3Jvc3MJAGsDBQhpbkFtb3VudAkBDXBhcnNlSW50VmFsdWUBCQCRAwIFAml0AAIDCQAAAgUNbmV1dHJpbm9QcmljZQAABQVNVUxUOAkAaAIFDW5ldXRyaW5vUHJpY2UAZAQKbXVsdGlwbGllcgkAZQIJAGQCBQVNVUxUNgUCZDYFA3BwNgMJAGcCAAAFCm11bHRpcGxpZXIJAAIBCQCsAgICKU5ldCBhbW91bnQgaXMgbm9uLXBvc2l0aXZlLCBtdWx0aXBsaWVyID0gCQCkAwEFCm11bHRpcGxpZXIECW5ldEFtb3VudAkAawMFDm91dEFtb3VudEdyb3NzBQptdWx0aXBsaWVyBQVNVUxUNgQJZmVlQW1vdW50AwkAZwIFCW5ldEFtb3VudAUOb3V0QW1vdW50R3Jvc3MAAAkAZQIFDm91dEFtb3VudEdyb3NzBQluZXRBbW91bnQJAMwIAgUOb3V0QW1vdW50R3Jvc3MJAMwIAgUJbmV0QW1vdW50CQDMCAIFCWZlZUFtb3VudAUDbmlsARdjb252ZXJ0TmV1dHJpbm9Ub0Jhc2tldAMGYW1vdW50DWJhc2tldEluZm9TdHIObmV1dHJpbm9TdXBwbHkECmJhc2tldEluZm8JALUJAgUNYmFza2V0SW5mb1N0cgIBXwoBBGNvbnYCA2FjYwRpdGVtBAVwYXJ0cwkAtQkCBQRpdGVtBQdMSVNUU0VQBAdhc3NldElkCQCRAwIFBXBhcnRzAAAEBXNoYXJlCQENcGFyc2VJbnRWYWx1ZQEJAJEDAgUFcGFydHMAAQQFcHJpY2UJAQ1wYXJzZUludFZhbHVlAQkAkQMCBQVwYXJ0cwACBAZyZXNlcnYJAQ1wYXJzZUludFZhbHVlAQkAkQMCBQVwYXJ0cwADAwkAAAIFDm5ldXRyaW5vU3VwcGx5AAAJAAIBAidBdHRlbXB0IHRvIGJ1cm4gbmV1dHJpbm8gYXQgemVybyBzdXBwbHkECW91dEFtb3VudAkAawMFBmFtb3VudAUGcmVzZXJ2BQ5uZXV0cmlub1N1cHBseQQKd2F2ZXNUdXBsZQMJAAACBQdhc3NldElkAgVXQVZFUwkAlAoCBQlvdXRBbW91bnQFBXByaWNlCQCUCgIIBQNhY2MCXzIIBQNhY2MCXzMJAJUKAwkAzQgCCAUDYWNjAl8xCQCWCgQFB2Fzc2V0SWQFCW91dEFtb3VudAUFcHJpY2UFBXNoYXJlCAUKd2F2ZXNUdXBsZQJfMQgFCndhdmVzVHVwbGUCXzIKAAIkbAUKYmFza2V0SW5mbwoAAiRzCQCQAwEFAiRsCgAFJGFjYzAJAJUKAwUDbmlsAAAAAAoBBSRmMF8xAgIkYQIkaQMJAGcCBQIkaQUCJHMFAiRhCQEEY29udgIFAiRhCQCRAwIFAiRsBQIkaQoBBSRmMF8yAgIkYQIkaQMJAGcCBQIkaQUCJHMFAiRhCQACAQIUTGlzdCBzaXplIGV4Y2VlZHMgMTAJAQUkZjBfMgIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIFBSRhY2MwAAAAAQACAAMABAAFAAYABwAIAAkACgERbWluU3dhcEFtb3VudEZBSUwCCHN3YXBUeXBlDW1pblN3YXBBbW91bnQJAAIBCQCsAgIJAKwCAgkArAICAhhUaGUgc3BlY2lmaWVkIGFtb3VudCBpbiAFCHN3YXBUeXBlAisgc3dhcCBpcyBsZXNzIHRoYW4gdGhlIHJlcXVpcmVkIG1pbmltdW0gb2YgCQCkAwEFDW1pblN3YXBBbW91bnQBFWVtZXJnZW5jeVNodXRkb3duRkFJTAAJAAIBAlpjb250cmFjdCBpcyBibG9ja2VkIGJ5IEVNRVJHRU5DWSBTSFVURE9XTiBhY3Rpb25zIHVudGlsbCByZWFjdGl2YXRpb24gYnkgZW1lcmdlbmN5IG9yYWNsZXMBDnByaWNlSW5kZXhGQUlMBQVpbmRleApwcmljZUluZGV4C2luZGV4SGVpZ2h0DHVubG9ja0hlaWdodA9wcmV2SW5kZXhIZWlnaHQJAAIBCQCsAgIJAKwCAgkArAICCQCsAgIJAKwCAgkArAICCQCsAgIJAKwCAgkArAICAiNpbnZhbGlkIHByaWNlIGhpc3RvcnkgaW5kZXg6IGluZGV4PQkApAMBBQVpbmRleAIMIHByaWNlSW5kZXg9CQCkAwEFCnByaWNlSW5kZXgCDSBpbmRleEhlaWdodD0JAKQDAQULaW5kZXhIZWlnaHQCDiB1bmxvY2tIZWlnaHQ9CQCkAwEFDHVubG9ja0hlaWdodAIRIHByZXZJbmRleEhlaWdodD0JAKQDAQUPcHJldkluZGV4SGVpZ2h0AA9uZXV0cmlub0Fzc2V0SWQJANkEAQkBDmdldFN0cmluZ0J5S2V5AQUSTmV1dHJpbm9Bc3NldElkS2V5AApwcmljZUluZGV4CQEYZ2V0TnVtYmVyQnlBZGRyZXNzQW5kS2V5AgUPY29udHJvbENvbnRyYWN0BQ1QcmljZUluZGV4S2V5AAlpc0Jsb2NrZWQJARZnZXRCb29sQnlBZGRyZXNzQW5kS2V5AgUPY29udHJvbENvbnRyYWN0BQxJc0Jsb2NrZWRLZXkAGG5vZGVPcmFjbGVQcm92aWRlclB1YktleQkA2QQBCQEOZ2V0U3RyaW5nQnlLZXkBBRtOb2RlT3JhY2xlUHJvdmlkZXJQdWJLZXlLZXkAC2JvbmRBc3NldElkCQDZBAECLEYzaWF4enJ1RmVLdWpmVmZZU1pFa2VqcGpoNjd3bVJmUENSSGlObVdLcDNaABVkZXByZWNhdGVkQm9uZEFzc2V0SWQJANkEAQIsOTc1YWtaQmZuTWo1MTNVN01aYUhLelFybXNFeDVhRTN3ZFdLVHJIQmhiakYAEG5ldXRyaW5vQ29udHJhY3QFBHRoaXMADGN1cnJlbnRQcmljZQkBGGdldE51bWJlckJ5QWRkcmVzc0FuZEtleQIFD2NvbnRyb2xDb250cmFjdAUIUHJpY2VLZXkBG2NoZWNrSXNWYWxpZE1pblNwb25zb3JlZEZlZQECdHgEDk1JTlRSQU5TRkVSRkVFAKCNBgQWU3BvbnNvcmVkRmVlVXBwZXJCb3VuZADoBwQPcmVhbE5ldXRyaW5vRmVlCQEWY29udmVydFdhdmVzVG9OZXV0cmlubwIFDk1JTlRSQU5TRkVSRkVFBQxjdXJyZW50UHJpY2UEDm1pbk5ldXRyaW5vRmVlCQBoAgUPcmVhbE5ldXRyaW5vRmVlAAIEDm1heE5ldXRyaW5vRmVlCQBrAwUPcmVhbE5ldXRyaW5vRmVlBRZTcG9uc29yZWRGZWVVcHBlckJvdW5kAGQECGlucHV0RmVlCQEFdmFsdWUBCAUCdHgUbWluU3BvbnNvcmVkQXNzZXRGZWUDAwkAZwIFCGlucHV0RmVlBQ5taW5OZXV0cmlub0ZlZQkAZwIFDm1heE5ldXRyaW5vRmVlBQhpbnB1dEZlZQcJAAACCAUCdHgHYXNzZXRJZAUPbmV1dHJpbm9Bc3NldElkBwEPZ2V0UHJpY2VIaXN0b3J5AQVibG9jawkBGGdldE51bWJlckJ5QWRkcmVzc0FuZEtleQIFD2NvbnRyb2xDb250cmFjdAkBEmdldFByaWNlSGlzdG9yeUtleQEFBWJsb2NrARVnZXRIZWlnaHRQcmljZUJ5SW5kZXgBBWluZGV4CQEYZ2V0TnVtYmVyQnlBZGRyZXNzQW5kS2V5AgUPY29udHJvbENvbnRyYWN0CQEYZ2V0SGVpZ2h0UHJpY2VCeUluZGV4S2V5AQUFaW5kZXgBFmtleUxvY2tQYXJhbVVzZXJBbW91bnQBC3VzZXJBZGRyZXNzCQC5CQIJAMwIAgIGJXMlcyVzCQDMCAICC3BhcmFtQnlVc2VyCQDMCAIFC3VzZXJBZGRyZXNzCQDMCAICBmFtb3VudAUDbmlsBQNTRVAADHNJZHhTd2FwVHlwZQABAApzSWR4U3RhdHVzAAIADHNJZHhJbkFtb3VudAADAA9zSWR4U3RhcnRIZWlnaHQABwASc0lkeFN0YXJ0VGltZXN0YW1wAAgAFHNJZHhTZWxmVW5sb2NrSGVpZ2h0AAsAC3NJZHhNaW5SYW5kAA8AC3NJZHhNYXhSYW5kABAAC3NJZHhJbkFzc2V0ABQBB3N3YXBLRVkCC3VzZXJBZGRyZXNzBHR4SWQJALkJAgkAzAgCAgQlcyVzCQDMCAIFC3VzZXJBZGRyZXNzCQDMCAIFBHR4SWQFA25pbAUDU0VQAQtzdHJTd2FwREFUQRQIc3dhcFR5cGUGc3RhdHVzCGluQW1vdW50BXByaWNlDG91dE5ldEFtb3VudAxvdXRGZWVBbW91bnQLc3RhcnRIZWlnaHQOc3RhcnRUaW1lc3RhbXAJZW5kSGVpZ2h0DGVuZFRpbWVzdGFtcBBzZWxmVW5sb2NrSGVpZ2h0EHJhbmRVbmxvY2tIZWlnaHQFaW5kZXgMd2l0aGRyYXdUeElkB3JhbmRNaW4HcmFuZE1heApvdXRTdXJmQW10AmJyCmJhc2tldERhdGEHaW5Bc3NldAkAuQkCCQDMCAICKCVzJXMlZCVkJWQlZCVkJWQlZCVkJWQlZCVkJXMlZCVkJWQlZCVzJXMJAMwIAgUIc3dhcFR5cGUJAMwIAgUGc3RhdHVzCQDMCAIFCGluQW1vdW50CQDMCAIFBXByaWNlCQDMCAIFDG91dE5ldEFtb3VudAkAzAgCBQxvdXRGZWVBbW91bnQJAMwIAgULc3RhcnRIZWlnaHQJAMwIAgUOc3RhcnRUaW1lc3RhbXAJAMwIAgUJZW5kSGVpZ2h0CQDMCAIFDGVuZFRpbWVzdGFtcAkAzAgCBRBzZWxmVW5sb2NrSGVpZ2h0CQDMCAIFEHJhbmRVbmxvY2tIZWlnaHQJAMwIAgUFaW5kZXgJAMwIAgUMd2l0aGRyYXdUeElkCQDMCAIFB3JhbmRNaW4JAMwIAgUHcmFuZE1heAkAzAgCBQpvdXRTdXJmQW10CQDMCAIFAmJyCQDMCAIFCmJhc2tldERhdGEJAMwIAgUHaW5Bc3NldAUDbmlsBQNTRVABD3BlbmRpbmdTd2FwREFUQQQIc3dhcFR5cGUNaW5Bc3NldEFtb3VudBBzZWxmVW5sb2NrSGVpZ2h0B2luQXNzZXQJAQtzdHJTd2FwREFUQRQFCHN3YXBUeXBlAgdQRU5ESU5HCQCkAwEFDWluQXNzZXRBbW91bnQCATACATACATAJAKQDAQUGaGVpZ2h0CQCkAwEIBQlsYXN0QmxvY2sJdGltZXN0YW1wAgEwAgEwCQCkAwEFEHNlbGZVbmxvY2tIZWlnaHQCATACATACBE5VTEwCATACATACATACATACAAUHaW5Bc3NldAEOZmluaXNoU3dhcERBVEELCWRhdGFBcnJheQVwcmljZQxvdXROZXRBbW91bnQMb3V0RmVlQW1vdW50EHJhbmRVbmxvY2tIZWlnaHQFaW5kZXgMd2l0aGRyYXdUeElkCm91dFN1cmZBbXQCYnIKYmFza2V0RGF0YQdpbkFzc2V0CQELc3RyU3dhcERBVEEUCQCRAwIFCWRhdGFBcnJheQUMc0lkeFN3YXBUeXBlAghGSU5JU0hFRAkAkQMCBQlkYXRhQXJyYXkFDHNJZHhJbkFtb3VudAkApAMBBQVwcmljZQkApAMBBQxvdXROZXRBbW91bnQJAKQDAQUMb3V0RmVlQW1vdW50CQCRAwIFCWRhdGFBcnJheQUPc0lkeFN0YXJ0SGVpZ2h0CQCRAwIFCWRhdGFBcnJheQUSc0lkeFN0YXJ0VGltZXN0YW1wCQCkAwEFBmhlaWdodAkApAMBCAUJbGFzdEJsb2NrCXRpbWVzdGFtcAkAkQMCBQlkYXRhQXJyYXkFFHNJZHhTZWxmVW5sb2NrSGVpZ2h0CQCkAwEFEHJhbmRVbmxvY2tIZWlnaHQJAKQDAQUFaW5kZXgFDHdpdGhkcmF3VHhJZAkAkQMCBQlkYXRhQXJyYXkFC3NJZHhNaW5SYW5kCQCRAwIFCWRhdGFBcnJheQULc0lkeE1heFJhbmQJAKQDAQUKb3V0U3VyZkFtdAkApAMBBQJicgUKYmFza2V0RGF0YQUHaW5Bc3NldAESc3dhcERhdGFGYWlsT3JSRUFEAgt1c2VyQWRkcmVzcwhzd2FwVHhJZAQHc3dhcEtleQkBB3N3YXBLRVkCBQt1c2VyQWRkcmVzcwUIc3dhcFR4SWQJALwJAgkBE3ZhbHVlT3JFcnJvck1lc3NhZ2UCCQCiCAEFB3N3YXBLZXkJAKwCAgIRbm8gc3dhcCBkYXRhIGZvciAFB3N3YXBLZXkFA1NFUAEJYXBwbHlGZWVzAw5hbW91bnRPdXRHcm9zcwtpbkFtdFRvU1VSRgdmZWVQYXJ0BAlmZWVBbW91bnQJAGsDBQ5hbW91bnRPdXRHcm9zcwUHZmVlUGFydAUFTVVMVDYJAMwIAgkAZQIFDmFtb3VudE91dEdyb3NzBQlmZWVBbW91bnQJAMwIAgUJZmVlQW1vdW50BQNuaWwBD2FwcGx5RmVlc0Jhc2tldAEJYmFza2V0T3V0BAJrNgkBC3ZhbHVlT3JFbHNlAgkAnwgBCQEKYmFza2V0S0tleQAFBU1VTFQ2BAJkNgkBC3ZhbHVlT3JFbHNlAgkAnwgBCQEKYmFza2V0REtleQAA0IYDBAdmZWVQYXJ0CQBrAwUCazYFAmQ2BQVNVUxUNgoBCmZlZUFwcGxpZXICA2FjYwRpdGVtBAlmZWVBbW91bnQJAGsDCAUEaXRlbQJfMgUHZmVlUGFydAUFTVVMVDYJAM0IAgUDYWNjCQC5CQIJAMwIAggFBGl0ZW0CXzEJAMwIAgkApAMBCAUEaXRlbQJfMwkAzAgCCQCkAwEJAGUCCAUEaXRlbQJfMgUJZmVlQW1vdW50CQDMCAIJAKQDAQUJZmVlQW1vdW50CQDMCAIJAKQDAQgFBGl0ZW0CXzQFA25pbAUHTElTVFNFUAQBYQoAAiRsBQliYXNrZXRPdXQKAAIkcwkAkAMBBQIkbAoABSRhY2MwBQNuaWwKAQUkZjBfMQICJGECJGkDCQBnAgUCJGkFAiRzBQIkYQkBCmZlZUFwcGxpZXICBQIkYQkAkQMCBQIkbAUCJGkKAQUkZjBfMgICJGECJGkDCQBnAgUCJGkFAiRzBQIkYQkAAgECFExpc3Qgc2l6ZSBleGNlZWRzIDEwCQEFJGYwXzICCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECBQUkYWNjMAAAAAEAAgADAAQABQAGAAcACAAJAAoJALkJAgUBYQUDU0VQAQNhYnMBAXgDCQBmAgAABQF4CQEBLQEFAXgFAXgBCnNlbGVjdE5vZGUBDXVubGVhc2VBbW91bnQEDWFtb3VudFRvTGVhc2UJAGUCCQBlAggJAO8HAQUQbmV1dHJpbm9Db250cmFjdAlhdmFpbGFibGUFDXVubGVhc2VBbW91bnQJAR9nZXRSZXNlcnZlZEFtb3VudEZvclNwb25zb3JzaGlwAAQKb2xkTGVhc2VkMAkBDmdldE51bWJlckJ5S2V5AQkBEWdldExlYXNlQW1vdW50S2V5AQAABApvbGRMZWFzZWQxCQEOZ2V0TnVtYmVyQnlLZXkBCQERZ2V0TGVhc2VBbW91bnRLZXkBAAEECm5ld0xlYXNlZDAJAGQCBQ1hbW91bnRUb0xlYXNlBQpvbGRMZWFzZWQwBApuZXdMZWFzZWQxCQBkAgUNYW1vdW50VG9MZWFzZQUKb2xkTGVhc2VkMQMDCQBmAgUKbmV3TGVhc2VkMAAABgkAZgIFCm5ld0xlYXNlZDEAAAQGZGVsdGEwCQEDYWJzAQkAZQIFCm5ld0xlYXNlZDAFCm9sZExlYXNlZDEEBmRlbHRhMQkBA2FicwEJAGUCBQpuZXdMZWFzZWQxBQpvbGRMZWFzZWQwAwkAZwIFBmRlbHRhMQUGZGVsdGEwCQCUCgIAAAUKbmV3TGVhc2VkMAkAlAoCAAEFCm5ld0xlYXNlZDEJAJQKAgD///////////8BAAABCHRoaXNPbmx5AQFpAwkBAiE9AggFAWkGY2FsbGVyBQR0aGlzCQACAQItUGVybWlzc2lvbiBkZW5pZWQ6IHRoaXMgY29udHJhY3Qgb25seSBhbGxvd2VkBgEWcHJlcGFyZVVubGVhc2VBbmRMZWFzZQENdW5sZWFzZUFtb3VudAQJbm9kZVR1cGxlCQEKc2VsZWN0Tm9kZQEFDXVubGVhc2VBbW91bnQECW5vZGVJbmRleAgFCW5vZGVUdXBsZQJfMQQObmV3TGVhc2VBbW91bnQIBQlub2RlVHVwbGUCXzIDCQBmAgUObmV3TGVhc2VBbW91bnQAAAQKbGVhc2VJZEtleQkBDWdldExlYXNlSWRLZXkBBQlub2RlSW5kZXgECG9sZExlYXNlCQCcCAIFBHRoaXMFCmxlYXNlSWRLZXkEDnVubGVhc2VPckVtcHR5AwkBCWlzRGVmaW5lZAEFCG9sZExlYXNlCQDMCAIJAQtMZWFzZUNhbmNlbAEJAQV2YWx1ZQEFCG9sZExlYXNlBQNuaWwFA25pbAQObGVhc2VBbW91bnRLZXkJARFnZXRMZWFzZUFtb3VudEtleQEFCW5vZGVJbmRleAQFbGVhc2UJAMQIAgkBHGdldFN0YWtpbmdOb2RlQWRkcmVzc0J5SW5kZXgBBQlub2RlSW5kZXgFDm5ld0xlYXNlQW1vdW50CQDOCAIFDnVubGVhc2VPckVtcHR5CQDMCAIFBWxlYXNlCQDMCAIJAQtCaW5hcnlFbnRyeQIFCmxlYXNlSWRLZXkJAQVsY2FsYwEFBWxlYXNlCQDMCAIJAQxJbnRlZ2VyRW50cnkCCQERZ2V0TGVhc2VBbW91bnRLZXkBBQlub2RlSW5kZXgFDm5ld0xlYXNlQW1vdW50BQNuaWwFA25pbAEMcmVhZE5vZGVJbmZvAQdub2RlSWR4BAtub2RlQWRkcmVzcwkBHGdldFN0YWtpbmdOb2RlQWRkcmVzc0J5SW5kZXgBBQdub2RlSWR4BAxsZWFzZWRBbXRLRVkJARFnZXRMZWFzZUFtb3VudEtleQEFB25vZGVJZHgECWxlYXNlZEFtdAkBDmdldE51bWJlckJ5S2V5AQUMbGVhc2VkQW10S0VZBApsZWFzZUlkS0VZCQENZ2V0TGVhc2VJZEtleQEFB25vZGVJZHgEB2xlYXNlSWQJAQV2YWx1ZQEJAJwIAgUEdGhpcwUKbGVhc2VJZEtFWQkAlwoFBQtub2RlQWRkcmVzcwUMbGVhc2VkQW10S0VZBQlsZWFzZWRBbXQFCmxlYXNlSWRLRVkFB2xlYXNlSWQBCmNvbW1vblN3YXAFCHN3YXBUeXBlCXBtdEFtb3VudAdpbkFzc2V0DnVzZXJBZGRyZXNzU3RyBnR4SWQ1OAQQc3dhcFBhcmFtc1NUUlVDVAkBEmFzU3dhcFBhcmFtc1NUUlVDVAEJAPwHBAUEdGhpcwIbc3dhcFBhcmFtc0J5VXNlclNZU1JFQURPTkxZCQDMCAIFDnVzZXJBZGRyZXNzU3RyCQDMCAIAAAUDbmlsBQNuaWwEDnN3YXBMaW1pdFNwZW50CAUQc3dhcFBhcmFtc1NUUlVDVAJfMgQOYmxja3MyTG10UmVzZXQIBRBzd2FwUGFyYW1zU1RSVUNUAl8zBBF3YXZlc1N3YXBMaW1pdE1heAgFEHN3YXBQYXJhbXNTVFJVQ1QCXzYEEHVzZG5Td2FwTGltaXRNYXgIBRBzd2FwUGFyYW1zU1RSVUNUAl83BA1taW5Td2FwQW1vdW50CQERbWluU3dhcEFtb3VudFJFQUQBBQhzd2FwVHlwZQQLdG90YWxMb2NrZWQJAQ90b3RhbExvY2tlZFJFQUQCBQhzd2FwVHlwZQUHaW5Bc3NldAQRdG90YWxMb2NrZWRCeVVzZXIJARV0b3RhbExvY2tlZEJ5VXNlclJFQUQDBQhzd2FwVHlwZQUOdXNlckFkZHJlc3NTdHIFB2luQXNzZXQEC25vZGVBZGRyZXNzCQEVZ2V0U3Rha2luZ05vZGVCeUluZGV4AQAABAxwcmljZUJ5SW5kZXgJAQ9nZXRQcmljZUhpc3RvcnkBCQEVZ2V0SGVpZ2h0UHJpY2VCeUluZGV4AQUKcHJpY2VJbmRleAQMaXNTd2FwQnlOb2RlCQAAAgULbm9kZUFkZHJlc3MFDnVzZXJBZGRyZXNzU3RyBBZiYWxhbmNlTG9ja01heEludGVydmFsAwUMaXNTd2FwQnlOb2RlCQEbbm9kZUJhbGFuY2VMb2NrSW50ZXJ2YWxSRUFEAAkBF2JhbGFuY2VMb2NrSW50ZXJ2YWxSRUFEAQUIc3dhcFR5cGUEEHNlbGZVbmxvY2tIZWlnaHQJAGQCBQZoZWlnaHQFFmJhbGFuY2VMb2NrTWF4SW50ZXJ2YWwEDnN3YXBVc2RuVm9sdW1lAwkAAAIFCHN3YXBUeXBlAgtvdXROZXV0cmlubwkBFmNvbnZlcnRXYXZlc1RvTmV1dHJpbm8CBQlwbXRBbW91bnQFDHByaWNlQnlJbmRleAUJcG10QW1vdW50BAxzd2FwTGltaXRNYXgDCQAAAgUIc3dhcFR5cGUCC291dE5ldXRyaW5vCQEWY29udmVydFdhdmVzVG9OZXV0cmlubwIFEXdhdmVzU3dhcExpbWl0TWF4BQxwcmljZUJ5SW5kZXgFEHVzZG5Td2FwTGltaXRNYXgDCQBmAgUNbWluU3dhcEFtb3VudAUJcG10QW1vdW50CQERbWluU3dhcEFtb3VudEZBSUwCBQhzd2FwVHlwZQUNbWluU3dhcEFtb3VudAMDCQEBIQEFDGlzU3dhcEJ5Tm9kZQkAZgIFDnN3YXBMaW1pdFNwZW50AAAHCQACAQkArAICAjpZb3UgaGF2ZSBleGNlZWRlZCBzd2FwIGxpbWl0ISBOZXh0IGFsbG93ZWQgc3dhcCBoZWlnaHQgaXMgCQCkAwEJAGQCBQZoZWlnaHQFDmJsY2tzMkxtdFJlc2V0AwMJAQEhAQUMaXNTd2FwQnlOb2RlCQBmAgUOc3dhcFVzZG5Wb2x1bWUFDHN3YXBMaW1pdE1heAcJAAIBCQCsAgIJAKwCAgkArAICAi5Zb3UgaGF2ZSBleGNlZWRlZCB5b3VyIHN3YXAgbGltaXQhIFJlcXVlc3RlZDogCQCkAwEFDnN3YXBVc2RuVm9sdW1lAg0sIGF2YWlsYWJsZTogCQCkAwEFDHN3YXBMaW1pdE1heAMFCWlzQmxvY2tlZAkBFWVtZXJnZW5jeVNodXRkb3duRkFJTAAECWxlYXNlUGFydAMDCQAAAgUIc3dhcFR5cGUCC291dE5ldXRyaW5vCQAAAgUHaW5Bc3NldAIFV0FWRVMHCQEWcHJlcGFyZVVubGVhc2VBbmRMZWFzZQEAAAUDbmlsCQCUCgIJAM4IAgkAzAgCCQEMSW50ZWdlckVudHJ5AgkBGGtleVN3YXBVc2VyU3BlbnRJblBlcmlvZAEFDnVzZXJBZGRyZXNzU3RyBQ5zd2FwVXNkblZvbHVtZQkAzAgCCQEMSW50ZWdlckVudHJ5AgkBFWtleVVzZXJMYXN0U3dhcEhlaWdodAEFDnVzZXJBZGRyZXNzU3RyBQZoZWlnaHQJAMwIAgkBDEludGVnZXJFbnRyeQIJARR0b3RhbExvY2tlZEJ5VXNlcktFWQMFCHN3YXBUeXBlBQ51c2VyQWRkcmVzc1N0cgUHaW5Bc3NldAkAZAIFEXRvdGFsTG9ja2VkQnlVc2VyBQlwbXRBbW91bnQJAMwIAgkBDEludGVnZXJFbnRyeQIJARhnZXRCYWxhbmNlVW5sb2NrQmxvY2tLZXkBBQ51c2VyQWRkcmVzc1N0cgUQc2VsZlVubG9ja0hlaWdodAkAzAgCCQEMSW50ZWdlckVudHJ5AgkBDnRvdGFsTG9ja2VkS0VZAgUIc3dhcFR5cGUFB2luQXNzZXQJAGQCBQt0b3RhbExvY2tlZAUJcG10QW1vdW50CQDMCAIJAQtTdHJpbmdFbnRyeQIJAQdzd2FwS0VZAgUOdXNlckFkZHJlc3NTdHIFBnR4SWQ1OAkBD3BlbmRpbmdTd2FwREFUQQQFCHN3YXBUeXBlBQlwbXRBbW91bnQFEHNlbGZVbmxvY2tIZWlnaHQFB2luQXNzZXQFA25pbAUJbGVhc2VQYXJ0BQR1bml0AA9uTWV0cmljSWR4UHJpY2UAAAAbbk1ldHJpY0lkeFVzZG5Mb2NrZWRCYWxhbmNlAAEAHG5NZXRyaWNJZHhXYXZlc0xvY2tlZEJhbGFuY2UAAgARbk1ldHJpY0lkeFJlc2VydmUAAwAXbk1ldHJpY0lkeFJlc2VydmVJblVzZG4ABAAUbk1ldHJpY0lkeFVzZG5TdXBwbHkABQARbk1ldHJpY0lkeFN1cnBsdXMABgAYbk1ldHJpY0lkeFN1cnBsdXNQZXJjZW50AAcADG5NZXRyaWNJZHhCUgAIABRuTWV0cmljSWR4TnNidFN1cHBseQAJABduTWV0cmljSWR4TWF4TnNidFN1cHBseQAKABRuTWV0cmljSWR4U3VyZlN1cHBseQALABJuTWV0cmljVXNkblVzZHRQZWcADAAWbk1ldHJpY0N1cnJlbnRQcmljZUFkagANABFuTWV0cmljQmFza2V0SW5mbwAOABRuTWV0cmljV2VpZ2h0ZWRQcmljZQAPABduTWV0cmljVG90YWxSZXNlcnZlc1VzZAAQAAxiRnVuY0lkeFN1cmYAAAANYkZ1bmNJZHhXYXZlcwABAAxiRnVuY0lkeFVzZG4AAgAUYkZ1bmNJZHhSZXNlcnZlU3RhcnQAAwATYkZ1bmNJZHhTdXBwbHlTdGFydAAEAA9iRnVuY0lkeEJSU3RhcnQABQASYkZ1bmNJZHhSZXNlcnZlRW5kAAYAEWJGdW5jSWR4U3VwcGx5RW5kAAcADWJGdW5jSWR4QlJFbmQACAAMYkZ1bmNJZHhSZXN0AAkAEmJGdW5jSWR4V2F2ZXNQcmljZQAKAQ9jYWxjV2l0aGRyYXdUMlUECGluQW1vdW50B2luQXNzZXQKYmFza2V0SW5mbw13ZWlnaHRlZFByaWNlBAZvdXRBbXQJARtjb252ZXJ0QmFza2V0SXRlbVRvTmV1dHJpbm8EBQhpbkFtb3VudAUHaW5Bc3NldAUKYmFza2V0SW5mbwUNd2VpZ2h0ZWRQcmljZQkAngoMCQCRAwIFBm91dEFtdAAABQ9uZXV0cmlub0Fzc2V0SWQAAAUHaW5Bc3NldAAABQ13ZWlnaHRlZFByaWNlBQhpbkFtb3VudAAAAAAAAAUDbmlsCQDMCAIJAJEDAgUGb3V0QW10AAEJAMwIAgkAkQMCBQZvdXRBbXQAAgUDbmlsARRjYWxjV2l0aGRyYXdVMkJhc2tldAUGdXNkbkluCmJhc2tldEluZm8CYnIOcmVzZXJ2ZXNJblVzZG4ObmV1dHJpbm9TdXBwbHkEC2JyUHJvdGVjdGVkCQELdmFsdWVPckVsc2UCCQCaCAIFBHRoaXMJAQ5iclByb3RlY3RlZEtFWQAFC0JSUFJPVEVDVEVEBBltYXhBbGxvd2VkVXNkbkJlZm9yZU1pbkJyAwkAZwIFC2JyUHJvdGVjdGVkBQJicgAACQBrAwkAZQIFDnJlc2VydmVzSW5Vc2RuCQBrAwULYnJQcm90ZWN0ZWQFDm5ldXRyaW5vU3VwcGx5BQVNVUxUNgUFTVVMVDYJAGUCBQVNVUxUNgULYnJQcm90ZWN0ZWQEFmFsbG93ZWRVc2RuQmVmb3JlTWluQnIDCQBmAgUGdXNkbkluBRltYXhBbGxvd2VkVXNkbkJlZm9yZU1pbkJyBRltYXhBbGxvd2VkVXNkbkJlZm9yZU1pbkJyBQZ1c2RuSW4EFWFsbG93ZWRVc2RuQWZ0ZXJNaW5CcgMJAGYCBQZ1c2RuSW4FGW1heEFsbG93ZWRVc2RuQmVmb3JlTWluQnIJAGsDCQBlAgUGdXNkbkluBRltYXhBbGxvd2VkVXNkbkJlZm9yZU1pbkJyBQJicgUFTVVMVDYAAAQLYWxsb3dlZFVzZG4JAGQCBRZhbGxvd2VkVXNkbkJlZm9yZU1pbkJyBRVhbGxvd2VkVXNkbkFmdGVyTWluQnIECXVzZG4yU1VSRgkAZQIFBnVzZG5JbgULYWxsb3dlZFVzZG4EC291dEFtdEdyb3NzCQEXY29udmVydE5ldXRyaW5vVG9CYXNrZXQDBQthbGxvd2VkVXNkbgUKYmFza2V0SW5mbwUObmV1dHJpbm9TdXBwbHkJAJ4KDAgFC291dEFtdEdyb3NzAl8yBQR1bml0BQl1c2RuMlNVUkYFD25ldXRyaW5vQXNzZXRJZAgFC291dEFtdEdyb3NzAl8yCAULb3V0QW10R3Jvc3MCXzMFC2FsbG93ZWRVc2RuBRltYXhBbGxvd2VkVXNkbkJlZm9yZU1pbkJyBRZhbGxvd2VkVXNkbkJlZm9yZU1pbkJyBRVhbGxvd2VkVXNkbkFmdGVyTWluQnIIBQtvdXRBbXRHcm9zcwJfMQUDbmlsAQxjYWxjV2l0aGRyYXcFCHN3YXBUeXBlCGluQW1vdW50BXByaWNlD25ldXRyaW5vTWV0cmljcwdpbkFzc2V0BAticlByb3RlY3RlZAkBC3ZhbHVlT3JFbHNlAgkAmggCBQR0aGlzCQEOYnJQcm90ZWN0ZWRLRVkABQtCUlBST1RFQ1RFRAQCQlIJAQVhc0ludAEJAJEDAgUPbmV1dHJpbm9NZXRyaWNzBQxuTWV0cmljSWR4QlIEDXJlc2VydmVzSW5Vc2QJAQVhc0ludAEJAJEDAgUPbmV1dHJpbm9NZXRyaWNzBRduTWV0cmljVG90YWxSZXNlcnZlc1VzZAQObmV1dHJpbm9TdXBwbHkJAQVhc0ludAEJAJEDAgUPbmV1dHJpbm9NZXRyaWNzBRRuTWV0cmljSWR4VXNkblN1cHBseQQKYmFza2V0SW5mbwkBCGFzU3RyaW5nAQkAkQMCBQ9uZXV0cmlub01ldHJpY3MFEW5NZXRyaWNCYXNrZXRJbmZvBA13ZWlnaHRlZFByaWNlCQEFYXNJbnQBCQCRAwIFD25ldXRyaW5vTWV0cmljcwUUbk1ldHJpY1dlaWdodGVkUHJpY2UEDG91dERhdGFUdXBsZQMJAAACBQhzd2FwVHlwZQILb3V0TmV1dHJpbm8JAQ9jYWxjV2l0aGRyYXdUMlUEBQhpbkFtb3VudAUHaW5Bc3NldAUKYmFza2V0SW5mbwUNd2VpZ2h0ZWRQcmljZQMJAAACBQhzd2FwVHlwZQIIb3V0TXVsdGkJARRjYWxjV2l0aGRyYXdVMkJhc2tldAUFCGluQW1vdW50BQpiYXNrZXRJbmZvBQJCUgUNcmVzZXJ2ZXNJblVzZAUObmV1dHJpbm9TdXBwbHkJAAIBCQCsAgICFlVuc3VwcG9ydGVkIHN3YXAgdHlwZSAFCHN3YXBUeXBlBAtvdXRBbXRHcm9zcwgFDG91dERhdGFUdXBsZQJfMQQKb3V0QXNzZXRJZAgFDG91dERhdGFUdXBsZQJfMgQPaW5BbXRUb1N1cmZQYXJ0CAUMb3V0RGF0YVR1cGxlAl8zBAlpbkFzc2V0SWQIBQxvdXREYXRhVHVwbGUCXzQECnVubGVhc2VBbXQIBQxvdXREYXRhVHVwbGUCXzUEDXdpdGhkcmF3UHJpY2UIBQxvdXREYXRhVHVwbGUCXzYECmJhc2tldERhdGEIBQxvdXREYXRhVHVwbGUDXzExBApuZXRGZWVEYXRhCAUMb3V0RGF0YVR1cGxlA18xMgQJb3V0TmV0QW10AwkAAAIFCHN3YXBUeXBlAgtvdXROZXV0cmlubwkAkQMCBQpuZXRGZWVEYXRhAAAAAAQJb3V0RmVlQW10AwkAAAIFCHN3YXBUeXBlAgtvdXROZXV0cmlubwkAkQMCBQpuZXRGZWVEYXRhAAEAAAQKb3V0U3VyZkFtdAMJAGcCAAAFD2luQW10VG9TdXJmUGFydAAACQEFYXNJbnQBCQCRAwIJAQlhc0FueUxpc3QBCQD8BwQFDG1hdGhDb250cmFjdAIUc3VyZkZ1bmN0aW9uUkVBRE9OTFkJAMwIAgUPaW5BbXRUb1N1cmZQYXJ0CQDMCAIFCWluQXNzZXRJZAUDbmlsBQNuaWwFDGJGdW5jSWR4U3VyZgQOd2l0aGRyYXdCYXNrZXQDCQAAAgUIc3dhcFR5cGUCCG91dE11bHRpCQEPYXBwbHlGZWVzQmFza2V0AQUKYmFza2V0RGF0YQIACQCbCgkFCW91dE5ldEFtdAUKb3V0QXNzZXRJZAUKb3V0U3VyZkFtdAUPaW5BbXRUb1N1cmZQYXJ0BQp1bmxlYXNlQW10BQlvdXRGZWVBbXQFC291dEFtdEdyb3NzBQ13aXRoZHJhd1ByaWNlBQ53aXRoZHJhd0Jhc2tldAESa2V5QXBwbHlJblByb2dyZXNzAAITJXNfX2FwcGx5SW5Qcm9ncmVzcwETa2V5UHJvcG9zYWxEYXRhQnlJZAEKcHJvcG9zYWxJZAkArAICAhQlcyVkX19wcm9wb3NhbERhdGFfXwkApAMBBQpwcm9wb3NhbElkAAtnb3ZJZHhUeElkcwAJAQ52YWxpZGF0ZVVwZGF0ZQECdHgEByRtYXRjaDAFAnR4AwkAAQIFByRtYXRjaDACBU9yZGVyBAFvBQckbWF0Y2gwCQACAQIVT3JkZXJzIGFyZW4ndCBhbGxvd2VkAwMJAAECBQckbWF0Y2gwAg9EYXRhVHJhbnNhY3Rpb24GAwkAAQIFByRtYXRjaDACFVNwb25zb3JGZWVUcmFuc2FjdGlvbgYDCQABAgUHJG1hdGNoMAIUU2V0U2NyaXB0VHJhbnNhY3Rpb24GAwkAAQIFByRtYXRjaDACFkNyZWF0ZUFsaWFzVHJhbnNhY3Rpb24GAwkAAQIFByRtYXRjaDACFkxlYXNlQ2FuY2VsVHJhbnNhY3Rpb24GAwkAAQIFByRtYXRjaDACEExlYXNlVHJhbnNhY3Rpb24GAwkAAQIFByRtYXRjaDACEElzc3VlVHJhbnNhY3Rpb24GAwkAAQIFByRtYXRjaDACG0ludm9rZUV4cHJlc3Npb25UcmFuc2FjdGlvbgYDCQABAgUHJG1hdGNoMAIaVXBkYXRlQXNzZXRJbmZvVHJhbnNhY3Rpb24GAwkAAQIFByRtYXRjaDACF0ludm9rZVNjcmlwdFRyYW5zYWN0aW9uBgMJAAECBQckbWF0Y2gwAhlTZXRBc3NldFNjcmlwdFRyYW5zYWN0aW9uBgMJAAECBQckbWF0Y2gwAhNUcmFuc2ZlclRyYW5zYWN0aW9uBgMJAAECBQckbWF0Y2gwAhNFeGNoYW5nZVRyYW5zYWN0aW9uBgMJAAECBQckbWF0Y2gwAhdNYXNzVHJhbnNmZXJUcmFuc2FjdGlvbgYDCQABAgUHJG1hdGNoMAIPQnVyblRyYW5zYWN0aW9uBgkAAQIFByRtYXRjaDACElJlaXNzdWVUcmFuc2FjdGlvbgQBdAUHJG1hdGNoMAQEdHhJZAkA2AQBCAUBdAJpZAQKcHJvcG9zYWxJZAkBE3ZhbHVlT3JFcnJvck1lc3NhZ2UCCQCaCAIFC2dvdkNvbnRyYWN0CQESa2V5QXBwbHlJblByb2dyZXNzAAIWQXBwbHkgaXMgbm90IGhhcHBlbmluZwQGdHhMaXN0CQC1CQIJAJEDAgkAtQkCCQEPZ2V0U3RyaW5nT3JGYWlsAgULZ292Q29udHJhY3QJARNrZXlQcm9wb3NhbERhdGFCeUlkAQUKcHJvcG9zYWxJZAUDU0VQBQtnb3ZJZHhUeElkcwUHTElTVFNFUAMJAQEhAQkBCWlzRGVmaW5lZAEJAM8IAgUGdHhMaXN0BQR0eElkCQACAQkArAICCQCsAgIJAKwCAgIOVW5rbm93biB0eElkOiAFBHR4SWQCECBmb3IgcHJvcG9zYWxJZD0JAKQDAQUKcHJvcG9zYWxJZAYJAAIBAgtNYXRjaCBlcnJvchABaQENY29uc3RydWN0b3JWMQwSbmV1dHJpbm9Bc3NldElkUHJtDmJvbmRBc3NldElkUHJtEmF1Y3Rpb25Db250cmFjdFBybRZsaXF1aWRhdGlvbkNvbnRyYWN0UHJtDnJwZENvbnRyYWN0UHJtG25vZGVPcmFjbGVQcm92aWRlclB1YktleVBybRtiYWxhbmNlV2F2ZXNMb2NrSW50ZXJ2YWxQcm0eYmFsYW5jZU5ldXRyaW5vTG9ja0ludGVydmFsUHJtFW1pblRva2VuU3dhcEFtb3VudFBybRhtaW5OZXV0cmlub1N3YXBBbW91bnRQcm0VbmV1dHJpbm9PdXRGZWVQYXJ0UHJtEndhdmVzT3V0RmVlUGFydFBybQQLY2hlY2tDYWxsZXIJAQh0aGlzT25seQEFAWkDCQAAAgULY2hlY2tDYWxsZXIFC2NoZWNrQ2FsbGVyAwkBAiE9AgkAkAMBCAUBaQhwYXltZW50cwAACQACAQITbm8gcGF5bWVudHMgYWxsb3dlZAkAzAgCCQELU3RyaW5nRW50cnkCBRJOZXV0cmlub0Fzc2V0SWRLZXkFEm5ldXRyaW5vQXNzZXRJZFBybQkAzAgCCQELU3RyaW5nRW50cnkCBQ5Cb25kQXNzZXRJZEtleQUOYm9uZEFzc2V0SWRQcm0JAMwIAgkBC1N0cmluZ0VudHJ5AgUSQXVjdGlvbkNvbnRyYWN0S2V5BRJhdWN0aW9uQ29udHJhY3RQcm0JAMwIAgkBC1N0cmluZ0VudHJ5AgUWTGlxdWlkYXRpb25Db250cmFjdEtleQUWbGlxdWlkYXRpb25Db250cmFjdFBybQkAzAgCCQELU3RyaW5nRW50cnkCBQ5SUERDb250cmFjdEtleQUOcnBkQ29udHJhY3RQcm0JAMwIAgkBC1N0cmluZ0VudHJ5AgUbTm9kZU9yYWNsZVByb3ZpZGVyUHViS2V5S2V5BRtub2RlT3JhY2xlUHJvdmlkZXJQdWJLZXlQcm0JAMwIAgkBDEludGVnZXJFbnRyeQIFG0JhbGFuY2VXYXZlc0xvY2tJbnRlcnZhbEtleQUbYmFsYW5jZVdhdmVzTG9ja0ludGVydmFsUHJtCQDMCAIJAQxJbnRlZ2VyRW50cnkCBR5CYWxhbmNlTmV1dHJpbm9Mb2NrSW50ZXJ2YWxLZXkFHmJhbGFuY2VOZXV0cmlub0xvY2tJbnRlcnZhbFBybQkAzAgCCQEMSW50ZWdlckVudHJ5AgUVTWluVG9rZW5Td2FwQW1vdW50S2V5BRVtaW5Ub2tlblN3YXBBbW91bnRQcm0JAMwIAgkBDEludGVnZXJFbnRyeQIFGE1pbk5ldXRyaW5vU3dhcEFtb3VudEtleQUYbWluTmV1dHJpbm9Td2FwQW1vdW50UHJtCQDMCAIJAQxJbnRlZ2VyRW50cnkCBRVOZXV0cmlub091dEZlZVBhcnRLZXkFFW5ldXRyaW5vT3V0RmVlUGFydFBybQkAzAgCCQEMSW50ZWdlckVudHJ5AgUSV2F2ZXNPdXRGZWVQYXJ0S2V5BRJ3YXZlc091dEZlZVBhcnRQcm0FA25pbAkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgFpAQ1jb25zdHJ1Y3RvclYyAwxtYXRoQ29udHJhY3QTbnNidFN0YWtpbmdDb250cmFjdBRzd2Fwc1RpbWVmcmFtZUJsb2NrcwQLY2hlY2tDYWxsZXIJAQh0aGlzT25seQEFAWkDCQAAAgULY2hlY2tDYWxsZXIFC2NoZWNrQ2FsbGVyAwkBAiE9AgkAkAMBCAUBaQhwYXltZW50cwAACQACAQITbm8gcGF5bWVudHMgYWxsb3dlZAkAzAgCCQELU3RyaW5nRW50cnkCBQ9NYXRoQ29udHJhY3RLZXkFDG1hdGhDb250cmFjdAkAzAgCCQELU3RyaW5nRW50cnkCBRZOc2J0U3Rha2luZ0NvbnRyYWN0S2V5BRNuc2J0U3Rha2luZ0NvbnRyYWN0CQDMCAIJAQxJbnRlZ2VyRW50cnkCCQERc3dhcHNUaW1lZnJhbWVLRVkABRRzd2Fwc1RpbWVmcmFtZUJsb2NrcwUDbmlsCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuAWkBDWNvbnN0cnVjdG9yVjMFDGFzc2V0V2VpZ2h0cwZkUGFyYW0Ga1BhcmFtBmJQYXJhbQpkZWx0YVBhcmFtBAtjaGVja0NhbGxlcgkBCHRoaXNPbmx5AQUBaQMJAAACBQtjaGVja0NhbGxlcgULY2hlY2tDYWxsZXIDCQECIT0CCQCQAwEIBQFpCHBheW1lbnRzAAAJAAIBAhNubyBwYXltZW50cyBhbGxvd2VkCgEHcHJlcGFyZQIDYWNjBGl0ZW0EBXBhcnRzCQC1CQIFBGl0ZW0CAV8EB2Fzc2V0SWQJAJEDAgUFcGFydHMAAAQGd2VpZ2h0CQENcGFyc2VJbnRWYWx1ZQEJAJEDAgUFcGFydHMAAQkAlAoCCQDNCAIIBQNhY2MCXzEFB2Fzc2V0SWQJAM0IAggFA2FjYwJfMgkBDEludGVnZXJFbnRyeQIJARZiYXNrZXRXZWlnaHRCeUFzc2V0S2V5AQUHYXNzZXRJZAUGd2VpZ2h0BAFyCgACJGwFDGFzc2V0V2VpZ2h0cwoAAiRzCQCQAwEFAiRsCgAFJGFjYzAJAJQKAgUDbmlsBQNuaWwKAQUkZjBfMQICJGECJGkDCQBnAgUCJGkFAiRzBQIkYQkBB3ByZXBhcmUCBQIkYQkAkQMCBQIkbAUCJGkKAQUkZjBfMgICJGECJGkDCQBnAgUCJGkFAiRzBQIkYQkAAgECFExpc3Qgc2l6ZSBleGNlZWRzIDEwCQEFJGYwXzICCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECBQUkYWNjMAAAAAEAAgADAAQABQAGAAcACAAJAAoJAM0IAgkAzQgCCQDNCAIJAM0IAgkAzQgCCAUBcgJfMgkBDEludGVnZXJFbnRyeQIJAQpiYXNrZXRES2V5AAUGZFBhcmFtCQEMSW50ZWdlckVudHJ5AgkBCmJhc2tldEtLZXkABQZrUGFyYW0JAQxJbnRlZ2VyRW50cnkCCQEKYmFza2V0QktleQAFBmJQYXJhbQkBDEludGVnZXJFbnRyeQIJARJiYXNrZXRMb2diRGVsdGFLZXkACQBtBgUGYlBhcmFtAAYFCmRlbHRhUGFyYW0ABgAGBQZIQUxGVVAJAQtTdHJpbmdFbnRyeQIJAQ9iYXNrZXRBc3NldHNLZXkACQC5CQIIBQFyAl8xBQNTRVAJAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4BaQETc3dhcFRva2VuVG9OZXV0cmlubwADCQECIT0CCQCQAwEIBQFpCHBheW1lbnRzAAEJAAIBAixzd2FwVG9rZW5Ub05ldXRyaW5vIHJlcXVpcmUgb25seSBvbmUgcGF5bWVudAQDcG10CQEFdmFsdWUBCQCRAwIIBQFpCHBheW1lbnRzAAAEDGJhc2tldEFzc2V0cwkBC3ZhbHVlT3JFbHNlAgkAoggBCQEPYmFza2V0QXNzZXRzS2V5AAIFV0FWRVMEDXBtdEFzc2V0SWRTdHIJANgEAQkBC3ZhbHVlT3JFbHNlAggFA3BtdAdhc3NldElkBQdXQVZFU0lEAwkBASEBCQEIY29udGFpbnMCBQxiYXNrZXRBc3NldHMFDXBtdEFzc2V0SWRTdHIJAAIBCQCsAgIJAKwCAgUNcG10QXNzZXRJZFN0cgIdIGlzIG5vdCBvbmUgb2YgYmFza2V0IHRva2VuczoFDGJhc2tldEFzc2V0cwQLdXNlckFkZHJlc3MJAKUIAQgFAWkGY2FsbGVyBAZ0eElkNTgJANgEAQgFAWkNdHJhbnNhY3Rpb25JZAkBCmNvbW1vblN3YXAFAgtvdXROZXV0cmlubwgFA3BtdAZhbW91bnQFDXBtdEFzc2V0SWRTdHIFC3VzZXJBZGRyZXNzBQZ0eElkNTgBaQEUc3dhcE5ldXRyaW5vVG9CYXNrZXQAAwkBAiE9AgkAkAMBCAUBaQhwYXltZW50cwABCQACAQItc3dhcE5ldXRyaW5vVG9CYXNrZXQgcmVxdWlyZSBvbmx5IG9uZSBwYXltZW50BANwbXQJAQV2YWx1ZQEJAJEDAggFAWkIcGF5bWVudHMAAAMJAQIhPQIIBQNwbXQHYXNzZXRJZAUPbmV1dHJpbm9Bc3NldElkCQACAQI6T25seSBhcHByb3ByaWF0ZSBOZXV0cmlubyB0b2tlbnMgYXJlIGFsbG93ZWQgZm9yIHN3YXBwaW5nLgQLdXNlckFkZHJlc3MJAKUIAQgFAWkGY2FsbGVyBAZ0eElkNTgJANgEAQgFAWkNdHJhbnNhY3Rpb25JZAkBCmNvbW1vblN3YXAFAghvdXRNdWx0aQgFA3BtdAZhbW91bnQJANgEAQUPbmV1dHJpbm9Bc3NldElkBQt1c2VyQWRkcmVzcwUGdHhJZDU4AWkBCHdpdGhkcmF3AwdhY2NvdW50BWluZGV4CHN3YXBUeElkBAR0eElkCQDYBAEIBQFpDXRyYW5zYWN0aW9uSWQDCQECIT0CCQCQAwEIBQFpCHBheW1lbnRzAAAJAAIBAhNubyBwYXltZW50cyBhbGxvd2VkBA9uZXV0cmlub01ldHJpY3MJAQlhc0FueUxpc3QBCQD8BwQFDG1hdGhDb250cmFjdAIaY2FsY05ldXRpbm9NZXRyaWNzUkVBRE9OTFkFA25pbAUDbmlsBAJCUgkBBWFzSW50AQkAkQMCBQ9uZXV0cmlub01ldHJpY3MFDG5NZXRyaWNJZHhCUgQLdXNlckFkZHJlc3MJARFAZXh0ck5hdGl2ZSgxMDYyKQEFB2FjY291bnQECWRhdGFBcnJheQkBEnN3YXBEYXRhRmFpbE9yUkVBRAIFB2FjY291bnQFCHN3YXBUeElkBBBzZWxmVW5sb2NrSGVpZ2h0CQENcGFyc2VJbnRWYWx1ZQEJAJEDAgUJZGF0YUFycmF5BRRzSWR4U2VsZlVubG9ja0hlaWdodAQIc3dhcFR5cGUJAJEDAgUJZGF0YUFycmF5BQxzSWR4U3dhcFR5cGUECGluQW1vdW50CQENcGFyc2VJbnRWYWx1ZQEJAJEDAgUJZGF0YUFycmF5BQxzSWR4SW5BbW91bnQECnN3YXBTdGF0dXMJAJEDAgUJZGF0YUFycmF5BQpzSWR4U3RhdHVzBAtzdGFydEhlaWdodAkBDXBhcnNlSW50VmFsdWUBCQCRAwIFCWRhdGFBcnJheQUPc0lkeFN0YXJ0SGVpZ2h0BAdpbkFzc2V0CQCRAwIFCWRhdGFBcnJheQULc0lkeEluQXNzZXQEC3RvdGFsTG9ja2VkCQEPdG90YWxMb2NrZWRSRUFEAgUIc3dhcFR5cGUFB2luQXNzZXQEEXRvdGFsTG9ja2VkQnlVc2VyCQEVdG90YWxMb2NrZWRCeVVzZXJSRUFEAwUIc3dhcFR5cGUFB2FjY291bnQFB2luQXNzZXQEDHVubG9ja0hlaWdodAUQc2VsZlVubG9ja0hlaWdodAQLaW5kZXhIZWlnaHQJARVnZXRIZWlnaHRQcmljZUJ5SW5kZXgBBQVpbmRleAQPcHJldkluZGV4SGVpZ2h0CQEVZ2V0SGVpZ2h0UHJpY2VCeUluZGV4AQkAZQIFBWluZGV4AAEEDHByaWNlQnlJbmRleAkBD2dldFByaWNlSGlzdG9yeQEFC2luZGV4SGVpZ2h0AwUJaXNCbG9ja2VkCQEVZW1lcmdlbmN5U2h1dGRvd25GQUlMAAMJAQIhPQIFCnN3YXBTdGF0dXMCB1BFTkRJTkcJAAIBAh9zd2FwIGhhcyBiZWVuIGFscmVhZHkgcHJvY2Vzc2VkAwkAZgIFDHVubG9ja0hlaWdodAUGaGVpZ2h0CQACAQkArAICCQCsAgICEXBsZWFzZSB3YWl0IGZvcjogCQCkAwEFDHVubG9ja0hlaWdodAIfIGJsb2NrIGhlaWdodCB0byB3aXRoZHJhdyBmdW5kcwMDAwkAZgIFBWluZGV4BQpwcmljZUluZGV4BgkAZgIFDHVubG9ja0hlaWdodAULaW5kZXhIZWlnaHQGAwkBAiE9AgUPcHJldkluZGV4SGVpZ2h0AAAJAGcCBQ9wcmV2SW5kZXhIZWlnaHQFDHVubG9ja0hlaWdodAcJAQ5wcmljZUluZGV4RkFJTAUFBWluZGV4BQpwcmljZUluZGV4BQtpbmRleEhlaWdodAUMdW5sb2NrSGVpZ2h0BQ9wcmV2SW5kZXhIZWlnaHQEDXdpdGhkcmF3VHVwbGUJAQxjYWxjV2l0aGRyYXcFBQhzd2FwVHlwZQUIaW5BbW91bnQFDHByaWNlQnlJbmRleAUPbmV1dHJpbm9NZXRyaWNzBQdpbkFzc2V0BAxvdXROZXRBbW91bnQIBQ13aXRoZHJhd1R1cGxlAl8xBApvdXRBc3NldElkCAUNd2l0aGRyYXdUdXBsZQJfMgQKb3V0U3VyZkFtdAgFDXdpdGhkcmF3VHVwbGUCXzMECnVubGVhc2VBbXQIBQ13aXRoZHJhd1R1cGxlAl81BAxvdXRGZWVBbW91bnQIBQ13aXRoZHJhd1R1cGxlAl82BAtvdXRBbXRHcm9zcwgFDXdpdGhkcmF3VHVwbGUCXzcEDXdpdGhkcmF3UHJpY2UIBQ13aXRoZHJhd1R1cGxlAl84BA53aXRoZHJhd0Jhc2tldAgFDXdpdGhkcmF3VHVwbGUCXzkDCQBnAgAABQtvdXRBbXRHcm9zcwkAAgECE2JhbGFuY2UgZXF1YWxzIHplcm8EDXN1cmZDb25kaXRpb24DCQBmAgUKb3V0U3VyZkFtdAAABAtpc3N1ZVJlc3VsdAkA/AcEBQ9hdWN0aW9uQ29udHJhY3QCCWlzc3VlU3VyZgkAzAgCBQpvdXRTdXJmQW10CQDMCAIFB2FjY291bnQFA25pbAUDbmlsAwkAAAIFC2lzc3VlUmVzdWx0BQtpc3N1ZVJlc3VsdAAACQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuAAADCQAAAgUNc3VyZkNvbmRpdGlvbgUNc3VyZkNvbmRpdGlvbgQRdW5sZWFzZUludk9yRW1wdHkJAPwHBAUEdGhpcwIXaW50ZXJuYWxVbmxlYXNlQW5kTGVhc2UJAMwIAgUKdW5sZWFzZUFtdAUDbmlsBQNuaWwDCQAAAgURdW5sZWFzZUludk9yRW1wdHkFEXVubGVhc2VJbnZPckVtcHR5BAlnbnNidERhdGEJAQlhc0FueUxpc3QBCQD8BwQFF2duc2J0Q29udHJvbGxlckNvbnRyYWN0AhRnbnNidEluZm9TWVNSRUFET05MWQkAzAgCAgAJAMwIAgAACQDMCAIAAAUDbmlsBQNuaWwEDWduc2J0QW10VG90YWwJAQVhc0ludAEJAJEDAgUJZ25zYnREYXRhAAEEFWduc2J0QW10RnJvbVN1cmZUb3RhbAkBBWFzSW50AQkAkQMCCQEJYXNBbnlMaXN0AQkAkQMCBQlnbnNidERhdGEAAwADBA1jb21tb25BY3Rpb25zCQDMCAIJAQxJbnRlZ2VyRW50cnkCCQEUdG90YWxMb2NrZWRCeVVzZXJLRVkDBQhzd2FwVHlwZQUHYWNjb3VudAUHaW5Bc3NldAkAZQIFEXRvdGFsTG9ja2VkQnlVc2VyBQhpbkFtb3VudAkAzAgCCQEMSW50ZWdlckVudHJ5AgkBDnRvdGFsTG9ja2VkS0VZAgUIc3dhcFR5cGUFB2luQXNzZXQJAGUCBQt0b3RhbExvY2tlZAUIaW5BbW91bnQJAMwIAgkBC1N0cmluZ0VudHJ5AgkBB3N3YXBLRVkCBQdhY2NvdW50BQhzd2FwVHhJZAkBDmZpbmlzaFN3YXBEQVRBCwUJZGF0YUFycmF5BQ13aXRoZHJhd1ByaWNlBQxvdXROZXRBbW91bnQFDG91dEZlZUFtb3VudAUMdW5sb2NrSGVpZ2h0BQVpbmRleAUEdHhJZAUKb3V0U3VyZkFtdAUCQlIFDndpdGhkcmF3QmFza2V0BQdpbkFzc2V0BQNuaWwDCQAAAgUIc3dhcFR5cGUCCG91dE11bHRpBAZiYXNrZXQJALUJAgUOd2l0aGRyYXdCYXNrZXQCAV8KAQZsb29wZXICA2FjYwRpdGVtBAJpdAkAtQkCBQRpdGVtBQdMSVNUU0VQBAt0ZW1wQXNzZXRJZAkA2QQBCQCRAwIFAml0AAAEB2Fzc2V0SWQDCQAAAgULdGVtcEFzc2V0SWQFB1dBVkVTSUQFBHVuaXQFC3RlbXBBc3NldElkBAluZXRBbW91bnQJAQ1wYXJzZUludFZhbHVlAQkAkQMCBQJpdAACBAlmZWVBbW91bnQJAQ1wYXJzZUludFZhbHVlAQkAkQMCBQJpdAADBAhzdXJmQW10MQMJAQIhPQIFDWduc2J0QW10VG90YWwAAAkAawMFCWZlZUFtb3VudAUVZ25zYnRBbXRGcm9tU3VyZlRvdGFsBQ1nbnNidEFtdFRvdGFsAAAECHN1cmZBbXQyAwkBAiE9AgUNZ25zYnRBbXRUb3RhbAAACQBrAwUJZmVlQW1vdW50CQBlAgUFTVVMVDYFAkJSBQVNVUxUNgAABAdzdXJmQW10CQCWAwEJAMwIAgUIc3VyZkFtdDEJAMwIAgUIc3VyZkFtdDIFA25pbAQHbnNidEFtdAkAZQIFCWZlZUFtb3VudAUHc3VyZkFtdAkAlwoFCQDNCAIIBQNhY2MCXzEJAQ5TY3JpcHRUcmFuc2ZlcgMFC3VzZXJBZGRyZXNzBQluZXRBbW91bnQFB2Fzc2V0SWQJAM0IAggFA2FjYwJfMgkBD0F0dGFjaGVkUGF5bWVudAIFB2Fzc2V0SWQFB3N1cmZBbXQJAM0IAggFA2FjYwJfMwkBD0F0dGFjaGVkUGF5bWVudAIFB2Fzc2V0SWQFB25zYnRBbXQJAGQCCAUDYWNjAl80BQdzdXJmQW10CQBkAggFA2FjYwJfNQUHbnNidEFtdAQBcgoAAiRsBQZiYXNrZXQKAAIkcwkAkAMBBQIkbAoABSRhY2MwCQCXCgUFA25pbAUDbmlsBQNuaWwAAAAACgEFJGYwXzECAiRhAiRpAwkAZwIFAiRpBQIkcwUCJGEJAQZsb29wZXICBQIkYQkAkQMCBQIkbAUCJGkKAQUkZjBfMgICJGECJGkDCQBnAgUCJGkFAiRzBQIkYQkAAgECFExpc3Qgc2l6ZSBleGNlZWRzIDEwCQEFJGYwXzICCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECBQUkYWNjMAAAAAEAAgADAAQABQAGAAcACAAJAAoEC3N1cmZEZXBvc2l0AwkAZgIIBQFyAl80AAAEB3N1cmZJbnYJAPwHBAUTc3VyZlN0YWtpbmdDb250cmFjdAIHZGVwb3NpdAUDbmlsCAUBcgJfMgMJAAACBQdzdXJmSW52BQdzdXJmSW52AAAJAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4AAAMJAAACBQtzdXJmRGVwb3NpdAULc3VyZkRlcG9zaXQEC25zYnREZXBvc2l0AwkAZgIIBQFyAl81AAAEB25zYnRJbnYJAPwHBAUTbnNidFN0YWtpbmdDb250cmFjdAIHZGVwb3NpdAUDbmlsCAUBcgJfMwMJAAACBQduc2J0SW52BQduc2J0SW52AAAJAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4AAAMJAAACBQtuc2J0RGVwb3NpdAULbnNidERlcG9zaXQJAM4IAgUNY29tbW9uQWN0aW9ucwgFAXICXzEJAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4JAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4EA2ZlZQkBD0F0dGFjaGVkUGF5bWVudAIFCm91dEFzc2V0SWQFDG91dEZlZUFtb3VudAQLc3VyZkZlZUFtdDEDCQECIT0CBQ1nbnNidEFtdFRvdGFsAAAJAGsDCAUDZmVlBmFtb3VudAUVZ25zYnRBbXRGcm9tU3VyZlRvdGFsBQ1nbnNidEFtdFRvdGFsAAAEC3N1cmZGZWVBbXQyAwkBAiE9AgUNZ25zYnRBbXRUb3RhbAAACQBrAwgFA2ZlZQZhbW91bnQJAGUCBQVNVUxUNgUCQlIFBU1VTFQ2AAAECnN1cmZGZWVBbXQJAJYDAQkAzAgCBQtzdXJmRmVlQW10MQkAzAgCBQtzdXJmRmVlQW10MgUDbmlsBApuc2J0RmVlQW10CQBlAggFA2ZlZQZhbW91bnQFCnN1cmZGZWVBbXQEC3N1cmZEZXBvc2l0AwkAZgIFCnN1cmZGZWVBbXQAAAQHc3VyZkludgkA/AcEBRNzdXJmU3Rha2luZ0NvbnRyYWN0AgdkZXBvc2l0BQNuaWwJAMwIAgkBD0F0dGFjaGVkUGF5bWVudAIIBQNmZWUHYXNzZXRJZAUKc3VyZkZlZUFtdAUDbmlsAwkAAAIFB3N1cmZJbnYFB3N1cmZJbnYFA25pbAkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgUDbmlsAwkAAAIFC3N1cmZEZXBvc2l0BQtzdXJmRGVwb3NpdAQLbnNidERlcG9zaXQDCQBmAgUKbnNidEZlZUFtdAAABAduc2J0SW52CQD8BwQFE25zYnRTdGFraW5nQ29udHJhY3QCB2RlcG9zaXQFA25pbAkAzAgCCQEPQXR0YWNoZWRQYXltZW50AggFA2ZlZQdhc3NldElkBQpuc2J0RmVlQW10BQNuaWwDCQAAAgUHbnNidEludgUHbnNidEludgUDbmlsCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuBQNuaWwDCQAAAgULbnNidERlcG9zaXQFC25zYnREZXBvc2l0CQDNCAIFDWNvbW1vbkFjdGlvbnMJAQ5TY3JpcHRUcmFuc2ZlcgMFC3VzZXJBZGRyZXNzBQxvdXROZXRBbW91bnQFCm91dEFzc2V0SWQJAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4JAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4JAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4JAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4BaQEXaW50ZXJuYWxVbmxlYXNlQW5kTGVhc2UBDXVubGVhc2VBbW91bnQDCQECIT0CCAUBaQZjYWxsZXIFBHRoaXMJAAIBAixpbnRlcm5hbFVubGVhc2VBbmRMZWFzZSBpcyBub3QgcHVibGljIG1ldGhvZAkBFnByZXBhcmVVbmxlYXNlQW5kTGVhc2UBBQ11bmxlYXNlQW1vdW50AWkBEnRyYW5zZmVyVXNkblRvVXNlcgIGYW1vdW50BGFkZHIDCQECIT0CCAUBaQZjYWxsZXIFD2F1Y3Rpb25Db250cmFjdAkAAgECI09ubHkgYXVjdGlvbiBjb250cmFjdCBpcyBhdXRob3JpemVkCQDMCAIJAQ5TY3JpcHRUcmFuc2ZlcgMJARFAZXh0ck5hdGl2ZSgxMDYyKQEFBGFkZHIFBmFtb3VudAUPbmV1dHJpbm9Bc3NldElkBQNuaWwBaQELYWNjZXB0V2F2ZXMAAwMJAQIhPQIIBQFpBmNhbGxlcgUPYXVjdGlvbkNvbnRyYWN0CQECIT0CCAUBaQZjYWxsZXIFC2dvdkNvbnRyYWN0BwkAAgECQ0N1cnJlbnRseSBvbmx5IGF1Y3Rpb24gYW5kIGdvdmVybmFuY2UgY29udHJhY3RzIGFyZSBhbGxvd2VkIHRvIGNhbGwJAJQKAgkBFnByZXBhcmVVbmxlYXNlQW5kTGVhc2UBAAACB3N1Y2Nlc3MBaQEPYXBwcm92ZUxlYXNpbmdzAwZuTGlzdFMIZ3JvdXBOdW0EbEFtdAQFbklkeHMJAMwIAgAACQDMCAIAAQkAzAgCAAIJAMwIAgADCQDMCAIABAkAzAgCAAUJAMwIAgAGCQDMCAIABwUDbmlsBAdtbmdQdWJTCQELdmFsdWVPckVsc2UCCQCiCAECHCVzJXNfX2NmZ19fbGVhc2luZ01hbmFnZXJQdWICLDdBVU1YNTR1a1lNWXZQbW1hN3lvRmY1TmpaaHM0QnU1bnozRXo5RVY4c3VyBAZtbmdQdWIJANkEAQUHbW5nUHViUwQObm9kZVJlZ0FkZHJTdHIJAQt2YWx1ZU9yRWxzZQIJAKIIAQIfJXMlc19fY2ZnX19ub2Rlc1JlZ2lzdHJ5QWRkcmVzcwIjM1A5dktxUUtqVWRtcFhBZmlXYXU4a3JSRVlBWTFYcjY5cEUEC25vZGVSZWdBZGRyCQERQGV4dHJOYXRpdmUoMTA2MikBBQ5ub2RlUmVnQWRkclN0cgQRbEdyb3VwTm9kZUxpc3RLRVkJARhnZXRMZWFzZUdyb3VwTm9kZUxpc3RLZXkBBQhncm91cE51bQQKbEdyTm9kZU9wdAkAnQgCBQR0aGlzBRFsR3JvdXBOb2RlTGlzdEtFWQMJAQlpc0RlZmluZWQBBQpsR3JOb2RlT3B0CQACAQkArAICCQCsAgICBmdyb3VwIAkApAMBBQhncm91cE51bQIUIGFscmVhZHkgaW5pdGlhbGl6ZWQEBW5MaXN0CQC1CQIFBm5MaXN0UwUDU0VQBAhleHBDb3VudAkAkAMBBQVuSWR4cwMJAQIhPQIIBQFpD2NhbGxlclB1YmxpY0tleQUGbW5nUHViCQACAQIeYXBwcm92ZUxlYXNpbmdzIG5vdCBhdXRob3JpemVkBA0kdDA0MzA1MjQzMTE0CQEMcmVhZE5vZGVJbmZvAQAABAZuQWRkcjAIBQ0kdDA0MzA1MjQzMTE0Al8xBAhsQW10S0VZMAgFDSR0MDQzMDUyNDMxMTQCXzIEBWxBbXQwCAUNJHQwNDMwNTI0MzExNAJfMwQHbElkS0VZMAgFDSR0MDQzMDUyNDMxMTQCXzQEBGxJZDAIBQ0kdDA0MzA1MjQzMTE0Al81BAVuZXdMMAkAxAgCBQZuQWRkcjAJAGUCBQVsQW10MAkAaAIFBGxBbXQFCGV4cENvdW50BAp2YWxpZGF0aW9uCQD8BwQFC25vZGVSZWdBZGRyAhp2YWxpZGF0ZUFuZEFwcHJvdmVMZWFzaW5ncwkAzAgCBQZuTGlzdFMFA25pbAUDbmlsAwkAAAIFCnZhbGlkYXRpb24FCnZhbGlkYXRpb24KASNmb3JFYWNoTm9kZVZhbGlkYXRlQW5kR2VuZXJhdGVMZWFzZQIBYQFpBARub2RlCQCRAwIFBW5MaXN0BQFpBAJsYQkAxAgCCQERQGV4dHJOYXRpdmUoMTA2MikBBQRub2RlBQRsQW10CQDOCAIFAWEJAMwIAgUCbGEJAMwIAgkBC0JpbmFyeUVudHJ5AgkBFmdldExlYXNlSWRCeUFkZHJlc3NLZXkBBQRub2RlCQEFbGNhbGMBBQJsYQkAzAgCCQEMSW50ZWdlckVudHJ5AgkBGmdldExlYXNlQW1vdW50QnlBZGRyZXNzS2V5AQUEbm9kZQUEbEFtdAUDbmlsCQDOCAIJAMwIAgkBC1N0cmluZ0VudHJ5AgURbEdyb3VwTm9kZUxpc3RLRVkFBm5MaXN0UwkAzAgCCQELQmluYXJ5RW50cnkCBQdsSWRLRVkwCQEFbGNhbGMBBQVuZXdMMAkAzAgCCQEMSW50ZWdlckVudHJ5AgUIbEFtdEtFWTAIBQVuZXdMMAZhbW91bnQJAMwIAgkBC0xlYXNlQ2FuY2VsAQUEbElkMAkAzAgCBQVuZXdMMAUDbmlsCgACJGwFBW5JZHhzCgACJHMJAJADAQUCJGwKAAUkYWNjMAUDbmlsCgEFJGYwXzECAiRhAiRpAwkAZwIFAiRpBQIkcwUCJGEJASNmb3JFYWNoTm9kZVZhbGlkYXRlQW5kR2VuZXJhdGVMZWFzZQIFAiRhCQCRAwIFAiRsBQIkaQoBBSRmMF8yAgIkYQIkaQMJAGcCBQIkaQUCJHMFAiRhCQACAQITTGlzdCBzaXplIGV4Y2VlZHMgOAkBBSRmMF8yAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgUFJGFjYzAAAAABAAIAAwAEAAUABgAHAAgJAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4BaQERcmViYWxhbmNlTGVhc2luZ3MCBmFtb3VudAhncm91cE51bQQFbklkeHMJAMwIAgAACQDMCAIAAQkAzAgCAAIJAMwIAgADCQDMCAIABAkAzAgCAAUJAMwIAgAGCQDMCAIABwUDbmlsBAdtbmdQdWJTCQELdmFsdWVPckVsc2UCCQCiCAECHCVzJXNfX2NmZ19fbGVhc2luZ01hbmFnZXJQdWICLDdBVU1YNTR1a1lNWXZQbW1hN3lvRmY1TmpaaHM0QnU1bnozRXo5RVY4c3VyBAZtbmdQdWIJANkEAQUHbW5nUHViUwQRbEdyb3VwTm9kZUxpc3RLRVkJARhnZXRMZWFzZUdyb3VwTm9kZUxpc3RLZXkBBQhncm91cE51bQQGbkxpc3RTCQEPZ2V0U3RyaW5nT3JGYWlsAgUEdGhpcwURbEdyb3VwTm9kZUxpc3RLRVkEBW5MaXN0CQC1CQIFBm5MaXN0UwUDU0VQAwkBAiE9AggFAWkPY2FsbGVyUHVibGljS2V5BQZtbmdQdWIJAAIBAiByZWJhbGFuY2VMZWFzaW5ncyBub3QgYXV0aG9yaXplZAQKdW5sZWFzZUFtdAkAZAIJAGkCBQZhbW91bnQJAJADAQUFbkxpc3QAAQQNJHQwNDQzNTQ0NDQxNgkBDHJlYWROb2RlSW5mbwEAAAQGbkFkZHIwCAUNJHQwNDQzNTQ0NDQxNgJfMQQIbEFtdEtFWTAIBQ0kdDA0NDM1NDQ0NDE2Al8yBAVsQW10MAgFDSR0MDQ0MzU0NDQ0MTYCXzMEB2xJZEtFWTAIBQ0kdDA0NDM1NDQ0NDE2Al80BARsSWQwCAUNJHQwNDQzNTQ0NDQxNgJfNQQFbmV3TDAJAMQIAgUGbkFkZHIwCQBkAgUFbEFtdDAJAGgCBQp1bmxlYXNlQW10CQCQAwEFBW5MaXN0CgEUZm9yRWFjaE5vZGVEb1VubGVhc2UCAWEBaQQEbm9kZQkAkQMCBQVuTGlzdAUBaQQGbElkS0VZCQEWZ2V0TGVhc2VJZEJ5QWRkcmVzc0tleQEFBG5vZGUEA2xJZAkBEUBleHRyTmF0aXZlKDEwNTIpAgUEdGhpcwUGbElkS0VZBAdsQW10S0VZCQEaZ2V0TGVhc2VBbW91bnRCeUFkZHJlc3NLZXkBBQRub2RlBARsQW10CQERQGV4dHJOYXRpdmUoMTA1MCkCBQR0aGlzBQdsQW10S0VZBAN1bGEJAQtMZWFzZUNhbmNlbAEJAQV2YWx1ZQEFA2xJZAQCbGEJAMQIAgkBEUBleHRyTmF0aXZlKDEwNjIpAQUEbm9kZQkAZQIFBGxBbXQFCnVubGVhc2VBbXQJAM4IAgUBYQkAzAgCCQELTGVhc2VDYW5jZWwBCQEFdmFsdWUBBQNsSWQJAMwIAgUCbGEJAMwIAgkBC0JpbmFyeUVudHJ5AgUGbElkS0VZCQEFbGNhbGMBBQJsYQkAzAgCCQEMSW50ZWdlckVudHJ5AgUHbEFtdEtFWQgFAmxhBmFtb3VudAUDbmlsCQDOCAIKAAIkbAUFbklkeHMKAAIkcwkAkAMBBQIkbAoABSRhY2MwBQNuaWwKAQUkZjBfMQICJGECJGkDCQBnAgUCJGkFAiRzBQIkYQkBFGZvckVhY2hOb2RlRG9VbmxlYXNlAgUCJGEJAJEDAgUCJGwFAiRpCgEFJGYwXzICAiRhAiRpAwkAZwIFAiRpBQIkcwUCJGEJAAIBAhNMaXN0IHNpemUgZXhjZWVkcyA4CQEFJGYwXzICCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECBQUkYWNjMAAAAAEAAgADAAQABQAGAAcACAkAzAgCCQELQmluYXJ5RW50cnkCBQdsSWRLRVkwCQEFbGNhbGMBBQVuZXdMMAkAzAgCCQEMSW50ZWdlckVudHJ5AgUIbEFtdEtFWTAIBQVuZXdMMAZhbW91bnQJAMwIAgkBC0xlYXNlQ2FuY2VsAQUEbElkMAkAzAgCBQVuZXdMMAUDbmlsAWkBG3N3YXBQYXJhbXNCeVVzZXJTWVNSRUFET05MWQIOdXNlckFkZHJlc3NTdHIJZ25zYnREaWZmBAlnbnNidERhdGEJAQlhc0FueUxpc3QBCQD8BwQFF2duc2J0Q29udHJvbGxlckNvbnRyYWN0AhRnbnNidEluZm9TWVNSRUFET05MWQkAzAgCBQ51c2VyQWRkcmVzc1N0cgkAzAgCAAAJAMwIAgAABQNuaWwFA25pbAQIZ25zYnRBbXQJAGQCCQEFYXNJbnQBCQCRAwIFCWduc2J0RGF0YQAABQlnbnNidERpZmYEDWduc2J0QW10VG90YWwJAGQCCQEFYXNJbnQBCQCRAwIFCWduc2J0RGF0YQABBQlnbnNidERpZmYEDXN3YXBMaW1pdERhdGEJAQlhc0FueUxpc3QBCQD8BwQFDG1hdGhDb250cmFjdAIVY2FsY1N3YXBMaW1pdFJFQURPTkxZCQDMCAIFCGduc2J0QW10BQNuaWwFA25pbAQXd2F2ZXNTd2FwTGltaXRJblVzZG5NYXgJAQVhc0ludAEJAJEDAgUNc3dhcExpbWl0RGF0YQAABBF3YXZlc1N3YXBMaW1pdE1heAkBBWFzSW50AQkAkQMCBQ1zd2FwTGltaXREYXRhAAEEEHVzZG5Td2FwTGltaXRNYXgJAQVhc0ludAEJAJEDAgUNc3dhcExpbWl0RGF0YQACBA5sYXN0U3dhcEhlaWdodAkBC3ZhbHVlT3JFbHNlAgkAmggCBQR0aGlzCQEVa2V5VXNlckxhc3RTd2FwSGVpZ2h0AQUOdXNlckFkZHJlc3NTdHIAAAQXc3dhcExpbWl0VGltZWxpZmVCbG9ja3MJARJzd2Fwc1RpbWVmcmFtZVJFQUQABBlwYXNzZWRCbG9ja3NBZnRlckxhc3RTd2FwCQBlAgUGaGVpZ2h0BQ5sYXN0U3dhcEhlaWdodAQRaXNTd2FwVGltZWxpZmVOZXcJAGcCBRlwYXNzZWRCbG9ja3NBZnRlckxhc3RTd2FwBRdzd2FwTGltaXRUaW1lbGlmZUJsb2NrcwQUc3dhcExpbWl0U3BlbnRJblVzZG4DBRFpc1N3YXBUaW1lbGlmZU5ldwAACQELdmFsdWVPckVsc2UCCQCaCAIFBHRoaXMJARhrZXlTd2FwVXNlclNwZW50SW5QZXJpb2QBBQ51c2VyQWRkcmVzc1N0cgAABA5ibGNrczJMbXRSZXNldAMFEWlzU3dhcFRpbWVsaWZlTmV3AAAJAGUCBRdzd2FwTGltaXRUaW1lbGlmZUJsb2NrcwUZcGFzc2VkQmxvY2tzQWZ0ZXJMYXN0U3dhcAkAlAoCBQNuaWwJAJkKBwUXd2F2ZXNTd2FwTGltaXRJblVzZG5NYXgFFHN3YXBMaW1pdFNwZW50SW5Vc2RuBQ5ibGNrczJMbXRSZXNldAUIZ25zYnRBbXQFDWduc2J0QW10VG90YWwFEXdhdmVzU3dhcExpbWl0TWF4BRB1c2RuU3dhcExpbWl0TWF4AWkBHWNhbGNXaXRoZHJhd1Jlc3VsdFNZU1JFQURPTkxZBAhzd2FwVHlwZQhpbkFtb3VudAVwcmljZQdpbkFzc2V0BA9uZXV0cmlub01ldHJpY3MJAQlhc0FueUxpc3QBCQD8BwQFDG1hdGhDb250cmFjdAIaY2FsY05ldXRpbm9NZXRyaWNzUkVBRE9OTFkFA25pbAUDbmlsCQCUCgIFA25pbAkBDGNhbGNXaXRoZHJhdwUFCHN3YXBUeXBlBQhpbkFtb3VudAUFcHJpY2UFD25ldXRyaW5vTWV0cmljcwUHaW5Bc3NldAFpARRyZXBsYWNlQ29tbXVuaXR5Tm9kZQQKb2xkQWRkclN0cgpuZXdBZGRyU3RyCGdyb3VwTnVtDXBlbmFsdHlBbW91bnQEB21uZ1B1YlMJAQt2YWx1ZU9yRWxzZQIJAKIIAQIcJXMlc19fY2ZnX19sZWFzaW5nTWFuYWdlclB1YgIsN0FVTVg1NHVrWU1ZdlBtbWE3eW9GZjVOalpoczRCdTVuejNFejlFVjhzdXIEBm1uZ1B1YgkA2QQBBQdtbmdQdWJTAwkBAiE9AggFAWkPY2FsbGVyUHVibGljS2V5BQZtbmdQdWIJAAIBAiNyZXBsYWNlQ29tbXVuaXR5Tm9kZSBub3QgYXV0aG9yaXplZAQIZ3JvdXBLZXkJARhnZXRMZWFzZUdyb3VwTm9kZUxpc3RLZXkBBQhncm91cE51bQQOZ3JvdXBOb2RlTGlzdFMJAQ9nZXRTdHJpbmdPckZhaWwCBQR0aGlzBQhncm91cEtleQMJAQEhAQkBCGNvbnRhaW5zAgUOZ3JvdXBOb2RlTGlzdFMFCm9sZEFkZHJTdHIJAAIBCQCsAgIJAKwCAgkArAICAgZHcm91cCAJAKQDAQUIZ3JvdXBOdW0CGiBkb2VzIG5vdCBjb250YWluIGFkZHJlc3MgBQpvbGRBZGRyU3RyBAlkb1JlcGxhY2UJAPwHBAUUbm9kZVJlZ2lzdHJ5Q29udHJhY3QCE3JlcGxhY2VBcHByb3ZlZE5vZGUJAMwIAgUKb2xkQWRkclN0cgkAzAgCBQpuZXdBZGRyU3RyCQDMCAIFCGdyb3VwTnVtCQDMCAIFDXBlbmFsdHlBbW91bnQFA25pbAUDbmlsAwkAAAIFCWRvUmVwbGFjZQUJZG9SZXBsYWNlBA1vbGRMZWFzZUlkS2V5CQEWZ2V0TGVhc2VJZEJ5QWRkcmVzc0tleQEFCm9sZEFkZHJTdHIEDm9sZExlYXNlQW10S2V5CQEaZ2V0TGVhc2VBbW91bnRCeUFkZHJlc3NLZXkBBQpvbGRBZGRyU3RyBAhsZWFzZUFtdAkBEUBleHRyTmF0aXZlKDEwNTUpAQUOb2xkTGVhc2VBbXRLZXkEDW5ld0xlYXNlSWRLZXkJARZnZXRMZWFzZUlkQnlBZGRyZXNzS2V5AQUKbmV3QWRkclN0cgQObmV3TGVhc2VBbXRLZXkJARpnZXRMZWFzZUFtb3VudEJ5QWRkcmVzc0tleQEFCm5ld0FkZHJTdHIECG5ld0xlYXNlCQDECAIJARFAZXh0ck5hdGl2ZSgxMDYyKQEFCm5ld0FkZHJTdHIFCGxlYXNlQW10BBV1cGRhdGVkR3JvdXBOb2RlTGlzdFMJALkJAgkAtQkCBQ5ncm91cE5vZGVMaXN0UwUKb2xkQWRkclN0cgUKbmV3QWRkclN0cgkAlAoCCQDMCAIJAQtMZWFzZUNhbmNlbAEJARFAZXh0ck5hdGl2ZSgxMDU3KQEFDW9sZExlYXNlSWRLZXkJAMwIAgkBC0RlbGV0ZUVudHJ5AQUNb2xkTGVhc2VJZEtleQkAzAgCCQELRGVsZXRlRW50cnkBBQ5vbGRMZWFzZUFtdEtleQkAzAgCCQELU3RyaW5nRW50cnkCBQhncm91cEtleQUVdXBkYXRlZEdyb3VwTm9kZUxpc3RTCQDMCAIFCG5ld0xlYXNlCQDMCAIJAQtCaW5hcnlFbnRyeQIFDW5ld0xlYXNlSWRLZXkJAQVsY2FsYwEFCG5ld0xlYXNlCQDMCAIJAQxJbnRlZ2VyRW50cnkCBQ5uZXdMZWFzZUFtdEtleQUIbGVhc2VBbXQFA25pbAUEdW5pdAkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgFpAR91cGRhdGVSZXNlcnZlc0FuZE5ldXRyaW5vU3VwcGx5AAoBFmdldE51bWJlckJ5S2V5SW50ZXJuYWwBA2tleQQHJG1hdGNoMAkAmggCBQR0aGlzBQNrZXkDCQABAgUHJG1hdGNoMAIDSW50BAFhBQckbWF0Y2gwBQFhAAAEB25NZXRyaXgJAQlhc0FueUxpc3QBCQD8BwQFDG1hdGhDb250cmFjdAIaY2FsY05ldXRpbm9NZXRyaWNzUkVBRE9OTFkFA25pbAUDbmlsBANpZHgJARZnZXROdW1iZXJCeUtleUludGVybmFsAQIidXBkYXRlUmVzZXJ2ZXNBbmROZXV0cmlub1N1cHBseUlkeAQGbmV3SWR4CQBkAgUDaWR4AAEJAMwIAgkBDEludGVnZXJFbnRyeQICInVwZGF0ZVJlc2VydmVzQW5kTmV1dHJpbm9TdXBwbHlJZHgFBm5ld0lkeAkAzAgCCQEMSW50ZWdlckVudHJ5AgIHcmVzZXJ2ZQkBBWFzSW50AQkAkQMCBQduTWV0cml4AAMJAMwIAgkBDEludGVnZXJFbnRyeQICDm5ldXRyaW5vU3VwcGx5CQEFYXNJbnQBCQCRAwIFB25NZXRyaXgABQkAzAgCCQEMSW50ZWdlckVudHJ5AgIOcmVzZXJ2ZXNJblVzZG4JARZjb252ZXJ0V2F2ZXNUb05ldXRyaW5vAgkBBWFzSW50AQkAkQMCBQduTWV0cml4AAMJAQVhc0ludAEJAJEDAgUHbk1ldHJpeAAACQDMCAIJAQxJbnRlZ2VyRW50cnkCAgdzdXJwbHVzCQEFYXNJbnQBCQCRAwIFB25NZXRyaXgABgkAzAgCCQEMSW50ZWdlckVudHJ5AgIHZGVmaWNpdAkBAS0BCQEFYXNJbnQBCQCRAwIFB25NZXRyaXgABgUDbmlsAWkBFndhdmVzQmFsYW5jZXNWc1BheW1lbnQABAFiCQDvBwEFBHRoaXMJAMwIAgkBDEludGVnZXJFbnRyeQICFndhdmVzQmFsYW5jZV9hdmFpbGFibGUIBQFiCWF2YWlsYWJsZQkAzAgCCQEMSW50ZWdlckVudHJ5AgIUd2F2ZXNCYWxhbmNlX3JlZ3VsYXIIBQFiB3JlZ3VsYXIJAMwIAgkBDEludGVnZXJFbnRyeQICF3dhdmVzQmFsYW5jZV9nZW5lcmF0aW5nCAUBYgpnZW5lcmF0aW5nCQDMCAIJAQxJbnRlZ2VyRW50cnkCAhZ3YXZlc0JhbGFuY2VfZWZmZWN0aXZlCAUBYgllZmZlY3RpdmUJAMwIAgkBDEludGVnZXJFbnRyeQICDXdhdmVzX3BheW1lbnQICQEFdmFsdWUBCQCRAwIIBQFpCHBheW1lbnRzAAAGYW1vdW50BQNuaWwBAnR4AQZ2ZXJpZnkABBNwdWJLZXlBZG1pbnNMaXN0U3RyCQC5CQIJAMwIAgIsRXh0RUVLMTlubUtqOW1DcG5XeXZFRUpGWUFUTE1jVkVNdm9oaFVIa3lITm0JAMwIAgIsRXY1cHk1RmZCUVg5Y1pwWUtuZlFyVEI0OUJ5ZjhRbXBaV2VEVlJpbTR5VjcJAMwIAgIsRFV1dUxqWHU5OG5Cd1pjN2Zxd0NUanRBM25uUndnVGJrTVNyNVNVMk5tRFIJAMwIAgIsRFV1dUxqWHU5OG5Cd1pjN2Zxd0NUanRBM25uUndnVGJrTVNyNVNVMk5tRFIFA25pbAUDU0VQBBBwdWJLZXlBZG1pbnNMaXN0CQC1CQIJAQt2YWx1ZU9yRWxzZQIJAJ0IAgUPY29udHJvbENvbnRyYWN0Agwlc19fbXVsdGlzaWcFE3B1YktleUFkbWluc0xpc3RTdHIFA1NFUAQFY291bnQJAGQCCQBkAgkAZAIDCQD0AwMIBQJ0eAlib2R5Qnl0ZXMJAJEDAggFAnR4BnByb29mcwAACQDZBAEJAJEDAgUQcHViS2V5QWRtaW5zTGlzdAAAAAEAAAMJAPQDAwgFAnR4CWJvZHlCeXRlcwkAkQMCCAUCdHgGcHJvb2ZzAAEJANkEAQkAkQMCBRBwdWJLZXlBZG1pbnNMaXN0AAEAAQAAAwkA9AMDCAUCdHgJYm9keUJ5dGVzCQCRAwIIBQJ0eAZwcm9vZnMAAgkA2QQBCQCRAwIFEHB1YktleUFkbWluc0xpc3QAAgABAAADCQD0AwMIBQJ0eAlib2R5Qnl0ZXMJAJEDAggFAnR4BnByb29mcwADCQDZBAEJAJEDAgUQcHViS2V5QWRtaW5zTGlzdAADAAIAAAMDBQlpc0Jsb2NrZWQJAAACCQERQGV4dHJOYXRpdmUoMTA1MykCBQ9jb250cm9sQ29udHJhY3QCEWlzX2Jsb2NrZWRfY2FsbGVyCQClCAEFC2dvdkNvbnRyYWN0BwkBDnZhbGlkYXRlVXBkYXRlAQUCdHgEByRtYXRjaDAFAnR4AwkAAQIFByRtYXRjaDACFVNwb25zb3JGZWVUcmFuc2FjdGlvbgQJc3BvbnNvclR4BQckbWF0Y2gwAwkBG2NoZWNrSXNWYWxpZE1pblNwb25zb3JlZEZlZQEFCXNwb25zb3JUeAkAZwIFBWNvdW50AAMHCQBnAgUFY291bnQAAwmMeF8=", "height": 2443255, "applicationStatus": "succeeded", "spentComplexity": 0 } View: original | compacted Prev: 4xKYLTW2puBWSmkjN2BfNKoWQSgA9qLvTiqbF69ipSpd Next: aAozP578c57yZmRydwC46Yeafx6XpbP3PrudmfWXcqG Diff:
OldNewDifferences
7777
7878 let LISTSEP = ":"
7979
80-let WAVELET = 100000000
80+let MULT8 = 100000000
8181
82-let PAULI = 1000000
83-
84-let PRICELET = 1000000
82+let MULT6 = 1000000
8583
8684 let DEFAULTSWAPFEE = 20000
8785
8886 let BRPROTECTED = 100000
87+
88+let WAVESID = base58'WAVES'
8989
9090 let IdxNetAmount = 0
9191
169169
170170 let BalanceNeutrinoLockIntervalKey = "balance_neutrino_lock_interval"
171171
172-let MinWavesSwapAmountKey = "min_waves_swap_amount"
172+let MinTokenSwapAmountKey = "min_outNeutrino_swap_amount"
173173
174-let MinNeutrinoSwapAmountKey = "min_neutrino_swap_amount"
174+let MinNeutrinoSwapAmountKey = "min_outMulti_swap_amount"
175175
176176 let NodeOracleProviderPubKeyKey = "node_oracle_provider"
177177
178178 let NeutrinoOutFeePartKey = "neutrinoOut_swap_feePart"
179179
180180 let WavesOutFeePartKey = "wavesOut_swap_feePart"
181-
182-let MultiOutFeePartKey = "multiOut_swap_feePart"
183181
184182 func keyNodeRegistry (address) = ("%s__" + address)
185183
202200 func getStakingNodeAddressByIndex (idx) = addressFromStringValue(getStakingNodeByIndex(idx))
203201
204202
205-func getReservedAmountForSponsorship () = valueOrElse(getInteger(this, makeString(["%s%s", "lease", "sponsorshipWavesReserve"], SEP)), (1000 * WAVELET))
203+func getReservedAmountForSponsorship () = valueOrElse(getInteger(this, makeString(["%s%s", "lease", "sponsorshipWavesReserve"], SEP)), (1000 * MULT8))
206204
207205
208206 func getBalanceUnlockBlockKey (owner) = ("balance_unlock_block_" + owner)
226224 func minSwapAmountKEY (swapType) = (("min_" + swapType) + "_swap_amount")
227225
228226
229-func totalLockedKEY (swapType) = ("balance_lock_" + swapType)
227+func totalLockedKEY (swapType,assetId) = makeString(["%s%s%s", "balanceLock", swapType, assetId], SEP)
230228
231229
232-func totalLockedByUserKEY (swapType,owner) = makeString(["balance_lock", swapType, owner], "_")
230+func totalLockedByUserKEY (swapType,owner,assetId) = makeString(["%s%s%s%s", "balanceLock", swapType, owner, assetId], "_")
233231
234232
235233 func balanceLockIntervalKEY (swapType) = (("balance_" + swapType) + "_lock_interval")
238236 func nodeBalanceLockIntervalKEY () = "balance_node_lock_interval"
239237
240238
241-func outFeePartKEY (swapType) = (swapType + "Out_swap_feePart")
242-
243-
244239 func swapsTimeframeKEY () = "swaps_timeframe"
245240
246241
247242 func brProtectedKEY () = "min_BR_protection_level"
243+
244+
245+func basketAssetsKey () = "%s%s__common__basketAssets"
246+
247+
248+func basketDKey () = "%s%s__common__basketD"
249+
250+
251+func basketKKey () = "%s%s__common__basketK"
252+
253+
254+func basketBKey () = "%s%s__common__basketB"
255+
256+
257+func basketLogbDeltaKey () = "%s%s__common__basketLogbDelta"
258+
259+
260+func basketWeightByAssetKey (assetId) = ("%s%s%s__common__weightByAsset__" + assetId)
248261
249262
250263 func minSwapAmountREAD (swapType) = valueOrElse(getInteger(this, minSwapAmountKEY(swapType)), 0)
253266 func swapsTimeframeREAD () = valueOrElse(getInteger(this, swapsTimeframeKEY()), 1440)
254267
255268
256-func totalLockedREAD (swapType) = valueOrElse(getInteger(this, totalLockedKEY(swapType)), 0)
269+func totalLockedREAD (swapType,assetId) = valueOrElse(getInteger(this, totalLockedKEY(swapType, assetId)), 0)
257270
258271
259-func totalLockedByUserREAD (swapType,owner) = valueOrElse(getInteger(this, totalLockedByUserKEY(swapType, owner)), 0)
272+func totalLockedByUserREAD (swapType,owner,assetId) = valueOrElse(getInteger(this, totalLockedByUserKEY(swapType, owner, assetId)), 0)
260273
261274
262275 func balanceLockIntervalREAD (swapType) = valueOrElse(getInteger(this, balanceLockIntervalKEY(swapType)), 1440)
271284 func keyUserLastSwapHeight (userAddress) = makeString(["%s%s", "userLastSwapHeight", userAddress], SEP)
272285
273286
274-func convertNeutrinoToWaves (amount,price) = fraction(fraction(amount, PRICELET, price), WAVELET, PAULI)
287+func convertNeutrinoToWaves (amount,price) = fraction(amount, MULT8, price)
275288
276289
277-func convertWavesToNeutrino (amount,price) = fraction(fraction(amount, price, PRICELET), PAULI, WAVELET)
290+func convertWavesToNeutrino (amount,price) = fraction(amount, price, MULT8)
278291
279292
280293 func convertWavesToBond (amount,price) = convertWavesToNeutrino(amount, price)
281294
282295
283296 func convertJsonArrayToList (jsonArray) = split(jsonArray, ",")
297+
298+
299+func convertBasketItemToNeutrino (inAmount,inAsset,basketInfoStr,neutrinoPrice) = {
300+ let basketInfo = split(basketInfoStr, "_")
301+ func findItem (acc,item) = {
302+ let bItem = split(item, LISTSEP)
303+ if ((bItem[0] == inAsset))
304+ then bItem
305+ else acc
306+ }
307+
308+ let it = {
309+ let $l = basketInfo
310+ let $s = size($l)
311+ let $acc0 = nil
312+ func $f0_1 ($a,$i) = if (($i >= $s))
313+ then $a
314+ else findItem($a, $l[$i])
315+
316+ func $f0_2 ($a,$i) = if (($i >= $s))
317+ then $a
318+ else throw("List size exceeds 10")
319+
320+ $f0_2($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($acc0, 0), 1), 2), 3), 4), 5), 6), 7), 8), 9), 10)
321+ }
322+ let dlog6 = valueOrElse(getInteger(basketLogbDeltaKey()), -18820642)
323+ let d6 = valueOrElse(getInteger(basketDKey()), 50000)
324+ let b6 = valueOrElse(getInteger(basketBKey()), 1250000)
325+ let w6 = valueOrElse(getInteger(basketWeightByAssetKey(inAsset)), 166666)
326+ let p6 = ((dlog6 - w6) + parseIntValue(it[1]))
327+ let pp6 = pow(b6, 6, p6, 6, 6, HALFUP)
328+ let outAmountGross = fraction(inAmount, parseIntValue(it[2]), if ((neutrinoPrice == 0))
329+ then MULT8
330+ else (neutrinoPrice * 100))
331+ let multiplier = ((MULT6 + d6) - pp6)
332+ if ((0 >= multiplier))
333+ then throw(("Net amount is non-positive, multiplier = " + toString(multiplier)))
334+ else {
335+ let netAmount = fraction(outAmountGross, multiplier, MULT6)
336+ let feeAmount = if ((netAmount >= outAmountGross))
337+ then 0
338+ else (outAmountGross - netAmount)
339+[outAmountGross, netAmount, feeAmount]
340+ }
341+ }
342+
343+
344+func convertNeutrinoToBasket (amount,basketInfoStr,neutrinoSupply) = {
345+ let basketInfo = split(basketInfoStr, "_")
346+ func conv (acc,item) = {
347+ let parts = split(item, LISTSEP)
348+ let assetId = parts[0]
349+ let share = parseIntValue(parts[1])
350+ let price = parseIntValue(parts[2])
351+ let reserv = parseIntValue(parts[3])
352+ if ((neutrinoSupply == 0))
353+ then throw("Attempt to burn neutrino at zero supply")
354+ else {
355+ let outAmount = fraction(amount, reserv, neutrinoSupply)
356+ let wavesTuple = if ((assetId == "WAVES"))
357+ then $Tuple2(outAmount, price)
358+ else $Tuple2(acc._2, acc._3)
359+ $Tuple3((acc._1 :+ $Tuple4(assetId, outAmount, price, share)), wavesTuple._1, wavesTuple._2)
360+ }
361+ }
362+
363+ let $l = basketInfo
364+ let $s = size($l)
365+ let $acc0 = $Tuple3(nil, 0, 0)
366+ func $f0_1 ($a,$i) = if (($i >= $s))
367+ then $a
368+ else conv($a, $l[$i])
369+
370+ func $f0_2 ($a,$i) = if (($i >= $s))
371+ then $a
372+ else throw("List size exceeds 10")
373+
374+ $f0_2($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($acc0, 0), 1), 2), 3), 4), 5), 6), 7), 8), 9), 10)
375+ }
284376
285377
286378 func minSwapAmountFAIL (swapType,minSwapAmount) = throw(((("The specified amount in " + swapType) + " swap is less than the required minimum of ") + toString(minSwapAmount)))
348440
349441 let sIdxMaxRand = 16
350442
443+let sIdxInAsset = 20
444+
351445 func swapKEY (userAddress,txId) = makeString(["%s%s", userAddress, txId], SEP)
352446
353447
354-func strSwapDATA (swapType,status,inAmount,price,outNetAmount,outFeeAmount,startHeight,startTimestamp,endHeight,endTimestamp,selfUnlockHeight,randUnlockHeight,index,withdrawTxId,randMin,randMax,outSurfAmt,br,basketData) = makeString(["%s%s%d%d%d%d%d%d%d%d%d%d%d%s%d%d%d%d%s", swapType, status, inAmount, price, outNetAmount, outFeeAmount, startHeight, startTimestamp, endHeight, endTimestamp, selfUnlockHeight, randUnlockHeight, index, withdrawTxId, randMin, randMax, outSurfAmt, br, basketData], SEP)
448+func strSwapDATA (swapType,status,inAmount,price,outNetAmount,outFeeAmount,startHeight,startTimestamp,endHeight,endTimestamp,selfUnlockHeight,randUnlockHeight,index,withdrawTxId,randMin,randMax,outSurfAmt,br,basketData,inAsset) = makeString(["%s%s%d%d%d%d%d%d%d%d%d%d%d%s%d%d%d%d%s%s", swapType, status, inAmount, price, outNetAmount, outFeeAmount, startHeight, startTimestamp, endHeight, endTimestamp, selfUnlockHeight, randUnlockHeight, index, withdrawTxId, randMin, randMax, outSurfAmt, br, basketData, inAsset], SEP)
355449
356450
357-func pendingSwapDATA (swapType,inAssetAmount,selfUnlockHeight) = strSwapDATA(swapType, "PENDING", toString(inAssetAmount), "0", "0", "0", toString(height), toString(lastBlock.timestamp), "0", "0", toString(selfUnlockHeight), "0", "0", "NULL", "0", "0", "0", "0", "")
451+func pendingSwapDATA (swapType,inAssetAmount,selfUnlockHeight,inAsset) = strSwapDATA(swapType, "PENDING", toString(inAssetAmount), "0", "0", "0", toString(height), toString(lastBlock.timestamp), "0", "0", toString(selfUnlockHeight), "0", "0", "NULL", "0", "0", "0", "0", "", inAsset)
358452
359453
360-func finishSwapDATA (dataArray,price,outNetAmount,outFeeAmount,randUnlockHeight,index,withdrawTxId,outSurfAmt,br,basketData) = strSwapDATA(dataArray[sIdxSwapType], "FINISHED", dataArray[sIdxInAmount], toString(price), toString(outNetAmount), toString(outFeeAmount), dataArray[sIdxStartHeight], dataArray[sIdxStartTimestamp], toString(height), toString(lastBlock.timestamp), dataArray[sIdxSelfUnlockHeight], toString(randUnlockHeight), toString(index), withdrawTxId, dataArray[sIdxMinRand], dataArray[sIdxMaxRand], toString(outSurfAmt), toString(br), basketData)
454+func finishSwapDATA (dataArray,price,outNetAmount,outFeeAmount,randUnlockHeight,index,withdrawTxId,outSurfAmt,br,basketData,inAsset) = strSwapDATA(dataArray[sIdxSwapType], "FINISHED", dataArray[sIdxInAmount], toString(price), toString(outNetAmount), toString(outFeeAmount), dataArray[sIdxStartHeight], dataArray[sIdxStartTimestamp], toString(height), toString(lastBlock.timestamp), dataArray[sIdxSelfUnlockHeight], toString(randUnlockHeight), toString(index), withdrawTxId, dataArray[sIdxMinRand], dataArray[sIdxMaxRand], toString(outSurfAmt), toString(br), basketData, inAsset)
361455
362456
363457 func swapDataFailOrREAD (userAddress,swapTxId) = {
364458 let swapKey = swapKEY(userAddress, swapTxId)
365- split(valueOrErrorMessage(getString(this, swapKey), ("no swap data for " + swapKey)), SEP)
459+ split_4C(valueOrErrorMessage(getString(swapKey), ("no swap data for " + swapKey)), SEP)
366460 }
367461
368462
369463 func applyFees (amountOutGross,inAmtToSURF,feePart) = {
370- let feeAmount = fraction(amountOutGross, feePart, PAULI)
464+ let feeAmount = fraction(amountOutGross, feePart, MULT6)
371465 [(amountOutGross - feeAmount), feeAmount]
466+ }
467+
468+
469+func applyFeesBasket (basketOut) = {
470+ let k6 = valueOrElse(getInteger(basketKKey()), MULT6)
471+ let d6 = valueOrElse(getInteger(basketDKey()), 50000)
472+ let feePart = fraction(k6, d6, MULT6)
473+ func feeApplier (acc,item) = {
474+ let feeAmount = fraction(item._2, feePart, MULT6)
475+ (acc :+ makeString([item._1, toString(item._3), toString((item._2 - feeAmount)), toString(feeAmount), toString(item._4)], LISTSEP))
476+ }
477+
478+ let a = {
479+ let $l = basketOut
480+ let $s = size($l)
481+ let $acc0 = nil
482+ func $f0_1 ($a,$i) = if (($i >= $s))
483+ then $a
484+ else feeApplier($a, $l[$i])
485+
486+ func $f0_2 ($a,$i) = if (($i >= $s))
487+ then $a
488+ else throw("List size exceeds 10")
489+
490+ $f0_2($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($acc0, 0), 1), 2), 3), 4), 5), 6), 7), 8), 9), 10)
491+ }
492+ makeString(a, SEP)
372493 }
373494
374495
431552 }
432553
433554
434-func commonSwap (swapType,pmtAmount,userAddressStr,txId58) = {
555+func commonSwap (swapType,pmtAmount,inAsset,userAddressStr,txId58) = {
435556 let swapParamsSTRUCT = asSwapParamsSTRUCT(invoke(this, "swapParamsByUserSYSREADONLY", [userAddressStr, 0], nil))
436557 let swapLimitSpent = swapParamsSTRUCT._2
437558 let blcks2LmtReset = swapParamsSTRUCT._3
438559 let wavesSwapLimitMax = swapParamsSTRUCT._6
439560 let usdnSwapLimitMax = swapParamsSTRUCT._7
440561 let minSwapAmount = minSwapAmountREAD(swapType)
441- let totalLocked = totalLockedREAD(swapType)
442- let totalLockedByUser = totalLockedByUserREAD(swapType, userAddressStr)
562+ let totalLocked = totalLockedREAD(swapType, inAsset)
563+ let totalLockedByUser = totalLockedByUserREAD(swapType, userAddressStr, inAsset)
443564 let nodeAddress = getStakingNodeByIndex(0)
444565 let priceByIndex = getPriceHistory(getHeightPriceByIndex(priceIndex))
445566 let isSwapByNode = (nodeAddress == userAddressStr)
447568 then nodeBalanceLockIntervalREAD()
448569 else balanceLockIntervalREAD(swapType)
449570 let selfUnlockHeight = (height + balanceLockMaxInterval)
450- let swapUsdnVolume = if ((swapType == "waves"))
571+ let swapUsdnVolume = if ((swapType == "outNeutrino"))
451572 then convertWavesToNeutrino(pmtAmount, priceByIndex)
452573 else pmtAmount
453- let swapLimitMax = if ((swapType == "waves"))
574+ let swapLimitMax = if ((swapType == "outNeutrino"))
454575 then convertWavesToNeutrino(wavesSwapLimitMax, priceByIndex)
455576 else usdnSwapLimitMax
456577 if ((minSwapAmount > pmtAmount))
466587 else if (isBlocked)
467588 then emergencyShutdownFAIL()
468589 else {
469- let leasePart = if ((swapType == "waves"))
590+ let leasePart = if (if ((swapType == "outNeutrino"))
591+ then (inAsset == "WAVES")
592+ else false)
470593 then prepareUnleaseAndLease(0)
471594 else nil
472- $Tuple2(([IntegerEntry(keySwapUserSpentInPeriod(userAddressStr), swapUsdnVolume), IntegerEntry(keyUserLastSwapHeight(userAddressStr), height), IntegerEntry(totalLockedByUserKEY(swapType, userAddressStr), (totalLockedByUser + pmtAmount)), IntegerEntry(getBalanceUnlockBlockKey(userAddressStr), selfUnlockHeight), IntegerEntry(totalLockedKEY(swapType), (totalLocked + pmtAmount)), StringEntry(swapKEY(userAddressStr, txId58), pendingSwapDATA(swapType, pmtAmount, selfUnlockHeight))] ++ leasePart), unit)
595+ $Tuple2(([IntegerEntry(keySwapUserSpentInPeriod(userAddressStr), swapUsdnVolume), IntegerEntry(keyUserLastSwapHeight(userAddressStr), height), IntegerEntry(totalLockedByUserKEY(swapType, userAddressStr, inAsset), (totalLockedByUser + pmtAmount)), IntegerEntry(getBalanceUnlockBlockKey(userAddressStr), selfUnlockHeight), IntegerEntry(totalLockedKEY(swapType, inAsset), (totalLocked + pmtAmount)), StringEntry(swapKEY(userAddressStr, txId58), pendingSwapDATA(swapType, pmtAmount, selfUnlockHeight, inAsset))] ++ leasePart), unit)
473596 }
474597 }
475598
502625
503626 let nMetricCurrentPriceAdj = 13
504627
628+let nMetricBasketInfo = 14
629+
630+let nMetricWeightedPrice = 15
631+
632+let nMetricTotalReservesUsd = 16
633+
505634 let bFuncIdxSurf = 0
506635
507636 let bFuncIdxWaves = 1
524653
525654 let bFuncIdxWavesPrice = 10
526655
527-func calcWithdrawW2U (wavesIn,price) = {
528- let outAmtGross = convertWavesToNeutrino(wavesIn, price)
529- $Tuple10(outAmtGross, neutrinoAssetId, 0, unit, 0, price, wavesIn, 0, 0, 0)
656+func calcWithdrawT2U (inAmount,inAsset,basketInfo,weightedPrice) = {
657+ let outAmt = convertBasketItemToNeutrino(inAmount, inAsset, basketInfo, weightedPrice)
658+ $Tuple12(outAmt[0], neutrinoAssetId, 0, inAsset, 0, weightedPrice, inAmount, 0, 0, 0, nil, [outAmt[1], outAmt[2]])
530659 }
531660
532661
533-func calcWithdrawU2Basket (usdnIn,price,br,reservesInUsdn,usdnSupply) = {
662+func calcWithdrawU2Basket (usdnIn,basketInfo,br,reservesInUsdn,neutrinoSupply) = {
534663 let brProtected = valueOrElse(getInteger(this, brProtectedKEY()), BRPROTECTED)
535664 let maxAllowedUsdnBeforeMinBr = if ((brProtected >= br))
536665 then 0
537- else fraction((reservesInUsdn - fraction(brProtected, usdnSupply, PAULI)), PAULI, (PAULI - brProtected))
666+ else fraction((reservesInUsdn - fraction(brProtected, neutrinoSupply, MULT6)), MULT6, (MULT6 - brProtected))
538667 let allowedUsdnBeforeMinBr = if ((usdnIn > maxAllowedUsdnBeforeMinBr))
539668 then maxAllowedUsdnBeforeMinBr
540669 else usdnIn
541670 let allowedUsdnAfterMinBr = if ((usdnIn > maxAllowedUsdnBeforeMinBr))
542- then fraction((usdnIn - maxAllowedUsdnBeforeMinBr), br, PAULI)
671+ then fraction((usdnIn - maxAllowedUsdnBeforeMinBr), br, MULT6)
543672 else 0
544673 let allowedUsdn = (allowedUsdnBeforeMinBr + allowedUsdnAfterMinBr)
545674 let usdn2SURF = (usdnIn - allowedUsdn)
546- let outAmtGross = convertNeutrinoToWaves(allowedUsdn, price)
547- $Tuple10(outAmtGross, unit, usdn2SURF, neutrinoAssetId, outAmtGross, price, allowedUsdn, maxAllowedUsdnBeforeMinBr, allowedUsdnBeforeMinBr, allowedUsdnAfterMinBr)
675+ let outAmtGross = convertNeutrinoToBasket(allowedUsdn, basketInfo, neutrinoSupply)
676+ $Tuple12(outAmtGross._2, unit, usdn2SURF, neutrinoAssetId, outAmtGross._2, outAmtGross._3, allowedUsdn, maxAllowedUsdnBeforeMinBr, allowedUsdnBeforeMinBr, allowedUsdnAfterMinBr, outAmtGross._1, nil)
548677 }
549678
550679
551-func calcWithdraw (swapType,inAmount,price,neutrinoMetrics) = {
552- let outFeePart = valueOrElse(getInteger(this, outFeePartKEY(swapType)), DEFAULTSWAPFEE)
553- if (if ((0 > outFeePart))
554- then true
555- else (outFeePart >= PAULI))
556- then throw(((("invalid outFeePart config for " + swapType) + " swap: outFeePart=") + toString(outFeePart)))
557- else {
558- let brProtected = valueOrElse(getInteger(this, brProtectedKEY()), BRPROTECTED)
559- let BR = asInt(neutrinoMetrics[nMetricIdxBR])
560- let reservesInUsdn = asInt(neutrinoMetrics[nMetricIdxReserveInUsdn])
561- let usdnSupply = asInt(neutrinoMetrics[nMetricIdxUsdnSupply])
562- let priceAdj = asInt(neutrinoMetrics[nMetricCurrentPriceAdj])
563- let outDataTuple = if ((swapType == "waves"))
564- then calcWithdrawW2U(inAmount, price)
565- else if ((swapType == "outMulti"))
566- then calcWithdrawU2Basket(inAmount, priceAdj, BR, reservesInUsdn, usdnSupply)
567- else throw(("Unsupported swap type " + swapType))
568- let outAmtGross = outDataTuple._1
569- let outAssetId = outDataTuple._2
570- let inAmtToSurfPart = outDataTuple._3
571- let inAssetId = outDataTuple._4
572- let unleaseAmt = outDataTuple._5
573- let withdrawPrice = outDataTuple._6
574- let payoutsArray = applyFees(outAmtGross, inAmtToSurfPart, outFeePart)
575- let outNetAmt = payoutsArray[IdxNetAmount]
576- let outFeeAmt = payoutsArray[IdxFeeAmount]
577- let outSurfAmt = if ((0 >= inAmtToSurfPart))
578- then 0
579- else {
580- let surfResult = asAnyList(invoke(mathContract, "surfFunctionREADONLY", [inAmtToSurfPart, inAssetId], nil))
581- asInt(surfResult[bFuncIdxSurf])
582- }
583- $Tuple8(outNetAmt, outAssetId, outSurfAmt, inAmtToSurfPart, unleaseAmt, outFeeAmt, outAmtGross, withdrawPrice)
584- }
585- }
586-
587-
588-func commonWithdraw (account,index,swapTxId,withdrawTxId,neutrinoMetrics) = {
589- let userAddress = addressFromStringValue(account)
590- let dataArray = swapDataFailOrREAD(account, swapTxId)
591- let selfUnlockHeight = parseIntValue(dataArray[sIdxSelfUnlockHeight])
592- let swapType = dataArray[sIdxSwapType]
593- let inAmount = parseIntValue(dataArray[sIdxInAmount])
594- let swapStatus = dataArray[sIdxStatus]
595- let startHeight = parseIntValue(dataArray[sIdxStartHeight])
596- let outFeePart = valueOrElse(getInteger(this, outFeePartKEY(swapType)), DEFAULTSWAPFEE)
597- let totalLocked = totalLockedREAD(swapType)
598- let totalLockedByUser = totalLockedByUserREAD(swapType, account)
599- let unlockHeight = selfUnlockHeight
600- let indexHeight = getHeightPriceByIndex(index)
601- let prevIndexHeight = getHeightPriceByIndex((index - 1))
602- let priceByIndex = getPriceHistory(indexHeight)
603- if (isBlocked)
604- then emergencyShutdownFAIL()
605- else if ((swapStatus != "PENDING"))
606- then throw("swap has been already processed")
607- else if ((unlockHeight > height))
608- then throw((("please wait for: " + toString(unlockHeight)) + " block height to withdraw funds"))
609- else if (if (if ((index > priceIndex))
610- then true
611- else (unlockHeight > indexHeight))
612- then true
613- else if ((prevIndexHeight != 0))
614- then (prevIndexHeight >= unlockHeight)
615- else false)
616- then priceIndexFAIL(index, priceIndex, indexHeight, unlockHeight, prevIndexHeight)
617- else {
618- let withdrawTuple = calcWithdraw(swapType, inAmount, priceByIndex, neutrinoMetrics)
619- let outNetAmount = withdrawTuple._1
620- let outAssetId = withdrawTuple._2
621- let outSurfAmt = withdrawTuple._3
622- let unleaseAmt = withdrawTuple._5
623- let outFeeAmount = withdrawTuple._6
624- let outAmtGross = withdrawTuple._7
625- let withdrawPrice = withdrawTuple._8
626- if ((0 >= outAmtGross))
627- then throw("balance equals zero")
628- else {
629- let BR = asInt(neutrinoMetrics[nMetricIdxBR])
630- let state = [IntegerEntry(totalLockedByUserKEY(swapType, account), (totalLockedByUser - inAmount)), IntegerEntry(totalLockedKEY(swapType), (totalLocked - inAmount)), ScriptTransfer(userAddress, outNetAmount, outAssetId), StringEntry(swapKEY(account, swapTxId), finishSwapDATA(dataArray, withdrawPrice, outNetAmount, outFeeAmount, unlockHeight, index, withdrawTxId, outSurfAmt, BR, "TODO"))]
631- let surfCondition = if ((outSurfAmt > 0))
632- then {
633- let issueResult = invoke(auctionContract, "issueSurf", [outSurfAmt, account], nil)
634- if ((issueResult == issueResult))
635- then 0
636- else throw("Strict value is not equal to itself.")
637- }
638- else 0
639- if ((surfCondition == surfCondition))
640- then $Tuple3(state, AttachedPayment(outAssetId, outFeeAmount), unleaseAmt)
641- else throw("Strict value is not equal to itself.")
642- }
643- }
680+func calcWithdraw (swapType,inAmount,price,neutrinoMetrics,inAsset) = {
681+ let brProtected = valueOrElse(getInteger(this, brProtectedKEY()), BRPROTECTED)
682+ let BR = asInt(neutrinoMetrics[nMetricIdxBR])
683+ let reservesInUsd = asInt(neutrinoMetrics[nMetricTotalReservesUsd])
684+ let neutrinoSupply = asInt(neutrinoMetrics[nMetricIdxUsdnSupply])
685+ let basketInfo = asString(neutrinoMetrics[nMetricBasketInfo])
686+ let weightedPrice = asInt(neutrinoMetrics[nMetricWeightedPrice])
687+ let outDataTuple = if ((swapType == "outNeutrino"))
688+ then calcWithdrawT2U(inAmount, inAsset, basketInfo, weightedPrice)
689+ else if ((swapType == "outMulti"))
690+ then calcWithdrawU2Basket(inAmount, basketInfo, BR, reservesInUsd, neutrinoSupply)
691+ else throw(("Unsupported swap type " + swapType))
692+ let outAmtGross = outDataTuple._1
693+ let outAssetId = outDataTuple._2
694+ let inAmtToSurfPart = outDataTuple._3
695+ let inAssetId = outDataTuple._4
696+ let unleaseAmt = outDataTuple._5
697+ let withdrawPrice = outDataTuple._6
698+ let basketData = outDataTuple._11
699+ let netFeeData = outDataTuple._12
700+ let outNetAmt = if ((swapType == "outNeutrino"))
701+ then netFeeData[0]
702+ else 0
703+ let outFeeAmt = if ((swapType == "outNeutrino"))
704+ then netFeeData[1]
705+ else 0
706+ let outSurfAmt = if ((0 >= inAmtToSurfPart))
707+ then 0
708+ else asInt(asAnyList(invoke(mathContract, "surfFunctionREADONLY", [inAmtToSurfPart, inAssetId], nil))[bFuncIdxSurf])
709+ let withdrawBasket = if ((swapType == "outMulti"))
710+ then applyFeesBasket(basketData)
711+ else ""
712+ $Tuple9(outNetAmt, outAssetId, outSurfAmt, inAmtToSurfPart, unleaseAmt, outFeeAmt, outAmtGross, withdrawPrice, withdrawBasket)
644713 }
645714
646715
668737
669738
670739 @Callable(i)
671-func constructorV1 (neutrinoAssetIdPrm,bondAssetIdPrm,auctionContractPrm,liquidationContractPrm,rpdContractPrm,nodeOracleProviderPubKeyPrm,balanceWavesLockIntervalPrm,balanceNeutrinoLockIntervalPrm,minWavesSwapAmountPrm,minNeutrinoSwapAmountPrm,neutrinoOutFeePartPrm,wavesOutFeePartPrm,multiOutFeePartPrm) = {
740+func constructorV1 (neutrinoAssetIdPrm,bondAssetIdPrm,auctionContractPrm,liquidationContractPrm,rpdContractPrm,nodeOracleProviderPubKeyPrm,balanceWavesLockIntervalPrm,balanceNeutrinoLockIntervalPrm,minTokenSwapAmountPrm,minNeutrinoSwapAmountPrm,neutrinoOutFeePartPrm,wavesOutFeePartPrm) = {
672741 let checkCaller = thisOnly(i)
673742 if ((checkCaller == checkCaller))
674743 then if ((size(i.payments) != 0))
675744 then throw("no payments allowed")
676- else [StringEntry(NeutrinoAssetIdKey, neutrinoAssetIdPrm), StringEntry(BondAssetIdKey, bondAssetIdPrm), StringEntry(AuctionContractKey, auctionContractPrm), StringEntry(LiquidationContractKey, liquidationContractPrm), StringEntry(RPDContractKey, rpdContractPrm), StringEntry(NodeOracleProviderPubKeyKey, nodeOracleProviderPubKeyPrm), IntegerEntry(BalanceWavesLockIntervalKey, balanceWavesLockIntervalPrm), IntegerEntry(BalanceNeutrinoLockIntervalKey, balanceNeutrinoLockIntervalPrm), IntegerEntry(MinWavesSwapAmountKey, minWavesSwapAmountPrm), IntegerEntry(MinNeutrinoSwapAmountKey, minNeutrinoSwapAmountPrm), IntegerEntry(NeutrinoOutFeePartKey, neutrinoOutFeePartPrm), IntegerEntry(WavesOutFeePartKey, wavesOutFeePartPrm), IntegerEntry(MultiOutFeePartKey, multiOutFeePartPrm)]
745+ else [StringEntry(NeutrinoAssetIdKey, neutrinoAssetIdPrm), StringEntry(BondAssetIdKey, bondAssetIdPrm), StringEntry(AuctionContractKey, auctionContractPrm), StringEntry(LiquidationContractKey, liquidationContractPrm), StringEntry(RPDContractKey, rpdContractPrm), StringEntry(NodeOracleProviderPubKeyKey, nodeOracleProviderPubKeyPrm), IntegerEntry(BalanceWavesLockIntervalKey, balanceWavesLockIntervalPrm), IntegerEntry(BalanceNeutrinoLockIntervalKey, balanceNeutrinoLockIntervalPrm), IntegerEntry(MinTokenSwapAmountKey, minTokenSwapAmountPrm), IntegerEntry(MinNeutrinoSwapAmountKey, minNeutrinoSwapAmountPrm), IntegerEntry(NeutrinoOutFeePartKey, neutrinoOutFeePartPrm), IntegerEntry(WavesOutFeePartKey, wavesOutFeePartPrm)]
677746 else throw("Strict value is not equal to itself.")
678747 }
679748
692761
693762
694763 @Callable(i)
695-func swapWavesToNeutrino () = if ((size(i.payments) != 1))
696- then throw("swapWavesToNeutrino require only one payment")
764+func constructorV3 (assetWeights,dParam,kParam,bParam,deltaParam) = {
765+ let checkCaller = thisOnly(i)
766+ if ((checkCaller == checkCaller))
767+ then if ((size(i.payments) != 0))
768+ then throw("no payments allowed")
769+ else {
770+ func prepare (acc,item) = {
771+ let parts = split(item, "_")
772+ let assetId = parts[0]
773+ let weight = parseIntValue(parts[1])
774+ $Tuple2((acc._1 :+ assetId), (acc._2 :+ IntegerEntry(basketWeightByAssetKey(assetId), weight)))
775+ }
776+
777+ let r = {
778+ let $l = assetWeights
779+ let $s = size($l)
780+ let $acc0 = $Tuple2(nil, nil)
781+ func $f0_1 ($a,$i) = if (($i >= $s))
782+ then $a
783+ else prepare($a, $l[$i])
784+
785+ func $f0_2 ($a,$i) = if (($i >= $s))
786+ then $a
787+ else throw("List size exceeds 10")
788+
789+ $f0_2($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($acc0, 0), 1), 2), 3), 4), 5), 6), 7), 8), 9), 10)
790+ }
791+ (((((r._2 :+ IntegerEntry(basketDKey(), dParam)) :+ IntegerEntry(basketKKey(), kParam)) :+ IntegerEntry(basketBKey(), bParam)) :+ IntegerEntry(basketLogbDeltaKey(), log(bParam, 6, deltaParam, 6, 6, HALFUP))) :+ StringEntry(basketAssetsKey(), makeString(r._1, SEP)))
792+ }
793+ else throw("Strict value is not equal to itself.")
794+ }
795+
796+
797+
798+@Callable(i)
799+func swapTokenToNeutrino () = if ((size(i.payments) != 1))
800+ then throw("swapTokenToNeutrino require only one payment")
697801 else {
698802 let pmt = value(i.payments[0])
699- if (isDefined(pmt.assetId))
700- then throw("Only Waves token is allowed for swapping.")
803+ let basketAssets = valueOrElse(getString(basketAssetsKey()), "WAVES")
804+ let pmtAssetIdStr = toBase58String(valueOrElse(pmt.assetId, WAVESID))
805+ if (!(contains(basketAssets, pmtAssetIdStr)))
806+ then throw(((pmtAssetIdStr + " is not one of basket tokens:") + basketAssets))
701807 else {
702808 let userAddress = toString(i.caller)
703809 let txId58 = toBase58String(i.transactionId)
704- commonSwap("waves", pmt.amount, userAddress, txId58)
810+ commonSwap("outNeutrino", pmt.amount, pmtAssetIdStr, userAddress, txId58)
705811 }
706812 }
707813
717823 else {
718824 let userAddress = toString(i.caller)
719825 let txId58 = toBase58String(i.transactionId)
720- commonSwap("outMulti", pmt.amount, userAddress, txId58)
826+ commonSwap("outMulti", pmt.amount, toBase58String(neutrinoAssetId), userAddress, txId58)
721827 }
722828 }
723829
731837 else {
732838 let neutrinoMetrics = asAnyList(invoke(mathContract, "calcNeutinoMetricsREADONLY", nil, nil))
733839 let BR = asInt(neutrinoMetrics[nMetricIdxBR])
734- let commonTuple = commonWithdraw(account, index, swapTxId, txId, neutrinoMetrics)
735- let state = commonTuple._1
736- let fee = commonTuple._2
737- let unleaseAmt = commonTuple._3
738- let unleaseInvOrEmpty = invoke(this, "internalUnleaseAndLease", [unleaseAmt], nil)
739- if ((unleaseInvOrEmpty == unleaseInvOrEmpty))
740- then {
741- let gnsbtData = asAnyList(invoke(gnsbtControllerContract, "gnsbtInfoSYSREADONLY", ["", 0, 0], nil))
742- let gnsbtAmtTotal = asInt(gnsbtData[1])
743- let gnsbtAmtFromSurfTotal = asInt(asAnyList(gnsbtData[3])[3])
744- let surfFeeAmt1 = if ((gnsbtAmtTotal != 0))
745- then fraction(fee.amount, gnsbtAmtFromSurfTotal, gnsbtAmtTotal)
746- else 0
747- let surfFeeAmt2 = if ((gnsbtAmtTotal != 0))
748- then fraction(fee.amount, (PAULI - BR), PAULI)
749- else 0
750- let surfFeeAmt = max([surfFeeAmt1, surfFeeAmt2])
751- let nsbtFeeAmt = (fee.amount - surfFeeAmt)
752- let surfDeposit = if ((surfFeeAmt > 0))
753- then {
754- let surfInv = invoke(surfStakingContract, "deposit", nil, [AttachedPayment(fee.assetId, surfFeeAmt)])
755- if ((surfInv == surfInv))
756- then nil
757- else throw("Strict value is not equal to itself.")
758- }
759- else nil
760- if ((surfDeposit == surfDeposit))
761- then {
762- let nsbtDeposit = if ((nsbtFeeAmt > 0))
763- then {
764- let nsbtInv = invoke(nsbtStakingContract, "deposit", nil, [AttachedPayment(fee.assetId, nsbtFeeAmt)])
765- if ((nsbtInv == nsbtInv))
766- then nil
767- else throw("Strict value is not equal to itself.")
768- }
769- else nil
770- if ((nsbtDeposit == nsbtDeposit))
771- then state
772- else throw("Strict value is not equal to itself.")
773- }
774- else throw("Strict value is not equal to itself.")
775- }
776- else throw("Strict value is not equal to itself.")
840+ let userAddress = addressFromStringValue(account)
841+ let dataArray = swapDataFailOrREAD(account, swapTxId)
842+ let selfUnlockHeight = parseIntValue(dataArray[sIdxSelfUnlockHeight])
843+ let swapType = dataArray[sIdxSwapType]
844+ let inAmount = parseIntValue(dataArray[sIdxInAmount])
845+ let swapStatus = dataArray[sIdxStatus]
846+ let startHeight = parseIntValue(dataArray[sIdxStartHeight])
847+ let inAsset = dataArray[sIdxInAsset]
848+ let totalLocked = totalLockedREAD(swapType, inAsset)
849+ let totalLockedByUser = totalLockedByUserREAD(swapType, account, inAsset)
850+ let unlockHeight = selfUnlockHeight
851+ let indexHeight = getHeightPriceByIndex(index)
852+ let prevIndexHeight = getHeightPriceByIndex((index - 1))
853+ let priceByIndex = getPriceHistory(indexHeight)
854+ if (isBlocked)
855+ then emergencyShutdownFAIL()
856+ else if ((swapStatus != "PENDING"))
857+ then throw("swap has been already processed")
858+ else if ((unlockHeight > height))
859+ then throw((("please wait for: " + toString(unlockHeight)) + " block height to withdraw funds"))
860+ else if (if (if ((index > priceIndex))
861+ then true
862+ else (unlockHeight > indexHeight))
863+ then true
864+ else if ((prevIndexHeight != 0))
865+ then (prevIndexHeight >= unlockHeight)
866+ else false)
867+ then priceIndexFAIL(index, priceIndex, indexHeight, unlockHeight, prevIndexHeight)
868+ else {
869+ let withdrawTuple = calcWithdraw(swapType, inAmount, priceByIndex, neutrinoMetrics, inAsset)
870+ let outNetAmount = withdrawTuple._1
871+ let outAssetId = withdrawTuple._2
872+ let outSurfAmt = withdrawTuple._3
873+ let unleaseAmt = withdrawTuple._5
874+ let outFeeAmount = withdrawTuple._6
875+ let outAmtGross = withdrawTuple._7
876+ let withdrawPrice = withdrawTuple._8
877+ let withdrawBasket = withdrawTuple._9
878+ if ((0 >= outAmtGross))
879+ then throw("balance equals zero")
880+ else {
881+ let surfCondition = if ((outSurfAmt > 0))
882+ then {
883+ let issueResult = invoke(auctionContract, "issueSurf", [outSurfAmt, account], nil)
884+ if ((issueResult == issueResult))
885+ then 0
886+ else throw("Strict value is not equal to itself.")
887+ }
888+ else 0
889+ if ((surfCondition == surfCondition))
890+ then {
891+ let unleaseInvOrEmpty = invoke(this, "internalUnleaseAndLease", [unleaseAmt], nil)
892+ if ((unleaseInvOrEmpty == unleaseInvOrEmpty))
893+ then {
894+ let gnsbtData = asAnyList(invoke(gnsbtControllerContract, "gnsbtInfoSYSREADONLY", ["", 0, 0], nil))
895+ let gnsbtAmtTotal = asInt(gnsbtData[1])
896+ let gnsbtAmtFromSurfTotal = asInt(asAnyList(gnsbtData[3])[3])
897+ let commonActions = [IntegerEntry(totalLockedByUserKEY(swapType, account, inAsset), (totalLockedByUser - inAmount)), IntegerEntry(totalLockedKEY(swapType, inAsset), (totalLocked - inAmount)), StringEntry(swapKEY(account, swapTxId), finishSwapDATA(dataArray, withdrawPrice, outNetAmount, outFeeAmount, unlockHeight, index, txId, outSurfAmt, BR, withdrawBasket, inAsset))]
898+ if ((swapType == "outMulti"))
899+ then {
900+ let basket = split(withdrawBasket, "_")
901+ func looper (acc,item) = {
902+ let it = split(item, LISTSEP)
903+ let tempAssetId = fromBase58String(it[0])
904+ let assetId = if ((tempAssetId == WAVESID))
905+ then unit
906+ else tempAssetId
907+ let netAmount = parseIntValue(it[2])
908+ let feeAmount = parseIntValue(it[3])
909+ let surfAmt1 = if ((gnsbtAmtTotal != 0))
910+ then fraction(feeAmount, gnsbtAmtFromSurfTotal, gnsbtAmtTotal)
911+ else 0
912+ let surfAmt2 = if ((gnsbtAmtTotal != 0))
913+ then fraction(feeAmount, (MULT6 - BR), MULT6)
914+ else 0
915+ let surfAmt = max([surfAmt1, surfAmt2])
916+ let nsbtAmt = (feeAmount - surfAmt)
917+ $Tuple5((acc._1 :+ ScriptTransfer(userAddress, netAmount, assetId)), (acc._2 :+ AttachedPayment(assetId, surfAmt)), (acc._3 :+ AttachedPayment(assetId, nsbtAmt)), (acc._4 + surfAmt), (acc._5 + nsbtAmt))
918+ }
919+
920+ let r = {
921+ let $l = basket
922+ let $s = size($l)
923+ let $acc0 = $Tuple5(nil, nil, nil, 0, 0)
924+ func $f0_1 ($a,$i) = if (($i >= $s))
925+ then $a
926+ else looper($a, $l[$i])
927+
928+ func $f0_2 ($a,$i) = if (($i >= $s))
929+ then $a
930+ else throw("List size exceeds 10")
931+
932+ $f0_2($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($acc0, 0), 1), 2), 3), 4), 5), 6), 7), 8), 9), 10)
933+ }
934+ let surfDeposit = if ((r._4 > 0))
935+ then {
936+ let surfInv = invoke(surfStakingContract, "deposit", nil, r._2)
937+ if ((surfInv == surfInv))
938+ then 0
939+ else throw("Strict value is not equal to itself.")
940+ }
941+ else 0
942+ if ((surfDeposit == surfDeposit))
943+ then {
944+ let nsbtDeposit = if ((r._5 > 0))
945+ then {
946+ let nsbtInv = invoke(nsbtStakingContract, "deposit", nil, r._3)
947+ if ((nsbtInv == nsbtInv))
948+ then 0
949+ else throw("Strict value is not equal to itself.")
950+ }
951+ else 0
952+ if ((nsbtDeposit == nsbtDeposit))
953+ then (commonActions ++ r._1)
954+ else throw("Strict value is not equal to itself.")
955+ }
956+ else throw("Strict value is not equal to itself.")
957+ }
958+ else {
959+ let fee = AttachedPayment(outAssetId, outFeeAmount)
960+ let surfFeeAmt1 = if ((gnsbtAmtTotal != 0))
961+ then fraction(fee.amount, gnsbtAmtFromSurfTotal, gnsbtAmtTotal)
962+ else 0
963+ let surfFeeAmt2 = if ((gnsbtAmtTotal != 0))
964+ then fraction(fee.amount, (MULT6 - BR), MULT6)
965+ else 0
966+ let surfFeeAmt = max([surfFeeAmt1, surfFeeAmt2])
967+ let nsbtFeeAmt = (fee.amount - surfFeeAmt)
968+ let surfDeposit = if ((surfFeeAmt > 0))
969+ then {
970+ let surfInv = invoke(surfStakingContract, "deposit", nil, [AttachedPayment(fee.assetId, surfFeeAmt)])
971+ if ((surfInv == surfInv))
972+ then nil
973+ else throw("Strict value is not equal to itself.")
974+ }
975+ else nil
976+ if ((surfDeposit == surfDeposit))
977+ then {
978+ let nsbtDeposit = if ((nsbtFeeAmt > 0))
979+ then {
980+ let nsbtInv = invoke(nsbtStakingContract, "deposit", nil, [AttachedPayment(fee.assetId, nsbtFeeAmt)])
981+ if ((nsbtInv == nsbtInv))
982+ then nil
983+ else throw("Strict value is not equal to itself.")
984+ }
985+ else nil
986+ if ((nsbtDeposit == nsbtDeposit))
987+ then (commonActions :+ ScriptTransfer(userAddress, outNetAmount, outAssetId))
988+ else throw("Strict value is not equal to itself.")
989+ }
990+ else throw("Strict value is not equal to itself.")
991+ }
992+ }
993+ else throw("Strict value is not equal to itself.")
994+ }
995+ else throw("Strict value is not equal to itself.")
996+ }
997+ }
777998 }
778999 }
7791000
8191040 if ((i.callerPublicKey != mngPub))
8201041 then throw("approveLeasings not authorized")
8211042 else {
822- let $t03577035832 = readNodeInfo(0)
823- let nAddr0 = $t03577035832._1
824- let lAmtKEY0 = $t03577035832._2
825- let lAmt0 = $t03577035832._3
826- let lIdKEY0 = $t03577035832._4
827- let lId0 = $t03577035832._5
1043+ let $t04305243114 = readNodeInfo(0)
1044+ let nAddr0 = $t04305243114._1
1045+ let lAmtKEY0 = $t04305243114._2
1046+ let lAmt0 = $t04305243114._3
1047+ let lIdKEY0 = $t04305243114._4
1048+ let lId0 = $t04305243114._5
8281049 let newL0 = Lease(nAddr0, (lAmt0 - (lAmt * expCount)))
8291050 let validation = invoke(nodeRegAddr, "validateAndApproveLeasings", [nListS], nil)
8301051 if ((validation == validation))
8691090 then throw("rebalanceLeasings not authorized")
8701091 else {
8711092 let unleaseAmt = ((amount / size(nList)) + 1)
872- let $t03707237134 = readNodeInfo(0)
873- let nAddr0 = $t03707237134._1
874- let lAmtKEY0 = $t03707237134._2
875- let lAmt0 = $t03707237134._3
876- let lIdKEY0 = $t03707237134._4
877- let lId0 = $t03707237134._5
1093+ let $t04435444416 = readNodeInfo(0)
1094+ let nAddr0 = $t04435444416._1
1095+ let lAmtKEY0 = $t04435444416._2
1096+ let lAmt0 = $t04435444416._3
1097+ let lIdKEY0 = $t04435444416._4
1098+ let lId0 = $t04435444416._5
8781099 let newL0 = Lease(nAddr0, (lAmt0 + (unleaseAmt * size(nList))))
8791100 func forEachNodeDoUnlease (a,i) = {
8801101 let node = nList[i]
9311152
9321153
9331154 @Callable(i)
934-func calcWithdrawResultSYSREADONLY (swapType,inAmount,price) = {
1155+func calcWithdrawResultSYSREADONLY (swapType,inAmount,price,inAsset) = {
9351156 let neutrinoMetrics = asAnyList(invoke(mathContract, "calcNeutinoMetricsREADONLY", nil, nil))
936- $Tuple2(nil, calcWithdraw(swapType, inAmount, price, neutrinoMetrics))
1157+ $Tuple2(nil, calcWithdraw(swapType, inAmount, price, neutrinoMetrics, inAsset))
9371158 }
9381159
9391160
9561177 let oldLeaseIdKey = getLeaseIdByAddressKey(oldAddrStr)
9571178 let oldLeaseAmtKey = getLeaseAmountByAddressKey(oldAddrStr)
9581179 let leaseAmt = getIntegerValue(oldLeaseAmtKey)
959- let newLeaseIdKey = getLeaseIdByAddressKey(oldAddrStr)
960- let newLeaseAmtKey = getLeaseAmountByAddressKey(oldAddrStr)
1180+ let newLeaseIdKey = getLeaseIdByAddressKey(newAddrStr)
1181+ let newLeaseAmtKey = getLeaseAmountByAddressKey(newAddrStr)
9611182 let newLease = Lease(addressFromStringValue(newAddrStr), leaseAmt)
9621183 let updatedGroupNodeListS = makeString(split(groupNodeListS, oldAddrStr), newAddrStr)
9631184 $Tuple2([LeaseCancel(getBinaryValue(oldLeaseIdKey)), DeleteEntry(oldLeaseIdKey), DeleteEntry(oldLeaseAmtKey), StringEntry(groupKey, updatedGroupNodeListS), newLease, BinaryEntry(newLeaseIdKey, lcalc(newLease)), IntegerEntry(newLeaseAmtKey, leaseAmt)], unit)
Full:
OldNewDifferences
11 {-# STDLIB_VERSION 6 #-}
22 {-# SCRIPT_TYPE ACCOUNT #-}
33 {-# CONTENT_TYPE DAPP #-}
44 func getStringOrFail (address,key) = valueOrErrorMessage(getString(address, key), makeString(["mandatory ", toString(address), ".", key, " is not defined"], ""))
55
66
77 func lcalc (l) = calculateLeaseId(l)
88
99
1010 func getNumberByKey (key) = valueOrElse(getInteger(this, key), 0)
1111
1212
1313 func getStringByKey (key) = valueOrElse(getString(this, key), "")
1414
1515
1616 func getBoolByKey (key) = valueOrElse(getBoolean(this, key), false)
1717
1818
1919 func getNumberByAddressAndKey (address,key) = valueOrElse(getInteger(address, key), 0)
2020
2121
2222 func getStringByAddressAndKey (address,key) = valueOrElse(getString(addressFromStringValue(address), key), "")
2323
2424
2525 func getBoolByAddressAndKey (address,key) = valueOrElse(getBoolean(address, key), false)
2626
2727
2828 func asAnyList (v) = match v {
2929 case l: List[Any] =>
3030 l
3131 case _ =>
3232 throw("fail to cast into List[Any]")
3333 }
3434
3535
3636 func asString (v) = match v {
3737 case s: String =>
3838 s
3939 case _ =>
4040 throw("fail to cast into String")
4141 }
4242
4343
4444 func asInt (v) = match v {
4545 case i: Int =>
4646 i
4747 case _ =>
4848 throw("fail to cast into Int")
4949 }
5050
5151
5252 func asBytes (val) = match val {
5353 case valByte: ByteVector =>
5454 valByte
5555 case _ =>
5656 throw("fail to cast into ByteVector")
5757 }
5858
5959
6060 func asPayment (v) = match v {
6161 case p: AttachedPayment =>
6262 p
6363 case _ =>
6464 throw("fail to cast into AttachedPayment")
6565 }
6666
6767
6868 func asSwapParamsSTRUCT (v) = match v {
6969 case struct: (Int, Int, Int, Int, Int, Int, Int) =>
7070 struct
7171 case _ =>
7272 throw("fail to cast into Tuple5 ints")
7373 }
7474
7575
7676 let SEP = "__"
7777
7878 let LISTSEP = ":"
7979
80-let WAVELET = 100000000
80+let MULT8 = 100000000
8181
82-let PAULI = 1000000
83-
84-let PRICELET = 1000000
82+let MULT6 = 1000000
8583
8684 let DEFAULTSWAPFEE = 20000
8785
8886 let BRPROTECTED = 100000
87+
88+let WAVESID = base58'WAVES'
8989
9090 let IdxNetAmount = 0
9191
9292 let IdxFeeAmount = 1
9393
9494 let IdxGrossAmount = 2
9595
9696 let IdxControlCfgNeutrinoDapp = 1
9797
9898 let IdxControlCfgAuctionDapp = 2
9999
100100 let IdxControlCfgRpdDapp = 3
101101
102102 let IdxControlCfgMathDapp = 4
103103
104104 let IdxControlCfgLiquidationDapp = 5
105105
106106 let IdxControlCfgRestDapp = 6
107107
108108 let IdxControlCfgNodeRegistryDapp = 7
109109
110110 let IdxControlCfgNsbtStakingDapp = 8
111111
112112 let IdxControlCfgMediatorDapp = 9
113113
114114 let IdxControlCfgSurfStakingDapp = 10
115115
116116 let IdxControlCfgGnsbtControllerDapp = 11
117117
118118 let IdxControlCfgRestV2Dapp = 12
119119
120120 let IdxControlCfgGovernanceDapp = 13
121121
122122 func keyControlAddress () = "%s%s__config__controlAddress"
123123
124124
125125 func keyControlCfg () = "%s__controlConfig"
126126
127127
128128 func readControlCfgOrFail (control) = split_4C(getStringOrFail(control, keyControlCfg()), SEP)
129129
130130
131131 func getContractAddressOrFail (controlCfg,idx) = valueOrErrorMessage(addressFromString(controlCfg[idx]), ("Control cfg doesn't contain address at index " + toString(idx)))
132132
133133
134134 let controlContract = addressFromStringValue(valueOrElse(getString(this, keyControlAddress()), "3N4NS7d4Jo9a6F14LiFUKKYVdUkkf2eP4Zx"))
135135
136136 let controlCfg = readControlCfgOrFail(controlContract)
137137
138138 let mathContract = getContractAddressOrFail(controlCfg, IdxControlCfgMathDapp)
139139
140140 let nsbtStakingContract = getContractAddressOrFail(controlCfg, IdxControlCfgNsbtStakingDapp)
141141
142142 let surfStakingContract = getContractAddressOrFail(controlCfg, IdxControlCfgSurfStakingDapp)
143143
144144 let gnsbtControllerContract = getContractAddressOrFail(controlCfg, IdxControlCfgGnsbtControllerDapp)
145145
146146 let auctionContract = getContractAddressOrFail(controlCfg, IdxControlCfgAuctionDapp)
147147
148148 let nodeRegistryContract = getContractAddressOrFail(controlCfg, IdxControlCfgNodeRegistryDapp)
149149
150150 let govContract = getContractAddressOrFail(controlCfg, IdxControlCfgGovernanceDapp)
151151
152152 let NeutrinoAssetIdKey = "neutrino_asset_id"
153153
154154 let BondAssetIdKey = "bond_asset_id"
155155
156156 let AuctionContractKey = "auction_contract"
157157
158158 let NsbtStakingContractKey = "nsbtStakingContract"
159159
160160 let LiquidationContractKey = "liquidation_contract"
161161
162162 let RPDContractKey = "rpd_contract"
163163
164164 let ContolContractKey = "control_contract"
165165
166166 let MathContractKey = "math_contract"
167167
168168 let BalanceWavesLockIntervalKey = "balance_waves_lock_interval"
169169
170170 let BalanceNeutrinoLockIntervalKey = "balance_neutrino_lock_interval"
171171
172-let MinWavesSwapAmountKey = "min_waves_swap_amount"
172+let MinTokenSwapAmountKey = "min_outNeutrino_swap_amount"
173173
174-let MinNeutrinoSwapAmountKey = "min_neutrino_swap_amount"
174+let MinNeutrinoSwapAmountKey = "min_outMulti_swap_amount"
175175
176176 let NodeOracleProviderPubKeyKey = "node_oracle_provider"
177177
178178 let NeutrinoOutFeePartKey = "neutrinoOut_swap_feePart"
179179
180180 let WavesOutFeePartKey = "wavesOut_swap_feePart"
181-
182-let MultiOutFeePartKey = "multiOut_swap_feePart"
183181
184182 func keyNodeRegistry (address) = ("%s__" + address)
185183
186184
187185 let PriceKey = "price"
188186
189187 let PriceIndexKey = "price_index"
190188
191189 let IsBlockedKey = "is_blocked"
192190
193191 func getPriceHistoryKey (block) = ((PriceKey + "_") + toString(block))
194192
195193
196194 func getHeightPriceByIndexKey (index) = ((PriceIndexKey + "_") + toString(index))
197195
198196
199197 func getStakingNodeByIndex (idx) = getStringByKey(makeString(["%s%d%s", "lease", toString(idx), "nodeAddress"], SEP))
200198
201199
202200 func getStakingNodeAddressByIndex (idx) = addressFromStringValue(getStakingNodeByIndex(idx))
203201
204202
205-func getReservedAmountForSponsorship () = valueOrElse(getInteger(this, makeString(["%s%s", "lease", "sponsorshipWavesReserve"], SEP)), (1000 * WAVELET))
203+func getReservedAmountForSponsorship () = valueOrElse(getInteger(this, makeString(["%s%s", "lease", "sponsorshipWavesReserve"], SEP)), (1000 * MULT8))
206204
207205
208206 func getBalanceUnlockBlockKey (owner) = ("balance_unlock_block_" + owner)
209207
210208
211209 func getLeaseIdKey (nodeIndex) = makeString(["%s%d%s", "lease", toString(nodeIndex), "id"], SEP)
212210
213211
214212 func getLeaseIdByAddressKey (nodeAddress) = makeString(["%s%s%s", "leaseByAddress", nodeAddress, "id"], SEP)
215213
216214
217215 func getLeaseAmountKey (nodeIndex) = makeString(["%s%d%s", "lease", toString(nodeIndex), "amount"], SEP)
218216
219217
220218 func getLeaseAmountByAddressKey (nodeAddress) = makeString(["%s%s%s", "leaseByAddress", nodeAddress, "amount"], SEP)
221219
222220
223221 func getLeaseGroupNodeListKey (groupNum) = makeString(["%s%d%s", "leaseGroup", toString(groupNum), "nodeList"], SEP)
224222
225223
226224 func minSwapAmountKEY (swapType) = (("min_" + swapType) + "_swap_amount")
227225
228226
229-func totalLockedKEY (swapType) = ("balance_lock_" + swapType)
227+func totalLockedKEY (swapType,assetId) = makeString(["%s%s%s", "balanceLock", swapType, assetId], SEP)
230228
231229
232-func totalLockedByUserKEY (swapType,owner) = makeString(["balance_lock", swapType, owner], "_")
230+func totalLockedByUserKEY (swapType,owner,assetId) = makeString(["%s%s%s%s", "balanceLock", swapType, owner, assetId], "_")
233231
234232
235233 func balanceLockIntervalKEY (swapType) = (("balance_" + swapType) + "_lock_interval")
236234
237235
238236 func nodeBalanceLockIntervalKEY () = "balance_node_lock_interval"
239237
240238
241-func outFeePartKEY (swapType) = (swapType + "Out_swap_feePart")
242-
243-
244239 func swapsTimeframeKEY () = "swaps_timeframe"
245240
246241
247242 func brProtectedKEY () = "min_BR_protection_level"
243+
244+
245+func basketAssetsKey () = "%s%s__common__basketAssets"
246+
247+
248+func basketDKey () = "%s%s__common__basketD"
249+
250+
251+func basketKKey () = "%s%s__common__basketK"
252+
253+
254+func basketBKey () = "%s%s__common__basketB"
255+
256+
257+func basketLogbDeltaKey () = "%s%s__common__basketLogbDelta"
258+
259+
260+func basketWeightByAssetKey (assetId) = ("%s%s%s__common__weightByAsset__" + assetId)
248261
249262
250263 func minSwapAmountREAD (swapType) = valueOrElse(getInteger(this, minSwapAmountKEY(swapType)), 0)
251264
252265
253266 func swapsTimeframeREAD () = valueOrElse(getInteger(this, swapsTimeframeKEY()), 1440)
254267
255268
256-func totalLockedREAD (swapType) = valueOrElse(getInteger(this, totalLockedKEY(swapType)), 0)
269+func totalLockedREAD (swapType,assetId) = valueOrElse(getInteger(this, totalLockedKEY(swapType, assetId)), 0)
257270
258271
259-func totalLockedByUserREAD (swapType,owner) = valueOrElse(getInteger(this, totalLockedByUserKEY(swapType, owner)), 0)
272+func totalLockedByUserREAD (swapType,owner,assetId) = valueOrElse(getInteger(this, totalLockedByUserKEY(swapType, owner, assetId)), 0)
260273
261274
262275 func balanceLockIntervalREAD (swapType) = valueOrElse(getInteger(this, balanceLockIntervalKEY(swapType)), 1440)
263276
264277
265278 func nodeBalanceLockIntervalREAD () = valueOrElse(getInteger(this, nodeBalanceLockIntervalKEY()), 1)
266279
267280
268281 func keySwapUserSpentInPeriod (userAddress) = makeString(["%s%s", "swapUserSpentInPeriod", userAddress], SEP)
269282
270283
271284 func keyUserLastSwapHeight (userAddress) = makeString(["%s%s", "userLastSwapHeight", userAddress], SEP)
272285
273286
274-func convertNeutrinoToWaves (amount,price) = fraction(fraction(amount, PRICELET, price), WAVELET, PAULI)
287+func convertNeutrinoToWaves (amount,price) = fraction(amount, MULT8, price)
275288
276289
277-func convertWavesToNeutrino (amount,price) = fraction(fraction(amount, price, PRICELET), PAULI, WAVELET)
290+func convertWavesToNeutrino (amount,price) = fraction(amount, price, MULT8)
278291
279292
280293 func convertWavesToBond (amount,price) = convertWavesToNeutrino(amount, price)
281294
282295
283296 func convertJsonArrayToList (jsonArray) = split(jsonArray, ",")
297+
298+
299+func convertBasketItemToNeutrino (inAmount,inAsset,basketInfoStr,neutrinoPrice) = {
300+ let basketInfo = split(basketInfoStr, "_")
301+ func findItem (acc,item) = {
302+ let bItem = split(item, LISTSEP)
303+ if ((bItem[0] == inAsset))
304+ then bItem
305+ else acc
306+ }
307+
308+ let it = {
309+ let $l = basketInfo
310+ let $s = size($l)
311+ let $acc0 = nil
312+ func $f0_1 ($a,$i) = if (($i >= $s))
313+ then $a
314+ else findItem($a, $l[$i])
315+
316+ func $f0_2 ($a,$i) = if (($i >= $s))
317+ then $a
318+ else throw("List size exceeds 10")
319+
320+ $f0_2($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($acc0, 0), 1), 2), 3), 4), 5), 6), 7), 8), 9), 10)
321+ }
322+ let dlog6 = valueOrElse(getInteger(basketLogbDeltaKey()), -18820642)
323+ let d6 = valueOrElse(getInteger(basketDKey()), 50000)
324+ let b6 = valueOrElse(getInteger(basketBKey()), 1250000)
325+ let w6 = valueOrElse(getInteger(basketWeightByAssetKey(inAsset)), 166666)
326+ let p6 = ((dlog6 - w6) + parseIntValue(it[1]))
327+ let pp6 = pow(b6, 6, p6, 6, 6, HALFUP)
328+ let outAmountGross = fraction(inAmount, parseIntValue(it[2]), if ((neutrinoPrice == 0))
329+ then MULT8
330+ else (neutrinoPrice * 100))
331+ let multiplier = ((MULT6 + d6) - pp6)
332+ if ((0 >= multiplier))
333+ then throw(("Net amount is non-positive, multiplier = " + toString(multiplier)))
334+ else {
335+ let netAmount = fraction(outAmountGross, multiplier, MULT6)
336+ let feeAmount = if ((netAmount >= outAmountGross))
337+ then 0
338+ else (outAmountGross - netAmount)
339+[outAmountGross, netAmount, feeAmount]
340+ }
341+ }
342+
343+
344+func convertNeutrinoToBasket (amount,basketInfoStr,neutrinoSupply) = {
345+ let basketInfo = split(basketInfoStr, "_")
346+ func conv (acc,item) = {
347+ let parts = split(item, LISTSEP)
348+ let assetId = parts[0]
349+ let share = parseIntValue(parts[1])
350+ let price = parseIntValue(parts[2])
351+ let reserv = parseIntValue(parts[3])
352+ if ((neutrinoSupply == 0))
353+ then throw("Attempt to burn neutrino at zero supply")
354+ else {
355+ let outAmount = fraction(amount, reserv, neutrinoSupply)
356+ let wavesTuple = if ((assetId == "WAVES"))
357+ then $Tuple2(outAmount, price)
358+ else $Tuple2(acc._2, acc._3)
359+ $Tuple3((acc._1 :+ $Tuple4(assetId, outAmount, price, share)), wavesTuple._1, wavesTuple._2)
360+ }
361+ }
362+
363+ let $l = basketInfo
364+ let $s = size($l)
365+ let $acc0 = $Tuple3(nil, 0, 0)
366+ func $f0_1 ($a,$i) = if (($i >= $s))
367+ then $a
368+ else conv($a, $l[$i])
369+
370+ func $f0_2 ($a,$i) = if (($i >= $s))
371+ then $a
372+ else throw("List size exceeds 10")
373+
374+ $f0_2($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($acc0, 0), 1), 2), 3), 4), 5), 6), 7), 8), 9), 10)
375+ }
284376
285377
286378 func minSwapAmountFAIL (swapType,minSwapAmount) = throw(((("The specified amount in " + swapType) + " swap is less than the required minimum of ") + toString(minSwapAmount)))
287379
288380
289381 func emergencyShutdownFAIL () = throw("contract is blocked by EMERGENCY SHUTDOWN actions untill reactivation by emergency oracles")
290382
291383
292384 func priceIndexFAIL (index,priceIndex,indexHeight,unlockHeight,prevIndexHeight) = throw(((((((((("invalid price history index: index=" + toString(index)) + " priceIndex=") + toString(priceIndex)) + " indexHeight=") + toString(indexHeight)) + " unlockHeight=") + toString(unlockHeight)) + " prevIndexHeight=") + toString(prevIndexHeight)))
293385
294386
295387 let neutrinoAssetId = fromBase58String(getStringByKey(NeutrinoAssetIdKey))
296388
297389 let priceIndex = getNumberByAddressAndKey(controlContract, PriceIndexKey)
298390
299391 let isBlocked = getBoolByAddressAndKey(controlContract, IsBlockedKey)
300392
301393 let nodeOracleProviderPubKey = fromBase58String(getStringByKey(NodeOracleProviderPubKeyKey))
302394
303395 let bondAssetId = fromBase58String("F3iaxzruFeKujfVfYSZEkejpjh67wmRfPCRHiNmWKp3Z")
304396
305397 let deprecatedBondAssetId = fromBase58String("975akZBfnMj513U7MZaHKzQrmsEx5aE3wdWKTrHBhbjF")
306398
307399 let neutrinoContract = this
308400
309401 let currentPrice = getNumberByAddressAndKey(controlContract, PriceKey)
310402
311403 func checkIsValidMinSponsoredFee (tx) = {
312404 let MINTRANSFERFEE = 100000
313405 let SponsoredFeeUpperBound = 1000
314406 let realNeutrinoFee = convertWavesToNeutrino(MINTRANSFERFEE, currentPrice)
315407 let minNeutrinoFee = (realNeutrinoFee * 2)
316408 let maxNeutrinoFee = fraction(realNeutrinoFee, SponsoredFeeUpperBound, 100)
317409 let inputFee = value(tx.minSponsoredAssetFee)
318410 if (if ((inputFee >= minNeutrinoFee))
319411 then (maxNeutrinoFee >= inputFee)
320412 else false)
321413 then (tx.assetId == neutrinoAssetId)
322414 else false
323415 }
324416
325417
326418 func getPriceHistory (block) = getNumberByAddressAndKey(controlContract, getPriceHistoryKey(block))
327419
328420
329421 func getHeightPriceByIndex (index) = getNumberByAddressAndKey(controlContract, getHeightPriceByIndexKey(index))
330422
331423
332424 func keyLockParamUserAmount (userAddress) = makeString(["%s%s%s", "paramByUser", userAddress, "amount"], SEP)
333425
334426
335427 let sIdxSwapType = 1
336428
337429 let sIdxStatus = 2
338430
339431 let sIdxInAmount = 3
340432
341433 let sIdxStartHeight = 7
342434
343435 let sIdxStartTimestamp = 8
344436
345437 let sIdxSelfUnlockHeight = 11
346438
347439 let sIdxMinRand = 15
348440
349441 let sIdxMaxRand = 16
350442
443+let sIdxInAsset = 20
444+
351445 func swapKEY (userAddress,txId) = makeString(["%s%s", userAddress, txId], SEP)
352446
353447
354-func strSwapDATA (swapType,status,inAmount,price,outNetAmount,outFeeAmount,startHeight,startTimestamp,endHeight,endTimestamp,selfUnlockHeight,randUnlockHeight,index,withdrawTxId,randMin,randMax,outSurfAmt,br,basketData) = makeString(["%s%s%d%d%d%d%d%d%d%d%d%d%d%s%d%d%d%d%s", swapType, status, inAmount, price, outNetAmount, outFeeAmount, startHeight, startTimestamp, endHeight, endTimestamp, selfUnlockHeight, randUnlockHeight, index, withdrawTxId, randMin, randMax, outSurfAmt, br, basketData], SEP)
448+func strSwapDATA (swapType,status,inAmount,price,outNetAmount,outFeeAmount,startHeight,startTimestamp,endHeight,endTimestamp,selfUnlockHeight,randUnlockHeight,index,withdrawTxId,randMin,randMax,outSurfAmt,br,basketData,inAsset) = makeString(["%s%s%d%d%d%d%d%d%d%d%d%d%d%s%d%d%d%d%s%s", swapType, status, inAmount, price, outNetAmount, outFeeAmount, startHeight, startTimestamp, endHeight, endTimestamp, selfUnlockHeight, randUnlockHeight, index, withdrawTxId, randMin, randMax, outSurfAmt, br, basketData, inAsset], SEP)
355449
356450
357-func pendingSwapDATA (swapType,inAssetAmount,selfUnlockHeight) = strSwapDATA(swapType, "PENDING", toString(inAssetAmount), "0", "0", "0", toString(height), toString(lastBlock.timestamp), "0", "0", toString(selfUnlockHeight), "0", "0", "NULL", "0", "0", "0", "0", "")
451+func pendingSwapDATA (swapType,inAssetAmount,selfUnlockHeight,inAsset) = strSwapDATA(swapType, "PENDING", toString(inAssetAmount), "0", "0", "0", toString(height), toString(lastBlock.timestamp), "0", "0", toString(selfUnlockHeight), "0", "0", "NULL", "0", "0", "0", "0", "", inAsset)
358452
359453
360-func finishSwapDATA (dataArray,price,outNetAmount,outFeeAmount,randUnlockHeight,index,withdrawTxId,outSurfAmt,br,basketData) = strSwapDATA(dataArray[sIdxSwapType], "FINISHED", dataArray[sIdxInAmount], toString(price), toString(outNetAmount), toString(outFeeAmount), dataArray[sIdxStartHeight], dataArray[sIdxStartTimestamp], toString(height), toString(lastBlock.timestamp), dataArray[sIdxSelfUnlockHeight], toString(randUnlockHeight), toString(index), withdrawTxId, dataArray[sIdxMinRand], dataArray[sIdxMaxRand], toString(outSurfAmt), toString(br), basketData)
454+func finishSwapDATA (dataArray,price,outNetAmount,outFeeAmount,randUnlockHeight,index,withdrawTxId,outSurfAmt,br,basketData,inAsset) = strSwapDATA(dataArray[sIdxSwapType], "FINISHED", dataArray[sIdxInAmount], toString(price), toString(outNetAmount), toString(outFeeAmount), dataArray[sIdxStartHeight], dataArray[sIdxStartTimestamp], toString(height), toString(lastBlock.timestamp), dataArray[sIdxSelfUnlockHeight], toString(randUnlockHeight), toString(index), withdrawTxId, dataArray[sIdxMinRand], dataArray[sIdxMaxRand], toString(outSurfAmt), toString(br), basketData, inAsset)
361455
362456
363457 func swapDataFailOrREAD (userAddress,swapTxId) = {
364458 let swapKey = swapKEY(userAddress, swapTxId)
365- split(valueOrErrorMessage(getString(this, swapKey), ("no swap data for " + swapKey)), SEP)
459+ split_4C(valueOrErrorMessage(getString(swapKey), ("no swap data for " + swapKey)), SEP)
366460 }
367461
368462
369463 func applyFees (amountOutGross,inAmtToSURF,feePart) = {
370- let feeAmount = fraction(amountOutGross, feePart, PAULI)
464+ let feeAmount = fraction(amountOutGross, feePart, MULT6)
371465 [(amountOutGross - feeAmount), feeAmount]
466+ }
467+
468+
469+func applyFeesBasket (basketOut) = {
470+ let k6 = valueOrElse(getInteger(basketKKey()), MULT6)
471+ let d6 = valueOrElse(getInteger(basketDKey()), 50000)
472+ let feePart = fraction(k6, d6, MULT6)
473+ func feeApplier (acc,item) = {
474+ let feeAmount = fraction(item._2, feePart, MULT6)
475+ (acc :+ makeString([item._1, toString(item._3), toString((item._2 - feeAmount)), toString(feeAmount), toString(item._4)], LISTSEP))
476+ }
477+
478+ let a = {
479+ let $l = basketOut
480+ let $s = size($l)
481+ let $acc0 = nil
482+ func $f0_1 ($a,$i) = if (($i >= $s))
483+ then $a
484+ else feeApplier($a, $l[$i])
485+
486+ func $f0_2 ($a,$i) = if (($i >= $s))
487+ then $a
488+ else throw("List size exceeds 10")
489+
490+ $f0_2($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($acc0, 0), 1), 2), 3), 4), 5), 6), 7), 8), 9), 10)
491+ }
492+ makeString(a, SEP)
372493 }
373494
374495
375496 func abs (x) = if ((0 > x))
376497 then -(x)
377498 else x
378499
379500
380501 func selectNode (unleaseAmount) = {
381502 let amountToLease = ((wavesBalance(neutrinoContract).available - unleaseAmount) - getReservedAmountForSponsorship())
382503 let oldLeased0 = getNumberByKey(getLeaseAmountKey(0))
383504 let oldLeased1 = getNumberByKey(getLeaseAmountKey(1))
384505 let newLeased0 = (amountToLease + oldLeased0)
385506 let newLeased1 = (amountToLease + oldLeased1)
386507 if (if ((newLeased0 > 0))
387508 then true
388509 else (newLeased1 > 0))
389510 then {
390511 let delta0 = abs((newLeased0 - oldLeased1))
391512 let delta1 = abs((newLeased1 - oldLeased0))
392513 if ((delta1 >= delta0))
393514 then $Tuple2(0, newLeased0)
394515 else $Tuple2(1, newLeased1)
395516 }
396517 else $Tuple2(-1, 0)
397518 }
398519
399520
400521 func thisOnly (i) = if ((i.caller != this))
401522 then throw("Permission denied: this contract only allowed")
402523 else true
403524
404525
405526 func prepareUnleaseAndLease (unleaseAmount) = {
406527 let nodeTuple = selectNode(unleaseAmount)
407528 let nodeIndex = nodeTuple._1
408529 let newLeaseAmount = nodeTuple._2
409530 if ((newLeaseAmount > 0))
410531 then {
411532 let leaseIdKey = getLeaseIdKey(nodeIndex)
412533 let oldLease = getBinary(this, leaseIdKey)
413534 let unleaseOrEmpty = if (isDefined(oldLease))
414535 then [LeaseCancel(value(oldLease))]
415536 else nil
416537 let leaseAmountKey = getLeaseAmountKey(nodeIndex)
417538 let lease = Lease(getStakingNodeAddressByIndex(nodeIndex), newLeaseAmount)
418539 (unleaseOrEmpty ++ [lease, BinaryEntry(leaseIdKey, lcalc(lease)), IntegerEntry(getLeaseAmountKey(nodeIndex), newLeaseAmount)])
419540 }
420541 else nil
421542 }
422543
423544
424545 func readNodeInfo (nodeIdx) = {
425546 let nodeAddress = getStakingNodeAddressByIndex(nodeIdx)
426547 let leasedAmtKEY = getLeaseAmountKey(nodeIdx)
427548 let leasedAmt = getNumberByKey(leasedAmtKEY)
428549 let leaseIdKEY = getLeaseIdKey(nodeIdx)
429550 let leaseId = value(getBinary(this, leaseIdKEY))
430551 $Tuple5(nodeAddress, leasedAmtKEY, leasedAmt, leaseIdKEY, leaseId)
431552 }
432553
433554
434-func commonSwap (swapType,pmtAmount,userAddressStr,txId58) = {
555+func commonSwap (swapType,pmtAmount,inAsset,userAddressStr,txId58) = {
435556 let swapParamsSTRUCT = asSwapParamsSTRUCT(invoke(this, "swapParamsByUserSYSREADONLY", [userAddressStr, 0], nil))
436557 let swapLimitSpent = swapParamsSTRUCT._2
437558 let blcks2LmtReset = swapParamsSTRUCT._3
438559 let wavesSwapLimitMax = swapParamsSTRUCT._6
439560 let usdnSwapLimitMax = swapParamsSTRUCT._7
440561 let minSwapAmount = minSwapAmountREAD(swapType)
441- let totalLocked = totalLockedREAD(swapType)
442- let totalLockedByUser = totalLockedByUserREAD(swapType, userAddressStr)
562+ let totalLocked = totalLockedREAD(swapType, inAsset)
563+ let totalLockedByUser = totalLockedByUserREAD(swapType, userAddressStr, inAsset)
443564 let nodeAddress = getStakingNodeByIndex(0)
444565 let priceByIndex = getPriceHistory(getHeightPriceByIndex(priceIndex))
445566 let isSwapByNode = (nodeAddress == userAddressStr)
446567 let balanceLockMaxInterval = if (isSwapByNode)
447568 then nodeBalanceLockIntervalREAD()
448569 else balanceLockIntervalREAD(swapType)
449570 let selfUnlockHeight = (height + balanceLockMaxInterval)
450- let swapUsdnVolume = if ((swapType == "waves"))
571+ let swapUsdnVolume = if ((swapType == "outNeutrino"))
451572 then convertWavesToNeutrino(pmtAmount, priceByIndex)
452573 else pmtAmount
453- let swapLimitMax = if ((swapType == "waves"))
574+ let swapLimitMax = if ((swapType == "outNeutrino"))
454575 then convertWavesToNeutrino(wavesSwapLimitMax, priceByIndex)
455576 else usdnSwapLimitMax
456577 if ((minSwapAmount > pmtAmount))
457578 then minSwapAmountFAIL(swapType, minSwapAmount)
458579 else if (if (!(isSwapByNode))
459580 then (swapLimitSpent > 0)
460581 else false)
461582 then throw(("You have exceeded swap limit! Next allowed swap height is " + toString((height + blcks2LmtReset))))
462583 else if (if (!(isSwapByNode))
463584 then (swapUsdnVolume > swapLimitMax)
464585 else false)
465586 then throw(((("You have exceeded your swap limit! Requested: " + toString(swapUsdnVolume)) + ", available: ") + toString(swapLimitMax)))
466587 else if (isBlocked)
467588 then emergencyShutdownFAIL()
468589 else {
469- let leasePart = if ((swapType == "waves"))
590+ let leasePart = if (if ((swapType == "outNeutrino"))
591+ then (inAsset == "WAVES")
592+ else false)
470593 then prepareUnleaseAndLease(0)
471594 else nil
472- $Tuple2(([IntegerEntry(keySwapUserSpentInPeriod(userAddressStr), swapUsdnVolume), IntegerEntry(keyUserLastSwapHeight(userAddressStr), height), IntegerEntry(totalLockedByUserKEY(swapType, userAddressStr), (totalLockedByUser + pmtAmount)), IntegerEntry(getBalanceUnlockBlockKey(userAddressStr), selfUnlockHeight), IntegerEntry(totalLockedKEY(swapType), (totalLocked + pmtAmount)), StringEntry(swapKEY(userAddressStr, txId58), pendingSwapDATA(swapType, pmtAmount, selfUnlockHeight))] ++ leasePart), unit)
595+ $Tuple2(([IntegerEntry(keySwapUserSpentInPeriod(userAddressStr), swapUsdnVolume), IntegerEntry(keyUserLastSwapHeight(userAddressStr), height), IntegerEntry(totalLockedByUserKEY(swapType, userAddressStr, inAsset), (totalLockedByUser + pmtAmount)), IntegerEntry(getBalanceUnlockBlockKey(userAddressStr), selfUnlockHeight), IntegerEntry(totalLockedKEY(swapType, inAsset), (totalLocked + pmtAmount)), StringEntry(swapKEY(userAddressStr, txId58), pendingSwapDATA(swapType, pmtAmount, selfUnlockHeight, inAsset))] ++ leasePart), unit)
473596 }
474597 }
475598
476599
477600 let nMetricIdxPrice = 0
478601
479602 let nMetricIdxUsdnLockedBalance = 1
480603
481604 let nMetricIdxWavesLockedBalance = 2
482605
483606 let nMetricIdxReserve = 3
484607
485608 let nMetricIdxReserveInUsdn = 4
486609
487610 let nMetricIdxUsdnSupply = 5
488611
489612 let nMetricIdxSurplus = 6
490613
491614 let nMetricIdxSurplusPercent = 7
492615
493616 let nMetricIdxBR = 8
494617
495618 let nMetricIdxNsbtSupply = 9
496619
497620 let nMetricIdxMaxNsbtSupply = 10
498621
499622 let nMetricIdxSurfSupply = 11
500623
501624 let nMetricUsdnUsdtPeg = 12
502625
503626 let nMetricCurrentPriceAdj = 13
504627
628+let nMetricBasketInfo = 14
629+
630+let nMetricWeightedPrice = 15
631+
632+let nMetricTotalReservesUsd = 16
633+
505634 let bFuncIdxSurf = 0
506635
507636 let bFuncIdxWaves = 1
508637
509638 let bFuncIdxUsdn = 2
510639
511640 let bFuncIdxReserveStart = 3
512641
513642 let bFuncIdxSupplyStart = 4
514643
515644 let bFuncIdxBRStart = 5
516645
517646 let bFuncIdxReserveEnd = 6
518647
519648 let bFuncIdxSupplyEnd = 7
520649
521650 let bFuncIdxBREnd = 8
522651
523652 let bFuncIdxRest = 9
524653
525654 let bFuncIdxWavesPrice = 10
526655
527-func calcWithdrawW2U (wavesIn,price) = {
528- let outAmtGross = convertWavesToNeutrino(wavesIn, price)
529- $Tuple10(outAmtGross, neutrinoAssetId, 0, unit, 0, price, wavesIn, 0, 0, 0)
656+func calcWithdrawT2U (inAmount,inAsset,basketInfo,weightedPrice) = {
657+ let outAmt = convertBasketItemToNeutrino(inAmount, inAsset, basketInfo, weightedPrice)
658+ $Tuple12(outAmt[0], neutrinoAssetId, 0, inAsset, 0, weightedPrice, inAmount, 0, 0, 0, nil, [outAmt[1], outAmt[2]])
530659 }
531660
532661
533-func calcWithdrawU2Basket (usdnIn,price,br,reservesInUsdn,usdnSupply) = {
662+func calcWithdrawU2Basket (usdnIn,basketInfo,br,reservesInUsdn,neutrinoSupply) = {
534663 let brProtected = valueOrElse(getInteger(this, brProtectedKEY()), BRPROTECTED)
535664 let maxAllowedUsdnBeforeMinBr = if ((brProtected >= br))
536665 then 0
537- else fraction((reservesInUsdn - fraction(brProtected, usdnSupply, PAULI)), PAULI, (PAULI - brProtected))
666+ else fraction((reservesInUsdn - fraction(brProtected, neutrinoSupply, MULT6)), MULT6, (MULT6 - brProtected))
538667 let allowedUsdnBeforeMinBr = if ((usdnIn > maxAllowedUsdnBeforeMinBr))
539668 then maxAllowedUsdnBeforeMinBr
540669 else usdnIn
541670 let allowedUsdnAfterMinBr = if ((usdnIn > maxAllowedUsdnBeforeMinBr))
542- then fraction((usdnIn - maxAllowedUsdnBeforeMinBr), br, PAULI)
671+ then fraction((usdnIn - maxAllowedUsdnBeforeMinBr), br, MULT6)
543672 else 0
544673 let allowedUsdn = (allowedUsdnBeforeMinBr + allowedUsdnAfterMinBr)
545674 let usdn2SURF = (usdnIn - allowedUsdn)
546- let outAmtGross = convertNeutrinoToWaves(allowedUsdn, price)
547- $Tuple10(outAmtGross, unit, usdn2SURF, neutrinoAssetId, outAmtGross, price, allowedUsdn, maxAllowedUsdnBeforeMinBr, allowedUsdnBeforeMinBr, allowedUsdnAfterMinBr)
675+ let outAmtGross = convertNeutrinoToBasket(allowedUsdn, basketInfo, neutrinoSupply)
676+ $Tuple12(outAmtGross._2, unit, usdn2SURF, neutrinoAssetId, outAmtGross._2, outAmtGross._3, allowedUsdn, maxAllowedUsdnBeforeMinBr, allowedUsdnBeforeMinBr, allowedUsdnAfterMinBr, outAmtGross._1, nil)
548677 }
549678
550679
551-func calcWithdraw (swapType,inAmount,price,neutrinoMetrics) = {
552- let outFeePart = valueOrElse(getInteger(this, outFeePartKEY(swapType)), DEFAULTSWAPFEE)
553- if (if ((0 > outFeePart))
554- then true
555- else (outFeePart >= PAULI))
556- then throw(((("invalid outFeePart config for " + swapType) + " swap: outFeePart=") + toString(outFeePart)))
557- else {
558- let brProtected = valueOrElse(getInteger(this, brProtectedKEY()), BRPROTECTED)
559- let BR = asInt(neutrinoMetrics[nMetricIdxBR])
560- let reservesInUsdn = asInt(neutrinoMetrics[nMetricIdxReserveInUsdn])
561- let usdnSupply = asInt(neutrinoMetrics[nMetricIdxUsdnSupply])
562- let priceAdj = asInt(neutrinoMetrics[nMetricCurrentPriceAdj])
563- let outDataTuple = if ((swapType == "waves"))
564- then calcWithdrawW2U(inAmount, price)
565- else if ((swapType == "outMulti"))
566- then calcWithdrawU2Basket(inAmount, priceAdj, BR, reservesInUsdn, usdnSupply)
567- else throw(("Unsupported swap type " + swapType))
568- let outAmtGross = outDataTuple._1
569- let outAssetId = outDataTuple._2
570- let inAmtToSurfPart = outDataTuple._3
571- let inAssetId = outDataTuple._4
572- let unleaseAmt = outDataTuple._5
573- let withdrawPrice = outDataTuple._6
574- let payoutsArray = applyFees(outAmtGross, inAmtToSurfPart, outFeePart)
575- let outNetAmt = payoutsArray[IdxNetAmount]
576- let outFeeAmt = payoutsArray[IdxFeeAmount]
577- let outSurfAmt = if ((0 >= inAmtToSurfPart))
578- then 0
579- else {
580- let surfResult = asAnyList(invoke(mathContract, "surfFunctionREADONLY", [inAmtToSurfPart, inAssetId], nil))
581- asInt(surfResult[bFuncIdxSurf])
582- }
583- $Tuple8(outNetAmt, outAssetId, outSurfAmt, inAmtToSurfPart, unleaseAmt, outFeeAmt, outAmtGross, withdrawPrice)
584- }
585- }
586-
587-
588-func commonWithdraw (account,index,swapTxId,withdrawTxId,neutrinoMetrics) = {
589- let userAddress = addressFromStringValue(account)
590- let dataArray = swapDataFailOrREAD(account, swapTxId)
591- let selfUnlockHeight = parseIntValue(dataArray[sIdxSelfUnlockHeight])
592- let swapType = dataArray[sIdxSwapType]
593- let inAmount = parseIntValue(dataArray[sIdxInAmount])
594- let swapStatus = dataArray[sIdxStatus]
595- let startHeight = parseIntValue(dataArray[sIdxStartHeight])
596- let outFeePart = valueOrElse(getInteger(this, outFeePartKEY(swapType)), DEFAULTSWAPFEE)
597- let totalLocked = totalLockedREAD(swapType)
598- let totalLockedByUser = totalLockedByUserREAD(swapType, account)
599- let unlockHeight = selfUnlockHeight
600- let indexHeight = getHeightPriceByIndex(index)
601- let prevIndexHeight = getHeightPriceByIndex((index - 1))
602- let priceByIndex = getPriceHistory(indexHeight)
603- if (isBlocked)
604- then emergencyShutdownFAIL()
605- else if ((swapStatus != "PENDING"))
606- then throw("swap has been already processed")
607- else if ((unlockHeight > height))
608- then throw((("please wait for: " + toString(unlockHeight)) + " block height to withdraw funds"))
609- else if (if (if ((index > priceIndex))
610- then true
611- else (unlockHeight > indexHeight))
612- then true
613- else if ((prevIndexHeight != 0))
614- then (prevIndexHeight >= unlockHeight)
615- else false)
616- then priceIndexFAIL(index, priceIndex, indexHeight, unlockHeight, prevIndexHeight)
617- else {
618- let withdrawTuple = calcWithdraw(swapType, inAmount, priceByIndex, neutrinoMetrics)
619- let outNetAmount = withdrawTuple._1
620- let outAssetId = withdrawTuple._2
621- let outSurfAmt = withdrawTuple._3
622- let unleaseAmt = withdrawTuple._5
623- let outFeeAmount = withdrawTuple._6
624- let outAmtGross = withdrawTuple._7
625- let withdrawPrice = withdrawTuple._8
626- if ((0 >= outAmtGross))
627- then throw("balance equals zero")
628- else {
629- let BR = asInt(neutrinoMetrics[nMetricIdxBR])
630- let state = [IntegerEntry(totalLockedByUserKEY(swapType, account), (totalLockedByUser - inAmount)), IntegerEntry(totalLockedKEY(swapType), (totalLocked - inAmount)), ScriptTransfer(userAddress, outNetAmount, outAssetId), StringEntry(swapKEY(account, swapTxId), finishSwapDATA(dataArray, withdrawPrice, outNetAmount, outFeeAmount, unlockHeight, index, withdrawTxId, outSurfAmt, BR, "TODO"))]
631- let surfCondition = if ((outSurfAmt > 0))
632- then {
633- let issueResult = invoke(auctionContract, "issueSurf", [outSurfAmt, account], nil)
634- if ((issueResult == issueResult))
635- then 0
636- else throw("Strict value is not equal to itself.")
637- }
638- else 0
639- if ((surfCondition == surfCondition))
640- then $Tuple3(state, AttachedPayment(outAssetId, outFeeAmount), unleaseAmt)
641- else throw("Strict value is not equal to itself.")
642- }
643- }
680+func calcWithdraw (swapType,inAmount,price,neutrinoMetrics,inAsset) = {
681+ let brProtected = valueOrElse(getInteger(this, brProtectedKEY()), BRPROTECTED)
682+ let BR = asInt(neutrinoMetrics[nMetricIdxBR])
683+ let reservesInUsd = asInt(neutrinoMetrics[nMetricTotalReservesUsd])
684+ let neutrinoSupply = asInt(neutrinoMetrics[nMetricIdxUsdnSupply])
685+ let basketInfo = asString(neutrinoMetrics[nMetricBasketInfo])
686+ let weightedPrice = asInt(neutrinoMetrics[nMetricWeightedPrice])
687+ let outDataTuple = if ((swapType == "outNeutrino"))
688+ then calcWithdrawT2U(inAmount, inAsset, basketInfo, weightedPrice)
689+ else if ((swapType == "outMulti"))
690+ then calcWithdrawU2Basket(inAmount, basketInfo, BR, reservesInUsd, neutrinoSupply)
691+ else throw(("Unsupported swap type " + swapType))
692+ let outAmtGross = outDataTuple._1
693+ let outAssetId = outDataTuple._2
694+ let inAmtToSurfPart = outDataTuple._3
695+ let inAssetId = outDataTuple._4
696+ let unleaseAmt = outDataTuple._5
697+ let withdrawPrice = outDataTuple._6
698+ let basketData = outDataTuple._11
699+ let netFeeData = outDataTuple._12
700+ let outNetAmt = if ((swapType == "outNeutrino"))
701+ then netFeeData[0]
702+ else 0
703+ let outFeeAmt = if ((swapType == "outNeutrino"))
704+ then netFeeData[1]
705+ else 0
706+ let outSurfAmt = if ((0 >= inAmtToSurfPart))
707+ then 0
708+ else asInt(asAnyList(invoke(mathContract, "surfFunctionREADONLY", [inAmtToSurfPart, inAssetId], nil))[bFuncIdxSurf])
709+ let withdrawBasket = if ((swapType == "outMulti"))
710+ then applyFeesBasket(basketData)
711+ else ""
712+ $Tuple9(outNetAmt, outAssetId, outSurfAmt, inAmtToSurfPart, unleaseAmt, outFeeAmt, outAmtGross, withdrawPrice, withdrawBasket)
644713 }
645714
646715
647716 func keyApplyInProgress () = "%s__applyInProgress"
648717
649718
650719 func keyProposalDataById (proposalId) = ("%s%d__proposalData__" + toString(proposalId))
651720
652721
653722 let govIdxTxIds = 9
654723
655724 func validateUpdate (tx) = match tx {
656725 case o: Order =>
657726 throw("Orders aren't allowed")
658727 case t: DataTransaction|SponsorFeeTransaction|SetScriptTransaction|CreateAliasTransaction|LeaseCancelTransaction|LeaseTransaction|IssueTransaction|InvokeExpressionTransaction|UpdateAssetInfoTransaction|InvokeScriptTransaction|SetAssetScriptTransaction|TransferTransaction|ExchangeTransaction|MassTransferTransaction|BurnTransaction|ReissueTransaction =>
659728 let txId = toBase58String(t.id)
660729 let proposalId = valueOrErrorMessage(getInteger(govContract, keyApplyInProgress()), "Apply is not happening")
661730 let txList = split(split(getStringOrFail(govContract, keyProposalDataById(proposalId)), SEP)[govIdxTxIds], LISTSEP)
662731 if (!(isDefined(indexOf(txList, txId))))
663732 then throw(((("Unknown txId: " + txId) + " for proposalId=") + toString(proposalId)))
664733 else true
665734 case _ =>
666735 throw("Match error")
667736 }
668737
669738
670739 @Callable(i)
671-func constructorV1 (neutrinoAssetIdPrm,bondAssetIdPrm,auctionContractPrm,liquidationContractPrm,rpdContractPrm,nodeOracleProviderPubKeyPrm,balanceWavesLockIntervalPrm,balanceNeutrinoLockIntervalPrm,minWavesSwapAmountPrm,minNeutrinoSwapAmountPrm,neutrinoOutFeePartPrm,wavesOutFeePartPrm,multiOutFeePartPrm) = {
740+func constructorV1 (neutrinoAssetIdPrm,bondAssetIdPrm,auctionContractPrm,liquidationContractPrm,rpdContractPrm,nodeOracleProviderPubKeyPrm,balanceWavesLockIntervalPrm,balanceNeutrinoLockIntervalPrm,minTokenSwapAmountPrm,minNeutrinoSwapAmountPrm,neutrinoOutFeePartPrm,wavesOutFeePartPrm) = {
672741 let checkCaller = thisOnly(i)
673742 if ((checkCaller == checkCaller))
674743 then if ((size(i.payments) != 0))
675744 then throw("no payments allowed")
676- else [StringEntry(NeutrinoAssetIdKey, neutrinoAssetIdPrm), StringEntry(BondAssetIdKey, bondAssetIdPrm), StringEntry(AuctionContractKey, auctionContractPrm), StringEntry(LiquidationContractKey, liquidationContractPrm), StringEntry(RPDContractKey, rpdContractPrm), StringEntry(NodeOracleProviderPubKeyKey, nodeOracleProviderPubKeyPrm), IntegerEntry(BalanceWavesLockIntervalKey, balanceWavesLockIntervalPrm), IntegerEntry(BalanceNeutrinoLockIntervalKey, balanceNeutrinoLockIntervalPrm), IntegerEntry(MinWavesSwapAmountKey, minWavesSwapAmountPrm), IntegerEntry(MinNeutrinoSwapAmountKey, minNeutrinoSwapAmountPrm), IntegerEntry(NeutrinoOutFeePartKey, neutrinoOutFeePartPrm), IntegerEntry(WavesOutFeePartKey, wavesOutFeePartPrm), IntegerEntry(MultiOutFeePartKey, multiOutFeePartPrm)]
745+ else [StringEntry(NeutrinoAssetIdKey, neutrinoAssetIdPrm), StringEntry(BondAssetIdKey, bondAssetIdPrm), StringEntry(AuctionContractKey, auctionContractPrm), StringEntry(LiquidationContractKey, liquidationContractPrm), StringEntry(RPDContractKey, rpdContractPrm), StringEntry(NodeOracleProviderPubKeyKey, nodeOracleProviderPubKeyPrm), IntegerEntry(BalanceWavesLockIntervalKey, balanceWavesLockIntervalPrm), IntegerEntry(BalanceNeutrinoLockIntervalKey, balanceNeutrinoLockIntervalPrm), IntegerEntry(MinTokenSwapAmountKey, minTokenSwapAmountPrm), IntegerEntry(MinNeutrinoSwapAmountKey, minNeutrinoSwapAmountPrm), IntegerEntry(NeutrinoOutFeePartKey, neutrinoOutFeePartPrm), IntegerEntry(WavesOutFeePartKey, wavesOutFeePartPrm)]
677746 else throw("Strict value is not equal to itself.")
678747 }
679748
680749
681750
682751 @Callable(i)
683752 func constructorV2 (mathContract,nsbtStakingContract,swapsTimeframeBlocks) = {
684753 let checkCaller = thisOnly(i)
685754 if ((checkCaller == checkCaller))
686755 then if ((size(i.payments) != 0))
687756 then throw("no payments allowed")
688757 else [StringEntry(MathContractKey, mathContract), StringEntry(NsbtStakingContractKey, nsbtStakingContract), IntegerEntry(swapsTimeframeKEY(), swapsTimeframeBlocks)]
689758 else throw("Strict value is not equal to itself.")
690759 }
691760
692761
693762
694763 @Callable(i)
695-func swapWavesToNeutrino () = if ((size(i.payments) != 1))
696- then throw("swapWavesToNeutrino require only one payment")
764+func constructorV3 (assetWeights,dParam,kParam,bParam,deltaParam) = {
765+ let checkCaller = thisOnly(i)
766+ if ((checkCaller == checkCaller))
767+ then if ((size(i.payments) != 0))
768+ then throw("no payments allowed")
769+ else {
770+ func prepare (acc,item) = {
771+ let parts = split(item, "_")
772+ let assetId = parts[0]
773+ let weight = parseIntValue(parts[1])
774+ $Tuple2((acc._1 :+ assetId), (acc._2 :+ IntegerEntry(basketWeightByAssetKey(assetId), weight)))
775+ }
776+
777+ let r = {
778+ let $l = assetWeights
779+ let $s = size($l)
780+ let $acc0 = $Tuple2(nil, nil)
781+ func $f0_1 ($a,$i) = if (($i >= $s))
782+ then $a
783+ else prepare($a, $l[$i])
784+
785+ func $f0_2 ($a,$i) = if (($i >= $s))
786+ then $a
787+ else throw("List size exceeds 10")
788+
789+ $f0_2($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($acc0, 0), 1), 2), 3), 4), 5), 6), 7), 8), 9), 10)
790+ }
791+ (((((r._2 :+ IntegerEntry(basketDKey(), dParam)) :+ IntegerEntry(basketKKey(), kParam)) :+ IntegerEntry(basketBKey(), bParam)) :+ IntegerEntry(basketLogbDeltaKey(), log(bParam, 6, deltaParam, 6, 6, HALFUP))) :+ StringEntry(basketAssetsKey(), makeString(r._1, SEP)))
792+ }
793+ else throw("Strict value is not equal to itself.")
794+ }
795+
796+
797+
798+@Callable(i)
799+func swapTokenToNeutrino () = if ((size(i.payments) != 1))
800+ then throw("swapTokenToNeutrino require only one payment")
697801 else {
698802 let pmt = value(i.payments[0])
699- if (isDefined(pmt.assetId))
700- then throw("Only Waves token is allowed for swapping.")
803+ let basketAssets = valueOrElse(getString(basketAssetsKey()), "WAVES")
804+ let pmtAssetIdStr = toBase58String(valueOrElse(pmt.assetId, WAVESID))
805+ if (!(contains(basketAssets, pmtAssetIdStr)))
806+ then throw(((pmtAssetIdStr + " is not one of basket tokens:") + basketAssets))
701807 else {
702808 let userAddress = toString(i.caller)
703809 let txId58 = toBase58String(i.transactionId)
704- commonSwap("waves", pmt.amount, userAddress, txId58)
810+ commonSwap("outNeutrino", pmt.amount, pmtAssetIdStr, userAddress, txId58)
705811 }
706812 }
707813
708814
709815
710816 @Callable(i)
711817 func swapNeutrinoToBasket () = if ((size(i.payments) != 1))
712818 then throw("swapNeutrinoToBasket require only one payment")
713819 else {
714820 let pmt = value(i.payments[0])
715821 if ((pmt.assetId != neutrinoAssetId))
716822 then throw("Only appropriate Neutrino tokens are allowed for swapping.")
717823 else {
718824 let userAddress = toString(i.caller)
719825 let txId58 = toBase58String(i.transactionId)
720- commonSwap("outMulti", pmt.amount, userAddress, txId58)
826+ commonSwap("outMulti", pmt.amount, toBase58String(neutrinoAssetId), userAddress, txId58)
721827 }
722828 }
723829
724830
725831
726832 @Callable(i)
727833 func withdraw (account,index,swapTxId) = {
728834 let txId = toBase58String(i.transactionId)
729835 if ((size(i.payments) != 0))
730836 then throw("no payments allowed")
731837 else {
732838 let neutrinoMetrics = asAnyList(invoke(mathContract, "calcNeutinoMetricsREADONLY", nil, nil))
733839 let BR = asInt(neutrinoMetrics[nMetricIdxBR])
734- let commonTuple = commonWithdraw(account, index, swapTxId, txId, neutrinoMetrics)
735- let state = commonTuple._1
736- let fee = commonTuple._2
737- let unleaseAmt = commonTuple._3
738- let unleaseInvOrEmpty = invoke(this, "internalUnleaseAndLease", [unleaseAmt], nil)
739- if ((unleaseInvOrEmpty == unleaseInvOrEmpty))
740- then {
741- let gnsbtData = asAnyList(invoke(gnsbtControllerContract, "gnsbtInfoSYSREADONLY", ["", 0, 0], nil))
742- let gnsbtAmtTotal = asInt(gnsbtData[1])
743- let gnsbtAmtFromSurfTotal = asInt(asAnyList(gnsbtData[3])[3])
744- let surfFeeAmt1 = if ((gnsbtAmtTotal != 0))
745- then fraction(fee.amount, gnsbtAmtFromSurfTotal, gnsbtAmtTotal)
746- else 0
747- let surfFeeAmt2 = if ((gnsbtAmtTotal != 0))
748- then fraction(fee.amount, (PAULI - BR), PAULI)
749- else 0
750- let surfFeeAmt = max([surfFeeAmt1, surfFeeAmt2])
751- let nsbtFeeAmt = (fee.amount - surfFeeAmt)
752- let surfDeposit = if ((surfFeeAmt > 0))
753- then {
754- let surfInv = invoke(surfStakingContract, "deposit", nil, [AttachedPayment(fee.assetId, surfFeeAmt)])
755- if ((surfInv == surfInv))
756- then nil
757- else throw("Strict value is not equal to itself.")
758- }
759- else nil
760- if ((surfDeposit == surfDeposit))
761- then {
762- let nsbtDeposit = if ((nsbtFeeAmt > 0))
763- then {
764- let nsbtInv = invoke(nsbtStakingContract, "deposit", nil, [AttachedPayment(fee.assetId, nsbtFeeAmt)])
765- if ((nsbtInv == nsbtInv))
766- then nil
767- else throw("Strict value is not equal to itself.")
768- }
769- else nil
770- if ((nsbtDeposit == nsbtDeposit))
771- then state
772- else throw("Strict value is not equal to itself.")
773- }
774- else throw("Strict value is not equal to itself.")
775- }
776- else throw("Strict value is not equal to itself.")
840+ let userAddress = addressFromStringValue(account)
841+ let dataArray = swapDataFailOrREAD(account, swapTxId)
842+ let selfUnlockHeight = parseIntValue(dataArray[sIdxSelfUnlockHeight])
843+ let swapType = dataArray[sIdxSwapType]
844+ let inAmount = parseIntValue(dataArray[sIdxInAmount])
845+ let swapStatus = dataArray[sIdxStatus]
846+ let startHeight = parseIntValue(dataArray[sIdxStartHeight])
847+ let inAsset = dataArray[sIdxInAsset]
848+ let totalLocked = totalLockedREAD(swapType, inAsset)
849+ let totalLockedByUser = totalLockedByUserREAD(swapType, account, inAsset)
850+ let unlockHeight = selfUnlockHeight
851+ let indexHeight = getHeightPriceByIndex(index)
852+ let prevIndexHeight = getHeightPriceByIndex((index - 1))
853+ let priceByIndex = getPriceHistory(indexHeight)
854+ if (isBlocked)
855+ then emergencyShutdownFAIL()
856+ else if ((swapStatus != "PENDING"))
857+ then throw("swap has been already processed")
858+ else if ((unlockHeight > height))
859+ then throw((("please wait for: " + toString(unlockHeight)) + " block height to withdraw funds"))
860+ else if (if (if ((index > priceIndex))
861+ then true
862+ else (unlockHeight > indexHeight))
863+ then true
864+ else if ((prevIndexHeight != 0))
865+ then (prevIndexHeight >= unlockHeight)
866+ else false)
867+ then priceIndexFAIL(index, priceIndex, indexHeight, unlockHeight, prevIndexHeight)
868+ else {
869+ let withdrawTuple = calcWithdraw(swapType, inAmount, priceByIndex, neutrinoMetrics, inAsset)
870+ let outNetAmount = withdrawTuple._1
871+ let outAssetId = withdrawTuple._2
872+ let outSurfAmt = withdrawTuple._3
873+ let unleaseAmt = withdrawTuple._5
874+ let outFeeAmount = withdrawTuple._6
875+ let outAmtGross = withdrawTuple._7
876+ let withdrawPrice = withdrawTuple._8
877+ let withdrawBasket = withdrawTuple._9
878+ if ((0 >= outAmtGross))
879+ then throw("balance equals zero")
880+ else {
881+ let surfCondition = if ((outSurfAmt > 0))
882+ then {
883+ let issueResult = invoke(auctionContract, "issueSurf", [outSurfAmt, account], nil)
884+ if ((issueResult == issueResult))
885+ then 0
886+ else throw("Strict value is not equal to itself.")
887+ }
888+ else 0
889+ if ((surfCondition == surfCondition))
890+ then {
891+ let unleaseInvOrEmpty = invoke(this, "internalUnleaseAndLease", [unleaseAmt], nil)
892+ if ((unleaseInvOrEmpty == unleaseInvOrEmpty))
893+ then {
894+ let gnsbtData = asAnyList(invoke(gnsbtControllerContract, "gnsbtInfoSYSREADONLY", ["", 0, 0], nil))
895+ let gnsbtAmtTotal = asInt(gnsbtData[1])
896+ let gnsbtAmtFromSurfTotal = asInt(asAnyList(gnsbtData[3])[3])
897+ let commonActions = [IntegerEntry(totalLockedByUserKEY(swapType, account, inAsset), (totalLockedByUser - inAmount)), IntegerEntry(totalLockedKEY(swapType, inAsset), (totalLocked - inAmount)), StringEntry(swapKEY(account, swapTxId), finishSwapDATA(dataArray, withdrawPrice, outNetAmount, outFeeAmount, unlockHeight, index, txId, outSurfAmt, BR, withdrawBasket, inAsset))]
898+ if ((swapType == "outMulti"))
899+ then {
900+ let basket = split(withdrawBasket, "_")
901+ func looper (acc,item) = {
902+ let it = split(item, LISTSEP)
903+ let tempAssetId = fromBase58String(it[0])
904+ let assetId = if ((tempAssetId == WAVESID))
905+ then unit
906+ else tempAssetId
907+ let netAmount = parseIntValue(it[2])
908+ let feeAmount = parseIntValue(it[3])
909+ let surfAmt1 = if ((gnsbtAmtTotal != 0))
910+ then fraction(feeAmount, gnsbtAmtFromSurfTotal, gnsbtAmtTotal)
911+ else 0
912+ let surfAmt2 = if ((gnsbtAmtTotal != 0))
913+ then fraction(feeAmount, (MULT6 - BR), MULT6)
914+ else 0
915+ let surfAmt = max([surfAmt1, surfAmt2])
916+ let nsbtAmt = (feeAmount - surfAmt)
917+ $Tuple5((acc._1 :+ ScriptTransfer(userAddress, netAmount, assetId)), (acc._2 :+ AttachedPayment(assetId, surfAmt)), (acc._3 :+ AttachedPayment(assetId, nsbtAmt)), (acc._4 + surfAmt), (acc._5 + nsbtAmt))
918+ }
919+
920+ let r = {
921+ let $l = basket
922+ let $s = size($l)
923+ let $acc0 = $Tuple5(nil, nil, nil, 0, 0)
924+ func $f0_1 ($a,$i) = if (($i >= $s))
925+ then $a
926+ else looper($a, $l[$i])
927+
928+ func $f0_2 ($a,$i) = if (($i >= $s))
929+ then $a
930+ else throw("List size exceeds 10")
931+
932+ $f0_2($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($acc0, 0), 1), 2), 3), 4), 5), 6), 7), 8), 9), 10)
933+ }
934+ let surfDeposit = if ((r._4 > 0))
935+ then {
936+ let surfInv = invoke(surfStakingContract, "deposit", nil, r._2)
937+ if ((surfInv == surfInv))
938+ then 0
939+ else throw("Strict value is not equal to itself.")
940+ }
941+ else 0
942+ if ((surfDeposit == surfDeposit))
943+ then {
944+ let nsbtDeposit = if ((r._5 > 0))
945+ then {
946+ let nsbtInv = invoke(nsbtStakingContract, "deposit", nil, r._3)
947+ if ((nsbtInv == nsbtInv))
948+ then 0
949+ else throw("Strict value is not equal to itself.")
950+ }
951+ else 0
952+ if ((nsbtDeposit == nsbtDeposit))
953+ then (commonActions ++ r._1)
954+ else throw("Strict value is not equal to itself.")
955+ }
956+ else throw("Strict value is not equal to itself.")
957+ }
958+ else {
959+ let fee = AttachedPayment(outAssetId, outFeeAmount)
960+ let surfFeeAmt1 = if ((gnsbtAmtTotal != 0))
961+ then fraction(fee.amount, gnsbtAmtFromSurfTotal, gnsbtAmtTotal)
962+ else 0
963+ let surfFeeAmt2 = if ((gnsbtAmtTotal != 0))
964+ then fraction(fee.amount, (MULT6 - BR), MULT6)
965+ else 0
966+ let surfFeeAmt = max([surfFeeAmt1, surfFeeAmt2])
967+ let nsbtFeeAmt = (fee.amount - surfFeeAmt)
968+ let surfDeposit = if ((surfFeeAmt > 0))
969+ then {
970+ let surfInv = invoke(surfStakingContract, "deposit", nil, [AttachedPayment(fee.assetId, surfFeeAmt)])
971+ if ((surfInv == surfInv))
972+ then nil
973+ else throw("Strict value is not equal to itself.")
974+ }
975+ else nil
976+ if ((surfDeposit == surfDeposit))
977+ then {
978+ let nsbtDeposit = if ((nsbtFeeAmt > 0))
979+ then {
980+ let nsbtInv = invoke(nsbtStakingContract, "deposit", nil, [AttachedPayment(fee.assetId, nsbtFeeAmt)])
981+ if ((nsbtInv == nsbtInv))
982+ then nil
983+ else throw("Strict value is not equal to itself.")
984+ }
985+ else nil
986+ if ((nsbtDeposit == nsbtDeposit))
987+ then (commonActions :+ ScriptTransfer(userAddress, outNetAmount, outAssetId))
988+ else throw("Strict value is not equal to itself.")
989+ }
990+ else throw("Strict value is not equal to itself.")
991+ }
992+ }
993+ else throw("Strict value is not equal to itself.")
994+ }
995+ else throw("Strict value is not equal to itself.")
996+ }
997+ }
777998 }
778999 }
7791000
7801001
7811002
7821003 @Callable(i)
7831004 func internalUnleaseAndLease (unleaseAmount) = if ((i.caller != this))
7841005 then throw("internalUnleaseAndLease is not public method")
7851006 else prepareUnleaseAndLease(unleaseAmount)
7861007
7871008
7881009
7891010 @Callable(i)
7901011 func transferUsdnToUser (amount,addr) = if ((i.caller != auctionContract))
7911012 then throw("Only auction contract is authorized")
7921013 else [ScriptTransfer(addressFromStringValue(addr), amount, neutrinoAssetId)]
7931014
7941015
7951016
7961017 @Callable(i)
7971018 func acceptWaves () = if (if ((i.caller != auctionContract))
7981019 then (i.caller != govContract)
7991020 else false)
8001021 then throw("Currently only auction and governance contracts are allowed to call")
8011022 else $Tuple2(prepareUnleaseAndLease(0), "success")
8021023
8031024
8041025
8051026 @Callable(i)
8061027 func approveLeasings (nListS,groupNum,lAmt) = {
8071028 let nIdxs = [0, 1, 2, 3, 4, 5, 6, 7]
8081029 let mngPubS = valueOrElse(getString("%s%s__cfg__leasingManagerPub"), "7AUMX54ukYMYvPmma7yoFf5NjZhs4Bu5nz3Ez9EV8sur")
8091030 let mngPub = fromBase58String(mngPubS)
8101031 let nodeRegAddrStr = valueOrElse(getString("%s%s__cfg__nodesRegistryAddress"), "3P9vKqQKjUdmpXAfiWau8krREYAY1Xr69pE")
8111032 let nodeRegAddr = addressFromStringValue(nodeRegAddrStr)
8121033 let lGroupNodeListKEY = getLeaseGroupNodeListKey(groupNum)
8131034 let lGrNodeOpt = getString(this, lGroupNodeListKEY)
8141035 if (isDefined(lGrNodeOpt))
8151036 then throw((("group " + toString(groupNum)) + " already initialized"))
8161037 else {
8171038 let nList = split(nListS, SEP)
8181039 let expCount = size(nIdxs)
8191040 if ((i.callerPublicKey != mngPub))
8201041 then throw("approveLeasings not authorized")
8211042 else {
822- let $t03577035832 = readNodeInfo(0)
823- let nAddr0 = $t03577035832._1
824- let lAmtKEY0 = $t03577035832._2
825- let lAmt0 = $t03577035832._3
826- let lIdKEY0 = $t03577035832._4
827- let lId0 = $t03577035832._5
1043+ let $t04305243114 = readNodeInfo(0)
1044+ let nAddr0 = $t04305243114._1
1045+ let lAmtKEY0 = $t04305243114._2
1046+ let lAmt0 = $t04305243114._3
1047+ let lIdKEY0 = $t04305243114._4
1048+ let lId0 = $t04305243114._5
8281049 let newL0 = Lease(nAddr0, (lAmt0 - (lAmt * expCount)))
8291050 let validation = invoke(nodeRegAddr, "validateAndApproveLeasings", [nListS], nil)
8301051 if ((validation == validation))
8311052 then {
8321053 func forEachNodeValidateAndGenerateLease (a,i) = {
8331054 let node = nList[i]
8341055 let la = Lease(addressFromStringValue(node), lAmt)
8351056 (a ++ [la, BinaryEntry(getLeaseIdByAddressKey(node), lcalc(la)), IntegerEntry(getLeaseAmountByAddressKey(node), lAmt)])
8361057 }
8371058
8381059 ([StringEntry(lGroupNodeListKEY, nListS), BinaryEntry(lIdKEY0, lcalc(newL0)), IntegerEntry(lAmtKEY0, newL0.amount), LeaseCancel(lId0), newL0] ++ {
8391060 let $l = nIdxs
8401061 let $s = size($l)
8411062 let $acc0 = nil
8421063 func $f0_1 ($a,$i) = if (($i >= $s))
8431064 then $a
8441065 else forEachNodeValidateAndGenerateLease($a, $l[$i])
8451066
8461067 func $f0_2 ($a,$i) = if (($i >= $s))
8471068 then $a
8481069 else throw("List size exceeds 8")
8491070
8501071 $f0_2($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($acc0, 0), 1), 2), 3), 4), 5), 6), 7), 8)
8511072 })
8521073 }
8531074 else throw("Strict value is not equal to itself.")
8541075 }
8551076 }
8561077 }
8571078
8581079
8591080
8601081 @Callable(i)
8611082 func rebalanceLeasings (amount,groupNum) = {
8621083 let nIdxs = [0, 1, 2, 3, 4, 5, 6, 7]
8631084 let mngPubS = valueOrElse(getString("%s%s__cfg__leasingManagerPub"), "7AUMX54ukYMYvPmma7yoFf5NjZhs4Bu5nz3Ez9EV8sur")
8641085 let mngPub = fromBase58String(mngPubS)
8651086 let lGroupNodeListKEY = getLeaseGroupNodeListKey(groupNum)
8661087 let nListS = getStringOrFail(this, lGroupNodeListKEY)
8671088 let nList = split(nListS, SEP)
8681089 if ((i.callerPublicKey != mngPub))
8691090 then throw("rebalanceLeasings not authorized")
8701091 else {
8711092 let unleaseAmt = ((amount / size(nList)) + 1)
872- let $t03707237134 = readNodeInfo(0)
873- let nAddr0 = $t03707237134._1
874- let lAmtKEY0 = $t03707237134._2
875- let lAmt0 = $t03707237134._3
876- let lIdKEY0 = $t03707237134._4
877- let lId0 = $t03707237134._5
1093+ let $t04435444416 = readNodeInfo(0)
1094+ let nAddr0 = $t04435444416._1
1095+ let lAmtKEY0 = $t04435444416._2
1096+ let lAmt0 = $t04435444416._3
1097+ let lIdKEY0 = $t04435444416._4
1098+ let lId0 = $t04435444416._5
8781099 let newL0 = Lease(nAddr0, (lAmt0 + (unleaseAmt * size(nList))))
8791100 func forEachNodeDoUnlease (a,i) = {
8801101 let node = nList[i]
8811102 let lIdKEY = getLeaseIdByAddressKey(node)
8821103 let lId = getBinaryValue(this, lIdKEY)
8831104 let lAmtKEY = getLeaseAmountByAddressKey(node)
8841105 let lAmt = getIntegerValue(this, lAmtKEY)
8851106 let ula = LeaseCancel(value(lId))
8861107 let la = Lease(addressFromStringValue(node), (lAmt - unleaseAmt))
8871108 (a ++ [LeaseCancel(value(lId)), la, BinaryEntry(lIdKEY, lcalc(la)), IntegerEntry(lAmtKEY, la.amount)])
8881109 }
8891110
8901111 ({
8911112 let $l = nIdxs
8921113 let $s = size($l)
8931114 let $acc0 = nil
8941115 func $f0_1 ($a,$i) = if (($i >= $s))
8951116 then $a
8961117 else forEachNodeDoUnlease($a, $l[$i])
8971118
8981119 func $f0_2 ($a,$i) = if (($i >= $s))
8991120 then $a
9001121 else throw("List size exceeds 8")
9011122
9021123 $f0_2($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($acc0, 0), 1), 2), 3), 4), 5), 6), 7), 8)
9031124 } ++ [BinaryEntry(lIdKEY0, lcalc(newL0)), IntegerEntry(lAmtKEY0, newL0.amount), LeaseCancel(lId0), newL0])
9041125 }
9051126 }
9061127
9071128
9081129
9091130 @Callable(i)
9101131 func swapParamsByUserSYSREADONLY (userAddressStr,gnsbtDiff) = {
9111132 let gnsbtData = asAnyList(invoke(gnsbtControllerContract, "gnsbtInfoSYSREADONLY", [userAddressStr, 0, 0], nil))
9121133 let gnsbtAmt = (asInt(gnsbtData[0]) + gnsbtDiff)
9131134 let gnsbtAmtTotal = (asInt(gnsbtData[1]) + gnsbtDiff)
9141135 let swapLimitData = asAnyList(invoke(mathContract, "calcSwapLimitREADONLY", [gnsbtAmt], nil))
9151136 let wavesSwapLimitInUsdnMax = asInt(swapLimitData[0])
9161137 let wavesSwapLimitMax = asInt(swapLimitData[1])
9171138 let usdnSwapLimitMax = asInt(swapLimitData[2])
9181139 let lastSwapHeight = valueOrElse(getInteger(this, keyUserLastSwapHeight(userAddressStr)), 0)
9191140 let swapLimitTimelifeBlocks = swapsTimeframeREAD()
9201141 let passedBlocksAfterLastSwap = (height - lastSwapHeight)
9211142 let isSwapTimelifeNew = (passedBlocksAfterLastSwap >= swapLimitTimelifeBlocks)
9221143 let swapLimitSpentInUsdn = if (isSwapTimelifeNew)
9231144 then 0
9241145 else valueOrElse(getInteger(this, keySwapUserSpentInPeriod(userAddressStr)), 0)
9251146 let blcks2LmtReset = if (isSwapTimelifeNew)
9261147 then 0
9271148 else (swapLimitTimelifeBlocks - passedBlocksAfterLastSwap)
9281149 $Tuple2(nil, $Tuple7(wavesSwapLimitInUsdnMax, swapLimitSpentInUsdn, blcks2LmtReset, gnsbtAmt, gnsbtAmtTotal, wavesSwapLimitMax, usdnSwapLimitMax))
9291150 }
9301151
9311152
9321153
9331154 @Callable(i)
934-func calcWithdrawResultSYSREADONLY (swapType,inAmount,price) = {
1155+func calcWithdrawResultSYSREADONLY (swapType,inAmount,price,inAsset) = {
9351156 let neutrinoMetrics = asAnyList(invoke(mathContract, "calcNeutinoMetricsREADONLY", nil, nil))
936- $Tuple2(nil, calcWithdraw(swapType, inAmount, price, neutrinoMetrics))
1157+ $Tuple2(nil, calcWithdraw(swapType, inAmount, price, neutrinoMetrics, inAsset))
9371158 }
9381159
9391160
9401161
9411162 @Callable(i)
9421163 func replaceCommunityNode (oldAddrStr,newAddrStr,groupNum,penaltyAmount) = {
9431164 let mngPubS = valueOrElse(getString("%s%s__cfg__leasingManagerPub"), "7AUMX54ukYMYvPmma7yoFf5NjZhs4Bu5nz3Ez9EV8sur")
9441165 let mngPub = fromBase58String(mngPubS)
9451166 if ((i.callerPublicKey != mngPub))
9461167 then throw("replaceCommunityNode not authorized")
9471168 else {
9481169 let groupKey = getLeaseGroupNodeListKey(groupNum)
9491170 let groupNodeListS = getStringOrFail(this, groupKey)
9501171 if (!(contains(groupNodeListS, oldAddrStr)))
9511172 then throw(((("Group " + toString(groupNum)) + " does not contain address ") + oldAddrStr))
9521173 else {
9531174 let doReplace = invoke(nodeRegistryContract, "replaceApprovedNode", [oldAddrStr, newAddrStr, groupNum, penaltyAmount], nil)
9541175 if ((doReplace == doReplace))
9551176 then {
9561177 let oldLeaseIdKey = getLeaseIdByAddressKey(oldAddrStr)
9571178 let oldLeaseAmtKey = getLeaseAmountByAddressKey(oldAddrStr)
9581179 let leaseAmt = getIntegerValue(oldLeaseAmtKey)
959- let newLeaseIdKey = getLeaseIdByAddressKey(oldAddrStr)
960- let newLeaseAmtKey = getLeaseAmountByAddressKey(oldAddrStr)
1180+ let newLeaseIdKey = getLeaseIdByAddressKey(newAddrStr)
1181+ let newLeaseAmtKey = getLeaseAmountByAddressKey(newAddrStr)
9611182 let newLease = Lease(addressFromStringValue(newAddrStr), leaseAmt)
9621183 let updatedGroupNodeListS = makeString(split(groupNodeListS, oldAddrStr), newAddrStr)
9631184 $Tuple2([LeaseCancel(getBinaryValue(oldLeaseIdKey)), DeleteEntry(oldLeaseIdKey), DeleteEntry(oldLeaseAmtKey), StringEntry(groupKey, updatedGroupNodeListS), newLease, BinaryEntry(newLeaseIdKey, lcalc(newLease)), IntegerEntry(newLeaseAmtKey, leaseAmt)], unit)
9641185 }
9651186 else throw("Strict value is not equal to itself.")
9661187 }
9671188 }
9681189 }
9691190
9701191
9711192
9721193 @Callable(i)
9731194 func updateReservesAndNeutrinoSupply () = {
9741195 func getNumberByKeyInternal (key) = match getInteger(this, key) {
9751196 case a: Int =>
9761197 a
9771198 case _ =>
9781199 0
9791200 }
9801201
9811202 let nMetrix = asAnyList(invoke(mathContract, "calcNeutinoMetricsREADONLY", nil, nil))
9821203 let idx = getNumberByKeyInternal("updateReservesAndNeutrinoSupplyIdx")
9831204 let newIdx = (idx + 1)
9841205 [IntegerEntry("updateReservesAndNeutrinoSupplyIdx", newIdx), IntegerEntry("reserve", asInt(nMetrix[3])), IntegerEntry("neutrinoSupply", asInt(nMetrix[5])), IntegerEntry("reservesInUsdn", convertWavesToNeutrino(asInt(nMetrix[3]), asInt(nMetrix[0]))), IntegerEntry("surplus", asInt(nMetrix[6])), IntegerEntry("deficit", -(asInt(nMetrix[6])))]
9851206 }
9861207
9871208
9881209
9891210 @Callable(i)
9901211 func wavesBalancesVsPayment () = {
9911212 let b = wavesBalance(this)
9921213 [IntegerEntry("wavesBalance_available", b.available), IntegerEntry("wavesBalance_regular", b.regular), IntegerEntry("wavesBalance_generating", b.generating), IntegerEntry("wavesBalance_effective", b.effective), IntegerEntry("waves_payment", value(i.payments[0]).amount)]
9931214 }
9941215
9951216
9961217 @Verifier(tx)
9971218 func verify () = {
9981219 let pubKeyAdminsListStr = makeString(["ExtEEK19nmKj9mCpnWyvEEJFYATLMcVEMvohhUHkyHNm", "Ev5py5FfBQX9cZpYKnfQrTB49Byf8QmpZWeDVRim4yV7", "DUuuLjXu98nBwZc7fqwCTjtA3nnRwgTbkMSr5SU2NmDR", "DUuuLjXu98nBwZc7fqwCTjtA3nnRwgTbkMSr5SU2NmDR"], SEP)
9991220 let pubKeyAdminsList = split(valueOrElse(getString(controlContract, "%s__multisig"), pubKeyAdminsListStr), SEP)
10001221 let count = ((((if (sigVerify(tx.bodyBytes, tx.proofs[0], fromBase58String(pubKeyAdminsList[0])))
10011222 then 1
10021223 else 0) + (if (sigVerify(tx.bodyBytes, tx.proofs[1], fromBase58String(pubKeyAdminsList[1])))
10031224 then 1
10041225 else 0)) + (if (sigVerify(tx.bodyBytes, tx.proofs[2], fromBase58String(pubKeyAdminsList[2])))
10051226 then 1
10061227 else 0)) + (if (sigVerify(tx.bodyBytes, tx.proofs[3], fromBase58String(pubKeyAdminsList[3])))
10071228 then 2
10081229 else 0))
10091230 if (if (isBlocked)
10101231 then (getStringValue(controlContract, "is_blocked_caller") == toString(govContract))
10111232 else false)
10121233 then validateUpdate(tx)
10131234 else match tx {
10141235 case sponsorTx: SponsorFeeTransaction =>
10151236 if (checkIsValidMinSponsoredFee(sponsorTx))
10161237 then (count >= 3)
10171238 else false
10181239 case _ =>
10191240 (count >= 3)
10201241 }
10211242 }
10221243

github/deemru/w8io/3ef1775 
525.68 ms