tx · 8xrWaRC9k2KAeGEBtUmfT1AhnrpSR59BDpXhkXVf5Zzf

3N3gSFrTwgiLAFZHMuJrEttht1nPhnkpvjs:  -0.01800000 Waves

2022.11.24 17:38 [2331308] smart account 3N3gSFrTwgiLAFZHMuJrEttht1nPhnkpvjs > SELF 0.00000000 Waves

{ "type": 13, "id": "8xrWaRC9k2KAeGEBtUmfT1AhnrpSR59BDpXhkXVf5Zzf", "fee": 1800000, "feeAssetId": null, "timestamp": 1669300707463, "version": 2, "chainId": 84, "sender": "3N3gSFrTwgiLAFZHMuJrEttht1nPhnkpvjs", "senderPublicKey": "5mttAQ8x5ig98rgbv6rZmwXQ2pAYSwgCkQPSVBcr7BVp", "proofs": [ "63KcPPmbWD7jCjxQP1WH49iuQn76kuBiuNXEgL5VQnuD3foTtZhtN7ecQMwmHd7Cw8r6KMQUhHhVC3rmrJHKZuNV" ], "script": "base64:BgIuCAISBgoECAEBARIECgIBARIHCgUICAEBGBIHCgUICAEBGBIDCgEBEgMKAQESAFYAC3JldmlzaW9uTnVtAgAAA1NFUAICX18AB0xJU1RTRVACAToADURFRkFVTFRRVU9SVU0AoMIeAApVUkxQQVRURVJOAhpodHRwczovL2ZvcnVtLm5ldXRyaW5vLmF0LwAITUFYVElUTEUAoAEABk1BWFVSTAD6AQANTUFYVk9USU5HVElNRQCAkOTABAAFTVVMVDYAwIQ9AA5ERUZBVUxUUEFZTUVOVACAlOvcAwAUREVGQVVMVENSRUFUSU9OR05TQlQAgJTr3AMAClBBU1RNQVJHSU4AgLq3AwAMRlVUVVJFTUFSR0lOAMDLyQIAEmdvdklkeFByb3Bvc2FsVHhJZAABAApnb3ZJZHhUeXBlAAIADGdvdklkeEF1dGhvcgADAAlnb3ZJZHhVcmwABAALZ292SWR4VGl0bGUABQASZ292SWR4Q3JlYXRpb25UaW1lAAYAC2dvdklkeFN0YXJ0AAcACWdvdklkeEVuZAAIAAtnb3ZJZHhUeElkcwAJAAxnb3ZJZHhRdW9ydW0ACgANZ292SWR4T3B0aW9ucwALABNnb3ZTdGF0dXNJZHhJc1ZhbGlkAAEAEmdvdlN0YXR1c0lkeFdpbk9wdAACABRnb3ZTdGF0dXNJZHhXaW5Wb3RlcwADABZnb3ZTdGF0dXNJZHhUb3RhbFZvdGVzAAQAFWdvdlN0YXR1c0lkeFNjQXBwbGllZAAFABJnb3ZTdGF0dXNJZHhTY1RpbWUABgAWZ292U3RhdHVzSWR4SXNDYW5jZWxlZAAHAQ9nZXRTdHJpbmdPckZhaWwCB2FkZHJlc3MDa2V5CQETdmFsdWVPckVycm9yTWVzc2FnZQIJAJ0IAgUHYWRkcmVzcwUDa2V5CQC5CQIJAMwIAgIKbWFuZGF0b3J5IAkAzAgCCQClCAEFB2FkZHJlc3MJAMwIAgIBLgkAzAgCBQNrZXkJAMwIAgIPIGlzIG5vdCBkZWZpbmVkBQNuaWwCAAEMZ2V0SW50T3JFbHNlAgNrZXkKZGVmYXVsdFZhbAkBC3ZhbHVlT3JFbHNlAgkAmggCBQR0aGlzBQNrZXkFCmRlZmF1bHRWYWwAGUlkeENvbnRyb2xDZmdOZXV0cmlub0RhcHAAAQAYSWR4Q29udHJvbENmZ0F1Y3Rpb25EYXBwAAIAFElkeENvbnRyb2xDZmdScGREYXBwAAMAFUlkeENvbnRyb2xDZmdNYXRoRGFwcAAEABxJZHhDb250cm9sQ2ZnTGlxdWlkYXRpb25EYXBwAAUAFUlkeENvbnRyb2xDZmdSZXN0RGFwcAAGAB1JZHhDb250cm9sQ2ZnTm9kZVJlZ2lzdHJ5RGFwcAAHABxJZHhDb250cm9sQ2ZnTnNidFN0YWtpbmdEYXBwAAgAGUlkeENvbnRyb2xDZmdNZWRpYXRvckRhcHAACQAcSWR4Q29udHJvbENmZ1N1cmZTdGFraW5nRGFwcAAKACBJZHhDb250cm9sQ2ZnR25zYnRDb250cm9sbGVyRGFwcAALABdJZHhDb250cm9sQ2ZnUmVzdFYyRGFwcAAMABtJZHhDb250cm9sQ2ZnR292ZXJuYW5jZURhcHAADQERa2V5Q29udHJvbEFkZHJlc3MAAhwlcyVzX19jb25maWdfX2NvbnRyb2xBZGRyZXNzAQ1rZXlDb250cm9sQ2ZnAAIRJXNfX2NvbnRyb2xDb25maWcBFHJlYWRDb250cm9sQ2ZnT3JGYWlsAQdjb250cm9sCQC8CQIJAQ9nZXRTdHJpbmdPckZhaWwCBQdjb250cm9sCQENa2V5Q29udHJvbENmZwAFA1NFUAEYZ2V0Q29udHJhY3RBZGRyZXNzT3JGYWlsAgpjb250cm9sQ2ZnA2lkeAkBE3ZhbHVlT3JFcnJvck1lc3NhZ2UCCQCmCAEJAJEDAgUKY29udHJvbENmZwUDaWR4CQCsAgICLUNvbnRyb2wgY2ZnIGRvZXNuJ3QgY29udGFpbiBhZGRyZXNzIGF0IGluZGV4IAkApAMBBQNpZHgAD2NvbnRyb2xDb250cmFjdAkBEUBleHRyTmF0aXZlKDEwNjIpAQkBC3ZhbHVlT3JFbHNlAgkAnQgCBQR0aGlzCQERa2V5Q29udHJvbEFkZHJlc3MAAiMzUDVCZmQ1OFBQZk52Qk0ySHk4UWZiY0RxTWVOdHpnN0tmUAAKY29udHJvbENmZwkBFHJlYWRDb250cm9sQ2ZnT3JGYWlsAQUPY29udHJvbENvbnRyYWN0ABBuZXV0cmlub0NvbnRyYWN0CQEYZ2V0Q29udHJhY3RBZGRyZXNzT3JGYWlsAgUKY29udHJvbENmZwUZSWR4Q29udHJvbENmZ05ldXRyaW5vRGFwcAAXZ25zYnRDb250cm9sbGVyQ29udHJhY3QJARhnZXRDb250cmFjdEFkZHJlc3NPckZhaWwCBQpjb250cm9sQ2ZnBSBJZHhDb250cm9sQ2ZnR25zYnRDb250cm9sbGVyRGFwcAEYa2V5UXVvcnVtUmVxdWlyZWRQZXJjZW50AAISJXNfX3F1b3J1bVJlcXVpcmVkARJrZXlQYXltZW50UmVxdWlyZWQAAhMlc19fcGF5bWVudFJlcXVpcmVkARBrZXlHbnNidFJlcXVpcmVkAAIRJXNfX2dOc2J0UmVxdWlyZWQBEWtleUxhc3RQcm9wb3NhbElkAAIOJXNfX3Byb3Bvc2FsSWQBFGtleUxhc3RVcGRhdGVWZXJzaW9uAAIRJXNfX3VwZGF0ZVZlcnNpb24BGWtleVByb3Bvc2FsU3RhdHVzRGF0YUJ5SWQBCnByb3Bvc2FsSWQJAKwCAgIaJXMlZF9fcHJvcG9zYWxTdGF0dXNEYXRhX18JAKQDAQUKcHJvcG9zYWxJZAETa2V5UHJvcG9zYWxEYXRhQnlJZAEKcHJvcG9zYWxJZAkArAICAhQlcyVkX19wcm9wb3NhbERhdGFfXwkApAMBBQpwcm9wb3NhbElkAR1rZXlQcm9wb3NhbFZvdGVzQnlJZEFuZE9wdGlvbgIKcHJvcG9zYWxJZANvcHQJALkJAgkAzAgCAgYlcyVkJWQJAMwIAgIKdm90ZXNCeU9wdAkAzAgCCQCkAwEFCnByb3Bvc2FsSWQJAMwIAgkApAMBBQNvcHQFA25pbAUDU0VQARtrZXlQcm9wb3NhbFZvdGVzQnlJZEFuZFVzZXICCnByb3Bvc2FsSWQIdXNlckFkZHIJALkJAgkAzAgCAgYlcyVkJXMJAMwIAgILdm90ZXNCeVVzZXIJAMwIAgkApAMBBQpwcm9wb3NhbElkCQDMCAIFCHVzZXJBZGRyBQNuaWwFA1NFUAEca2V5UHJvcG9zYWxDaG9pY2VCeUlkQW5kVXNlcgIKcHJvcG9zYWxJZAh1c2VyQWRkcgkAuQkCCQDMCAICBiVzJWQlcwkAzAgCAgxvcHRpb25CeVVzZXIJAMwIAgkApAMBBQpwcm9wb3NhbElkCQDMCAIFCHVzZXJBZGRyBQNuaWwFA1NFUAESa2V5QXBwbHlJblByb2dyZXNzAAITJXNfX2FwcGx5SW5Qcm9ncmVzcwEWa2V5UHJvcG9zYWxJZEJ5VG9waWNJZAEHdG9waWNJZAkArAICAhslcyVzX19wcm9wb3NhbElkQnlUb3BpY0lkX18JAKQDAQUHdG9waWNJZAEea2V5TnVtVW5pcXVlVm90ZXJzQnlQcm9wb3NhbElkAQpwcm9wb3NhbElkCQCsAgICESVzJWRfX251bVZvdGVyc19fCQCkAwEFCnByb3Bvc2FsSWQBGGtleVN0YXRzQXZlclVuaXF1ZVZvdGVycwACICVzJXMlc19fc3RhdHNfX2F2Z19fdW5pcXVlVm90ZXJzARZrZXlTdGF0c0F2ZXJHbnNidFZvdGVkAAIeJXMlcyVzX19zdGF0c19fYXZnX19nbnNidFZvdGVkARVrZXlTdGF0c1VuaXF1ZUF1dGhvcnMAAholcyVzX19zdGF0c19fdW5pcXVlQXV0aG9ycwEXa2V5TnVtUHJvcG9zYWxzQnlBdXRob3IBCmFkZHJlc3NTdHIJAKwCAgIcJXMlc19fbnVtUHJvcG9zYWxzQnlBdXRob3JfXwUKYWRkcmVzc1N0cgEPa2V5QXBwbHlIaXN0b3J5AQl0aW1lc3RhbXAJAKwCAgIUJXMlZF9fYXBwbHlIaXN0b3J5X18JAKQDAQUJdGltZXN0YW1wAQlhc0FueUxpc3QBAXYEByRtYXRjaDAFAXYDCQABAgUHJG1hdGNoMAIJTGlzdFtBbnldBAFsBQckbWF0Y2gwBQFsCQACAQIbZmFpbCB0byBjYXN0IGludG8gTGlzdFtBbnldAQVhc0ludAEBdgQHJG1hdGNoMAUBdgMJAAECBQckbWF0Y2gwAgNJbnQEAWkFByRtYXRjaDAFAWkJAAIBAhVmYWlsIHRvIGNhc3QgaW50byBJbnQBCnN0YXR1c0RhdGEHDWlzVm90aW5nVmFsaWQJd2luT3B0aW9uDndpbk9wdGlvblZvdGVzCnRvdGFsVm90ZXMRYXJlU2NyaXB0c0FwcGxpZWQQc2NyaXB0c1RpbWVzdGFtcA5jYW5jZWxlZEJ5VGVhbQkAuQkCCQDMCAICDiViJWQlZCVkJWIlZCViCQDMCAIJAKUDAQUNaXNWb3RpbmdWYWxpZAkAzAgCCQCkAwEFCXdpbk9wdGlvbgkAzAgCCQCkAwEFDndpbk9wdGlvblZvdGVzCQDMCAIJAKQDAQUKdG90YWxWb3RlcwkAzAgCCQClAwEFEWFyZVNjcmlwdHNBcHBsaWVkCQDMCAIJAKQDAQUQc2NyaXB0c1RpbWVzdGFtcAkAzAgCCQClAwEFDmNhbmNlbGVkQnlUZWFtBQNuaWwFA1NFUAEMcHJvcG9zYWxEYXRhCwxwcm9wb3NhbFR4SWQEdHlwZQZhdXRob3IJZm9ydW1MaW5rBXRpdGxlDHByb3Bvc2FsVGltZQ92b3RpbmdTdGFydFRpbWUNdm90aW5nRW5kVGltZQV0eElkcw1xdW9ydW1Jbkduc2J0B29wdGlvbnMJALkJAgkAzAgCAhYlcyVzJXMlcyVzJWQlZCVkJXMlZCVzCQDMCAIFDHByb3Bvc2FsVHhJZAkAzAgCBQR0eXBlCQDMCAIFBmF1dGhvcgkAzAgCBQlmb3J1bUxpbmsJAMwIAgUFdGl0bGUJAMwIAgkApAMBBQxwcm9wb3NhbFRpbWUJAMwIAgkApAMBBQ92b3RpbmdTdGFydFRpbWUJAMwIAgkApAMBBQ12b3RpbmdFbmRUaW1lCQDMCAIFBXR4SWRzCQDMCAIJAKQDAQUNcXVvcnVtSW5HbnNidAkAzAgCBQdvcHRpb25zBQNuaWwFA1NFUAELY2hlY2tUeExpc3QBBnR4TGlzdAMJAGYCCQCQAwEFBnR4TGlzdAAUCQACAQkArAICAhdUb28gbWFueSB0cmFuc2FjdGlvbnM6IAkApAMBCQCQAwEFBnR4TGlzdAoBCGNvbWJpbmVyAgNhY2MCdHgDCQECIT0CCQDIAQEJANkEAQUCdHgAIAkAAgEJAKwCAgIMV3JvbmcgdHhJZDogBQJ0eAMJAAACBQNhY2MCAAUCdHgJAKwCAgkArAICBQNhY2MFB0xJU1RTRVAFAnR4CgACJGwFBnR4TGlzdAoAAiRzCQCQAwEFAiRsCgAFJGFjYzACAAoBBSRmMF8xAgIkYQIkaQMJAGcCBQIkaQUCJHMFAiRhCQEIY29tYmluZXICBQIkYQkAkQMCBQIkbAUCJGkKAQUkZjBfMgICJGECJGkDCQBnAgUCJGkFAiRzBQIkYQkAAgECFExpc3Qgc2l6ZSBleGNlZWRzIDIwCQEFJGYwXzICCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECBQUkYWNjMAAAAAEAAgADAAQABQAGAAcACAAJAAoACwAMAA0ADgAPABAAEQASABMAFAALSWR4RWZmVG90YWwAAAAKSWR4RWZmVXNlcgABARFnZXRFZmZlY3RpdmVHbnNidAESdXNlckFkZHJTdHJPckVtcHR5BAlnbnNidERhdGEJAQlhc0FueUxpc3QBCQD8BwQFF2duc2J0Q29udHJvbGxlckNvbnRyYWN0AhRnbnNidEluZm9TWVNSRUFET05MWQkAzAgCBRJ1c2VyQWRkclN0ck9yRW1wdHkJAMwIAgAACQDMCAIAAAUDbmlsBQNuaWwECG5zYnREYXRhCQEJYXNBbnlMaXN0AQkAkQMCBQlnbnNidERhdGEAAgQMdXNlckZyb21Oc2J0CQEFYXNJbnQBCQCRAwIFCG5zYnREYXRhAAIEDXRvdGFsRnJvbU5zYnQJAQVhc0ludAEJAJEDAgUIbnNidERhdGEAAwQSdXNlck1hdHVyZUZyb21TdXJmCQEFYXNJbnQBCQCRAwIFCWduc2J0RGF0YQAJBBN0b3RhbE1hdHVyZUZyb21TdXJmCQEFYXNJbnQBCQCRAwIFCWduc2J0RGF0YQAGCQDMCAIJAGQCBQ10b3RhbEZyb21Oc2J0BRN0b3RhbE1hdHVyZUZyb21TdXJmCQDMCAIJAGQCBQx1c2VyRnJvbU5zYnQFEnVzZXJNYXR1cmVGcm9tU3VyZgUDbmlsAQx2YWxpZGF0ZUxpbmsBA3VybAMJAQIhPQIJAQV2YWx1ZQEJALMJAgUDdXJsBQpVUkxQQVRURVJOAAAJAAIBAgtJbnZhbGlkIHVybAMJAGYCCQCxAgEFA3VybAUGTUFYVVJMCQACAQINVXJsIHRvbyBsb25nIQQHdG9waWNJZAkBE3ZhbHVlT3JFcnJvck1lc3NhZ2UCCQC2CQEJALACAgUDdXJsCQBkAgkBBXZhbHVlAQkAtwkCBQN1cmwCAS8AAQINV3JvbmcgdG9waWNJZAQMcmVnaXN0ZXJlZElkCQCfCAEJARZrZXlQcm9wb3NhbElkQnlUb3BpY0lkAQUHdG9waWNJZAMJAQlpc0RlZmluZWQBBQxyZWdpc3RlcmVkSWQJAAIBCQCsAgICOFZvdGluZyB3aXRoIHN1Y2ggZm9ydW0gbGluayBpcyBhbHJlYWR5IHJlZ2lzdGVyZWQgYnkgaWQ9CQCkAwEJAQV2YWx1ZQEFDHJlZ2lzdGVyZWRJZAUHdG9waWNJZAEOaW5pdGlhdGVWb3RpbmcLB3BheW1lbnQMcHJvcG9zYWxUeElkBHR5cGUGYXV0aG9yCWZvcnVtTGluawV0aXRsZQ92b3RpbmdTdGFydFRpbWUNdm90aW5nRW5kVGltZQZzdGF0dXMGdHhMaXN0C29wdGlvbnNMaXN0AwkBAiE9AggFB3BheW1lbnQHYXNzZXRJZAUEdW5pdAkAAgECG0FsbG93ZWQgV0FWRVMgcGF5bWVudCBvbmx5IQQGcG10UmVxCQEMZ2V0SW50T3JFbHNlAgkBEmtleVBheW1lbnRSZXF1aXJlZAAFDkRFRkFVTFRQQVlNRU5UAwkAZgIFBnBtdFJlcQgFB3BheW1lbnQGYW1vdW50CQACAQkArAICAiRQYXltZW50IGF0dGFjaGVkIHNob3VsZCBiZSBhdCBsZWFzdCAJAKQDAQUGcG10UmVxBAd0b3BpY0lkCQEMdmFsaWRhdGVMaW5rAQUJZm9ydW1MaW5rAwkAAAIFBXRpdGxlAgAJAAIBAg5UaXRsZSBpcyBlbXB0eQMJAGYCCQCxAgEFBXRpdGxlBQhNQVhUSVRMRQkAAgECDlRvbyBsb25nIHRpdGxlBAxwcm9wb3NhbFRpbWUIBQlsYXN0QmxvY2sJdGltZXN0YW1wAwkAZgIFDHByb3Bvc2FsVGltZQUPdm90aW5nU3RhcnRUaW1lCQACAQkArAICCQCsAgIJAKwCAgIQdm90aW5nU3RhcnRUaW1lPQkApAMBBQ92b3RpbmdTdGFydFRpbWUCECA8IHByb3Bvc2FsVGltZT0JAKQDAQUMcHJvcG9zYWxUaW1lAwkAZgIFD3ZvdGluZ1N0YXJ0VGltZQUNdm90aW5nRW5kVGltZQkAAgEJAKwCAgkArAICCQCsAgICDnZvdGluZ0VuZFRpbWU9CQCkAwEFDXZvdGluZ0VuZFRpbWUCEyA8IHZvdGluZ1N0YXJ0VGltZT0JAKQDAQUPdm90aW5nU3RhcnRUaW1lAwkAZgIJAGUCBQ12b3RpbmdFbmRUaW1lBQ92b3RpbmdTdGFydFRpbWUFDU1BWFZPVElOR1RJTUUJAAIBCQCsAgIJAKwCAgkArAICAhtWb3RpbmcgcGVyaW9kIGV4Y2VlZHMgbWF4OiAJAKQDAQkAZQIFDXZvdGluZ0VuZFRpbWUFD3ZvdGluZ1N0YXJ0VGltZQIDID4gCQCkAwEFDU1BWFZPVElOR1RJTUUEBXR4SWRzAwkAAAIFBHR5cGUCBElERUECAAkBC2NoZWNrVHhMaXN0AQUGdHhMaXN0AwkAZwIAAQkAkAMBBQtvcHRpb25zTGlzdAkAAgECF1RvbyBmZXcgY2hvaWNlcyB0byB2b3RlBANlZmYJARFnZXRFZmZlY3RpdmVHbnNidAEFBmF1dGhvcgQKZ25zYnRUb3RhbAkAkQMCBQNlZmYFC0lkeEVmZlRvdGFsBAlnTnNidFVzZXIJAJEDAgUDZWZmBQpJZHhFZmZVc2VyBAhnbnNidFJlcQkBDGdldEludE9yRWxzZQIJARBrZXlHbnNidFJlcXVpcmVkAAUUREVGQVVMVENSRUFUSU9OR05TQlQDCQBmAgUIZ25zYnRSZXEFCWdOc2J0VXNlcgkAAgEJAKwCAgkArAICAhJZb3UgbmVlZCBhdCBsZWFzdCAJAKQDAQUIZ25zYnRSZXECFyBnTnNidCB0byBjcmVhdGUgdm90aW5nBAxhbW91bnRMZWFzZWQJAPwHBAUQbmV1dHJpbm9Db250cmFjdAILYWNjZXB0V2F2ZXMFA25pbAkAzAgCBQdwYXltZW50BQNuaWwDCQAAAgUMYW1vdW50TGVhc2VkBQxhbW91bnRMZWFzZWQEBnF1b3J1bQkBDGdldEludE9yRWxzZQIJARhrZXlRdW9ydW1SZXF1aXJlZFBlcmNlbnQABQ1ERUZBVUxUUVVPUlVNBA1xdW9ydW1Jbkduc2J0CQBrAwUGcXVvcnVtBQpnbnNidFRvdGFsBQVNVUxUNgQKcHJvcG9zYWxJZAkAZAIJAQxnZXRJbnRPckVsc2UCCQERa2V5TGFzdFByb3Bvc2FsSWQAAAAAAQQUbnVtUHJvcG9zYWxzQnlBdXRob3IJAGQCCQEMZ2V0SW50T3JFbHNlAgkBF2tleU51bVByb3Bvc2Fsc0J5QXV0aG9yAQUGYXV0aG9yAAAAAQQLdW5pcUF1dGhvcnMJAGQCCQEMZ2V0SW50T3JFbHNlAgkBFWtleVN0YXRzVW5pcXVlQXV0aG9ycwAAAAMJAAACBRRudW1Qcm9wb3NhbHNCeUF1dGhvcgABAAEAAAQKb3B0aW9uc1N0cgkAuQkCBQtvcHRpb25zTGlzdAUHTElTVFNFUAkAlAoCCQDMCAIJAQxJbnRlZ2VyRW50cnkCCQERa2V5TGFzdFByb3Bvc2FsSWQABQpwcm9wb3NhbElkCQDMCAIJAQxJbnRlZ2VyRW50cnkCCQEWa2V5UHJvcG9zYWxJZEJ5VG9waWNJZAEFB3RvcGljSWQFCnByb3Bvc2FsSWQJAMwIAgkBC1N0cmluZ0VudHJ5AgkBGWtleVByb3Bvc2FsU3RhdHVzRGF0YUJ5SWQBBQpwcm9wb3NhbElkCQEKc3RhdHVzRGF0YQcHAAAAAAAABwAABwkAzAgCCQELU3RyaW5nRW50cnkCCQETa2V5UHJvcG9zYWxEYXRhQnlJZAEFCnByb3Bvc2FsSWQJAQxwcm9wb3NhbERhdGELBQxwcm9wb3NhbFR4SWQFBHR5cGUFBmF1dGhvcgUJZm9ydW1MaW5rBQV0aXRsZQUMcHJvcG9zYWxUaW1lBQ92b3RpbmdTdGFydFRpbWUFDXZvdGluZ0VuZFRpbWUFBXR4SWRzBQ1xdW9ydW1Jbkduc2J0BQpvcHRpb25zU3RyCQDMCAIJAQxJbnRlZ2VyRW50cnkCCQEXa2V5TnVtUHJvcG9zYWxzQnlBdXRob3IBBQZhdXRob3IFFG51bVByb3Bvc2Fsc0J5QXV0aG9yCQDMCAIJAQxJbnRlZ2VyRW50cnkCCQEVa2V5U3RhdHNVbmlxdWVBdXRob3JzAAULdW5pcUF1dGhvcnMFA25pbAUMcHJvcG9zYWxUeElkCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuAQ1jYWxjV2luT3B0aW9uBwpwcm9wb3NhbElkC29wdGlvbnNMaXN0DmlzUHJldk9wdGlvbmFsCW9sZENob2ljZRBvcHRpb25hbFRvdGFsT2xkCW5ld0Nob2ljZRNuZXdUb3RhbEJ5TmV3Q2hvaWNlCgEIZmluZEJlc3QCA2FjYwRlbGVtBANpZHgJAQV2YWx1ZQEJAM8IAgULb3B0aW9uc0xpc3QFBGVsZW0EA3ZhbAMFDmlzUHJldk9wdGlvbmFsAwkAAAIFA2lkeAUJbmV3Q2hvaWNlBRNuZXdUb3RhbEJ5TmV3Q2hvaWNlCQEMZ2V0SW50T3JFbHNlAgkBHWtleVByb3Bvc2FsVm90ZXNCeUlkQW5kT3B0aW9uAgUKcHJvcG9zYWxJZAUDaWR4AAADCQAAAgUDaWR4CQEFdmFsdWUBBQlvbGRDaG9pY2UFEG9wdGlvbmFsVG90YWxPbGQDCQAAAgUDaWR4BQluZXdDaG9pY2UFE25ld1RvdGFsQnlOZXdDaG9pY2UJAQxnZXRJbnRPckVsc2UCCQEda2V5UHJvcG9zYWxWb3Rlc0J5SWRBbmRPcHRpb24CBQpwcm9wb3NhbElkBQNpZHgAAAMJAGYCCAUDYWNjAl8yBQN2YWwFA2FjYwkAlAoCBQNpZHgFA3ZhbAoAAiRsBQtvcHRpb25zTGlzdAoAAiRzCQCQAwEFAiRsCgAFJGFjYzAJAJQKAgAAAAAKAQUkZjBfMQICJGECJGkDCQBnAgUCJGkFAiRzBQIkYQkBCGZpbmRCZXN0AgUCJGEJAJEDAgUCJGwFAiRpCgEFJGYwXzICAiRhAiRpAwkAZwIFAiRpBQIkcwUCJGEJAAIBAhRMaXN0IHNpemUgZXhjZWVkcyAxMAkBBSRmMF8yAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgUFJGFjYzAAAAABAAIAAwAEAAUABgAHAAgACQAKARB1cGRhdGVTdGF0dXNEYXRhBAdvbGREYXRhB2lzVmFsaWQJbmV3V2luT3B0DW5ld1RvdGFsVm90ZXMJALkJAgkAzAgCAg4lYiVkJWQlZCViJWQlYgkAzAgCCQClAwEFB2lzVmFsaWQJAMwIAgkApAMBCAUJbmV3V2luT3B0Al8xCQDMCAIJAKQDAQgFCW5ld1dpbk9wdAJfMgkAzAgCCQCkAwEFDW5ld1RvdGFsVm90ZXMJAMwIAgkAkQMCBQdvbGREYXRhBRVnb3ZTdGF0dXNJZHhTY0FwcGxpZWQJAMwIAgkAkQMCBQdvbGREYXRhBRJnb3ZTdGF0dXNJZHhTY1RpbWUJAMwIAgkAkQMCBQdvbGREYXRhBRZnb3ZTdGF0dXNJZHhJc0NhbmNlbGVkBQNuaWwFA1NFUAERc3RhdHVzQXBwbHlTY3JpcHQBB29sZERhdGEJALkJAgkAzAgCAg4lYiVkJWQlZCViJWQlYgkAzAgCCQCRAwIFB29sZERhdGEFE2dvdlN0YXR1c0lkeElzVmFsaWQJAMwIAgkAkQMCBQdvbGREYXRhBRJnb3ZTdGF0dXNJZHhXaW5PcHQJAMwIAgkAkQMCBQdvbGREYXRhBRRnb3ZTdGF0dXNJZHhXaW5Wb3RlcwkAzAgCCQCRAwIFB29sZERhdGEFFmdvdlN0YXR1c0lkeFRvdGFsVm90ZXMJAMwIAgIEdHJ1ZQkAzAgCCQCRAwIFB29sZERhdGEFEmdvdlN0YXR1c0lkeFNjVGltZQkAzAgCCQCRAwIFB29sZERhdGEFFmdvdlN0YXR1c0lkeElzQ2FuY2VsZWQFA25pbAUDU0VQARBFeGVjdXRpb25IaXN0b3J5Awpwcm9wb3NhbElkBXRpdGxlA3VybAQKZ25zYnRUb3RhbAkAkQMCCQERZ2V0RWZmZWN0aXZlR25zYnQBAgAFC0lkeEVmZlRvdGFsBAd0dXJub3V0AKDCHgkBC1N0cmluZ0VudHJ5AgkBD2tleUFwcGx5SGlzdG9yeQEIBQlsYXN0QmxvY2sJdGltZXN0YW1wCQC5CQIJAMwIAgIKJWQlZCVkJXMlcwkAzAgCCQCkAwEFCnByb3Bvc2FsSWQJAMwIAgkApAMBBQpnbnNidFRvdGFsCQDMCAIJAKQDAQUHdHVybm91dAkAzAgCBQV0aXRsZQkAzAgCBQN1cmwFA25pbAUDU0VQBwFpAQtjb25zdHJ1Y3RvcgQLY29udHJvbEFkZHIOZ05zYnRSZXFUb0luaXQOd2F2ZXNSZXFUb0luaXQNcXVvcnVtUmVxUGVyYwMJAQIhPQIIBQFpBmNhbGxlcgUEdGhpcwkAAgECEVBlcm1pc3Npb24gZGVuaWVkCQDMCAIJAQtTdHJpbmdFbnRyeQIJARFrZXlDb250cm9sQWRkcmVzcwAFC2NvbnRyb2xBZGRyCQDMCAIJAQxJbnRlZ2VyRW50cnkCCQEQa2V5R25zYnRSZXF1aXJlZAAFDmdOc2J0UmVxVG9Jbml0CQDMCAIJAQxJbnRlZ2VyRW50cnkCCQESa2V5UGF5bWVudFJlcXVpcmVkAAUOd2F2ZXNSZXFUb0luaXQJAMwIAgkBDEludGVnZXJFbnRyeQIJARhrZXlRdW9ydW1SZXF1aXJlZFBlcmNlbnQABQ1xdW9ydW1SZXFQZXJjBQNuaWwBaQEIY2FzdFZvdGUCCnByb3Bvc2FsSWQGY2hvaWNlBA51c2VyQWRkcmVzc1N0cgkApQgBCAUBaQZjYWxsZXIEC2R5bmFtaWNEYXRhCQC1CQIJAQ9nZXRTdHJpbmdPckZhaWwCBQR0aGlzCQEZa2V5UHJvcG9zYWxTdGF0dXNEYXRhQnlJZAEFCnByb3Bvc2FsSWQFA1NFUAMJAAACCQCRAwIFC2R5bmFtaWNEYXRhBRZnb3ZTdGF0dXNJZHhJc0NhbmNlbGVkAgR0cnVlCQACAQIaVm90aW5nIGlzIGNhbmNlbGVkIGJ5IHRlYW0ECHByb3BEYXRhCQC1CQIJAQ9nZXRTdHJpbmdPckZhaWwCBQR0aGlzCQETa2V5UHJvcG9zYWxEYXRhQnlJZAEFCnByb3Bvc2FsSWQFA1NFUAQFc3RhcnQJAQ1wYXJzZUludFZhbHVlAQkAkQMCBQhwcm9wRGF0YQULZ292SWR4U3RhcnQEA2VuZAkBDXBhcnNlSW50VmFsdWUBCQCRAwIFCHByb3BEYXRhBQlnb3ZJZHhFbmQEA25vdwgFCWxhc3RCbG9jawl0aW1lc3RhbXADCQBmAgUFc3RhcnQFA25vdwkAAgECFlZvdGluZyBub3Qgc3RhcnRlZCB5ZXQDCQBnAgUDbm93BQNlbmQJAAIBAhdWb3RpbmcgYWxyZWFkeSBmaW5pc2hlZAQQYXZhaWxhYmxlT3B0aW9ucwkAtQkCCQCRAwIFCHByb3BEYXRhBQ1nb3ZJZHhPcHRpb25zBQdMSVNUU0VQBApudW1PcHRpb25zCQCQAwEFEGF2YWlsYWJsZU9wdGlvbnMDCQBnAgABBQpudW1PcHRpb25zCQACAQIXVG9vIGZldyBjaG9pY2VzIHRvIHZvdGUDCQBnAgUGY2hvaWNlBQpudW1PcHRpb25zCQACAQkArAICAhtVbmtub3duIGNob2ljZSEgTXVzdCBiZSAwLi4JAKQDAQkAZQIFCm51bU9wdGlvbnMAAQQDZWZmCQERZ2V0RWZmZWN0aXZlR25zYnQBBQ51c2VyQWRkcmVzc1N0cgQIZ25zYnRBbXQJAJEDAgUDZWZmBQpJZHhFZmZVc2VyAwkAZwIAAAUIZ25zYnRBbXQJAAIBAhBubyBnbnNidCB0byB2b3RlBApnbnNidFRvdGFsCQCRAwIFA2VmZgULSWR4RWZmVG90YWwECW9sZENob2ljZQkAnwgBCQEca2V5UHJvcG9zYWxDaG9pY2VCeUlkQW5kVXNlcgIFCnByb3Bvc2FsSWQFDnVzZXJBZGRyZXNzU3RyBAxvbGRVc2VyVm90ZXMDCQEBIQEJAQlpc0RlZmluZWQBBQlvbGRDaG9pY2UAAAkBDGdldEludE9yRWxzZQIJARtrZXlQcm9wb3NhbFZvdGVzQnlJZEFuZFVzZXICBQpwcm9wb3NhbElkBQ51c2VyQWRkcmVzc1N0cgAABBNvbGRUb3RhbEJ5T2xkQ2hvaWNlAwkBCWlzRGVmaW5lZAEFCW9sZENob2ljZQkBDGdldEludE9yRWxzZQIJAR1rZXlQcm9wb3NhbFZvdGVzQnlJZEFuZE9wdGlvbgIFCnByb3Bvc2FsSWQJAQV2YWx1ZQEFCW9sZENob2ljZQAAAAAEE29sZFRvdGFsQnlOZXdDaG9pY2UJAQxnZXRJbnRPckVsc2UCCQEda2V5UHJvcG9zYWxWb3Rlc0J5SWRBbmRPcHRpb24CBQpwcm9wb3NhbElkBQZjaG9pY2UAAAQIb2xkVG90YWwJAQ1wYXJzZUludFZhbHVlAQkAkQMCBQtkeW5hbWljRGF0YQUWZ292U3RhdHVzSWR4VG90YWxWb3RlcwQTbmV3VG90YWxCeU9sZENob2ljZQMJAQEhAQkBCWlzRGVmaW5lZAEFCW9sZENob2ljZQAACQBkAgkAZQIFE29sZFRvdGFsQnlPbGRDaG9pY2UFDG9sZFVzZXJWb3RlcwMJAAACCQEFdmFsdWUBBQlvbGRDaG9pY2UFBmNob2ljZQUIZ25zYnRBbXQAAAQTbmV3VG90YWxCeU5ld0Nob2ljZQMDCQEJaXNEZWZpbmVkAQUJb2xkQ2hvaWNlCQAAAgkBBXZhbHVlAQUJb2xkQ2hvaWNlBQZjaG9pY2UHBRNuZXdUb3RhbEJ5T2xkQ2hvaWNlCQBkAgUTb2xkVG90YWxCeU5ld0Nob2ljZQUIZ25zYnRBbXQECG5ld1RvdGFsCQBkAgkAZQIFCG9sZFRvdGFsBQxvbGRVc2VyVm90ZXMFCGduc2J0QW10BA9pc1F1b3J1bVJlYWNoZWQJAGcCBQhuZXdUb3RhbAkBDXBhcnNlSW50VmFsdWUBCQCRAwIFCHByb3BEYXRhBQxnb3ZJZHhRdW9ydW0EFW51bVZvdGVyc0J5UHJvcG9zYWxJZAkBDGdldEludE9yRWxzZQIJAR5rZXlOdW1VbmlxdWVWb3RlcnNCeVByb3Bvc2FsSWQBBQpwcm9wb3NhbElkAAAEFG9sZEF2ZXJVbmlxdWVWb3RlcnM2CQEMZ2V0SW50T3JFbHNlAgkBGGtleVN0YXRzQXZlclVuaXF1ZVZvdGVycwAAAAQMbnVtUHJvcG9zYWxzCQERQGV4dHJOYXRpdmUoMTA1NSkBCQERa2V5TGFzdFByb3Bvc2FsSWQABAp1bmlxdWVEaWZmAwkAAAIFDG9sZFVzZXJWb3RlcwAAAAEAAAQUbmV3QXZlclVuaXF1ZVZvdGVyczYJAGQCBRRvbGRBdmVyVW5pcXVlVm90ZXJzNgkAawMFCnVuaXF1ZURpZmYFBU1VTFQ2BQxudW1Qcm9wb3NhbHMEDG9sZEF2ZXJHbnNidAkBDGdldEludE9yRWxzZQIJARZrZXlTdGF0c0F2ZXJHbnNidFZvdGVkAAAABAxuZXdBdmVyR25zYnQJAGQCBQxvbGRBdmVyR25zYnQJAGkCCQBlAgUIZ25zYnRBbXQFDG9sZFVzZXJWb3RlcwUMbnVtUHJvcG9zYWxzBA5pc1ByZXZPcHRpb25hbAMJAQEhAQkBCWlzRGVmaW5lZAEFCW9sZENob2ljZQYJAAACCQEFdmFsdWUBBQlvbGRDaG9pY2UFBmNob2ljZQQQb3B0aW9uYWxUb3RhbE9sZAMFDmlzUHJldk9wdGlvbmFsBQNuaWwJAMwIAgkBDEludGVnZXJFbnRyeQIJAR1rZXlQcm9wb3NhbFZvdGVzQnlJZEFuZE9wdGlvbgIFCnByb3Bvc2FsSWQJAQV2YWx1ZQEFCW9sZENob2ljZQUTbmV3VG90YWxCeU9sZENob2ljZQUDbmlsBAZ3aW5PcHQJAQ1jYWxjV2luT3B0aW9uBwUKcHJvcG9zYWxJZAUQYXZhaWxhYmxlT3B0aW9ucwUOaXNQcmV2T3B0aW9uYWwFCW9sZENob2ljZQUTbmV3VG90YWxCeU9sZENob2ljZQUGY2hvaWNlBRNuZXdUb3RhbEJ5TmV3Q2hvaWNlCQCUCgIJAM4IAgkAzAgCCQEMSW50ZWdlckVudHJ5AgkBHGtleVByb3Bvc2FsQ2hvaWNlQnlJZEFuZFVzZXICBQpwcm9wb3NhbElkBQ51c2VyQWRkcmVzc1N0cgUGY2hvaWNlCQDMCAIJAQxJbnRlZ2VyRW50cnkCCQEba2V5UHJvcG9zYWxWb3Rlc0J5SWRBbmRVc2VyAgUKcHJvcG9zYWxJZAUOdXNlckFkZHJlc3NTdHIFCGduc2J0QW10CQDMCAIJAQxJbnRlZ2VyRW50cnkCCQEda2V5UHJvcG9zYWxWb3Rlc0J5SWRBbmRPcHRpb24CBQpwcm9wb3NhbElkBQZjaG9pY2UFE25ld1RvdGFsQnlOZXdDaG9pY2UJAMwIAgkBDEludGVnZXJFbnRyeQIJAR5rZXlOdW1VbmlxdWVWb3RlcnNCeVByb3Bvc2FsSWQBBQpwcm9wb3NhbElkCQBkAgUVbnVtVm90ZXJzQnlQcm9wb3NhbElkBQp1bmlxdWVEaWZmCQDMCAIJAQxJbnRlZ2VyRW50cnkCCQEYa2V5U3RhdHNBdmVyVW5pcXVlVm90ZXJzAAUUbmV3QXZlclVuaXF1ZVZvdGVyczYJAMwIAgkBDEludGVnZXJFbnRyeQIJARZrZXlTdGF0c0F2ZXJHbnNidFZvdGVkAAUMbmV3QXZlckduc2J0CQDMCAIJAQtTdHJpbmdFbnRyeQIJARlrZXlQcm9wb3NhbFN0YXR1c0RhdGFCeUlkAQUKcHJvcG9zYWxJZAkBEHVwZGF0ZVN0YXR1c0RhdGEEBQtkeW5hbWljRGF0YQUPaXNRdW9ydW1SZWFjaGVkBQZ3aW5PcHQFCG5ld1RvdGFsBQNuaWwFEG9wdGlvbmFsVG90YWxPbGQFBHVuaXQBaQESaW5pdGlhdGVJZGVhVm90aW5nBQlmb3J1bUxpbmsFdGl0bGUPdm90aW5nU3RhcnRUaW1lDXZvdGluZ0VuZFRpbWULb3B0aW9uc0xpc3QDCQECIT0CCQCQAwEIBQFpCHBheW1lbnRzAAEJAAIBAhxFeGFjdGx5IG9uZSBwYXltZW50IHJlcXVpcmVkAwkBAiE9AgkAkAMBBQtvcHRpb25zTGlzdAACCQACAQIrRXhhY3RseSAyIG9wdGlvbiBbJ05PJywgJ1lFUyddIGFyZSBleHBlY3RlZAMJAQIhPQIJAJEDAgULb3B0aW9uc0xpc3QAAAICTk8JAAIBAh1PcHRpb24gTk8gc2hvdWxkIGJlIHRoZSBmaXJzdAMJAQIhPQIJAJEDAgULb3B0aW9uc0xpc3QAAQIDWUVTCQACAQIfT3B0aW9uIFlFUyBzaG91bGQgYmUgdGhlIHNlY29uZAkBDmluaXRpYXRlVm90aW5nCwkBBXZhbHVlAQkAkQMCCAUBaQhwYXltZW50cwAACQDYBAEIBQFpDXRyYW5zYWN0aW9uSWQCBElERUEJAKUIAQgFAWkGY2FsbGVyBQlmb3J1bUxpbmsFBXRpdGxlBQ92b3RpbmdTdGFydFRpbWUFDXZvdGluZ0VuZFRpbWUCB1BFTkRJTkcFA25pbAULb3B0aW9uc0xpc3QBaQEUaW5pdGlhdGVVcGRhdGVWb3RpbmcFCWZvcnVtTGluawV0aXRsZQ92b3RpbmdTdGFydFRpbWUNdm90aW5nRW5kVGltZQZ0eExpc3QDCQECIT0CCQCQAwEIBQFpCHBheW1lbnRzAAEJAAIBAhxFeGFjdGx5IG9uZSBwYXltZW50IHJlcXVpcmVkAwkAZgIAAQkAkAMBBQZ0eExpc3QJAAIBAhpUcmFuc2FjdGlvbnMgbGlzdCBpcyBlbXB0eQMJAQIhPQIIBQFpBmNhbGxlcgUEdGhpcwkAAgECDm5vdCBhdXRob3JpemVkCQEOaW5pdGlhdGVWb3RpbmcLCQEFdmFsdWUBCQCRAwIIBQFpCHBheW1lbnRzAAAJANgEAQgFAWkNdHJhbnNhY3Rpb25JZAIGVVBEQVRFCQClCAEIBQFpBmNhbGxlcgUJZm9ydW1MaW5rBQV0aXRsZQUPdm90aW5nU3RhcnRUaW1lBQ12b3RpbmdFbmRUaW1lAgdQRU5ESU5HBQZ0eExpc3QJAMwIAgICTk8JAMwIAgIDWUVTBQNuaWwBaQEMY2FuY2VsVm90aW5nAQpwcm9wb3NhbElkAwkBAiE9AggFAWkGY2FsbGVyBQR0aGlzCQACAQIObm90IGF1dGhvcml6ZWQEC2N1cnJlbnREYXRhCQEPZ2V0U3RyaW5nT3JGYWlsAgUEdGhpcwkBGWtleVByb3Bvc2FsU3RhdHVzRGF0YUJ5SWQBBQpwcm9wb3NhbElkBAt1cGRhdGVkRGF0YQkArAICCQCsAgIJAK8CAgULY3VycmVudERhdGEJAQV2YWx1ZQEJALcJAgULY3VycmVudERhdGEFA1NFUAUDU0VQAgR0cnVlCQCUCgIJAMwIAgkBC1N0cmluZ0VudHJ5AgkBGWtleVByb3Bvc2FsU3RhdHVzRGF0YUJ5SWQBBQpwcm9wb3NhbElkBQt1cGRhdGVkRGF0YQUDbmlsBQR1bml0AWkBC2FwcGx5VXBkYXRlAQpwcm9wb3NhbElkBAhwcm9wRGF0YQkAtQkCCQEPZ2V0U3RyaW5nT3JGYWlsAgUEdGhpcwkBE2tleVByb3Bvc2FsRGF0YUJ5SWQBBQpwcm9wb3NhbElkBQNTRVAEA2VuZAkBDXBhcnNlSW50VmFsdWUBCQCRAwIFCHByb3BEYXRhBQlnb3ZJZHhFbmQEA25vdwgFCWxhc3RCbG9jawl0aW1lc3RhbXADCQBmAgUDZW5kBQNub3cJAAIBAhpWb3RpbmcgaXMgbm90IGZpbmlzaGVkIHlldAMJAQIhPQICBlVQREFURQkAkQMCBQhwcm9wRGF0YQUKZ292SWR4VHlwZQkAAgECH09ubHkgVVBEQVRFIHR5cGUgY2FuIGJlIGFwcGxpZWQEC2R5bmFtaWNEYXRhCQC1CQIJAQ9nZXRTdHJpbmdPckZhaWwCBQR0aGlzCQEZa2V5UHJvcG9zYWxTdGF0dXNEYXRhQnlJZAEFCnByb3Bvc2FsSWQFA1NFUAMJAAACCQCRAwIFC2R5bmFtaWNEYXRhBRZnb3ZTdGF0dXNJZHhJc0NhbmNlbGVkAgR0cnVlCQACAQISVm90aW5nIGlzIGNhbmNlbGVkAwkBAiE9AgkAkQMCBQtkeW5hbWljRGF0YQUTZ292U3RhdHVzSWR4SXNWYWxpZAIEdHJ1ZQkAAgECFVZvdGluZyBzdGF0dXMgaW52YWxpZAMJAQIhPQIJAJEDAgULZHluYW1pY0RhdGEFEmdvdlN0YXR1c0lkeFdpbk9wdAIBMQkAAgECIVdpbm5lciBpcyAnTk8nIC0gbm90aGluZyB0byBhcHBseQMJAAACCQCRAwIFC2R5bmFtaWNEYXRhBRVnb3ZTdGF0dXNJZHhTY0FwcGxpZWQCBHRydWUJAAIBAhtTY3JpcHRzIGFyZSBhbHJlYWR5IGFwcGxpZWQECnNjcmlwdFRpbWUJAQ1wYXJzZUludFZhbHVlAQkAkQMCBQtkeW5hbWljRGF0YQUSZ292U3RhdHVzSWR4U2NUaW1lAwkAZgIJAGUCBQNub3cFClBBU1RNQVJHSU4FCnNjcmlwdFRpbWUJAAIBCQCsAgIJAKwCAgISU2NyaXB0cyB0aW1lc3RhbXA9CQCkAwEFCnNjcmlwdFRpbWUCKiBpcyB0b28gZmFyIGluIHRoZSBwYXN0LCBtYXggMiBocnMgYWxsb3dlZAMJAGYCBQpzY3JpcHRUaW1lCQBkAgUDbm93BQxGVVRVUkVNQVJHSU4JAAIBCQCsAgIJAKwCAgISU2NyaXB0cyB0aW1lc3RhbXA9CQCkAwEFCnNjcmlwdFRpbWUCLiBpcyB0b28gZmFyIGluIHRoZSBmdXR1cmUsIG1heCAxLjUgaHJzIGFsbG93ZWQEDGluUHJvZ3Jlc3NJZAkBDGdldEludE9yRWxzZQIJARJrZXlBcHBseUluUHJvZ3Jlc3MAAP///////////wEDCQECIT0CBQxpblByb2dyZXNzSWQA////////////AQkAAgEJAKwCAgkArAICAgtwcm9wb3NhbElkPQkApAMBBQxpblByb2dyZXNzSWQCKyBpcyBhbHJlYWR5IGJlaW5nIGFwcGxpZWQuIEZpbmlzaCBpdCBmaXJzdCEECHNodXRkb3duCQD8BwQFD2NvbnRyb2xDb250cmFjdAIVY2FsbEVtZXJnZW5jeVNodXRkb3duCQDMCAICGkFwcGx5aW5nIEdvdmVybmFuY2UgVVBEQVRFBQNuaWwFA25pbAMJAAACBQhzaHV0ZG93bgUIc2h1dGRvd24JAJQKAgkAzAgCCQEMSW50ZWdlckVudHJ5AgkBEmtleUFwcGx5SW5Qcm9ncmVzcwAFCnByb3Bvc2FsSWQFA25pbAUEdW5pdAkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgFpAQtmaW5pc2hBcHBseQAECnByb3Bvc2FsSWQJARN2YWx1ZU9yRXJyb3JNZXNzYWdlAgkAnwgBCQESa2V5QXBwbHlJblByb2dyZXNzAAInTm8gYXBwbHkgaW4gcHJvZ3Jlc3MsIG5vdGhpbmcgdG8gZmluaXNoBAhwcm9wRGF0YQkAtQkCCQEPZ2V0U3RyaW5nT3JGYWlsAgUEdGhpcwkBE2tleVByb3Bvc2FsRGF0YUJ5SWQBBQpwcm9wb3NhbElkBQNTRVAEBnR4TGlzdAkAtQkCCQCRAwIFCHByb3BEYXRhBQtnb3ZJZHhUeElkcwUHTElTVFNFUAQLZHluYW1pY0RhdGEJALUJAgkBD2dldFN0cmluZ09yRmFpbAIFBHRoaXMJARlrZXlQcm9wb3NhbFN0YXR1c0RhdGFCeUlkAQUKcHJvcG9zYWxJZAUDU0VQAwkAAAIJAJEDAgULZHluYW1pY0RhdGEFFWdvdlN0YXR1c0lkeFNjQXBwbGllZAIEdHJ1ZQkAAgECG1NjcmlwdHMgYXJlIGFscmVhZHkgYXBwbGllZAoBB2NoZWNrZXICA2FjYwJ0eAMJAQEhAQkBCWlzRGVmaW5lZAEJAOkHAQkA2QQBBQJ0eAkAAgEJAKwCAgISTk9UIGFwcGxpZWQgdHhJZDogBQJ0eAUEdW5pdAQHaWdub3JlZAoAAiRsBQZ0eExpc3QKAAIkcwkAkAMBBQIkbAoABSRhY2MwBQR1bml0CgEFJGYwXzECAiRhAiRpAwkAZwIFAiRpBQIkcwUCJGEJAQdjaGVja2VyAgUCJGEJAJEDAgUCJGwFAiRpCgEFJGYwXzICAiRhAiRpAwkAZwIFAiRpBQIkcwUCJGEJAAIBAhRMaXN0IHNpemUgZXhjZWVkcyAyMAkBBSRmMF8yAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgUFJGFjYzAAAAABAAIAAwAEAAUABgAHAAgACQAKAAsADAANAA4ADwAQABEAEgATABQEB3ZlcnNpb24JAGQCCQEMZ2V0SW50T3JFbHNlAgkBFGtleUxhc3RVcGRhdGVWZXJzaW9uAAAAAAEJAJQKAgkAzAgCCQELRGVsZXRlRW50cnkBCQESa2V5QXBwbHlJblByb2dyZXNzAAkAzAgCCQEMSW50ZWdlckVudHJ5AgkBFGtleUxhc3RVcGRhdGVWZXJzaW9uAAUHdmVyc2lvbgkAzAgCCQELU3RyaW5nRW50cnkCCQEZa2V5UHJvcG9zYWxTdGF0dXNEYXRhQnlJZAEFCnByb3Bvc2FsSWQJARFzdGF0dXNBcHBseVNjcmlwdAEFC2R5bmFtaWNEYXRhCQDMCAIJARBFeGVjdXRpb25IaXN0b3J5AwUHdmVyc2lvbgkAkQMCBQhwcm9wRGF0YQULZ292SWR4VGl0bGUJAJEDAgUIcHJvcERhdGEFCWdvdklkeFVybAUDbmlsBQdpZ25vcmVkAJr69A0=", "height": 2331308, "applicationStatus": "succeeded", "spentComplexity": 0 } View: original | compacted Prev: CMCQseThWrf5ma1kFxZLiGmdmSeJAtf9FvsshJbegvwe Next: BVYgNmwxdYep6xXWHL6P9DDVAptudMqpAp1JMie9oUzL Diff:
OldNewDifferences
148148 func keyApplyInProgress () = "%s__applyInProgress"
149149
150150
151-func keyProposalIdByUrl (forumLink) = ("%s%s__proposalIdByUrl__" + toBase64String(toBytes(forumLink)))
151+func keyProposalIdByTopicId (topicId) = ("%s%s__proposalIdByTopicId__" + toString(topicId))
152152
153153
154154 func keyNumUniqueVotersByProposalId (proposalId) = ("%s%d__numVoters__" + toString(proposalId))
230230 }
231231
232232
233+func validateLink (url) = if ((value(indexOf(url, URLPATTERN)) != 0))
234+ then throw("Invalid url")
235+ else if ((size(url) > MAXURL))
236+ then throw("Url too long!")
237+ else {
238+ let topicId = valueOrErrorMessage(parseInt(drop(url, (value(lastIndexOf(url, "/")) + 1))), "Wrong topicId")
239+ let registeredId = getInteger(keyProposalIdByTopicId(topicId))
240+ if (isDefined(registeredId))
241+ then throw(("Voting with such forum link is already registered by id=" + toString(value(registeredId))))
242+ else topicId
243+ }
244+
245+
233246 func initiateVoting (payment,proposalTxId,type,author,forumLink,title,votingStartTime,votingEndTime,status,txList,optionsList) = if ((payment.assetId != unit))
234247 then throw("Allowed WAVES payment only!")
235248 else {
236249 let pmtReq = getIntOrElse(keyPaymentRequired(), DEFAULTPAYMENT)
237250 if ((pmtReq > payment.amount))
238251 then throw(("Payment attached should be at least " + toString(pmtReq)))
239- else if ((value(indexOf(forumLink, URLPATTERN)) != 0))
240- then throw("Invalid url")
241- else if ((size(forumLink) > MAXURL))
242- then throw("Url too long!")
243- else {
244- let registeredId = getInteger(keyProposalIdByUrl(forumLink))
245- if (isDefined(registeredId))
246- then throw(("Voting with such forum link is already registered by id=" + toString(value(registeredId))))
247- else if ((title == ""))
248- then throw("Title is empty")
249- else if ((size(title) > MAXTITLE))
250- then throw("Too long title")
251- else {
252- let proposalTime = lastBlock.timestamp
253- if ((proposalTime > votingStartTime))
254- then throw(((("votingStartTime=" + toString(votingStartTime)) + " < proposalTime=") + toString(proposalTime)))
255- else if ((votingStartTime > votingEndTime))
256- then throw(((("votingEndTime=" + toString(votingEndTime)) + " < votingStartTime=") + toString(votingStartTime)))
257- else if (((votingEndTime - votingStartTime) > MAXVOTINGTIME))
258- then throw(((("Voting period exceeds max: " + toString((votingEndTime - votingStartTime))) + " > ") + toString(MAXVOTINGTIME)))
259- else {
260- let txIds = if ((type == "IDEA"))
261- then ""
262- else checkTxList(txList)
263- if ((1 >= size(optionsList)))
264- then throw("Too few choices to vote")
265- else {
266- let eff = getEffectiveGnsbt(author)
267- let gnsbtTotal = eff[IdxEffTotal]
268- let gNsbtUser = eff[IdxEffUser]
269- let gnsbtReq = getIntOrElse(keyGnsbtRequired(), DEFAULTCREATIONGNSBT)
270- if ((gnsbtReq > gNsbtUser))
271- then throw((("You need at least " + toString(gnsbtReq)) + " gNsbt to create voting"))
272- else {
273- let amountLeased = invoke(neutrinoContract, "acceptWaves", nil, [payment])
274- if ((amountLeased == amountLeased))
275- then {
276- let quorum = getIntOrElse(keyQuorumRequiredPercent(), DEFAULTQUORUM)
277- let quorumInGnsbt = fraction(quorum, gnsbtTotal, MULT6)
278- let proposalId = (getIntOrElse(keyLastProposalId(), 0) + 1)
279- let numProposalsByAuthor = (getIntOrElse(keyNumProposalsByAuthor(author), 0) + 1)
280- let uniqAuthors = (getIntOrElse(keyStatsUniqueAuthors(), 0) + (if ((numProposalsByAuthor == 1))
281- then 1
282- else 0))
283- let optionsStr = makeString(optionsList, LISTSEP)
284- $Tuple2([IntegerEntry(keyLastProposalId(), proposalId), IntegerEntry(keyProposalIdByUrl(forumLink), proposalId), StringEntry(keyProposalStatusDataById(proposalId), statusData(false, 0, 0, 0, false, 0, false)), StringEntry(keyProposalDataById(proposalId), proposalData(proposalTxId, type, author, forumLink, title, proposalTime, votingStartTime, votingEndTime, txIds, quorumInGnsbt, optionsStr)), IntegerEntry(keyNumProposalsByAuthor(author), numProposalsByAuthor), IntegerEntry(keyStatsUniqueAuthors(), uniqAuthors)], proposalTxId)
285- }
286- else throw("Strict value is not equal to itself.")
287- }
288- }
289- }
290- }
291- }
252+ else {
253+ let topicId = validateLink(forumLink)
254+ if ((title == ""))
255+ then throw("Title is empty")
256+ else if ((size(title) > MAXTITLE))
257+ then throw("Too long title")
258+ else {
259+ let proposalTime = lastBlock.timestamp
260+ if ((proposalTime > votingStartTime))
261+ then throw(((("votingStartTime=" + toString(votingStartTime)) + " < proposalTime=") + toString(proposalTime)))
262+ else if ((votingStartTime > votingEndTime))
263+ then throw(((("votingEndTime=" + toString(votingEndTime)) + " < votingStartTime=") + toString(votingStartTime)))
264+ else if (((votingEndTime - votingStartTime) > MAXVOTINGTIME))
265+ then throw(((("Voting period exceeds max: " + toString((votingEndTime - votingStartTime))) + " > ") + toString(MAXVOTINGTIME)))
266+ else {
267+ let txIds = if ((type == "IDEA"))
268+ then ""
269+ else checkTxList(txList)
270+ if ((1 >= size(optionsList)))
271+ then throw("Too few choices to vote")
272+ else {
273+ let eff = getEffectiveGnsbt(author)
274+ let gnsbtTotal = eff[IdxEffTotal]
275+ let gNsbtUser = eff[IdxEffUser]
276+ let gnsbtReq = getIntOrElse(keyGnsbtRequired(), DEFAULTCREATIONGNSBT)
277+ if ((gnsbtReq > gNsbtUser))
278+ then throw((("You need at least " + toString(gnsbtReq)) + " gNsbt to create voting"))
279+ else {
280+ let amountLeased = invoke(neutrinoContract, "acceptWaves", nil, [payment])
281+ if ((amountLeased == amountLeased))
282+ then {
283+ let quorum = getIntOrElse(keyQuorumRequiredPercent(), DEFAULTQUORUM)
284+ let quorumInGnsbt = fraction(quorum, gnsbtTotal, MULT6)
285+ let proposalId = (getIntOrElse(keyLastProposalId(), 0) + 1)
286+ let numProposalsByAuthor = (getIntOrElse(keyNumProposalsByAuthor(author), 0) + 1)
287+ let uniqAuthors = (getIntOrElse(keyStatsUniqueAuthors(), 0) + (if ((numProposalsByAuthor == 1))
288+ then 1
289+ else 0))
290+ let optionsStr = makeString(optionsList, LISTSEP)
291+ $Tuple2([IntegerEntry(keyLastProposalId(), proposalId), IntegerEntry(keyProposalIdByTopicId(topicId), proposalId), StringEntry(keyProposalStatusDataById(proposalId), statusData(false, 0, 0, 0, false, 0, false)), StringEntry(keyProposalDataById(proposalId), proposalData(proposalTxId, type, author, forumLink, title, proposalTime, votingStartTime, votingEndTime, txIds, quorumInGnsbt, optionsStr)), IntegerEntry(keyNumProposalsByAuthor(author), numProposalsByAuthor), IntegerEntry(keyStatsUniqueAuthors(), uniqAuthors)], proposalTxId)
292+ }
293+ else throw("Strict value is not equal to itself.")
294+ }
295+ }
296+ }
297+ }
298+ }
292299 }
293300
294301
Full:
OldNewDifferences
11 {-# STDLIB_VERSION 6 #-}
22 {-# SCRIPT_TYPE ACCOUNT #-}
33 {-# CONTENT_TYPE DAPP #-}
44 let revisionNum = ""
55
66 let SEP = "__"
77
88 let LISTSEP = ":"
99
1010 let DEFAULTQUORUM = 500000
1111
1212 let URLPATTERN = "https://forum.neutrino.at/"
1313
1414 let MAXTITLE = 160
1515
1616 let MAXURL = 250
1717
1818 let MAXVOTINGTIME = 1209600000
1919
2020 let MULT6 = 1000000
2121
2222 let DEFAULTPAYMENT = 1000000000
2323
2424 let DEFAULTCREATIONGNSBT = 1000000000
2525
2626 let PASTMARGIN = 7200000
2727
2828 let FUTUREMARGIN = 5400000
2929
3030 let govIdxProposalTxId = 1
3131
3232 let govIdxType = 2
3333
3434 let govIdxAuthor = 3
3535
3636 let govIdxUrl = 4
3737
3838 let govIdxTitle = 5
3939
4040 let govIdxCreationTime = 6
4141
4242 let govIdxStart = 7
4343
4444 let govIdxEnd = 8
4545
4646 let govIdxTxIds = 9
4747
4848 let govIdxQuorum = 10
4949
5050 let govIdxOptions = 11
5151
5252 let govStatusIdxIsValid = 1
5353
5454 let govStatusIdxWinOpt = 2
5555
5656 let govStatusIdxWinVotes = 3
5757
5858 let govStatusIdxTotalVotes = 4
5959
6060 let govStatusIdxScApplied = 5
6161
6262 let govStatusIdxScTime = 6
6363
6464 let govStatusIdxIsCanceled = 7
6565
6666 func getStringOrFail (address,key) = valueOrErrorMessage(getString(address, key), makeString(["mandatory ", toString(address), ".", key, " is not defined"], ""))
6767
6868
6969 func getIntOrElse (key,defaultVal) = valueOrElse(getInteger(this, key), defaultVal)
7070
7171
7272 let IdxControlCfgNeutrinoDapp = 1
7373
7474 let IdxControlCfgAuctionDapp = 2
7575
7676 let IdxControlCfgRpdDapp = 3
7777
7878 let IdxControlCfgMathDapp = 4
7979
8080 let IdxControlCfgLiquidationDapp = 5
8181
8282 let IdxControlCfgRestDapp = 6
8383
8484 let IdxControlCfgNodeRegistryDapp = 7
8585
8686 let IdxControlCfgNsbtStakingDapp = 8
8787
8888 let IdxControlCfgMediatorDapp = 9
8989
9090 let IdxControlCfgSurfStakingDapp = 10
9191
9292 let IdxControlCfgGnsbtControllerDapp = 11
9393
9494 let IdxControlCfgRestV2Dapp = 12
9595
9696 let IdxControlCfgGovernanceDapp = 13
9797
9898 func keyControlAddress () = "%s%s__config__controlAddress"
9999
100100
101101 func keyControlCfg () = "%s__controlConfig"
102102
103103
104104 func readControlCfgOrFail (control) = split_4C(getStringOrFail(control, keyControlCfg()), SEP)
105105
106106
107107 func getContractAddressOrFail (controlCfg,idx) = valueOrErrorMessage(addressFromString(controlCfg[idx]), ("Control cfg doesn't contain address at index " + toString(idx)))
108108
109109
110110 let controlContract = addressFromStringValue(valueOrElse(getString(this, keyControlAddress()), "3P5Bfd58PPfNvBM2Hy8QfbcDqMeNtzg7KfP"))
111111
112112 let controlCfg = readControlCfgOrFail(controlContract)
113113
114114 let neutrinoContract = getContractAddressOrFail(controlCfg, IdxControlCfgNeutrinoDapp)
115115
116116 let gnsbtControllerContract = getContractAddressOrFail(controlCfg, IdxControlCfgGnsbtControllerDapp)
117117
118118 func keyQuorumRequiredPercent () = "%s__quorumRequired"
119119
120120
121121 func keyPaymentRequired () = "%s__paymentRequired"
122122
123123
124124 func keyGnsbtRequired () = "%s__gNsbtRequired"
125125
126126
127127 func keyLastProposalId () = "%s__proposalId"
128128
129129
130130 func keyLastUpdateVersion () = "%s__updateVersion"
131131
132132
133133 func keyProposalStatusDataById (proposalId) = ("%s%d__proposalStatusData__" + toString(proposalId))
134134
135135
136136 func keyProposalDataById (proposalId) = ("%s%d__proposalData__" + toString(proposalId))
137137
138138
139139 func keyProposalVotesByIdAndOption (proposalId,opt) = makeString(["%s%d%d", "votesByOpt", toString(proposalId), toString(opt)], SEP)
140140
141141
142142 func keyProposalVotesByIdAndUser (proposalId,userAddr) = makeString(["%s%d%s", "votesByUser", toString(proposalId), userAddr], SEP)
143143
144144
145145 func keyProposalChoiceByIdAndUser (proposalId,userAddr) = makeString(["%s%d%s", "optionByUser", toString(proposalId), userAddr], SEP)
146146
147147
148148 func keyApplyInProgress () = "%s__applyInProgress"
149149
150150
151-func keyProposalIdByUrl (forumLink) = ("%s%s__proposalIdByUrl__" + toBase64String(toBytes(forumLink)))
151+func keyProposalIdByTopicId (topicId) = ("%s%s__proposalIdByTopicId__" + toString(topicId))
152152
153153
154154 func keyNumUniqueVotersByProposalId (proposalId) = ("%s%d__numVoters__" + toString(proposalId))
155155
156156
157157 func keyStatsAverUniqueVoters () = "%s%s%s__stats__avg__uniqueVoters"
158158
159159
160160 func keyStatsAverGnsbtVoted () = "%s%s%s__stats__avg__gnsbtVoted"
161161
162162
163163 func keyStatsUniqueAuthors () = "%s%s__stats__uniqueAuthors"
164164
165165
166166 func keyNumProposalsByAuthor (addressStr) = ("%s%s__numProposalsByAuthor__" + addressStr)
167167
168168
169169 func keyApplyHistory (timestamp) = ("%s%d__applyHistory__" + toString(timestamp))
170170
171171
172172 func asAnyList (v) = match v {
173173 case l: List[Any] =>
174174 l
175175 case _ =>
176176 throw("fail to cast into List[Any]")
177177 }
178178
179179
180180 func asInt (v) = match v {
181181 case i: Int =>
182182 i
183183 case _ =>
184184 throw("fail to cast into Int")
185185 }
186186
187187
188188 func statusData (isVotingValid,winOption,winOptionVotes,totalVotes,areScriptsApplied,scriptsTimestamp,canceledByTeam) = makeString(["%b%d%d%d%b%d%b", toString(isVotingValid), toString(winOption), toString(winOptionVotes), toString(totalVotes), toString(areScriptsApplied), toString(scriptsTimestamp), toString(canceledByTeam)], SEP)
189189
190190
191191 func proposalData (proposalTxId,type,author,forumLink,title,proposalTime,votingStartTime,votingEndTime,txIds,quorumInGnsbt,options) = makeString(["%s%s%s%s%s%d%d%d%s%d%s", proposalTxId, type, author, forumLink, title, toString(proposalTime), toString(votingStartTime), toString(votingEndTime), txIds, toString(quorumInGnsbt), options], SEP)
192192
193193
194194 func checkTxList (txList) = if ((size(txList) > 20))
195195 then throw(("Too many transactions: " + toString(size(txList))))
196196 else {
197197 func combiner (acc,tx) = if ((size(fromBase58String(tx)) != 32))
198198 then throw(("Wrong txId: " + tx))
199199 else if ((acc == ""))
200200 then tx
201201 else ((acc + LISTSEP) + tx)
202202
203203 let $l = txList
204204 let $s = size($l)
205205 let $acc0 = ""
206206 func $f0_1 ($a,$i) = if (($i >= $s))
207207 then $a
208208 else combiner($a, $l[$i])
209209
210210 func $f0_2 ($a,$i) = if (($i >= $s))
211211 then $a
212212 else throw("List size exceeds 20")
213213
214214 $f0_2($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($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), 11), 12), 13), 14), 15), 16), 17), 18), 19), 20)
215215 }
216216
217217
218218 let IdxEffTotal = 0
219219
220220 let IdxEffUser = 1
221221
222222 func getEffectiveGnsbt (userAddrStrOrEmpty) = {
223223 let gnsbtData = asAnyList(invoke(gnsbtControllerContract, "gnsbtInfoSYSREADONLY", [userAddrStrOrEmpty, 0, 0], nil))
224224 let nsbtData = asAnyList(gnsbtData[2])
225225 let userFromNsbt = asInt(nsbtData[2])
226226 let totalFromNsbt = asInt(nsbtData[3])
227227 let userMatureFromSurf = asInt(gnsbtData[9])
228228 let totalMatureFromSurf = asInt(gnsbtData[6])
229229 [(totalFromNsbt + totalMatureFromSurf), (userFromNsbt + userMatureFromSurf)]
230230 }
231231
232232
233+func validateLink (url) = if ((value(indexOf(url, URLPATTERN)) != 0))
234+ then throw("Invalid url")
235+ else if ((size(url) > MAXURL))
236+ then throw("Url too long!")
237+ else {
238+ let topicId = valueOrErrorMessage(parseInt(drop(url, (value(lastIndexOf(url, "/")) + 1))), "Wrong topicId")
239+ let registeredId = getInteger(keyProposalIdByTopicId(topicId))
240+ if (isDefined(registeredId))
241+ then throw(("Voting with such forum link is already registered by id=" + toString(value(registeredId))))
242+ else topicId
243+ }
244+
245+
233246 func initiateVoting (payment,proposalTxId,type,author,forumLink,title,votingStartTime,votingEndTime,status,txList,optionsList) = if ((payment.assetId != unit))
234247 then throw("Allowed WAVES payment only!")
235248 else {
236249 let pmtReq = getIntOrElse(keyPaymentRequired(), DEFAULTPAYMENT)
237250 if ((pmtReq > payment.amount))
238251 then throw(("Payment attached should be at least " + toString(pmtReq)))
239- else if ((value(indexOf(forumLink, URLPATTERN)) != 0))
240- then throw("Invalid url")
241- else if ((size(forumLink) > MAXURL))
242- then throw("Url too long!")
243- else {
244- let registeredId = getInteger(keyProposalIdByUrl(forumLink))
245- if (isDefined(registeredId))
246- then throw(("Voting with such forum link is already registered by id=" + toString(value(registeredId))))
247- else if ((title == ""))
248- then throw("Title is empty")
249- else if ((size(title) > MAXTITLE))
250- then throw("Too long title")
251- else {
252- let proposalTime = lastBlock.timestamp
253- if ((proposalTime > votingStartTime))
254- then throw(((("votingStartTime=" + toString(votingStartTime)) + " < proposalTime=") + toString(proposalTime)))
255- else if ((votingStartTime > votingEndTime))
256- then throw(((("votingEndTime=" + toString(votingEndTime)) + " < votingStartTime=") + toString(votingStartTime)))
257- else if (((votingEndTime - votingStartTime) > MAXVOTINGTIME))
258- then throw(((("Voting period exceeds max: " + toString((votingEndTime - votingStartTime))) + " > ") + toString(MAXVOTINGTIME)))
259- else {
260- let txIds = if ((type == "IDEA"))
261- then ""
262- else checkTxList(txList)
263- if ((1 >= size(optionsList)))
264- then throw("Too few choices to vote")
265- else {
266- let eff = getEffectiveGnsbt(author)
267- let gnsbtTotal = eff[IdxEffTotal]
268- let gNsbtUser = eff[IdxEffUser]
269- let gnsbtReq = getIntOrElse(keyGnsbtRequired(), DEFAULTCREATIONGNSBT)
270- if ((gnsbtReq > gNsbtUser))
271- then throw((("You need at least " + toString(gnsbtReq)) + " gNsbt to create voting"))
272- else {
273- let amountLeased = invoke(neutrinoContract, "acceptWaves", nil, [payment])
274- if ((amountLeased == amountLeased))
275- then {
276- let quorum = getIntOrElse(keyQuorumRequiredPercent(), DEFAULTQUORUM)
277- let quorumInGnsbt = fraction(quorum, gnsbtTotal, MULT6)
278- let proposalId = (getIntOrElse(keyLastProposalId(), 0) + 1)
279- let numProposalsByAuthor = (getIntOrElse(keyNumProposalsByAuthor(author), 0) + 1)
280- let uniqAuthors = (getIntOrElse(keyStatsUniqueAuthors(), 0) + (if ((numProposalsByAuthor == 1))
281- then 1
282- else 0))
283- let optionsStr = makeString(optionsList, LISTSEP)
284- $Tuple2([IntegerEntry(keyLastProposalId(), proposalId), IntegerEntry(keyProposalIdByUrl(forumLink), proposalId), StringEntry(keyProposalStatusDataById(proposalId), statusData(false, 0, 0, 0, false, 0, false)), StringEntry(keyProposalDataById(proposalId), proposalData(proposalTxId, type, author, forumLink, title, proposalTime, votingStartTime, votingEndTime, txIds, quorumInGnsbt, optionsStr)), IntegerEntry(keyNumProposalsByAuthor(author), numProposalsByAuthor), IntegerEntry(keyStatsUniqueAuthors(), uniqAuthors)], proposalTxId)
285- }
286- else throw("Strict value is not equal to itself.")
287- }
288- }
289- }
290- }
291- }
252+ else {
253+ let topicId = validateLink(forumLink)
254+ if ((title == ""))
255+ then throw("Title is empty")
256+ else if ((size(title) > MAXTITLE))
257+ then throw("Too long title")
258+ else {
259+ let proposalTime = lastBlock.timestamp
260+ if ((proposalTime > votingStartTime))
261+ then throw(((("votingStartTime=" + toString(votingStartTime)) + " < proposalTime=") + toString(proposalTime)))
262+ else if ((votingStartTime > votingEndTime))
263+ then throw(((("votingEndTime=" + toString(votingEndTime)) + " < votingStartTime=") + toString(votingStartTime)))
264+ else if (((votingEndTime - votingStartTime) > MAXVOTINGTIME))
265+ then throw(((("Voting period exceeds max: " + toString((votingEndTime - votingStartTime))) + " > ") + toString(MAXVOTINGTIME)))
266+ else {
267+ let txIds = if ((type == "IDEA"))
268+ then ""
269+ else checkTxList(txList)
270+ if ((1 >= size(optionsList)))
271+ then throw("Too few choices to vote")
272+ else {
273+ let eff = getEffectiveGnsbt(author)
274+ let gnsbtTotal = eff[IdxEffTotal]
275+ let gNsbtUser = eff[IdxEffUser]
276+ let gnsbtReq = getIntOrElse(keyGnsbtRequired(), DEFAULTCREATIONGNSBT)
277+ if ((gnsbtReq > gNsbtUser))
278+ then throw((("You need at least " + toString(gnsbtReq)) + " gNsbt to create voting"))
279+ else {
280+ let amountLeased = invoke(neutrinoContract, "acceptWaves", nil, [payment])
281+ if ((amountLeased == amountLeased))
282+ then {
283+ let quorum = getIntOrElse(keyQuorumRequiredPercent(), DEFAULTQUORUM)
284+ let quorumInGnsbt = fraction(quorum, gnsbtTotal, MULT6)
285+ let proposalId = (getIntOrElse(keyLastProposalId(), 0) + 1)
286+ let numProposalsByAuthor = (getIntOrElse(keyNumProposalsByAuthor(author), 0) + 1)
287+ let uniqAuthors = (getIntOrElse(keyStatsUniqueAuthors(), 0) + (if ((numProposalsByAuthor == 1))
288+ then 1
289+ else 0))
290+ let optionsStr = makeString(optionsList, LISTSEP)
291+ $Tuple2([IntegerEntry(keyLastProposalId(), proposalId), IntegerEntry(keyProposalIdByTopicId(topicId), proposalId), StringEntry(keyProposalStatusDataById(proposalId), statusData(false, 0, 0, 0, false, 0, false)), StringEntry(keyProposalDataById(proposalId), proposalData(proposalTxId, type, author, forumLink, title, proposalTime, votingStartTime, votingEndTime, txIds, quorumInGnsbt, optionsStr)), IntegerEntry(keyNumProposalsByAuthor(author), numProposalsByAuthor), IntegerEntry(keyStatsUniqueAuthors(), uniqAuthors)], proposalTxId)
292+ }
293+ else throw("Strict value is not equal to itself.")
294+ }
295+ }
296+ }
297+ }
298+ }
292299 }
293300
294301
295302 func calcWinOption (proposalId,optionsList,isPrevOptional,oldChoice,optionalTotalOld,newChoice,newTotalByNewChoice) = {
296303 func findBest (acc,elem) = {
297304 let idx = value(indexOf(optionsList, elem))
298305 let val = if (isPrevOptional)
299306 then if ((idx == newChoice))
300307 then newTotalByNewChoice
301308 else getIntOrElse(keyProposalVotesByIdAndOption(proposalId, idx), 0)
302309 else if ((idx == value(oldChoice)))
303310 then optionalTotalOld
304311 else if ((idx == newChoice))
305312 then newTotalByNewChoice
306313 else getIntOrElse(keyProposalVotesByIdAndOption(proposalId, idx), 0)
307314 if ((acc._2 > val))
308315 then acc
309316 else $Tuple2(idx, val)
310317 }
311318
312319 let $l = optionsList
313320 let $s = size($l)
314321 let $acc0 = $Tuple2(0, 0)
315322 func $f0_1 ($a,$i) = if (($i >= $s))
316323 then $a
317324 else findBest($a, $l[$i])
318325
319326 func $f0_2 ($a,$i) = if (($i >= $s))
320327 then $a
321328 else throw("List size exceeds 10")
322329
323330 $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)
324331 }
325332
326333
327334 func updateStatusData (oldData,isValid,newWinOpt,newTotalVotes) = makeString(["%b%d%d%d%b%d%b", toString(isValid), toString(newWinOpt._1), toString(newWinOpt._2), toString(newTotalVotes), oldData[govStatusIdxScApplied], oldData[govStatusIdxScTime], oldData[govStatusIdxIsCanceled]], SEP)
328335
329336
330337 func statusApplyScript (oldData) = makeString(["%b%d%d%d%b%d%b", oldData[govStatusIdxIsValid], oldData[govStatusIdxWinOpt], oldData[govStatusIdxWinVotes], oldData[govStatusIdxTotalVotes], "true", oldData[govStatusIdxScTime], oldData[govStatusIdxIsCanceled]], SEP)
331338
332339
333340 func ExecutionHistory (proposalId,title,url) = {
334341 let gnsbtTotal = getEffectiveGnsbt("")[IdxEffTotal]
335342 let turnout = 500000
336343 StringEntry(keyApplyHistory(lastBlock.timestamp), makeString(["%d%d%d%s%s", toString(proposalId), toString(gnsbtTotal), toString(turnout), title, url], SEP))
337344 }
338345
339346
340347 @Callable(i)
341348 func constructor (controlAddr,gNsbtReqToInit,wavesReqToInit,quorumReqPerc) = if ((i.caller != this))
342349 then throw("Permission denied")
343350 else [StringEntry(keyControlAddress(), controlAddr), IntegerEntry(keyGnsbtRequired(), gNsbtReqToInit), IntegerEntry(keyPaymentRequired(), wavesReqToInit), IntegerEntry(keyQuorumRequiredPercent(), quorumReqPerc)]
344351
345352
346353
347354 @Callable(i)
348355 func castVote (proposalId,choice) = {
349356 let userAddressStr = toString(i.caller)
350357 let dynamicData = split(getStringOrFail(this, keyProposalStatusDataById(proposalId)), SEP)
351358 if ((dynamicData[govStatusIdxIsCanceled] == "true"))
352359 then throw("Voting is canceled by team")
353360 else {
354361 let propData = split(getStringOrFail(this, keyProposalDataById(proposalId)), SEP)
355362 let start = parseIntValue(propData[govIdxStart])
356363 let end = parseIntValue(propData[govIdxEnd])
357364 let now = lastBlock.timestamp
358365 if ((start > now))
359366 then throw("Voting not started yet")
360367 else if ((now >= end))
361368 then throw("Voting already finished")
362369 else {
363370 let availableOptions = split(propData[govIdxOptions], LISTSEP)
364371 let numOptions = size(availableOptions)
365372 if ((1 >= numOptions))
366373 then throw("Too few choices to vote")
367374 else if ((choice >= numOptions))
368375 then throw(("Unknown choice! Must be 0.." + toString((numOptions - 1))))
369376 else {
370377 let eff = getEffectiveGnsbt(userAddressStr)
371378 let gnsbtAmt = eff[IdxEffUser]
372379 if ((0 >= gnsbtAmt))
373380 then throw("no gnsbt to vote")
374381 else {
375382 let gnsbtTotal = eff[IdxEffTotal]
376383 let oldChoice = getInteger(keyProposalChoiceByIdAndUser(proposalId, userAddressStr))
377384 let oldUserVotes = if (!(isDefined(oldChoice)))
378385 then 0
379386 else getIntOrElse(keyProposalVotesByIdAndUser(proposalId, userAddressStr), 0)
380387 let oldTotalByOldChoice = if (isDefined(oldChoice))
381388 then getIntOrElse(keyProposalVotesByIdAndOption(proposalId, value(oldChoice)), 0)
382389 else 0
383390 let oldTotalByNewChoice = getIntOrElse(keyProposalVotesByIdAndOption(proposalId, choice), 0)
384391 let oldTotal = parseIntValue(dynamicData[govStatusIdxTotalVotes])
385392 let newTotalByOldChoice = if (!(isDefined(oldChoice)))
386393 then 0
387394 else ((oldTotalByOldChoice - oldUserVotes) + (if ((value(oldChoice) == choice))
388395 then gnsbtAmt
389396 else 0))
390397 let newTotalByNewChoice = if (if (isDefined(oldChoice))
391398 then (value(oldChoice) == choice)
392399 else false)
393400 then newTotalByOldChoice
394401 else (oldTotalByNewChoice + gnsbtAmt)
395402 let newTotal = ((oldTotal - oldUserVotes) + gnsbtAmt)
396403 let isQuorumReached = (newTotal >= parseIntValue(propData[govIdxQuorum]))
397404 let numVotersByProposalId = getIntOrElse(keyNumUniqueVotersByProposalId(proposalId), 0)
398405 let oldAverUniqueVoters6 = getIntOrElse(keyStatsAverUniqueVoters(), 0)
399406 let numProposals = getIntegerValue(keyLastProposalId())
400407 let uniqueDiff = if ((oldUserVotes == 0))
401408 then 1
402409 else 0
403410 let newAverUniqueVoters6 = (oldAverUniqueVoters6 + fraction(uniqueDiff, MULT6, numProposals))
404411 let oldAverGnsbt = getIntOrElse(keyStatsAverGnsbtVoted(), 0)
405412 let newAverGnsbt = (oldAverGnsbt + ((gnsbtAmt - oldUserVotes) / numProposals))
406413 let isPrevOptional = if (!(isDefined(oldChoice)))
407414 then true
408415 else (value(oldChoice) == choice)
409416 let optionalTotalOld = if (isPrevOptional)
410417 then nil
411418 else [IntegerEntry(keyProposalVotesByIdAndOption(proposalId, value(oldChoice)), newTotalByOldChoice)]
412419 let winOpt = calcWinOption(proposalId, availableOptions, isPrevOptional, oldChoice, newTotalByOldChoice, choice, newTotalByNewChoice)
413420 $Tuple2(([IntegerEntry(keyProposalChoiceByIdAndUser(proposalId, userAddressStr), choice), IntegerEntry(keyProposalVotesByIdAndUser(proposalId, userAddressStr), gnsbtAmt), IntegerEntry(keyProposalVotesByIdAndOption(proposalId, choice), newTotalByNewChoice), IntegerEntry(keyNumUniqueVotersByProposalId(proposalId), (numVotersByProposalId + uniqueDiff)), IntegerEntry(keyStatsAverUniqueVoters(), newAverUniqueVoters6), IntegerEntry(keyStatsAverGnsbtVoted(), newAverGnsbt), StringEntry(keyProposalStatusDataById(proposalId), updateStatusData(dynamicData, isQuorumReached, winOpt, newTotal))] ++ optionalTotalOld), unit)
414421 }
415422 }
416423 }
417424 }
418425 }
419426
420427
421428
422429 @Callable(i)
423430 func initiateIdeaVoting (forumLink,title,votingStartTime,votingEndTime,optionsList) = if ((size(i.payments) != 1))
424431 then throw("Exactly one payment required")
425432 else if ((size(optionsList) != 2))
426433 then throw("Exactly 2 option ['NO', 'YES'] are expected")
427434 else if ((optionsList[0] != "NO"))
428435 then throw("Option NO should be the first")
429436 else if ((optionsList[1] != "YES"))
430437 then throw("Option YES should be the second")
431438 else initiateVoting(value(i.payments[0]), toBase58String(i.transactionId), "IDEA", toString(i.caller), forumLink, title, votingStartTime, votingEndTime, "PENDING", nil, optionsList)
432439
433440
434441
435442 @Callable(i)
436443 func initiateUpdateVoting (forumLink,title,votingStartTime,votingEndTime,txList) = if ((size(i.payments) != 1))
437444 then throw("Exactly one payment required")
438445 else if ((1 > size(txList)))
439446 then throw("Transactions list is empty")
440447 else if ((i.caller != this))
441448 then throw("not authorized")
442449 else initiateVoting(value(i.payments[0]), toBase58String(i.transactionId), "UPDATE", toString(i.caller), forumLink, title, votingStartTime, votingEndTime, "PENDING", txList, ["NO", "YES"])
443450
444451
445452
446453 @Callable(i)
447454 func cancelVoting (proposalId) = if ((i.caller != this))
448455 then throw("not authorized")
449456 else {
450457 let currentData = getStringOrFail(this, keyProposalStatusDataById(proposalId))
451458 let updatedData = ((take(currentData, value(lastIndexOf(currentData, SEP))) + SEP) + "true")
452459 $Tuple2([StringEntry(keyProposalStatusDataById(proposalId), updatedData)], unit)
453460 }
454461
455462
456463
457464 @Callable(i)
458465 func applyUpdate (proposalId) = {
459466 let propData = split(getStringOrFail(this, keyProposalDataById(proposalId)), SEP)
460467 let end = parseIntValue(propData[govIdxEnd])
461468 let now = lastBlock.timestamp
462469 if ((end > now))
463470 then throw("Voting is not finished yet")
464471 else if (("UPDATE" != propData[govIdxType]))
465472 then throw("Only UPDATE type can be applied")
466473 else {
467474 let dynamicData = split(getStringOrFail(this, keyProposalStatusDataById(proposalId)), SEP)
468475 if ((dynamicData[govStatusIdxIsCanceled] == "true"))
469476 then throw("Voting is canceled")
470477 else if ((dynamicData[govStatusIdxIsValid] != "true"))
471478 then throw("Voting status invalid")
472479 else if ((dynamicData[govStatusIdxWinOpt] != "1"))
473480 then throw("Winner is 'NO' - nothing to apply")
474481 else if ((dynamicData[govStatusIdxScApplied] == "true"))
475482 then throw("Scripts are already applied")
476483 else {
477484 let scriptTime = parseIntValue(dynamicData[govStatusIdxScTime])
478485 if (((now - PASTMARGIN) > scriptTime))
479486 then throw((("Scripts timestamp=" + toString(scriptTime)) + " is too far in the past, max 2 hrs allowed"))
480487 else if ((scriptTime > (now + FUTUREMARGIN)))
481488 then throw((("Scripts timestamp=" + toString(scriptTime)) + " is too far in the future, max 1.5 hrs allowed"))
482489 else {
483490 let inProgressId = getIntOrElse(keyApplyInProgress(), -1)
484491 if ((inProgressId != -1))
485492 then throw((("proposalId=" + toString(inProgressId)) + " is already being applied. Finish it first!"))
486493 else {
487494 let shutdown = invoke(controlContract, "callEmergencyShutdown", ["Applying Governance UPDATE"], nil)
488495 if ((shutdown == shutdown))
489496 then $Tuple2([IntegerEntry(keyApplyInProgress(), proposalId)], unit)
490497 else throw("Strict value is not equal to itself.")
491498 }
492499 }
493500 }
494501 }
495502 }
496503
497504
498505
499506 @Callable(i)
500507 func finishApply () = {
501508 let proposalId = valueOrErrorMessage(getInteger(keyApplyInProgress()), "No apply in progress, nothing to finish")
502509 let propData = split(getStringOrFail(this, keyProposalDataById(proposalId)), SEP)
503510 let txList = split(propData[govIdxTxIds], LISTSEP)
504511 let dynamicData = split(getStringOrFail(this, keyProposalStatusDataById(proposalId)), SEP)
505512 if ((dynamicData[govStatusIdxScApplied] == "true"))
506513 then throw("Scripts are already applied")
507514 else {
508515 func checker (acc,tx) = if (!(isDefined(transactionHeightById(fromBase58String(tx)))))
509516 then throw(("NOT applied txId: " + tx))
510517 else unit
511518
512519 let ignored = {
513520 let $l = txList
514521 let $s = size($l)
515522 let $acc0 = unit
516523 func $f0_1 ($a,$i) = if (($i >= $s))
517524 then $a
518525 else checker($a, $l[$i])
519526
520527 func $f0_2 ($a,$i) = if (($i >= $s))
521528 then $a
522529 else throw("List size exceeds 20")
523530
524531 $f0_2($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($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), 11), 12), 13), 14), 15), 16), 17), 18), 19), 20)
525532 }
526533 let version = (getIntOrElse(keyLastUpdateVersion(), 0) + 1)
527534 $Tuple2([DeleteEntry(keyApplyInProgress()), IntegerEntry(keyLastUpdateVersion(), version), StringEntry(keyProposalStatusDataById(proposalId), statusApplyScript(dynamicData)), ExecutionHistory(version, propData[govIdxTitle], propData[govIdxUrl])], ignored)
528535 }
529536 }
530537
531538

github/deemru/w8io/873ac7e 
67.20 ms