2022.10.27 15:42 [3356221] smart account 3PNikM6yp4NqcSU8guxQtmR5onr2D4e8yTJ > SELF 0.00000000 Waves

{ "type": 13, "id": "GP21jWTTDctgHXEzoHqyHExnGNtbBfiycDQ3FdnVgxjV", "fee": 2800000, "feeAssetId": null, "timestamp": 1666875107368, "version": 1, "sender": "3PNikM6yp4NqcSU8guxQtmR5onr2D4e8yTJ", "senderPublicKey": "6E2n1DAd4jnydbL7hMWLtjfW6VKrAGnFrNzfddAUnWq7", "proofs": [ "", "27RFPSkg8i9LHavrMNkodPn87986XqgMt6eH8ZL5qiHmVDai7MKP6MgUwSN65ATfqFkmxdcSzLJ9oCqE77ZqjLKw", "", "3yDoXtCycxmhznD8KCtQTVheik45HEtR1mxJnaKrcU6P2U7VATgtanUBncKnaiQzNf2LzY1vry6JnRZvw7pAhhKi" ], "script": "base64:BgI+CAISBQoDAQgIEgMKAQgSABIAEgMKAQESABIAEgASAwoBCBIECgIIARIAEgQKAggBEgASBAoCAQgSBAoCAQhoAAtyZXZpc2lvbk51bQIoYmExMmNlOTNkZjNiNGEwOTI1MzJhZGVkOTVlNTI4OGE0OTE5YzI0YQADU0VQAgJfXwAFTVVMVDYAwIQ9AAVNVUxUOACAwtcvAAZNVUxUWDYJALYCAQUFTVVMVDYABk1VTFRYOAkAtgIBBQVNVUxUOAAHTVVMVFgxOAkAtgIBAICAkLu61q3wDQAKV0FWRVNJRFNUUgIFV0FWRVMAB1dBVkVTSUQJANkEAQUKV0FWRVNJRFNUUgAJREFZTUlMTElTAIC4mSkAGUlkeENvbnRyb2xDZmdOZXV0cmlub0RhcHAAAQAYSWR4Q29udHJvbENmZ0F1Y3Rpb25EYXBwAAIAFElkeENvbnRyb2xDZmdScGREYXBwAAMAFUlkeENvbnRyb2xDZmdNYXRoRGFwcAAEABxJZHhDb250cm9sQ2ZnTGlxdWlkYXRpb25EYXBwAAUAFUlkeENvbnRyb2xDZmdSZXN0RGFwcAAGAB1JZHhDb250cm9sQ2ZnTm9kZVJlZ2lzdHJ5RGFwcAAHABxJZHhDb250cm9sQ2ZnTnNidFN0YWtpbmdEYXBwAAgAGUlkeENvbnRyb2xDZmdNZWRpYXRvckRhcHAACQAcSWR4Q29udHJvbENmZ1N1cmZTdGFraW5nRGFwcAAKACBJZHhDb250cm9sQ2ZnR25zYnRDb250cm9sbGVyRGFwcAALAQ9nZXRTdHJpbmdPckZhaWwCB2FkZHJlc3MDa2V5CQETdmFsdWVPckVycm9yTWVzc2FnZQIJAJ0IAgUHYWRkcmVzcwUDa2V5CQCsAgIJAKwCAgkArAICCQCsAgICCm1hbmRhdG9yeSAJAKUIAQUHYWRkcmVzcwIBLgUDa2V5Ag8gaXMgbm90IGRlZmluZWQBDGdldEludE9yRmFpbAEDa2V5CQETdmFsdWVPckVycm9yTWVzc2FnZQIJAJoIAgUEdGhpcwUDa2V5CQCsAgIJAKwCAgIPTWFuZGF0b3J5IHRoaXMuBQNrZXkCDyBpcyBub3QgZGVmaW5lZAEMZ2V0U3RyT3JFbHNlAgNrZXkKZGVmYXVsdFZhbAkBC3ZhbHVlT3JFbHNlAgkAnQgCBQR0aGlzBQNrZXkFCmRlZmF1bHRWYWwBEGtleU1pbkxvY2tBbW91bnQAAhElc19fbWluTG9ja0Ftb3VudAEQa2V5U3Rha2VkQXNzZXRJZAACESVzX19zdGFrZWRBc3NldElkARFrZXlDb250cm9sQWRkcmVzcwACHCVzJXNfX2NvbmZpZ19fY29udHJvbEFkZHJlc3MBDWtleUNvbnRyb2xDZmcAAhElc19fY29udHJvbENvbmZpZwEYa2V5U3VwcG9ydGVkUmV3YXJkQXNzZXRzAAIVc3VwcG9ydGVkUmV3YXJkQXNzZXRzARRyZWFkQ29udHJvbENmZ09yRmFpbAEHY29udHJvbAkAvAkCCQEPZ2V0U3RyaW5nT3JGYWlsAgUHY29udHJvbAkBDWtleUNvbnRyb2xDZmcABQNTRVABGGdldENvbnRyYWN0QWRkcmVzc09yRmFpbAIKY29udHJvbENmZwNpZHgJARN2YWx1ZU9yRXJyb3JNZXNzYWdlAgkApggBCQCRAwIFCmNvbnRyb2xDZmcFA2lkeAkArAICAi1Db250cm9sIGNmZyBkb2Vzbid0IGNvbnRhaW4gYWRkcmVzcyBhdCBpbmRleCAJAKQDAQUDaWR4AA9jb250cm9sQ29udHJhY3QJARFAZXh0ck5hdGl2ZSgxMDYyKQEJAQt2YWx1ZU9yRWxzZQIJAJ0IAgUEdGhpcwkBEWtleUNvbnRyb2xBZGRyZXNzAAIjM1A1QmZkNThQUGZOdkJNMkh5OFFmYmNEcU1lTnR6ZzdLZlAACmNvbnRyb2xDZmcJARRyZWFkQ29udHJvbENmZ09yRmFpbAEFD2NvbnRyb2xDb250cmFjdAAMbWF0aENvbnRyYWN0CQEYZ2V0Q29udHJhY3RBZGRyZXNzT3JGYWlsAgUKY29udHJvbENmZwUVSWR4Q29udHJvbENmZ01hdGhEYXBwABBuZXV0cmlub0NvbnRyYWN0CQEYZ2V0Q29udHJhY3RBZGRyZXNzT3JGYWlsAgUKY29udHJvbENmZwUZSWR4Q29udHJvbENmZ05ldXRyaW5vRGFwcAAPYXVjdGlvbkNvbnRyYWN0CQEYZ2V0Q29udHJhY3RBZGRyZXNzT3JGYWlsAgUKY29udHJvbENmZwUYSWR4Q29udHJvbENmZ0F1Y3Rpb25EYXBwABBzdGFrZWRBc3NldElkU3RyCQEPZ2V0U3RyaW5nT3JGYWlsAgUEdGhpcwkBEGtleVN0YWtlZEFzc2V0SWQAAA1zdGFrZWRBc3NldElkCQDZBAEFEHN0YWtlZEFzc2V0SWRTdHIADW1pbkxvY2tBbW91bnQJAQxnZXRJbnRPckZhaWwBCQEQa2V5TWluTG9ja0Ftb3VudAAAEnN1cHBvcnRlZEFzc2V0c1N0cgkBDGdldFN0ck9yRWxzZQIJARhrZXlTdXBwb3J0ZWRSZXdhcmRBc3NldHMAAgAAE3N1cHBvcnRlZEFzc2V0c0xpc3QJALUJAgUSc3VwcG9ydGVkQXNzZXRzU3RyAgFfARZrZXlMb2NrUGFyYW1Vc2VyQW1vdW50AQt1c2VyQWRkcmVzcwkAuQkCCQDMCAICBiVzJXMlcwkAzAgCAgtwYXJhbUJ5VXNlcgkAzAgCBQt1c2VyQWRkcmVzcwkAzAgCAgZhbW91bnQFA25pbAUDU0VQARZrZXlMb2NrUGFyYW1TdGFydEJsb2NrAQt1c2VyQWRkcmVzcwkAuQkCCQDMCAICBiVzJXMlcwkAzAgCAgtwYXJhbUJ5VXNlcgkAzAgCBQt1c2VyQWRkcmVzcwkAzAgCAgVzdGFydAUDbmlsBQNTRVABEGtleUhpc3RvcnlSZWNvcmQDBHR5cGULdXNlckFkZHJlc3MEdHhJZAkAuQkCCQDMCAICCCVzJXMlcyVzCQDMCAICB2hpc3RvcnkJAMwIAgUEdHlwZQkAzAgCBQt1c2VyQWRkcmVzcwkAzAgCCQDYBAEFBHR4SWQFA25pbAUDU0VQARdrZXlMb2NrUGFyYW1Ub3RhbEFtb3VudAAJALkJAgkAzAgCAgQlcyVzCQDMCAICBXN0YXRzCQDMCAICEWFjdGl2ZVRvdGFsTG9ja2VkBQNuaWwFA1NFUAESa2V5U3RhdHNMb2Nrc0NvdW50AAkAuQkCCQDMCAICBCVzJXMJAMwIAgIFc3RhdHMJAMwIAgIKbG9ja3NDb3VudAUDbmlsBQNTRVABEmtleVN0YXRzVXNlcnNDb3VudAAJALkJAgkAzAgCAgQlcyVzCQDMCAICBXN0YXRzCQDMCAICEGFjdGl2ZVVzZXJzQ291bnQFA25pbAUDU0VQARdrZXlTdGF0c0RlcG9zaXRBbXRCeURheQEJdGltZXN0YW1wCQC5CQIJAMwIAgIGJXMlcyVkCQDMCAICBXN0YXRzCQDMCAICD2RlcG9zaXRBbXRCeURheQkAzAgCCQCkAwEFCXRpbWVzdGFtcAUDbmlsBQNTRVABGGtleVN0YXRzRGVwb3NpdEFtdFRvdGFscwAJALkJAgkAzAgCAgYlcyVzJWQJAMwIAgIFc3RhdHMJAMwIAgIQZGVwb3NpdEFtdFRvdGFscwUDbmlsBQNTRVABDWtleU5leHRQZXJpb2QAAg4lc19fbmV4dFBlcmlvZAERa2V5RGVwb3NpdE51bUxhc3QACQC5CQIJAMwIAgIGJXMlcyVzCQDMCAICA2RlcAkAzAgCAgdsYXN0TnVtBQNuaWwFA1NFUAEba2V5VXNlclJld2FyZEZyb21EZXBvc2l0TnVtAQt1c2VyQWRkcmVzcwkAuQkCCQDMCAICBiVzJXMlcwkAzAgCAhF1c2VyUndkRnJvbURlcE51bQkAzAgCBQt1c2VyQWRkcmVzcwUDbmlsBQNTRVABFWtleVJld2FyZFBlck5zYnRTdW1BdAIKZGVwb3NpdE51bQN0a24JALkJAgkAzAgCAgQlcyVkCQDMCAICFXJ3ZFBlck5zYnRTdW1CeURlcE51bQkAzAgCCQCkAwEFCmRlcG9zaXROdW0JAMwIAgUDdGtuBQNuaWwFA1NFUAEJa2V5UmV3YXJkAgt1c2VyQWRkcmVzcwN0a24JALkJAgkAzAgCAgYlcyVzJXMJAMwIAgIDcndkCQDMCAIFC3VzZXJBZGRyZXNzCQDMCAIFA3RrbgUDbmlsBQNTRVABCmtleUNsYWltZWQCC3VzZXJBZGRyZXNzA3RrbgkAuQkCCQDMCAICBiVzJXMlcwkAzAgCAgNjbG0JAMwIAgULdXNlckFkZHJlc3MJAMwIAgUDdGtuBQNuaWwFA1NFUAEXa2V5Tm90RGlzdHJpYnV0ZWRSZXdhcmQBA3RrbgkAuQkCCQDMCAICBCVzJXMJAMwIAgIObm90RGlzdHJpYnV0ZWQJAMwIAgUDdGtuBQNuaWwFA1NFUAEUa2V5TGVnYWN5VXNlckJhbGFuY2UCCHVzZXJBZGRyA3RrbgkAuQkCCQDMCAICC3JwZF9iYWxhbmNlCQDMCAIFA3RrbgkAzAgCBQh1c2VyQWRkcgUDbmlsAgFfARVrZXlMZWdhY3lUb3RhbEJhbGFuY2UBA3RrbgkAuQkCCQDMCAICC3JwZF9iYWxhbmNlCQDMCAIFA3RrbgUDbmlsAgFfAQV0b1gxOAIHb3JpZ1ZhbAhvcmlnTXVsdAkAvAIDCQC2AgEFB29yaWdWYWwFB01VTFRYMTgFCG9yaWdNdWx0AQxnZXRJbnRPclplcm8BA2tleQkBC3ZhbHVlT3JFbHNlAgkAmggCBQR0aGlzBQNrZXkAAAEMZ2V0SW50T3JFbHNlAgNrZXkKZGVmYXVsdFZhbAkBC3ZhbHVlT3JFbHNlAgkAmggCBQR0aGlzBQNrZXkFCmRlZmF1bHRWYWwBD3RvQWRkcmVzc09yRmFpbAEKYWRkcmVzc1N0cgkBE3ZhbHVlT3JFcnJvck1lc3NhZ2UCCQCmCAEFCmFkZHJlc3NTdHIJAKwCAgIhY291bGRuJ3QgcGFyc2UgcGFzc2VkIGFkZHJlc3NTdHI9BQphZGRyZXNzU3RyAQt0b0Fzc2V0VmVjdAEIYXNzZXRTdHIDCQAAAgUIYXNzZXRTdHIFCldBVkVTSURTVFIFBHVuaXQJANkEAQUIYXNzZXRTdHIBBWFzSW50AQN2YWwEByRtYXRjaDAFA3ZhbAMJAAECBQckbWF0Y2gwAgNJbnQEBnZhbEludAUHJG1hdGNoMAUGdmFsSW50CQACAQIVZmFpbCB0byBjYXN0IGludG8gSW50ARJhc1N3YXBQYXJhbXNTVFJVQ1QBAXYEByRtYXRjaDAFAXYDCQABAgUHJG1hdGNoMAIjKEludCwgSW50LCBJbnQsIEludCwgSW50LCBJbnQsIEludCkEBnN0cnVjdAUHJG1hdGNoMAUGc3RydWN0CQACAQIVZmFpbCB0byBjYXN0IGludG8gSW50ARNmb3JtYXRIaXN0b3J5UmVjb3JkAwt1c2VyQWRkcmVzcwlvbGRBbW91bnQJbmV3QW1vdW50CQC5CQIJAMwIAgIKJXMlZCVkJWQlZAkAzAgCBQt1c2VyQWRkcmVzcwkAzAgCCQCkAwEIBQlsYXN0QmxvY2sGaGVpZ2h0CQDMCAIJAKQDAQgFCWxhc3RCbG9jawl0aW1lc3RhbXAJAMwIAgkApAMBBQlvbGRBbW91bnQJAMwIAgkApAMBBQluZXdBbW91bnQFA25pbAUDU0VQARhmb3JtYXRDbGFpbUhpc3RvcnlSZWNvcmQCC3VzZXJBZGRyZXNzDmNsYWltZWRSZXdhcmRzCQC5CQIJAMwIAgIIJXMlZCVkJXMJAMwIAgULdXNlckFkZHJlc3MJAMwIAgkApAMBCAUJbGFzdEJsb2NrBmhlaWdodAkAzAgCCQCkAwEIBQlsYXN0QmxvY2sJdGltZXN0YW1wCQDMCAIFDmNsYWltZWRSZXdhcmRzBQNuaWwFA1NFUAESSGlzdG9yeVJlY29yZEVudHJ5BQR0eXBlC3VzZXJBZGRyZXNzBHR4SWQJb2xkQW1vdW50CW5ld0Ftb3VudAkBC1N0cmluZ0VudHJ5AgkBEGtleUhpc3RvcnlSZWNvcmQDBQR0eXBlBQt1c2VyQWRkcmVzcwUEdHhJZAkBE2Zvcm1hdEhpc3RvcnlSZWNvcmQDBQt1c2VyQWRkcmVzcwUJb2xkQW1vdW50BQluZXdBbW91bnQBEUNsYWltSGlzdG9yeUVudHJ5Awt1c2VyQWRkcmVzcwR0eElkDmNsYWltZWRSZXdhcmRzCQELU3RyaW5nRW50cnkCCQEQa2V5SGlzdG9yeVJlY29yZAMCBWNsYWltBQt1c2VyQWRkcmVzcwUEdHhJZAkBGGZvcm1hdENsYWltSGlzdG9yeVJlY29yZAIFC3VzZXJBZGRyZXNzBQ5jbGFpbWVkUmV3YXJkcwELU3RhdHNSZXN1bHQEDnRvdGFsTG9ja2VkSW5jDGxvY2tDb3VudEluYw11c2Vyc0NvdW50SW5jC2lzTWlncmF0aW9uBApsb2Nrc0NvdW50CQEMZ2V0SW50T3JaZXJvAQkBEmtleVN0YXRzTG9ja3NDb3VudAAECnVzZXJzQ291bnQJAQxnZXRJbnRPclplcm8BCQESa2V5U3RhdHNVc2Vyc0NvdW50AAQLdG90YWxBbW91bnQJAQxnZXRJbnRPclplcm8BCQEXa2V5TG9ja1BhcmFtVG90YWxBbW91bnQABA50b3RhbEFtb3VudE5ldwkAZAIFC3RvdGFsQW1vdW50BQ50b3RhbExvY2tlZEluYwkAlQoDCQDOCAIJAMwIAgkBDEludGVnZXJFbnRyeQIJARJrZXlTdGF0c0xvY2tzQ291bnQACQBkAgUKbG9ja3NDb3VudAUMbG9ja0NvdW50SW5jCQDMCAIJAQxJbnRlZ2VyRW50cnkCCQESa2V5U3RhdHNVc2Vyc0NvdW50AAkAZAIFCnVzZXJzQ291bnQFDXVzZXJzQ291bnRJbmMJAMwIAgkBDEludGVnZXJFbnRyeQIJARdrZXlMb2NrUGFyYW1Ub3RhbEFtb3VudAAFDnRvdGFsQW1vdW50TmV3BQNuaWwDBQtpc01pZ3JhdGlvbgUDbmlsCQDMCAIJAQxJbnRlZ2VyRW50cnkCCQEVa2V5TGVnYWN5VG90YWxCYWxhbmNlAQUQc3Rha2VkQXNzZXRJZFN0cgUOdG90YWxBbW91bnROZXcFA25pbAULdG90YWxBbW91bnQFDnRvdGFsQW1vdW50TmV3AQ9Mb2NrUGFyYW1zRW50cnkEC3VzZXJBZGRyZXNzBmFtb3VudBJzdGFraW5nU3RhcnRIZWlnaHQLaXNNaWdyYXRpb24JAM4IAgkAzAgCCQEMSW50ZWdlckVudHJ5AgkBFmtleUxvY2tQYXJhbVVzZXJBbW91bnQBBQt1c2VyQWRkcmVzcwUGYW1vdW50CQDMCAIJAQxJbnRlZ2VyRW50cnkCCQEWa2V5TG9ja1BhcmFtU3RhcnRCbG9jawEFC3VzZXJBZGRyZXNzBRJzdGFraW5nU3RhcnRIZWlnaHQFA25pbAMFC2lzTWlncmF0aW9uBQNuaWwJAMwIAgkBDEludGVnZXJFbnRyeQIJARRrZXlMZWdhY3lVc2VyQmFsYW5jZQIFC3VzZXJBZGRyZXNzBRBzdGFrZWRBc3NldElkU3RyBQZhbW91bnQFA25pbAEPZ2V0UGFyYW1zT3JGYWlsAAkAlAoCCQDZBAEJAQ9nZXRTdHJpbmdPckZhaWwCBQR0aGlzCQEQa2V5U3Rha2VkQXNzZXRJZAAJAQxnZXRJbnRPckZhaWwBCQEQa2V5TWluTG9ja0Ftb3VudAABDGlzQWN0aXZlVXNlcgELdXNlckFkZHJlc3MJAGYCCQEMZ2V0SW50T3JFbHNlAgkBFmtleUxvY2tQYXJhbVVzZXJBbW91bnQBBQt1c2VyQWRkcmVzcwAAAAABE2dldFVzZXJQYXJhbXNPclVuaXQBC3VzZXJBZGRyZXNzAwkBDGlzQWN0aXZlVXNlcgEFC3VzZXJBZGRyZXNzCQCVCgMHCQEMZ2V0SW50T3JGYWlsAQkBFmtleUxvY2tQYXJhbVVzZXJBbW91bnQBBQt1c2VyQWRkcmVzcwkBDGdldEludE9yRmFpbAEJARZrZXlMb2NrUGFyYW1TdGFydEJsb2NrAQULdXNlckFkZHJlc3MFBHVuaXQBE2dldFVzZXJQYXJhbXNPckZhaWwBC3VzZXJBZGRyZXNzCQETdmFsdWVPckVycm9yTWVzc2FnZQIJARNnZXRVc2VyUGFyYW1zT3JVbml0AQULdXNlckFkZHJlc3MJAKwCAgkArAICAgVVc2VyIAULdXNlckFkZHJlc3MCDyBpcyBub3QgZGVmaW5lZAEKY2FsY1Jld2FyZAULdXNlckFkZHJlc3MHYXNzZXRJZA1zdGFrZWRBbW91bnRYDmRlcG9zaXROdW1Vc2VyDmRlcG9zaXROdW1MYXN0BBdyZXdhcmRQZXJOc2J0U3VtTGFzdEtFWQkBFWtleVJld2FyZFBlck5zYnRTdW1BdAIFDmRlcG9zaXROdW1MYXN0BQdhc3NldElkBApzdW1MYXN0WDE4CQCnAwEJAQxnZXRTdHJPckVsc2UCCQEVa2V5UmV3YXJkUGVyTnNidFN1bUF0AgUOZGVwb3NpdE51bUxhc3QFB2Fzc2V0SWQCATAECnN1bVVzZXJYMTgJAKcDAQkBDGdldFN0ck9yRWxzZQIJARVrZXlSZXdhcmRQZXJOc2J0U3VtQXQCBQ5kZXBvc2l0TnVtVXNlcgUHYXNzZXRJZAIBMAQRcmV3YXJkRHluYW1pY1BhcnQJAKADAQkAvAIDCQC4AgIFCnN1bUxhc3RYMTgFCnN1bVVzZXJYMTgFDXN0YWtlZEFtb3VudFgFB01VTFRYMTgEE3Jld2FyZENhY2hlZFBhcnRLRVkJAQlrZXlSZXdhcmQCBQt1c2VyQWRkcmVzcwUHYXNzZXRJZAQQcmV3YXJkQ2FjaGVkUGFydAkBDGdldEludE9yRWxzZQIFE3Jld2FyZENhY2hlZFBhcnRLRVkAAAkAlgoECQBkAgUQcmV3YXJkQ2FjaGVkUGFydAURcmV3YXJkRHluYW1pY1BhcnQFEHJld2FyZENhY2hlZFBhcnQFEXJld2FyZER5bmFtaWNQYXJ0BRNyZXdhcmRDYWNoZWRQYXJ0S0VZAQx0b1N0YXJ0T2ZEYXkBCXRpbWVzdGFtcAkAaAIJAGkCBQl0aW1lc3RhbXAFCURBWU1JTExJUwUJREFZTUlMTElTARNmaW5kRWxlbWVudFBvc2l0aW9uAwNzcmMHZWxlbWVudANzZXAEDGVsZW1lbnRTdGFydAkBE3ZhbHVlT3JFcnJvck1lc3NhZ2UCCQCzCQIFA3NyYwUHZWxlbWVudAkArAICCQCsAgIJAKwCAgIWdGhlcmUgaXMgbm8gc3Vic3RyaW5nIAUHZWxlbWVudAIEIGluIAUDc3JjAwkAAAIFDGVsZW1lbnRTdGFydAAAAAAEBGxlZnQJAK8CAgUDc3JjBQxlbGVtZW50U3RhcnQJAGUCCQCQAwEJALUJAgUEbGVmdAUDc2VwAAEAE0RlcG9zaXRUb3RhbHNQUkVGSVgCBCVkJWQBE3VwZGF0ZURlcG9zaXRUb3RhbHMDB2N1cnJWYWwLaWR4VG9VcGRhdGUIZGVsdGFBbXQEB2N1cnJBcnIJALUJAgUHY3VyclZhbAUDU0VQCgEOdXBkRGVwVG90QnlJZHgBA2lkeAMJAQIhPQIFA2lkeAULaWR4VG9VcGRhdGUJAJEDAgUHY3VyckFycgUDaWR4CQCkAwEJAGQCCQENcGFyc2VJbnRWYWx1ZQEJAJEDAgUHY3VyckFycgUDaWR4BQhkZWx0YUFtdAkAuQkCCQDMCAIFE0RlcG9zaXRUb3RhbHNQUkVGSVgJAMwIAgkBDnVwZERlcFRvdEJ5SWR4AQABCQDMCAIJAQ51cGREZXBUb3RCeUlkeAEAAgUDbmlsBQNTRVABFURlcG9zaXRzVG90YWxzRW50cmllcwINZGVwb3NpdEFtb3VudAphc3NldElkU3RyBApzdGFydE9mRGF5CQEMdG9TdGFydE9mRGF5AQgFCWxhc3RCbG9jawl0aW1lc3RhbXAECGJ5RGF5S0VZCQEXa2V5U3RhdHNEZXBvc2l0QW10QnlEYXkBBQpzdGFydE9mRGF5BAl0b3RhbHNLRVkJARhrZXlTdGF0c0RlcG9zaXRBbXRUb3RhbHMABAhwb3NpdGlvbgkBE2ZpbmRFbGVtZW50UG9zaXRpb24DBRJzdXBwb3J0ZWRBc3NldHNTdHIFCmFzc2V0SWRTdHICAV8EC2RlZmF1bHREQVRBCQCsAgIFE0RlcG9zaXRUb3RhbHNQUkVGSVgCBl9fMF9fMAQOY3VyclRvdGFsc0RBVEEJAQt2YWx1ZU9yRWxzZQIJAJ0IAgUEdGhpcwUJdG90YWxzS0VZBQtkZWZhdWx0REFUQQQNbmV3VG90YWxzREFUQQkBE3VwZGF0ZURlcG9zaXRUb3RhbHMDBQ5jdXJyVG90YWxzREFUQQkAZAIFCHBvc2l0aW9uAAEFDWRlcG9zaXRBbW91bnQJAMwIAgkBC1N0cmluZ0VudHJ5AgUJdG90YWxzS0VZBQ1uZXdUb3RhbHNEQVRBCQDMCAIJAQtTdHJpbmdFbnRyeQIFCGJ5RGF5S0VZBQ1uZXdUb3RhbHNEQVRBBQNuaWwBDVJld2FyZEVudHJpZXMDCWlzTmV3VXNlcgt1c2VyQWRkcmVzcwxzdGFrZWRBbW91bnQEDXN0YWtlZEFtb3VudFgJALYCAQUMc3Rha2VkQW1vdW50BBt1c2VyUmV3YXJkRnJvbURlcG9zaXROdW1LRVkJARtrZXlVc2VyUmV3YXJkRnJvbURlcG9zaXROdW0BBQt1c2VyQWRkcmVzcwQOZGVwb3NpdE51bVVzZXIJAQxnZXRJbnRPckVsc2UCBRt1c2VyUmV3YXJkRnJvbURlcG9zaXROdW1LRVkA////////////AQQOZGVwb3NpdE51bUxhc3QJAQxnZXRJbnRPckVsc2UCCQERa2V5RGVwb3NpdE51bUxhc3QAAP///////////wEKARtmb3JFYWNoQXNzZXRDYWNoZVVzZXJSZXdhcmQCBWFjY3VtBWFzc2V0BA0kdDAxMDc2NTEwOTAwCQEKY2FsY1Jld2FyZAUFC3VzZXJBZGRyZXNzBQVhc3NldAUNc3Rha2VkQW1vdW50WAUOZGVwb3NpdE51bVVzZXIFDmRlcG9zaXROdW1MYXN0BAtyZXdhcmRUb3RhbAgFDSR0MDEwNzY1MTA5MDACXzEEBmNhY2hlZAgFDSR0MDEwNzY1MTA5MDACXzIEB2R5bmFtaWMIBQ0kdDAxMDc2NTEwOTAwAl8zBBNyZXdhcmRDYWNoZWRQYXJ0S0VZCAUNJHQwMTA3NjUxMDkwMAJfNAkAzQgCBQVhY2N1bQkBDEludGVnZXJFbnRyeQIFE3Jld2FyZENhY2hlZFBhcnRLRVkFC3Jld2FyZFRvdGFsAwMJAAACBQ5kZXBvc2l0TnVtTGFzdAD///////////8BCQAAAgUOZGVwb3NpdE51bVVzZXIA////////////AQcFA25pbAMDCQAAAgUOZGVwb3NpdE51bUxhc3QA////////////AQkAZgIFDmRlcG9zaXROdW1Vc2VyAP///////////wEHCQACAQIvaW52YWxpZCBkZXBvc2l0TnVtTGFzdCBhbmQgZGVwb3NpdE51bVVzZXIgc3RhdGUDAwkAZgIFDmRlcG9zaXROdW1MYXN0AP///////////wEJAGcCBQ5kZXBvc2l0TnVtVXNlcgD///////////8BBwMFCWlzTmV3VXNlcgkAzAgCCQEMSW50ZWdlckVudHJ5AgUbdXNlclJld2FyZEZyb21EZXBvc2l0TnVtS0VZBQ5kZXBvc2l0TnVtTGFzdAUDbmlsCQDNCAIKAAIkbAUTc3VwcG9ydGVkQXNzZXRzTGlzdAoAAiRzCQCQAwEFAiRsCgAFJGFjYzAFA25pbAoBBSRmMF8xAgIkYQIkaQMJAGcCBQIkaQUCJHMFAiRhCQEbZm9yRWFjaEFzc2V0Q2FjaGVVc2VyUmV3YXJkAgUCJGEJAJEDAgUCJGwFAiRpCgEFJGYwXzICAiRhAiRpAwkAZwIFAiRpBQIkcwUCJGEJAAIBAhNMaXN0IHNpemUgZXhjZWVkcyAyCQEFJGYwXzICCQEFJGYwXzECCQEFJGYwXzECBQUkYWNjMAAAAAEAAgkBDEludGVnZXJFbnRyeQIFG3VzZXJSZXdhcmRGcm9tRGVwb3NpdE51bUtFWQUOZGVwb3NpdE51bUxhc3QJAAIBCQCsAgIJAKwCAgkArAICAiR1bmNvdmVyZWQgY29uZGl0aW9uOiBkZXBvc2l0TnVtTGFzdD0JAKQDAQUOZGVwb3NpdE51bUxhc3QCECBkZXBvc2l0TnVtVXNlcj0JAKQDAQUOZGVwb3NpdE51bVVzZXIBIkluY3JlbWVudE5vdERpc3RyaWJ1dGVkUmV3YXJkRW50cnkCA3RrbglhbW91bnRJbmMEF25vdERpc3RyaWJ1dGVkUmV3YXJkS0VZCQEXa2V5Tm90RGlzdHJpYnV0ZWRSZXdhcmQBBQN0a24EFG5vdERpc3RyaWJ1dGVkUmV3YXJkCQEMZ2V0SW50T3JFbHNlAgUXbm90RGlzdHJpYnV0ZWRSZXdhcmRLRVkAAAkAzAgCCQEMSW50ZWdlckVudHJ5AgUXbm90RGlzdHJpYnV0ZWRSZXdhcmRLRVkJAGQCBRRub3REaXN0cmlidXRlZFJld2FyZAUJYW1vdW50SW5jBQNuaWwBCm1lcmdlU3Rha2UCC3VzZXJBZGRyZXNzC2Ftb3VudFRvQWRkBA0kdDAxMzcyNDEzODQwCQELdmFsdWVPckVsc2UCCQETZ2V0VXNlclBhcmFtc09yVW5pdAEFC3VzZXJBZGRyZXNzCQCVCgMGAAAFBmhlaWdodAQJaXNOZXdVc2VyCAUNJHQwMTM3MjQxMzg0MAJfMQQMc3Rha2VkQW1vdW50CAUNJHQwMTM3MjQxMzg0MAJfMgQSc3Rha2luZ1N0YXJ0SGVpZ2h0CAUNJHQwMTM3MjQxMzg0MAJfMwQPc3Rha2VkQW1vdW50TkVXAwUJaXNOZXdVc2VyBQthbW91bnRUb0FkZAkAZAIFC2Ftb3VudFRvQWRkBQxzdGFrZWRBbW91bnQJAJYKBAUJaXNOZXdVc2VyBQxzdGFrZWRBbW91bnQFEnN0YWtpbmdTdGFydEhlaWdodAUPc3Rha2VkQW1vdW50TkVXARppc1VzZG5TdGFraW5nTWlncmF0aW9uRG9uZQAEEmxlZ2FjeVRvdGFsQmFsYW5jZQkBDGdldEludE9yRWxzZQIJARVrZXlMZWdhY3lUb3RhbEJhbGFuY2UBBRBzdGFrZWRBc3NldElkU3RyAAAEDHRvdGFsQmFsYW5jZQkBDGdldEludE9yRWxzZQIJARdrZXlMb2NrUGFyYW1Ub3RhbEFtb3VudAAAAAkAAAIFEmxlZ2FjeVRvdGFsQmFsYW5jZQUMdG90YWxCYWxhbmNlARpmYWlsSWZVc2RuTWlncmF0aW9uTm90RG9uZQADCQEaaXNVc2RuU3Rha2luZ01pZ3JhdGlvbkRvbmUABgkAAgECTlVTRE4gc3Rha2luZyBtaWdyYXRpb24gaXMgSU4gUFJPR1JFU1MuIEFsbCBvcGVyYXRpb25zIGFyZSB0ZW1wb3Jhcnkgc3VzcGVuZGVkLgELY29tbW9uU3Rha2UDC3VzZXJBZGRyZXNzAWkLaXNNaWdyYXRpb24ECG1pZ0NoZWNrAwkBASEBBQtpc01pZ3JhdGlvbgkBGmZhaWxJZlVzZG5NaWdyYXRpb25Ob3REb25lAAYDCQAAAgUIbWlnQ2hlY2sFCG1pZ0NoZWNrAwkBAiE9AgkAkAMBCAUBaQhwYXltZW50cwABCQACAQIVSW52YWxpZCBwYXltZW50cyBzaXplBAdwYXltZW50CQCRAwIIBQFpCHBheW1lbnRzAAAEBmFtb3VudAgFB3BheW1lbnQGYW1vdW50BBNpbnZhbGlkQXNzZXRNZXNzYWdlCQCsAgIJAKwCAgIPSW52YWxpZCBhc3NldC4gCQDYBAEFDXN0YWtlZEFzc2V0SWQCDCBpcyBleHBlY3RlZAQHYXNzZXRJZAkBE3ZhbHVlT3JFcnJvck1lc3NhZ2UCCAUHcGF5bWVudAdhc3NldElkBRNpbnZhbGlkQXNzZXRNZXNzYWdlAwkBAiE9AgUHYXNzZXRJZAUNc3Rha2VkQXNzZXRJZAkAAgEFE2ludmFsaWRBc3NldE1lc3NhZ2UEDnVzZXJBZGRyZXNzU3RyCQClCAEFC3VzZXJBZGRyZXNzBAptZXJnZWREYXRhCQEKbWVyZ2VTdGFrZQIFDnVzZXJBZGRyZXNzU3RyBQZhbW91bnQECWlzTmV3VXNlcggFCm1lcmdlZERhdGECXzEEDHN0YWtlZEFtb3VudAgFCm1lcmdlZERhdGECXzIEEnN0YWtpbmdTdGFydEhlaWdodAgFCm1lcmdlZERhdGECXzMED3N0YWtlZEFtb3VudE5FVwgFCm1lcmdlZERhdGECXzQDCQBmAgUNbWluTG9ja0Ftb3VudAUPc3Rha2VkQW1vdW50TkVXCQACAQkArAICAhNNaW4gbG9jayBhbW91bnQgaXMgCQCkAwEFDW1pbkxvY2tBbW91bnQEDSR0MDE1MzIzMTU0MzgJAQtTdGF0c1Jlc3VsdAQFBmFtb3VudAABAwUJaXNOZXdVc2VyAAEAAAULaXNNaWdyYXRpb24EDHN0YXRzRW50cmllcwgFDSR0MDE1MzIzMTU0MzgCXzEEC3RvdGFsU3Rha2VkCAUNJHQwMTUzMjMxNTQzOAJfMgQOdG90YWxTdGFrZWROZXcIBQ0kdDAxNTMyMzE1NDM4Al8zCQDOCAIJAM4IAgkAzggCCQDMCAIJARJIaXN0b3J5UmVjb3JkRW50cnkFAgVzdGFrZQUOdXNlckFkZHJlc3NTdHIIBQFpDXRyYW5zYWN0aW9uSWQFDHN0YWtlZEFtb3VudAUPc3Rha2VkQW1vdW50TkVXBQNuaWwJAQ1SZXdhcmRFbnRyaWVzAwUJaXNOZXdVc2VyBQ51c2VyQWRkcmVzc1N0cgUMc3Rha2VkQW1vdW50CQEPTG9ja1BhcmFtc0VudHJ5BAUOdXNlckFkZHJlc3NTdHIFD3N0YWtlZEFtb3VudE5FVwUSc3Rha2luZ1N0YXJ0SGVpZ2h0BQtpc01pZ3JhdGlvbgUMc3RhdHNFbnRyaWVzCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuAQ1jb21tb25VbnN0YWtlAwZhbW91bnQBaQtpc01pZ3JhdGlvbgQObWlncmF0aW9uQ2hlY2sJARpmYWlsSWZVc2RuTWlncmF0aW9uTm90RG9uZQADCQAAAgUObWlncmF0aW9uQ2hlY2sFDm1pZ3JhdGlvbkNoZWNrAwkBAiE9AgkAkAMBCAUBaQhwYXltZW50cwAACQACAQIjdW5zdGFrZSBkb2Vzbid0IHJlcXVpcmUgYW55IHBheW1lbnQEC3VzZXJBZGRyZXNzCAUBaQZjYWxsZXIEDnVzZXJBZGRyZXNzU3RyCQClCAEFC3VzZXJBZGRyZXNzBA0kdDAxNjAwMzE2MDkxCQETZ2V0VXNlclBhcmFtc09yRmFpbAEFDnVzZXJBZGRyZXNzU3RyBAlpc05ld1VzZXIIBQ0kdDAxNjAwMzE2MDkxAl8xBAxzdGFrZWRBbW91bnQIBQ0kdDAxNjAwMzE2MDkxAl8yBBJzdGFraW5nU3RhcnRIZWlnaHQIBQ0kdDAxNjAwMzE2MDkxAl8zBBBzd2FwUGFyYW1zU1RSVUNUCQESYXNTd2FwUGFyYW1zU1RSVUNUAQkA/QcEBRBuZXV0cmlub0NvbnRyYWN0Ahtzd2FwUGFyYW1zQnlVc2VyU1lTUkVBRE9OTFkJAMwIAgUOdXNlckFkZHJlc3NTdHIJAMwIAgAABQNuaWwFA25pbAQUc3dhcExpbWl0U3BlbnRJblVzZG4IBRBzd2FwUGFyYW1zU1RSVUNUAl8yBA5ibGNrczJMbXRSZXNldAgFEHN3YXBQYXJhbXNTVFJVQ1QCXzMDCQBmAgUUc3dhcExpbWl0U3BlbnRJblVzZG4AAAkAAgEJAKwCAgkArAICAi1Zb3UgaGF2ZSBhbHJlYWR5IG1hZGUgYSBzd2FwIG9wZXJhdGlvbi4gV2FpdCAJAKQDAQkAZAIFBmhlaWdodAUOYmxja3MyTG10UmVzZXQCEiBoZWlnaHQgdG8gdW5zdGFrZQMJAGcCAAAFDHN0YWtlZEFtb3VudAkAAgECEk5vdGhpbmcgdG8gdW5zdGFrZQMJAGYCBQZhbW91bnQFDHN0YWtlZEFtb3VudAkAAgEJAKwCAgkArAICCQCsAgICClJlcXVlc3RlZCAJAKQDAQUGYW1vdW50AhIsIGJ1dCBzdGFrZWQgb25seSAJAKQDAQUMc3Rha2VkQW1vdW50BA9zdGFrZWRBbW91bnRORVcJAGUCBQxzdGFrZWRBbW91bnQFBmFtb3VudAQNJHQwMTY3MjkxNjkwMAkBC1N0YXRzUmVzdWx0BAkBAS0BBQZhbW91bnQDCQAAAgUGYW1vdW50BQxzdGFrZWRBbW91bnQA////////////AQAAAwkAAAIFBmFtb3VudAUMc3Rha2VkQW1vdW50AP///////////wEAAAULaXNNaWdyYXRpb24EDHN0YXRzRW50cmllcwgFDSR0MDE2NzI5MTY5MDACXzEEC3RvdGFsU3Rha2VkCAUNJHQwMTY3MjkxNjkwMAJfMgQOdG90YWxTdGFrZWROZXcIBQ0kdDAxNjcyOTE2OTAwAl8zCQDOCAIJAM4IAgkAzggCCQDMCAIJAQ5TY3JpcHRUcmFuc2ZlcgMFC3VzZXJBZGRyZXNzBQZhbW91bnQFDXN0YWtlZEFzc2V0SWQJAMwIAgkBEkhpc3RvcnlSZWNvcmRFbnRyeQUCB3Vuc3Rha2UFDnVzZXJBZGRyZXNzU3RyCAUBaQ10cmFuc2FjdGlvbklkBQxzdGFrZWRBbW91bnQFD3N0YWtlZEFtb3VudE5FVwUDbmlsCQENUmV3YXJkRW50cmllcwMHBQ51c2VyQWRkcmVzc1N0cgUMc3Rha2VkQW1vdW50CQEPTG9ja1BhcmFtc0VudHJ5BAUOdXNlckFkZHJlc3NTdHIFD3N0YWtlZEFtb3VudE5FVwUSc3Rha2luZ1N0YXJ0SGVpZ2h0BQtpc01pZ3JhdGlvbgUMc3RhdHNFbnRyaWVzCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuAQtjb21tb25DbGFpbQILdXNlckFkZHJlc3MBaQQObWlncmF0aW9uQ2hlY2sJARpmYWlsSWZVc2RuTWlncmF0aW9uTm90RG9uZQADCQAAAgUObWlncmF0aW9uQ2hlY2sFDm1pZ3JhdGlvbkNoZWNrBA51c2VyQWRkcmVzc1N0cgkApQgBBQt1c2VyQWRkcmVzcwMJAGYCCQCQAwEIBQFpCHBheW1lbnRzAAAJAAIBAhlwYXltZW50cyBhcmUgbm90IGFjY2VwdGVkBA0kdDAxNzQ2NDE3NTcyCQELdmFsdWVPckVsc2UCCQETZ2V0VXNlclBhcmFtc09yVW5pdAEFDnVzZXJBZGRyZXNzU3RyCQCVCgMGAAAAAAQJaXNOZXdVc2VyCAUNJHQwMTc0NjQxNzU3MgJfMQQMc3Rha2VkQW1vdW50CAUNJHQwMTc0NjQxNzU3MgJfMgQMc3Rha2luZ1N0YXJ0CAUNJHQwMTc0NjQxNzU3MgJfMwQNc3Rha2VkQW1vdW50WAkAtgIBBQxzdGFrZWRBbW91bnQEG3VzZXJSZXdhcmRGcm9tRGVwb3NpdE51bUtFWQkBG2tleVVzZXJSZXdhcmRGcm9tRGVwb3NpdE51bQEFDnVzZXJBZGRyZXNzU3RyBA5kZXBvc2l0TnVtVXNlcgkBDGdldEludE9yRWxzZQIFG3VzZXJSZXdhcmRGcm9tRGVwb3NpdE51bUtFWQD///////////8BBA5kZXBvc2l0TnVtTGFzdAkBDGdldEludE9yRWxzZQIJARFrZXlEZXBvc2l0TnVtTGFzdAAA////////////AQoBH2ZvckVhY2hBc3NldENhbGNVbmNsYWltZWRSZXdhcmQCBWFjY3VtBWFzc2V0BA0kdDAxNzk0MzE4MDgxCQEKY2FsY1Jld2FyZAUFDnVzZXJBZGRyZXNzU3RyBQVhc3NldAUNc3Rha2VkQW1vdW50WAUOZGVwb3NpdE51bVVzZXIFDmRlcG9zaXROdW1MYXN0BAtyZXdhcmRUb3RhbAgFDSR0MDE3OTQzMTgwODECXzEEBmNhY2hlZAgFDSR0MDE3OTQzMTgwODECXzIEB2R5bmFtaWMIBQ0kdDAxNzk0MzE4MDgxAl8zBBNyZXdhcmRDYWNoZWRQYXJ0S0VZCAUNJHQwMTc5NDMxODA4MQJfNAQKY2xhaW1lZEtFWQkBCmtleUNsYWltZWQCBQ51c2VyQWRkcmVzc1N0cgUFYXNzZXQEDSR0MDE4MTQxMTgxNzgFBWFjY3VtBARkYXRhCAUNJHQwMTgxNDExODE3OAJfMQQRY2xhaW1lZEFtdEJ5QXNzZXQIBQ0kdDAxODE0MTE4MTc4Al8yBAduZXdQYXJ0CQC5CQIJAMwIAgUFYXNzZXQJAMwIAgkApAMBBQtyZXdhcmRUb3RhbAUDbmlsAgE6BBRjbGFpbWVkQW10QnlBc3NldE5ldwkAuQkCCQDMCAIFEWNsYWltZWRBbXRCeUFzc2V0CQDMCAIFB25ld1BhcnQFA25pbAIBXwMJAGcCAAAFC3Jld2FyZFRvdGFsCQCUCgIFBGRhdGEFFGNsYWltZWRBbXRCeUFzc2V0TmV3CQCUCgIJAM0IAgkAzQgCCQDNCAIFBGRhdGEJAQ5TY3JpcHRUcmFuc2ZlcgMFC3VzZXJBZGRyZXNzBQtyZXdhcmRUb3RhbAkBC3RvQXNzZXRWZWN0AQUFYXNzZXQJAQxJbnRlZ2VyRW50cnkCBQpjbGFpbWVkS0VZCQBkAgkBC3ZhbHVlT3JFbHNlAgkAnwgBBQpjbGFpbWVkS0VZAAAFC3Jld2FyZFRvdGFsCQEMSW50ZWdlckVudHJ5AgUTcmV3YXJkQ2FjaGVkUGFydEtFWQAABRRjbGFpbWVkQW10QnlBc3NldE5ldwQNJHQwMTg2MzgxODc1MQoAAiRsBRNzdXBwb3J0ZWRBc3NldHNMaXN0CgACJHMJAJADAQUCJGwKAAUkYWNjMAkAlAoCBQNuaWwCAAoBBSRmMF8xAgIkYQIkaQMJAGcCBQIkaQUCJHMFAiRhCQEfZm9yRWFjaEFzc2V0Q2FsY1VuY2xhaW1lZFJld2FyZAIFAiRhCQCRAwIFAiRsBQIkaQoBBSRmMF8yAgIkYQIkaQMJAGcCBQIkaQUCJHMFAiRhCQACAQITTGlzdCBzaXplIGV4Y2VlZHMgMgkBBSRmMF8yAgkBBSRmMF8xAgkBBSRmMF8xAgUFJGFjYzAAAAABAAIECXRyYW5zZmVycwgFDSR0MDE4NjM4MTg3NTECXzEEF2NsYWltZWRBbXRCeUFzc2V0UmVzdWx0CAUNJHQwMTg2MzgxODc1MQJfMgMJAGcCAAAJAJADAQUJdHJhbnNmZXJzCQCUCgIFA25pbAAACQCUCgIJAM0IAgkAzQgCBQl0cmFuc2ZlcnMJAQxJbnRlZ2VyRW50cnkCBRt1c2VyUmV3YXJkRnJvbURlcG9zaXROdW1LRVkFDmRlcG9zaXROdW1MYXN0CQERQ2xhaW1IaXN0b3J5RW50cnkDBQ51c2VyQWRkcmVzc1N0cggFAWkNdHJhbnNhY3Rpb25JZAkAsAICBRdjbGFpbWVkQW10QnlBc3NldFJlc3VsdAABCQCQAwEFCXRyYW5zZmVycwkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgAIVVNETlRZUEUCBFVTRE4ACE5TQlRUWVBFAgROU0JUABJOZXV0cmlub0Fzc2V0SWRLZXkCEW5ldXRyaW5vX2Fzc2V0X2lkABNOZXV0cmlub0NvbnRyYWN0S2V5AhFuZXV0cmlub19jb250cmFjdAAOTnNidEFzc2V0SWRLZXkCDWJvbmRfYXNzZXRfaWQACkJhbGFuY2VLZXkCC3JwZF9iYWxhbmNlAA9uZXV0cmlub0Fzc2V0SWQJANkEAQkBD2dldFN0cmluZ09yRmFpbAIFEG5ldXRyaW5vQ29udHJhY3QFEk5ldXRyaW5vQXNzZXRJZEtleQAObnNidEFzc2V0SWRTdHIJAQ9nZXRTdHJpbmdPckZhaWwCBRBuZXV0cmlub0NvbnRyYWN0BQ5Oc2J0QXNzZXRJZEtleQALbnNidEFzc2V0SWQJANkEAQUObnNidEFzc2V0SWRTdHIBEWdldFVzZXJCYWxhbmNlS2V5AgVvd25lcgdhc3NldElkCQC5CQIJAMwIAgUKQmFsYW5jZUtleQkAzAgCBQdhc3NldElkCQDMCAIFBW93bmVyBQNuaWwCAV8BFWdldENvbnRyYWN0QmFsYW5jZUtleQEHYXNzZXRJZAkArAICCQCsAgIFCkJhbGFuY2VLZXkCAV8FB2Fzc2V0SWQBEmdldENvbnRyYWN0QmFsYW5jZQEHYXNzZXRJZAkBDGdldEludE9yRWxzZQIJARVnZXRDb250cmFjdEJhbGFuY2VLZXkBBQdhc3NldElkAAABDmdldFVzZXJCYWxhbmNlAgVvd25lcgdhc3NldElkCQEMZ2V0SW50T3JFbHNlAgkBEWdldFVzZXJCYWxhbmNlS2V5AgUFb3duZXIFB2Fzc2V0SWQAAAEaZ2V0VmFsaWRTdGFraW5nQXNzZXRPckZhaWwCC3N0YWtpbmdUeXBlB2Fzc2V0SWQDAwkAAAIFC3N0YWtpbmdUeXBlBQhVU0ROVFlQRQkBAiE9AgUHYXNzZXRJZAUPbmV1dHJpbm9Bc3NldElkBwkAAgECEWNhbiB1c2UgVVNETiBvbmx5AwMJAAACBQtzdGFraW5nVHlwZQUITlNCVFRZUEUJAQIhPQIFB2Fzc2V0SWQFC25zYnRBc3NldElkBwkAAgECEWNhbiB1c2UgTlNCVCBvbmx5AwMJAQIhPQIFC3N0YWtpbmdUeXBlBQhVU0ROVFlQRQkBAiE9AgULc3Rha2luZ1R5cGUFCE5TQlRUWVBFBwkAAgEJAKwCAgIZdW5zdXBwb3J0ZWQgc3Rha2luZyB0eXBlIAULc3Rha2luZ1R5cGUFB2Fzc2V0SWQBDmludGVybmFsVW5sb2NrBAtzdGFraW5nVHlwZQFpDHVubG9ja0Ftb3VudAxhc3NldElkUGFyYW0EB2FjY291bnQJAKUIAQgFAWkGY2FsbGVyBAdhc3NldElkCQEaZ2V0VmFsaWRTdGFraW5nQXNzZXRPckZhaWwCBQtzdGFraW5nVHlwZQkA2QQBBQxhc3NldElkUGFyYW0EDWFzc2V0SWRTdHJpbmcJANgEAQUHYXNzZXRJZAQHYmFsYW5jZQkAZQIJAQ5nZXRVc2VyQmFsYW5jZQIFB2FjY291bnQFDWFzc2V0SWRTdHJpbmcFDHVubG9ja0Ftb3VudAMJAGYCAAAFB2JhbGFuY2UJAAIBAg5pbnZhbGlkIGFtb3VudAkAlAoCCQDMCAIJAQxJbnRlZ2VyRW50cnkCCQEVZ2V0Q29udHJhY3RCYWxhbmNlS2V5AQUNYXNzZXRJZFN0cmluZwkAZQIJARJnZXRDb250cmFjdEJhbGFuY2UBBQ1hc3NldElkU3RyaW5nBQx1bmxvY2tBbW91bnQJAMwIAgkBDEludGVnZXJFbnRyeQIJARFnZXRVc2VyQmFsYW5jZUtleQIFB2FjY291bnQFDWFzc2V0SWRTdHJpbmcFB2JhbGFuY2UJAMwIAgkBDlNjcmlwdFRyYW5zZmVyAwkBEUBleHRyTmF0aXZlKDEwNjIpAQUHYWNjb3VudAUMdW5sb2NrQW1vdW50BQdhc3NldElkBQNuaWwFBHVuaXQPAWkBC2NvbnN0cnVjdG9yAw1taW5Mb2NrQW1vdW50FXN1cHBvcnRlZFJld2FyZEFzc2V0cw5wU3Rha2VkQXNzZXRJZAMJAQIhPQIIBQFpBmNhbGxlcgUEdGhpcwkAAgECEVBlcm1pc3Npb24gZGVuaWVkCQDMCAIJAQxJbnRlZ2VyRW50cnkCCQEQa2V5TWluTG9ja0Ftb3VudAAFDW1pbkxvY2tBbW91bnQJAMwIAgkBC1N0cmluZ0VudHJ5AgkBGGtleVN1cHBvcnRlZFJld2FyZEFzc2V0cwAFFXN1cHBvcnRlZFJld2FyZEFzc2V0cwkAzAgCCQELU3RyaW5nRW50cnkCCQEQa2V5U3Rha2VkQXNzZXRJZAAFDnBTdGFrZWRBc3NldElkBQNuaWwBaQESbWlncmF0ZVVzZG5TdGFraW5nAQ51c2VyQWRkcmVzc1N0cgQHbW5nUHViUwkBC3ZhbHVlT3JFbHNlAgkAoggBAhwlcyVzX19jZmdfX2xlYXNpbmdNYW5hZ2VyUHViAiw3QVVNWDU0dWtZTVl2UG1tYTd5b0ZmNU5qWmhzNEJ1NW56M0V6OUVWOHN1cgQGbW5nUHViCQDZBAEFB21uZ1B1YlMDCQECIT0CCAUBaQ9jYWxsZXJQdWJsaWNLZXkFBm1uZ1B1YgkAAgECIW1pZ3JhdGVVc2RuU3Rha2luZyBub3QgYXV0aG9yaXplZAMJARppc1VzZG5TdGFraW5nTWlncmF0aW9uRG9uZQAJAAIBAhdtaWdyYXRpb24gaGFzIGJlZW4gZG9uZQMJAQIhPQIJAJADAQgFAWkIcGF5bWVudHMAAAkAAgECGHBheW1lbnRzIGFyZSBub3QgYWxsb3dlZAMJAQIhPQIIBQFpCmZlZUFzc2V0SWQFBHVuaXQJAAIBAhxmZWUgaW4gV0FWRVMgaXMgYWxsb3dlZCBvbmx5AwkBAiE9AggFAWkDZmVlAKDCHgkAAgECHzAuMDA1IFdBVkVTIGZlZSBpcyBhbGxvd2VkIG9ubHkEEWxlZ2FjeVVzZXJCYWxhbmNlCQEMZ2V0SW50T3JFbHNlAgkBFGtleUxlZ2FjeVVzZXJCYWxhbmNlAgUOdXNlckFkZHJlc3NTdHIFEHN0YWtlZEFzc2V0SWRTdHIAAAMJAAACBRFsZWdhY3lVc2VyQmFsYW5jZQAACQACAQkArAICAhhubyBuZWVkIHRvIG1pZ3JhdGUgdXNlciAFDnVzZXJBZGRyZXNzU3RyAwkBDGlzQWN0aXZlVXNlcgEFDnVzZXJBZGRyZXNzU3RyCQACAQkArAICAhZhbHJlYWR5IG1pZ3JhdGVkIHVzZXIgBQ51c2VyQWRkcmVzc1N0cgQLdXNlckFkZHJlc3MJARFAZXh0ck5hdGl2ZSgxMDYyKQEFDnVzZXJBZGRyZXNzU3RyBAllbXB0eVZlY3QJANkEAQIACQELY29tbW9uU3Rha2UDBQt1c2VyQWRkcmVzcwkBCkludm9jYXRpb24ICQDMCAIJAQ9BdHRhY2hlZFBheW1lbnQCBQ1zdGFrZWRBc3NldElkBRFsZWdhY3lVc2VyQmFsYW5jZQUDbmlsBQt1c2VyQWRkcmVzcwUJZW1wdHlWZWN0CAUBaQ10cmFuc2FjdGlvbklkAAAFBHVuaXQFC3VzZXJBZGRyZXNzBQllbXB0eVZlY3QGAWkBBXN0YWtlAAkBC2NvbW1vblN0YWtlAwgFAWkGY2FsbGVyBQFpBwFpARNzdGFrZUJ5T3JpZ2luQ2FsbGVyAAkBC2NvbW1vblN0YWtlAwgFAWkMb3JpZ2luQ2FsbGVyBQFpBwFpAQd1bnN0YWtlAQZhbW91bnQJAQ1jb21tb25VbnN0YWtlAwUGYW1vdW50BQFpBwFpAQdkZXBvc2l0AAQObWlncmF0aW9uQ2hlY2sJARpmYWlsSWZVc2RuTWlncmF0aW9uTm90RG9uZQADCQAAAgUObWlncmF0aW9uQ2hlY2sFDm1pZ3JhdGlvbkNoZWNrAwkBAiE9AgkAkAMBCAUBaQhwYXltZW50cwABCQACAQIfZXhhY3QgMSBwYXltZW50IGlzIGFsbG93ZWQgb25seQQDcG10CQCRAwIIBQFpCHBheW1lbnRzAAAEBmFtb3VudAgFA3BtdAZhbW91bnQECnBtdEFzc2V0SWQJAQt2YWx1ZU9yRWxzZQIIBQNwbXQHYXNzZXRJZAUHV0FWRVNJRAQNcG10QXNzZXRJZFN0cgkA2AQBBQpwbXRBc3NldElkBAhwbXRNdWx0WAMJAAACBQpwbXRBc3NldElkBQdXQVZFU0lEBQZNVUxUWDgFBk1VTFRYNgQHYW1vdW50WAkAtgIBBQZhbW91bnQEC3RvdGFsU3Rha2VkCQEMZ2V0SW50T3JFbHNlAgkBF2tleUxvY2tQYXJhbVRvdGFsQW1vdW50AAAABAx0b3RhbFN0YWtlZFgJALYCAQULdG90YWxTdGFrZWQDCQBmAgAABQt0b3RhbFN0YWtlZAkAAgECG1RPRE86IGNhc2UgaXMgbm90IHN1cHBvcnRlZAMJAAACBQt0b3RhbFN0YWtlZAAACQEiSW5jcmVtZW50Tm90RGlzdHJpYnV0ZWRSZXdhcmRFbnRyeQIFDXBtdEFzc2V0SWRTdHIFBmFtb3VudAQQcmV3YXJkUGVyTnNidFgxOAkAvAIDBQdhbW91bnRYBQdNVUxUWDE4BQx0b3RhbFN0YWtlZFgEEWRlcG9zaXROdW1MYXN0S0VZCQERa2V5RGVwb3NpdE51bUxhc3QABA5kZXBvc2l0TnVtTGFzdAkBDGdldEludE9yRWxzZQIFEWRlcG9zaXROdW1MYXN0S0VZAP///////////wEEDWRlcG9zaXROdW1OZXcJAGQCBQ5kZXBvc2l0TnVtTGFzdAABAwkBASEBCQEIY29udGFpbnMCBRJzdXBwb3J0ZWRBc3NldHNTdHIFDXBtdEFzc2V0SWRTdHIJAAIBCQCsAgIJAKwCAgUSc3VwcG9ydGVkQXNzZXRzU3RyAhEgZG9lc24ndCBjb250YWluIAUNcG10QXNzZXRJZFN0cgoBF3JlZnJlc2hSZXdhcmRQZXJOc2J0U1VNAgVhY2N1bQluZXh0QXNzZXQEFnJld2FyZFBlck5zYnRTdW1OZXdLRVkJARVrZXlSZXdhcmRQZXJOc2J0U3VtQXQCBQ1kZXBvc2l0TnVtTmV3BQluZXh0QXNzZXQECnN1bUxhc3RTdHIJAQxnZXRTdHJPckVsc2UCCQEVa2V5UmV3YXJkUGVyTnNidFN1bUF0AgUOZGVwb3NpdE51bUxhc3QFCW5leHRBc3NldAIBMAkAzQgCBQVhY2N1bQMJAAACBQluZXh0QXNzZXQFDXBtdEFzc2V0SWRTdHIJAQtTdHJpbmdFbnRyeQIFFnJld2FyZFBlck5zYnRTdW1OZXdLRVkJAKYDAQkAtwICCQCnAwEFCnN1bUxhc3RTdHIFEHJld2FyZFBlck5zYnRYMTgJAQtTdHJpbmdFbnRyeQIFFnJld2FyZFBlck5zYnRTdW1OZXdLRVkFCnN1bUxhc3RTdHIJAM4IAgkAzQgCCgACJGwFE3N1cHBvcnRlZEFzc2V0c0xpc3QKAAIkcwkAkAMBBQIkbAoABSRhY2MwBQNuaWwKAQUkZjBfMQICJGECJGkDCQBnAgUCJGkFAiRzBQIkYQkBF3JlZnJlc2hSZXdhcmRQZXJOc2J0U1VNAgUCJGEJAJEDAgUCJGwFAiRpCgEFJGYwXzICAiRhAiRpAwkAZwIFAiRpBQIkcwUCJGEJAAIBAhNMaXN0IHNpemUgZXhjZWVkcyAyCQEFJGYwXzICCQEFJGYwXzECCQEFJGYwXzECBQUkYWNjMAAAAAEAAgkBDEludGVnZXJFbnRyeQIFEWRlcG9zaXROdW1MYXN0S0VZBQ1kZXBvc2l0TnVtTmV3CQEVRGVwb3NpdHNUb3RhbHNFbnRyaWVzAgUGYW1vdW50BQ1wbXRBc3NldElkU3RyCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuAWkBDGNsYWltUmV3YXJkcwAJAQtjb21tb25DbGFpbQIIBQFpBmNhbGxlcgUBaQFpARpjbGFpbVJld2FyZHNCeU9yaWdpbkNhbGxlcgAJAQtjb21tb25DbGFpbQIIBQFpDG9yaWdpbkNhbGxlcgUBaQFpARh1bmNsYWltZWRSZXdhcmRzUkVBRE9OTFkBDnVzZXJBZGRyZXNzU3RyCgEWZm9yRWFjaEFzc2V0WmVyb1Jld2FyZAIFYWNjdW0FYXNzZXQJAKwCAgkArAICBQVhY2N1bQkAuQkCCQDMCAIFBWFzc2V0CQDMCAICATAJAMwIAgIBMAUDbmlsAgE6AgFfBBJ1bmNsYWltZWRSZXdhcmRTdHIDCQAAAgUOdXNlckFkZHJlc3NTdHICAAoAAiRsBRNzdXBwb3J0ZWRBc3NldHNMaXN0CgACJHMJAJADAQUCJGwKAAUkYWNjMAIACgEFJGYwXzECAiRhAiRpAwkAZwIFAiRpBQIkcwUCJGEJARZmb3JFYWNoQXNzZXRaZXJvUmV3YXJkAgUCJGEJAJEDAgUCJGwFAiRpCgEFJGYwXzICAiRhAiRpAwkAZwIFAiRpBQIkcwUCJGEJAAIBAhNMaXN0IHNpemUgZXhjZWVkcyAyCQEFJGYwXzICCQEFJGYwXzECCQEFJGYwXzECBQUkYWNjMAAAAAEAAgQLdXNlckFkZHJlc3MJARFAZXh0ck5hdGl2ZSgxMDYyKQEFDnVzZXJBZGRyZXNzU3RyBA0kdDAyNTA4MjI1MTk2CQELdmFsdWVPckVsc2UCCQETZ2V0VXNlclBhcmFtc09yVW5pdAEFDnVzZXJBZGRyZXNzU3RyCQCVCgMGAAAAAAQJaXNOZXdVc2VyCAUNJHQwMjUwODIyNTE5NgJfMQQMc3Rha2VkQW1vdW50CAUNJHQwMjUwODIyNTE5NgJfMgQSc3Rha2luZ1N0YXJ0SGVpZ2h0CAUNJHQwMjUwODIyNTE5NgJfMwQNc3Rha2VkQW1vdW50WAkAtgIBBQxzdGFrZWRBbW91bnQEG3VzZXJSZXdhcmRGcm9tRGVwb3NpdE51bUtFWQkBG2tleVVzZXJSZXdhcmRGcm9tRGVwb3NpdE51bQEFDnVzZXJBZGRyZXNzU3RyBA5kZXBvc2l0TnVtVXNlcgkBDGdldEludE9yRWxzZQIFG3VzZXJSZXdhcmRGcm9tRGVwb3NpdE51bUtFWQD///////////8BBA5kZXBvc2l0TnVtTGFzdAkBDGdldEludE9yRWxzZQIJARFrZXlEZXBvc2l0TnVtTGFzdAAA////////////AQoBH2ZvckVhY2hBc3NldENhbGNVbmNsYWltZWRSZXdhcmQCBWFjY3VtBWFzc2V0BA0kdDAyNTU0MjI1NjgwCQEKY2FsY1Jld2FyZAUFDnVzZXJBZGRyZXNzU3RyBQVhc3NldAUNc3Rha2VkQW1vdW50WAUOZGVwb3NpdE51bVVzZXIFDmRlcG9zaXROdW1MYXN0BAtyZXdhcmRUb3RhbAgFDSR0MDI1NTQyMjU2ODACXzEEBmNhY2hlZAgFDSR0MDI1NTQyMjU2ODACXzIEB2R5bmFtaWMIBQ0kdDAyNTU0MjI1NjgwAl8zBBNyZXdhcmRDYWNoZWRQYXJ0S0VZCAUNJHQwMjU1NDIyNTY4MAJfNAQHY2xhaW1lZAkBC3ZhbHVlT3JFbHNlAgkAnwgBCQEKa2V5Q2xhaW1lZAIFDnVzZXJBZGRyZXNzU3RyBQVhc3NldAAACQCsAgIJAKwCAgUFYWNjdW0JALkJAgkAzAgCBQVhc3NldAkAzAgCCQCkAwEFC3Jld2FyZFRvdGFsCQDMCAIJAKQDAQUHY2xhaW1lZAUDbmlsAgE6AgFfCgACJGwFE3N1cHBvcnRlZEFzc2V0c0xpc3QKAAIkcwkAkAMBBQIkbAoABSRhY2MwAgAKAQUkZjBfMQICJGECJGkDCQBnAgUCJGkFAiRzBQIkYQkBH2ZvckVhY2hBc3NldENhbGNVbmNsYWltZWRSZXdhcmQCBQIkYQkAkQMCBQIkbAUCJGkKAQUkZjBfMgICJGECJGkDCQBnAgUCJGkFAiRzBQIkYQkAAgECE0xpc3Qgc2l6ZSBleGNlZWRzIDIJAQUkZjBfMgIJAQUkZjBfMQIJAQUkZjBfMQIFBSRhY2MwAAAAAQACCQCUCgIFA25pbAkAswICBRJ1bmNsYWltZWRSZXdhcmRTdHIAAQFpARZ1c2RuU3Rha2luZ1NZU1JFQURPTkxZAhV1c2VyQWRkcmVzc1N0ck9yRW1wdHkIdXNkbkRpZmYEEnVzZG5Ub3RhbEFtdFN0YWtlZAkBDGdldEludE9yRWxzZQIJARdrZXlMb2NrUGFyYW1Ub3RhbEFtb3VudAAAAAMJAAACBRV1c2VyQWRkcmVzc1N0ck9yRW1wdHkCAAkAlAoCBQNuaWwJAMwIAgAACQDMCAIFEnVzZG5Ub3RhbEFtdFN0YWtlZAkAzAgCAAAFA25pbAQLdXNlckFkZHJlc3MJAQ90b0FkZHJlc3NPckZhaWwBBRV1c2VyQWRkcmVzc1N0ck9yRW1wdHkECm1lcmdlZERhdGEJAQptZXJnZVN0YWtlAgUVdXNlckFkZHJlc3NTdHJPckVtcHR5BQh1c2RuRGlmZgQJaXNOZXdVc2VyCAUKbWVyZ2VkRGF0YQJfMQQQdXNkblN0YWtlZEJ5VXNlcggFCm1lcmdlZERhdGECXzIEEnN0YWtpbmdTdGFydEhlaWdodAgFCm1lcmdlZERhdGECXzMED3N0YWtlZEFtb3VudE5FVwgFCm1lcmdlZERhdGECXzQJAJQKAgUDbmlsCQDMCAIFEHVzZG5TdGFrZWRCeVVzZXIJAMwIAgUSdXNkblRvdGFsQW10U3Rha2VkBQNuaWwBaQERY29uZmlnU1lTUkVBRE9OTFkABAptaW5Mb2NrQW10CQERQGV4dHJOYXRpdmUoMTA1NSkBCQEQa2V5TWluTG9ja0Ftb3VudAAJAJQKAgUDbmlsCQDMCAIFCm1pbkxvY2tBbXQFA25pbAFpAQ5sb2NrTmV1dHJpbm9TUAIIcmVjZWl2ZXIFc2hhcmUJAQtjb21tb25TdGFrZQMIBQFpBmNhbGxlcgUBaQcBaQEMbG9ja05ldXRyaW5vAAkBC2NvbW1vblN0YWtlAwgFAWkGY2FsbGVyBQFpBwFpAQ51bmxvY2tOZXV0cmlubwIMdW5sb2NrQW1vdW50DWFzc2V0SWRTdHJpbmcJAQ1jb21tb25VbnN0YWtlAwUMdW5sb2NrQW1vdW50BQFpBwFpAQp1bmxvY2tOc2J0Agx1bmxvY2tBbW91bnQNYXNzZXRJZFN0cmluZwkBDmludGVybmFsVW5sb2NrBAUITlNCVFRZUEUFAWkFDHVubG9ja0Ftb3VudAUNYXNzZXRJZFN0cmluZwECdHgBBnZlcmlmeQAEE3B1YktleUFkbWluc0xpc3RTdHIJALkJAgkAzAgCAixHSmRMU2FMaXY1Szd4dWVqYWM4bWNSY0hveW8zZFByRVNydmt0RzNhNk1BUgkAzAgCAixFWXdabVVSZDVLS2FRUkJqc1ZhNmc4RFBpc0ZvUzZTb3ZSSnRGaUw1Z01IVQkAzAgCAixEdG1BZnVEZENySEs4c3BkQWVBWXpxNk1zWmVnZUQ5Z25zcnB1VFJrQ2JWQQkAzAgCAiw1V1JYRlNqd2NUYk5mS2NKczhacVhtU1NXWXNTVkpVdE12TXFaajVoSDROYwUDbmlsBQNTRVAEEHB1YktleUFkbWluc0xpc3QJALUJAgkBC3ZhbHVlT3JFbHNlAgkAnQgCBQ9jb250cm9sQ29udHJhY3QCDCVzX19tdWx0aXNpZwUTcHViS2V5QWRtaW5zTGlzdFN0cgUDU0VQBAVjb3VudAkAZAIJAGQCCQBkAgMJAPQDAwgFAnR4CWJvZHlCeXRlcwkAkQMCCAUCdHgGcHJvb2ZzAAAJANkEAQkAkQMCBRBwdWJLZXlBZG1pbnNMaXN0AAAAAQAAAwkA9AMDCAUCdHgJYm9keUJ5dGVzCQCRAwIIBQJ0eAZwcm9vZnMAAQkA2QQBCQCRAwIFEHB1YktleUFkbWluc0xpc3QAAQABAAADCQD0AwMIBQJ0eAlib2R5Qnl0ZXMJAJEDAggFAnR4BnByb29mcwACCQDZBAEJAJEDAgUQcHViS2V5QWRtaW5zTGlzdAACAAEAAAMJAPQDAwgFAnR4CWJvZHlCeXRlcwkAkQMCCAUCdHgGcHJvb2ZzAAMJANkEAQkAkQMCBRBwdWJLZXlBZG1pbnNMaXN0AAMAAgAACQBnAgUFY291bnQAAzDsmao=", "chainId": 87, "height": 3356221, "applicationStatus": "succeeded", "spentComplexity": 0 } View: original | compacted Prev: Grf8PLjnzLeGoBhUYzvpZeEanTtujv7YUMEt56WPcsb1 Next: uWWQRFdMLWFW58WFT4eNrNnc77tKtkJeuZ7UAErBuFD Diff:
OldNewDifferences
1-{-# STDLIB_VERSION 5 #-}
1+{-# STDLIB_VERSION 6 #-}
22 {-# SCRIPT_TYPE ACCOUNT #-}
33 {-# CONTENT_TYPE DAPP #-}
4-let revisionNum = "cbd0bdc8bbba91db64066b16a84913a4c965e23e"
4+let revisionNum = "ba12ce93df3b4a092532aded95e5288a4919c24a"
55
66 let SEP = "__"
77
8-func getNumberByKey (key) = valueOrElse(getInteger(this, key), 0)
8+let MULT6 = 1000000
99
10+let MULT8 = 100000000
1011
11-func getStringByKey (key) = valueOrElse(getString(this, key), "")
12+let MULTX6 = toBigInt(MULT6)
1213
14+let MULTX8 = toBigInt(MULT8)
1315
14-func getBoolByKey (key) = valueOrElse(getBoolean(this, key), false)
16+let MULTX18 = toBigInt(1000000000000000000)
1517
18+let WAVESIDSTR = "WAVES"
1619
17-func getNumberByAddressAndKey (address,key) = valueOrElse(getInteger(addressFromStringValue(address), key), 0)
20+let WAVESID = fromBase58String(WAVESIDSTR)
1821
19-
20-func getStringByAddressAndKey (address,key) = valueOrElse(getString(address, key), "")
21-
22+let DAYMILLIS = 86400000
2223
2324 let IdxControlCfgNeutrinoDapp = 1
2425
3839
3940 let IdxControlCfgMediatorDapp = 9
4041
42+let IdxControlCfgSurfStakingDapp = 10
43+
44+let IdxControlCfgGnsbtControllerDapp = 11
45+
4146 func getStringOrFail (address,key) = valueOrErrorMessage(getString(address, key), (((("mandatory " + toString(address)) + ".") + key) + " is not defined"))
47+
48+
49+func getIntOrFail (key) = valueOrErrorMessage(getInteger(this, key), (("Mandatory this." + key) + " is not defined"))
50+
51+
52+func getStrOrElse (key,defaultVal) = valueOrElse(getString(this, key), defaultVal)
53+
54+
55+func keyMinLockAmount () = "%s__minLockAmount"
56+
57+
58+func keyStakedAssetId () = "%s__stakedAssetId"
4259
4360
4461 func keyControlAddress () = "%s%s__config__controlAddress"
4764 func keyControlCfg () = "%s__controlConfig"
4865
4966
50-func readControlCfgOrFail (control) = split(getStringOrFail(control, keyControlCfg()), SEP)
67+func keySupportedRewardAssets () = "supportedRewardAssets"
68+
69+
70+func readControlCfgOrFail (control) = split_4C(getStringOrFail(control, keyControlCfg()), SEP)
5171
5272
5373 func getContractAddressOrFail (controlCfg,idx) = valueOrErrorMessage(addressFromString(controlCfg[idx]), ("Control cfg doesn't contain address at index " + toString(idx)))
5777
5878 let controlCfg = readControlCfgOrFail(controlContract)
5979
80+let mathContract = getContractAddressOrFail(controlCfg, IdxControlCfgMathDapp)
81+
6082 let neutrinoContract = getContractAddressOrFail(controlCfg, IdxControlCfgNeutrinoDapp)
83+
84+let auctionContract = getContractAddressOrFail(controlCfg, IdxControlCfgAuctionDapp)
85+
86+let stakedAssetIdStr = getStringOrFail(this, keyStakedAssetId())
87+
88+let stakedAssetId = fromBase58String(stakedAssetIdStr)
89+
90+let minLockAmount = getIntOrFail(keyMinLockAmount())
91+
92+let supportedAssetsStr = getStrOrElse(keySupportedRewardAssets(), "")
93+
94+let supportedAssetsList = split(supportedAssetsStr, "_")
95+
96+func keyLockParamUserAmount (userAddress) = makeString(["%s%s%s", "paramByUser", userAddress, "amount"], SEP)
97+
98+
99+func keyLockParamStartBlock (userAddress) = makeString(["%s%s%s", "paramByUser", userAddress, "start"], SEP)
100+
101+
102+func keyHistoryRecord (type,userAddress,txId) = makeString(["%s%s%s%s", "history", type, userAddress, toBase58String(txId)], SEP)
103+
104+
105+func keyLockParamTotalAmount () = makeString(["%s%s", "stats", "activeTotalLocked"], SEP)
106+
107+
108+func keyStatsLocksCount () = makeString(["%s%s", "stats", "locksCount"], SEP)
109+
110+
111+func keyStatsUsersCount () = makeString(["%s%s", "stats", "activeUsersCount"], SEP)
112+
113+
114+func keyStatsDepositAmtByDay (timestamp) = makeString(["%s%s%d", "stats", "depositAmtByDay", toString(timestamp)], SEP)
115+
116+
117+func keyStatsDepositAmtTotals () = makeString(["%s%s%d", "stats", "depositAmtTotals"], SEP)
118+
119+
120+func keyNextPeriod () = "%s__nextPeriod"
121+
122+
123+func keyDepositNumLast () = makeString(["%s%s%s", "dep", "lastNum"], SEP)
124+
125+
126+func keyUserRewardFromDepositNum (userAddress) = makeString(["%s%s%s", "userRwdFromDepNum", userAddress], SEP)
127+
128+
129+func keyRewardPerNsbtSumAt (depositNum,tkn) = makeString(["%s%d", "rwdPerNsbtSumByDepNum", toString(depositNum), tkn], SEP)
130+
131+
132+func keyReward (userAddress,tkn) = makeString(["%s%s%s", "rwd", userAddress, tkn], SEP)
133+
134+
135+func keyClaimed (userAddress,tkn) = makeString(["%s%s%s", "clm", userAddress, tkn], SEP)
136+
137+
138+func keyNotDistributedReward (tkn) = makeString(["%s%s", "notDistributed", tkn], SEP)
139+
140+
141+func keyLegacyUserBalance (userAddr,tkn) = makeString(["rpd_balance", tkn, userAddr], "_")
142+
143+
144+func keyLegacyTotalBalance (tkn) = makeString(["rpd_balance", tkn], "_")
145+
146+
147+func toX18 (origVal,origMult) = fraction(toBigInt(origVal), MULTX18, origMult)
148+
149+
150+func getIntOrZero (key) = valueOrElse(getInteger(this, key), 0)
151+
152+
153+func getIntOrElse (key,defaultVal) = valueOrElse(getInteger(this, key), defaultVal)
154+
155+
156+func toAddressOrFail (addressStr) = valueOrErrorMessage(addressFromString(addressStr), ("couldn't parse passed addressStr=" + addressStr))
157+
158+
159+func toAssetVect (assetStr) = if ((assetStr == WAVESIDSTR))
160+ then unit
161+ else fromBase58String(assetStr)
162+
163+
164+func asInt (val) = match val {
165+ case valInt: Int =>
166+ valInt
167+ case _ =>
168+ throw("fail to cast into Int")
169+}
170+
171+
172+func asSwapParamsSTRUCT (v) = match v {
173+ case struct: (Int, Int, Int, Int, Int, Int, Int) =>
174+ struct
175+ case _ =>
176+ throw("fail to cast into Int")
177+}
178+
179+
180+func formatHistoryRecord (userAddress,oldAmount,newAmount) = makeString(["%s%d%d%d%d", userAddress, toString(lastBlock.height), toString(lastBlock.timestamp), toString(oldAmount), toString(newAmount)], SEP)
181+
182+
183+func formatClaimHistoryRecord (userAddress,claimedRewards) = makeString(["%s%d%d%s", userAddress, toString(lastBlock.height), toString(lastBlock.timestamp), claimedRewards], SEP)
184+
185+
186+func HistoryRecordEntry (type,userAddress,txId,oldAmount,newAmount) = StringEntry(keyHistoryRecord(type, userAddress, txId), formatHistoryRecord(userAddress, oldAmount, newAmount))
187+
188+
189+func ClaimHistoryEntry (userAddress,txId,claimedRewards) = StringEntry(keyHistoryRecord("claim", userAddress, txId), formatClaimHistoryRecord(userAddress, claimedRewards))
190+
191+
192+func StatsResult (totalLockedInc,lockCountInc,usersCountInc,isMigration) = {
193+ let locksCount = getIntOrZero(keyStatsLocksCount())
194+ let usersCount = getIntOrZero(keyStatsUsersCount())
195+ let totalAmount = getIntOrZero(keyLockParamTotalAmount())
196+ let totalAmountNew = (totalAmount + totalLockedInc)
197+ $Tuple3(([IntegerEntry(keyStatsLocksCount(), (locksCount + lockCountInc)), IntegerEntry(keyStatsUsersCount(), (usersCount + usersCountInc)), IntegerEntry(keyLockParamTotalAmount(), totalAmountNew)] ++ (if (isMigration)
198+ then nil
199+ else [IntegerEntry(keyLegacyTotalBalance(stakedAssetIdStr), totalAmountNew)])), totalAmount, totalAmountNew)
200+ }
201+
202+
203+func LockParamsEntry (userAddress,amount,stakingStartHeight,isMigration) = ([IntegerEntry(keyLockParamUserAmount(userAddress), amount), IntegerEntry(keyLockParamStartBlock(userAddress), stakingStartHeight)] ++ (if (isMigration)
204+ then nil
205+ else [IntegerEntry(keyLegacyUserBalance(userAddress, stakedAssetIdStr), amount)]))
206+
207+
208+func getParamsOrFail () = $Tuple2(fromBase58String(getStringOrFail(this, keyStakedAssetId())), getIntOrFail(keyMinLockAmount()))
209+
210+
211+func isActiveUser (userAddress) = (getIntOrElse(keyLockParamUserAmount(userAddress), 0) > 0)
212+
213+
214+func getUserParamsOrUnit (userAddress) = if (isActiveUser(userAddress))
215+ then $Tuple3(false, getIntOrFail(keyLockParamUserAmount(userAddress)), getIntOrFail(keyLockParamStartBlock(userAddress)))
216+ else unit
217+
218+
219+func getUserParamsOrFail (userAddress) = valueOrErrorMessage(getUserParamsOrUnit(userAddress), (("User " + userAddress) + " is not defined"))
220+
221+
222+func calcReward (userAddress,assetId,stakedAmountX,depositNumUser,depositNumLast) = {
223+ let rewardPerNsbtSumLastKEY = keyRewardPerNsbtSumAt(depositNumLast, assetId)
224+ let sumLastX18 = parseBigIntValue(getStrOrElse(keyRewardPerNsbtSumAt(depositNumLast, assetId), "0"))
225+ let sumUserX18 = parseBigIntValue(getStrOrElse(keyRewardPerNsbtSumAt(depositNumUser, assetId), "0"))
226+ let rewardDynamicPart = toInt(fraction((sumLastX18 - sumUserX18), stakedAmountX, MULTX18))
227+ let rewardCachedPartKEY = keyReward(userAddress, assetId)
228+ let rewardCachedPart = getIntOrElse(rewardCachedPartKEY, 0)
229+ $Tuple4((rewardCachedPart + rewardDynamicPart), rewardCachedPart, rewardDynamicPart, rewardCachedPartKEY)
230+ }
231+
232+
233+func toStartOfDay (timestamp) = ((timestamp / DAYMILLIS) * DAYMILLIS)
234+
235+
236+func findElementPosition (src,element,sep) = {
237+ let elementStart = valueOrErrorMessage(indexOf(src, element), ((("there is no substring " + element) + " in ") + src))
238+ if ((elementStart == 0))
239+ then 0
240+ else {
241+ let left = take(src, elementStart)
242+ (size(split(left, sep)) - 1)
243+ }
244+ }
245+
246+
247+let DepositTotalsPREFIX = "%d%d"
248+
249+func updateDepositTotals (currVal,idxToUpdate,deltaAmt) = {
250+ let currArr = split(currVal, SEP)
251+ func updDepTotByIdx (idx) = if ((idx != idxToUpdate))
252+ then currArr[idx]
253+ else toString((parseIntValue(currArr[idx]) + deltaAmt))
254+
255+ makeString([DepositTotalsPREFIX, updDepTotByIdx(1), updDepTotByIdx(2)], SEP)
256+ }
257+
258+
259+func DepositsTotalsEntries (depositAmount,assetIdStr) = {
260+ let startOfDay = toStartOfDay(lastBlock.timestamp)
261+ let byDayKEY = keyStatsDepositAmtByDay(startOfDay)
262+ let totalsKEY = keyStatsDepositAmtTotals()
263+ let position = findElementPosition(supportedAssetsStr, assetIdStr, "_")
264+ let defaultDATA = (DepositTotalsPREFIX + "__0__0")
265+ let currTotalsDATA = valueOrElse(getString(this, totalsKEY), defaultDATA)
266+ let newTotalsDATA = updateDepositTotals(currTotalsDATA, (position + 1), depositAmount)
267+[StringEntry(totalsKEY, newTotalsDATA), StringEntry(byDayKEY, newTotalsDATA)]
268+ }
269+
270+
271+func RewardEntries (isNewUser,userAddress,stakedAmount) = {
272+ let stakedAmountX = toBigInt(stakedAmount)
273+ let userRewardFromDepositNumKEY = keyUserRewardFromDepositNum(userAddress)
274+ let depositNumUser = getIntOrElse(userRewardFromDepositNumKEY, -1)
275+ let depositNumLast = getIntOrElse(keyDepositNumLast(), -1)
276+ func forEachAssetCacheUserReward (accum,asset) = {
277+ let $t01076510900 = calcReward(userAddress, asset, stakedAmountX, depositNumUser, depositNumLast)
278+ let rewardTotal = $t01076510900._1
279+ let cached = $t01076510900._2
280+ let dynamic = $t01076510900._3
281+ let rewardCachedPartKEY = $t01076510900._4
282+ (accum :+ IntegerEntry(rewardCachedPartKEY, rewardTotal))
283+ }
284+
285+ if (if ((depositNumLast == -1))
286+ then (depositNumUser == -1)
287+ else false)
288+ then nil
289+ else if (if ((depositNumLast == -1))
290+ then (depositNumUser > -1)
291+ else false)
292+ then throw("invalid depositNumLast and depositNumUser state")
293+ else if (if ((depositNumLast > -1))
294+ then (depositNumUser >= -1)
295+ else false)
296+ then if (isNewUser)
297+ then [IntegerEntry(userRewardFromDepositNumKEY, depositNumLast)]
298+ else ({
299+ let $l = supportedAssetsList
300+ let $s = size($l)
301+ let $acc0 = nil
302+ func $f0_1 ($a,$i) = if (($i >= $s))
303+ then $a
304+ else forEachAssetCacheUserReward($a, $l[$i])
305+
306+ func $f0_2 ($a,$i) = if (($i >= $s))
307+ then $a
308+ else throw("List size exceeds 2")
309+
310+ $f0_2($f0_1($f0_1($acc0, 0), 1), 2)
311+ } :+ IntegerEntry(userRewardFromDepositNumKEY, depositNumLast))
312+ else throw(((("uncovered condition: depositNumLast=" + toString(depositNumLast)) + " depositNumUser=") + toString(depositNumUser)))
313+ }
314+
315+
316+func IncrementNotDistributedRewardEntry (tkn,amountInc) = {
317+ let notDistributedRewardKEY = keyNotDistributedReward(tkn)
318+ let notDistributedReward = getIntOrElse(notDistributedRewardKEY, 0)
319+[IntegerEntry(notDistributedRewardKEY, (notDistributedReward + amountInc))]
320+ }
321+
322+
323+func mergeStake (userAddress,amountToAdd) = {
324+ let $t01372413840 = valueOrElse(getUserParamsOrUnit(userAddress), $Tuple3(true, 0, height))
325+ let isNewUser = $t01372413840._1
326+ let stakedAmount = $t01372413840._2
327+ let stakingStartHeight = $t01372413840._3
328+ let stakedAmountNEW = if (isNewUser)
329+ then amountToAdd
330+ else (amountToAdd + stakedAmount)
331+ $Tuple4(isNewUser, stakedAmount, stakingStartHeight, stakedAmountNEW)
332+ }
333+
334+
335+func isUsdnStakingMigrationDone () = {
336+ let legacyTotalBalance = getIntOrElse(keyLegacyTotalBalance(stakedAssetIdStr), 0)
337+ let totalBalance = getIntOrElse(keyLockParamTotalAmount(), 0)
338+ (legacyTotalBalance == totalBalance)
339+ }
340+
341+
342+func failIfUsdnMigrationNotDone () = if (isUsdnStakingMigrationDone())
343+ then true
344+ else throw("USDN staking migration is IN PROGRESS. All operations are temporary suspended.")
345+
346+
347+func commonStake (userAddress,i,isMigration) = {
348+ let migCheck = if (!(isMigration))
349+ then failIfUsdnMigrationNotDone()
350+ else true
351+ if ((migCheck == migCheck))
352+ then if ((size(i.payments) != 1))
353+ then throw("Invalid payments size")
354+ else {
355+ let payment = i.payments[0]
356+ let amount = payment.amount
357+ let invalidAssetMessage = (("Invalid asset. " + toBase58String(stakedAssetId)) + " is expected")
358+ let assetId = valueOrErrorMessage(payment.assetId, invalidAssetMessage)
359+ if ((assetId != stakedAssetId))
360+ then throw(invalidAssetMessage)
361+ else {
362+ let userAddressStr = toString(userAddress)
363+ let mergedData = mergeStake(userAddressStr, amount)
364+ let isNewUser = mergedData._1
365+ let stakedAmount = mergedData._2
366+ let stakingStartHeight = mergedData._3
367+ let stakedAmountNEW = mergedData._4
368+ if ((minLockAmount > stakedAmountNEW))
369+ then throw(("Min lock amount is " + toString(minLockAmount)))
370+ else {
371+ let $t01532315438 = StatsResult(amount, 1, if (isNewUser)
372+ then 1
373+ else 0, isMigration)
374+ let statsEntries = $t01532315438._1
375+ let totalStaked = $t01532315438._2
376+ let totalStakedNew = $t01532315438._3
377+ ((([HistoryRecordEntry("stake", userAddressStr, i.transactionId, stakedAmount, stakedAmountNEW)] ++ RewardEntries(isNewUser, userAddressStr, stakedAmount)) ++ LockParamsEntry(userAddressStr, stakedAmountNEW, stakingStartHeight, isMigration)) ++ statsEntries)
378+ }
379+ }
380+ }
381+ else throw("Strict value is not equal to itself.")
382+ }
383+
384+
385+func commonUnstake (amount,i,isMigration) = {
386+ let migrationCheck = failIfUsdnMigrationNotDone()
387+ if ((migrationCheck == migrationCheck))
388+ then if ((size(i.payments) != 0))
389+ then throw("unstake doesn't require any payment")
390+ else {
391+ let userAddress = i.caller
392+ let userAddressStr = toString(userAddress)
393+ let $t01600316091 = getUserParamsOrFail(userAddressStr)
394+ let isNewUser = $t01600316091._1
395+ let stakedAmount = $t01600316091._2
396+ let stakingStartHeight = $t01600316091._3
397+ let swapParamsSTRUCT = asSwapParamsSTRUCT(reentrantInvoke(neutrinoContract, "swapParamsByUserSYSREADONLY", [userAddressStr, 0], nil))
398+ let swapLimitSpentInUsdn = swapParamsSTRUCT._2
399+ let blcks2LmtReset = swapParamsSTRUCT._3
400+ if ((swapLimitSpentInUsdn > 0))
401+ then throw((("You have already made a swap operation. Wait " + toString((height + blcks2LmtReset))) + " height to unstake"))
402+ else if ((0 >= stakedAmount))
403+ then throw("Nothing to unstake")
404+ else if ((amount > stakedAmount))
405+ then throw(((("Requested " + toString(amount)) + ", but staked only ") + toString(stakedAmount)))
406+ else {
407+ let stakedAmountNEW = (stakedAmount - amount)
408+ let $t01672916900 = StatsResult(-(amount), if ((amount == stakedAmount))
409+ then -1
410+ else 0, if ((amount == stakedAmount))
411+ then -1
412+ else 0, isMigration)
413+ let statsEntries = $t01672916900._1
414+ let totalStaked = $t01672916900._2
415+ let totalStakedNew = $t01672916900._3
416+ ((([ScriptTransfer(userAddress, amount, stakedAssetId), HistoryRecordEntry("unstake", userAddressStr, i.transactionId, stakedAmount, stakedAmountNEW)] ++ RewardEntries(false, userAddressStr, stakedAmount)) ++ LockParamsEntry(userAddressStr, stakedAmountNEW, stakingStartHeight, isMigration)) ++ statsEntries)
417+ }
418+ }
419+ else throw("Strict value is not equal to itself.")
420+ }
421+
422+
423+func commonClaim (userAddress,i) = {
424+ let migrationCheck = failIfUsdnMigrationNotDone()
425+ if ((migrationCheck == migrationCheck))
426+ then {
427+ let userAddressStr = toString(userAddress)
428+ if ((size(i.payments) > 0))
429+ then throw("payments are not accepted")
430+ else {
431+ let $t01746417572 = valueOrElse(getUserParamsOrUnit(userAddressStr), $Tuple3(true, 0, 0))
432+ let isNewUser = $t01746417572._1
433+ let stakedAmount = $t01746417572._2
434+ let stakingStart = $t01746417572._3
435+ let stakedAmountX = toBigInt(stakedAmount)
436+ let userRewardFromDepositNumKEY = keyUserRewardFromDepositNum(userAddressStr)
437+ let depositNumUser = getIntOrElse(userRewardFromDepositNumKEY, -1)
438+ let depositNumLast = getIntOrElse(keyDepositNumLast(), -1)
439+ func forEachAssetCalcUnclaimedReward (accum,asset) = {
440+ let $t01794318081 = calcReward(userAddressStr, asset, stakedAmountX, depositNumUser, depositNumLast)
441+ let rewardTotal = $t01794318081._1
442+ let cached = $t01794318081._2
443+ let dynamic = $t01794318081._3
444+ let rewardCachedPartKEY = $t01794318081._4
445+ let claimedKEY = keyClaimed(userAddressStr, asset)
446+ let $t01814118178 = accum
447+ let data = $t01814118178._1
448+ let claimedAmtByAsset = $t01814118178._2
449+ let newPart = makeString([asset, toString(rewardTotal)], ":")
450+ let claimedAmtByAssetNew = makeString([claimedAmtByAsset, newPart], "_")
451+ if ((0 >= rewardTotal))
452+ then $Tuple2(data, claimedAmtByAssetNew)
453+ else $Tuple2((((data :+ ScriptTransfer(userAddress, rewardTotal, toAssetVect(asset))) :+ IntegerEntry(claimedKEY, (valueOrElse(getInteger(claimedKEY), 0) + rewardTotal))) :+ IntegerEntry(rewardCachedPartKEY, 0)), claimedAmtByAssetNew)
454+ }
455+
456+ let $t01863818751 = {
457+ let $l = supportedAssetsList
458+ let $s = size($l)
459+ let $acc0 = $Tuple2(nil, "")
460+ func $f0_1 ($a,$i) = if (($i >= $s))
461+ then $a
462+ else forEachAssetCalcUnclaimedReward($a, $l[$i])
463+
464+ func $f0_2 ($a,$i) = if (($i >= $s))
465+ then $a
466+ else throw("List size exceeds 2")
467+
468+ $f0_2($f0_1($f0_1($acc0, 0), 1), 2)
469+ }
470+ let transfers = $t01863818751._1
471+ let claimedAmtByAssetResult = $t01863818751._2
472+ if ((0 >= size(transfers)))
473+ then $Tuple2(nil, 0)
474+ else $Tuple2(((transfers :+ IntegerEntry(userRewardFromDepositNumKEY, depositNumLast)) :+ ClaimHistoryEntry(userAddressStr, i.transactionId, drop(claimedAmtByAssetResult, 1))), size(transfers))
475+ }
476+ }
477+ else throw("Strict value is not equal to itself.")
478+ }
479+
480+
481+let USDNTYPE = "USDN"
482+
483+let NSBTTYPE = "NSBT"
61484
62485 let NeutrinoAssetIdKey = "neutrino_asset_id"
63486
64487 let NeutrinoContractKey = "neutrino_contract"
65488
489+let NsbtAssetIdKey = "bond_asset_id"
490+
66491 let BalanceKey = "rpd_balance"
67492
68-let ControlContractKey = "control_contract"
493+let neutrinoAssetId = fromBase58String(getStringOrFail(neutrinoContract, NeutrinoAssetIdKey))
69494
70-let NsbtAssetIdKey = "bond_asset_id"
495+let nsbtAssetIdStr = getStringOrFail(neutrinoContract, NsbtAssetIdKey)
71496
72-let AdminsKey = "admins"
73-
74-let USDNTYPE = "USDN"
75-
76-let NSBTTYPE = "NSBT"
497+let nsbtAssetId = fromBase58String(nsbtAssetIdStr)
77498
78499 func getUserBalanceKey (owner,assetId) = makeString([BalanceKey, assetId, owner], "_")
79500
81502 func getContractBalanceKey (assetId) = ((BalanceKey + "_") + assetId)
82503
83504
84-func getExpireProposalKey (hash) = (("proposal_expire" + "_") + hash)
505+func getContractBalance (assetId) = getIntOrElse(getContractBalanceKey(assetId), 0)
85506
86507
87-func getOwnerProposalKey (hash) = (("proposal_owner" + "_") + hash)
88-
89-
90-func getArgumentsProposalKey (hash) = (("proposal_arguments" + "_") + hash)
91-
92-
93-func getVoteKey (owner,hash) = (((("proposal_vote" + "_") + owner) + "_") + hash)
94-
95-
96-func convertJsonArrayToList (jsonArray) = split(jsonArray, ",")
97-
98-
99-let neutrinoAssetId = fromBase58String(getStringByAddressAndKey(neutrinoContract, NeutrinoAssetIdKey))
100-
101-let nsbtAssetIdStr = getStringByAddressAndKey(neutrinoContract, NsbtAssetIdKey)
102-
103-let nsbtAssetId = fromBase58String(nsbtAssetIdStr)
104-
105-func getContractBalance (assetId) = getNumberByKey(getContractBalanceKey(assetId))
106-
107-
108-func getUserBalance (owner,assetId) = getNumberByKey(getUserBalanceKey(owner, assetId))
109-
110-
111-func getRewardsConfigKey (owner,share,receiver) = makeString(["stakingconfig", owner, toString(share), receiver], "_")
112-
113-
114-func getCurrentRewardsConfigKey (owner) = ("stakingconfig_current_" + owner)
115-
116-
117-func getRewardsConfigStartKey (configKey,isStart) = (configKey + (if (isStart)
118- then "_start"
119- else "_end"))
120-
121-
122-func getRewardConfigInitialShare (owner) = (owner + "_initialShare")
508+func getUserBalance (owner,assetId) = getIntOrElse(getUserBalanceKey(owner, assetId), 0)
123509
124510
125511 func getValidStakingAssetOrFail (stakingType,assetId) = if (if ((stakingType == USDNTYPE))
137523 else assetId
138524
139525
140-func internalLockNeutrino (stakingType,i,receiver,share) = {
141- let pmt = value(i.payments[0])
142- let assetId = getValidStakingAssetOrFail(stakingType, value(pmt.assetId))
143- if (!(isDefined(addressFromString(receiver))))
144- then throw(("Invalid address format " + receiver))
145- else if ((share > 100))
146- then throw("staking rewards share cannot be higher than 100%")
147- else if ((1 > share))
148- then throw("staking rewards share cannot be lower than 1%")
149- else {
150- let account = toString(i.caller)
151- let assetIdString = toBase58String(assetId)
152- let currentConfig = getStringByKey(getCurrentRewardsConfigKey(account))
153- let correctData = if ((currentConfig != ""))
154- then {
155- let currentConfigData = split(currentConfig, "_")
156- let currShare = parseIntValue(currentConfigData[2])
157- let currReceiver = currentConfigData[3]
158- let notMigratedInitialShare = getNumberByKey(getRewardConfigInitialShare(account))
159- let actualInitialShare = if ((notMigratedInitialShare == 0))
160- then currShare
161- else notMigratedInitialShare
162- let newShare = if (if ((actualInitialShare > share))
163- then true
164- else (currReceiver != receiver))
165- then actualInitialShare
166- else share
167-[toString(actualInitialShare), toString(newShare), currReceiver]
168- }
169- else [toString(share), toString(share), receiver]
170- let correctInitialShare = parseIntValue(correctData[0])
171- let correctShare = parseIntValue(correctData[1])
172- let correctReceiver = correctData[2]
173- let newCurrentConfig = getRewardsConfigKey(account, correctShare, correctReceiver)
174- let isNewConfig = !((currentConfig == newCurrentConfig))
175- let end = if (isNewConfig)
176- then height
177- else 0
178- let start = if (isNewConfig)
179- then height
180- else getNumberByKey(getRewardsConfigStartKey(newCurrentConfig, true))
181- $Tuple2([IntegerEntry(getContractBalanceKey(assetIdString), (getContractBalance(assetIdString) + pmt.amount)), IntegerEntry(getUserBalanceKey(account, assetIdString), (getUserBalance(account, assetIdString) + pmt.amount)), IntegerEntry(getRewardsConfigStartKey(currentConfig, false), end), IntegerEntry(getRewardsConfigStartKey(newCurrentConfig, true), start), IntegerEntry(getRewardConfigInitialShare(account), correctInitialShare), StringEntry(getCurrentRewardsConfigKey(account), newCurrentConfig)], unit)
182- }
183- }
184-
185-
186526 func internalUnlock (stakingType,i,unlockAmount,assetIdParam) = {
187527 let account = toString(i.caller)
188528 let assetId = getValidStakingAssetOrFail(stakingType, fromBase58String(assetIdParam))
195535
196536
197537 @Callable(i)
198-func lockNeutrinoSP (receiver,share) = internalLockNeutrino(USDNTYPE, i, receiver, share)
538+func constructor (minLockAmount,supportedRewardAssets,pStakedAssetId) = if ((i.caller != this))
539+ then throw("Permission denied")
540+ else [IntegerEntry(keyMinLockAmount(), minLockAmount), StringEntry(keySupportedRewardAssets(), supportedRewardAssets), StringEntry(keyStakedAssetId(), pStakedAssetId)]
199541
200542
201543
202544 @Callable(i)
203-func lockNeutrino () = internalLockNeutrino(USDNTYPE, i, toString(i.caller), 100)
545+func migrateUsdnStaking (userAddressStr) = {
546+ let mngPubS = valueOrElse(getString("%s%s__cfg__leasingManagerPub"), "7AUMX54ukYMYvPmma7yoFf5NjZhs4Bu5nz3Ez9EV8sur")
547+ let mngPub = fromBase58String(mngPubS)
548+ if ((i.callerPublicKey != mngPub))
549+ then throw("migrateUsdnStaking not authorized")
550+ else if (isUsdnStakingMigrationDone())
551+ then throw("migration has been done")
552+ else if ((size(i.payments) != 0))
553+ then throw("payments are not allowed")
554+ else if ((i.feeAssetId != unit))
555+ then throw("fee in WAVES is allowed only")
556+ else if ((i.fee != 500000))
557+ then throw("0.005 WAVES fee is allowed only")
558+ else {
559+ let legacyUserBalance = getIntOrElse(keyLegacyUserBalance(userAddressStr, stakedAssetIdStr), 0)
560+ if ((legacyUserBalance == 0))
561+ then throw(("no need to migrate user " + userAddressStr))
562+ else if (isActiveUser(userAddressStr))
563+ then throw(("already migrated user " + userAddressStr))
564+ else {
565+ let userAddress = addressFromStringValue(userAddressStr)
566+ let emptyVect = fromBase58String("")
567+ commonStake(userAddress, Invocation([AttachedPayment(stakedAssetId, legacyUserBalance)], userAddress, emptyVect, i.transactionId, 0, unit, userAddress, emptyVect), true)
568+ }
569+ }
570+ }
204571
205572
206573
207574 @Callable(i)
208-func lockNsbtSP (receiver,share) = internalLockNeutrino(NSBTTYPE, i, receiver, share)
575+func stake () = commonStake(i.caller, i, false)
209576
210577
211578
212579 @Callable(i)
213-func lockNsbt () = internalLockNeutrino(NSBTTYPE, i, toString(i.caller), 100)
580+func stakeByOriginCaller () = commonStake(i.originCaller, i, false)
214581
215582
216583
217584 @Callable(i)
218-func unlockNeutrino (unlockAmount,assetIdString) = internalUnlock(USDNTYPE, i, unlockAmount, assetIdString)
585+func unstake (amount) = commonUnstake(amount, i, false)
586+
587+
588+
589+@Callable(i)
590+func deposit () = {
591+ let migrationCheck = failIfUsdnMigrationNotDone()
592+ if ((migrationCheck == migrationCheck))
593+ then if ((size(i.payments) != 1))
594+ then throw("exact 1 payment is allowed only")
595+ else {
596+ let pmt = i.payments[0]
597+ let amount = pmt.amount
598+ let pmtAssetId = valueOrElse(pmt.assetId, WAVESID)
599+ let pmtAssetIdStr = toBase58String(pmtAssetId)
600+ let pmtMultX = if ((pmtAssetId == WAVESID))
601+ then MULTX8
602+ else MULTX6
603+ let amountX = toBigInt(amount)
604+ let totalStaked = getIntOrElse(keyLockParamTotalAmount(), 0)
605+ let totalStakedX = toBigInt(totalStaked)
606+ if ((0 > totalStaked))
607+ then throw("TODO: case is not supported")
608+ else if ((totalStaked == 0))
609+ then IncrementNotDistributedRewardEntry(pmtAssetIdStr, amount)
610+ else {
611+ let rewardPerNsbtX18 = fraction(amountX, MULTX18, totalStakedX)
612+ let depositNumLastKEY = keyDepositNumLast()
613+ let depositNumLast = getIntOrElse(depositNumLastKEY, -1)
614+ let depositNumNew = (depositNumLast + 1)
615+ if (!(contains(supportedAssetsStr, pmtAssetIdStr)))
616+ then throw(((supportedAssetsStr + " doesn't contain ") + pmtAssetIdStr))
617+ else {
618+ func refreshRewardPerNsbtSUM (accum,nextAsset) = {
619+ let rewardPerNsbtSumNewKEY = keyRewardPerNsbtSumAt(depositNumNew, nextAsset)
620+ let sumLastStr = getStrOrElse(keyRewardPerNsbtSumAt(depositNumLast, nextAsset), "0")
621+ (accum :+ (if ((nextAsset == pmtAssetIdStr))
622+ then StringEntry(rewardPerNsbtSumNewKEY, toString((parseBigIntValue(sumLastStr) + rewardPerNsbtX18)))
623+ else StringEntry(rewardPerNsbtSumNewKEY, sumLastStr)))
624+ }
625+
626+ (({
627+ let $l = supportedAssetsList
628+ let $s = size($l)
629+ let $acc0 = nil
630+ func $f0_1 ($a,$i) = if (($i >= $s))
631+ then $a
632+ else refreshRewardPerNsbtSUM($a, $l[$i])
633+
634+ func $f0_2 ($a,$i) = if (($i >= $s))
635+ then $a
636+ else throw("List size exceeds 2")
637+
638+ $f0_2($f0_1($f0_1($acc0, 0), 1), 2)
639+ } :+ IntegerEntry(depositNumLastKEY, depositNumNew)) ++ DepositsTotalsEntries(amount, pmtAssetIdStr))
640+ }
641+ }
642+ }
643+ else throw("Strict value is not equal to itself.")
644+ }
645+
646+
647+
648+@Callable(i)
649+func claimRewards () = commonClaim(i.caller, i)
650+
651+
652+
653+@Callable(i)
654+func claimRewardsByOriginCaller () = commonClaim(i.originCaller, i)
655+
656+
657+
658+@Callable(i)
659+func unclaimedRewardsREADONLY (userAddressStr) = {
660+ func forEachAssetZeroReward (accum,asset) = ((accum + makeString([asset, "0", "0"], ":")) + "_")
661+
662+ let unclaimedRewardStr = if ((userAddressStr == ""))
663+ then {
664+ let $l = supportedAssetsList
665+ let $s = size($l)
666+ let $acc0 = ""
667+ func $f0_1 ($a,$i) = if (($i >= $s))
668+ then $a
669+ else forEachAssetZeroReward($a, $l[$i])
670+
671+ func $f0_2 ($a,$i) = if (($i >= $s))
672+ then $a
673+ else throw("List size exceeds 2")
674+
675+ $f0_2($f0_1($f0_1($acc0, 0), 1), 2)
676+ }
677+ else {
678+ let userAddress = addressFromStringValue(userAddressStr)
679+ let $t02508225196 = valueOrElse(getUserParamsOrUnit(userAddressStr), $Tuple3(true, 0, 0))
680+ let isNewUser = $t02508225196._1
681+ let stakedAmount = $t02508225196._2
682+ let stakingStartHeight = $t02508225196._3
683+ let stakedAmountX = toBigInt(stakedAmount)
684+ let userRewardFromDepositNumKEY = keyUserRewardFromDepositNum(userAddressStr)
685+ let depositNumUser = getIntOrElse(userRewardFromDepositNumKEY, -1)
686+ let depositNumLast = getIntOrElse(keyDepositNumLast(), -1)
687+ func forEachAssetCalcUnclaimedReward (accum,asset) = {
688+ let $t02554225680 = calcReward(userAddressStr, asset, stakedAmountX, depositNumUser, depositNumLast)
689+ let rewardTotal = $t02554225680._1
690+ let cached = $t02554225680._2
691+ let dynamic = $t02554225680._3
692+ let rewardCachedPartKEY = $t02554225680._4
693+ let claimed = valueOrElse(getInteger(keyClaimed(userAddressStr, asset)), 0)
694+ ((accum + makeString([asset, toString(rewardTotal), toString(claimed)], ":")) + "_")
695+ }
696+
697+ let $l = supportedAssetsList
698+ let $s = size($l)
699+ let $acc0 = ""
700+ func $f0_1 ($a,$i) = if (($i >= $s))
701+ then $a
702+ else forEachAssetCalcUnclaimedReward($a, $l[$i])
703+
704+ func $f0_2 ($a,$i) = if (($i >= $s))
705+ then $a
706+ else throw("List size exceeds 2")
707+
708+ $f0_2($f0_1($f0_1($acc0, 0), 1), 2)
709+ }
710+ $Tuple2(nil, dropRight(unclaimedRewardStr, 1))
711+ }
712+
713+
714+
715+@Callable(i)
716+func usdnStakingSYSREADONLY (userAddressStrOrEmpty,usdnDiff) = {
717+ let usdnTotalAmtStaked = getIntOrElse(keyLockParamTotalAmount(), 0)
718+ if ((userAddressStrOrEmpty == ""))
719+ then $Tuple2(nil, [0, usdnTotalAmtStaked, 0])
720+ else {
721+ let userAddress = toAddressOrFail(userAddressStrOrEmpty)
722+ let mergedData = mergeStake(userAddressStrOrEmpty, usdnDiff)
723+ let isNewUser = mergedData._1
724+ let usdnStakedByUser = mergedData._2
725+ let stakingStartHeight = mergedData._3
726+ let stakedAmountNEW = mergedData._4
727+ $Tuple2(nil, [usdnStakedByUser, usdnTotalAmtStaked])
728+ }
729+ }
730+
731+
732+
733+@Callable(i)
734+func configSYSREADONLY () = {
735+ let minLockAmt = getIntegerValue(keyMinLockAmount())
736+ $Tuple2(nil, [minLockAmt])
737+ }
738+
739+
740+
741+@Callable(i)
742+func lockNeutrinoSP (receiver,share) = commonStake(i.caller, i, false)
743+
744+
745+
746+@Callable(i)
747+func lockNeutrino () = commonStake(i.caller, i, false)
748+
749+
750+
751+@Callable(i)
752+func unlockNeutrino (unlockAmount,assetIdString) = commonUnstake(unlockAmount, i, false)
219753
220754
221755
Full:
OldNewDifferences
1-{-# STDLIB_VERSION 5 #-}
1+{-# STDLIB_VERSION 6 #-}
22 {-# SCRIPT_TYPE ACCOUNT #-}
33 {-# CONTENT_TYPE DAPP #-}
4-let revisionNum = "cbd0bdc8bbba91db64066b16a84913a4c965e23e"
4+let revisionNum = "ba12ce93df3b4a092532aded95e5288a4919c24a"
55
66 let SEP = "__"
77
8-func getNumberByKey (key) = valueOrElse(getInteger(this, key), 0)
8+let MULT6 = 1000000
99
10+let MULT8 = 100000000
1011
11-func getStringByKey (key) = valueOrElse(getString(this, key), "")
12+let MULTX6 = toBigInt(MULT6)
1213
14+let MULTX8 = toBigInt(MULT8)
1315
14-func getBoolByKey (key) = valueOrElse(getBoolean(this, key), false)
16+let MULTX18 = toBigInt(1000000000000000000)
1517
18+let WAVESIDSTR = "WAVES"
1619
17-func getNumberByAddressAndKey (address,key) = valueOrElse(getInteger(addressFromStringValue(address), key), 0)
20+let WAVESID = fromBase58String(WAVESIDSTR)
1821
19-
20-func getStringByAddressAndKey (address,key) = valueOrElse(getString(address, key), "")
21-
22+let DAYMILLIS = 86400000
2223
2324 let IdxControlCfgNeutrinoDapp = 1
2425
2526 let IdxControlCfgAuctionDapp = 2
2627
2728 let IdxControlCfgRpdDapp = 3
2829
2930 let IdxControlCfgMathDapp = 4
3031
3132 let IdxControlCfgLiquidationDapp = 5
3233
3334 let IdxControlCfgRestDapp = 6
3435
3536 let IdxControlCfgNodeRegistryDapp = 7
3637
3738 let IdxControlCfgNsbtStakingDapp = 8
3839
3940 let IdxControlCfgMediatorDapp = 9
4041
42+let IdxControlCfgSurfStakingDapp = 10
43+
44+let IdxControlCfgGnsbtControllerDapp = 11
45+
4146 func getStringOrFail (address,key) = valueOrErrorMessage(getString(address, key), (((("mandatory " + toString(address)) + ".") + key) + " is not defined"))
47+
48+
49+func getIntOrFail (key) = valueOrErrorMessage(getInteger(this, key), (("Mandatory this." + key) + " is not defined"))
50+
51+
52+func getStrOrElse (key,defaultVal) = valueOrElse(getString(this, key), defaultVal)
53+
54+
55+func keyMinLockAmount () = "%s__minLockAmount"
56+
57+
58+func keyStakedAssetId () = "%s__stakedAssetId"
4259
4360
4461 func keyControlAddress () = "%s%s__config__controlAddress"
4562
4663
4764 func keyControlCfg () = "%s__controlConfig"
4865
4966
50-func readControlCfgOrFail (control) = split(getStringOrFail(control, keyControlCfg()), SEP)
67+func keySupportedRewardAssets () = "supportedRewardAssets"
68+
69+
70+func readControlCfgOrFail (control) = split_4C(getStringOrFail(control, keyControlCfg()), SEP)
5171
5272
5373 func getContractAddressOrFail (controlCfg,idx) = valueOrErrorMessage(addressFromString(controlCfg[idx]), ("Control cfg doesn't contain address at index " + toString(idx)))
5474
5575
5676 let controlContract = addressFromStringValue(valueOrElse(getString(this, keyControlAddress()), "3P5Bfd58PPfNvBM2Hy8QfbcDqMeNtzg7KfP"))
5777
5878 let controlCfg = readControlCfgOrFail(controlContract)
5979
80+let mathContract = getContractAddressOrFail(controlCfg, IdxControlCfgMathDapp)
81+
6082 let neutrinoContract = getContractAddressOrFail(controlCfg, IdxControlCfgNeutrinoDapp)
83+
84+let auctionContract = getContractAddressOrFail(controlCfg, IdxControlCfgAuctionDapp)
85+
86+let stakedAssetIdStr = getStringOrFail(this, keyStakedAssetId())
87+
88+let stakedAssetId = fromBase58String(stakedAssetIdStr)
89+
90+let minLockAmount = getIntOrFail(keyMinLockAmount())
91+
92+let supportedAssetsStr = getStrOrElse(keySupportedRewardAssets(), "")
93+
94+let supportedAssetsList = split(supportedAssetsStr, "_")
95+
96+func keyLockParamUserAmount (userAddress) = makeString(["%s%s%s", "paramByUser", userAddress, "amount"], SEP)
97+
98+
99+func keyLockParamStartBlock (userAddress) = makeString(["%s%s%s", "paramByUser", userAddress, "start"], SEP)
100+
101+
102+func keyHistoryRecord (type,userAddress,txId) = makeString(["%s%s%s%s", "history", type, userAddress, toBase58String(txId)], SEP)
103+
104+
105+func keyLockParamTotalAmount () = makeString(["%s%s", "stats", "activeTotalLocked"], SEP)
106+
107+
108+func keyStatsLocksCount () = makeString(["%s%s", "stats", "locksCount"], SEP)
109+
110+
111+func keyStatsUsersCount () = makeString(["%s%s", "stats", "activeUsersCount"], SEP)
112+
113+
114+func keyStatsDepositAmtByDay (timestamp) = makeString(["%s%s%d", "stats", "depositAmtByDay", toString(timestamp)], SEP)
115+
116+
117+func keyStatsDepositAmtTotals () = makeString(["%s%s%d", "stats", "depositAmtTotals"], SEP)
118+
119+
120+func keyNextPeriod () = "%s__nextPeriod"
121+
122+
123+func keyDepositNumLast () = makeString(["%s%s%s", "dep", "lastNum"], SEP)
124+
125+
126+func keyUserRewardFromDepositNum (userAddress) = makeString(["%s%s%s", "userRwdFromDepNum", userAddress], SEP)
127+
128+
129+func keyRewardPerNsbtSumAt (depositNum,tkn) = makeString(["%s%d", "rwdPerNsbtSumByDepNum", toString(depositNum), tkn], SEP)
130+
131+
132+func keyReward (userAddress,tkn) = makeString(["%s%s%s", "rwd", userAddress, tkn], SEP)
133+
134+
135+func keyClaimed (userAddress,tkn) = makeString(["%s%s%s", "clm", userAddress, tkn], SEP)
136+
137+
138+func keyNotDistributedReward (tkn) = makeString(["%s%s", "notDistributed", tkn], SEP)
139+
140+
141+func keyLegacyUserBalance (userAddr,tkn) = makeString(["rpd_balance", tkn, userAddr], "_")
142+
143+
144+func keyLegacyTotalBalance (tkn) = makeString(["rpd_balance", tkn], "_")
145+
146+
147+func toX18 (origVal,origMult) = fraction(toBigInt(origVal), MULTX18, origMult)
148+
149+
150+func getIntOrZero (key) = valueOrElse(getInteger(this, key), 0)
151+
152+
153+func getIntOrElse (key,defaultVal) = valueOrElse(getInteger(this, key), defaultVal)
154+
155+
156+func toAddressOrFail (addressStr) = valueOrErrorMessage(addressFromString(addressStr), ("couldn't parse passed addressStr=" + addressStr))
157+
158+
159+func toAssetVect (assetStr) = if ((assetStr == WAVESIDSTR))
160+ then unit
161+ else fromBase58String(assetStr)
162+
163+
164+func asInt (val) = match val {
165+ case valInt: Int =>
166+ valInt
167+ case _ =>
168+ throw("fail to cast into Int")
169+}
170+
171+
172+func asSwapParamsSTRUCT (v) = match v {
173+ case struct: (Int, Int, Int, Int, Int, Int, Int) =>
174+ struct
175+ case _ =>
176+ throw("fail to cast into Int")
177+}
178+
179+
180+func formatHistoryRecord (userAddress,oldAmount,newAmount) = makeString(["%s%d%d%d%d", userAddress, toString(lastBlock.height), toString(lastBlock.timestamp), toString(oldAmount), toString(newAmount)], SEP)
181+
182+
183+func formatClaimHistoryRecord (userAddress,claimedRewards) = makeString(["%s%d%d%s", userAddress, toString(lastBlock.height), toString(lastBlock.timestamp), claimedRewards], SEP)
184+
185+
186+func HistoryRecordEntry (type,userAddress,txId,oldAmount,newAmount) = StringEntry(keyHistoryRecord(type, userAddress, txId), formatHistoryRecord(userAddress, oldAmount, newAmount))
187+
188+
189+func ClaimHistoryEntry (userAddress,txId,claimedRewards) = StringEntry(keyHistoryRecord("claim", userAddress, txId), formatClaimHistoryRecord(userAddress, claimedRewards))
190+
191+
192+func StatsResult (totalLockedInc,lockCountInc,usersCountInc,isMigration) = {
193+ let locksCount = getIntOrZero(keyStatsLocksCount())
194+ let usersCount = getIntOrZero(keyStatsUsersCount())
195+ let totalAmount = getIntOrZero(keyLockParamTotalAmount())
196+ let totalAmountNew = (totalAmount + totalLockedInc)
197+ $Tuple3(([IntegerEntry(keyStatsLocksCount(), (locksCount + lockCountInc)), IntegerEntry(keyStatsUsersCount(), (usersCount + usersCountInc)), IntegerEntry(keyLockParamTotalAmount(), totalAmountNew)] ++ (if (isMigration)
198+ then nil
199+ else [IntegerEntry(keyLegacyTotalBalance(stakedAssetIdStr), totalAmountNew)])), totalAmount, totalAmountNew)
200+ }
201+
202+
203+func LockParamsEntry (userAddress,amount,stakingStartHeight,isMigration) = ([IntegerEntry(keyLockParamUserAmount(userAddress), amount), IntegerEntry(keyLockParamStartBlock(userAddress), stakingStartHeight)] ++ (if (isMigration)
204+ then nil
205+ else [IntegerEntry(keyLegacyUserBalance(userAddress, stakedAssetIdStr), amount)]))
206+
207+
208+func getParamsOrFail () = $Tuple2(fromBase58String(getStringOrFail(this, keyStakedAssetId())), getIntOrFail(keyMinLockAmount()))
209+
210+
211+func isActiveUser (userAddress) = (getIntOrElse(keyLockParamUserAmount(userAddress), 0) > 0)
212+
213+
214+func getUserParamsOrUnit (userAddress) = if (isActiveUser(userAddress))
215+ then $Tuple3(false, getIntOrFail(keyLockParamUserAmount(userAddress)), getIntOrFail(keyLockParamStartBlock(userAddress)))
216+ else unit
217+
218+
219+func getUserParamsOrFail (userAddress) = valueOrErrorMessage(getUserParamsOrUnit(userAddress), (("User " + userAddress) + " is not defined"))
220+
221+
222+func calcReward (userAddress,assetId,stakedAmountX,depositNumUser,depositNumLast) = {
223+ let rewardPerNsbtSumLastKEY = keyRewardPerNsbtSumAt(depositNumLast, assetId)
224+ let sumLastX18 = parseBigIntValue(getStrOrElse(keyRewardPerNsbtSumAt(depositNumLast, assetId), "0"))
225+ let sumUserX18 = parseBigIntValue(getStrOrElse(keyRewardPerNsbtSumAt(depositNumUser, assetId), "0"))
226+ let rewardDynamicPart = toInt(fraction((sumLastX18 - sumUserX18), stakedAmountX, MULTX18))
227+ let rewardCachedPartKEY = keyReward(userAddress, assetId)
228+ let rewardCachedPart = getIntOrElse(rewardCachedPartKEY, 0)
229+ $Tuple4((rewardCachedPart + rewardDynamicPart), rewardCachedPart, rewardDynamicPart, rewardCachedPartKEY)
230+ }
231+
232+
233+func toStartOfDay (timestamp) = ((timestamp / DAYMILLIS) * DAYMILLIS)
234+
235+
236+func findElementPosition (src,element,sep) = {
237+ let elementStart = valueOrErrorMessage(indexOf(src, element), ((("there is no substring " + element) + " in ") + src))
238+ if ((elementStart == 0))
239+ then 0
240+ else {
241+ let left = take(src, elementStart)
242+ (size(split(left, sep)) - 1)
243+ }
244+ }
245+
246+
247+let DepositTotalsPREFIX = "%d%d"
248+
249+func updateDepositTotals (currVal,idxToUpdate,deltaAmt) = {
250+ let currArr = split(currVal, SEP)
251+ func updDepTotByIdx (idx) = if ((idx != idxToUpdate))
252+ then currArr[idx]
253+ else toString((parseIntValue(currArr[idx]) + deltaAmt))
254+
255+ makeString([DepositTotalsPREFIX, updDepTotByIdx(1), updDepTotByIdx(2)], SEP)
256+ }
257+
258+
259+func DepositsTotalsEntries (depositAmount,assetIdStr) = {
260+ let startOfDay = toStartOfDay(lastBlock.timestamp)
261+ let byDayKEY = keyStatsDepositAmtByDay(startOfDay)
262+ let totalsKEY = keyStatsDepositAmtTotals()
263+ let position = findElementPosition(supportedAssetsStr, assetIdStr, "_")
264+ let defaultDATA = (DepositTotalsPREFIX + "__0__0")
265+ let currTotalsDATA = valueOrElse(getString(this, totalsKEY), defaultDATA)
266+ let newTotalsDATA = updateDepositTotals(currTotalsDATA, (position + 1), depositAmount)
267+[StringEntry(totalsKEY, newTotalsDATA), StringEntry(byDayKEY, newTotalsDATA)]
268+ }
269+
270+
271+func RewardEntries (isNewUser,userAddress,stakedAmount) = {
272+ let stakedAmountX = toBigInt(stakedAmount)
273+ let userRewardFromDepositNumKEY = keyUserRewardFromDepositNum(userAddress)
274+ let depositNumUser = getIntOrElse(userRewardFromDepositNumKEY, -1)
275+ let depositNumLast = getIntOrElse(keyDepositNumLast(), -1)
276+ func forEachAssetCacheUserReward (accum,asset) = {
277+ let $t01076510900 = calcReward(userAddress, asset, stakedAmountX, depositNumUser, depositNumLast)
278+ let rewardTotal = $t01076510900._1
279+ let cached = $t01076510900._2
280+ let dynamic = $t01076510900._3
281+ let rewardCachedPartKEY = $t01076510900._4
282+ (accum :+ IntegerEntry(rewardCachedPartKEY, rewardTotal))
283+ }
284+
285+ if (if ((depositNumLast == -1))
286+ then (depositNumUser == -1)
287+ else false)
288+ then nil
289+ else if (if ((depositNumLast == -1))
290+ then (depositNumUser > -1)
291+ else false)
292+ then throw("invalid depositNumLast and depositNumUser state")
293+ else if (if ((depositNumLast > -1))
294+ then (depositNumUser >= -1)
295+ else false)
296+ then if (isNewUser)
297+ then [IntegerEntry(userRewardFromDepositNumKEY, depositNumLast)]
298+ else ({
299+ let $l = supportedAssetsList
300+ let $s = size($l)
301+ let $acc0 = nil
302+ func $f0_1 ($a,$i) = if (($i >= $s))
303+ then $a
304+ else forEachAssetCacheUserReward($a, $l[$i])
305+
306+ func $f0_2 ($a,$i) = if (($i >= $s))
307+ then $a
308+ else throw("List size exceeds 2")
309+
310+ $f0_2($f0_1($f0_1($acc0, 0), 1), 2)
311+ } :+ IntegerEntry(userRewardFromDepositNumKEY, depositNumLast))
312+ else throw(((("uncovered condition: depositNumLast=" + toString(depositNumLast)) + " depositNumUser=") + toString(depositNumUser)))
313+ }
314+
315+
316+func IncrementNotDistributedRewardEntry (tkn,amountInc) = {
317+ let notDistributedRewardKEY = keyNotDistributedReward(tkn)
318+ let notDistributedReward = getIntOrElse(notDistributedRewardKEY, 0)
319+[IntegerEntry(notDistributedRewardKEY, (notDistributedReward + amountInc))]
320+ }
321+
322+
323+func mergeStake (userAddress,amountToAdd) = {
324+ let $t01372413840 = valueOrElse(getUserParamsOrUnit(userAddress), $Tuple3(true, 0, height))
325+ let isNewUser = $t01372413840._1
326+ let stakedAmount = $t01372413840._2
327+ let stakingStartHeight = $t01372413840._3
328+ let stakedAmountNEW = if (isNewUser)
329+ then amountToAdd
330+ else (amountToAdd + stakedAmount)
331+ $Tuple4(isNewUser, stakedAmount, stakingStartHeight, stakedAmountNEW)
332+ }
333+
334+
335+func isUsdnStakingMigrationDone () = {
336+ let legacyTotalBalance = getIntOrElse(keyLegacyTotalBalance(stakedAssetIdStr), 0)
337+ let totalBalance = getIntOrElse(keyLockParamTotalAmount(), 0)
338+ (legacyTotalBalance == totalBalance)
339+ }
340+
341+
342+func failIfUsdnMigrationNotDone () = if (isUsdnStakingMigrationDone())
343+ then true
344+ else throw("USDN staking migration is IN PROGRESS. All operations are temporary suspended.")
345+
346+
347+func commonStake (userAddress,i,isMigration) = {
348+ let migCheck = if (!(isMigration))
349+ then failIfUsdnMigrationNotDone()
350+ else true
351+ if ((migCheck == migCheck))
352+ then if ((size(i.payments) != 1))
353+ then throw("Invalid payments size")
354+ else {
355+ let payment = i.payments[0]
356+ let amount = payment.amount
357+ let invalidAssetMessage = (("Invalid asset. " + toBase58String(stakedAssetId)) + " is expected")
358+ let assetId = valueOrErrorMessage(payment.assetId, invalidAssetMessage)
359+ if ((assetId != stakedAssetId))
360+ then throw(invalidAssetMessage)
361+ else {
362+ let userAddressStr = toString(userAddress)
363+ let mergedData = mergeStake(userAddressStr, amount)
364+ let isNewUser = mergedData._1
365+ let stakedAmount = mergedData._2
366+ let stakingStartHeight = mergedData._3
367+ let stakedAmountNEW = mergedData._4
368+ if ((minLockAmount > stakedAmountNEW))
369+ then throw(("Min lock amount is " + toString(minLockAmount)))
370+ else {
371+ let $t01532315438 = StatsResult(amount, 1, if (isNewUser)
372+ then 1
373+ else 0, isMigration)
374+ let statsEntries = $t01532315438._1
375+ let totalStaked = $t01532315438._2
376+ let totalStakedNew = $t01532315438._3
377+ ((([HistoryRecordEntry("stake", userAddressStr, i.transactionId, stakedAmount, stakedAmountNEW)] ++ RewardEntries(isNewUser, userAddressStr, stakedAmount)) ++ LockParamsEntry(userAddressStr, stakedAmountNEW, stakingStartHeight, isMigration)) ++ statsEntries)
378+ }
379+ }
380+ }
381+ else throw("Strict value is not equal to itself.")
382+ }
383+
384+
385+func commonUnstake (amount,i,isMigration) = {
386+ let migrationCheck = failIfUsdnMigrationNotDone()
387+ if ((migrationCheck == migrationCheck))
388+ then if ((size(i.payments) != 0))
389+ then throw("unstake doesn't require any payment")
390+ else {
391+ let userAddress = i.caller
392+ let userAddressStr = toString(userAddress)
393+ let $t01600316091 = getUserParamsOrFail(userAddressStr)
394+ let isNewUser = $t01600316091._1
395+ let stakedAmount = $t01600316091._2
396+ let stakingStartHeight = $t01600316091._3
397+ let swapParamsSTRUCT = asSwapParamsSTRUCT(reentrantInvoke(neutrinoContract, "swapParamsByUserSYSREADONLY", [userAddressStr, 0], nil))
398+ let swapLimitSpentInUsdn = swapParamsSTRUCT._2
399+ let blcks2LmtReset = swapParamsSTRUCT._3
400+ if ((swapLimitSpentInUsdn > 0))
401+ then throw((("You have already made a swap operation. Wait " + toString((height + blcks2LmtReset))) + " height to unstake"))
402+ else if ((0 >= stakedAmount))
403+ then throw("Nothing to unstake")
404+ else if ((amount > stakedAmount))
405+ then throw(((("Requested " + toString(amount)) + ", but staked only ") + toString(stakedAmount)))
406+ else {
407+ let stakedAmountNEW = (stakedAmount - amount)
408+ let $t01672916900 = StatsResult(-(amount), if ((amount == stakedAmount))
409+ then -1
410+ else 0, if ((amount == stakedAmount))
411+ then -1
412+ else 0, isMigration)
413+ let statsEntries = $t01672916900._1
414+ let totalStaked = $t01672916900._2
415+ let totalStakedNew = $t01672916900._3
416+ ((([ScriptTransfer(userAddress, amount, stakedAssetId), HistoryRecordEntry("unstake", userAddressStr, i.transactionId, stakedAmount, stakedAmountNEW)] ++ RewardEntries(false, userAddressStr, stakedAmount)) ++ LockParamsEntry(userAddressStr, stakedAmountNEW, stakingStartHeight, isMigration)) ++ statsEntries)
417+ }
418+ }
419+ else throw("Strict value is not equal to itself.")
420+ }
421+
422+
423+func commonClaim (userAddress,i) = {
424+ let migrationCheck = failIfUsdnMigrationNotDone()
425+ if ((migrationCheck == migrationCheck))
426+ then {
427+ let userAddressStr = toString(userAddress)
428+ if ((size(i.payments) > 0))
429+ then throw("payments are not accepted")
430+ else {
431+ let $t01746417572 = valueOrElse(getUserParamsOrUnit(userAddressStr), $Tuple3(true, 0, 0))
432+ let isNewUser = $t01746417572._1
433+ let stakedAmount = $t01746417572._2
434+ let stakingStart = $t01746417572._3
435+ let stakedAmountX = toBigInt(stakedAmount)
436+ let userRewardFromDepositNumKEY = keyUserRewardFromDepositNum(userAddressStr)
437+ let depositNumUser = getIntOrElse(userRewardFromDepositNumKEY, -1)
438+ let depositNumLast = getIntOrElse(keyDepositNumLast(), -1)
439+ func forEachAssetCalcUnclaimedReward (accum,asset) = {
440+ let $t01794318081 = calcReward(userAddressStr, asset, stakedAmountX, depositNumUser, depositNumLast)
441+ let rewardTotal = $t01794318081._1
442+ let cached = $t01794318081._2
443+ let dynamic = $t01794318081._3
444+ let rewardCachedPartKEY = $t01794318081._4
445+ let claimedKEY = keyClaimed(userAddressStr, asset)
446+ let $t01814118178 = accum
447+ let data = $t01814118178._1
448+ let claimedAmtByAsset = $t01814118178._2
449+ let newPart = makeString([asset, toString(rewardTotal)], ":")
450+ let claimedAmtByAssetNew = makeString([claimedAmtByAsset, newPart], "_")
451+ if ((0 >= rewardTotal))
452+ then $Tuple2(data, claimedAmtByAssetNew)
453+ else $Tuple2((((data :+ ScriptTransfer(userAddress, rewardTotal, toAssetVect(asset))) :+ IntegerEntry(claimedKEY, (valueOrElse(getInteger(claimedKEY), 0) + rewardTotal))) :+ IntegerEntry(rewardCachedPartKEY, 0)), claimedAmtByAssetNew)
454+ }
455+
456+ let $t01863818751 = {
457+ let $l = supportedAssetsList
458+ let $s = size($l)
459+ let $acc0 = $Tuple2(nil, "")
460+ func $f0_1 ($a,$i) = if (($i >= $s))
461+ then $a
462+ else forEachAssetCalcUnclaimedReward($a, $l[$i])
463+
464+ func $f0_2 ($a,$i) = if (($i >= $s))
465+ then $a
466+ else throw("List size exceeds 2")
467+
468+ $f0_2($f0_1($f0_1($acc0, 0), 1), 2)
469+ }
470+ let transfers = $t01863818751._1
471+ let claimedAmtByAssetResult = $t01863818751._2
472+ if ((0 >= size(transfers)))
473+ then $Tuple2(nil, 0)
474+ else $Tuple2(((transfers :+ IntegerEntry(userRewardFromDepositNumKEY, depositNumLast)) :+ ClaimHistoryEntry(userAddressStr, i.transactionId, drop(claimedAmtByAssetResult, 1))), size(transfers))
475+ }
476+ }
477+ else throw("Strict value is not equal to itself.")
478+ }
479+
480+
481+let USDNTYPE = "USDN"
482+
483+let NSBTTYPE = "NSBT"
61484
62485 let NeutrinoAssetIdKey = "neutrino_asset_id"
63486
64487 let NeutrinoContractKey = "neutrino_contract"
65488
489+let NsbtAssetIdKey = "bond_asset_id"
490+
66491 let BalanceKey = "rpd_balance"
67492
68-let ControlContractKey = "control_contract"
493+let neutrinoAssetId = fromBase58String(getStringOrFail(neutrinoContract, NeutrinoAssetIdKey))
69494
70-let NsbtAssetIdKey = "bond_asset_id"
495+let nsbtAssetIdStr = getStringOrFail(neutrinoContract, NsbtAssetIdKey)
71496
72-let AdminsKey = "admins"
73-
74-let USDNTYPE = "USDN"
75-
76-let NSBTTYPE = "NSBT"
497+let nsbtAssetId = fromBase58String(nsbtAssetIdStr)
77498
78499 func getUserBalanceKey (owner,assetId) = makeString([BalanceKey, assetId, owner], "_")
79500
80501
81502 func getContractBalanceKey (assetId) = ((BalanceKey + "_") + assetId)
82503
83504
84-func getExpireProposalKey (hash) = (("proposal_expire" + "_") + hash)
505+func getContractBalance (assetId) = getIntOrElse(getContractBalanceKey(assetId), 0)
85506
86507
87-func getOwnerProposalKey (hash) = (("proposal_owner" + "_") + hash)
88-
89-
90-func getArgumentsProposalKey (hash) = (("proposal_arguments" + "_") + hash)
91-
92-
93-func getVoteKey (owner,hash) = (((("proposal_vote" + "_") + owner) + "_") + hash)
94-
95-
96-func convertJsonArrayToList (jsonArray) = split(jsonArray, ",")
97-
98-
99-let neutrinoAssetId = fromBase58String(getStringByAddressAndKey(neutrinoContract, NeutrinoAssetIdKey))
100-
101-let nsbtAssetIdStr = getStringByAddressAndKey(neutrinoContract, NsbtAssetIdKey)
102-
103-let nsbtAssetId = fromBase58String(nsbtAssetIdStr)
104-
105-func getContractBalance (assetId) = getNumberByKey(getContractBalanceKey(assetId))
106-
107-
108-func getUserBalance (owner,assetId) = getNumberByKey(getUserBalanceKey(owner, assetId))
109-
110-
111-func getRewardsConfigKey (owner,share,receiver) = makeString(["stakingconfig", owner, toString(share), receiver], "_")
112-
113-
114-func getCurrentRewardsConfigKey (owner) = ("stakingconfig_current_" + owner)
115-
116-
117-func getRewardsConfigStartKey (configKey,isStart) = (configKey + (if (isStart)
118- then "_start"
119- else "_end"))
120-
121-
122-func getRewardConfigInitialShare (owner) = (owner + "_initialShare")
508+func getUserBalance (owner,assetId) = getIntOrElse(getUserBalanceKey(owner, assetId), 0)
123509
124510
125511 func getValidStakingAssetOrFail (stakingType,assetId) = if (if ((stakingType == USDNTYPE))
126512 then (assetId != neutrinoAssetId)
127513 else false)
128514 then throw("can use USDN only")
129515 else if (if ((stakingType == NSBTTYPE))
130516 then (assetId != nsbtAssetId)
131517 else false)
132518 then throw("can use NSBT only")
133519 else if (if ((stakingType != USDNTYPE))
134520 then (stakingType != NSBTTYPE)
135521 else false)
136522 then throw(("unsupported staking type " + stakingType))
137523 else assetId
138524
139525
140-func internalLockNeutrino (stakingType,i,receiver,share) = {
141- let pmt = value(i.payments[0])
142- let assetId = getValidStakingAssetOrFail(stakingType, value(pmt.assetId))
143- if (!(isDefined(addressFromString(receiver))))
144- then throw(("Invalid address format " + receiver))
145- else if ((share > 100))
146- then throw("staking rewards share cannot be higher than 100%")
147- else if ((1 > share))
148- then throw("staking rewards share cannot be lower than 1%")
149- else {
150- let account = toString(i.caller)
151- let assetIdString = toBase58String(assetId)
152- let currentConfig = getStringByKey(getCurrentRewardsConfigKey(account))
153- let correctData = if ((currentConfig != ""))
154- then {
155- let currentConfigData = split(currentConfig, "_")
156- let currShare = parseIntValue(currentConfigData[2])
157- let currReceiver = currentConfigData[3]
158- let notMigratedInitialShare = getNumberByKey(getRewardConfigInitialShare(account))
159- let actualInitialShare = if ((notMigratedInitialShare == 0))
160- then currShare
161- else notMigratedInitialShare
162- let newShare = if (if ((actualInitialShare > share))
163- then true
164- else (currReceiver != receiver))
165- then actualInitialShare
166- else share
167-[toString(actualInitialShare), toString(newShare), currReceiver]
168- }
169- else [toString(share), toString(share), receiver]
170- let correctInitialShare = parseIntValue(correctData[0])
171- let correctShare = parseIntValue(correctData[1])
172- let correctReceiver = correctData[2]
173- let newCurrentConfig = getRewardsConfigKey(account, correctShare, correctReceiver)
174- let isNewConfig = !((currentConfig == newCurrentConfig))
175- let end = if (isNewConfig)
176- then height
177- else 0
178- let start = if (isNewConfig)
179- then height
180- else getNumberByKey(getRewardsConfigStartKey(newCurrentConfig, true))
181- $Tuple2([IntegerEntry(getContractBalanceKey(assetIdString), (getContractBalance(assetIdString) + pmt.amount)), IntegerEntry(getUserBalanceKey(account, assetIdString), (getUserBalance(account, assetIdString) + pmt.amount)), IntegerEntry(getRewardsConfigStartKey(currentConfig, false), end), IntegerEntry(getRewardsConfigStartKey(newCurrentConfig, true), start), IntegerEntry(getRewardConfigInitialShare(account), correctInitialShare), StringEntry(getCurrentRewardsConfigKey(account), newCurrentConfig)], unit)
182- }
183- }
184-
185-
186526 func internalUnlock (stakingType,i,unlockAmount,assetIdParam) = {
187527 let account = toString(i.caller)
188528 let assetId = getValidStakingAssetOrFail(stakingType, fromBase58String(assetIdParam))
189529 let assetIdString = toBase58String(assetId)
190530 let balance = (getUserBalance(account, assetIdString) - unlockAmount)
191531 if ((0 > balance))
192532 then throw("invalid amount")
193533 else $Tuple2([IntegerEntry(getContractBalanceKey(assetIdString), (getContractBalance(assetIdString) - unlockAmount)), IntegerEntry(getUserBalanceKey(account, assetIdString), balance), ScriptTransfer(addressFromStringValue(account), unlockAmount, assetId)], unit)
194534 }
195535
196536
197537 @Callable(i)
198-func lockNeutrinoSP (receiver,share) = internalLockNeutrino(USDNTYPE, i, receiver, share)
538+func constructor (minLockAmount,supportedRewardAssets,pStakedAssetId) = if ((i.caller != this))
539+ then throw("Permission denied")
540+ else [IntegerEntry(keyMinLockAmount(), minLockAmount), StringEntry(keySupportedRewardAssets(), supportedRewardAssets), StringEntry(keyStakedAssetId(), pStakedAssetId)]
199541
200542
201543
202544 @Callable(i)
203-func lockNeutrino () = internalLockNeutrino(USDNTYPE, i, toString(i.caller), 100)
545+func migrateUsdnStaking (userAddressStr) = {
546+ let mngPubS = valueOrElse(getString("%s%s__cfg__leasingManagerPub"), "7AUMX54ukYMYvPmma7yoFf5NjZhs4Bu5nz3Ez9EV8sur")
547+ let mngPub = fromBase58String(mngPubS)
548+ if ((i.callerPublicKey != mngPub))
549+ then throw("migrateUsdnStaking not authorized")
550+ else if (isUsdnStakingMigrationDone())
551+ then throw("migration has been done")
552+ else if ((size(i.payments) != 0))
553+ then throw("payments are not allowed")
554+ else if ((i.feeAssetId != unit))
555+ then throw("fee in WAVES is allowed only")
556+ else if ((i.fee != 500000))
557+ then throw("0.005 WAVES fee is allowed only")
558+ else {
559+ let legacyUserBalance = getIntOrElse(keyLegacyUserBalance(userAddressStr, stakedAssetIdStr), 0)
560+ if ((legacyUserBalance == 0))
561+ then throw(("no need to migrate user " + userAddressStr))
562+ else if (isActiveUser(userAddressStr))
563+ then throw(("already migrated user " + userAddressStr))
564+ else {
565+ let userAddress = addressFromStringValue(userAddressStr)
566+ let emptyVect = fromBase58String("")
567+ commonStake(userAddress, Invocation([AttachedPayment(stakedAssetId, legacyUserBalance)], userAddress, emptyVect, i.transactionId, 0, unit, userAddress, emptyVect), true)
568+ }
569+ }
570+ }
204571
205572
206573
207574 @Callable(i)
208-func lockNsbtSP (receiver,share) = internalLockNeutrino(NSBTTYPE, i, receiver, share)
575+func stake () = commonStake(i.caller, i, false)
209576
210577
211578
212579 @Callable(i)
213-func lockNsbt () = internalLockNeutrino(NSBTTYPE, i, toString(i.caller), 100)
580+func stakeByOriginCaller () = commonStake(i.originCaller, i, false)
214581
215582
216583
217584 @Callable(i)
218-func unlockNeutrino (unlockAmount,assetIdString) = internalUnlock(USDNTYPE, i, unlockAmount, assetIdString)
585+func unstake (amount) = commonUnstake(amount, i, false)
586+
587+
588+
589+@Callable(i)
590+func deposit () = {
591+ let migrationCheck = failIfUsdnMigrationNotDone()
592+ if ((migrationCheck == migrationCheck))
593+ then if ((size(i.payments) != 1))
594+ then throw("exact 1 payment is allowed only")
595+ else {
596+ let pmt = i.payments[0]
597+ let amount = pmt.amount
598+ let pmtAssetId = valueOrElse(pmt.assetId, WAVESID)
599+ let pmtAssetIdStr = toBase58String(pmtAssetId)
600+ let pmtMultX = if ((pmtAssetId == WAVESID))
601+ then MULTX8
602+ else MULTX6
603+ let amountX = toBigInt(amount)
604+ let totalStaked = getIntOrElse(keyLockParamTotalAmount(), 0)
605+ let totalStakedX = toBigInt(totalStaked)
606+ if ((0 > totalStaked))
607+ then throw("TODO: case is not supported")
608+ else if ((totalStaked == 0))
609+ then IncrementNotDistributedRewardEntry(pmtAssetIdStr, amount)
610+ else {
611+ let rewardPerNsbtX18 = fraction(amountX, MULTX18, totalStakedX)
612+ let depositNumLastKEY = keyDepositNumLast()
613+ let depositNumLast = getIntOrElse(depositNumLastKEY, -1)
614+ let depositNumNew = (depositNumLast + 1)
615+ if (!(contains(supportedAssetsStr, pmtAssetIdStr)))
616+ then throw(((supportedAssetsStr + " doesn't contain ") + pmtAssetIdStr))
617+ else {
618+ func refreshRewardPerNsbtSUM (accum,nextAsset) = {
619+ let rewardPerNsbtSumNewKEY = keyRewardPerNsbtSumAt(depositNumNew, nextAsset)
620+ let sumLastStr = getStrOrElse(keyRewardPerNsbtSumAt(depositNumLast, nextAsset), "0")
621+ (accum :+ (if ((nextAsset == pmtAssetIdStr))
622+ then StringEntry(rewardPerNsbtSumNewKEY, toString((parseBigIntValue(sumLastStr) + rewardPerNsbtX18)))
623+ else StringEntry(rewardPerNsbtSumNewKEY, sumLastStr)))
624+ }
625+
626+ (({
627+ let $l = supportedAssetsList
628+ let $s = size($l)
629+ let $acc0 = nil
630+ func $f0_1 ($a,$i) = if (($i >= $s))
631+ then $a
632+ else refreshRewardPerNsbtSUM($a, $l[$i])
633+
634+ func $f0_2 ($a,$i) = if (($i >= $s))
635+ then $a
636+ else throw("List size exceeds 2")
637+
638+ $f0_2($f0_1($f0_1($acc0, 0), 1), 2)
639+ } :+ IntegerEntry(depositNumLastKEY, depositNumNew)) ++ DepositsTotalsEntries(amount, pmtAssetIdStr))
640+ }
641+ }
642+ }
643+ else throw("Strict value is not equal to itself.")
644+ }
645+
646+
647+
648+@Callable(i)
649+func claimRewards () = commonClaim(i.caller, i)
650+
651+
652+
653+@Callable(i)
654+func claimRewardsByOriginCaller () = commonClaim(i.originCaller, i)
655+
656+
657+
658+@Callable(i)
659+func unclaimedRewardsREADONLY (userAddressStr) = {
660+ func forEachAssetZeroReward (accum,asset) = ((accum + makeString([asset, "0", "0"], ":")) + "_")
661+
662+ let unclaimedRewardStr = if ((userAddressStr == ""))
663+ then {
664+ let $l = supportedAssetsList
665+ let $s = size($l)
666+ let $acc0 = ""
667+ func $f0_1 ($a,$i) = if (($i >= $s))
668+ then $a
669+ else forEachAssetZeroReward($a, $l[$i])
670+
671+ func $f0_2 ($a,$i) = if (($i >= $s))
672+ then $a
673+ else throw("List size exceeds 2")
674+
675+ $f0_2($f0_1($f0_1($acc0, 0), 1), 2)
676+ }
677+ else {
678+ let userAddress = addressFromStringValue(userAddressStr)
679+ let $t02508225196 = valueOrElse(getUserParamsOrUnit(userAddressStr), $Tuple3(true, 0, 0))
680+ let isNewUser = $t02508225196._1
681+ let stakedAmount = $t02508225196._2
682+ let stakingStartHeight = $t02508225196._3
683+ let stakedAmountX = toBigInt(stakedAmount)
684+ let userRewardFromDepositNumKEY = keyUserRewardFromDepositNum(userAddressStr)
685+ let depositNumUser = getIntOrElse(userRewardFromDepositNumKEY, -1)
686+ let depositNumLast = getIntOrElse(keyDepositNumLast(), -1)
687+ func forEachAssetCalcUnclaimedReward (accum,asset) = {
688+ let $t02554225680 = calcReward(userAddressStr, asset, stakedAmountX, depositNumUser, depositNumLast)
689+ let rewardTotal = $t02554225680._1
690+ let cached = $t02554225680._2
691+ let dynamic = $t02554225680._3
692+ let rewardCachedPartKEY = $t02554225680._4
693+ let claimed = valueOrElse(getInteger(keyClaimed(userAddressStr, asset)), 0)
694+ ((accum + makeString([asset, toString(rewardTotal), toString(claimed)], ":")) + "_")
695+ }
696+
697+ let $l = supportedAssetsList
698+ let $s = size($l)
699+ let $acc0 = ""
700+ func $f0_1 ($a,$i) = if (($i >= $s))
701+ then $a
702+ else forEachAssetCalcUnclaimedReward($a, $l[$i])
703+
704+ func $f0_2 ($a,$i) = if (($i >= $s))
705+ then $a
706+ else throw("List size exceeds 2")
707+
708+ $f0_2($f0_1($f0_1($acc0, 0), 1), 2)
709+ }
710+ $Tuple2(nil, dropRight(unclaimedRewardStr, 1))
711+ }
712+
713+
714+
715+@Callable(i)
716+func usdnStakingSYSREADONLY (userAddressStrOrEmpty,usdnDiff) = {
717+ let usdnTotalAmtStaked = getIntOrElse(keyLockParamTotalAmount(), 0)
718+ if ((userAddressStrOrEmpty == ""))
719+ then $Tuple2(nil, [0, usdnTotalAmtStaked, 0])
720+ else {
721+ let userAddress = toAddressOrFail(userAddressStrOrEmpty)
722+ let mergedData = mergeStake(userAddressStrOrEmpty, usdnDiff)
723+ let isNewUser = mergedData._1
724+ let usdnStakedByUser = mergedData._2
725+ let stakingStartHeight = mergedData._3
726+ let stakedAmountNEW = mergedData._4
727+ $Tuple2(nil, [usdnStakedByUser, usdnTotalAmtStaked])
728+ }
729+ }
730+
731+
732+
733+@Callable(i)
734+func configSYSREADONLY () = {
735+ let minLockAmt = getIntegerValue(keyMinLockAmount())
736+ $Tuple2(nil, [minLockAmt])
737+ }
738+
739+
740+
741+@Callable(i)
742+func lockNeutrinoSP (receiver,share) = commonStake(i.caller, i, false)
743+
744+
745+
746+@Callable(i)
747+func lockNeutrino () = commonStake(i.caller, i, false)
748+
749+
750+
751+@Callable(i)
752+func unlockNeutrino (unlockAmount,assetIdString) = commonUnstake(unlockAmount, i, false)
219753
220754
221755
222756 @Callable(i)
223757 func unlockNsbt (unlockAmount,assetIdString) = internalUnlock(NSBTTYPE, i, unlockAmount, assetIdString)
224758
225759
226760 @Verifier(tx)
227761 func verify () = {
228762 let pubKeyAdminsListStr = makeString(["GJdLSaLiv5K7xuejac8mcRcHoyo3dPrESrvktG3a6MAR", "EYwZmURd5KKaQRBjsVa6g8DPisFoS6SovRJtFiL5gMHU", "DtmAfuDdCrHK8spdAeAYzq6MsZegeD9gnsrpuTRkCbVA", "5WRXFSjwcTbNfKcJs8ZqXmSSWYsSVJUtMvMqZj5hH4Nc"], SEP)
229763 let pubKeyAdminsList = split(valueOrElse(getString(controlContract, "%s__multisig"), pubKeyAdminsListStr), SEP)
230764 let count = ((((if (sigVerify(tx.bodyBytes, tx.proofs[0], fromBase58String(pubKeyAdminsList[0])))
231765 then 1
232766 else 0) + (if (sigVerify(tx.bodyBytes, tx.proofs[1], fromBase58String(pubKeyAdminsList[1])))
233767 then 1
234768 else 0)) + (if (sigVerify(tx.bodyBytes, tx.proofs[2], fromBase58String(pubKeyAdminsList[2])))
235769 then 1
236770 else 0)) + (if (sigVerify(tx.bodyBytes, tx.proofs[3], fromBase58String(pubKeyAdminsList[3])))
237771 then 2
238772 else 0))
239773 (count >= 3)
240774 }
241775

github/deemru/w8io/786bc32 
77.34 ms