tx · BAk3WBRB9ftqvAH6Qc357RPUTfCsoc2U7DRSgtvLWXb9

3N9LkJahTMx41wGhSxLS42prCZtRCp4dhTs:  -0.04000000 Waves

2022.10.19 14:08 [2279194] smart account 3N9LkJahTMx41wGhSxLS42prCZtRCp4dhTs > SELF 0.00000000 Waves

{ "type": 13, "id": "BAk3WBRB9ftqvAH6Qc357RPUTfCsoc2U7DRSgtvLWXb9", "fee": 4000000, "feeAssetId": null, "timestamp": 1666177735841, "version": 1, "sender": "3N9LkJahTMx41wGhSxLS42prCZtRCp4dhTs", "senderPublicKey": "8hm3x3tdBbKUu4XpjDGwNdaPA2qEdrdchHue4k49BsYJ", "proofs": [ "26MqkQ918oNZKSUoAnwtPjJqwoXThYvnjnCFtkgy6e5mpdeJM43e5yq2RaRxs6HA4Qsy6H3cCctctjSYATywkSnt", "65GQj2DpjTEtC1YjfvVLqiMA9Zvo7qorPXH33MaJg7SGNAMnJguVzbGw2vH5abKrfAaQw3Sr9DjQupS8ifmNHavG", "2St4DNFS3XDeb5k7Hiv3yefFpVwdGzwXLvFNFdS28SQ422ntm9ZKzEMhXx7DdquUhXR1CdoiGM1t7oJvp6wAJ477" ], "script": "base64:BgI5CAISBQoDAQgIEgASABIDCgEBEgASABIAEgMKAQgSBAoCCAESABIECgIIARIAEgQKAgEIEgQKAgEIZQADU0VQAgJfXwAFTVVMVDYAwIQ9AAVNVUxUOACAwtcvAAZNVUxUWDYJALYCAQUFTVVMVDYABk1VTFRYOAkAtgIBBQVNVUxUOAAHTVVMVFgxOAkAtgIBAICAkLu61q3wDQAKV0FWRVNJRFNUUgIFV0FWRVMAB1dBVkVTSUQJANkEAQUKV0FWRVNJRFNUUgAJREFZTUlMTElTAIC4mSkAGUlkeENvbnRyb2xDZmdOZXV0cmlub0RhcHAAAQAYSWR4Q29udHJvbENmZ0F1Y3Rpb25EYXBwAAIAFElkeENvbnRyb2xDZmdScGREYXBwAAMAFUlkeENvbnRyb2xDZmdNYXRoRGFwcAAEABxJZHhDb250cm9sQ2ZnTGlxdWlkYXRpb25EYXBwAAUAFUlkeENvbnRyb2xDZmdSZXN0RGFwcAAGAB1JZHhDb250cm9sQ2ZnTm9kZVJlZ2lzdHJ5RGFwcAAHABxJZHhDb250cm9sQ2ZnTnNidFN0YWtpbmdEYXBwAAgAGUlkeENvbnRyb2xDZmdNZWRpYXRvckRhcHAACQAcSWR4Q29udHJvbENmZ1N1cmZTdGFraW5nRGFwcAAKACBJZHhDb250cm9sQ2ZnR25zYnRDb250cm9sbGVyRGFwcAALAQ9nZXRTdHJpbmdPckZhaWwCB2FkZHJlc3MDa2V5CQETdmFsdWVPckVycm9yTWVzc2FnZQIJAJ0IAgUHYWRkcmVzcwUDa2V5CQCsAgIJAKwCAgkArAICCQCsAgICCm1hbmRhdG9yeSAJAKUIAQUHYWRkcmVzcwIBLgUDa2V5Ag8gaXMgbm90IGRlZmluZWQBDGdldEludE9yRmFpbAEDa2V5CQETdmFsdWVPckVycm9yTWVzc2FnZQIJAJoIAgUEdGhpcwUDa2V5CQCsAgIJAKwCAgIPTWFuZGF0b3J5IHRoaXMuBQNrZXkCDyBpcyBub3QgZGVmaW5lZAEMZ2V0U3RyT3JFbHNlAgNrZXkKZGVmYXVsdFZhbAkBC3ZhbHVlT3JFbHNlAgkAnQgCBQR0aGlzBQNrZXkFCmRlZmF1bHRWYWwBEGtleU1pbkxvY2tBbW91bnQAAhElc19fbWluTG9ja0Ftb3VudAEQa2V5U3Rha2VkQXNzZXRJZAACESVzX19zdGFrZWRBc3NldElkARFrZXlDb250cm9sQWRkcmVzcwACHCVzJXNfX2NvbmZpZ19fY29udHJvbEFkZHJlc3MBDWtleUNvbnRyb2xDZmcAAhElc19fY29udHJvbENvbmZpZwEYa2V5U3VwcG9ydGVkUmV3YXJkQXNzZXRzAAIVc3VwcG9ydGVkUmV3YXJkQXNzZXRzARRyZWFkQ29udHJvbENmZ09yRmFpbAEHY29udHJvbAkAvAkCCQEPZ2V0U3RyaW5nT3JGYWlsAgUHY29udHJvbAkBDWtleUNvbnRyb2xDZmcABQNTRVABGGdldENvbnRyYWN0QWRkcmVzc09yRmFpbAIKY29udHJvbENmZwNpZHgJARN2YWx1ZU9yRXJyb3JNZXNzYWdlAgkApggBCQCRAwIFCmNvbnRyb2xDZmcFA2lkeAkArAICAi1Db250cm9sIGNmZyBkb2Vzbid0IGNvbnRhaW4gYWRkcmVzcyBhdCBpbmRleCAJAKQDAQUDaWR4AA9jb250cm9sQ29udHJhY3QJARFAZXh0ck5hdGl2ZSgxMDYyKQEJAQt2YWx1ZU9yRWxzZQIJAJ0IAgUEdGhpcwkBEWtleUNvbnRyb2xBZGRyZXNzAAIjM040TlM3ZDRKbzlhNkYxNExpRlVLS1lWZFVra2YyZVA0WngACmNvbnRyb2xDZmcJARRyZWFkQ29udHJvbENmZ09yRmFpbAEFD2NvbnRyb2xDb250cmFjdAAMbWF0aENvbnRyYWN0CQEYZ2V0Q29udHJhY3RBZGRyZXNzT3JGYWlsAgUKY29udHJvbENmZwUVSWR4Q29udHJvbENmZ01hdGhEYXBwABBuZXV0cmlub0NvbnRyYWN0CQEYZ2V0Q29udHJhY3RBZGRyZXNzT3JGYWlsAgUKY29udHJvbENmZwUZSWR4Q29udHJvbENmZ05ldXRyaW5vRGFwcAAPYXVjdGlvbkNvbnRyYWN0CQEYZ2V0Q29udHJhY3RBZGRyZXNzT3JGYWlsAgUKY29udHJvbENmZwUYSWR4Q29udHJvbENmZ0F1Y3Rpb25EYXBwABBzdGFrZWRBc3NldElkU3RyCQEPZ2V0U3RyaW5nT3JGYWlsAgUEdGhpcwkBEGtleVN0YWtlZEFzc2V0SWQAAA1zdGFrZWRBc3NldElkCQDZBAEFEHN0YWtlZEFzc2V0SWRTdHIADW1pbkxvY2tBbW91bnQJAQxnZXRJbnRPckZhaWwBCQEQa2V5TWluTG9ja0Ftb3VudAAAEnN1cHBvcnRlZEFzc2V0c1N0cgkBDGdldFN0ck9yRWxzZQIJARhrZXlTdXBwb3J0ZWRSZXdhcmRBc3NldHMAAgAAE3N1cHBvcnRlZEFzc2V0c0xpc3QJALUJAgUSc3VwcG9ydGVkQXNzZXRzU3RyAgFfARZrZXlMb2NrUGFyYW1Vc2VyQW1vdW50AQt1c2VyQWRkcmVzcwkAuQkCCQDMCAICBiVzJXMlcwkAzAgCAgtwYXJhbUJ5VXNlcgkAzAgCCQClCAEFC3VzZXJBZGRyZXNzCQDMCAICBmFtb3VudAUDbmlsBQNTRVABFmtleUxvY2tQYXJhbVN0YXJ0QmxvY2sBC3VzZXJBZGRyZXNzCQC5CQIJAMwIAgIGJXMlcyVzCQDMCAICC3BhcmFtQnlVc2VyCQDMCAIJAKUIAQULdXNlckFkZHJlc3MJAMwIAgIFc3RhcnQFA25pbAUDU0VQARBrZXlIaXN0b3J5UmVjb3JkAwR0eXBlC3VzZXJBZGRyZXNzBHR4SWQJALkJAgkAzAgCAgglcyVzJXMlcwkAzAgCAgdoaXN0b3J5CQDMCAIFBHR5cGUJAMwIAgULdXNlckFkZHJlc3MJAMwIAgkA2AQBBQR0eElkBQNuaWwFA1NFUAEXa2V5TG9ja1BhcmFtVG90YWxBbW91bnQACQC5CQIJAMwIAgIEJXMlcwkAzAgCAgVzdGF0cwkAzAgCAhFhY3RpdmVUb3RhbExvY2tlZAUDbmlsBQNTRVABEmtleVN0YXRzTG9ja3NDb3VudAAJALkJAgkAzAgCAgQlcyVzCQDMCAICBXN0YXRzCQDMCAICCmxvY2tzQ291bnQFA25pbAUDU0VQARJrZXlTdGF0c1VzZXJzQ291bnQACQC5CQIJAMwIAgIEJXMlcwkAzAgCAgVzdGF0cwkAzAgCAhBhY3RpdmVVc2Vyc0NvdW50BQNuaWwFA1NFUAEXa2V5U3RhdHNEZXBvc2l0QW10QnlEYXkBCXRpbWVzdGFtcAkAuQkCCQDMCAICBiVzJXMlZAkAzAgCAgVzdGF0cwkAzAgCAg9kZXBvc2l0QW10QnlEYXkJAMwIAgkApAMBBQl0aW1lc3RhbXAFA25pbAUDU0VQARhrZXlTdGF0c0RlcG9zaXRBbXRUb3RhbHMACQC5CQIJAMwIAgIGJXMlcyVkCQDMCAICBXN0YXRzCQDMCAICEGRlcG9zaXRBbXRUb3RhbHMFA25pbAUDU0VQAQ1rZXlOZXh0UGVyaW9kAAIOJXNfX25leHRQZXJpb2QBEWtleURlcG9zaXROdW1MYXN0AAkAuQkCCQDMCAICBiVzJXMlcwkAzAgCAgNkZXAJAMwIAgIHbGFzdE51bQUDbmlsBQNTRVABG2tleVVzZXJSZXdhcmRGcm9tRGVwb3NpdE51bQELdXNlckFkZHJlc3MJALkJAgkAzAgCAgYlcyVzJXMJAMwIAgIRdXNlclJ3ZEZyb21EZXBOdW0JAMwIAgULdXNlckFkZHJlc3MFA25pbAUDU0VQARVrZXlSZXdhcmRQZXJOc2J0U3VtQXQCCmRlcG9zaXROdW0DdGtuCQC5CQIJAMwIAgIEJXMlZAkAzAgCAhVyd2RQZXJOc2J0U3VtQnlEZXBOdW0JAMwIAgkApAMBBQpkZXBvc2l0TnVtCQDMCAIFA3RrbgUDbmlsBQNTRVABCWtleVJld2FyZAILdXNlckFkZHJlc3MDdGtuCQC5CQIJAMwIAgIGJXMlcyVzCQDMCAICA3J3ZAkAzAgCBQt1c2VyQWRkcmVzcwkAzAgCBQN0a24FA25pbAUDU0VQAQprZXlDbGFpbWVkAgt1c2VyQWRkcmVzcwN0a24JALkJAgkAzAgCAgYlcyVzJXMJAMwIAgIDY2xtCQDMCAIFC3VzZXJBZGRyZXNzCQDMCAIFA3RrbgUDbmlsBQNTRVABF2tleU5vdERpc3RyaWJ1dGVkUmV3YXJkAQN0a24JALkJAgkAzAgCAgQlcyVzCQDMCAICDm5vdERpc3RyaWJ1dGVkCQDMCAIFA3RrbgUDbmlsBQNTRVABFGtleUxlZ2FjeVVzZXJCYWxhbmNlAgh1c2VyQWRkcgN0a24JALkJAgkAzAgCAgtycGRfYmFsYW5jZQkAzAgCBQN0a24JAMwIAgUIdXNlckFkZHIFA25pbAUDU0VQARVrZXlMZWdhY3lUb3RhbEJhbGFuY2UBA3RrbgkAuQkCCQDMCAICC3JwZF9iYWxhbmNlCQDMCAIFA3RrbgUDbmlsBQNTRVABBXRvWDE4AgdvcmlnVmFsCG9yaWdNdWx0CQC8AgMJALYCAQUHb3JpZ1ZhbAUHTVVMVFgxOAUIb3JpZ011bHQBDGdldEludE9yWmVybwEDa2V5CQELdmFsdWVPckVsc2UCCQCaCAIFBHRoaXMFA2tleQAAAQxnZXRJbnRPckVsc2UCA2tleQpkZWZhdWx0VmFsCQELdmFsdWVPckVsc2UCCQCaCAIFBHRoaXMFA2tleQUKZGVmYXVsdFZhbAEPdG9BZGRyZXNzT3JGYWlsAQphZGRyZXNzU3RyCQETdmFsdWVPckVycm9yTWVzc2FnZQIJAKYIAQUKYWRkcmVzc1N0cgkArAICAiFjb3VsZG4ndCBwYXJzZSBwYXNzZWQgYWRkcmVzc1N0cj0FCmFkZHJlc3NTdHIBC3RvQXNzZXRWZWN0AQhhc3NldFN0cgMJAAACBQhhc3NldFN0cgUKV0FWRVNJRFNUUgUEdW5pdAkA2QQBBQhhc3NldFN0cgEFYXNJbnQBA3ZhbAQHJG1hdGNoMAUDdmFsAwkAAQIFByRtYXRjaDACA0ludAQGdmFsSW50BQckbWF0Y2gwBQZ2YWxJbnQJAAIBAhVmYWlsIHRvIGNhc3QgaW50byBJbnQBEmFzU3dhcFBhcmFtc1NUUlVDVAEBdgQHJG1hdGNoMAUBdgMJAAECBQckbWF0Y2gwAiMoSW50LCBJbnQsIEludCwgSW50LCBJbnQsIEludCwgSW50KQQGc3RydWN0BQckbWF0Y2gwBQZzdHJ1Y3QJAAIBAhVmYWlsIHRvIGNhc3QgaW50byBJbnQBE2Zvcm1hdEhpc3RvcnlSZWNvcmQDC3VzZXJBZGRyZXNzCW9sZEFtb3VudAluZXdBbW91bnQJALkJAgkAzAgCAgolcyVkJWQlZCVkCQDMCAIFC3VzZXJBZGRyZXNzCQDMCAIJAKQDAQgFCWxhc3RCbG9jawZoZWlnaHQJAMwIAgkApAMBCAUJbGFzdEJsb2NrCXRpbWVzdGFtcAkAzAgCCQCkAwEFCW9sZEFtb3VudAkAzAgCCQCkAwEFCW5ld0Ftb3VudAUDbmlsBQNTRVABGGZvcm1hdENsYWltSGlzdG9yeVJlY29yZAILdXNlckFkZHJlc3MOY2xhaW1lZFJld2FyZHMJALkJAgkAzAgCAgglcyVkJWQlcwkAzAgCBQt1c2VyQWRkcmVzcwkAzAgCCQCkAwEIBQlsYXN0QmxvY2sGaGVpZ2h0CQDMCAIJAKQDAQgFCWxhc3RCbG9jawl0aW1lc3RhbXAJAMwIAgUOY2xhaW1lZFJld2FyZHMFA25pbAUDU0VQARJIaXN0b3J5UmVjb3JkRW50cnkFBHR5cGULdXNlckFkZHJlc3MEdHhJZAlvbGRBbW91bnQJbmV3QW1vdW50CQELU3RyaW5nRW50cnkCCQEQa2V5SGlzdG9yeVJlY29yZAMFBHR5cGUFC3VzZXJBZGRyZXNzBQR0eElkCQETZm9ybWF0SGlzdG9yeVJlY29yZAMFC3VzZXJBZGRyZXNzBQlvbGRBbW91bnQFCW5ld0Ftb3VudAERQ2xhaW1IaXN0b3J5RW50cnkDC3VzZXJBZGRyZXNzBHR4SWQOY2xhaW1lZFJld2FyZHMJAQtTdHJpbmdFbnRyeQIJARBrZXlIaXN0b3J5UmVjb3JkAwIFY2xhaW0FC3VzZXJBZGRyZXNzBQR0eElkCQEYZm9ybWF0Q2xhaW1IaXN0b3J5UmVjb3JkAgULdXNlckFkZHJlc3MFDmNsYWltZWRSZXdhcmRzAQtTdGF0c1Jlc3VsdAMOdG90YWxMb2NrZWRJbmMMbG9ja0NvdW50SW5jDXVzZXJzQ291bnRJbmMECmxvY2tzQ291bnQJAQxnZXRJbnRPclplcm8BCQESa2V5U3RhdHNMb2Nrc0NvdW50AAQKdXNlcnNDb3VudAkBDGdldEludE9yWmVybwEJARJrZXlTdGF0c1VzZXJzQ291bnQABAt0b3RhbEFtb3VudAkBDGdldEludE9yWmVybwEJARdrZXlMb2NrUGFyYW1Ub3RhbEFtb3VudAAEDnRvdGFsQW1vdW50TmV3CQBkAgULdG90YWxBbW91bnQFDnRvdGFsTG9ja2VkSW5jCQCVCgMJAMwIAgkBDEludGVnZXJFbnRyeQIJARJrZXlTdGF0c0xvY2tzQ291bnQACQBkAgUKbG9ja3NDb3VudAUMbG9ja0NvdW50SW5jCQDMCAIJAQxJbnRlZ2VyRW50cnkCCQESa2V5U3RhdHNVc2Vyc0NvdW50AAkAZAIFCnVzZXJzQ291bnQFDXVzZXJzQ291bnRJbmMJAMwIAgkBDEludGVnZXJFbnRyeQIJARdrZXlMb2NrUGFyYW1Ub3RhbEFtb3VudAAFDnRvdGFsQW1vdW50TmV3CQDMCAIJAQxJbnRlZ2VyRW50cnkCCQEVa2V5TGVnYWN5VG90YWxCYWxhbmNlAQUQc3Rha2VkQXNzZXRJZFN0cgUOdG90YWxBbW91bnROZXcFA25pbAULdG90YWxBbW91bnQFDnRvdGFsQW1vdW50TmV3AQ9Mb2NrUGFyYW1zRW50cnkDC3VzZXJBZGRyZXNzBmFtb3VudBJzdGFraW5nU3RhcnRIZWlnaHQJAMwIAgkBDEludGVnZXJFbnRyeQIJARZrZXlMb2NrUGFyYW1Vc2VyQW1vdW50AQULdXNlckFkZHJlc3MFBmFtb3VudAkAzAgCCQEMSW50ZWdlckVudHJ5AgkBFmtleUxvY2tQYXJhbVN0YXJ0QmxvY2sBBQt1c2VyQWRkcmVzcwUSc3Rha2luZ1N0YXJ0SGVpZ2h0BQNuaWwBD2dldFBhcmFtc09yRmFpbAAJAJQKAgkA2QQBCQEPZ2V0U3RyaW5nT3JGYWlsAgUEdGhpcwkBEGtleVN0YWtlZEFzc2V0SWQACQEMZ2V0SW50T3JGYWlsAQkBEGtleU1pbkxvY2tBbW91bnQAAQxpc0FjdGl2ZVVzZXIBC3VzZXJBZGRyZXNzCQBmAgkBDGdldEludE9yRWxzZQIJARZrZXlMb2NrUGFyYW1Vc2VyQW1vdW50AQULdXNlckFkZHJlc3MAAAAAARNnZXRVc2VyUGFyYW1zT3JVbml0AQt1c2VyQWRkcmVzcwMJAQxpc0FjdGl2ZVVzZXIBBQt1c2VyQWRkcmVzcwkAlQoDBwkBDGdldEludE9yRmFpbAEJARZrZXlMb2NrUGFyYW1Vc2VyQW1vdW50AQULdXNlckFkZHJlc3MJAQxnZXRJbnRPckZhaWwBCQEWa2V5TG9ja1BhcmFtU3RhcnRCbG9jawEFC3VzZXJBZGRyZXNzBQR1bml0ARNnZXRVc2VyUGFyYW1zT3JGYWlsAQt1c2VyQWRkcmVzcwkBE3ZhbHVlT3JFcnJvck1lc3NhZ2UCCQETZ2V0VXNlclBhcmFtc09yVW5pdAEFC3VzZXJBZGRyZXNzCQCsAgIJAKwCAgIFVXNlciAJAKUIAQULdXNlckFkZHJlc3MCDyBpcyBub3QgZGVmaW5lZAEKY2FsY1Jld2FyZAULdXNlckFkZHJlc3MHYXNzZXRJZA1zdGFrZWRBbW91bnRYDmRlcG9zaXROdW1Vc2VyDmRlcG9zaXROdW1MYXN0BBdyZXdhcmRQZXJOc2J0U3VtTGFzdEtFWQkBFWtleVJld2FyZFBlck5zYnRTdW1BdAIFDmRlcG9zaXROdW1MYXN0BQdhc3NldElkBApzdW1MYXN0WDE4CQCnAwEJAQxnZXRTdHJPckVsc2UCCQEVa2V5UmV3YXJkUGVyTnNidFN1bUF0AgUOZGVwb3NpdE51bUxhc3QFB2Fzc2V0SWQCATAECnN1bVVzZXJYMTgJAKcDAQkBDGdldFN0ck9yRWxzZQIJARVrZXlSZXdhcmRQZXJOc2J0U3VtQXQCBQ5kZXBvc2l0TnVtVXNlcgUHYXNzZXRJZAIBMAQRcmV3YXJkRHluYW1pY1BhcnQJAKADAQkAvAIDCQC4AgIFCnN1bUxhc3RYMTgFCnN1bVVzZXJYMTgFDXN0YWtlZEFtb3VudFgFB01VTFRYMTgEE3Jld2FyZENhY2hlZFBhcnRLRVkJAQlrZXlSZXdhcmQCBQt1c2VyQWRkcmVzcwUHYXNzZXRJZAQQcmV3YXJkQ2FjaGVkUGFydAkBDGdldEludE9yRWxzZQIFE3Jld2FyZENhY2hlZFBhcnRLRVkAAAkAlgoECQBkAgUQcmV3YXJkQ2FjaGVkUGFydAURcmV3YXJkRHluYW1pY1BhcnQFEHJld2FyZENhY2hlZFBhcnQFEXJld2FyZER5bmFtaWNQYXJ0BRNyZXdhcmRDYWNoZWRQYXJ0S0VZAQx0b1N0YXJ0T2ZEYXkBCXRpbWVzdGFtcAkAaAIJAGkCBQl0aW1lc3RhbXAFCURBWU1JTExJUwUJREFZTUlMTElTARNmaW5kRWxlbWVudFBvc2l0aW9uAwNzcmMHZWxlbWVudANzZXAEDGVsZW1lbnRTdGFydAkBE3ZhbHVlT3JFcnJvck1lc3NhZ2UCCQCzCQIFA3NyYwUHZWxlbWVudAkArAICCQCsAgIJAKwCAgIWdGhlcmUgaXMgbm8gc3Vic3RyaW5nIAUHZWxlbWVudAIEIGluIAUDc3JjAwkAAAIFDGVsZW1lbnRTdGFydAAAAAAEBGxlZnQJAK8CAgUDc3JjBQxlbGVtZW50U3RhcnQJAGUCCQCQAwEJALUJAgUEbGVmdAUDc2VwAAEAE0RlcG9zaXRUb3RhbHNQUkVGSVgCBCVkJWQBE3VwZGF0ZURlcG9zaXRUb3RhbHMDB2N1cnJWYWwLaWR4VG9VcGRhdGUIZGVsdGFBbXQEB2N1cnJBcnIJALUJAgUHY3VyclZhbAUDU0VQCgEOdXBkRGVwVG90QnlJZHgBA2lkeAMJAQIhPQIFA2lkeAULaWR4VG9VcGRhdGUJAJEDAgUHY3VyckFycgUDaWR4CQCkAwEJAGQCCQENcGFyc2VJbnRWYWx1ZQEJAJEDAgUHY3VyckFycgUDaWR4BQhkZWx0YUFtdAkAuQkCCQDMCAIFE0RlcG9zaXRUb3RhbHNQUkVGSVgJAMwIAgkBDnVwZERlcFRvdEJ5SWR4AQABCQDMCAIJAQ51cGREZXBUb3RCeUlkeAEAAgUDbmlsBQNTRVABFURlcG9zaXRzVG90YWxzRW50cmllcwINZGVwb3NpdEFtb3VudAphc3NldElkU3RyBApzdGFydE9mRGF5CQEMdG9TdGFydE9mRGF5AQgFCWxhc3RCbG9jawl0aW1lc3RhbXAECGJ5RGF5S0VZCQEXa2V5U3RhdHNEZXBvc2l0QW10QnlEYXkBBQpzdGFydE9mRGF5BAl0b3RhbHNLRVkJARhrZXlTdGF0c0RlcG9zaXRBbXRUb3RhbHMABAhwb3NpdGlvbgkBE2ZpbmRFbGVtZW50UG9zaXRpb24DBRJzdXBwb3J0ZWRBc3NldHNTdHIFCmFzc2V0SWRTdHICAV8EC2RlZmF1bHREQVRBCQCsAgIFE0RlcG9zaXRUb3RhbHNQUkVGSVgCBl9fMF9fMAQOY3VyclRvdGFsc0RBVEEJAQt2YWx1ZU9yRWxzZQIJAJ0IAgUEdGhpcwUJdG90YWxzS0VZBQtkZWZhdWx0REFUQQQNbmV3VG90YWxzREFUQQkBE3VwZGF0ZURlcG9zaXRUb3RhbHMDBQ5jdXJyVG90YWxzREFUQQkAZAIFCHBvc2l0aW9uAAEFDWRlcG9zaXRBbW91bnQJAMwIAgkBC1N0cmluZ0VudHJ5AgUJdG90YWxzS0VZBQ1uZXdUb3RhbHNEQVRBCQDMCAIJAQtTdHJpbmdFbnRyeQIFCGJ5RGF5S0VZBQ1uZXdUb3RhbHNEQVRBBQNuaWwBDVJld2FyZEVudHJpZXMDCWlzTmV3VXNlcgt1c2VyQWRkcmVzcwxzdGFrZWRBbW91bnQEDXN0YWtlZEFtb3VudFgJALYCAQUMc3Rha2VkQW1vdW50BBt1c2VyUmV3YXJkRnJvbURlcG9zaXROdW1LRVkJARtrZXlVc2VyUmV3YXJkRnJvbURlcG9zaXROdW0BBQt1c2VyQWRkcmVzcwQOZGVwb3NpdE51bVVzZXIJAQxnZXRJbnRPckVsc2UCBRt1c2VyUmV3YXJkRnJvbURlcG9zaXROdW1LRVkA////////////AQQOZGVwb3NpdE51bUxhc3QJAQxnZXRJbnRPckVsc2UCCQERa2V5RGVwb3NpdE51bUxhc3QAAP///////////wEKARtmb3JFYWNoQXNzZXRDYWNoZVVzZXJSZXdhcmQCBWFjY3VtBWFzc2V0BA0kdDAxMDU3ODEwNzEzCQEKY2FsY1Jld2FyZAUFC3VzZXJBZGRyZXNzBQVhc3NldAUNc3Rha2VkQW1vdW50WAUOZGVwb3NpdE51bVVzZXIFDmRlcG9zaXROdW1MYXN0BAtyZXdhcmRUb3RhbAgFDSR0MDEwNTc4MTA3MTMCXzEEBmNhY2hlZAgFDSR0MDEwNTc4MTA3MTMCXzIEB2R5bmFtaWMIBQ0kdDAxMDU3ODEwNzEzAl8zBBNyZXdhcmRDYWNoZWRQYXJ0S0VZCAUNJHQwMTA1NzgxMDcxMwJfNAkAzQgCBQVhY2N1bQkBDEludGVnZXJFbnRyeQIFE3Jld2FyZENhY2hlZFBhcnRLRVkFC3Jld2FyZFRvdGFsAwMJAAACBQ5kZXBvc2l0TnVtTGFzdAD///////////8BCQAAAgUOZGVwb3NpdE51bVVzZXIA////////////AQcFA25pbAMDCQAAAgUOZGVwb3NpdE51bUxhc3QA////////////AQkAZgIFDmRlcG9zaXROdW1Vc2VyAP///////////wEHCQACAQIvaW52YWxpZCBkZXBvc2l0TnVtTGFzdCBhbmQgZGVwb3NpdE51bVVzZXIgc3RhdGUDAwkAZgIFDmRlcG9zaXROdW1MYXN0AP///////////wEJAGcCBQ5kZXBvc2l0TnVtVXNlcgD///////////8BBwMFCWlzTmV3VXNlcgkAzAgCCQEMSW50ZWdlckVudHJ5AgUbdXNlclJld2FyZEZyb21EZXBvc2l0TnVtS0VZBQ5kZXBvc2l0TnVtTGFzdAUDbmlsCQDNCAIKAAIkbAUTc3VwcG9ydGVkQXNzZXRzTGlzdAoAAiRzCQCQAwEFAiRsCgAFJGFjYzAFA25pbAoBBSRmMF8xAgIkYQIkaQMJAGcCBQIkaQUCJHMFAiRhCQEbZm9yRWFjaEFzc2V0Q2FjaGVVc2VyUmV3YXJkAgUCJGEJAJEDAgUCJGwFAiRpCgEFJGYwXzICAiRhAiRpAwkAZwIFAiRpBQIkcwUCJGEJAAIBAhNMaXN0IHNpemUgZXhjZWVkcyAyCQEFJGYwXzICCQEFJGYwXzECCQEFJGYwXzECBQUkYWNjMAAAAAEAAgkBDEludGVnZXJFbnRyeQIFG3VzZXJSZXdhcmRGcm9tRGVwb3NpdE51bUtFWQUOZGVwb3NpdE51bUxhc3QJAAIBCQCsAgIJAKwCAgkArAICAiR1bmNvdmVyZWQgY29uZGl0aW9uOiBkZXBvc2l0TnVtTGFzdD0JAKQDAQUOZGVwb3NpdE51bUxhc3QCECBkZXBvc2l0TnVtVXNlcj0JAKQDAQUOZGVwb3NpdE51bVVzZXIBIkluY3JlbWVudE5vdERpc3RyaWJ1dGVkUmV3YXJkRW50cnkCA3RrbglhbW91bnRJbmMEF25vdERpc3RyaWJ1dGVkUmV3YXJkS0VZCQEXa2V5Tm90RGlzdHJpYnV0ZWRSZXdhcmQBBQN0a24EFG5vdERpc3RyaWJ1dGVkUmV3YXJkCQEMZ2V0SW50T3JFbHNlAgUXbm90RGlzdHJpYnV0ZWRSZXdhcmRLRVkAAAkAzAgCCQEMSW50ZWdlckVudHJ5AgUXbm90RGlzdHJpYnV0ZWRSZXdhcmRLRVkJAGQCBRRub3REaXN0cmlidXRlZFJld2FyZAUJYW1vdW50SW5jBQNuaWwBCm1lcmdlU3Rha2UCC3VzZXJBZGRyZXNzC2Ftb3VudFRvQWRkBA0kdDAxMzUzODEzNjU0CQELdmFsdWVPckVsc2UCCQETZ2V0VXNlclBhcmFtc09yVW5pdAEFC3VzZXJBZGRyZXNzCQCVCgMGAAAFBmhlaWdodAQJaXNOZXdVc2VyCAUNJHQwMTM1MzgxMzY1NAJfMQQMc3Rha2VkQW1vdW50CAUNJHQwMTM1MzgxMzY1NAJfMgQSc3Rha2luZ1N0YXJ0SGVpZ2h0CAUNJHQwMTM1MzgxMzY1NAJfMwQPc3Rha2VkQW1vdW50TkVXAwUJaXNOZXdVc2VyBQthbW91bnRUb0FkZAkAZAIFC2Ftb3VudFRvQWRkBQxzdGFrZWRBbW91bnQJAJYKBAUJaXNOZXdVc2VyBQxzdGFrZWRBbW91bnQFEnN0YWtpbmdTdGFydEhlaWdodAUPc3Rha2VkQW1vdW50TkVXAQtjb21tb25TdGFrZQILdXNlckFkZHJlc3MBaQMJAQIhPQIJAJADAQgFAWkIcGF5bWVudHMAAQkAAgECFUludmFsaWQgcGF5bWVudHMgc2l6ZQQHcGF5bWVudAkAkQMCCAUBaQhwYXltZW50cwAABAZhbW91bnQIBQdwYXltZW50BmFtb3VudAQTaW52YWxpZEFzc2V0TWVzc2FnZQkArAICCQCsAgICD0ludmFsaWQgYXNzZXQuIAkA2AQBBQ1zdGFrZWRBc3NldElkAgwgaXMgZXhwZWN0ZWQEB2Fzc2V0SWQJARN2YWx1ZU9yRXJyb3JNZXNzYWdlAggFB3BheW1lbnQHYXNzZXRJZAUTaW52YWxpZEFzc2V0TWVzc2FnZQMJAQIhPQIFB2Fzc2V0SWQFDXN0YWtlZEFzc2V0SWQJAAIBBRNpbnZhbGlkQXNzZXRNZXNzYWdlBA51c2VyQWRkcmVzc1N0cgkApQgBBQt1c2VyQWRkcmVzcwQKbWVyZ2VkRGF0YQkBCm1lcmdlU3Rha2UCBQt1c2VyQWRkcmVzcwUGYW1vdW50BAlpc05ld1VzZXIIBQptZXJnZWREYXRhAl8xBAxzdGFrZWRBbW91bnQIBQptZXJnZWREYXRhAl8yBBJzdGFraW5nU3RhcnRIZWlnaHQIBQptZXJnZWREYXRhAl8zBA9zdGFrZWRBbW91bnRORVcIBQptZXJnZWREYXRhAl80AwkAZgIFDW1pbkxvY2tBbW91bnQFD3N0YWtlZEFtb3VudE5FVwkAAgEJAKwCAgITTWluIGxvY2sgYW1vdW50IGlzIAkApAMBBQ1taW5Mb2NrQW1vdW50BA0kdDAxNDYyNDE0NzI2CQELU3RhdHNSZXN1bHQDBQZhbW91bnQAAQMFCWlzTmV3VXNlcgABAAAEDHN0YXRzRW50cmllcwgFDSR0MDE0NjI0MTQ3MjYCXzEEC3RvdGFsU3Rha2VkCAUNJHQwMTQ2MjQxNDcyNgJfMgQOdG90YWxTdGFrZWROZXcIBQ0kdDAxNDYyNDE0NzI2Al8zCQDOCAIJAM4IAgkAzggCCQDMCAIJARJIaXN0b3J5UmVjb3JkRW50cnkFAgVzdGFrZQUOdXNlckFkZHJlc3NTdHIIBQFpDXRyYW5zYWN0aW9uSWQFDHN0YWtlZEFtb3VudAUPc3Rha2VkQW1vdW50TkVXBQNuaWwJAQ1SZXdhcmRFbnRyaWVzAwUJaXNOZXdVc2VyBQ51c2VyQWRkcmVzc1N0cgUMc3Rha2VkQW1vdW50CQEPTG9ja1BhcmFtc0VudHJ5AwULdXNlckFkZHJlc3MFD3N0YWtlZEFtb3VudE5FVwUSc3Rha2luZ1N0YXJ0SGVpZ2h0BQxzdGF0c0VudHJpZXMBDWNvbW1vblVuc3Rha2UCBmFtb3VudAFpAwkBAiE9AgkAkAMBCAUBaQhwYXltZW50cwAACQACAQIjdW5zdGFrZSBkb2Vzbid0IHJlcXVpcmUgYW55IHBheW1lbnQEC3VzZXJBZGRyZXNzCAUBaQZjYWxsZXIEDnVzZXJBZGRyZXNzU3RyCQClCAEFC3VzZXJBZGRyZXNzBA0kdDAxNTE5ODE1MjgzCQETZ2V0VXNlclBhcmFtc09yRmFpbAEFC3VzZXJBZGRyZXNzBAlpc05ld1VzZXIIBQ0kdDAxNTE5ODE1MjgzAl8xBAxzdGFrZWRBbW91bnQIBQ0kdDAxNTE5ODE1MjgzAl8yBBJzdGFraW5nU3RhcnRIZWlnaHQIBQ0kdDAxNTE5ODE1MjgzAl8zBBBzd2FwUGFyYW1zU1RSVUNUCQESYXNTd2FwUGFyYW1zU1RSVUNUAQkA/QcEBRBuZXV0cmlub0NvbnRyYWN0Ahtzd2FwUGFyYW1zQnlVc2VyU1lTUkVBRE9OTFkJAMwIAgUOdXNlckFkZHJlc3NTdHIJAMwIAgAABQNuaWwFA25pbAQUc3dhcExpbWl0U3BlbnRJblVzZG4IBRBzd2FwUGFyYW1zU1RSVUNUAl8yBA5ibGNrczJMbXRSZXNldAgFEHN3YXBQYXJhbXNTVFJVQ1QCXzMDCQBmAgUUc3dhcExpbWl0U3BlbnRJblVzZG4AAAkAAgEJAKwCAgkArAICAi1Zb3UgaGF2ZSBhbHJlYWR5IG1hZGUgYSBzd2FwIG9wZXJhdGlvbi4gV2FpdCAJAKQDAQkAZAIFBmhlaWdodAUOYmxja3MyTG10UmVzZXQCEiBoZWlnaHQgdG8gdW5zdGFrZQMJAGcCAAAFDHN0YWtlZEFtb3VudAkAAgECEk5vdGhpbmcgdG8gdW5zdGFrZQMJAGYCBQZhbW91bnQFDHN0YWtlZEFtb3VudAkAAgEJAKwCAgkArAICCQCsAgICClJlcXVlc3RlZCAJAKQDAQUGYW1vdW50AhIsIGJ1dCBzdGFrZWQgb25seSAJAKQDAQUMc3Rha2VkQW1vdW50BA9zdGFrZWRBbW91bnRORVcJAGUCBQxzdGFrZWRBbW91bnQFBmFtb3VudAQNJHQwMTU5MjExNjA3OQkBC1N0YXRzUmVzdWx0AwkBAS0BBQZhbW91bnQDCQAAAgUGYW1vdW50BQxzdGFrZWRBbW91bnQA////////////AQAAAwkAAAIFBmFtb3VudAUMc3Rha2VkQW1vdW50AP///////////wEAAAQMc3RhdHNFbnRyaWVzCAUNJHQwMTU5MjExNjA3OQJfMQQLdG90YWxTdGFrZWQIBQ0kdDAxNTkyMTE2MDc5Al8yBA50b3RhbFN0YWtlZE5ldwgFDSR0MDE1OTIxMTYwNzkCXzMJAM4IAgkAzggCCQDOCAIJAMwIAgkBDlNjcmlwdFRyYW5zZmVyAwULdXNlckFkZHJlc3MFBmFtb3VudAUNc3Rha2VkQXNzZXRJZAkAzAgCCQESSGlzdG9yeVJlY29yZEVudHJ5BQIHdW5zdGFrZQUOdXNlckFkZHJlc3NTdHIIBQFpDXRyYW5zYWN0aW9uSWQFDHN0YWtlZEFtb3VudAUPc3Rha2VkQW1vdW50TkVXBQNuaWwJAQ1SZXdhcmRFbnRyaWVzAwcFDnVzZXJBZGRyZXNzU3RyBQxzdGFrZWRBbW91bnQJAQ9Mb2NrUGFyYW1zRW50cnkDBQt1c2VyQWRkcmVzcwUPc3Rha2VkQW1vdW50TkVXBRJzdGFraW5nU3RhcnRIZWlnaHQFDHN0YXRzRW50cmllcwELY29tbW9uQ2xhaW0CC3VzZXJBZGRyZXNzAWkEDnVzZXJBZGRyZXNzU3RyCQClCAEFC3VzZXJBZGRyZXNzAwkAZgIJAJADAQgFAWkIcGF5bWVudHMAAAkAAgECGXBheW1lbnRzIGFyZSBub3QgYWNjZXB0ZWQEDSR0MDE2NTcyMTY2NzcJAQt2YWx1ZU9yRWxzZQIJARNnZXRVc2VyUGFyYW1zT3JVbml0AQULdXNlckFkZHJlc3MJAJUKAwYAAAAABAlpc05ld1VzZXIIBQ0kdDAxNjU3MjE2Njc3Al8xBAxzdGFrZWRBbW91bnQIBQ0kdDAxNjU3MjE2Njc3Al8yBAxzdGFraW5nU3RhcnQIBQ0kdDAxNjU3MjE2Njc3Al8zBA1zdGFrZWRBbW91bnRYCQC2AgEFDHN0YWtlZEFtb3VudAQbdXNlclJld2FyZEZyb21EZXBvc2l0TnVtS0VZCQEba2V5VXNlclJld2FyZEZyb21EZXBvc2l0TnVtAQUOdXNlckFkZHJlc3NTdHIEDmRlcG9zaXROdW1Vc2VyCQEMZ2V0SW50T3JFbHNlAgUbdXNlclJld2FyZEZyb21EZXBvc2l0TnVtS0VZAP///////////wEEDmRlcG9zaXROdW1MYXN0CQEMZ2V0SW50T3JFbHNlAgkBEWtleURlcG9zaXROdW1MYXN0AAD///////////8BCgEfZm9yRWFjaEFzc2V0Q2FsY1VuY2xhaW1lZFJld2FyZAIFYWNjdW0FYXNzZXQEDSR0MDE3MDQ4MTcxODYJAQpjYWxjUmV3YXJkBQUOdXNlckFkZHJlc3NTdHIFBWFzc2V0BQ1zdGFrZWRBbW91bnRYBQ5kZXBvc2l0TnVtVXNlcgUOZGVwb3NpdE51bUxhc3QEC3Jld2FyZFRvdGFsCAUNJHQwMTcwNDgxNzE4NgJfMQQGY2FjaGVkCAUNJHQwMTcwNDgxNzE4NgJfMgQHZHluYW1pYwgFDSR0MDE3MDQ4MTcxODYCXzMEE3Jld2FyZENhY2hlZFBhcnRLRVkIBQ0kdDAxNzA0ODE3MTg2Al80BApjbGFpbWVkS0VZCQEKa2V5Q2xhaW1lZAIFDnVzZXJBZGRyZXNzU3RyBQVhc3NldAQNJHQwMTcyNDYxNzI4MwUFYWNjdW0EBGRhdGEIBQ0kdDAxNzI0NjE3MjgzAl8xBBFjbGFpbWVkQW10QnlBc3NldAgFDSR0MDE3MjQ2MTcyODMCXzIEB25ld1BhcnQJALkJAgkAzAgCBQVhc3NldAkAzAgCCQCkAwEFC3Jld2FyZFRvdGFsBQNuaWwCAToEFGNsYWltZWRBbXRCeUFzc2V0TmV3CQC5CQIJAMwIAgURY2xhaW1lZEFtdEJ5QXNzZXQJAMwIAgUHbmV3UGFydAUDbmlsAgFfAwkAZwIAAAULcmV3YXJkVG90YWwJAJQKAgUEZGF0YQUUY2xhaW1lZEFtdEJ5QXNzZXROZXcJAJQKAgkAzQgCCQDNCAIJAM0IAgUEZGF0YQkBDlNjcmlwdFRyYW5zZmVyAwULdXNlckFkZHJlc3MFC3Jld2FyZFRvdGFsCQELdG9Bc3NldFZlY3QBBQVhc3NldAkBDEludGVnZXJFbnRyeQIFCmNsYWltZWRLRVkJAGQCCQELdmFsdWVPckVsc2UCCQCfCAEFCmNsYWltZWRLRVkAAAULcmV3YXJkVG90YWwJAQxJbnRlZ2VyRW50cnkCBRNyZXdhcmRDYWNoZWRQYXJ0S0VZAAAFFGNsYWltZWRBbXRCeUFzc2V0TmV3BA0kdDAxNzc0MzE3ODU2CgACJGwFE3N1cHBvcnRlZEFzc2V0c0xpc3QKAAIkcwkAkAMBBQIkbAoABSRhY2MwCQCUCgIFA25pbAIACgEFJGYwXzECAiRhAiRpAwkAZwIFAiRpBQIkcwUCJGEJAR9mb3JFYWNoQXNzZXRDYWxjVW5jbGFpbWVkUmV3YXJkAgUCJGEJAJEDAgUCJGwFAiRpCgEFJGYwXzICAiRhAiRpAwkAZwIFAiRpBQIkcwUCJGEJAAIBAhNMaXN0IHNpemUgZXhjZWVkcyAyCQEFJGYwXzICCQEFJGYwXzECCQEFJGYwXzECBQUkYWNjMAAAAAEAAgQJdHJhbnNmZXJzCAUNJHQwMTc3NDMxNzg1NgJfMQQXY2xhaW1lZEFtdEJ5QXNzZXRSZXN1bHQIBQ0kdDAxNzc0MzE3ODU2Al8yAwkAZwIAAAkAkAMBBQl0cmFuc2ZlcnMJAJQKAgUDbmlsAAAJAJQKAgkAzQgCCQDNCAIFCXRyYW5zZmVycwkBDEludGVnZXJFbnRyeQIFG3VzZXJSZXdhcmRGcm9tRGVwb3NpdE51bUtFWQUOZGVwb3NpdE51bUxhc3QJARFDbGFpbUhpc3RvcnlFbnRyeQMFDnVzZXJBZGRyZXNzU3RyCAUBaQ10cmFuc2FjdGlvbklkCQCwAgIFF2NsYWltZWRBbXRCeUFzc2V0UmVzdWx0AAEJAJADAQUJdHJhbnNmZXJzAAhVU0ROVFlQRQIEVVNETgAITlNCVFRZUEUCBE5TQlQAEk5ldXRyaW5vQXNzZXRJZEtleQIRbmV1dHJpbm9fYXNzZXRfaWQAE05ldXRyaW5vQ29udHJhY3RLZXkCEW5ldXRyaW5vX2NvbnRyYWN0AA5Oc2J0QXNzZXRJZEtleQINYm9uZF9hc3NldF9pZAAKQmFsYW5jZUtleQILcnBkX2JhbGFuY2UAD25ldXRyaW5vQXNzZXRJZAkA2QQBCQEPZ2V0U3RyaW5nT3JGYWlsAgUQbmV1dHJpbm9Db250cmFjdAUSTmV1dHJpbm9Bc3NldElkS2V5AA5uc2J0QXNzZXRJZFN0cgkBD2dldFN0cmluZ09yRmFpbAIFEG5ldXRyaW5vQ29udHJhY3QFDk5zYnRBc3NldElkS2V5AAtuc2J0QXNzZXRJZAkA2QQBBQ5uc2J0QXNzZXRJZFN0cgERZ2V0VXNlckJhbGFuY2VLZXkCBW93bmVyB2Fzc2V0SWQJALkJAgkAzAgCBQpCYWxhbmNlS2V5CQDMCAIFB2Fzc2V0SWQJAMwIAgUFb3duZXIFA25pbAIBXwEVZ2V0Q29udHJhY3RCYWxhbmNlS2V5AQdhc3NldElkCQCsAgIJAKwCAgUKQmFsYW5jZUtleQIBXwUHYXNzZXRJZAESZ2V0Q29udHJhY3RCYWxhbmNlAQdhc3NldElkCQEMZ2V0SW50T3JFbHNlAgkBFWdldENvbnRyYWN0QmFsYW5jZUtleQEFB2Fzc2V0SWQAAAEOZ2V0VXNlckJhbGFuY2UCBW93bmVyB2Fzc2V0SWQJAQxnZXRJbnRPckVsc2UCCQERZ2V0VXNlckJhbGFuY2VLZXkCBQVvd25lcgUHYXNzZXRJZAAAARpnZXRWYWxpZFN0YWtpbmdBc3NldE9yRmFpbAILc3Rha2luZ1R5cGUHYXNzZXRJZAMDCQAAAgULc3Rha2luZ1R5cGUFCFVTRE5UWVBFCQECIT0CBQdhc3NldElkBQ9uZXV0cmlub0Fzc2V0SWQHCQACAQIRY2FuIHVzZSBVU0ROIG9ubHkDAwkAAAIFC3N0YWtpbmdUeXBlBQhOU0JUVFlQRQkBAiE9AgUHYXNzZXRJZAULbnNidEFzc2V0SWQHCQACAQIRY2FuIHVzZSBOU0JUIG9ubHkDAwkBAiE9AgULc3Rha2luZ1R5cGUFCFVTRE5UWVBFCQECIT0CBQtzdGFraW5nVHlwZQUITlNCVFRZUEUHCQACAQkArAICAhl1bnN1cHBvcnRlZCBzdGFraW5nIHR5cGUgBQtzdGFraW5nVHlwZQUHYXNzZXRJZAEOaW50ZXJuYWxVbmxvY2sEC3N0YWtpbmdUeXBlAWkMdW5sb2NrQW1vdW50DGFzc2V0SWRQYXJhbQQHYWNjb3VudAkApQgBCAUBaQZjYWxsZXIEB2Fzc2V0SWQJARpnZXRWYWxpZFN0YWtpbmdBc3NldE9yRmFpbAIFC3N0YWtpbmdUeXBlCQDZBAEFDGFzc2V0SWRQYXJhbQQNYXNzZXRJZFN0cmluZwkA2AQBBQdhc3NldElkBAdiYWxhbmNlCQBlAgkBDmdldFVzZXJCYWxhbmNlAgUHYWNjb3VudAUNYXNzZXRJZFN0cmluZwUMdW5sb2NrQW1vdW50AwkAZgIAAAUHYmFsYW5jZQkAAgECDmludmFsaWQgYW1vdW50CQCUCgIJAMwIAgkBDEludGVnZXJFbnRyeQIJARVnZXRDb250cmFjdEJhbGFuY2VLZXkBBQ1hc3NldElkU3RyaW5nCQBlAgkBEmdldENvbnRyYWN0QmFsYW5jZQEFDWFzc2V0SWRTdHJpbmcFDHVubG9ja0Ftb3VudAkAzAgCCQEMSW50ZWdlckVudHJ5AgkBEWdldFVzZXJCYWxhbmNlS2V5AgUHYWNjb3VudAUNYXNzZXRJZFN0cmluZwUHYmFsYW5jZQkAzAgCCQEOU2NyaXB0VHJhbnNmZXIDCQERQGV4dHJOYXRpdmUoMTA2MikBBQdhY2NvdW50BQx1bmxvY2tBbW91bnQFB2Fzc2V0SWQFA25pbAUEdW5pdA4BaQELY29uc3RydWN0b3IDDW1pbkxvY2tBbW91bnQVc3VwcG9ydGVkUmV3YXJkQXNzZXRzDnBTdGFrZWRBc3NldElkAwkBAiE9AggFAWkGY2FsbGVyBQR0aGlzCQACAQIRUGVybWlzc2lvbiBkZW5pZWQJAMwIAgkBDEludGVnZXJFbnRyeQIJARBrZXlNaW5Mb2NrQW1vdW50AAUNbWluTG9ja0Ftb3VudAkAzAgCCQELU3RyaW5nRW50cnkCCQEYa2V5U3VwcG9ydGVkUmV3YXJkQXNzZXRzAAUVc3VwcG9ydGVkUmV3YXJkQXNzZXRzCQDMCAIJAQtTdHJpbmdFbnRyeQIJARBrZXlTdGFrZWRBc3NldElkAAUOcFN0YWtlZEFzc2V0SWQFA25pbAFpAQVzdGFrZQAJAQtjb21tb25TdGFrZQIIBQFpBmNhbGxlcgUBaQFpARNzdGFrZUJ5T3JpZ2luQ2FsbGVyAAkBC2NvbW1vblN0YWtlAggFAWkMb3JpZ2luQ2FsbGVyBQFpAWkBB3Vuc3Rha2UBBmFtb3VudAkBDWNvbW1vblVuc3Rha2UCBQZhbW91bnQFAWkBaQEHZGVwb3NpdAADCQECIT0CCQCQAwEIBQFpCHBheW1lbnRzAAEJAAIBAh9leGFjdCAxIHBheW1lbnQgaXMgYWxsb3dlZCBvbmx5BANwbXQJAJEDAggFAWkIcGF5bWVudHMAAAQGYW1vdW50CAUDcG10BmFtb3VudAQKcG10QXNzZXRJZAkBC3ZhbHVlT3JFbHNlAggFA3BtdAdhc3NldElkBQdXQVZFU0lEBA1wbXRBc3NldElkU3RyCQDYBAEFCnBtdEFzc2V0SWQECHBtdE11bHRYAwkAAAIFCnBtdEFzc2V0SWQFB1dBVkVTSUQFBk1VTFRYOAUGTVVMVFg2BAdhbW91bnRYCQC2AgEFBmFtb3VudAQLdG90YWxTdGFrZWQJAQxnZXRJbnRPckVsc2UCCQEXa2V5TG9ja1BhcmFtVG90YWxBbW91bnQAAAAEDHRvdGFsU3Rha2VkWAkAtgIBBQt0b3RhbFN0YWtlZAMJAGYCAAAFC3RvdGFsU3Rha2VkCQACAQIbVE9ETzogY2FzZSBpcyBub3Qgc3VwcG9ydGVkAwkAAAIFC3RvdGFsU3Rha2VkAAAJASJJbmNyZW1lbnROb3REaXN0cmlidXRlZFJld2FyZEVudHJ5AgUNcG10QXNzZXRJZFN0cgUGYW1vdW50BBByZXdhcmRQZXJOc2J0WDE4CQC8AgMFB2Ftb3VudFgFB01VTFRYMTgFDHRvdGFsU3Rha2VkWAQRZGVwb3NpdE51bUxhc3RLRVkJARFrZXlEZXBvc2l0TnVtTGFzdAAEDmRlcG9zaXROdW1MYXN0CQEMZ2V0SW50T3JFbHNlAgURZGVwb3NpdE51bUxhc3RLRVkA////////////AQQNZGVwb3NpdE51bU5ldwkAZAIFDmRlcG9zaXROdW1MYXN0AAEDCQEBIQEJAQhjb250YWlucwIFEnN1cHBvcnRlZEFzc2V0c1N0cgUNcG10QXNzZXRJZFN0cgkAAgEJAKwCAgkArAICBRJzdXBwb3J0ZWRBc3NldHNTdHICESBkb2Vzbid0IGNvbnRhaW4gBQ1wbXRBc3NldElkU3RyCgEXcmVmcmVzaFJld2FyZFBlck5zYnRTVU0CBWFjY3VtCW5leHRBc3NldAQWcmV3YXJkUGVyTnNidFN1bU5ld0tFWQkBFWtleVJld2FyZFBlck5zYnRTdW1BdAIFDWRlcG9zaXROdW1OZXcFCW5leHRBc3NldAQKc3VtTGFzdFN0cgkBDGdldFN0ck9yRWxzZQIJARVrZXlSZXdhcmRQZXJOc2J0U3VtQXQCBQ5kZXBvc2l0TnVtTGFzdAUJbmV4dEFzc2V0AgEwCQDNCAIFBWFjY3VtAwkAAAIFCW5leHRBc3NldAUNcG10QXNzZXRJZFN0cgkBC1N0cmluZ0VudHJ5AgUWcmV3YXJkUGVyTnNidFN1bU5ld0tFWQkApgMBCQC3AgIJAKcDAQUKc3VtTGFzdFN0cgUQcmV3YXJkUGVyTnNidFgxOAkBC1N0cmluZ0VudHJ5AgUWcmV3YXJkUGVyTnNidFN1bU5ld0tFWQUKc3VtTGFzdFN0cgkAzggCCQDNCAIKAAIkbAUTc3VwcG9ydGVkQXNzZXRzTGlzdAoAAiRzCQCQAwEFAiRsCgAFJGFjYzAFA25pbAoBBSRmMF8xAgIkYQIkaQMJAGcCBQIkaQUCJHMFAiRhCQEXcmVmcmVzaFJld2FyZFBlck5zYnRTVU0CBQIkYQkAkQMCBQIkbAUCJGkKAQUkZjBfMgICJGECJGkDCQBnAgUCJGkFAiRzBQIkYQkAAgECE0xpc3Qgc2l6ZSBleGNlZWRzIDIJAQUkZjBfMgIJAQUkZjBfMQIJAQUkZjBfMQIFBSRhY2MwAAAAAQACCQEMSW50ZWdlckVudHJ5AgURZGVwb3NpdE51bUxhc3RLRVkFDWRlcG9zaXROdW1OZXcJARVEZXBvc2l0c1RvdGFsc0VudHJpZXMCBQZhbW91bnQFDXBtdEFzc2V0SWRTdHIBaQEMY2xhaW1SZXdhcmRzAAkBC2NvbW1vbkNsYWltAggFAWkGY2FsbGVyBQFpAWkBGmNsYWltUmV3YXJkc0J5T3JpZ2luQ2FsbGVyAAkBC2NvbW1vbkNsYWltAggFAWkMb3JpZ2luQ2FsbGVyBQFpAWkBGHVuY2xhaW1lZFJld2FyZHNSRUFET05MWQEOdXNlckFkZHJlc3NTdHIKARZmb3JFYWNoQXNzZXRaZXJvUmV3YXJkAgVhY2N1bQVhc3NldAkArAICCQCsAgIFBWFjY3VtCQC5CQIJAMwIAgUFYXNzZXQJAMwIAgIBMAkAzAgCAgEwBQNuaWwCAToCAV8EEnVuY2xhaW1lZFJld2FyZFN0cgMJAAACBQ51c2VyQWRkcmVzc1N0cgIACgACJGwFE3N1cHBvcnRlZEFzc2V0c0xpc3QKAAIkcwkAkAMBBQIkbAoABSRhY2MwAgAKAQUkZjBfMQICJGECJGkDCQBnAgUCJGkFAiRzBQIkYQkBFmZvckVhY2hBc3NldFplcm9SZXdhcmQCBQIkYQkAkQMCBQIkbAUCJGkKAQUkZjBfMgICJGECJGkDCQBnAgUCJGkFAiRzBQIkYQkAAgECE0xpc3Qgc2l6ZSBleGNlZWRzIDIJAQUkZjBfMgIJAQUkZjBfMQIJAQUkZjBfMQIFBSRhY2MwAAAAAQACBAt1c2VyQWRkcmVzcwkBEUBleHRyTmF0aXZlKDEwNjIpAQUOdXNlckFkZHJlc3NTdHIEDSR0MDIyOTExMjMwMjIJAQt2YWx1ZU9yRWxzZQIJARNnZXRVc2VyUGFyYW1zT3JVbml0AQULdXNlckFkZHJlc3MJAJUKAwYAAAAABAlpc05ld1VzZXIIBQ0kdDAyMjkxMTIzMDIyAl8xBAxzdGFrZWRBbW91bnQIBQ0kdDAyMjkxMTIzMDIyAl8yBBJzdGFraW5nU3RhcnRIZWlnaHQIBQ0kdDAyMjkxMTIzMDIyAl8zBA1zdGFrZWRBbW91bnRYCQC2AgEFDHN0YWtlZEFtb3VudAQbdXNlclJld2FyZEZyb21EZXBvc2l0TnVtS0VZCQEba2V5VXNlclJld2FyZEZyb21EZXBvc2l0TnVtAQUOdXNlckFkZHJlc3NTdHIEDmRlcG9zaXROdW1Vc2VyCQEMZ2V0SW50T3JFbHNlAgUbdXNlclJld2FyZEZyb21EZXBvc2l0TnVtS0VZAP///////////wEEDmRlcG9zaXROdW1MYXN0CQEMZ2V0SW50T3JFbHNlAgkBEWtleURlcG9zaXROdW1MYXN0AAD///////////8BCgEfZm9yRWFjaEFzc2V0Q2FsY1VuY2xhaW1lZFJld2FyZAIFYWNjdW0FYXNzZXQEDSR0MDIzMzY4MjM1MDYJAQpjYWxjUmV3YXJkBQUOdXNlckFkZHJlc3NTdHIFBWFzc2V0BQ1zdGFrZWRBbW91bnRYBQ5kZXBvc2l0TnVtVXNlcgUOZGVwb3NpdE51bUxhc3QEC3Jld2FyZFRvdGFsCAUNJHQwMjMzNjgyMzUwNgJfMQQGY2FjaGVkCAUNJHQwMjMzNjgyMzUwNgJfMgQHZHluYW1pYwgFDSR0MDIzMzY4MjM1MDYCXzMEE3Jld2FyZENhY2hlZFBhcnRLRVkIBQ0kdDAyMzM2ODIzNTA2Al80BAdjbGFpbWVkCQELdmFsdWVPckVsc2UCCQCfCAEJAQprZXlDbGFpbWVkAgUOdXNlckFkZHJlc3NTdHIFBWFzc2V0AAAJAKwCAgkArAICBQVhY2N1bQkAuQkCCQDMCAIFBWFzc2V0CQDMCAIJAKQDAQULcmV3YXJkVG90YWwJAMwIAgkApAMBBQdjbGFpbWVkBQNuaWwCAToCAV8KAAIkbAUTc3VwcG9ydGVkQXNzZXRzTGlzdAoAAiRzCQCQAwEFAiRsCgAFJGFjYzACAAoBBSRmMF8xAgIkYQIkaQMJAGcCBQIkaQUCJHMFAiRhCQEfZm9yRWFjaEFzc2V0Q2FsY1VuY2xhaW1lZFJld2FyZAIFAiRhCQCRAwIFAiRsBQIkaQoBBSRmMF8yAgIkYQIkaQMJAGcCBQIkaQUCJHMFAiRhCQACAQITTGlzdCBzaXplIGV4Y2VlZHMgMgkBBSRmMF8yAgkBBSRmMF8xAgkBBSRmMF8xAgUFJGFjYzAAAAABAAIJAJQKAgUDbmlsCQCzAgIFEnVuY2xhaW1lZFJld2FyZFN0cgABAWkBFnVzZG5TdGFraW5nU1lTUkVBRE9OTFkCFXVzZXJBZGRyZXNzU3RyT3JFbXB0eQh1c2RuRGlmZgQSdXNkblRvdGFsQW10U3Rha2VkCQEMZ2V0SW50T3JFbHNlAgkBF2tleUxvY2tQYXJhbVRvdGFsQW1vdW50AAAAAwkAAAIFFXVzZXJBZGRyZXNzU3RyT3JFbXB0eQIACQCUCgIFA25pbAkAzAgCAAAJAMwIAgUSdXNkblRvdGFsQW10U3Rha2VkCQDMCAIAAAUDbmlsBAt1c2VyQWRkcmVzcwkBD3RvQWRkcmVzc09yRmFpbAEFFXVzZXJBZGRyZXNzU3RyT3JFbXB0eQQKbWVyZ2VkRGF0YQkBCm1lcmdlU3Rha2UCBQt1c2VyQWRkcmVzcwUIdXNkbkRpZmYECWlzTmV3VXNlcggFCm1lcmdlZERhdGECXzEEEHVzZG5TdGFrZWRCeVVzZXIIBQptZXJnZWREYXRhAl8yBBJzdGFraW5nU3RhcnRIZWlnaHQIBQptZXJnZWREYXRhAl8zBA9zdGFrZWRBbW91bnRORVcIBQptZXJnZWREYXRhAl80CQCUCgIFA25pbAkAzAgCBRB1c2RuU3Rha2VkQnlVc2VyCQDMCAIFEnVzZG5Ub3RhbEFtdFN0YWtlZAUDbmlsAWkBEWNvbmZpZ1NZU1JFQURPTkxZAAQKbWluTG9ja0FtdAkBEUBleHRyTmF0aXZlKDEwNTUpAQkBEGtleU1pbkxvY2tBbW91bnQACQCUCgIFA25pbAkAzAgCBQptaW5Mb2NrQW10BQNuaWwBaQEObG9ja05ldXRyaW5vU1ACCHJlY2VpdmVyBXNoYXJlCQELY29tbW9uU3Rha2UCCAUBaQZjYWxsZXIFAWkBaQEMbG9ja05ldXRyaW5vAAkBC2NvbW1vblN0YWtlAggFAWkGY2FsbGVyBQFpAWkBDnVubG9ja05ldXRyaW5vAgx1bmxvY2tBbW91bnQNYXNzZXRJZFN0cmluZwkBDWNvbW1vblVuc3Rha2UCBQx1bmxvY2tBbW91bnQFAWkBaQEKdW5sb2NrTnNidAIMdW5sb2NrQW1vdW50DWFzc2V0SWRTdHJpbmcJAQ5pbnRlcm5hbFVubG9jawQFCE5TQlRUWVBFBQFpBQx1bmxvY2tBbW91bnQFDWFzc2V0SWRTdHJpbmcBAnR4AQZ2ZXJpZnkABBNwdWJLZXlBZG1pbnNMaXN0U3RyCQC5CQIJAMwIAgIsRXh0RUVLMTlubUtqOW1DcG5XeXZFRUpGWUFUTE1jVkVNdm9oaFVIa3lITm0JAMwIAgIsRXY1cHk1RmZCUVg5Y1pwWUtuZlFyVEI0OUJ5ZjhRbXBaV2VEVlJpbTR5VjcJAMwIAgIsRFV1dUxqWHU5OG5Cd1pjN2Zxd0NUanRBM25uUndnVGJrTVNyNVNVMk5tRFIJAMwIAgIsRFV1dUxqWHU5OG5Cd1pjN2Zxd0NUanRBM25uUndnVGJrTVNyNVNVMk5tRFIFA25pbAUDU0VQBBBwdWJLZXlBZG1pbnNMaXN0CQC1CQIJAQt2YWx1ZU9yRWxzZQIJAJ0IAgUPY29udHJvbENvbnRyYWN0Agwlc19fbXVsdGlzaWcFE3B1YktleUFkbWluc0xpc3RTdHIFA1NFUAQFY291bnQJAGQCCQBkAgkAZAIDCQD0AwMIBQJ0eAlib2R5Qnl0ZXMJAJEDAggFAnR4BnByb29mcwAACQDZBAEJAJEDAgUQcHViS2V5QWRtaW5zTGlzdAAAAAEAAAMJAPQDAwgFAnR4CWJvZHlCeXRlcwkAkQMCCAUCdHgGcHJvb2ZzAAEJANkEAQkAkQMCBRBwdWJLZXlBZG1pbnNMaXN0AAEAAQAAAwkA9AMDCAUCdHgJYm9keUJ5dGVzCQCRAwIIBQJ0eAZwcm9vZnMAAgkA2QQBCQCRAwIFEHB1YktleUFkbWluc0xpc3QAAgABAAADCQD0AwMIBQJ0eAlib2R5Qnl0ZXMJAJEDAggFAnR4BnByb29mcwADCQDZBAEJAJEDAgUQcHViS2V5QWRtaW5zTGlzdAADAAIAAAkAZwIFBWNvdW50AAMc/Ar0", "chainId": 84, "height": 2279194, "applicationStatus": "succeeded", "spentComplexity": 0 } View: original | compacted Prev: 4QUN8aBuen5JnuegaH99WiavzNRH35yfiS2DYo6tMhfk Next: 3jh6dkt1R7KUXxJZ2Dbg82p4DraHUKknmgByigfFUiUR Diff:
OldNewDifferences
1-{-# STDLIB_VERSION 5 #-}
1+{-# STDLIB_VERSION 6 #-}
22 {-# SCRIPT_TYPE ACCOUNT #-}
33 {-# CONTENT_TYPE DAPP #-}
4-func getNumberByKey (key) = valueOrElse(getInteger(this, key), 0)
4+let SEP = "__"
5+
6+let MULT6 = 1000000
7+
8+let MULT8 = 100000000
9+
10+let MULTX6 = toBigInt(MULT6)
11+
12+let MULTX8 = toBigInt(MULT8)
13+
14+let MULTX18 = toBigInt(1000000000000000000)
15+
16+let WAVESIDSTR = "WAVES"
17+
18+let WAVESID = fromBase58String(WAVESIDSTR)
19+
20+let DAYMILLIS = 86400000
21+
22+let IdxControlCfgNeutrinoDapp = 1
23+
24+let IdxControlCfgAuctionDapp = 2
25+
26+let IdxControlCfgRpdDapp = 3
27+
28+let IdxControlCfgMathDapp = 4
29+
30+let IdxControlCfgLiquidationDapp = 5
31+
32+let IdxControlCfgRestDapp = 6
33+
34+let IdxControlCfgNodeRegistryDapp = 7
35+
36+let IdxControlCfgNsbtStakingDapp = 8
37+
38+let IdxControlCfgMediatorDapp = 9
39+
40+let IdxControlCfgSurfStakingDapp = 10
41+
42+let IdxControlCfgGnsbtControllerDapp = 11
43+
44+func getStringOrFail (address,key) = valueOrErrorMessage(getString(address, key), (((("mandatory " + toString(address)) + ".") + key) + " is not defined"))
545
646
7-func getStringByKey (key) = valueOrElse(getString(this, key), "")
47+func getIntOrFail (key) = valueOrErrorMessage(getInteger(this, key), (("Mandatory this." + key) + " is not defined"))
848
949
10-func getBoolByKey (key) = valueOrElse(getBoolean(this, key), false)
50+func getStrOrElse (key,defaultVal) = valueOrElse(getString(this, key), defaultVal)
1151
1252
13-func getNumberByAddressAndKey (address,key) = valueOrElse(getInteger(addressFromStringValue(address), key), 0)
53+func keyMinLockAmount () = "%s__minLockAmount"
1454
1555
16-func getStringByAddressAndKey (address,key) = valueOrElse(getString(address, key), "")
56+func keyStakedAssetId () = "%s__stakedAssetId"
1757
58+
59+func keyControlAddress () = "%s%s__config__controlAddress"
60+
61+
62+func keyControlCfg () = "%s__controlConfig"
63+
64+
65+func keySupportedRewardAssets () = "supportedRewardAssets"
66+
67+
68+func readControlCfgOrFail (control) = split_4C(getStringOrFail(control, keyControlCfg()), SEP)
69+
70+
71+func getContractAddressOrFail (controlCfg,idx) = valueOrErrorMessage(addressFromString(controlCfg[idx]), ("Control cfg doesn't contain address at index " + toString(idx)))
72+
73+
74+let controlContract = addressFromStringValue(valueOrElse(getString(this, keyControlAddress()), "3N4NS7d4Jo9a6F14LiFUKKYVdUkkf2eP4Zx"))
75+
76+let controlCfg = readControlCfgOrFail(controlContract)
77+
78+let mathContract = getContractAddressOrFail(controlCfg, IdxControlCfgMathDapp)
79+
80+let neutrinoContract = getContractAddressOrFail(controlCfg, IdxControlCfgNeutrinoDapp)
81+
82+let auctionContract = getContractAddressOrFail(controlCfg, IdxControlCfgAuctionDapp)
83+
84+let stakedAssetIdStr = getStringOrFail(this, keyStakedAssetId())
85+
86+let stakedAssetId = fromBase58String(stakedAssetIdStr)
87+
88+let minLockAmount = getIntOrFail(keyMinLockAmount())
89+
90+let supportedAssetsStr = getStrOrElse(keySupportedRewardAssets(), "")
91+
92+let supportedAssetsList = split(supportedAssetsStr, "_")
93+
94+func keyLockParamUserAmount (userAddress) = makeString(["%s%s%s", "paramByUser", toString(userAddress), "amount"], SEP)
95+
96+
97+func keyLockParamStartBlock (userAddress) = makeString(["%s%s%s", "paramByUser", toString(userAddress), "start"], SEP)
98+
99+
100+func keyHistoryRecord (type,userAddress,txId) = makeString(["%s%s%s%s", "history", type, userAddress, toBase58String(txId)], SEP)
101+
102+
103+func keyLockParamTotalAmount () = makeString(["%s%s", "stats", "activeTotalLocked"], SEP)
104+
105+
106+func keyStatsLocksCount () = makeString(["%s%s", "stats", "locksCount"], SEP)
107+
108+
109+func keyStatsUsersCount () = makeString(["%s%s", "stats", "activeUsersCount"], SEP)
110+
111+
112+func keyStatsDepositAmtByDay (timestamp) = makeString(["%s%s%d", "stats", "depositAmtByDay", toString(timestamp)], SEP)
113+
114+
115+func keyStatsDepositAmtTotals () = makeString(["%s%s%d", "stats", "depositAmtTotals"], SEP)
116+
117+
118+func keyNextPeriod () = "%s__nextPeriod"
119+
120+
121+func keyDepositNumLast () = makeString(["%s%s%s", "dep", "lastNum"], SEP)
122+
123+
124+func keyUserRewardFromDepositNum (userAddress) = makeString(["%s%s%s", "userRwdFromDepNum", userAddress], SEP)
125+
126+
127+func keyRewardPerNsbtSumAt (depositNum,tkn) = makeString(["%s%d", "rwdPerNsbtSumByDepNum", toString(depositNum), tkn], SEP)
128+
129+
130+func keyReward (userAddress,tkn) = makeString(["%s%s%s", "rwd", userAddress, tkn], SEP)
131+
132+
133+func keyClaimed (userAddress,tkn) = makeString(["%s%s%s", "clm", userAddress, tkn], SEP)
134+
135+
136+func keyNotDistributedReward (tkn) = makeString(["%s%s", "notDistributed", tkn], SEP)
137+
138+
139+func keyLegacyUserBalance (userAddr,tkn) = makeString(["rpd_balance", tkn, userAddr], SEP)
140+
141+
142+func keyLegacyTotalBalance (tkn) = makeString(["rpd_balance", tkn], SEP)
143+
144+
145+func toX18 (origVal,origMult) = fraction(toBigInt(origVal), MULTX18, origMult)
146+
147+
148+func getIntOrZero (key) = valueOrElse(getInteger(this, key), 0)
149+
150+
151+func getIntOrElse (key,defaultVal) = valueOrElse(getInteger(this, key), defaultVal)
152+
153+
154+func toAddressOrFail (addressStr) = valueOrErrorMessage(addressFromString(addressStr), ("couldn't parse passed addressStr=" + addressStr))
155+
156+
157+func toAssetVect (assetStr) = if ((assetStr == WAVESIDSTR))
158+ then unit
159+ else fromBase58String(assetStr)
160+
161+
162+func asInt (val) = match val {
163+ case valInt: Int =>
164+ valInt
165+ case _ =>
166+ throw("fail to cast into Int")
167+}
168+
169+
170+func asSwapParamsSTRUCT (v) = match v {
171+ case struct: (Int, Int, Int, Int, Int, Int, Int) =>
172+ struct
173+ case _ =>
174+ throw("fail to cast into Int")
175+}
176+
177+
178+func formatHistoryRecord (userAddress,oldAmount,newAmount) = makeString(["%s%d%d%d%d", userAddress, toString(lastBlock.height), toString(lastBlock.timestamp), toString(oldAmount), toString(newAmount)], SEP)
179+
180+
181+func formatClaimHistoryRecord (userAddress,claimedRewards) = makeString(["%s%d%d%s", userAddress, toString(lastBlock.height), toString(lastBlock.timestamp), claimedRewards], SEP)
182+
183+
184+func HistoryRecordEntry (type,userAddress,txId,oldAmount,newAmount) = StringEntry(keyHistoryRecord(type, userAddress, txId), formatHistoryRecord(userAddress, oldAmount, newAmount))
185+
186+
187+func ClaimHistoryEntry (userAddress,txId,claimedRewards) = StringEntry(keyHistoryRecord("claim", userAddress, txId), formatClaimHistoryRecord(userAddress, claimedRewards))
188+
189+
190+func StatsResult (totalLockedInc,lockCountInc,usersCountInc) = {
191+ let locksCount = getIntOrZero(keyStatsLocksCount())
192+ let usersCount = getIntOrZero(keyStatsUsersCount())
193+ let totalAmount = getIntOrZero(keyLockParamTotalAmount())
194+ let totalAmountNew = (totalAmount + totalLockedInc)
195+ $Tuple3([IntegerEntry(keyStatsLocksCount(), (locksCount + lockCountInc)), IntegerEntry(keyStatsUsersCount(), (usersCount + usersCountInc)), IntegerEntry(keyLockParamTotalAmount(), totalAmountNew), IntegerEntry(keyLegacyTotalBalance(stakedAssetIdStr), totalAmountNew)], totalAmount, totalAmountNew)
196+ }
197+
198+
199+func LockParamsEntry (userAddress,amount,stakingStartHeight) = [IntegerEntry(keyLockParamUserAmount(userAddress), amount), IntegerEntry(keyLockParamStartBlock(userAddress), stakingStartHeight)]
200+
201+
202+func getParamsOrFail () = $Tuple2(fromBase58String(getStringOrFail(this, keyStakedAssetId())), getIntOrFail(keyMinLockAmount()))
203+
204+
205+func isActiveUser (userAddress) = (getIntOrElse(keyLockParamUserAmount(userAddress), 0) > 0)
206+
207+
208+func getUserParamsOrUnit (userAddress) = if (isActiveUser(userAddress))
209+ then $Tuple3(false, getIntOrFail(keyLockParamUserAmount(userAddress)), getIntOrFail(keyLockParamStartBlock(userAddress)))
210+ else unit
211+
212+
213+func getUserParamsOrFail (userAddress) = valueOrErrorMessage(getUserParamsOrUnit(userAddress), (("User " + toString(userAddress)) + " is not defined"))
214+
215+
216+func calcReward (userAddress,assetId,stakedAmountX,depositNumUser,depositNumLast) = {
217+ let rewardPerNsbtSumLastKEY = keyRewardPerNsbtSumAt(depositNumLast, assetId)
218+ let sumLastX18 = parseBigIntValue(getStrOrElse(keyRewardPerNsbtSumAt(depositNumLast, assetId), "0"))
219+ let sumUserX18 = parseBigIntValue(getStrOrElse(keyRewardPerNsbtSumAt(depositNumUser, assetId), "0"))
220+ let rewardDynamicPart = toInt(fraction((sumLastX18 - sumUserX18), stakedAmountX, MULTX18))
221+ let rewardCachedPartKEY = keyReward(userAddress, assetId)
222+ let rewardCachedPart = getIntOrElse(rewardCachedPartKEY, 0)
223+ $Tuple4((rewardCachedPart + rewardDynamicPart), rewardCachedPart, rewardDynamicPart, rewardCachedPartKEY)
224+ }
225+
226+
227+func toStartOfDay (timestamp) = ((timestamp / DAYMILLIS) * DAYMILLIS)
228+
229+
230+func findElementPosition (src,element,sep) = {
231+ let elementStart = valueOrErrorMessage(indexOf(src, element), ((("there is no substring " + element) + " in ") + src))
232+ if ((elementStart == 0))
233+ then 0
234+ else {
235+ let left = take(src, elementStart)
236+ (size(split(left, sep)) - 1)
237+ }
238+ }
239+
240+
241+let DepositTotalsPREFIX = "%d%d"
242+
243+func updateDepositTotals (currVal,idxToUpdate,deltaAmt) = {
244+ let currArr = split(currVal, SEP)
245+ func updDepTotByIdx (idx) = if ((idx != idxToUpdate))
246+ then currArr[idx]
247+ else toString((parseIntValue(currArr[idx]) + deltaAmt))
248+
249+ makeString([DepositTotalsPREFIX, updDepTotByIdx(1), updDepTotByIdx(2)], SEP)
250+ }
251+
252+
253+func DepositsTotalsEntries (depositAmount,assetIdStr) = {
254+ let startOfDay = toStartOfDay(lastBlock.timestamp)
255+ let byDayKEY = keyStatsDepositAmtByDay(startOfDay)
256+ let totalsKEY = keyStatsDepositAmtTotals()
257+ let position = findElementPosition(supportedAssetsStr, assetIdStr, "_")
258+ let defaultDATA = (DepositTotalsPREFIX + "__0__0")
259+ let currTotalsDATA = valueOrElse(getString(this, totalsKEY), defaultDATA)
260+ let newTotalsDATA = updateDepositTotals(currTotalsDATA, (position + 1), depositAmount)
261+[StringEntry(totalsKEY, newTotalsDATA), StringEntry(byDayKEY, newTotalsDATA)]
262+ }
263+
264+
265+func RewardEntries (isNewUser,userAddress,stakedAmount) = {
266+ let stakedAmountX = toBigInt(stakedAmount)
267+ let userRewardFromDepositNumKEY = keyUserRewardFromDepositNum(userAddress)
268+ let depositNumUser = getIntOrElse(userRewardFromDepositNumKEY, -1)
269+ let depositNumLast = getIntOrElse(keyDepositNumLast(), -1)
270+ func forEachAssetCacheUserReward (accum,asset) = {
271+ let $t01057810713 = calcReward(userAddress, asset, stakedAmountX, depositNumUser, depositNumLast)
272+ let rewardTotal = $t01057810713._1
273+ let cached = $t01057810713._2
274+ let dynamic = $t01057810713._3
275+ let rewardCachedPartKEY = $t01057810713._4
276+ (accum :+ IntegerEntry(rewardCachedPartKEY, rewardTotal))
277+ }
278+
279+ if (if ((depositNumLast == -1))
280+ then (depositNumUser == -1)
281+ else false)
282+ then nil
283+ else if (if ((depositNumLast == -1))
284+ then (depositNumUser > -1)
285+ else false)
286+ then throw("invalid depositNumLast and depositNumUser state")
287+ else if (if ((depositNumLast > -1))
288+ then (depositNumUser >= -1)
289+ else false)
290+ then if (isNewUser)
291+ then [IntegerEntry(userRewardFromDepositNumKEY, depositNumLast)]
292+ else ({
293+ let $l = supportedAssetsList
294+ let $s = size($l)
295+ let $acc0 = nil
296+ func $f0_1 ($a,$i) = if (($i >= $s))
297+ then $a
298+ else forEachAssetCacheUserReward($a, $l[$i])
299+
300+ func $f0_2 ($a,$i) = if (($i >= $s))
301+ then $a
302+ else throw("List size exceeds 2")
303+
304+ $f0_2($f0_1($f0_1($acc0, 0), 1), 2)
305+ } :+ IntegerEntry(userRewardFromDepositNumKEY, depositNumLast))
306+ else throw(((("uncovered condition: depositNumLast=" + toString(depositNumLast)) + " depositNumUser=") + toString(depositNumUser)))
307+ }
308+
309+
310+func IncrementNotDistributedRewardEntry (tkn,amountInc) = {
311+ let notDistributedRewardKEY = keyNotDistributedReward(tkn)
312+ let notDistributedReward = getIntOrElse(notDistributedRewardKEY, 0)
313+[IntegerEntry(notDistributedRewardKEY, (notDistributedReward + amountInc))]
314+ }
315+
316+
317+func mergeStake (userAddress,amountToAdd) = {
318+ let $t01353813654 = valueOrElse(getUserParamsOrUnit(userAddress), $Tuple3(true, 0, height))
319+ let isNewUser = $t01353813654._1
320+ let stakedAmount = $t01353813654._2
321+ let stakingStartHeight = $t01353813654._3
322+ let stakedAmountNEW = if (isNewUser)
323+ then amountToAdd
324+ else (amountToAdd + stakedAmount)
325+ $Tuple4(isNewUser, stakedAmount, stakingStartHeight, stakedAmountNEW)
326+ }
327+
328+
329+func commonStake (userAddress,i) = if ((size(i.payments) != 1))
330+ then throw("Invalid payments size")
331+ else {
332+ let payment = i.payments[0]
333+ let amount = payment.amount
334+ let invalidAssetMessage = (("Invalid asset. " + toBase58String(stakedAssetId)) + " is expected")
335+ let assetId = valueOrErrorMessage(payment.assetId, invalidAssetMessage)
336+ if ((assetId != stakedAssetId))
337+ then throw(invalidAssetMessage)
338+ else {
339+ let userAddressStr = toString(userAddress)
340+ let mergedData = mergeStake(userAddress, amount)
341+ let isNewUser = mergedData._1
342+ let stakedAmount = mergedData._2
343+ let stakingStartHeight = mergedData._3
344+ let stakedAmountNEW = mergedData._4
345+ if ((minLockAmount > stakedAmountNEW))
346+ then throw(("Min lock amount is " + toString(minLockAmount)))
347+ else {
348+ let $t01462414726 = StatsResult(amount, 1, if (isNewUser)
349+ then 1
350+ else 0)
351+ let statsEntries = $t01462414726._1
352+ let totalStaked = $t01462414726._2
353+ let totalStakedNew = $t01462414726._3
354+ ((([HistoryRecordEntry("stake", userAddressStr, i.transactionId, stakedAmount, stakedAmountNEW)] ++ RewardEntries(isNewUser, userAddressStr, stakedAmount)) ++ LockParamsEntry(userAddress, stakedAmountNEW, stakingStartHeight)) ++ statsEntries)
355+ }
356+ }
357+ }
358+
359+
360+func commonUnstake (amount,i) = if ((size(i.payments) != 0))
361+ then throw("unstake doesn't require any payment")
362+ else {
363+ let userAddress = i.caller
364+ let userAddressStr = toString(userAddress)
365+ let $t01519815283 = getUserParamsOrFail(userAddress)
366+ let isNewUser = $t01519815283._1
367+ let stakedAmount = $t01519815283._2
368+ let stakingStartHeight = $t01519815283._3
369+ let swapParamsSTRUCT = asSwapParamsSTRUCT(reentrantInvoke(neutrinoContract, "swapParamsByUserSYSREADONLY", [userAddressStr, 0], nil))
370+ let swapLimitSpentInUsdn = swapParamsSTRUCT._2
371+ let blcks2LmtReset = swapParamsSTRUCT._3
372+ if ((swapLimitSpentInUsdn > 0))
373+ then throw((("You have already made a swap operation. Wait " + toString((height + blcks2LmtReset))) + " height to unstake"))
374+ else if ((0 >= stakedAmount))
375+ then throw("Nothing to unstake")
376+ else if ((amount > stakedAmount))
377+ then throw(((("Requested " + toString(amount)) + ", but staked only ") + toString(stakedAmount)))
378+ else {
379+ let stakedAmountNEW = (stakedAmount - amount)
380+ let $t01592116079 = StatsResult(-(amount), if ((amount == stakedAmount))
381+ then -1
382+ else 0, if ((amount == stakedAmount))
383+ then -1
384+ else 0)
385+ let statsEntries = $t01592116079._1
386+ let totalStaked = $t01592116079._2
387+ let totalStakedNew = $t01592116079._3
388+ ((([ScriptTransfer(userAddress, amount, stakedAssetId), HistoryRecordEntry("unstake", userAddressStr, i.transactionId, stakedAmount, stakedAmountNEW)] ++ RewardEntries(false, userAddressStr, stakedAmount)) ++ LockParamsEntry(userAddress, stakedAmountNEW, stakingStartHeight)) ++ statsEntries)
389+ }
390+ }
391+
392+
393+func commonClaim (userAddress,i) = {
394+ let userAddressStr = toString(userAddress)
395+ if ((size(i.payments) > 0))
396+ then throw("payments are not accepted")
397+ else {
398+ let $t01657216677 = valueOrElse(getUserParamsOrUnit(userAddress), $Tuple3(true, 0, 0))
399+ let isNewUser = $t01657216677._1
400+ let stakedAmount = $t01657216677._2
401+ let stakingStart = $t01657216677._3
402+ let stakedAmountX = toBigInt(stakedAmount)
403+ let userRewardFromDepositNumKEY = keyUserRewardFromDepositNum(userAddressStr)
404+ let depositNumUser = getIntOrElse(userRewardFromDepositNumKEY, -1)
405+ let depositNumLast = getIntOrElse(keyDepositNumLast(), -1)
406+ func forEachAssetCalcUnclaimedReward (accum,asset) = {
407+ let $t01704817186 = calcReward(userAddressStr, asset, stakedAmountX, depositNumUser, depositNumLast)
408+ let rewardTotal = $t01704817186._1
409+ let cached = $t01704817186._2
410+ let dynamic = $t01704817186._3
411+ let rewardCachedPartKEY = $t01704817186._4
412+ let claimedKEY = keyClaimed(userAddressStr, asset)
413+ let $t01724617283 = accum
414+ let data = $t01724617283._1
415+ let claimedAmtByAsset = $t01724617283._2
416+ let newPart = makeString([asset, toString(rewardTotal)], ":")
417+ let claimedAmtByAssetNew = makeString([claimedAmtByAsset, newPart], "_")
418+ if ((0 >= rewardTotal))
419+ then $Tuple2(data, claimedAmtByAssetNew)
420+ else $Tuple2((((data :+ ScriptTransfer(userAddress, rewardTotal, toAssetVect(asset))) :+ IntegerEntry(claimedKEY, (valueOrElse(getInteger(claimedKEY), 0) + rewardTotal))) :+ IntegerEntry(rewardCachedPartKEY, 0)), claimedAmtByAssetNew)
421+ }
422+
423+ let $t01774317856 = {
424+ let $l = supportedAssetsList
425+ let $s = size($l)
426+ let $acc0 = $Tuple2(nil, "")
427+ func $f0_1 ($a,$i) = if (($i >= $s))
428+ then $a
429+ else forEachAssetCalcUnclaimedReward($a, $l[$i])
430+
431+ func $f0_2 ($a,$i) = if (($i >= $s))
432+ then $a
433+ else throw("List size exceeds 2")
434+
435+ $f0_2($f0_1($f0_1($acc0, 0), 1), 2)
436+ }
437+ let transfers = $t01774317856._1
438+ let claimedAmtByAssetResult = $t01774317856._2
439+ if ((0 >= size(transfers)))
440+ then $Tuple2(nil, 0)
441+ else $Tuple2(((transfers :+ IntegerEntry(userRewardFromDepositNumKEY, depositNumLast)) :+ ClaimHistoryEntry(userAddressStr, i.transactionId, drop(claimedAmtByAssetResult, 1))), size(transfers))
442+ }
443+ }
444+
445+
446+let USDNTYPE = "USDN"
447+
448+let NSBTTYPE = "NSBT"
18449
19450 let NeutrinoAssetIdKey = "neutrino_asset_id"
20451
21452 let NeutrinoContractKey = "neutrino_contract"
22453
454+let NsbtAssetIdKey = "bond_asset_id"
455+
23456 let BalanceKey = "rpd_balance"
24457
25-let ControlContractKey = "control_contract"
458+let neutrinoAssetId = fromBase58String(getStringOrFail(neutrinoContract, NeutrinoAssetIdKey))
26459
27-let NsbtAssetIdKey = "bond_asset_id"
460+let nsbtAssetIdStr = getStringOrFail(neutrinoContract, NsbtAssetIdKey)
28461
29-let AdminsKey = "admins"
30-
31-let USDNTYPE = "USDN"
32-
33-let NSBTTYPE = "NSBT"
462+let nsbtAssetId = fromBase58String(nsbtAssetIdStr)
34463
35464 func getUserBalanceKey (owner,assetId) = makeString([BalanceKey, assetId, owner], "_")
36465
38467 func getContractBalanceKey (assetId) = ((BalanceKey + "_") + assetId)
39468
40469
41-func getExpireProposalKey (hash) = (("proposal_expire" + "_") + hash)
470+func getContractBalance (assetId) = getIntOrElse(getContractBalanceKey(assetId), 0)
42471
43472
44-func getOwnerProposalKey (hash) = (("proposal_owner" + "_") + hash)
45-
46-
47-func getArgumentsProposalKey (hash) = (("proposal_arguments" + "_") + hash)
48-
49-
50-func getVoteKey (owner,hash) = (((("proposal_vote" + "_") + owner) + "_") + hash)
51-
52-
53-func convertJsonArrayToList (jsonArray) = split(jsonArray, ",")
54-
55-
56-let neutrinoContract = addressFromStringValue(getStringByKey(NeutrinoContractKey))
57-
58-let controlContract = addressFromStringValue(getStringByAddressAndKey(neutrinoContract, ControlContractKey))
59-
60-let neutrinoAssetId = fromBase58String(getStringByAddressAndKey(neutrinoContract, NeutrinoAssetIdKey))
61-
62-let nsbtAssetIdStr = getStringByAddressAndKey(neutrinoContract, NsbtAssetIdKey)
63-
64-let nsbtAssetId = fromBase58String(nsbtAssetIdStr)
65-
66-func getContractBalance (assetId) = getNumberByKey(getContractBalanceKey(assetId))
67-
68-
69-func getUserBalance (owner,assetId) = getNumberByKey(getUserBalanceKey(owner, assetId))
70-
71-
72-func getRewardsConfigKey (owner,share,receiver) = makeString(["stakingconfig", owner, toString(share), receiver], "_")
73-
74-
75-func getCurrentRewardsConfigKey (owner) = ("stakingconfig_current_" + owner)
76-
77-
78-func getRewardsConfigStartKey (configKey,isStart) = (configKey + (if (isStart)
79- then "_start"
80- else "_end"))
81-
82-
83-func getRewardConfigInitialShare (owner) = (owner + "_initialShare")
473+func getUserBalance (owner,assetId) = getIntOrElse(getUserBalanceKey(owner, assetId), 0)
84474
85475
86476 func getValidStakingAssetOrFail (stakingType,assetId) = if (if ((stakingType == USDNTYPE))
98488 else assetId
99489
100490
101-func internalLockNeutrino (stakingType,i,receiver,share) = {
102- let pmt = value(i.payments[0])
103- let assetId = getValidStakingAssetOrFail(stakingType, value(pmt.assetId))
104- if (!(isDefined(addressFromString(receiver))))
105- then throw(("Invalid address format " + receiver))
106- else if ((share > 100))
107- then throw("staking rewards share cannot be higher than 100%")
108- else if ((1 > share))
109- then throw("staking rewards share cannot be lower than 1%")
110- else {
111- let account = toString(i.caller)
112- let assetIdString = toBase58String(assetId)
113- let currentConfig = getStringByKey(getCurrentRewardsConfigKey(account))
114- let correctData = if ((currentConfig != ""))
115- then {
116- let currentConfigData = split(currentConfig, "_")
117- let currShare = parseIntValue(currentConfigData[2])
118- let currReceiver = currentConfigData[3]
119- let notMigratedInitialShare = getNumberByKey(getRewardConfigInitialShare(account))
120- let actualInitialShare = if ((notMigratedInitialShare == 0))
121- then currShare
122- else notMigratedInitialShare
123- let newShare = if (if ((actualInitialShare > share))
124- then true
125- else (currReceiver != receiver))
126- then actualInitialShare
127- else share
128-[toString(actualInitialShare), toString(newShare), currReceiver]
129- }
130- else [toString(share), toString(share), receiver]
131- let correctInitialShare = parseIntValue(correctData[0])
132- let correctShare = parseIntValue(correctData[1])
133- let correctReceiver = correctData[2]
134- let newCurrentConfig = getRewardsConfigKey(account, correctShare, correctReceiver)
135- let isNewConfig = !((currentConfig == newCurrentConfig))
136- let end = if (isNewConfig)
137- then height
138- else 0
139- let start = if (isNewConfig)
140- then height
141- else getNumberByKey(getRewardsConfigStartKey(newCurrentConfig, true))
142- $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)
143- }
144- }
145-
146-
147491 func internalUnlock (stakingType,i,unlockAmount,assetIdParam) = {
148492 let account = toString(i.caller)
149493 let assetId = getValidStakingAssetOrFail(stakingType, fromBase58String(assetIdParam))
156500
157501
158502 @Callable(i)
159-func lockNeutrinoSP (receiver,share) = internalLockNeutrino(USDNTYPE, i, receiver, share)
503+func constructor (minLockAmount,supportedRewardAssets,pStakedAssetId) = if ((i.caller != this))
504+ then throw("Permission denied")
505+ else [IntegerEntry(keyMinLockAmount(), minLockAmount), StringEntry(keySupportedRewardAssets(), supportedRewardAssets), StringEntry(keyStakedAssetId(), pStakedAssetId)]
160506
161507
162508
163509 @Callable(i)
164-func lockNeutrino () = internalLockNeutrino(USDNTYPE, i, toString(i.caller), 100)
510+func stake () = commonStake(i.caller, i)
165511
166512
167513
168514 @Callable(i)
169-func lockNsbtSP (receiver,share) = internalLockNeutrino(NSBTTYPE, i, receiver, share)
515+func stakeByOriginCaller () = commonStake(i.originCaller, i)
170516
171517
172518
173519 @Callable(i)
174-func lockNsbt () = internalLockNeutrino(NSBTTYPE, i, toString(i.caller), 100)
520+func unstake (amount) = commonUnstake(amount, i)
175521
176522
177523
178524 @Callable(i)
179-func unlockNeutrino (unlockAmount,assetIdString) = internalUnlock(USDNTYPE, i, unlockAmount, assetIdString)
525+func deposit () = if ((size(i.payments) != 1))
526+ then throw("exact 1 payment is allowed only")
527+ else {
528+ let pmt = i.payments[0]
529+ let amount = pmt.amount
530+ let pmtAssetId = valueOrElse(pmt.assetId, WAVESID)
531+ let pmtAssetIdStr = toBase58String(pmtAssetId)
532+ let pmtMultX = if ((pmtAssetId == WAVESID))
533+ then MULTX8
534+ else MULTX6
535+ let amountX = toBigInt(amount)
536+ let totalStaked = getIntOrElse(keyLockParamTotalAmount(), 0)
537+ let totalStakedX = toBigInt(totalStaked)
538+ if ((0 > totalStaked))
539+ then throw("TODO: case is not supported")
540+ else if ((totalStaked == 0))
541+ then IncrementNotDistributedRewardEntry(pmtAssetIdStr, amount)
542+ else {
543+ let rewardPerNsbtX18 = fraction(amountX, MULTX18, totalStakedX)
544+ let depositNumLastKEY = keyDepositNumLast()
545+ let depositNumLast = getIntOrElse(depositNumLastKEY, -1)
546+ let depositNumNew = (depositNumLast + 1)
547+ if (!(contains(supportedAssetsStr, pmtAssetIdStr)))
548+ then throw(((supportedAssetsStr + " doesn't contain ") + pmtAssetIdStr))
549+ else {
550+ func refreshRewardPerNsbtSUM (accum,nextAsset) = {
551+ let rewardPerNsbtSumNewKEY = keyRewardPerNsbtSumAt(depositNumNew, nextAsset)
552+ let sumLastStr = getStrOrElse(keyRewardPerNsbtSumAt(depositNumLast, nextAsset), "0")
553+ (accum :+ (if ((nextAsset == pmtAssetIdStr))
554+ then StringEntry(rewardPerNsbtSumNewKEY, toString((parseBigIntValue(sumLastStr) + rewardPerNsbtX18)))
555+ else StringEntry(rewardPerNsbtSumNewKEY, sumLastStr)))
556+ }
557+
558+ (({
559+ let $l = supportedAssetsList
560+ let $s = size($l)
561+ let $acc0 = nil
562+ func $f0_1 ($a,$i) = if (($i >= $s))
563+ then $a
564+ else refreshRewardPerNsbtSUM($a, $l[$i])
565+
566+ func $f0_2 ($a,$i) = if (($i >= $s))
567+ then $a
568+ else throw("List size exceeds 2")
569+
570+ $f0_2($f0_1($f0_1($acc0, 0), 1), 2)
571+ } :+ IntegerEntry(depositNumLastKEY, depositNumNew)) ++ DepositsTotalsEntries(amount, pmtAssetIdStr))
572+ }
573+ }
574+ }
575+
576+
577+
578+@Callable(i)
579+func claimRewards () = commonClaim(i.caller, i)
580+
581+
582+
583+@Callable(i)
584+func claimRewardsByOriginCaller () = commonClaim(i.originCaller, i)
585+
586+
587+
588+@Callable(i)
589+func unclaimedRewardsREADONLY (userAddressStr) = {
590+ func forEachAssetZeroReward (accum,asset) = ((accum + makeString([asset, "0", "0"], ":")) + "_")
591+
592+ let unclaimedRewardStr = if ((userAddressStr == ""))
593+ then {
594+ let $l = supportedAssetsList
595+ let $s = size($l)
596+ let $acc0 = ""
597+ func $f0_1 ($a,$i) = if (($i >= $s))
598+ then $a
599+ else forEachAssetZeroReward($a, $l[$i])
600+
601+ func $f0_2 ($a,$i) = if (($i >= $s))
602+ then $a
603+ else throw("List size exceeds 2")
604+
605+ $f0_2($f0_1($f0_1($acc0, 0), 1), 2)
606+ }
607+ else {
608+ let userAddress = addressFromStringValue(userAddressStr)
609+ let $t02291123022 = valueOrElse(getUserParamsOrUnit(userAddress), $Tuple3(true, 0, 0))
610+ let isNewUser = $t02291123022._1
611+ let stakedAmount = $t02291123022._2
612+ let stakingStartHeight = $t02291123022._3
613+ let stakedAmountX = toBigInt(stakedAmount)
614+ let userRewardFromDepositNumKEY = keyUserRewardFromDepositNum(userAddressStr)
615+ let depositNumUser = getIntOrElse(userRewardFromDepositNumKEY, -1)
616+ let depositNumLast = getIntOrElse(keyDepositNumLast(), -1)
617+ func forEachAssetCalcUnclaimedReward (accum,asset) = {
618+ let $t02336823506 = calcReward(userAddressStr, asset, stakedAmountX, depositNumUser, depositNumLast)
619+ let rewardTotal = $t02336823506._1
620+ let cached = $t02336823506._2
621+ let dynamic = $t02336823506._3
622+ let rewardCachedPartKEY = $t02336823506._4
623+ let claimed = valueOrElse(getInteger(keyClaimed(userAddressStr, asset)), 0)
624+ ((accum + makeString([asset, toString(rewardTotal), toString(claimed)], ":")) + "_")
625+ }
626+
627+ let $l = supportedAssetsList
628+ let $s = size($l)
629+ let $acc0 = ""
630+ func $f0_1 ($a,$i) = if (($i >= $s))
631+ then $a
632+ else forEachAssetCalcUnclaimedReward($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+ }
640+ $Tuple2(nil, dropRight(unclaimedRewardStr, 1))
641+ }
642+
643+
644+
645+@Callable(i)
646+func usdnStakingSYSREADONLY (userAddressStrOrEmpty,usdnDiff) = {
647+ let usdnTotalAmtStaked = getIntOrElse(keyLockParamTotalAmount(), 0)
648+ if ((userAddressStrOrEmpty == ""))
649+ then $Tuple2(nil, [0, usdnTotalAmtStaked, 0])
650+ else {
651+ let userAddress = toAddressOrFail(userAddressStrOrEmpty)
652+ let mergedData = mergeStake(userAddress, usdnDiff)
653+ let isNewUser = mergedData._1
654+ let usdnStakedByUser = mergedData._2
655+ let stakingStartHeight = mergedData._3
656+ let stakedAmountNEW = mergedData._4
657+ $Tuple2(nil, [usdnStakedByUser, usdnTotalAmtStaked])
658+ }
659+ }
660+
661+
662+
663+@Callable(i)
664+func configSYSREADONLY () = {
665+ let minLockAmt = getIntegerValue(keyMinLockAmount())
666+ $Tuple2(nil, [minLockAmt])
667+ }
668+
669+
670+
671+@Callable(i)
672+func lockNeutrinoSP (receiver,share) = commonStake(i.caller, i)
673+
674+
675+
676+@Callable(i)
677+func lockNeutrino () = commonStake(i.caller, i)
678+
679+
680+
681+@Callable(i)
682+func unlockNeutrino (unlockAmount,assetIdString) = commonUnstake(unlockAmount, i)
180683
181684
182685
186689
187690 @Verifier(tx)
188691 func verify () = {
189- let pubKeyAdminsList = ["ExtEEK19nmKj9mCpnWyvEEJFYATLMcVEMvohhUHkyHNm", "Ev5py5FfBQX9cZpYKnfQrTB49Byf8QmpZWeDVRim4yV7", "DUuuLjXu98nBwZc7fqwCTjtA3nnRwgTbkMSr5SU2NmDR", "5WRXFSjwcTbNfKcJs8ZqXmSSWYsSVJUtMvMqZj5hH4Nc"]
692+ let pubKeyAdminsListStr = makeString(["ExtEEK19nmKj9mCpnWyvEEJFYATLMcVEMvohhUHkyHNm", "Ev5py5FfBQX9cZpYKnfQrTB49Byf8QmpZWeDVRim4yV7", "DUuuLjXu98nBwZc7fqwCTjtA3nnRwgTbkMSr5SU2NmDR", "DUuuLjXu98nBwZc7fqwCTjtA3nnRwgTbkMSr5SU2NmDR"], SEP)
693+ let pubKeyAdminsList = split(valueOrElse(getString(controlContract, "%s__multisig"), pubKeyAdminsListStr), SEP)
190694 let count = ((((if (sigVerify(tx.bodyBytes, tx.proofs[0], fromBase58String(pubKeyAdminsList[0])))
191695 then 1
192696 else 0) + (if (sigVerify(tx.bodyBytes, tx.proofs[1], fromBase58String(pubKeyAdminsList[1])))
Full:
OldNewDifferences
1-{-# STDLIB_VERSION 5 #-}
1+{-# STDLIB_VERSION 6 #-}
22 {-# SCRIPT_TYPE ACCOUNT #-}
33 {-# CONTENT_TYPE DAPP #-}
4-func getNumberByKey (key) = valueOrElse(getInteger(this, key), 0)
4+let SEP = "__"
5+
6+let MULT6 = 1000000
7+
8+let MULT8 = 100000000
9+
10+let MULTX6 = toBigInt(MULT6)
11+
12+let MULTX8 = toBigInt(MULT8)
13+
14+let MULTX18 = toBigInt(1000000000000000000)
15+
16+let WAVESIDSTR = "WAVES"
17+
18+let WAVESID = fromBase58String(WAVESIDSTR)
19+
20+let DAYMILLIS = 86400000
21+
22+let IdxControlCfgNeutrinoDapp = 1
23+
24+let IdxControlCfgAuctionDapp = 2
25+
26+let IdxControlCfgRpdDapp = 3
27+
28+let IdxControlCfgMathDapp = 4
29+
30+let IdxControlCfgLiquidationDapp = 5
31+
32+let IdxControlCfgRestDapp = 6
33+
34+let IdxControlCfgNodeRegistryDapp = 7
35+
36+let IdxControlCfgNsbtStakingDapp = 8
37+
38+let IdxControlCfgMediatorDapp = 9
39+
40+let IdxControlCfgSurfStakingDapp = 10
41+
42+let IdxControlCfgGnsbtControllerDapp = 11
43+
44+func getStringOrFail (address,key) = valueOrErrorMessage(getString(address, key), (((("mandatory " + toString(address)) + ".") + key) + " is not defined"))
545
646
7-func getStringByKey (key) = valueOrElse(getString(this, key), "")
47+func getIntOrFail (key) = valueOrErrorMessage(getInteger(this, key), (("Mandatory this." + key) + " is not defined"))
848
949
10-func getBoolByKey (key) = valueOrElse(getBoolean(this, key), false)
50+func getStrOrElse (key,defaultVal) = valueOrElse(getString(this, key), defaultVal)
1151
1252
13-func getNumberByAddressAndKey (address,key) = valueOrElse(getInteger(addressFromStringValue(address), key), 0)
53+func keyMinLockAmount () = "%s__minLockAmount"
1454
1555
16-func getStringByAddressAndKey (address,key) = valueOrElse(getString(address, key), "")
56+func keyStakedAssetId () = "%s__stakedAssetId"
1757
58+
59+func keyControlAddress () = "%s%s__config__controlAddress"
60+
61+
62+func keyControlCfg () = "%s__controlConfig"
63+
64+
65+func keySupportedRewardAssets () = "supportedRewardAssets"
66+
67+
68+func readControlCfgOrFail (control) = split_4C(getStringOrFail(control, keyControlCfg()), SEP)
69+
70+
71+func getContractAddressOrFail (controlCfg,idx) = valueOrErrorMessage(addressFromString(controlCfg[idx]), ("Control cfg doesn't contain address at index " + toString(idx)))
72+
73+
74+let controlContract = addressFromStringValue(valueOrElse(getString(this, keyControlAddress()), "3N4NS7d4Jo9a6F14LiFUKKYVdUkkf2eP4Zx"))
75+
76+let controlCfg = readControlCfgOrFail(controlContract)
77+
78+let mathContract = getContractAddressOrFail(controlCfg, IdxControlCfgMathDapp)
79+
80+let neutrinoContract = getContractAddressOrFail(controlCfg, IdxControlCfgNeutrinoDapp)
81+
82+let auctionContract = getContractAddressOrFail(controlCfg, IdxControlCfgAuctionDapp)
83+
84+let stakedAssetIdStr = getStringOrFail(this, keyStakedAssetId())
85+
86+let stakedAssetId = fromBase58String(stakedAssetIdStr)
87+
88+let minLockAmount = getIntOrFail(keyMinLockAmount())
89+
90+let supportedAssetsStr = getStrOrElse(keySupportedRewardAssets(), "")
91+
92+let supportedAssetsList = split(supportedAssetsStr, "_")
93+
94+func keyLockParamUserAmount (userAddress) = makeString(["%s%s%s", "paramByUser", toString(userAddress), "amount"], SEP)
95+
96+
97+func keyLockParamStartBlock (userAddress) = makeString(["%s%s%s", "paramByUser", toString(userAddress), "start"], SEP)
98+
99+
100+func keyHistoryRecord (type,userAddress,txId) = makeString(["%s%s%s%s", "history", type, userAddress, toBase58String(txId)], SEP)
101+
102+
103+func keyLockParamTotalAmount () = makeString(["%s%s", "stats", "activeTotalLocked"], SEP)
104+
105+
106+func keyStatsLocksCount () = makeString(["%s%s", "stats", "locksCount"], SEP)
107+
108+
109+func keyStatsUsersCount () = makeString(["%s%s", "stats", "activeUsersCount"], SEP)
110+
111+
112+func keyStatsDepositAmtByDay (timestamp) = makeString(["%s%s%d", "stats", "depositAmtByDay", toString(timestamp)], SEP)
113+
114+
115+func keyStatsDepositAmtTotals () = makeString(["%s%s%d", "stats", "depositAmtTotals"], SEP)
116+
117+
118+func keyNextPeriod () = "%s__nextPeriod"
119+
120+
121+func keyDepositNumLast () = makeString(["%s%s%s", "dep", "lastNum"], SEP)
122+
123+
124+func keyUserRewardFromDepositNum (userAddress) = makeString(["%s%s%s", "userRwdFromDepNum", userAddress], SEP)
125+
126+
127+func keyRewardPerNsbtSumAt (depositNum,tkn) = makeString(["%s%d", "rwdPerNsbtSumByDepNum", toString(depositNum), tkn], SEP)
128+
129+
130+func keyReward (userAddress,tkn) = makeString(["%s%s%s", "rwd", userAddress, tkn], SEP)
131+
132+
133+func keyClaimed (userAddress,tkn) = makeString(["%s%s%s", "clm", userAddress, tkn], SEP)
134+
135+
136+func keyNotDistributedReward (tkn) = makeString(["%s%s", "notDistributed", tkn], SEP)
137+
138+
139+func keyLegacyUserBalance (userAddr,tkn) = makeString(["rpd_balance", tkn, userAddr], SEP)
140+
141+
142+func keyLegacyTotalBalance (tkn) = makeString(["rpd_balance", tkn], SEP)
143+
144+
145+func toX18 (origVal,origMult) = fraction(toBigInt(origVal), MULTX18, origMult)
146+
147+
148+func getIntOrZero (key) = valueOrElse(getInteger(this, key), 0)
149+
150+
151+func getIntOrElse (key,defaultVal) = valueOrElse(getInteger(this, key), defaultVal)
152+
153+
154+func toAddressOrFail (addressStr) = valueOrErrorMessage(addressFromString(addressStr), ("couldn't parse passed addressStr=" + addressStr))
155+
156+
157+func toAssetVect (assetStr) = if ((assetStr == WAVESIDSTR))
158+ then unit
159+ else fromBase58String(assetStr)
160+
161+
162+func asInt (val) = match val {
163+ case valInt: Int =>
164+ valInt
165+ case _ =>
166+ throw("fail to cast into Int")
167+}
168+
169+
170+func asSwapParamsSTRUCT (v) = match v {
171+ case struct: (Int, Int, Int, Int, Int, Int, Int) =>
172+ struct
173+ case _ =>
174+ throw("fail to cast into Int")
175+}
176+
177+
178+func formatHistoryRecord (userAddress,oldAmount,newAmount) = makeString(["%s%d%d%d%d", userAddress, toString(lastBlock.height), toString(lastBlock.timestamp), toString(oldAmount), toString(newAmount)], SEP)
179+
180+
181+func formatClaimHistoryRecord (userAddress,claimedRewards) = makeString(["%s%d%d%s", userAddress, toString(lastBlock.height), toString(lastBlock.timestamp), claimedRewards], SEP)
182+
183+
184+func HistoryRecordEntry (type,userAddress,txId,oldAmount,newAmount) = StringEntry(keyHistoryRecord(type, userAddress, txId), formatHistoryRecord(userAddress, oldAmount, newAmount))
185+
186+
187+func ClaimHistoryEntry (userAddress,txId,claimedRewards) = StringEntry(keyHistoryRecord("claim", userAddress, txId), formatClaimHistoryRecord(userAddress, claimedRewards))
188+
189+
190+func StatsResult (totalLockedInc,lockCountInc,usersCountInc) = {
191+ let locksCount = getIntOrZero(keyStatsLocksCount())
192+ let usersCount = getIntOrZero(keyStatsUsersCount())
193+ let totalAmount = getIntOrZero(keyLockParamTotalAmount())
194+ let totalAmountNew = (totalAmount + totalLockedInc)
195+ $Tuple3([IntegerEntry(keyStatsLocksCount(), (locksCount + lockCountInc)), IntegerEntry(keyStatsUsersCount(), (usersCount + usersCountInc)), IntegerEntry(keyLockParamTotalAmount(), totalAmountNew), IntegerEntry(keyLegacyTotalBalance(stakedAssetIdStr), totalAmountNew)], totalAmount, totalAmountNew)
196+ }
197+
198+
199+func LockParamsEntry (userAddress,amount,stakingStartHeight) = [IntegerEntry(keyLockParamUserAmount(userAddress), amount), IntegerEntry(keyLockParamStartBlock(userAddress), stakingStartHeight)]
200+
201+
202+func getParamsOrFail () = $Tuple2(fromBase58String(getStringOrFail(this, keyStakedAssetId())), getIntOrFail(keyMinLockAmount()))
203+
204+
205+func isActiveUser (userAddress) = (getIntOrElse(keyLockParamUserAmount(userAddress), 0) > 0)
206+
207+
208+func getUserParamsOrUnit (userAddress) = if (isActiveUser(userAddress))
209+ then $Tuple3(false, getIntOrFail(keyLockParamUserAmount(userAddress)), getIntOrFail(keyLockParamStartBlock(userAddress)))
210+ else unit
211+
212+
213+func getUserParamsOrFail (userAddress) = valueOrErrorMessage(getUserParamsOrUnit(userAddress), (("User " + toString(userAddress)) + " is not defined"))
214+
215+
216+func calcReward (userAddress,assetId,stakedAmountX,depositNumUser,depositNumLast) = {
217+ let rewardPerNsbtSumLastKEY = keyRewardPerNsbtSumAt(depositNumLast, assetId)
218+ let sumLastX18 = parseBigIntValue(getStrOrElse(keyRewardPerNsbtSumAt(depositNumLast, assetId), "0"))
219+ let sumUserX18 = parseBigIntValue(getStrOrElse(keyRewardPerNsbtSumAt(depositNumUser, assetId), "0"))
220+ let rewardDynamicPart = toInt(fraction((sumLastX18 - sumUserX18), stakedAmountX, MULTX18))
221+ let rewardCachedPartKEY = keyReward(userAddress, assetId)
222+ let rewardCachedPart = getIntOrElse(rewardCachedPartKEY, 0)
223+ $Tuple4((rewardCachedPart + rewardDynamicPart), rewardCachedPart, rewardDynamicPart, rewardCachedPartKEY)
224+ }
225+
226+
227+func toStartOfDay (timestamp) = ((timestamp / DAYMILLIS) * DAYMILLIS)
228+
229+
230+func findElementPosition (src,element,sep) = {
231+ let elementStart = valueOrErrorMessage(indexOf(src, element), ((("there is no substring " + element) + " in ") + src))
232+ if ((elementStart == 0))
233+ then 0
234+ else {
235+ let left = take(src, elementStart)
236+ (size(split(left, sep)) - 1)
237+ }
238+ }
239+
240+
241+let DepositTotalsPREFIX = "%d%d"
242+
243+func updateDepositTotals (currVal,idxToUpdate,deltaAmt) = {
244+ let currArr = split(currVal, SEP)
245+ func updDepTotByIdx (idx) = if ((idx != idxToUpdate))
246+ then currArr[idx]
247+ else toString((parseIntValue(currArr[idx]) + deltaAmt))
248+
249+ makeString([DepositTotalsPREFIX, updDepTotByIdx(1), updDepTotByIdx(2)], SEP)
250+ }
251+
252+
253+func DepositsTotalsEntries (depositAmount,assetIdStr) = {
254+ let startOfDay = toStartOfDay(lastBlock.timestamp)
255+ let byDayKEY = keyStatsDepositAmtByDay(startOfDay)
256+ let totalsKEY = keyStatsDepositAmtTotals()
257+ let position = findElementPosition(supportedAssetsStr, assetIdStr, "_")
258+ let defaultDATA = (DepositTotalsPREFIX + "__0__0")
259+ let currTotalsDATA = valueOrElse(getString(this, totalsKEY), defaultDATA)
260+ let newTotalsDATA = updateDepositTotals(currTotalsDATA, (position + 1), depositAmount)
261+[StringEntry(totalsKEY, newTotalsDATA), StringEntry(byDayKEY, newTotalsDATA)]
262+ }
263+
264+
265+func RewardEntries (isNewUser,userAddress,stakedAmount) = {
266+ let stakedAmountX = toBigInt(stakedAmount)
267+ let userRewardFromDepositNumKEY = keyUserRewardFromDepositNum(userAddress)
268+ let depositNumUser = getIntOrElse(userRewardFromDepositNumKEY, -1)
269+ let depositNumLast = getIntOrElse(keyDepositNumLast(), -1)
270+ func forEachAssetCacheUserReward (accum,asset) = {
271+ let $t01057810713 = calcReward(userAddress, asset, stakedAmountX, depositNumUser, depositNumLast)
272+ let rewardTotal = $t01057810713._1
273+ let cached = $t01057810713._2
274+ let dynamic = $t01057810713._3
275+ let rewardCachedPartKEY = $t01057810713._4
276+ (accum :+ IntegerEntry(rewardCachedPartKEY, rewardTotal))
277+ }
278+
279+ if (if ((depositNumLast == -1))
280+ then (depositNumUser == -1)
281+ else false)
282+ then nil
283+ else if (if ((depositNumLast == -1))
284+ then (depositNumUser > -1)
285+ else false)
286+ then throw("invalid depositNumLast and depositNumUser state")
287+ else if (if ((depositNumLast > -1))
288+ then (depositNumUser >= -1)
289+ else false)
290+ then if (isNewUser)
291+ then [IntegerEntry(userRewardFromDepositNumKEY, depositNumLast)]
292+ else ({
293+ let $l = supportedAssetsList
294+ let $s = size($l)
295+ let $acc0 = nil
296+ func $f0_1 ($a,$i) = if (($i >= $s))
297+ then $a
298+ else forEachAssetCacheUserReward($a, $l[$i])
299+
300+ func $f0_2 ($a,$i) = if (($i >= $s))
301+ then $a
302+ else throw("List size exceeds 2")
303+
304+ $f0_2($f0_1($f0_1($acc0, 0), 1), 2)
305+ } :+ IntegerEntry(userRewardFromDepositNumKEY, depositNumLast))
306+ else throw(((("uncovered condition: depositNumLast=" + toString(depositNumLast)) + " depositNumUser=") + toString(depositNumUser)))
307+ }
308+
309+
310+func IncrementNotDistributedRewardEntry (tkn,amountInc) = {
311+ let notDistributedRewardKEY = keyNotDistributedReward(tkn)
312+ let notDistributedReward = getIntOrElse(notDistributedRewardKEY, 0)
313+[IntegerEntry(notDistributedRewardKEY, (notDistributedReward + amountInc))]
314+ }
315+
316+
317+func mergeStake (userAddress,amountToAdd) = {
318+ let $t01353813654 = valueOrElse(getUserParamsOrUnit(userAddress), $Tuple3(true, 0, height))
319+ let isNewUser = $t01353813654._1
320+ let stakedAmount = $t01353813654._2
321+ let stakingStartHeight = $t01353813654._3
322+ let stakedAmountNEW = if (isNewUser)
323+ then amountToAdd
324+ else (amountToAdd + stakedAmount)
325+ $Tuple4(isNewUser, stakedAmount, stakingStartHeight, stakedAmountNEW)
326+ }
327+
328+
329+func commonStake (userAddress,i) = if ((size(i.payments) != 1))
330+ then throw("Invalid payments size")
331+ else {
332+ let payment = i.payments[0]
333+ let amount = payment.amount
334+ let invalidAssetMessage = (("Invalid asset. " + toBase58String(stakedAssetId)) + " is expected")
335+ let assetId = valueOrErrorMessage(payment.assetId, invalidAssetMessage)
336+ if ((assetId != stakedAssetId))
337+ then throw(invalidAssetMessage)
338+ else {
339+ let userAddressStr = toString(userAddress)
340+ let mergedData = mergeStake(userAddress, amount)
341+ let isNewUser = mergedData._1
342+ let stakedAmount = mergedData._2
343+ let stakingStartHeight = mergedData._3
344+ let stakedAmountNEW = mergedData._4
345+ if ((minLockAmount > stakedAmountNEW))
346+ then throw(("Min lock amount is " + toString(minLockAmount)))
347+ else {
348+ let $t01462414726 = StatsResult(amount, 1, if (isNewUser)
349+ then 1
350+ else 0)
351+ let statsEntries = $t01462414726._1
352+ let totalStaked = $t01462414726._2
353+ let totalStakedNew = $t01462414726._3
354+ ((([HistoryRecordEntry("stake", userAddressStr, i.transactionId, stakedAmount, stakedAmountNEW)] ++ RewardEntries(isNewUser, userAddressStr, stakedAmount)) ++ LockParamsEntry(userAddress, stakedAmountNEW, stakingStartHeight)) ++ statsEntries)
355+ }
356+ }
357+ }
358+
359+
360+func commonUnstake (amount,i) = if ((size(i.payments) != 0))
361+ then throw("unstake doesn't require any payment")
362+ else {
363+ let userAddress = i.caller
364+ let userAddressStr = toString(userAddress)
365+ let $t01519815283 = getUserParamsOrFail(userAddress)
366+ let isNewUser = $t01519815283._1
367+ let stakedAmount = $t01519815283._2
368+ let stakingStartHeight = $t01519815283._3
369+ let swapParamsSTRUCT = asSwapParamsSTRUCT(reentrantInvoke(neutrinoContract, "swapParamsByUserSYSREADONLY", [userAddressStr, 0], nil))
370+ let swapLimitSpentInUsdn = swapParamsSTRUCT._2
371+ let blcks2LmtReset = swapParamsSTRUCT._3
372+ if ((swapLimitSpentInUsdn > 0))
373+ then throw((("You have already made a swap operation. Wait " + toString((height + blcks2LmtReset))) + " height to unstake"))
374+ else if ((0 >= stakedAmount))
375+ then throw("Nothing to unstake")
376+ else if ((amount > stakedAmount))
377+ then throw(((("Requested " + toString(amount)) + ", but staked only ") + toString(stakedAmount)))
378+ else {
379+ let stakedAmountNEW = (stakedAmount - amount)
380+ let $t01592116079 = StatsResult(-(amount), if ((amount == stakedAmount))
381+ then -1
382+ else 0, if ((amount == stakedAmount))
383+ then -1
384+ else 0)
385+ let statsEntries = $t01592116079._1
386+ let totalStaked = $t01592116079._2
387+ let totalStakedNew = $t01592116079._3
388+ ((([ScriptTransfer(userAddress, amount, stakedAssetId), HistoryRecordEntry("unstake", userAddressStr, i.transactionId, stakedAmount, stakedAmountNEW)] ++ RewardEntries(false, userAddressStr, stakedAmount)) ++ LockParamsEntry(userAddress, stakedAmountNEW, stakingStartHeight)) ++ statsEntries)
389+ }
390+ }
391+
392+
393+func commonClaim (userAddress,i) = {
394+ let userAddressStr = toString(userAddress)
395+ if ((size(i.payments) > 0))
396+ then throw("payments are not accepted")
397+ else {
398+ let $t01657216677 = valueOrElse(getUserParamsOrUnit(userAddress), $Tuple3(true, 0, 0))
399+ let isNewUser = $t01657216677._1
400+ let stakedAmount = $t01657216677._2
401+ let stakingStart = $t01657216677._3
402+ let stakedAmountX = toBigInt(stakedAmount)
403+ let userRewardFromDepositNumKEY = keyUserRewardFromDepositNum(userAddressStr)
404+ let depositNumUser = getIntOrElse(userRewardFromDepositNumKEY, -1)
405+ let depositNumLast = getIntOrElse(keyDepositNumLast(), -1)
406+ func forEachAssetCalcUnclaimedReward (accum,asset) = {
407+ let $t01704817186 = calcReward(userAddressStr, asset, stakedAmountX, depositNumUser, depositNumLast)
408+ let rewardTotal = $t01704817186._1
409+ let cached = $t01704817186._2
410+ let dynamic = $t01704817186._3
411+ let rewardCachedPartKEY = $t01704817186._4
412+ let claimedKEY = keyClaimed(userAddressStr, asset)
413+ let $t01724617283 = accum
414+ let data = $t01724617283._1
415+ let claimedAmtByAsset = $t01724617283._2
416+ let newPart = makeString([asset, toString(rewardTotal)], ":")
417+ let claimedAmtByAssetNew = makeString([claimedAmtByAsset, newPart], "_")
418+ if ((0 >= rewardTotal))
419+ then $Tuple2(data, claimedAmtByAssetNew)
420+ else $Tuple2((((data :+ ScriptTransfer(userAddress, rewardTotal, toAssetVect(asset))) :+ IntegerEntry(claimedKEY, (valueOrElse(getInteger(claimedKEY), 0) + rewardTotal))) :+ IntegerEntry(rewardCachedPartKEY, 0)), claimedAmtByAssetNew)
421+ }
422+
423+ let $t01774317856 = {
424+ let $l = supportedAssetsList
425+ let $s = size($l)
426+ let $acc0 = $Tuple2(nil, "")
427+ func $f0_1 ($a,$i) = if (($i >= $s))
428+ then $a
429+ else forEachAssetCalcUnclaimedReward($a, $l[$i])
430+
431+ func $f0_2 ($a,$i) = if (($i >= $s))
432+ then $a
433+ else throw("List size exceeds 2")
434+
435+ $f0_2($f0_1($f0_1($acc0, 0), 1), 2)
436+ }
437+ let transfers = $t01774317856._1
438+ let claimedAmtByAssetResult = $t01774317856._2
439+ if ((0 >= size(transfers)))
440+ then $Tuple2(nil, 0)
441+ else $Tuple2(((transfers :+ IntegerEntry(userRewardFromDepositNumKEY, depositNumLast)) :+ ClaimHistoryEntry(userAddressStr, i.transactionId, drop(claimedAmtByAssetResult, 1))), size(transfers))
442+ }
443+ }
444+
445+
446+let USDNTYPE = "USDN"
447+
448+let NSBTTYPE = "NSBT"
18449
19450 let NeutrinoAssetIdKey = "neutrino_asset_id"
20451
21452 let NeutrinoContractKey = "neutrino_contract"
22453
454+let NsbtAssetIdKey = "bond_asset_id"
455+
23456 let BalanceKey = "rpd_balance"
24457
25-let ControlContractKey = "control_contract"
458+let neutrinoAssetId = fromBase58String(getStringOrFail(neutrinoContract, NeutrinoAssetIdKey))
26459
27-let NsbtAssetIdKey = "bond_asset_id"
460+let nsbtAssetIdStr = getStringOrFail(neutrinoContract, NsbtAssetIdKey)
28461
29-let AdminsKey = "admins"
30-
31-let USDNTYPE = "USDN"
32-
33-let NSBTTYPE = "NSBT"
462+let nsbtAssetId = fromBase58String(nsbtAssetIdStr)
34463
35464 func getUserBalanceKey (owner,assetId) = makeString([BalanceKey, assetId, owner], "_")
36465
37466
38467 func getContractBalanceKey (assetId) = ((BalanceKey + "_") + assetId)
39468
40469
41-func getExpireProposalKey (hash) = (("proposal_expire" + "_") + hash)
470+func getContractBalance (assetId) = getIntOrElse(getContractBalanceKey(assetId), 0)
42471
43472
44-func getOwnerProposalKey (hash) = (("proposal_owner" + "_") + hash)
45-
46-
47-func getArgumentsProposalKey (hash) = (("proposal_arguments" + "_") + hash)
48-
49-
50-func getVoteKey (owner,hash) = (((("proposal_vote" + "_") + owner) + "_") + hash)
51-
52-
53-func convertJsonArrayToList (jsonArray) = split(jsonArray, ",")
54-
55-
56-let neutrinoContract = addressFromStringValue(getStringByKey(NeutrinoContractKey))
57-
58-let controlContract = addressFromStringValue(getStringByAddressAndKey(neutrinoContract, ControlContractKey))
59-
60-let neutrinoAssetId = fromBase58String(getStringByAddressAndKey(neutrinoContract, NeutrinoAssetIdKey))
61-
62-let nsbtAssetIdStr = getStringByAddressAndKey(neutrinoContract, NsbtAssetIdKey)
63-
64-let nsbtAssetId = fromBase58String(nsbtAssetIdStr)
65-
66-func getContractBalance (assetId) = getNumberByKey(getContractBalanceKey(assetId))
67-
68-
69-func getUserBalance (owner,assetId) = getNumberByKey(getUserBalanceKey(owner, assetId))
70-
71-
72-func getRewardsConfigKey (owner,share,receiver) = makeString(["stakingconfig", owner, toString(share), receiver], "_")
73-
74-
75-func getCurrentRewardsConfigKey (owner) = ("stakingconfig_current_" + owner)
76-
77-
78-func getRewardsConfigStartKey (configKey,isStart) = (configKey + (if (isStart)
79- then "_start"
80- else "_end"))
81-
82-
83-func getRewardConfigInitialShare (owner) = (owner + "_initialShare")
473+func getUserBalance (owner,assetId) = getIntOrElse(getUserBalanceKey(owner, assetId), 0)
84474
85475
86476 func getValidStakingAssetOrFail (stakingType,assetId) = if (if ((stakingType == USDNTYPE))
87477 then (assetId != neutrinoAssetId)
88478 else false)
89479 then throw("can use USDN only")
90480 else if (if ((stakingType == NSBTTYPE))
91481 then (assetId != nsbtAssetId)
92482 else false)
93483 then throw("can use NSBT only")
94484 else if (if ((stakingType != USDNTYPE))
95485 then (stakingType != NSBTTYPE)
96486 else false)
97487 then throw(("unsupported staking type " + stakingType))
98488 else assetId
99489
100490
101-func internalLockNeutrino (stakingType,i,receiver,share) = {
102- let pmt = value(i.payments[0])
103- let assetId = getValidStakingAssetOrFail(stakingType, value(pmt.assetId))
104- if (!(isDefined(addressFromString(receiver))))
105- then throw(("Invalid address format " + receiver))
106- else if ((share > 100))
107- then throw("staking rewards share cannot be higher than 100%")
108- else if ((1 > share))
109- then throw("staking rewards share cannot be lower than 1%")
110- else {
111- let account = toString(i.caller)
112- let assetIdString = toBase58String(assetId)
113- let currentConfig = getStringByKey(getCurrentRewardsConfigKey(account))
114- let correctData = if ((currentConfig != ""))
115- then {
116- let currentConfigData = split(currentConfig, "_")
117- let currShare = parseIntValue(currentConfigData[2])
118- let currReceiver = currentConfigData[3]
119- let notMigratedInitialShare = getNumberByKey(getRewardConfigInitialShare(account))
120- let actualInitialShare = if ((notMigratedInitialShare == 0))
121- then currShare
122- else notMigratedInitialShare
123- let newShare = if (if ((actualInitialShare > share))
124- then true
125- else (currReceiver != receiver))
126- then actualInitialShare
127- else share
128-[toString(actualInitialShare), toString(newShare), currReceiver]
129- }
130- else [toString(share), toString(share), receiver]
131- let correctInitialShare = parseIntValue(correctData[0])
132- let correctShare = parseIntValue(correctData[1])
133- let correctReceiver = correctData[2]
134- let newCurrentConfig = getRewardsConfigKey(account, correctShare, correctReceiver)
135- let isNewConfig = !((currentConfig == newCurrentConfig))
136- let end = if (isNewConfig)
137- then height
138- else 0
139- let start = if (isNewConfig)
140- then height
141- else getNumberByKey(getRewardsConfigStartKey(newCurrentConfig, true))
142- $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)
143- }
144- }
145-
146-
147491 func internalUnlock (stakingType,i,unlockAmount,assetIdParam) = {
148492 let account = toString(i.caller)
149493 let assetId = getValidStakingAssetOrFail(stakingType, fromBase58String(assetIdParam))
150494 let assetIdString = toBase58String(assetId)
151495 let balance = (getUserBalance(account, assetIdString) - unlockAmount)
152496 if ((0 > balance))
153497 then throw("invalid amount")
154498 else $Tuple2([IntegerEntry(getContractBalanceKey(assetIdString), (getContractBalance(assetIdString) - unlockAmount)), IntegerEntry(getUserBalanceKey(account, assetIdString), balance), ScriptTransfer(addressFromStringValue(account), unlockAmount, assetId)], unit)
155499 }
156500
157501
158502 @Callable(i)
159-func lockNeutrinoSP (receiver,share) = internalLockNeutrino(USDNTYPE, i, receiver, share)
503+func constructor (minLockAmount,supportedRewardAssets,pStakedAssetId) = if ((i.caller != this))
504+ then throw("Permission denied")
505+ else [IntegerEntry(keyMinLockAmount(), minLockAmount), StringEntry(keySupportedRewardAssets(), supportedRewardAssets), StringEntry(keyStakedAssetId(), pStakedAssetId)]
160506
161507
162508
163509 @Callable(i)
164-func lockNeutrino () = internalLockNeutrino(USDNTYPE, i, toString(i.caller), 100)
510+func stake () = commonStake(i.caller, i)
165511
166512
167513
168514 @Callable(i)
169-func lockNsbtSP (receiver,share) = internalLockNeutrino(NSBTTYPE, i, receiver, share)
515+func stakeByOriginCaller () = commonStake(i.originCaller, i)
170516
171517
172518
173519 @Callable(i)
174-func lockNsbt () = internalLockNeutrino(NSBTTYPE, i, toString(i.caller), 100)
520+func unstake (amount) = commonUnstake(amount, i)
175521
176522
177523
178524 @Callable(i)
179-func unlockNeutrino (unlockAmount,assetIdString) = internalUnlock(USDNTYPE, i, unlockAmount, assetIdString)
525+func deposit () = if ((size(i.payments) != 1))
526+ then throw("exact 1 payment is allowed only")
527+ else {
528+ let pmt = i.payments[0]
529+ let amount = pmt.amount
530+ let pmtAssetId = valueOrElse(pmt.assetId, WAVESID)
531+ let pmtAssetIdStr = toBase58String(pmtAssetId)
532+ let pmtMultX = if ((pmtAssetId == WAVESID))
533+ then MULTX8
534+ else MULTX6
535+ let amountX = toBigInt(amount)
536+ let totalStaked = getIntOrElse(keyLockParamTotalAmount(), 0)
537+ let totalStakedX = toBigInt(totalStaked)
538+ if ((0 > totalStaked))
539+ then throw("TODO: case is not supported")
540+ else if ((totalStaked == 0))
541+ then IncrementNotDistributedRewardEntry(pmtAssetIdStr, amount)
542+ else {
543+ let rewardPerNsbtX18 = fraction(amountX, MULTX18, totalStakedX)
544+ let depositNumLastKEY = keyDepositNumLast()
545+ let depositNumLast = getIntOrElse(depositNumLastKEY, -1)
546+ let depositNumNew = (depositNumLast + 1)
547+ if (!(contains(supportedAssetsStr, pmtAssetIdStr)))
548+ then throw(((supportedAssetsStr + " doesn't contain ") + pmtAssetIdStr))
549+ else {
550+ func refreshRewardPerNsbtSUM (accum,nextAsset) = {
551+ let rewardPerNsbtSumNewKEY = keyRewardPerNsbtSumAt(depositNumNew, nextAsset)
552+ let sumLastStr = getStrOrElse(keyRewardPerNsbtSumAt(depositNumLast, nextAsset), "0")
553+ (accum :+ (if ((nextAsset == pmtAssetIdStr))
554+ then StringEntry(rewardPerNsbtSumNewKEY, toString((parseBigIntValue(sumLastStr) + rewardPerNsbtX18)))
555+ else StringEntry(rewardPerNsbtSumNewKEY, sumLastStr)))
556+ }
557+
558+ (({
559+ let $l = supportedAssetsList
560+ let $s = size($l)
561+ let $acc0 = nil
562+ func $f0_1 ($a,$i) = if (($i >= $s))
563+ then $a
564+ else refreshRewardPerNsbtSUM($a, $l[$i])
565+
566+ func $f0_2 ($a,$i) = if (($i >= $s))
567+ then $a
568+ else throw("List size exceeds 2")
569+
570+ $f0_2($f0_1($f0_1($acc0, 0), 1), 2)
571+ } :+ IntegerEntry(depositNumLastKEY, depositNumNew)) ++ DepositsTotalsEntries(amount, pmtAssetIdStr))
572+ }
573+ }
574+ }
575+
576+
577+
578+@Callable(i)
579+func claimRewards () = commonClaim(i.caller, i)
580+
581+
582+
583+@Callable(i)
584+func claimRewardsByOriginCaller () = commonClaim(i.originCaller, i)
585+
586+
587+
588+@Callable(i)
589+func unclaimedRewardsREADONLY (userAddressStr) = {
590+ func forEachAssetZeroReward (accum,asset) = ((accum + makeString([asset, "0", "0"], ":")) + "_")
591+
592+ let unclaimedRewardStr = if ((userAddressStr == ""))
593+ then {
594+ let $l = supportedAssetsList
595+ let $s = size($l)
596+ let $acc0 = ""
597+ func $f0_1 ($a,$i) = if (($i >= $s))
598+ then $a
599+ else forEachAssetZeroReward($a, $l[$i])
600+
601+ func $f0_2 ($a,$i) = if (($i >= $s))
602+ then $a
603+ else throw("List size exceeds 2")
604+
605+ $f0_2($f0_1($f0_1($acc0, 0), 1), 2)
606+ }
607+ else {
608+ let userAddress = addressFromStringValue(userAddressStr)
609+ let $t02291123022 = valueOrElse(getUserParamsOrUnit(userAddress), $Tuple3(true, 0, 0))
610+ let isNewUser = $t02291123022._1
611+ let stakedAmount = $t02291123022._2
612+ let stakingStartHeight = $t02291123022._3
613+ let stakedAmountX = toBigInt(stakedAmount)
614+ let userRewardFromDepositNumKEY = keyUserRewardFromDepositNum(userAddressStr)
615+ let depositNumUser = getIntOrElse(userRewardFromDepositNumKEY, -1)
616+ let depositNumLast = getIntOrElse(keyDepositNumLast(), -1)
617+ func forEachAssetCalcUnclaimedReward (accum,asset) = {
618+ let $t02336823506 = calcReward(userAddressStr, asset, stakedAmountX, depositNumUser, depositNumLast)
619+ let rewardTotal = $t02336823506._1
620+ let cached = $t02336823506._2
621+ let dynamic = $t02336823506._3
622+ let rewardCachedPartKEY = $t02336823506._4
623+ let claimed = valueOrElse(getInteger(keyClaimed(userAddressStr, asset)), 0)
624+ ((accum + makeString([asset, toString(rewardTotal), toString(claimed)], ":")) + "_")
625+ }
626+
627+ let $l = supportedAssetsList
628+ let $s = size($l)
629+ let $acc0 = ""
630+ func $f0_1 ($a,$i) = if (($i >= $s))
631+ then $a
632+ else forEachAssetCalcUnclaimedReward($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+ }
640+ $Tuple2(nil, dropRight(unclaimedRewardStr, 1))
641+ }
642+
643+
644+
645+@Callable(i)
646+func usdnStakingSYSREADONLY (userAddressStrOrEmpty,usdnDiff) = {
647+ let usdnTotalAmtStaked = getIntOrElse(keyLockParamTotalAmount(), 0)
648+ if ((userAddressStrOrEmpty == ""))
649+ then $Tuple2(nil, [0, usdnTotalAmtStaked, 0])
650+ else {
651+ let userAddress = toAddressOrFail(userAddressStrOrEmpty)
652+ let mergedData = mergeStake(userAddress, usdnDiff)
653+ let isNewUser = mergedData._1
654+ let usdnStakedByUser = mergedData._2
655+ let stakingStartHeight = mergedData._3
656+ let stakedAmountNEW = mergedData._4
657+ $Tuple2(nil, [usdnStakedByUser, usdnTotalAmtStaked])
658+ }
659+ }
660+
661+
662+
663+@Callable(i)
664+func configSYSREADONLY () = {
665+ let minLockAmt = getIntegerValue(keyMinLockAmount())
666+ $Tuple2(nil, [minLockAmt])
667+ }
668+
669+
670+
671+@Callable(i)
672+func lockNeutrinoSP (receiver,share) = commonStake(i.caller, i)
673+
674+
675+
676+@Callable(i)
677+func lockNeutrino () = commonStake(i.caller, i)
678+
679+
680+
681+@Callable(i)
682+func unlockNeutrino (unlockAmount,assetIdString) = commonUnstake(unlockAmount, i)
180683
181684
182685
183686 @Callable(i)
184687 func unlockNsbt (unlockAmount,assetIdString) = internalUnlock(NSBTTYPE, i, unlockAmount, assetIdString)
185688
186689
187690 @Verifier(tx)
188691 func verify () = {
189- let pubKeyAdminsList = ["ExtEEK19nmKj9mCpnWyvEEJFYATLMcVEMvohhUHkyHNm", "Ev5py5FfBQX9cZpYKnfQrTB49Byf8QmpZWeDVRim4yV7", "DUuuLjXu98nBwZc7fqwCTjtA3nnRwgTbkMSr5SU2NmDR", "5WRXFSjwcTbNfKcJs8ZqXmSSWYsSVJUtMvMqZj5hH4Nc"]
692+ let pubKeyAdminsListStr = makeString(["ExtEEK19nmKj9mCpnWyvEEJFYATLMcVEMvohhUHkyHNm", "Ev5py5FfBQX9cZpYKnfQrTB49Byf8QmpZWeDVRim4yV7", "DUuuLjXu98nBwZc7fqwCTjtA3nnRwgTbkMSr5SU2NmDR", "DUuuLjXu98nBwZc7fqwCTjtA3nnRwgTbkMSr5SU2NmDR"], SEP)
693+ let pubKeyAdminsList = split(valueOrElse(getString(controlContract, "%s__multisig"), pubKeyAdminsListStr), SEP)
190694 let count = ((((if (sigVerify(tx.bodyBytes, tx.proofs[0], fromBase58String(pubKeyAdminsList[0])))
191695 then 1
192696 else 0) + (if (sigVerify(tx.bodyBytes, tx.proofs[1], fromBase58String(pubKeyAdminsList[1])))
193697 then 1
194698 else 0)) + (if (sigVerify(tx.bodyBytes, tx.proofs[2], fromBase58String(pubKeyAdminsList[2])))
195699 then 1
196700 else 0)) + (if (sigVerify(tx.bodyBytes, tx.proofs[3], fromBase58String(pubKeyAdminsList[3])))
197701 then 2
198702 else 0))
199703 (count >= 3)
200704 }
201705

github/deemru/w8io/873ac7e 
95.62 ms