2023.05.11 17:41 [3638820] smart account 3P9vKqQKjUdmpXAfiWau8krREYAY1Xr69pE > SELF 0.00000000 Waves
{ "type": 13, "id": "4VURPAEc2M9S47VYAj1wcF9B838jACvRk8djxyKn9gED", "fee": 2200000, "feeAssetId": null, "timestamp": 1683818129094, "version": 1, "sender": "3P9vKqQKjUdmpXAfiWau8krREYAY1Xr69pE", "senderPublicKey": "7AUMX54ukYMYvPmma7yoFf5NjZhs4Bu5nz3Ez9EV8sur", "proofs": [ "2sHDbxCvS3pjCDB2quMSmENj5dpFXyuyrcDmrGnnordMs5cYec5VynfT9wq2sFSJKALVGf9azT1ErTDK3Tzc25qY" ], "script": "base64:BgI6CAISBwoFAQgIAQESBQoDCAgIEgASAwoBCBIDCgEIEgYKBAgIAQESAwoBCBIGCgQBAQgIEgUKAwgICEoAA1NFUAICX18ABU1VTFQ2AMCEPQAFTVVMVDgAgMLXLwAKV0FWRVNJRFNUUgIFV0FWRVMAB1dBVkVTSUQJANkEAQUKV0FWRVNJRFNUUgEMZ2V0SW50T3JGYWlsAQNrZXkJARN2YWx1ZU9yRXJyb3JNZXNzYWdlAgkAmggCBQR0aGlzBQNrZXkJAKwCAgkArAICAg9NYW5kYXRvcnkgdGhpcy4FA2tleQIPIGlzIG5vdCBkZWZpbmVkAQxnZXRJbnRPckVsc2UCA2tleQN2YWwJAQt2YWx1ZU9yRWxzZQIJAJoIAgUEdGhpcwUDa2V5BQN2YWwBDGdldFN0ck9yRWxzZQMHYWRkcmVzcwNrZXkKZGVmYXVsdFZhbAkBC3ZhbHVlT3JFbHNlAgkAnQgCBQdhZGRyZXNzBQNrZXkFCmRlZmF1bHRWYWwBD2dldFN0cmluZ09yRmFpbAIHYWRkcmVzcwNrZXkJARN2YWx1ZU9yRXJyb3JNZXNzYWdlAgkAnQgCBQdhZGRyZXNzBQNrZXkJAKwCAgkArAICAg9NYW5kYXRvcnkgdGhpcy4FA2tleQIPIGlzIG5vdCBkZWZpbmVkARBnZXRBZGRyZXNzT3JGYWlsAQNrZXkJARN2YWx1ZU9yRXJyb3JNZXNzYWdlAgkApggBCQEPZ2V0U3RyaW5nT3JGYWlsAgUEdGhpcwUDa2V5CQCsAgIJAKwCAgIVRmFpbCB0byBjb252ZXJ0IHRoaXMuBQNrZXkCEyB2YWx1ZSBpbnRvIEFkZHJlc3MBCWFzQW55TGlzdAEDdmFsBAckbWF0Y2gwBQN2YWwDCQABAgUHJG1hdGNoMAIJTGlzdFtBbnldBAp2YWxBbnlMaXN0BQckbWF0Y2gwBQp2YWxBbnlMaXN0CQACAQIbZmFpbCB0byBjYXN0IGludG8gTGlzdFtBbnldAQVhc0ludAEBdgQHJG1hdGNoMAUBdgMJAAECBQckbWF0Y2gwAgNJbnQEAWkFByRtYXRjaDAFAWkJAAIBAhVmYWlsIHRvIGNhc3QgaW50byBJbnQBC2FzQnl0ZVZlY290AQF2BAckbWF0Y2gwBQF2AwkAAQIFByRtYXRjaDACCkJ5dGVWZWN0b3IEAWkFByRtYXRjaDAFAWkJAAIBAhxmYWlsIHRvIGNhc3QgaW50byBCeXRlVmVjdG9yARV2YWxpZGF0ZUFkZHJlc3NPckZhaWwBCmFkZHJlc3NTdHIEB2FkZHJlc3MJARN2YWx1ZU9yRXJyb3JNZXNzYWdlAgkApggBBQphZGRyZXNzU3RyCQCsAgICHWZhaWwgdG8gY29udmVydCBpbnRvIGFkZHJlc3MgBQphZGRyZXNzU3RyAwkAZgIAAAgJAO8HAQUHYWRkcmVzcwlhdmFpbGFibGUJAQV0aHJvdwAFCmFkZHJlc3NTdHIBD2tleU5vZGVSZWdpc3RyeQEHYWRkcmVzcwkArAICAgQlc19fBQdhZGRyZXNzARRrZXlOb2RlUmV3YXJkc1RvdGFscwEHYWRkcmVzcwkArAICAg4lcyVzX190b3RhbHNfXwUHYWRkcmVzcwETa2V5QXBwcm92ZWROb2RlTGlzdAACFCVzX19hcHByb3ZlZE5vZGVMaXN0ARBrZXlEZXBvc2l0QW1vdW50AAIYJXMlc19fY2ZnX19kZXBvc2l0QW1vdW50ARVrZXlOZXV0cmlub0FkZHJlc3NTdHIAAholcyVzX19jZmdfX25ldXRyaW5vQWRkcmVzcwEXa2V5TWFzdGVyTm9kZUFkZHJlc3NTdHIAAiIlcyVzX19jZmdfX21haW5OZXV0cmlub05vZGVBZGRyZXNzARFrZXlQcm90b2NvbFBhcnRYNgACGSVzJXNfX2NmZ19fcHJvdG9jb2xQYXJ0WDYBE2tleUJlbmlmaWNhcnlQYXJ0WDYAAhslcyVzX19jZmdfX2JlbmlmaWNhcnlQYXJ0WDYBFmtleU1pbkRpc3RyaWJ1dGVBbW91bnQAAh4lcyVzX19jZmdfX21pbkRpc3RyaWJ1dGVBbW91bnQBHGtleVN3b3BmaVVzZHRVc2RuUG9vbEFkZHJlc3MAAiQlcyVzX19jZmdfX3N3b3BmaVVzZHRVc2RuUG9vbEFkZHJlc3MBEGtleVd4U3dhcEFkZHJlc3MAAhglcyVzX19jZmdfX3d4U3dhcEFkZHJlc3MBEWtleVNhdmluZ3NBZGRyZXNzAAIZJXMlc19fY2ZnX19zYXZpbmdzQWRkcmVzcwEVa2V5QmVuZWZpY2lhcnlBZGRyZXNzAAIdJXMlc19fY2ZnX19iZW5lZmljaWFyeUFkZHJlc3MBFWtleU5vZGVzVm90aW5nTmV4dE51bQACIyVzJXNfX25vZGVzVm90aW5nQ2ZnX19uZXh0Vm90aW5nTnVtARdrZXlOb2Rlc1ZvdGluZ09wdGlvbktleQIJdm90aW5nTnVtCW9wdGlvbk51bQkAuQkCCQDMCAICFiVzJWQlc19fbm9kZXNWb3RpbmdDZmcJAMwIAgUJdm90aW5nTnVtCQDMCAIFCW9wdGlvbk51bQUDbmlsBQNTRVABEmtleU5vZGVzVm90aW5nRGVzYwEJdm90aW5nTnVtCQC5CQIJAMwIAgIWJXMlZCVzX19ub2Rlc1ZvdGluZ0NmZwkAzAgCBQl2b3RpbmdOdW0JAMwIAgILZGVzY3JpcHRpb24FA25pbAUDU0VQARlrZXlOb2Rlc1ZvdGluZ1N0YXJ0SGVpZ2h0AQl2b3RpbmdOdW0JALkJAgkAzAgCAhYlcyVkJXNfX25vZGVzVm90aW5nQ2ZnCQDMCAIFCXZvdGluZ051bQkAzAgCAhF2b3RpbmdTdGFydEhlaWdodAUDbmlsBQNTRVABF2tleU5vZGVzVm90aW5nRW5kSGVpZ2h0AQl2b3RpbmdOdW0JALkJAgkAzAgCAhYlcyVkJXNfX25vZGVzVm90aW5nQ2ZnCQDMCAIFCXZvdGluZ051bQkAzAgCAg92b3RpbmdFbmRIZWlnaHQFA25pbAUDU0VQAR5rZXlOb2Rlc1ZvdGluZ09wdGlvblRvdGFsVm90ZXMCCXZvdGluZ051bQlvcHRpb25OdW0JALkJAgkAzAgCAhklcyVkJXMlc19fbm9kZXNWb3RpbmdEYXRhCQDMCAIFCXZvdGluZ051bQkAzAgCAhBvcHRpb25Ub3RhbFZvdGVzCQDMCAIFCW9wdGlvbk51bQUDbmlsBQNTRVABGGtleU5vZGVzVm90aW5nTm9kZUNob2ljZQIJdm90aW5nTnVtDm5vZGVBZGRyZXNzU3RyCQC5CQIJAMwIAgIZJXMlZCVzJXNfX25vZGVzVm90aW5nRGF0YQkAzAgCBQl2b3RpbmdOdW0JAMwIAgIKbm9kZUNob2ljZQkAzAgCBQ5ub2RlQWRkcmVzc1N0cgUDbmlsBQNTRVAAGUlkeENvbnRyb2xDZmdOZXV0cmlub0RhcHAAAQAYSWR4Q29udHJvbENmZ0F1Y3Rpb25EYXBwAAIAFElkeENvbnRyb2xDZmdScGREYXBwAAMAFUlkeENvbnRyb2xDZmdNYXRoRGFwcAAEABxJZHhDb250cm9sQ2ZnTGlxdWlkYXRpb25EYXBwAAUAFUlkeENvbnRyb2xDZmdSZXN0RGFwcAAGAB1JZHhDb250cm9sQ2ZnTm9kZVJlZ2lzdHJ5RGFwcAAHABxJZHhDb250cm9sQ2ZnTnNidFN0YWtpbmdEYXBwAAgAGUlkeENvbnRyb2xDZmdNZWRpYXRvckRhcHAACQAcSWR4Q29udHJvbENmZ1N1cmZTdGFraW5nRGFwcAAKACBJZHhDb250cm9sQ2ZnR25zYnRDb250cm9sbGVyRGFwcAALABdJZHhDb250cm9sQ2ZnUmVzdFYyRGFwcAAMABtJZHhDb250cm9sQ2ZnR292ZXJuYW5jZURhcHAADQERa2V5Q29udHJvbEFkZHJlc3MAAhwlcyVzX19jb25maWdfX2NvbnRyb2xBZGRyZXNzAQ1rZXlDb250cm9sQ2ZnAAIRJXNfX2NvbnRyb2xDb25maWcBDmtleVVzZG5Bc3NldElkAAIRbmV1dHJpbm9fYXNzZXRfaWQBFHJlYWRDb250cm9sQ2ZnT3JGYWlsAQdjb250cm9sCQC8CQIJAQ9nZXRTdHJpbmdPckZhaWwCBQdjb250cm9sCQENa2V5Q29udHJvbENmZwAFA1NFUAEYZ2V0Q29udHJhY3RBZGRyZXNzT3JGYWlsAgpjb250cm9sQ2ZnA2lkeAkBE3ZhbHVlT3JFcnJvck1lc3NhZ2UCCQCmCAEJAJEDAgUKY29udHJvbENmZwUDaWR4CQCsAgICLUNvbnRyb2wgY2ZnIGRvZXNuJ3QgY29udGFpbiBhZGRyZXNzIGF0IGluZGV4IAkApAMBBQNpZHgAD2NvbnRyb2xDb250cmFjdAkBEUBleHRyTmF0aXZlKDEwNjIpAQkBC3ZhbHVlT3JFbHNlAgkAnQgCBQR0aGlzCQERa2V5Q29udHJvbEFkZHJlc3MAAiMzUDVCZmQ1OFBQZk52Qk0ySHk4UWZiY0RxTWVOdHpnN0tmUAAKY29udHJvbENmZwkBFHJlYWRDb250cm9sQ2ZnT3JGYWlsAQUPY29udHJvbENvbnRyYWN0ABBuZXV0cmlub0NvbnRyYWN0CQEYZ2V0Q29udHJhY3RBZGRyZXNzT3JGYWlsAgUKY29udHJvbENmZwUZSWR4Q29udHJvbENmZ05ldXRyaW5vRGFwcAAOdXNkbkFzc2V0SWRTdHIJAQxnZXRTdHJPckVsc2UDBRBuZXV0cmlub0NvbnRyYWN0CQEOa2V5VXNkbkFzc2V0SWQAAixERzJ4RmtQZER3S1VvQmt6R0FoUXRMcFNHemZYTGlDWVBFemVLSDJBZDI0cAALdXNkbkFzc2V0SWQJANkEAQUOdXNkbkFzc2V0SWRTdHIADG5SZWdJZHhUeEFkZAABABBuUmVnSWR4SGVpZ2h0QWRkAAIADm5SZWdJZHhUaW1lQWRkAAMADm5SZWdJZHhEZXBvQW10AAQAEm5SZWdJZHhEZXBvQW10TGVmdAAFAA1uUmVnSWR4U3RhdHVzAAYAGW5SZWdJZHhTdGF0dXNDaGFuZ2VIZWlnaHQABwAXblJlZ0lkeFN0YXR1c0NoYW5nZVRpbWUACAEQZm9ybWF0RGVwb3NpdEFkZAQHdHhJZFN0cg1kZXBvc2l0QW1vdW50FnJlbWFpbmluZ0RlcG9zaXRBbW91bnQGc3RhdHVzCQC5CQIJAMwIAgIQJXMlZCVkJWQlZCVzJWQlZAkAzAgCBQd0eElkU3RyCQDMCAIJAKQDAQgFCWxhc3RCbG9jawZoZWlnaHQJAMwIAgkApAMBCAUJbGFzdEJsb2NrCXRpbWVzdGFtcAkAzAgCCQCkAwEFDWRlcG9zaXRBbW91bnQJAMwIAgkApAMBBRZyZW1haW5pbmdEZXBvc2l0QW1vdW50CQDMCAIFBnN0YXR1cwkAzAgCAgEwCQDMCAICATAFA25pbAUDU0VQARlmb3JtYXREZXBvc2l0Q2hhbmdlU3RhdHVzAgVvcmlnUwluZXdTdGF0dXMEBG9yaWcJALUJAgUFb3JpZ1MFA1NFUAkAuQkCCQDMCAICECVzJWQlZCVkJWQlcyVkJWQJAMwIAgkAkQMCBQRvcmlnBQxuUmVnSWR4VHhBZGQJAMwIAgkAkQMCBQRvcmlnBRBuUmVnSWR4SGVpZ2h0QWRkCQDMCAIJAJEDAgUEb3JpZwUOblJlZ0lkeFRpbWVBZGQJAMwIAgkAkQMCBQRvcmlnBQ5uUmVnSWR4RGVwb0FtdAkAzAgCCQCRAwIFBG9yaWcFEm5SZWdJZHhEZXBvQW10TGVmdAkAzAgCBQluZXdTdGF0dXMJAMwIAgkApAMBCAUJbGFzdEJsb2NrBmhlaWdodAkAzAgCCQCkAwEIBQlsYXN0QmxvY2sJdGltZXN0YW1wBQNuaWwFA1NFUAEQa2V5UmV3YXJkSGlzdG9yeQILbm9kZUFkZHJlc3MEdHhJZAkAuQkCCQDMCAICBiVzJXMlcwkAzAgCAgdoaXN0b3J5CQDMCAIJAKUIAQULbm9kZUFkZHJlc3MJAMwIAgkA2AQBBQR0eElkBQNuaWwFA1NFUAERZGF0YVJld2FyZEhpc3RvcnkFEHdhdmVzQW1vdW50VG90YWwOYmVuZWZpY2lhcnlBbXQLcHJvdG9jb2xBbXQQcHJvdG9jb2xBbXRQYXJ0MRBwcm90b2NvbEFtdFBhcnQyCQC5CQIJAMwIAgIOJWQlZCVkJWQlZCVkJWQJAMwIAgkApAMBCAUJbGFzdEJsb2NrBmhlaWdodAkAzAgCCQCkAwEIBQlsYXN0QmxvY2sJdGltZXN0YW1wCQDMCAIJAKQDAQUQd2F2ZXNBbW91bnRUb3RhbAkAzAgCCQCkAwEFDmJlbmVmaWNpYXJ5QW10CQDMCAIJAKQDAQULcHJvdG9jb2xBbXQJAMwIAgkApAMBBRBwcm90b2NvbEFtdFBhcnQxCQDMCAIJAKQDAQUQcHJvdG9jb2xBbXRQYXJ0MgUDbmlsBQNTRVABElJld2FyZEhpc3RvcnlFbnRyeQcLbm9kZUFkZHJlc3MEdHhJZAhtaW5lZEFtdA5iZW5lZmljaWFyeUFtdAtwcm90b2NvbEFtdAxwcm90b2NvbEFtdDEMcHJvdG9jb2xBbXQyCQELU3RyaW5nRW50cnkCCQEQa2V5UmV3YXJkSGlzdG9yeQIFC25vZGVBZGRyZXNzBQR0eElkCQERZGF0YVJld2FyZEhpc3RvcnkFBQhtaW5lZEFtdAUOYmVuZWZpY2lhcnlBbXQFC3Byb3RvY29sQW10BQxwcm90b2NvbEFtdDEFDHByb3RvY29sQW10MgEQZGF0YVJld2FyZFRvdGFscwUIbWluZWRBbXQOYmVuZWZpY2lhcnlBbXQLcHJvdG9jb2xBbXQQcHJvdG9jb2xBbXRQYXJ0MRBwcm90b2NvbEFtdFBhcnQyCQC5CQIJAMwIAgIKJWQlZCVkJWQlZAkAzAgCCQCkAwEFCG1pbmVkQW10CQDMCAIJAKQDAQUOYmVuZWZpY2lhcnlBbXQJAMwIAgkApAMBBQtwcm90b2NvbEFtdAkAzAgCCQCkAwEFEHByb3RvY29sQW10UGFydDEJAMwIAgkApAMBBRBwcm90b2NvbEFtdFBhcnQyBQNuaWwFA1NFUAERUmV3YXJkVG90YWxzRW50cnkGBW1pbmVyCG1pbmVkQW10DmJlbmVmaWNpYXJ5QW10C3Byb3RvY29sQW10EHByb3RvY29sQW10UGFydDEQcHJvdG9jb2xBbXRQYXJ0MgQUbm9kZVJld2FyZHNUb3RhbHNLRVkJARRrZXlOb2RlUmV3YXJkc1RvdGFscwEJAKUIAQUFbWluZXIEEG5vZGVSZXdhcmRzQXJyYXkJALUJAgkBDGdldFN0ck9yRWxzZQMFBHRoaXMFFG5vZGVSZXdhcmRzVG90YWxzS0VZCQEQZGF0YVJld2FyZFRvdGFscwUAAAAAAAAAAAAABQNTRVAEC25ld01pbmVkQW10CQBkAgkBDXBhcnNlSW50VmFsdWUBCQCRAwIFEG5vZGVSZXdhcmRzQXJyYXkAAQUIbWluZWRBbXQEEW5ld0JlbmVmaWNpYXJ5QW10CQBkAgkBDXBhcnNlSW50VmFsdWUBCQCRAwIFEG5vZGVSZXdhcmRzQXJyYXkAAgUOYmVuZWZpY2lhcnlBbXQEDm5ld1Byb3RvY29sQW10CQBkAgkBDXBhcnNlSW50VmFsdWUBCQCRAwIFEG5vZGVSZXdhcmRzQXJyYXkAAwULcHJvdG9jb2xBbXQEE25ld1Byb3RvY29sQW10UGFydDEJAGQCCQENcGFyc2VJbnRWYWx1ZQEJAJEDAgUQbm9kZVJld2FyZHNBcnJheQAEBRBwcm90b2NvbEFtdFBhcnQxBBNuZXdQcm90b2NvbEFtdFBhcnQyCQBkAgkBDXBhcnNlSW50VmFsdWUBCQCRAwIFEG5vZGVSZXdhcmRzQXJyYXkABQUQcHJvdG9jb2xBbXRQYXJ0MgkBC1N0cmluZ0VudHJ5AgUUbm9kZVJld2FyZHNUb3RhbHNLRVkJARBkYXRhUmV3YXJkVG90YWxzBQULbmV3TWluZWRBbXQFEW5ld0JlbmVmaWNpYXJ5QW10BQ5uZXdQcm90b2NvbEFtdAUTbmV3UHJvdG9jb2xBbXRQYXJ0MQUTbmV3UHJvdG9jb2xBbXRQYXJ0MgEUcmVzb2x2ZUJlbmVmaWNpYXJpZXMCDG1pbmVyQWRkcmVzcxRiZW5lZmljaWFyeUZyb21QYXJhbQQZYmVuZWZpY2lhcnlGcm9tTWluZXJTdGF0ZQkBEUBleHRyTmF0aXZlKDEwNjIpAQkBD2dldFN0cmluZ09yRmFpbAIFDG1pbmVyQWRkcmVzcwkBFWtleUJlbmVmaWNpYXJ5QWRkcmVzcwADCQAAAgkA8QcBBQxtaW5lckFkZHJlc3MFBHVuaXQFFGJlbmVmaWNpYXJ5RnJvbVBhcmFtBRliZW5lZmljaWFyeUZyb21NaW5lclN0YXRlAR1yZWFkTm9kZURhdGFBbmRWYWxpZGF0ZVN0YXR1cwIIbm9kZUFkZHIOZXhwZWN0ZWRTdGF0dXMEC25vZGVEYXRhT3B0CQCiCAEJAQ9rZXlOb2RlUmVnaXN0cnkBBQhub2RlQWRkcgMJAQEhAQkBCWlzRGVmaW5lZAEFC25vZGVEYXRhT3B0CQACAQkArAICAg1Vbmtub3duIE5vZGUgBQhub2RlQWRkcgQLbm9kZURhdGFTdHIJAQV2YWx1ZQEFC25vZGVEYXRhT3B0BAhub2RlRGF0YQkAtQkCBQtub2RlRGF0YVN0cgUDU0VQBApub2RlU3RhdHVzAwkAZwIJAJADAQUIbm9kZURhdGEACQkAkQMCBQhub2RlRGF0YQUNblJlZ0lkeFN0YXR1cwIHUEVORElORwMJAQIhPQIFCm5vZGVTdGF0dXMFDmV4cGVjdGVkU3RhdHVzCQACAQkArAICCQCsAgIJAKwCAgkArAICCQCsAgICBU5vZGUgBQhub2RlQWRkcgISIHN0YXR1cyBzaG91bGQgYmUgBQ5leHBlY3RlZFN0YXR1cwIPLCBidXQgYWN0dWFsbHkgBQpub2RlU3RhdHVzBQtub2RlRGF0YVN0cgkBaQENY29uc3RydWN0b3JWMQUNZGVwb3NpdEFtb3VudBJuZXV0cmlub0FkZHJlc3NTdHIUbWFzdGVyTm9kZUFkZHJlc3NTdHIOcHJvdG9jb2xQYXJ0WDYQYmVuaWZpY2FyeVBhcnRYNgMJAQIhPQIIBQFpBmNhbGxlcgUEdGhpcwkAAgECEnBlcm1pc3Npb25zIGRlbmllZAMJAGYCAAAFDWRlcG9zaXRBbW91bnQJAAIBAhxkZXBvc2l0QW1vdW50IGxlc3MgdGhlbiB6ZXJvAwkAZgIAAAUOcHJvdG9jb2xQYXJ0WDYJAAIBAh1wcm90b2NvbFBhcnRYNiBsZXNzIHRoZW4gemVybwMJAGYCAAAFEGJlbmlmaWNhcnlQYXJ0WDYJAAIBAh9iZW5pZmljYXJ5UGFydFg2IGxlc3MgdGhlbiB6ZXJvAwkBAiE9AgkAZAIFDnByb3RvY29sUGFydFg2BRBiZW5pZmljYXJ5UGFydFg2BQVNVUxUNgkAAgECE3BhcnRzIHN1bSBtdXN0IGJlIDEJAMwIAgkBDEludGVnZXJFbnRyeQIJARBrZXlEZXBvc2l0QW1vdW50AAUNZGVwb3NpdEFtb3VudAkAzAgCCQELU3RyaW5nRW50cnkCCQEVa2V5TmV1dHJpbm9BZGRyZXNzU3RyAAkBFXZhbGlkYXRlQWRkcmVzc09yRmFpbAEFEm5ldXRyaW5vQWRkcmVzc1N0cgkAzAgCCQELU3RyaW5nRW50cnkCCQEXa2V5TWFzdGVyTm9kZUFkZHJlc3NTdHIACQEVdmFsaWRhdGVBZGRyZXNzT3JGYWlsAQUUbWFzdGVyTm9kZUFkZHJlc3NTdHIJAMwIAgkBDEludGVnZXJFbnRyeQIJARFrZXlQcm90b2NvbFBhcnRYNgAFDnByb3RvY29sUGFydFg2CQDMCAIJAQxJbnRlZ2VyRW50cnkCCQETa2V5QmVuaWZpY2FyeVBhcnRYNgAFEGJlbmlmaWNhcnlQYXJ0WDYFA25pbAFpAQ1jb25zdHJ1Y3RvclYyAxBzd29wZmlBZGRyZXNzU3RyEWNvbnRyb2xBZGRyZXNzU3RyEXNhdmluZ3NBZGRyZXNzU3RyAwkBAiE9AggFAWkGY2FsbGVyBQR0aGlzCQACAQIScGVybWlzc2lvbnMgZGVuaWVkCQDMCAIJAQtTdHJpbmdFbnRyeQIJARxrZXlTd29wZmlVc2R0VXNkblBvb2xBZGRyZXNzAAkBFXZhbGlkYXRlQWRkcmVzc09yRmFpbAEFEHN3b3BmaUFkZHJlc3NTdHIJAMwIAgkBC1N0cmluZ0VudHJ5AgkBEWtleUNvbnRyb2xBZGRyZXNzAAkBFXZhbGlkYXRlQWRkcmVzc09yRmFpbAEFEWNvbnRyb2xBZGRyZXNzU3RyCQDMCAIJAQtTdHJpbmdFbnRyeQIJARFrZXlTYXZpbmdzQWRkcmVzcwAJARV2YWxpZGF0ZUFkZHJlc3NPckZhaWwBBRFzYXZpbmdzQWRkcmVzc1N0cgUDbmlsAWkBEXN1bWJpdEFwcGxpY2F0aW9uAAMJAQIhPQIJAJADAQgFAWkIcGF5bWVudHMAAQkAAgECIGV4YWN0IDEgcGF5bWVudCBtdXN0IGJlIGF0dGFjaGVkAwkBAiE9AggFAWkGY2FsbGVyCAUBaQxvcmlnaW5DYWxsZXIJAAIBAiJkYXBwIHRvIGRhcHAgY2FsbHMgYXJlIG5vdCBhbGxvd2VkBANwbXQJAJEDAggFAWkIcGF5bWVudHMAAAQHZGVwb3NpdAgFA3BtdAZhbW91bnQECnBtdEFzc2V0SWQJAQt2YWx1ZU9yRWxzZQIIBQNwbXQHYXNzZXRJZAUHV0FWRVNJRAQVZXhwZWN0ZWREZXBvc2l0QW1vdW50CQEMZ2V0SW50T3JGYWlsAQkBEGtleURlcG9zaXRBbW91bnQABAR0eElkCQDYBAEIBQFpDXRyYW5zYWN0aW9uSWQEC25vZGVBZGRyZXNzCQClCAEIBQFpBmNhbGxlcgQPbm9kZVJlZ2lzdHJ5S0VZCQEPa2V5Tm9kZVJlZ2lzdHJ5AQULbm9kZUFkZHJlc3MDCQECIT0CBQpwbXRBc3NldElkBQdXQVZFU0lECQACAQIib25seSBXYXZlcyBjYW4gYmUgdXNlZCBmb3IgZGVwb3NpdAMJAQIhPQIFB2RlcG9zaXQFFWV4cGVjdGVkRGVwb3NpdEFtb3VudAkAAgEJAKwCAgkArAICAghleGFjdGx5IAkApAMBBRVleHBlY3RlZERlcG9zaXRBbW91bnQCFiBXYXZlbGV0cyBhcmUgZXhwZWN0ZWQDCQECIT0CCQEMZ2V0U3RyT3JFbHNlAwUEdGhpcwUPbm9kZVJlZ2lzdHJ5S0VZAgACAAkAAgECHHlvdXIgbm9kZSBhbHJlYWR5IHJlZ2lzdGVyZWQJAMwIAgkBC1N0cmluZ0VudHJ5AgUPbm9kZVJlZ2lzdHJ5S0VZCQEQZm9ybWF0RGVwb3NpdEFkZAQFBHR4SWQFB2RlcG9zaXQFB2RlcG9zaXQCB1BFTkRJTkcFA25pbAFpARVkaXN0cmlidXRlTWluZXJSZXdhcmQBFWJlbmVmaWNpYXJ5QWRkcmVzc1N0cgQTbWluRGlzdHJpYnV0ZUFtb3VudAkBC3ZhbHVlT3JFbHNlAgkAmggCBQR0aGlzCQEWa2V5TWluRGlzdHJpYnV0ZUFtb3VudAAJAGgCABgFBU1VTFQ4AwkBAiE9AgkAkAMBCAUBaQhwYXltZW50cwABCQACAQIgZXhhY3QgMSBwYXltZW50IG11c3QgYmUgYXR0YWNoZWQDCQECIT0CCAUBaQZjYWxsZXIIBQFpDG9yaWdpbkNhbGxlcgkAAgECImRhcHAgdG8gZGFwcCBjYWxscyBhcmUgbm90IGFsbG93ZWQEA3BtdAkAkQMCCAUBaQhwYXltZW50cwAABApwbXRBc3NldElkCQELdmFsdWVPckVsc2UCCAUDcG10B2Fzc2V0SWQFB1dBVkVTSUQEBmZlZUFtdAgFAWkDZmVlBApmZWVBc3NldElkCQELdmFsdWVPckVsc2UCCAUBaQpmZWVBc3NldElkBQdXQVZFU0lEBApjYWxsUmV3YXJkCQBoAgUGZmVlQW10AAMECG1pbmVkQW10CQBlAggFA3BtdAZhbW91bnQFCmNhbGxSZXdhcmQDCQECIT0CCAUDcG10BmFtb3VudAUTbWluRGlzdHJpYnV0ZUFtb3VudAkAAgEJAKwCAgkArAICAh9hdHRhY2hlZCBwYXltZW50IG11c3QgYmUgZXhhY3QgCQCkAwEFE21pbkRpc3RyaWJ1dGVBbW91bnQCCSBXYXZlbGV0cwMJAQIhPQIFCnBtdEFzc2V0SWQFB1dBVkVTSUQJAAIBAiFvbmx5IFdhdmVzIGNhbiBiZSB1c2VkIGluIHBheW1lbnQDCQECIT0CBQpmZWVBc3NldElkBQdXQVZFU0lECQACAQIeb25seSBXYXZlcyBjYW4gYmUgdXNlZCBpbiBmZWVzAwkBAiE9AgUGZmVlQW10CQBpAgkAaAIABQUFTVVMVDgA6AcJAAIBAiBmZWUgYW1vdW50IGNvdWxkbid0IGV4Y2VlZCAwLjAwNQQPbmV1dHJpbm9BZGRyZXNzCQEQZ2V0QWRkcmVzc09yRmFpbAEJARVrZXlOZXV0cmlub0FkZHJlc3NTdHIABBFtYXN0ZXJOb2RlQWRkcmVzcwkBEGdldEFkZHJlc3NPckZhaWwBCQEXa2V5TWFzdGVyTm9kZUFkZHJlc3NTdHIABAxtaW5lckFkZHJlc3MIBQFpBmNhbGxlcgQUYmVuZWZpY2lhcnlGcm9tUGFyYW0JARFAZXh0ck5hdGl2ZSgxMDYyKQEFFWJlbmVmaWNpYXJ5QWRkcmVzc1N0cgQSYmVuZWZpY2lhcnlBZGRyZXNzCQEUcmVzb2x2ZUJlbmVmaWNpYXJpZXMCBQxtaW5lckFkZHJlc3MFFGJlbmVmaWNpYXJ5RnJvbVBhcmFtBBB0eFJld2FyZFJlY2VpdmVyBRRiZW5lZmljaWFyeUZyb21QYXJhbQQMcHJvdG9jb2xQYXJ0CQEMZ2V0SW50T3JGYWlsAQkBEWtleVByb3RvY29sUGFydFg2AAQPYmVuZWZpY2lhcnlQYXJ0CQEMZ2V0SW50T3JGYWlsAQkBE2tleUJlbmlmaWNhcnlQYXJ0WDYABA5iZW5lZmljaWFyeUFtdAkAawMFCG1pbmVkQW10BQ9iZW5lZmljaWFyeVBhcnQFBU1VTFQ2BAtwcm90b2NvbEFtdAkAZQIFCG1pbmVkQW10BQ5iZW5lZmljaWFyeUFtdAQKYnV5QmFja0FtdAkAaAIAeAUFTVVMVDgEDnNhdmluZ3NBZGRyZXNzCQERQGV4dHJOYXRpdmUoMTA2MikBCQEMZ2V0U3RyT3JFbHNlAwUEdGhpcwkBEWtleVNhdmluZ3NBZGRyZXNzAAIjM1BDN0RCaW1hdXlLeXR4OHRYaEtvNXUyRUNiNkZzbmRZS0gECnNhdmluZ3NBbXQJAQVhc0ludAEJAPwHBAUOc2F2aW5nc0FkZHJlc3MCCHdpdGhkcmF3CQDMCAIFCmJ1eUJhY2tBbXQFA25pbAUDbmlsBAx3eEFzc2V0SWRTdHICLEF0cXY1OUVZempGR3VpdEtWbk1SazZIOEZ1a2pvVjNrdFBvcmJFeXMyNW9uBA13eFN3YXBBZGRyZXNzCQERQGV4dHJOYXRpdmUoMTA2MikBCQEMZ2V0U3RyT3JFbHNlAwUEdGhpcwkBEGtleVd4U3dhcEFkZHJlc3MAAiMzUDY4ek5pdWZzdTF2aVpwdTFhWTNjZGFoUlJLY3ZWNU45MwQKc3dhcFJlc3VsdAkA/AcEBQ13eFN3YXBBZGRyZXNzAgRzd2FwCQDMCAIAAQkAzAgCBQx3eEFzc2V0SWRTdHIJAMwIAgkApQgBBRBuZXV0cmlub0NvbnRyYWN0BQNuaWwJAMwIAgkBD0F0dGFjaGVkUGF5bWVudAIFBHVuaXQFCnNhdmluZ3NBbXQFA25pbAMJAAACBQpzd2FwUmVzdWx0BQpzd2FwUmVzdWx0CQDMCAIJAQ5TY3JpcHRUcmFuc2ZlcgMFEmJlbmVmaWNpYXJ5QWRkcmVzcwUOYmVuZWZpY2lhcnlBbXQFBHVuaXQJAMwIAgkBDlNjcmlwdFRyYW5zZmVyAwUQbmV1dHJpbm9Db250cmFjdAULcHJvdG9jb2xBbXQFBHVuaXQJAMwIAgkBDlNjcmlwdFRyYW5zZmVyAwUQdHhSZXdhcmRSZWNlaXZlcgUKY2FsbFJld2FyZAUEdW5pdAkAzAgCCQESUmV3YXJkSGlzdG9yeUVudHJ5BwUMbWluZXJBZGRyZXNzCAUBaQ10cmFuc2FjdGlvbklkBQhtaW5lZEFtdAUOYmVuZWZpY2lhcnlBbXQFC3Byb3RvY29sQW10BQtwcm90b2NvbEFtdAAACQDMCAIJARFSZXdhcmRUb3RhbHNFbnRyeQYFDG1pbmVyQWRkcmVzcwUIbWluZWRBbXQFDmJlbmVmaWNpYXJ5QW10BQtwcm90b2NvbEFtdAULcHJvdG9jb2xBbXQAAAUDbmlsCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuAWkBGnZhbGlkYXRlQW5kQXBwcm92ZUxlYXNpbmdzAQZuTGlzdFMEBW5JZHhzCQDMCAIAAAkAzAgCAAEJAMwIAgACCQDMCAIAAwkAzAgCAAQJAMwIAgAFCQDMCAIABgkAzAgCAAcFA25pbAQFbkxpc3QJALUJAgUGbkxpc3RTBQNTRVAEDWV4cGVjdGVkQ291bnQJAJADAQUFbklkeHMED25ldXRyaW5vQWRkcmVzcwkBEGdldEFkZHJlc3NPckZhaWwBCQEVa2V5TmV1dHJpbm9BZGRyZXNzU3RyAAMJAQIhPQIIBQFpBmNhbGxlcgUPbmV1dHJpbm9BZGRyZXNzCQACAQIpdmFsaWRhdGVBbmRBcHByb3ZlTGVhc2luZ3Mgbm90IGF1dGhvcml6ZWQDCQECIT0CCQBqAgkAkAMBBQVuTGlzdAACAAAJAAIBAiRPbmx5IGV2ZW4gbnVtYmVyIG9mIG5vZGVzIGlzIGFsbG93ZWQDCQECIT0CCQCQAwEFBW5MaXN0BQ1leHBlY3RlZENvdW50CQACAQkArAICCQCsAgICBU9ubHkgCQCkAwEFDWV4cGVjdGVkQ291bnQCIiBvZiBub2RlcycgYWRkcmVzc2VzIGNhbiBiZSBwYXNzZWQKARdmb3JFYWNoTm9kZUNoYW5nZVN0YXR1cwIDYWNjAWkECG5vZGVBZGRyCQCRAwIFBW5MaXN0BQFpBAtub2RlRGF0YVN0cgkBHXJlYWROb2RlRGF0YUFuZFZhbGlkYXRlU3RhdHVzAgUIbm9kZUFkZHICB1BFTkRJTkcJAM0IAgUDYWNjCQELU3RyaW5nRW50cnkCCQEPa2V5Tm9kZVJlZ2lzdHJ5AQUIbm9kZUFkZHIJARlmb3JtYXREZXBvc2l0Q2hhbmdlU3RhdHVzAgULbm9kZURhdGFTdHICCEFQUFJPVkVEBBFhcHByb3ZlZE5vZGVzRGF0YQoAAiRsBQVuSWR4cwoAAiRzCQCQAwEFAiRsCgAFJGFjYzAFA25pbAoBBSRmMF8xAgIkYQIkaQMJAGcCBQIkaQUCJHMFAiRhCQEXZm9yRWFjaE5vZGVDaGFuZ2VTdGF0dXMCBQIkYQkAkQMCBQIkbAUCJGkKAQUkZjBfMgICJGECJGkDCQBnAgUCJGkFAiRzBQIkYQkAAgECE0xpc3Qgc2l6ZSBleGNlZWRzIDgJAQUkZjBfMgIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIFBSRhY2MwAAAAAQACAAMABAAFAAYABwAICQCUCgIFEWFwcHJvdmVkTm9kZXNEYXRhBQNuaWwBaQETcmVwbGFjZUFwcHJvdmVkTm9kZQQOb2xkTm9kZUFkZHJTdHIObmV3Tm9kZUFkZHJTdHIIZ3JvdXBOdW0NcGVuYWx0eUFtb3VudAMJAQIhPQIIBQFpBmNhbGxlcgUQbmV1dHJpbm9Db250cmFjdAkAAgECEnBlcm1pc3Npb25zIGRlbmllZAQKb2xkRGF0YVN0cgkBHXJlYWROb2RlRGF0YUFuZFZhbGlkYXRlU3RhdHVzAgUOb2xkTm9kZUFkZHJTdHICCEFQUFJPVkVEBApuZXdEYXRhU3RyCQEdcmVhZE5vZGVEYXRhQW5kVmFsaWRhdGVTdGF0dXMCBQ5uZXdOb2RlQWRkclN0cgIHUEVORElORwQRYXBwcm92ZWROb2RlTGlzdFMJAQ9nZXRTdHJpbmdPckZhaWwCBQR0aGlzCQETa2V5QXBwcm92ZWROb2RlTGlzdAADCQEBIQEJAQhjb250YWlucwIFEWFwcHJvdmVkTm9kZUxpc3RTBQ5vbGROb2RlQWRkclN0cgkAAgEJAKwCAgIiYXBwcm92ZWROb2RlTGlzdCBkb2VzIG5vdCBjb250YWluIAUOb2xkTm9kZUFkZHJTdHIEEHVwZGF0ZWROb2RlTGlzdFMJALoJAgkAvAkCBRFhcHByb3ZlZE5vZGVMaXN0UwUOb2xkTm9kZUFkZHJTdHIFDm5ld05vZGVBZGRyU3RyBAtvbGROb2RlQWRkcgkBEUBleHRyTmF0aXZlKDEwNjIpAQUOb2xkTm9kZUFkZHJTdHIEEmJlbmVmaWNpYXJ5QWRkcmVzcwkBFHJlc29sdmVCZW5lZmljaWFyaWVzAgULb2xkTm9kZUFkZHIFC29sZE5vZGVBZGRyBApkZXBvQW1vdW50CQENcGFyc2VJbnRWYWx1ZQEJAJEDAgkAtQkCBQpvbGREYXRhU3RyBQNTRVAFDm5SZWdJZHhEZXBvQW10BAdhY3Rpb25zCQDMCAIJAQtTdHJpbmdFbnRyeQIJARNrZXlBcHByb3ZlZE5vZGVMaXN0AAUQdXBkYXRlZE5vZGVMaXN0UwkAzAgCCQELRGVsZXRlRW50cnkBCQEPa2V5Tm9kZVJlZ2lzdHJ5AQUOb2xkTm9kZUFkZHJTdHIJAMwIAgkBC1N0cmluZ0VudHJ5AgkBD2tleU5vZGVSZWdpc3RyeQEFDm5ld05vZGVBZGRyU3RyCQEZZm9ybWF0RGVwb3NpdENoYW5nZVN0YXR1cwIFCm5ld0RhdGFTdHICCEFQUFJPVkVEBQNuaWwJAJQKAgMJAGYCBQpkZXBvQW1vdW50BQ1wZW5hbHR5QW1vdW50CQDNCAIFB2FjdGlvbnMJAQ5TY3JpcHRUcmFuc2ZlcgMFEmJlbmVmaWNpYXJ5QWRkcmVzcwkAZQIFCmRlcG9BbW91bnQFDXBlbmFsdHlBbW91bnQFBHVuaXQFB2FjdGlvbnMFBHVuaXQBaQENcmV0dXJuRGVwb3NpdAEObm9kZUFkZHJlc3NTdHIDCQECIT0CCQCQAwEIBQFpCHBheW1lbnRzAAAJAAIBAhNObyBwYXltZW50cyBhbGxvd2VkBA1jYWxsZXJBZGRyZXNzCAUBaQZjYWxsZXIED25vZGVSZWdpc3RyeUtFWQkBD2tleU5vZGVSZWdpc3RyeQEFDm5vZGVBZGRyZXNzU3RyBAtub2RlRGF0YU9wdAkAnQgCBQR0aGlzBQ9ub2RlUmVnaXN0cnlLRVkDCQEBIQEJAQlpc0RlZmluZWQBBQtub2RlRGF0YU9wdAkAAgEJAKwCAgIpQWRkcmVzcyBpcyBub3QgYXBwbGllZCBhcyBuZXV0cmlubyBub2RlOiAFDm5vZGVBZGRyZXNzU3RyBBBhcHByb3ZlZE5vZGVMaXN0CQEPZ2V0U3RyaW5nT3JGYWlsAgUEdGhpcwkBE2tleUFwcHJvdmVkTm9kZUxpc3QAAwkBCGNvbnRhaW5zAgUQYXBwcm92ZWROb2RlTGlzdAUObm9kZUFkZHJlc3NTdHIJAAIBAkxJbXBvc3NpYmxlIHRvIHJldHVybiBkZXBvc2l0IGZyb20gYXBwcm92ZWQgTm9kZSwgcGxlYXNlIGNvbnRhY3Qgc3VwcG9ydCB0ZWFtBAhub2RlRGF0YQkAtQkCCQEFdmFsdWUBBQtub2RlRGF0YU9wdAUDU0VQBAZzdGF0dXMDCQBnAgkAkAMBBQhub2RlRGF0YQAJCQCRAwIFCG5vZGVEYXRhBQ1uUmVnSWR4U3RhdHVzAgdQRU5ESU5HAwkBAiE9AgUGc3RhdHVzAgdQRU5ESU5HCQACAQIiQXBwbGljYXRpb24gc3RhdHVzIGlzIG5vdCBQRU5ESU5HIQQKZGVwb0Ftb3VudAkBDXBhcnNlSW50VmFsdWUBCQCRAwIFCG5vZGVEYXRhBQ5uUmVnSWR4RGVwb0FtdAQLbm9kZUFkZHJlc3MJARFAZXh0ck5hdGl2ZSgxMDYyKQEFDm5vZGVBZGRyZXNzU3RyBBJiZW5lZmljaWFyeUFkZHJlc3MJARRyZXNvbHZlQmVuZWZpY2lhcmllcwIFC25vZGVBZGRyZXNzBQtub2RlQWRkcmVzcwMJAQEhAQMDCQAAAgUNY2FsbGVyQWRkcmVzcwUEdGhpcwYDCQECIT0CBRJiZW5lZmljaWFyeUFkZHJlc3MFC25vZGVBZGRyZXNzCQAAAgUNY2FsbGVyQWRkcmVzcwUSYmVuZWZpY2lhcnlBZGRyZXNzBwYDCQAAAgUSYmVuZWZpY2lhcnlBZGRyZXNzBQtub2RlQWRkcmVzcwkAAAIFDWNhbGxlckFkZHJlc3MFC25vZGVBZGRyZXNzBwkAAgECIHJldHVybkRlcG9zaXQgcGVybWlzc2lvbnMgZGVuaWVkCQDMCAIJAQtEZWxldGVFbnRyeQEFD25vZGVSZWdpc3RyeUtFWQkAzAgCCQEOU2NyaXB0VHJhbnNmZXIDBRJiZW5lZmljaWFyeUFkZHJlc3MFCmRlcG9BbW91bnQFBHVuaXQFA25pbAFpAQ9pbml0Tm9kZXNWb3RpbmcEC3N0YXJ0SGVpZ2h0CWVuZEhlaWdodAdvcHRpb25zC2Rlc2NyaXB0aW9uAwkBAiE9AggFAWkGY2FsbGVyBQR0aGlzCQACAQIScGVybWlzc2lvbnMgZGVuaWVkBAxvcHRpb25zQXJyYXkJALUJAgUHb3B0aW9ucwICX18DCQBmAgkAkAMBBQxvcHRpb25zQXJyYXkACgkAAgECH01heCAxMCBvcHRpb25zIGNhbiBiZSBzcGVjaWZpZWQEEW5vZGVzVm90aW5nTnVtS0VZCQEVa2V5Tm9kZXNWb3RpbmdOZXh0TnVtAAQJdm90aW5nTnVtCQEMZ2V0SW50T3JFbHNlAgURbm9kZXNWb3RpbmdOdW1LRVkAAAQMdm90aW5nTnVtU3RyCQCkAwEFCXZvdGluZ051bQoBEHRyYW5zZm9ybU9wdGlvbnMCBWFjY3VtB25leHRPcHQEB29wdERhdGEJALUJAgUHbmV4dE9wdAIBOgQDb3B0CQCRAwIFB29wdERhdGEAAAQGb3B0S0VZCQEXa2V5Tm9kZXNWb3RpbmdPcHRpb25LZXkCBQx2b3RpbmdOdW1TdHIFA29wdAQIb3B0RGVzY3IJAJEDAgUHb3B0RGF0YQABBBBvcHRUb3RhbFZvdGVzS0VZCQEea2V5Tm9kZXNWb3RpbmdPcHRpb25Ub3RhbFZvdGVzAgUMdm90aW5nTnVtU3RyBQNvcHQJAM0IAgkAzQgCBQVhY2N1bQkBC1N0cmluZ0VudHJ5AgUGb3B0S0VZBQhvcHREZXNjcgkBDEludGVnZXJFbnRyeQIFEG9wdFRvdGFsVm90ZXNLRVkAAAkAzQgCCQDNCAIJAM0IAgkAzQgCCgACJGwFDG9wdGlvbnNBcnJheQoAAiRzCQCQAwEFAiRsCgAFJGFjYzAFA25pbAoBBSRmMF8xAgIkYQIkaQMJAGcCBQIkaQUCJHMFAiRhCQEQdHJhbnNmb3JtT3B0aW9ucwIFAiRhCQCRAwIFAiRsBQIkaQoBBSRmMF8yAgIkYQIkaQMJAGcCBQIkaQUCJHMFAiRhCQACAQIUTGlzdCBzaXplIGV4Y2VlZHMgMTAJAQUkZjBfMgIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIFBSRhY2MwAAAAAQACAAMABAAFAAYABwAIAAkACgkBDEludGVnZXJFbnRyeQIFEW5vZGVzVm90aW5nTnVtS0VZCQBkAgUJdm90aW5nTnVtAAEJAQtTdHJpbmdFbnRyeQIJARJrZXlOb2Rlc1ZvdGluZ0Rlc2MBBQx2b3RpbmdOdW1TdHIFC2Rlc2NyaXB0aW9uCQEMSW50ZWdlckVudHJ5AgkBGWtleU5vZGVzVm90aW5nU3RhcnRIZWlnaHQBBQx2b3RpbmdOdW1TdHIFC3N0YXJ0SGVpZ2h0CQEMSW50ZWdlckVudHJ5AgkBF2tleU5vZGVzVm90aW5nRW5kSGVpZ2h0AQUMdm90aW5nTnVtU3RyBQllbmRIZWlnaHQBaQEKdm90ZUJ5Tm9kZQMObm9kZUFkZHJlc3NTdHIJdm90aW5nTnVtCW9wdGlvbk51bQQMdmFsaWRhdGVOb2RlCQEdcmVhZE5vZGVEYXRhQW5kVmFsaWRhdGVTdGF0dXMCBQ5ub2RlQWRkcmVzc1N0cgIIQVBQUk9WRUQDCQAAAgUMdmFsaWRhdGVOb2RlBQx2YWxpZGF0ZU5vZGUEC25vZGVBZGRyZXNzCQERQGV4dHJOYXRpdmUoMTA2MikBBQ5ub2RlQWRkcmVzc1N0cgQPbm9kZUJlbmVmaWNpYXJ5CQEPZ2V0U3RyaW5nT3JGYWlsAgULbm9kZUFkZHJlc3MJARVrZXlCZW5lZmljaWFyeUFkZHJlc3MABA92b3RlckFkZHJlc3NTdHIJAKUIAQgFAWkGY2FsbGVyAwkBAiE9AgUPdm90ZXJBZGRyZXNzU3RyBQ9ub2RlQmVuZWZpY2lhcnkJAAIBAiVPbmx5IE5vZGUncyBiZW5lZmljaWFyeSBjYW4gZG8gdm90aW5nBAtzdGFydEhlaWdodAkBDGdldEludE9yRmFpbAEJARlrZXlOb2Rlc1ZvdGluZ1N0YXJ0SGVpZ2h0AQUJdm90aW5nTnVtBAllbmRIZWlnaHQJAQxnZXRJbnRPckZhaWwBCQEXa2V5Tm9kZXNWb3RpbmdFbmRIZWlnaHQBBQl2b3RpbmdOdW0DCQBmAgULc3RhcnRIZWlnaHQFBmhlaWdodAkAAgECH1ZvdGluZyBoYXMgbm90IGJlZW4gc3RhcnRlZCB5ZXQDCQBmAgUGaGVpZ2h0BQllbmRIZWlnaHQJAAIBAg9Wb3RpbmcgZmluaXNoZWQEDW5vZGVDaG9pY2VLRVkJARhrZXlOb2Rlc1ZvdGluZ05vZGVDaG9pY2UCBQl2b3RpbmdOdW0FDm5vZGVBZGRyZXNzU3RyBApub2RlQ2hvaWNlCQEMZ2V0U3RyT3JFbHNlAwUEdGhpcwUNbm9kZUNob2ljZUtFWQIAAwkBAiE9AgUKbm9kZUNob2ljZQIACQACAQI3Tm9kZSBoYXMgYWxyZWFkeSB2b3RlZC4gSXQgaXMgYWxsb3dlZCB0byB2b3RlIG9ubHkgb25jZQQTb3B0aW9uVG90YWxWb3Rlc0tFWQkBHmtleU5vZGVzVm90aW5nT3B0aW9uVG90YWxWb3RlcwIFCXZvdGluZ051bQUJb3B0aW9uTnVtBBBvcHRpb25Ub3RhbFZvdGVzCQEMZ2V0SW50T3JGYWlsAQUTb3B0aW9uVG90YWxWb3Rlc0tFWQkAzAgCCQEMSW50ZWdlckVudHJ5AgUTb3B0aW9uVG90YWxWb3Rlc0tFWQkAZAIFEG9wdGlvblRvdGFsVm90ZXMAAQkAzAgCCQELU3RyaW5nRW50cnkCBQ1ub2RlQ2hvaWNlS0VZBQlvcHRpb25OdW0FA25pbAkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgBVFZdj", "chainId": 87, "height": 3638820, "applicationStatus": "succeeded", "spentComplexity": 0 } View: original | compacted Prev: tyeGHVqBvsGEjbhFt1uvbbGpEBde22TJF4rmBrHLxWp Next: BptRuPzdmjjZB26fZErG8Kwog3Ha7XCpyJxoywxQaBn1 Diff:
Old | New | Differences | |
---|---|---|---|
12 | 12 | let WAVESID = fromBase58String(WAVESIDSTR) | |
13 | 13 | ||
14 | 14 | func getIntOrFail (key) = valueOrErrorMessage(getInteger(this, key), (("Mandatory this." + key) + " is not defined")) | |
15 | + | ||
16 | + | ||
17 | + | func getIntOrElse (key,val) = valueOrElse(getInteger(this, key), val) | |
15 | 18 | ||
16 | 19 | ||
17 | 20 | func getStrOrElse (address,key,defaultVal) = valueOrElse(getString(address, key), defaultVal) | |
92 | 95 | ||
93 | 96 | ||
94 | 97 | func keyBeneficiaryAddress () = "%s%s__cfg__beneficiaryAddress" | |
98 | + | ||
99 | + | ||
100 | + | func keyNodesVotingNextNum () = "%s%s__nodesVotingCfg__nextVotingNum" | |
101 | + | ||
102 | + | ||
103 | + | func keyNodesVotingOptionKey (votingNum,optionNum) = makeString(["%s%d%s__nodesVotingCfg", votingNum, optionNum], SEP) | |
104 | + | ||
105 | + | ||
106 | + | func keyNodesVotingDesc (votingNum) = makeString(["%s%d%s__nodesVotingCfg", votingNum, "description"], SEP) | |
107 | + | ||
108 | + | ||
109 | + | func keyNodesVotingStartHeight (votingNum) = makeString(["%s%d%s__nodesVotingCfg", votingNum, "votingStartHeight"], SEP) | |
110 | + | ||
111 | + | ||
112 | + | func keyNodesVotingEndHeight (votingNum) = makeString(["%s%d%s__nodesVotingCfg", votingNum, "votingEndHeight"], SEP) | |
113 | + | ||
114 | + | ||
115 | + | func keyNodesVotingOptionTotalVotes (votingNum,optionNum) = makeString(["%s%d%s%s__nodesVotingData", votingNum, "optionTotalVotes", optionNum], SEP) | |
116 | + | ||
117 | + | ||
118 | + | func keyNodesVotingNodeChoice (votingNum,nodeAddressStr) = makeString(["%s%d%s%s__nodesVotingData", votingNum, "nodeChoice", nodeAddressStr], SEP) | |
95 | 119 | ||
96 | 120 | ||
97 | 121 | let IdxControlCfgNeutrinoDapp = 1 | |
416 | 440 | } | |
417 | 441 | ||
418 | 442 | ||
443 | + | ||
444 | + | @Callable(i) | |
445 | + | func initNodesVoting (startHeight,endHeight,options,description) = if ((i.caller != this)) | |
446 | + | then throw("permissions denied") | |
447 | + | else { | |
448 | + | let optionsArray = split(options, "__") | |
449 | + | if ((size(optionsArray) > 10)) | |
450 | + | then throw("Max 10 options can be specified") | |
451 | + | else { | |
452 | + | let nodesVotingNumKEY = keyNodesVotingNextNum() | |
453 | + | let votingNum = getIntOrElse(nodesVotingNumKEY, 0) | |
454 | + | let votingNumStr = toString(votingNum) | |
455 | + | func transformOptions (accum,nextOpt) = { | |
456 | + | let optData = split(nextOpt, ":") | |
457 | + | let opt = optData[0] | |
458 | + | let optKEY = keyNodesVotingOptionKey(votingNumStr, opt) | |
459 | + | let optDescr = optData[1] | |
460 | + | let optTotalVotesKEY = keyNodesVotingOptionTotalVotes(votingNumStr, opt) | |
461 | + | ((accum :+ StringEntry(optKEY, optDescr)) :+ IntegerEntry(optTotalVotesKEY, 0)) | |
462 | + | } | |
463 | + | ||
464 | + | (((({ | |
465 | + | let $l = optionsArray | |
466 | + | let $s = size($l) | |
467 | + | let $acc0 = nil | |
468 | + | func $f0_1 ($a,$i) = if (($i >= $s)) | |
469 | + | then $a | |
470 | + | else transformOptions($a, $l[$i]) | |
471 | + | ||
472 | + | func $f0_2 ($a,$i) = if (($i >= $s)) | |
473 | + | then $a | |
474 | + | else throw("List size exceeds 10") | |
475 | + | ||
476 | + | $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) | |
477 | + | } :+ IntegerEntry(nodesVotingNumKEY, (votingNum + 1))) :+ StringEntry(keyNodesVotingDesc(votingNumStr), description)) :+ IntegerEntry(keyNodesVotingStartHeight(votingNumStr), startHeight)) :+ IntegerEntry(keyNodesVotingEndHeight(votingNumStr), endHeight)) | |
478 | + | } | |
479 | + | } | |
480 | + | ||
481 | + | ||
482 | + | ||
483 | + | @Callable(i) | |
484 | + | func voteByNode (nodeAddressStr,votingNum,optionNum) = { | |
485 | + | let validateNode = readNodeDataAndValidateStatus(nodeAddressStr, "APPROVED") | |
486 | + | if ((validateNode == validateNode)) | |
487 | + | then { | |
488 | + | let nodeAddress = addressFromStringValue(nodeAddressStr) | |
489 | + | let nodeBeneficiary = getStringOrFail(nodeAddress, keyBeneficiaryAddress()) | |
490 | + | let voterAddressStr = toString(i.caller) | |
491 | + | if ((voterAddressStr != nodeBeneficiary)) | |
492 | + | then throw("Only Node's beneficiary can do voting") | |
493 | + | else { | |
494 | + | let startHeight = getIntOrFail(keyNodesVotingStartHeight(votingNum)) | |
495 | + | let endHeight = getIntOrFail(keyNodesVotingEndHeight(votingNum)) | |
496 | + | if ((startHeight > height)) | |
497 | + | then throw("Voting has not been started yet") | |
498 | + | else if ((height > endHeight)) | |
499 | + | then throw("Voting finished") | |
500 | + | else { | |
501 | + | let nodeChoiceKEY = keyNodesVotingNodeChoice(votingNum, nodeAddressStr) | |
502 | + | let nodeChoice = getStrOrElse(this, nodeChoiceKEY, "") | |
503 | + | if ((nodeChoice != "")) | |
504 | + | then throw("Node has already voted. It is allowed to vote only once") | |
505 | + | else { | |
506 | + | let optionTotalVotesKEY = keyNodesVotingOptionTotalVotes(votingNum, optionNum) | |
507 | + | let optionTotalVotes = getIntOrFail(optionTotalVotesKEY) | |
508 | + | [IntegerEntry(optionTotalVotesKEY, (optionTotalVotes + 1)), StringEntry(nodeChoiceKEY, optionNum)] | |
509 | + | } | |
510 | + | } | |
511 | + | } | |
512 | + | } | |
513 | + | else throw("Strict value is not equal to itself.") | |
514 | + | } | |
515 | + | ||
516 | + |
Old | New | Differences | |
---|---|---|---|
1 | 1 | {-# STDLIB_VERSION 6 #-} | |
2 | 2 | {-# SCRIPT_TYPE ACCOUNT #-} | |
3 | 3 | {-# CONTENT_TYPE DAPP #-} | |
4 | 4 | let SEP = "__" | |
5 | 5 | ||
6 | 6 | let MULT6 = 1000000 | |
7 | 7 | ||
8 | 8 | let MULT8 = 100000000 | |
9 | 9 | ||
10 | 10 | let WAVESIDSTR = "WAVES" | |
11 | 11 | ||
12 | 12 | let WAVESID = fromBase58String(WAVESIDSTR) | |
13 | 13 | ||
14 | 14 | func getIntOrFail (key) = valueOrErrorMessage(getInteger(this, key), (("Mandatory this." + key) + " is not defined")) | |
15 | + | ||
16 | + | ||
17 | + | func getIntOrElse (key,val) = valueOrElse(getInteger(this, key), val) | |
15 | 18 | ||
16 | 19 | ||
17 | 20 | func getStrOrElse (address,key,defaultVal) = valueOrElse(getString(address, key), defaultVal) | |
18 | 21 | ||
19 | 22 | ||
20 | 23 | func getStringOrFail (address,key) = valueOrErrorMessage(getString(address, key), (("Mandatory this." + key) + " is not defined")) | |
21 | 24 | ||
22 | 25 | ||
23 | 26 | func getAddressOrFail (key) = valueOrErrorMessage(addressFromString(getStringOrFail(this, key)), (("Fail to convert this." + key) + " value into Address")) | |
24 | 27 | ||
25 | 28 | ||
26 | 29 | func asAnyList (val) = match val { | |
27 | 30 | case valAnyList: List[Any] => | |
28 | 31 | valAnyList | |
29 | 32 | case _ => | |
30 | 33 | throw("fail to cast into List[Any]") | |
31 | 34 | } | |
32 | 35 | ||
33 | 36 | ||
34 | 37 | func asInt (v) = match v { | |
35 | 38 | case i: Int => | |
36 | 39 | i | |
37 | 40 | case _ => | |
38 | 41 | throw("fail to cast into Int") | |
39 | 42 | } | |
40 | 43 | ||
41 | 44 | ||
42 | 45 | func asByteVecot (v) = match v { | |
43 | 46 | case i: ByteVector => | |
44 | 47 | i | |
45 | 48 | case _ => | |
46 | 49 | throw("fail to cast into ByteVector") | |
47 | 50 | } | |
48 | 51 | ||
49 | 52 | ||
50 | 53 | func validateAddressOrFail (addressStr) = { | |
51 | 54 | let address = valueOrErrorMessage(addressFromString(addressStr), ("fail to convert into address " + addressStr)) | |
52 | 55 | if ((0 > wavesBalance(address).available)) | |
53 | 56 | then throw() | |
54 | 57 | else addressStr | |
55 | 58 | } | |
56 | 59 | ||
57 | 60 | ||
58 | 61 | func keyNodeRegistry (address) = ("%s__" + address) | |
59 | 62 | ||
60 | 63 | ||
61 | 64 | func keyNodeRewardsTotals (address) = ("%s%s__totals__" + address) | |
62 | 65 | ||
63 | 66 | ||
64 | 67 | func keyApprovedNodeList () = "%s__approvedNodeList" | |
65 | 68 | ||
66 | 69 | ||
67 | 70 | func keyDepositAmount () = "%s%s__cfg__depositAmount" | |
68 | 71 | ||
69 | 72 | ||
70 | 73 | func keyNeutrinoAddressStr () = "%s%s__cfg__neutrinoAddress" | |
71 | 74 | ||
72 | 75 | ||
73 | 76 | func keyMasterNodeAddressStr () = "%s%s__cfg__mainNeutrinoNodeAddress" | |
74 | 77 | ||
75 | 78 | ||
76 | 79 | func keyProtocolPartX6 () = "%s%s__cfg__protocolPartX6" | |
77 | 80 | ||
78 | 81 | ||
79 | 82 | func keyBenificaryPartX6 () = "%s%s__cfg__benificaryPartX6" | |
80 | 83 | ||
81 | 84 | ||
82 | 85 | func keyMinDistributeAmount () = "%s%s__cfg__minDistributeAmount" | |
83 | 86 | ||
84 | 87 | ||
85 | 88 | func keySwopfiUsdtUsdnPoolAddress () = "%s%s__cfg__swopfiUsdtUsdnPoolAddress" | |
86 | 89 | ||
87 | 90 | ||
88 | 91 | func keyWxSwapAddress () = "%s%s__cfg__wxSwapAddress" | |
89 | 92 | ||
90 | 93 | ||
91 | 94 | func keySavingsAddress () = "%s%s__cfg__savingsAddress" | |
92 | 95 | ||
93 | 96 | ||
94 | 97 | func keyBeneficiaryAddress () = "%s%s__cfg__beneficiaryAddress" | |
98 | + | ||
99 | + | ||
100 | + | func keyNodesVotingNextNum () = "%s%s__nodesVotingCfg__nextVotingNum" | |
101 | + | ||
102 | + | ||
103 | + | func keyNodesVotingOptionKey (votingNum,optionNum) = makeString(["%s%d%s__nodesVotingCfg", votingNum, optionNum], SEP) | |
104 | + | ||
105 | + | ||
106 | + | func keyNodesVotingDesc (votingNum) = makeString(["%s%d%s__nodesVotingCfg", votingNum, "description"], SEP) | |
107 | + | ||
108 | + | ||
109 | + | func keyNodesVotingStartHeight (votingNum) = makeString(["%s%d%s__nodesVotingCfg", votingNum, "votingStartHeight"], SEP) | |
110 | + | ||
111 | + | ||
112 | + | func keyNodesVotingEndHeight (votingNum) = makeString(["%s%d%s__nodesVotingCfg", votingNum, "votingEndHeight"], SEP) | |
113 | + | ||
114 | + | ||
115 | + | func keyNodesVotingOptionTotalVotes (votingNum,optionNum) = makeString(["%s%d%s%s__nodesVotingData", votingNum, "optionTotalVotes", optionNum], SEP) | |
116 | + | ||
117 | + | ||
118 | + | func keyNodesVotingNodeChoice (votingNum,nodeAddressStr) = makeString(["%s%d%s%s__nodesVotingData", votingNum, "nodeChoice", nodeAddressStr], SEP) | |
95 | 119 | ||
96 | 120 | ||
97 | 121 | let IdxControlCfgNeutrinoDapp = 1 | |
98 | 122 | ||
99 | 123 | let IdxControlCfgAuctionDapp = 2 | |
100 | 124 | ||
101 | 125 | let IdxControlCfgRpdDapp = 3 | |
102 | 126 | ||
103 | 127 | let IdxControlCfgMathDapp = 4 | |
104 | 128 | ||
105 | 129 | let IdxControlCfgLiquidationDapp = 5 | |
106 | 130 | ||
107 | 131 | let IdxControlCfgRestDapp = 6 | |
108 | 132 | ||
109 | 133 | let IdxControlCfgNodeRegistryDapp = 7 | |
110 | 134 | ||
111 | 135 | let IdxControlCfgNsbtStakingDapp = 8 | |
112 | 136 | ||
113 | 137 | let IdxControlCfgMediatorDapp = 9 | |
114 | 138 | ||
115 | 139 | let IdxControlCfgSurfStakingDapp = 10 | |
116 | 140 | ||
117 | 141 | let IdxControlCfgGnsbtControllerDapp = 11 | |
118 | 142 | ||
119 | 143 | let IdxControlCfgRestV2Dapp = 12 | |
120 | 144 | ||
121 | 145 | let IdxControlCfgGovernanceDapp = 13 | |
122 | 146 | ||
123 | 147 | func keyControlAddress () = "%s%s__config__controlAddress" | |
124 | 148 | ||
125 | 149 | ||
126 | 150 | func keyControlCfg () = "%s__controlConfig" | |
127 | 151 | ||
128 | 152 | ||
129 | 153 | func keyUsdnAssetId () = "neutrino_asset_id" | |
130 | 154 | ||
131 | 155 | ||
132 | 156 | func readControlCfgOrFail (control) = split_4C(getStringOrFail(control, keyControlCfg()), SEP) | |
133 | 157 | ||
134 | 158 | ||
135 | 159 | func getContractAddressOrFail (controlCfg,idx) = valueOrErrorMessage(addressFromString(controlCfg[idx]), ("Control cfg doesn't contain address at index " + toString(idx))) | |
136 | 160 | ||
137 | 161 | ||
138 | 162 | let controlContract = addressFromStringValue(valueOrElse(getString(this, keyControlAddress()), "3P5Bfd58PPfNvBM2Hy8QfbcDqMeNtzg7KfP")) | |
139 | 163 | ||
140 | 164 | let controlCfg = readControlCfgOrFail(controlContract) | |
141 | 165 | ||
142 | 166 | let neutrinoContract = getContractAddressOrFail(controlCfg, IdxControlCfgNeutrinoDapp) | |
143 | 167 | ||
144 | 168 | let usdnAssetIdStr = getStrOrElse(neutrinoContract, keyUsdnAssetId(), "DG2xFkPdDwKUoBkzGAhQtLpSGzfXLiCYPEzeKH2Ad24p") | |
145 | 169 | ||
146 | 170 | let usdnAssetId = fromBase58String(usdnAssetIdStr) | |
147 | 171 | ||
148 | 172 | let nRegIdxTxAdd = 1 | |
149 | 173 | ||
150 | 174 | let nRegIdxHeightAdd = 2 | |
151 | 175 | ||
152 | 176 | let nRegIdxTimeAdd = 3 | |
153 | 177 | ||
154 | 178 | let nRegIdxDepoAmt = 4 | |
155 | 179 | ||
156 | 180 | let nRegIdxDepoAmtLeft = 5 | |
157 | 181 | ||
158 | 182 | let nRegIdxStatus = 6 | |
159 | 183 | ||
160 | 184 | let nRegIdxStatusChangeHeight = 7 | |
161 | 185 | ||
162 | 186 | let nRegIdxStatusChangeTime = 8 | |
163 | 187 | ||
164 | 188 | func formatDepositAdd (txIdStr,depositAmount,remainingDepositAmount,status) = makeString(["%s%d%d%d%d%s%d%d", txIdStr, toString(lastBlock.height), toString(lastBlock.timestamp), toString(depositAmount), toString(remainingDepositAmount), status, "0", "0"], SEP) | |
165 | 189 | ||
166 | 190 | ||
167 | 191 | func formatDepositChangeStatus (origS,newStatus) = { | |
168 | 192 | let orig = split(origS, SEP) | |
169 | 193 | makeString(["%s%d%d%d%d%s%d%d", orig[nRegIdxTxAdd], orig[nRegIdxHeightAdd], orig[nRegIdxTimeAdd], orig[nRegIdxDepoAmt], orig[nRegIdxDepoAmtLeft], newStatus, toString(lastBlock.height), toString(lastBlock.timestamp)], SEP) | |
170 | 194 | } | |
171 | 195 | ||
172 | 196 | ||
173 | 197 | func keyRewardHistory (nodeAddress,txId) = makeString(["%s%s%s", "history", toString(nodeAddress), toBase58String(txId)], SEP) | |
174 | 198 | ||
175 | 199 | ||
176 | 200 | func dataRewardHistory (wavesAmountTotal,beneficiaryAmt,protocolAmt,protocolAmtPart1,protocolAmtPart2) = makeString(["%d%d%d%d%d%d%d", toString(lastBlock.height), toString(lastBlock.timestamp), toString(wavesAmountTotal), toString(beneficiaryAmt), toString(protocolAmt), toString(protocolAmtPart1), toString(protocolAmtPart2)], SEP) | |
177 | 201 | ||
178 | 202 | ||
179 | 203 | func RewardHistoryEntry (nodeAddress,txId,minedAmt,beneficiaryAmt,protocolAmt,protocolAmt1,protocolAmt2) = StringEntry(keyRewardHistory(nodeAddress, txId), dataRewardHistory(minedAmt, beneficiaryAmt, protocolAmt, protocolAmt1, protocolAmt2)) | |
180 | 204 | ||
181 | 205 | ||
182 | 206 | func dataRewardTotals (minedAmt,beneficiaryAmt,protocolAmt,protocolAmtPart1,protocolAmtPart2) = makeString(["%d%d%d%d%d", toString(minedAmt), toString(beneficiaryAmt), toString(protocolAmt), toString(protocolAmtPart1), toString(protocolAmtPart2)], SEP) | |
183 | 207 | ||
184 | 208 | ||
185 | 209 | func RewardTotalsEntry (miner,minedAmt,beneficiaryAmt,protocolAmt,protocolAmtPart1,protocolAmtPart2) = { | |
186 | 210 | let nodeRewardsTotalsKEY = keyNodeRewardsTotals(toString(miner)) | |
187 | 211 | let nodeRewardsArray = split(getStrOrElse(this, nodeRewardsTotalsKEY, dataRewardTotals(0, 0, 0, 0, 0)), SEP) | |
188 | 212 | let newMinedAmt = (parseIntValue(nodeRewardsArray[1]) + minedAmt) | |
189 | 213 | let newBeneficiaryAmt = (parseIntValue(nodeRewardsArray[2]) + beneficiaryAmt) | |
190 | 214 | let newProtocolAmt = (parseIntValue(nodeRewardsArray[3]) + protocolAmt) | |
191 | 215 | let newProtocolAmtPart1 = (parseIntValue(nodeRewardsArray[4]) + protocolAmtPart1) | |
192 | 216 | let newProtocolAmtPart2 = (parseIntValue(nodeRewardsArray[5]) + protocolAmtPart2) | |
193 | 217 | StringEntry(nodeRewardsTotalsKEY, dataRewardTotals(newMinedAmt, newBeneficiaryAmt, newProtocolAmt, newProtocolAmtPart1, newProtocolAmtPart2)) | |
194 | 218 | } | |
195 | 219 | ||
196 | 220 | ||
197 | 221 | func resolveBeneficiaries (minerAddress,beneficiaryFromParam) = { | |
198 | 222 | let beneficiaryFromMinerState = addressFromStringValue(getStringOrFail(minerAddress, keyBeneficiaryAddress())) | |
199 | 223 | if ((scriptHash(minerAddress) == unit)) | |
200 | 224 | then beneficiaryFromParam | |
201 | 225 | else beneficiaryFromMinerState | |
202 | 226 | } | |
203 | 227 | ||
204 | 228 | ||
205 | 229 | func readNodeDataAndValidateStatus (nodeAddr,expectedStatus) = { | |
206 | 230 | let nodeDataOpt = getString(keyNodeRegistry(nodeAddr)) | |
207 | 231 | if (!(isDefined(nodeDataOpt))) | |
208 | 232 | then throw(("Unknown Node " + nodeAddr)) | |
209 | 233 | else { | |
210 | 234 | let nodeDataStr = value(nodeDataOpt) | |
211 | 235 | let nodeData = split(nodeDataStr, SEP) | |
212 | 236 | let nodeStatus = if ((size(nodeData) >= 9)) | |
213 | 237 | then nodeData[nRegIdxStatus] | |
214 | 238 | else "PENDING" | |
215 | 239 | if ((nodeStatus != expectedStatus)) | |
216 | 240 | then throw(((((("Node " + nodeAddr) + " status should be ") + expectedStatus) + ", but actually ") + nodeStatus)) | |
217 | 241 | else nodeDataStr | |
218 | 242 | } | |
219 | 243 | } | |
220 | 244 | ||
221 | 245 | ||
222 | 246 | @Callable(i) | |
223 | 247 | func constructorV1 (depositAmount,neutrinoAddressStr,masterNodeAddressStr,protocolPartX6,benificaryPartX6) = if ((i.caller != this)) | |
224 | 248 | then throw("permissions denied") | |
225 | 249 | else if ((0 > depositAmount)) | |
226 | 250 | then throw("depositAmount less then zero") | |
227 | 251 | else if ((0 > protocolPartX6)) | |
228 | 252 | then throw("protocolPartX6 less then zero") | |
229 | 253 | else if ((0 > benificaryPartX6)) | |
230 | 254 | then throw("benificaryPartX6 less then zero") | |
231 | 255 | else if (((protocolPartX6 + benificaryPartX6) != MULT6)) | |
232 | 256 | then throw("parts sum must be 1") | |
233 | 257 | else [IntegerEntry(keyDepositAmount(), depositAmount), StringEntry(keyNeutrinoAddressStr(), validateAddressOrFail(neutrinoAddressStr)), StringEntry(keyMasterNodeAddressStr(), validateAddressOrFail(masterNodeAddressStr)), IntegerEntry(keyProtocolPartX6(), protocolPartX6), IntegerEntry(keyBenificaryPartX6(), benificaryPartX6)] | |
234 | 258 | ||
235 | 259 | ||
236 | 260 | ||
237 | 261 | @Callable(i) | |
238 | 262 | func constructorV2 (swopfiAddressStr,controlAddressStr,savingsAddressStr) = if ((i.caller != this)) | |
239 | 263 | then throw("permissions denied") | |
240 | 264 | else [StringEntry(keySwopfiUsdtUsdnPoolAddress(), validateAddressOrFail(swopfiAddressStr)), StringEntry(keyControlAddress(), validateAddressOrFail(controlAddressStr)), StringEntry(keySavingsAddress(), validateAddressOrFail(savingsAddressStr))] | |
241 | 265 | ||
242 | 266 | ||
243 | 267 | ||
244 | 268 | @Callable(i) | |
245 | 269 | func sumbitApplication () = if ((size(i.payments) != 1)) | |
246 | 270 | then throw("exact 1 payment must be attached") | |
247 | 271 | else if ((i.caller != i.originCaller)) | |
248 | 272 | then throw("dapp to dapp calls are not allowed") | |
249 | 273 | else { | |
250 | 274 | let pmt = i.payments[0] | |
251 | 275 | let deposit = pmt.amount | |
252 | 276 | let pmtAssetId = valueOrElse(pmt.assetId, WAVESID) | |
253 | 277 | let expectedDepositAmount = getIntOrFail(keyDepositAmount()) | |
254 | 278 | let txId = toBase58String(i.transactionId) | |
255 | 279 | let nodeAddress = toString(i.caller) | |
256 | 280 | let nodeRegistryKEY = keyNodeRegistry(nodeAddress) | |
257 | 281 | if ((pmtAssetId != WAVESID)) | |
258 | 282 | then throw("only Waves can be used for deposit") | |
259 | 283 | else if ((deposit != expectedDepositAmount)) | |
260 | 284 | then throw((("exactly " + toString(expectedDepositAmount)) + " Wavelets are expected")) | |
261 | 285 | else if ((getStrOrElse(this, nodeRegistryKEY, "") != "")) | |
262 | 286 | then throw("your node already registered") | |
263 | 287 | else [StringEntry(nodeRegistryKEY, formatDepositAdd(txId, deposit, deposit, "PENDING"))] | |
264 | 288 | } | |
265 | 289 | ||
266 | 290 | ||
267 | 291 | ||
268 | 292 | @Callable(i) | |
269 | 293 | func distributeMinerReward (beneficiaryAddressStr) = { | |
270 | 294 | let minDistributeAmount = valueOrElse(getInteger(this, keyMinDistributeAmount()), (24 * MULT8)) | |
271 | 295 | if ((size(i.payments) != 1)) | |
272 | 296 | then throw("exact 1 payment must be attached") | |
273 | 297 | else if ((i.caller != i.originCaller)) | |
274 | 298 | then throw("dapp to dapp calls are not allowed") | |
275 | 299 | else { | |
276 | 300 | let pmt = i.payments[0] | |
277 | 301 | let pmtAssetId = valueOrElse(pmt.assetId, WAVESID) | |
278 | 302 | let feeAmt = i.fee | |
279 | 303 | let feeAssetId = valueOrElse(i.feeAssetId, WAVESID) | |
280 | 304 | let callReward = (feeAmt * 3) | |
281 | 305 | let minedAmt = (pmt.amount - callReward) | |
282 | 306 | if ((pmt.amount != minDistributeAmount)) | |
283 | 307 | then throw((("attached payment must be exact " + toString(minDistributeAmount)) + " Wavelets")) | |
284 | 308 | else if ((pmtAssetId != WAVESID)) | |
285 | 309 | then throw("only Waves can be used in payment") | |
286 | 310 | else if ((feeAssetId != WAVESID)) | |
287 | 311 | then throw("only Waves can be used in fees") | |
288 | 312 | else if ((feeAmt != ((5 * MULT8) / 1000))) | |
289 | 313 | then throw("fee amount couldn't exceed 0.005") | |
290 | 314 | else { | |
291 | 315 | let neutrinoAddress = getAddressOrFail(keyNeutrinoAddressStr()) | |
292 | 316 | let masterNodeAddress = getAddressOrFail(keyMasterNodeAddressStr()) | |
293 | 317 | let minerAddress = i.caller | |
294 | 318 | let beneficiaryFromParam = addressFromStringValue(beneficiaryAddressStr) | |
295 | 319 | let beneficiaryAddress = resolveBeneficiaries(minerAddress, beneficiaryFromParam) | |
296 | 320 | let txRewardReceiver = beneficiaryFromParam | |
297 | 321 | let protocolPart = getIntOrFail(keyProtocolPartX6()) | |
298 | 322 | let beneficiaryPart = getIntOrFail(keyBenificaryPartX6()) | |
299 | 323 | let beneficiaryAmt = fraction(minedAmt, beneficiaryPart, MULT6) | |
300 | 324 | let protocolAmt = (minedAmt - beneficiaryAmt) | |
301 | 325 | let buyBackAmt = (120 * MULT8) | |
302 | 326 | let savingsAddress = addressFromStringValue(getStrOrElse(this, keySavingsAddress(), "3PC7DBimauyKytx8tXhKo5u2ECb6FsndYKH")) | |
303 | 327 | let savingsAmt = asInt(invoke(savingsAddress, "withdraw", [buyBackAmt], nil)) | |
304 | 328 | let wxAssetIdStr = "Atqv59EYzjFGuitKVnMRk6H8FukjoV3ktPorbEys25on" | |
305 | 329 | let wxSwapAddress = addressFromStringValue(getStrOrElse(this, keyWxSwapAddress(), "3P68zNiufsu1viZpu1aY3cdahRRKcvV5N93")) | |
306 | 330 | let swapResult = invoke(wxSwapAddress, "swap", [1, wxAssetIdStr, toString(neutrinoContract)], [AttachedPayment(unit, savingsAmt)]) | |
307 | 331 | if ((swapResult == swapResult)) | |
308 | 332 | then [ScriptTransfer(beneficiaryAddress, beneficiaryAmt, unit), ScriptTransfer(neutrinoContract, protocolAmt, unit), ScriptTransfer(txRewardReceiver, callReward, unit), RewardHistoryEntry(minerAddress, i.transactionId, minedAmt, beneficiaryAmt, protocolAmt, protocolAmt, 0), RewardTotalsEntry(minerAddress, minedAmt, beneficiaryAmt, protocolAmt, protocolAmt, 0)] | |
309 | 333 | else throw("Strict value is not equal to itself.") | |
310 | 334 | } | |
311 | 335 | } | |
312 | 336 | } | |
313 | 337 | ||
314 | 338 | ||
315 | 339 | ||
316 | 340 | @Callable(i) | |
317 | 341 | func validateAndApproveLeasings (nListS) = { | |
318 | 342 | let nIdxs = [0, 1, 2, 3, 4, 5, 6, 7] | |
319 | 343 | let nList = split(nListS, SEP) | |
320 | 344 | let expectedCount = size(nIdxs) | |
321 | 345 | let neutrinoAddress = getAddressOrFail(keyNeutrinoAddressStr()) | |
322 | 346 | if ((i.caller != neutrinoAddress)) | |
323 | 347 | then throw("validateAndApproveLeasings not authorized") | |
324 | 348 | else if (((size(nList) % 2) != 0)) | |
325 | 349 | then throw("Only even number of nodes is allowed") | |
326 | 350 | else if ((size(nList) != expectedCount)) | |
327 | 351 | then throw((("Only " + toString(expectedCount)) + " of nodes' addresses can be passed")) | |
328 | 352 | else { | |
329 | 353 | func forEachNodeChangeStatus (acc,i) = { | |
330 | 354 | let nodeAddr = nList[i] | |
331 | 355 | let nodeDataStr = readNodeDataAndValidateStatus(nodeAddr, "PENDING") | |
332 | 356 | (acc :+ StringEntry(keyNodeRegistry(nodeAddr), formatDepositChangeStatus(nodeDataStr, "APPROVED"))) | |
333 | 357 | } | |
334 | 358 | ||
335 | 359 | let approvedNodesData = { | |
336 | 360 | let $l = nIdxs | |
337 | 361 | let $s = size($l) | |
338 | 362 | let $acc0 = nil | |
339 | 363 | func $f0_1 ($a,$i) = if (($i >= $s)) | |
340 | 364 | then $a | |
341 | 365 | else forEachNodeChangeStatus($a, $l[$i]) | |
342 | 366 | ||
343 | 367 | func $f0_2 ($a,$i) = if (($i >= $s)) | |
344 | 368 | then $a | |
345 | 369 | else throw("List size exceeds 8") | |
346 | 370 | ||
347 | 371 | $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) | |
348 | 372 | } | |
349 | 373 | $Tuple2(approvedNodesData, nil) | |
350 | 374 | } | |
351 | 375 | } | |
352 | 376 | ||
353 | 377 | ||
354 | 378 | ||
355 | 379 | @Callable(i) | |
356 | 380 | func replaceApprovedNode (oldNodeAddrStr,newNodeAddrStr,groupNum,penaltyAmount) = if ((i.caller != neutrinoContract)) | |
357 | 381 | then throw("permissions denied") | |
358 | 382 | else { | |
359 | 383 | let oldDataStr = readNodeDataAndValidateStatus(oldNodeAddrStr, "APPROVED") | |
360 | 384 | let newDataStr = readNodeDataAndValidateStatus(newNodeAddrStr, "PENDING") | |
361 | 385 | let approvedNodeListS = getStringOrFail(this, keyApprovedNodeList()) | |
362 | 386 | if (!(contains(approvedNodeListS, oldNodeAddrStr))) | |
363 | 387 | then throw(("approvedNodeList does not contain " + oldNodeAddrStr)) | |
364 | 388 | else { | |
365 | 389 | let updatedNodeListS = makeString_2C(split_4C(approvedNodeListS, oldNodeAddrStr), newNodeAddrStr) | |
366 | 390 | let oldNodeAddr = addressFromStringValue(oldNodeAddrStr) | |
367 | 391 | let beneficiaryAddress = resolveBeneficiaries(oldNodeAddr, oldNodeAddr) | |
368 | 392 | let depoAmount = parseIntValue(split(oldDataStr, SEP)[nRegIdxDepoAmt]) | |
369 | 393 | let actions = [StringEntry(keyApprovedNodeList(), updatedNodeListS), DeleteEntry(keyNodeRegistry(oldNodeAddrStr)), StringEntry(keyNodeRegistry(newNodeAddrStr), formatDepositChangeStatus(newDataStr, "APPROVED"))] | |
370 | 394 | $Tuple2(if ((depoAmount > penaltyAmount)) | |
371 | 395 | then (actions :+ ScriptTransfer(beneficiaryAddress, (depoAmount - penaltyAmount), unit)) | |
372 | 396 | else actions, unit) | |
373 | 397 | } | |
374 | 398 | } | |
375 | 399 | ||
376 | 400 | ||
377 | 401 | ||
378 | 402 | @Callable(i) | |
379 | 403 | func returnDeposit (nodeAddressStr) = if ((size(i.payments) != 0)) | |
380 | 404 | then throw("No payments allowed") | |
381 | 405 | else { | |
382 | 406 | let callerAddress = i.caller | |
383 | 407 | let nodeRegistryKEY = keyNodeRegistry(nodeAddressStr) | |
384 | 408 | let nodeDataOpt = getString(this, nodeRegistryKEY) | |
385 | 409 | if (!(isDefined(nodeDataOpt))) | |
386 | 410 | then throw(("Address is not applied as neutrino node: " + nodeAddressStr)) | |
387 | 411 | else { | |
388 | 412 | let approvedNodeList = getStringOrFail(this, keyApprovedNodeList()) | |
389 | 413 | if (contains(approvedNodeList, nodeAddressStr)) | |
390 | 414 | then throw("Impossible to return deposit from approved Node, please contact support team") | |
391 | 415 | else { | |
392 | 416 | let nodeData = split(value(nodeDataOpt), SEP) | |
393 | 417 | let status = if ((size(nodeData) >= 9)) | |
394 | 418 | then nodeData[nRegIdxStatus] | |
395 | 419 | else "PENDING" | |
396 | 420 | if ((status != "PENDING")) | |
397 | 421 | then throw("Application status is not PENDING!") | |
398 | 422 | else { | |
399 | 423 | let depoAmount = parseIntValue(nodeData[nRegIdxDepoAmt]) | |
400 | 424 | let nodeAddress = addressFromStringValue(nodeAddressStr) | |
401 | 425 | let beneficiaryAddress = resolveBeneficiaries(nodeAddress, nodeAddress) | |
402 | 426 | if (!(if (if ((callerAddress == this)) | |
403 | 427 | then true | |
404 | 428 | else if ((beneficiaryAddress != nodeAddress)) | |
405 | 429 | then (callerAddress == beneficiaryAddress) | |
406 | 430 | else false) | |
407 | 431 | then true | |
408 | 432 | else if ((beneficiaryAddress == nodeAddress)) | |
409 | 433 | then (callerAddress == nodeAddress) | |
410 | 434 | else false)) | |
411 | 435 | then throw("returnDeposit permissions denied") | |
412 | 436 | else [DeleteEntry(nodeRegistryKEY), ScriptTransfer(beneficiaryAddress, depoAmount, unit)] | |
413 | 437 | } | |
414 | 438 | } | |
415 | 439 | } | |
416 | 440 | } | |
417 | 441 | ||
418 | 442 | ||
443 | + | ||
444 | + | @Callable(i) | |
445 | + | func initNodesVoting (startHeight,endHeight,options,description) = if ((i.caller != this)) | |
446 | + | then throw("permissions denied") | |
447 | + | else { | |
448 | + | let optionsArray = split(options, "__") | |
449 | + | if ((size(optionsArray) > 10)) | |
450 | + | then throw("Max 10 options can be specified") | |
451 | + | else { | |
452 | + | let nodesVotingNumKEY = keyNodesVotingNextNum() | |
453 | + | let votingNum = getIntOrElse(nodesVotingNumKEY, 0) | |
454 | + | let votingNumStr = toString(votingNum) | |
455 | + | func transformOptions (accum,nextOpt) = { | |
456 | + | let optData = split(nextOpt, ":") | |
457 | + | let opt = optData[0] | |
458 | + | let optKEY = keyNodesVotingOptionKey(votingNumStr, opt) | |
459 | + | let optDescr = optData[1] | |
460 | + | let optTotalVotesKEY = keyNodesVotingOptionTotalVotes(votingNumStr, opt) | |
461 | + | ((accum :+ StringEntry(optKEY, optDescr)) :+ IntegerEntry(optTotalVotesKEY, 0)) | |
462 | + | } | |
463 | + | ||
464 | + | (((({ | |
465 | + | let $l = optionsArray | |
466 | + | let $s = size($l) | |
467 | + | let $acc0 = nil | |
468 | + | func $f0_1 ($a,$i) = if (($i >= $s)) | |
469 | + | then $a | |
470 | + | else transformOptions($a, $l[$i]) | |
471 | + | ||
472 | + | func $f0_2 ($a,$i) = if (($i >= $s)) | |
473 | + | then $a | |
474 | + | else throw("List size exceeds 10") | |
475 | + | ||
476 | + | $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) | |
477 | + | } :+ IntegerEntry(nodesVotingNumKEY, (votingNum + 1))) :+ StringEntry(keyNodesVotingDesc(votingNumStr), description)) :+ IntegerEntry(keyNodesVotingStartHeight(votingNumStr), startHeight)) :+ IntegerEntry(keyNodesVotingEndHeight(votingNumStr), endHeight)) | |
478 | + | } | |
479 | + | } | |
480 | + | ||
481 | + | ||
482 | + | ||
483 | + | @Callable(i) | |
484 | + | func voteByNode (nodeAddressStr,votingNum,optionNum) = { | |
485 | + | let validateNode = readNodeDataAndValidateStatus(nodeAddressStr, "APPROVED") | |
486 | + | if ((validateNode == validateNode)) | |
487 | + | then { | |
488 | + | let nodeAddress = addressFromStringValue(nodeAddressStr) | |
489 | + | let nodeBeneficiary = getStringOrFail(nodeAddress, keyBeneficiaryAddress()) | |
490 | + | let voterAddressStr = toString(i.caller) | |
491 | + | if ((voterAddressStr != nodeBeneficiary)) | |
492 | + | then throw("Only Node's beneficiary can do voting") | |
493 | + | else { | |
494 | + | let startHeight = getIntOrFail(keyNodesVotingStartHeight(votingNum)) | |
495 | + | let endHeight = getIntOrFail(keyNodesVotingEndHeight(votingNum)) | |
496 | + | if ((startHeight > height)) | |
497 | + | then throw("Voting has not been started yet") | |
498 | + | else if ((height > endHeight)) | |
499 | + | then throw("Voting finished") | |
500 | + | else { | |
501 | + | let nodeChoiceKEY = keyNodesVotingNodeChoice(votingNum, nodeAddressStr) | |
502 | + | let nodeChoice = getStrOrElse(this, nodeChoiceKEY, "") | |
503 | + | if ((nodeChoice != "")) | |
504 | + | then throw("Node has already voted. It is allowed to vote only once") | |
505 | + | else { | |
506 | + | let optionTotalVotesKEY = keyNodesVotingOptionTotalVotes(votingNum, optionNum) | |
507 | + | let optionTotalVotes = getIntOrFail(optionTotalVotesKEY) | |
508 | + | [IntegerEntry(optionTotalVotesKEY, (optionTotalVotes + 1)), StringEntry(nodeChoiceKEY, optionNum)] | |
509 | + | } | |
510 | + | } | |
511 | + | } | |
512 | + | } | |
513 | + | else throw("Strict value is not equal to itself.") | |
514 | + | } | |
515 | + | ||
516 | + |
github/deemru/w8io/786bc32 68.51 ms ◑