tx · 8a3BDFGh4g8n8JXpMTA6kD59wusUwGNTwyCMwavRs5Qm 3N6HSS7Toat1RhyEsyqVGgVSRzH19W1FTbP: -0.02900000 Waves 2023.07.27 14:19 [2684188] smart account 3N6HSS7Toat1RhyEsyqVGgVSRzH19W1FTbP > SELF 0.00000000 Waves
{ "type": 13, "id": "8a3BDFGh4g8n8JXpMTA6kD59wusUwGNTwyCMwavRs5Qm", "fee": 2900000, "feeAssetId": null, "timestamp": 1690456728676, "version": 2, "chainId": 84, "sender": "3N6HSS7Toat1RhyEsyqVGgVSRzH19W1FTbP", "senderPublicKey": "AoKo4segKHU4DeJnxXQYJj2u7J6XJeux6r8KLW52cd2Q", "proofs": [ "4Beh6FiGVQSgSpGqYcfTSbormkbsaeR1wxuAwJuSqiZF3dRXaPnWQdLuFn2GijgR88b3jN9V8g6qrsw5EXCvZB9d" ], "script": "base64:BwIxCAISAwoBAhIGCgQCAQEBEgUKAwEBARIDCgECEgMKAQISBAoCAgISBAoCAgISAwoBAjEAA1NFUAICX18ADUNPTlRSQUNUX05BTUUCD2NhbGN1bGF0b3IucmlkZQAGU0NBTEU4AIDC1y8AB1BFTkRJTkcCB1BFTkRJTkcACEZJTklTSEVEAghGSU5JU0hFRAAFV0FWRVMCBVdBVkVTAQd3cmFwRXJyAQFzCQCsAgIJAKwCAgUNQ09OVFJBQ1RfTkFNRQICOiAFAXMBCHRocm93RXJyAQFzCQACAQkBB3dyYXBFcnIBBQFzAQ9hc3NldElkVG9TdHJpbmcBB2Fzc2V0SWQEByRtYXRjaDAFB2Fzc2V0SWQDCQABAgUHJG1hdGNoMAIKQnl0ZVZlY3RvcgQBYgUHJG1hdGNoMAkA2AQBBQFiAwkAAQIFByRtYXRjaDACBFVuaXQFBVdBVkVTCQACAQILTWF0Y2ggZXJyb3IBD3N0cmluZ1RvQXNzZXRJZAEBcwMJAAACBQFzBQVXQVZFUwUEdW5pdAkA2QQBBQFzAQNhYnMBAW4DCQBmAgAABQFuCQEBLQEFAW4FAW4BEWtleUZhY3RvcnlBZGRyZXNzAAkAuQkCCQDMCAICAiVzCQDMCAICB2ZhY3RvcnkFA25pbAUDU0VQARNrZXlNYW5hZ2VyUHVibGljS2V5AAkAuQkCCQDMCAICAiVzCQDMCAICEG1hbmFnZXJQdWJsaWNLZXkFA25pbAUDU0VQAQxrZXlMcEFzc2V0SWQACQC5CQIJAMwIAgICJXMJAMwIAgIJbHBBc3NldElkBQNuaWwFA1NFUAEXa2V5UHJveHlUcmVhc3VyeUFkZHJlc3MACQC5CQIJAMwIAgICJXMJAMwIAgINcHJveHlUcmVhc3VyeQUDbmlsBQNTRVABFmtleU1haW5UcmVhc3VyeUFkZHJlc3MACQC5CQIJAMwIAgICJXMJAMwIAgIMbWFpblRyZWFzdXJ5BQNuaWwFA1NFUAELa2V5SW52ZXN0ZWQBB2Fzc2V0SWQJALkJAgkAzAgCAgQlcyVzCQDMCAICCGludmVzdGVkCQDMCAIJAQ9hc3NldElkVG9TdHJpbmcBBQdhc3NldElkBQNuaWwFA1NFUAEKa2V5RG9uYXRlZAEHYXNzZXRJZAkAuQkCCQDMCAICBCVzJXMJAMwIAgIHZG9uYXRlZAkAzAgCCQEPYXNzZXRJZFRvU3RyaW5nAQUHYXNzZXRJZAUDbmlsBQNTRVABDGtleUF2YWlsYWJsZQELdXNlckFkZHJlc3MJALkJAgkAzAgCAgQlcyVzCQDMCAICCWF2YWlsYWJsZQkAzAgCCQClCAEFC3VzZXJBZGRyZXNzBQNuaWwFA1NFUAEKa2V5Q2xhaW1lZAELdXNlckFkZHJlc3MJALkJAgkAzAgCAgQlcyVzCQDMCAICB2NsYWltZWQJAMwIAgkApQgBBQt1c2VyQWRkcmVzcwUDbmlsBQNTRVABEGtleUN1cnJlbnRQZXJpb2QACQC5CQIJAMwIAgICJXMJAMwIAgINY3VycmVudFBlcmlvZAUDbmlsBQNTRVABEWtleVByaWNlRm9yUGVyaW9kAQZwZXJpb2QJALkJAgkAzAgCAgQlcyVkCQDMCAICBXByaWNlCQDMCAIJAKQDAQUGcGVyaW9kBQNuaWwFA1NFUAEOa2V5U3RhcnRIZWlnaHQBBnBlcmlvZAkAuQkCCQDMCAICBCVzJWQJAMwIAgILc3RhcnRIZWlnaHQJAMwIAgkApAMBBQZwZXJpb2QFA25pbAUDU0VQARRrZXlQb3dlck1hbmFnZXJCb251cwEGcGVyaW9kCQC5CQIJAMwIAgIEJXMlZAkAzAgCAhFwb3dlck1hbmFnZXJCb251cwkAzAgCCQCkAwEFBnBlcmlvZAUDbmlsBQNTRVABD2tleVBlcmlvZExlbmd0aAACECVzX19wZXJpb2RMZW5ndGgBGGtleUJsb2NrUHJvY2Vzc2luZ1Jld2FyZAACGSVzX19ibG9ja1Byb2Nlc3NpbmdSZXdhcmQBFWtleU5leHRCbG9ja1RvUHJvY2VzcwACFiVzX19uZXh0QmxvY2tUb1Byb2Nlc3MBEWtleUJsb2NrUHJvY2Vzc2VkAQZoZWlnaHQJALkJAgkAzAgCAgQlcyVkCQDMCAICDmJsb2NrUHJvY2Vzc2VkCQDMCAIJAKQDAQUGaGVpZ2h0BQNuaWwFA1NFUAENa2V5V2l0aGRyYXdhbAACDiVzX193aXRoZHJhd2FsARRrZXlXaXRoZHJhd2FsUmVxdWVzdAILdXNlckFkZHJlc3MEdHhJZAkAuQkCCQDMCAICBiVzJXMlcwkAzAgCAgp3aXRoZHJhd2FsCQDMCAIJAKUIAQULdXNlckFkZHJlc3MJAMwIAgkA2AQBBQR0eElkBQNuaWwFA1NFUAEWdmFsdWVXaXRoZHJhd2FsUmVxdWVzdAQGc3RhdHVzDWxwQXNzZXRBbW91bnQMdGFyZ2V0UGVyaW9kCWNsYWltVHhJZAQMY2xhaW1UeElkU3RyBAckbWF0Y2gwBQljbGFpbVR4SWQDCQABAgUHJG1hdGNoMAIKQnl0ZVZlY3RvcgQBYgUHJG1hdGNoMAkA2AQBBQFiAwkAAQIFByRtYXRjaDACBFVuaXQCBFNPT04JAAIBAgtNYXRjaCBlcnJvcgkAuQkCCQDMCAICCCVzJWQlZCVzCQDMCAIFBnN0YXR1cwkAzAgCCQCkAwEFDWxwQXNzZXRBbW91bnQJAMwIAgkApAMBBQx0YXJnZXRQZXJpb2QJAMwIAgUMY2xhaW1UeElkU3RyBQNuaWwFA1NFUAEba2V5UGVyaW9kV2l0aGRyYXdhbEFzc2V0SWRzAQZwZXJpb2QJALkJAgkAzAgCAgQlcyVkCQDMCAICDHBlcmlvZFJld2FyZAkAzAgCCQCkAwEFBnBlcmlvZAUDbmlsBQNTRVABGmtleVBlcmlvZFdpdGhkcmF3YWxBbW91bnRzAQZwZXJpb2QJALkJAgkAzAgCAgQlcyVkCQDMCAICEnBlcmlvZFJld2FyZEFtb3VudAkAzAgCCQCkAwEFBnBlcmlvZAUDbmlsBQNTRVAAF2tleU1pbkhlaWdodEZvcldpdGhkcmF3CQC5CQIJAMwIAgICJXMJAMwIAgIUbWluSGVpZ2h0Rm9yV2l0aGRyYXcFA25pbAUDU0VQABdrZXlNYXhIZWlnaHRGb3JXaXRoZHJhdwkAuQkCCQDMCAICAiVzCQDMCAICFG1heEhlaWdodEZvcldpdGhkcmF3BQNuaWwFA1NFUAEhcGFyc2VXaXRoZHJhd2FsUmVxdWVzdFZhbHVlT3JGYWlsAQFzBAVwYXJ0cwkAtQkCBQFzBQNTRVADCQAAAgkAkAMBBQVwYXJ0cwAFBAZzdGF0dXMJAJEDAgUFcGFydHMAAQQNbHBBc3NldEFtb3VudAkBE3ZhbHVlT3JFcnJvck1lc3NhZ2UCCQC2CQEJAJEDAgUFcGFydHMAAgkBB3dyYXBFcnIBAhVpbnZhbGlkIGxwQXNzZXRBbW91bnQEDHRhcmdldFBlcmlvZAkBE3ZhbHVlT3JFcnJvck1lc3NhZ2UCCQC2CQEJAJEDAgUFcGFydHMAAwkBB3dyYXBFcnIBAhRpbnZhbGlkIHRhcmdldFBlcmlvZAQJY2xhaW1UeElkCQCRAwIFBXBhcnRzAAQJAJYKBAUGc3RhdHVzBQ1scEFzc2V0QW1vdW50BQx0YXJnZXRQZXJpb2QFCWNsYWltVHhJZAkBCHRocm93RXJyAQIgaW52YWxpZCB3aXRoZHJhd2FsIHJlcXVlc3QgdmFsdWUAFGZhY3RvcnlBZGRyZXNzT3B0aW9uBAckbWF0Y2gwCQCdCAIFBHRoaXMJARFrZXlGYWN0b3J5QWRkcmVzcwADCQABAgUHJG1hdGNoMAIGU3RyaW5nBAFzBQckbWF0Y2gwCQCmCAEFAXMDCQABAgUHJG1hdGNoMAIEVW5pdAUEdW5pdAkAAgECC01hdGNoIGVycm9yABRmYWN0b3J5QWRkcmVzc09yRmFpbAkBE3ZhbHVlT3JFcnJvck1lc3NhZ2UCBRRmYWN0b3J5QWRkcmVzc09wdGlvbgkBB3dyYXBFcnIBAhdpbnZhbGlkIGZhY3RvcnkgYWRkcmVzcwAPbHBBc3NldElkT3B0aW9uBAckbWF0Y2gwBRRmYWN0b3J5QWRkcmVzc09wdGlvbgMJAAECBQckbWF0Y2gwAgdBZGRyZXNzBAFhBQckbWF0Y2gwBAckbWF0Y2gxCQCdCAIFAWEJAQxrZXlMcEFzc2V0SWQAAwkAAQIFByRtYXRjaDECBlN0cmluZwQBcwUHJG1hdGNoMQkA2QQBBQFzAwkAAQIFByRtYXRjaDECBFVuaXQFBHVuaXQJAAIBAgtNYXRjaCBlcnJvcgMJAAECBQckbWF0Y2gwAgRVbml0BQR1bml0CQACAQILTWF0Y2ggZXJyb3IAD2xwQXNzZXRJZE9yRmFpbAkBE3ZhbHVlT3JFcnJvck1lc3NhZ2UCBQ9scEFzc2V0SWRPcHRpb24JAQd3cmFwRXJyAQIRaW52YWxpZCBscEFzc2V0SWQAGnByb3h5VHJlYXN1cnlBZGRyZXNzT3B0aW9uBAckbWF0Y2gwBRRmYWN0b3J5QWRkcmVzc09wdGlvbgMJAAECBQckbWF0Y2gwAgdBZGRyZXNzBAFhBQckbWF0Y2gwBAckbWF0Y2gxCQCdCAIFAWEJARdrZXlQcm94eVRyZWFzdXJ5QWRkcmVzcwADCQABAgUHJG1hdGNoMQIGU3RyaW5nBAFzBQckbWF0Y2gxCQCmCAEFAXMDCQABAgUHJG1hdGNoMQIEVW5pdAUEdW5pdAkAAgECC01hdGNoIGVycm9yAwkAAQIFByRtYXRjaDACBFVuaXQFBHVuaXQJAAIBAgtNYXRjaCBlcnJvcgAacHJveHlUcmVhc3VyeUFkZHJlc3NPckZhaWwJARN2YWx1ZU9yRXJyb3JNZXNzYWdlAgUacHJveHlUcmVhc3VyeUFkZHJlc3NPcHRpb24JAQd3cmFwRXJyAQIeaW52YWxpZCBwcm94eSB0cmVhc3VyeSBhZGRyZXNzABltYWluVHJlYXN1cnlBZGRyZXNzT3B0aW9uBAckbWF0Y2gwBRRmYWN0b3J5QWRkcmVzc09wdGlvbgMJAAECBQckbWF0Y2gwAgdBZGRyZXNzBAFhBQckbWF0Y2gwBAckbWF0Y2gxCQCdCAIFAWEJARZrZXlNYWluVHJlYXN1cnlBZGRyZXNzAAMJAAECBQckbWF0Y2gxAgZTdHJpbmcEAXMFByRtYXRjaDEJAKYIAQUBcwMJAAECBQckbWF0Y2gxAgRVbml0BQR1bml0CQACAQILTWF0Y2ggZXJyb3IDCQABAgUHJG1hdGNoMAIEVW5pdAUEdW5pdAkAAgECC01hdGNoIGVycm9yABltYWluVHJlYXN1cnlBZGRyZXNzT3JGYWlsCQETdmFsdWVPckVycm9yTWVzc2FnZQIFGW1haW5UcmVhc3VyeUFkZHJlc3NPcHRpb24JAQd3cmFwRXJyAQIdaW52YWxpZCBtYWluIHRyZWFzdXJ5IGFkZHJlc3MBGWdldE1hbmFnZXJQdWJsaWNLZXlPclVuaXQABAckbWF0Y2gwBRRmYWN0b3J5QWRkcmVzc09wdGlvbgMJAAECBQckbWF0Y2gwAgdBZGRyZXNzBAJmYQUHJG1hdGNoMAQHJG1hdGNoMQkAnQgCBQJmYQkBE2tleU1hbmFnZXJQdWJsaWNLZXkAAwkAAQIFByRtYXRjaDECBlN0cmluZwQDcHViBQckbWF0Y2gxCQDZBAEFA3B1YgUEdW5pdAUEdW5pdAELb25seUFkZHJlc3MCAWkHYWRkcmVzcwMJAAACCAUBaQZjYWxsZXIFB2FkZHJlc3MGCQEIdGhyb3dFcnIBAhFwZXJtaXNzaW9uIGRlbmllZAELb25seUZhY3RvcnkBAWkJAQtvbmx5QWRkcmVzcwIFAWkFFGZhY3RvcnlBZGRyZXNzT3JGYWlsAQ9yZXdhcmRGb3JPcHRpb24CB3Jld2FyZHMGdGFyZ2V0BAFzCQCQAwEFB3Jld2FyZHMECyR0MDUyMDg1MjMzCQCRAwIFB3Jld2FyZHMAAAQCYTAIBQskdDA1MjA4NTIzMwJfMQQCcjAIBQskdDA1MjA4NTIzMwJfMgQLJHQwNTIzNjUyNjEJAJEDAgUHcmV3YXJkcwABBAJhMQgFCyR0MDUyMzY1MjYxAl8xBAJyMQgFCyR0MDUyMzY1MjYxAl8yBAskdDA1MjY0NTI4OQkAkQMCBQdyZXdhcmRzAAIEAmEyCAULJHQwNTI2NDUyODkCXzEEAnIyCAULJHQwNTI2NDUyODkCXzIDAwkAZgIFAXMAAAkAAAIFAmEwBQZ0YXJnZXQHBQJyMAMDCQBmAgUBcwABCQAAAgUCYTEFBnRhcmdldAcFAnIxAwMJAGYCBQFzAAIJAAACBQJhMgUGdGFyZ2V0BwUCcjIFBHVuaXQBEGZpbmFsaXplSU5URVJOQUwDGG5ld1RyZWFzdXJ5Vm9sdW1lSW5XYXZlcxdwd3JNYW5hZ2Vyc0JvbnVzSW5XYXZlcyB0cmVhc3VyeVZvbHVtZURpZmZBbGxvY2F0aW9uQ29lZgQSZG9uYXRlZFdhdmVzQW1vdW50CQELdmFsdWVPckVsc2UCCQCaCAIFFGZhY3RvcnlBZGRyZXNzT3JGYWlsCQEKa2V5RG9uYXRlZAEFBHVuaXQAAAQTaW52ZXN0ZWRXYXZlc0Ftb3VudAkBC3ZhbHVlT3JFbHNlAgkAmggCBRRmYWN0b3J5QWRkcmVzc09yRmFpbAkBC2tleUludmVzdGVkAQUEdW5pdAAABBxjdXJyZW50VHJlYXN1cnlWb2x1bWVJbldhdmVzCQBkAgUSZG9uYXRlZFdhdmVzQW1vdW50BRNpbnZlc3RlZFdhdmVzQW1vdW50BAlwcm9maXRSYXcJAGUCBRhuZXdUcmVhc3VyeVZvbHVtZUluV2F2ZXMFHGN1cnJlbnRUcmVhc3VyeVZvbHVtZUluV2F2ZXMEFnB3ck1hbmFnZXJzQm9udXNBbW91bnQDAwkAZwIFCXByb2ZpdFJhdwUXcHdyTWFuYWdlcnNCb251c0luV2F2ZXMGCQAAAgUXcHdyTWFuYWdlcnNCb251c0luV2F2ZXMAAAUXcHdyTWFuYWdlcnNCb251c0luV2F2ZXMJAQh0aHJvd0VycgECH3Bvd2VyIGJvbnVzIGlzIG1vcmUgdGhhbiBwcm9maXQEBnByb2ZpdAkAZQIFCXByb2ZpdFJhdwUWcHdyTWFuYWdlcnNCb251c0Ftb3VudAQMZG9uYXRpb25QYXJ0AwkAZgIFHGN1cnJlbnRUcmVhc3VyeVZvbHVtZUluV2F2ZXMAAAkAawMFEmRvbmF0ZWRXYXZlc0Ftb3VudAUGU0NBTEU4BRxjdXJyZW50VHJlYXN1cnlWb2x1bWVJbldhdmVzAAAEFWRvbmF0aW9uUHJvZml0UGFydFJhdwkAawMFBnByb2ZpdAUMZG9uYXRpb25QYXJ0BQZTQ0FMRTgEF2ludmVzdG1lbnRQcm9maXRQYXJ0UmF3CQBlAgUGcHJvZml0BRVkb25hdGlvblByb2ZpdFBhcnRSYXcEI3RyZWFzdXJ5Vm9sdW1lRGlmZkFsbG9jYXRpb25Db2VmQWJzCQEDYWJzAQUgdHJlYXN1cnlWb2x1bWVEaWZmQWxsb2NhdGlvbkNvZWYEEGFtb3VudFRvRG9uYXRpb24JAGsDBRdpbnZlc3RtZW50UHJvZml0UGFydFJhdwMJAGYCAAAFIHRyZWFzdXJ5Vm9sdW1lRGlmZkFsbG9jYXRpb25Db2VmBSN0cmVhc3VyeVZvbHVtZURpZmZBbGxvY2F0aW9uQ29lZkFicwAABQZTQ0FMRTgEEmFtb3VudFRvSW52ZXN0bWVudAkAawMFFWRvbmF0aW9uUHJvZml0UGFydFJhdwMJAGYCBSB0cmVhc3VyeVZvbHVtZURpZmZBbGxvY2F0aW9uQ29lZgAABSN0cmVhc3VyeVZvbHVtZURpZmZBbGxvY2F0aW9uQ29lZkFicwAABQZTQ0FMRTgEEmRvbmF0aW9uUHJvZml0UGFydAkAZAIJAGUCBRVkb25hdGlvblByb2ZpdFBhcnRSYXcFEmFtb3VudFRvSW52ZXN0bWVudAUQYW1vdW50VG9Eb25hdGlvbgQUaW52ZXN0bWVudFByb2ZpdFBhcnQJAGQCCQBlAgUXaW52ZXN0bWVudFByb2ZpdFBhcnRSYXcFEGFtb3VudFRvRG9uYXRpb24FEmFtb3VudFRvSW52ZXN0bWVudAQYZG9uYXRlZFdhdmVzQW1vdW50TmV3UmF3CQBkAgUSZG9uYXRlZFdhdmVzQW1vdW50BRJkb25hdGlvblByb2ZpdFBhcnQEGWludmVzdGVkV2F2ZXNBbW91bnROZXdSYXcJAGQCBRNpbnZlc3RlZFdhdmVzQW1vdW50BRRpbnZlc3RtZW50UHJvZml0UGFydAQPZG9uYXRlZFBhcnREZWJ0CQCXAwEJAMwIAgAACQDMCAIFGGRvbmF0ZWRXYXZlc0Ftb3VudE5ld1JhdwUDbmlsBBBpbnZlc3RlZFBhcnREZWJ0CQCXAwEJAMwIAgAACQDMCAIFGWludmVzdGVkV2F2ZXNBbW91bnROZXdSYXcFA25pbAQVZG9uYXRlZFdhdmVzQW1vdW50TmV3CQBkAgkAlgMBCQDMCAIAAAkAzAgCBRhkb25hdGVkV2F2ZXNBbW91bnROZXdSYXcFA25pbAUQaW52ZXN0ZWRQYXJ0RGVidAQWaW52ZXN0ZWRXYXZlc0Ftb3VudE5ldwkAZAIJAJYDAQkAzAgCAAAJAMwIAgUZaW52ZXN0ZWRXYXZlc0Ftb3VudE5ld1JhdwUDbmlsBQ9kb25hdGVkUGFydERlYnQED2xwQXNzZXRRdWFudGl0eQgJARN2YWx1ZU9yRXJyb3JNZXNzYWdlAgkA7AcBBQ9scEFzc2V0SWRPckZhaWwJAQd3cmFwRXJyAQIUaW52YWxpZCBscEFzc2V0IGluZm8IcXVhbnRpdHkECG5ld1ByaWNlCQBrAwUWaW52ZXN0ZWRXYXZlc0Ftb3VudE5ldwUGU0NBTEU4BQ9scEFzc2V0UXVhbnRpdHkEE2NoZWNrSWZQcmljZU5vdFplcm8DCQECIT0CBQhuZXdQcmljZQAABgkBCHRocm93RXJyAQIUTFAgcHJpY2UgY2Fubm90IGJlIDADCQAAAgUTY2hlY2tJZlByaWNlTm90WmVybwUTY2hlY2tJZlByaWNlTm90WmVybwQTbHBBc3NldEFtb3VudFRvQnVybgkBC3ZhbHVlT3JFbHNlAgkAmggCBRRmYWN0b3J5QWRkcmVzc09yRmFpbAkBDWtleVdpdGhkcmF3YWwAAAAEEHBheW1lbnRBbW91bnRNaW4JAJYDAQkAzAgCAAAJAMwIAgkAawMFE2xwQXNzZXRBbW91bnRUb0J1cm4FCG5ld1ByaWNlBQZTQ0FMRTgFA25pbAQYZmluYWxJbnZlc3RlZFdhdmVzQW1vdW50CQBlAgUWaW52ZXN0ZWRXYXZlc0Ftb3VudE5ldwUQcGF5bWVudEFtb3VudE1pbgQUbHBBc3NldEZpbmFsUXVhbnRpdHkJAGUCBQ9scEFzc2V0UXVhbnRpdHkFE2xwQXNzZXRBbW91bnRUb0J1cm4JAJgKBgUQcGF5bWVudEFtb3VudE1pbgUYZmluYWxJbnZlc3RlZFdhdmVzQW1vdW50BRVkb25hdGVkV2F2ZXNBbW91bnROZXcFCG5ld1ByaWNlBRNscEFzc2V0QW1vdW50VG9CdXJuBRRscEFzc2V0RmluYWxRdWFudGl0eQkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLggBaQEHY2xhaW1MUAEQdXNlckFkZHJlc3NCeXRlcwQLY2hlY2tDYWxsZXIJAQtvbmx5RmFjdG9yeQEFAWkDCQAAAgULY2hlY2tDYWxsZXIFC2NoZWNrQ2FsbGVyBAt1c2VyQWRkcmVzcwkBB0FkZHJlc3MBBRB1c2VyQWRkcmVzc0J5dGVzBAlhdmFpbGFibGUJAQt2YWx1ZU9yRWxzZQIJAJoIAgUUZmFjdG9yeUFkZHJlc3NPckZhaWwJAQxrZXlBdmFpbGFibGUBBQt1c2VyQWRkcmVzcwAABAdjbGFpbWVkCQELdmFsdWVPckVsc2UCCQCaCAIFFGZhY3RvcnlBZGRyZXNzT3JGYWlsCQEKa2V5Q2xhaW1lZAEFC3VzZXJBZGRyZXNzAAAEDmZhY3RvcnlBY3Rpb25zAwkAZgIFCWF2YWlsYWJsZQAACQDMCAIJAPwHBAUUZmFjdG9yeUFkZHJlc3NPckZhaWwCDXRyYW5zZmVyQXNzZXQJAMwIAgUQdXNlckFkZHJlc3NCeXRlcwkAzAgCBQlhdmFpbGFibGUJAMwIAgUPbHBBc3NldElkT3JGYWlsBQNuaWwFA25pbAkAzAgCCQD8BwQFFGZhY3RvcnlBZGRyZXNzT3JGYWlsAgxpbnRlZ2VyRW50cnkJAMwIAgkBDGtleUF2YWlsYWJsZQEFC3VzZXJBZGRyZXNzCQDMCAIAAAUDbmlsBQNuaWwJAMwIAgkA/AcEBRRmYWN0b3J5QWRkcmVzc09yRmFpbAIMaW50ZWdlckVudHJ5CQDMCAIJAQprZXlDbGFpbWVkAQULdXNlckFkZHJlc3MJAMwIAgkAZAIFB2NsYWltZWQFCWF2YWlsYWJsZQUDbmlsBQNuaWwFA25pbAkBCHRocm93RXJyAQIQbm90aGluZyB0byBjbGFpbQkAlAoCBQNuaWwFDmZhY3RvcnlBY3Rpb25zCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuAWkBCGZpbmFsaXplBBB1c2VyQWRkcmVzc0J5dGVzGG5ld1RyZWFzdXJ5Vm9sdW1lSW5XYXZlcxdwd3JNYW5hZ2Vyc0JvbnVzSW5XYXZlcyB0cmVhc3VyeVZvbHVtZURpZmZBbGxvY2F0aW9uQ29lZgQLY2hlY2tDYWxsZXIJAQtvbmx5RmFjdG9yeQEFAWkDCQAAAgULY2hlY2tDYWxsZXIFC2NoZWNrQ2FsbGVyBBNjdXJyZW50UGVyaW9kT3JGYWlsCQETdmFsdWVPckVycm9yTWVzc2FnZQIJAJoIAgUUZmFjdG9yeUFkZHJlc3NPckZhaWwJARBrZXlDdXJyZW50UGVyaW9kAAkBB3dyYXBFcnIBAg5pbnZhbGlkIHBlcmlvZAQMcGVyaW9kTGVuZ3RoCQETdmFsdWVPckVycm9yTWVzc2FnZQIJAJoIAgUUZmFjdG9yeUFkZHJlc3NPckZhaWwJAQ9rZXlQZXJpb2RMZW5ndGgACQEHd3JhcEVycgECFWludmFsaWQgcGVyaW9kIGxlbmd0aAQSY3VycmVudFN0YXJ0SGVpZ2h0CQETdmFsdWVPckVycm9yTWVzc2FnZQIJAJoIAgUUZmFjdG9yeUFkZHJlc3NPckZhaWwJAQ5rZXlTdGFydEhlaWdodAEFE2N1cnJlbnRQZXJpb2RPckZhaWwJAQd3cmFwRXJyAQIUaW52YWxpZCBzdGFydCBoZWlnaHQEEmN1cnJlbnRQcmljZU9yRmFpbAkBE3ZhbHVlT3JFcnJvck1lc3NhZ2UCCQCaCAIFFGZhY3RvcnlBZGRyZXNzT3JGYWlsCQERa2V5UHJpY2VGb3JQZXJpb2QBBRNjdXJyZW50UGVyaW9kT3JGYWlsCQEHd3JhcEVycgECDWludmFsaWQgcHJpY2UEEm5leHRCbG9ja1RvUHJvY2VzcwkBE3ZhbHVlT3JFcnJvck1lc3NhZ2UCCQCaCAIFFGZhY3RvcnlBZGRyZXNzT3JGYWlsCQEVa2V5TmV4dEJsb2NrVG9Qcm9jZXNzAAkBB3dyYXBFcnIBAh1pbnZhbGlkIG5leHQgYmxvY2sgdG8gcHJvY2VzcwQPcGVyaW9kRW5kSGVpZ2h0CQBlAgkAZAIFEmN1cnJlbnRTdGFydEhlaWdodAUMcGVyaW9kTGVuZ3RoAAEEBmNoZWNrcwkAzAgCAwkAAAIIBQFpDG9yaWdpbkNhbGxlcgUZbWFpblRyZWFzdXJ5QWRkcmVzc09yRmFpbAYJAQh0aHJvd0VycgECEXBlcm1pc3Npb24gZGVuaWVkCQDMCAIDCQBmAgUSbmV4dEJsb2NrVG9Qcm9jZXNzBQ9wZXJpb2RFbmRIZWlnaHQGCQEIdGhyb3dFcnIBAhJ1bnByb2Nlc3NlZCBibG9ja3MJAMwIAgMJAGcCBRhuZXdUcmVhc3VyeVZvbHVtZUluV2F2ZXMAAAYJAQh0aHJvd0VycgECG2ludmFsaWQgbmV3IHRyZWFzdXJ5IHZvbHVtZQkAzAgCAwkAZwIFF3B3ck1hbmFnZXJzQm9udXNJbldhdmVzAAAGCQEIdGhyb3dFcnIBAhppbnZhbGlkIFBXUiBtYW5hZ2VycyBib251cwkAzAgCAwMJAGcCBSB0cmVhc3VyeVZvbHVtZURpZmZBbGxvY2F0aW9uQ29lZgkBAS0BBQZTQ0FMRTgJAGcCBQZTQ0FMRTgFIHRyZWFzdXJ5Vm9sdW1lRGlmZkFsbG9jYXRpb25Db2VmBwYJAQh0aHJvd0VycgECM2ludmFsaWQgdHJlYXN1cnkgdm9sdW1lIGRpZmYgYWxsb2NhdGlvbiBjb2VmZmljaWVudAUDbmlsAwkAAAIFBmNoZWNrcwUGY2hlY2tzBA0kdDAxMDc4MzExMDQzCQEQZmluYWxpemVJTlRFUk5BTAMFGG5ld1RyZWFzdXJ5Vm9sdW1lSW5XYXZlcwUXcHdyTWFuYWdlcnNCb251c0luV2F2ZXMFIHRyZWFzdXJ5Vm9sdW1lRGlmZkFsbG9jYXRpb25Db2VmBBBwYXltZW50QW1vdW50TWluCAUNJHQwMTA3ODMxMTA0MwJfMQQYZmluYWxJbnZlc3RlZFdhdmVzQW1vdW50CAUNJHQwMTA3ODMxMTA0MwJfMgQVZG9uYXRlZFdhdmVzQW1vdW50TmV3CAUNJHQwMTA3ODMxMTA0MwJfMwQIbmV3UHJpY2UIBQ0kdDAxMDc4MzExMDQzAl80BBNscEFzc2V0QW1vdW50VG9CdXJuCAUNJHQwMTA3ODMxMTA0MwJfNQQUbHBBc3NldEZpbmFsUXVhbnRpdHkIBQ0kdDAxMDc4MzExMDQzAl82BAluZXdQZXJpb2QJAGQCBRNjdXJyZW50UGVyaW9kT3JGYWlsAAEKAQxhZGROZXdBY3Rpb24CB2FjdGlvbnMHcGF5bWVudAQNJHQwMTExOTExMTI1MwUHYWN0aW9ucwQPc2NyaXB0VHJhbnNmZXJzCAUNJHQwMTExOTExMTI1MwJfMQQOYXNzZXRJZHNTdHJpbmcIBQ0kdDAxMTE5MTExMjUzAl8yBA1hbW91bnRzU3RyaW5nCAUNJHQwMTExOTExMTI1MwJfMwQNcGF5bWVudEFtb3VudAgFB3BheW1lbnQGYW1vdW50BA5wYXltZW50QXNzZXRJZAgFB3BheW1lbnQHYXNzZXRJZAQRbmV3QXNzZXRJZHNTdHJpbmcJAKwCAgICJXMJALkJAgkAzAgCBQ5hc3NldElkc1N0cmluZwkAzAgCCQEPYXNzZXRJZFRvU3RyaW5nAQUOcGF5bWVudEFzc2V0SWQFA25pbAUDU0VQBBBuZXdBbW91bnRzU3RyaW5nCQCsAgICAiVkCQC5CQIJAMwIAgUNYW1vdW50c1N0cmluZwkAzAgCCQCkAwEFDXBheW1lbnRBbW91bnQFA25pbAUDU0VQBBFuZXdTY3JpcHRUcmFuc2ZlcgkBDlNjcmlwdFRyYW5zZmVyAwUUZmFjdG9yeUFkZHJlc3NPckZhaWwFDXBheW1lbnRBbW91bnQFDnBheW1lbnRBc3NldElkCQCVCgMJAM0IAgUPc2NyaXB0VHJhbnNmZXJzBRFuZXdTY3JpcHRUcmFuc2ZlcgURbmV3QXNzZXRJZHNTdHJpbmcFEG5ld0Ftb3VudHNTdHJpbmcEDSR0MDExNzEzMTE4MDQKAAIkbAgFAWkIcGF5bWVudHMKAAIkcwkAkAMBBQIkbAoABSRhY2MwCQCVCgMFA25pbAIAAgAKAQUkZjBfMQICJGECJGkDCQBnAgUCJGkFAiRzBQIkYQkBDGFkZE5ld0FjdGlvbgIFAiRhCQCRAwIFAiRsBQIkaQoBBSRmMF8yAgIkYQIkaQMJAGcCBQIkaQUCJHMFAiRhCQACAQIUTGlzdCBzaXplIGV4Y2VlZHMgMTAJAQUkZjBfMgIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIFBSRhY2MwAAAAAQACAAMABAAFAAYABwAIAAkACgQPc2NyaXB0VHJhbnNmZXJzCAUNJHQwMTE3MTMxMTgwNAJfMQQIQXNzZXRJZHMIBQ0kdDAxMTcxMzExODA0Al8yBAdBbW91bnRzCAUNJHQwMTE3MTMxMTgwNAJfMwQOZmFjdG9yeUFjdGlvbnMJAMwIAgkA/AcEBRRmYWN0b3J5QWRkcmVzc09yRmFpbAIMaW50ZWdlckVudHJ5CQDMCAIJARRrZXlQb3dlck1hbmFnZXJCb251cwEFE2N1cnJlbnRQZXJpb2RPckZhaWwJAMwIAgUXcHdyTWFuYWdlcnNCb251c0luV2F2ZXMFA25pbAUDbmlsCQDMCAIJAPwHBAUUZmFjdG9yeUFkZHJlc3NPckZhaWwCDGludGVnZXJFbnRyeQkAzAgCCQEQa2V5Q3VycmVudFBlcmlvZAAJAMwIAgUJbmV3UGVyaW9kBQNuaWwFA25pbAkAzAgCCQD8BwQFFGZhY3RvcnlBZGRyZXNzT3JGYWlsAgxpbnRlZ2VyRW50cnkJAMwIAgkBEWtleVByaWNlRm9yUGVyaW9kAQUJbmV3UGVyaW9kCQDMCAIFCG5ld1ByaWNlBQNuaWwFA25pbAkAzAgCCQD8BwQFFGZhY3RvcnlBZGRyZXNzT3JGYWlsAgxpbnRlZ2VyRW50cnkJAMwIAgkBDmtleVN0YXJ0SGVpZ2h0AQUJbmV3UGVyaW9kCQDMCAIJAGQCBQ9wZXJpb2RFbmRIZWlnaHQAAQUDbmlsBQNuaWwJAMwIAgkA/AcEBRRmYWN0b3J5QWRkcmVzc09yRmFpbAIEYnVybgkAzAgCBRNscEFzc2V0QW1vdW50VG9CdXJuBQNuaWwFA25pbAkAzAgCCQD8BwQFFGZhY3RvcnlBZGRyZXNzT3JGYWlsAgxpbnRlZ2VyRW50cnkJAMwIAgkBDWtleVdpdGhkcmF3YWwACQDMCAIAAAUDbmlsBQNuaWwJAMwIAgkA/AcEBRRmYWN0b3J5QWRkcmVzc09yRmFpbAIMaW50ZWdlckVudHJ5CQDMCAIJAQtrZXlJbnZlc3RlZAEFBHVuaXQJAMwIAgUYZmluYWxJbnZlc3RlZFdhdmVzQW1vdW50BQNuaWwFA25pbAkAzAgCCQD8BwQFFGZhY3RvcnlBZGRyZXNzT3JGYWlsAgxpbnRlZ2VyRW50cnkJAMwIAgkBCmtleURvbmF0ZWQBBQR1bml0CQDMCAIFFWRvbmF0ZWRXYXZlc0Ftb3VudE5ldwUDbmlsBQNuaWwJAMwIAgkA/AcEBRRmYWN0b3J5QWRkcmVzc09yRmFpbAILc3RyaW5nRW50cnkJAMwIAgkBG2tleVBlcmlvZFdpdGhkcmF3YWxBc3NldElkcwEFCW5ld1BlcmlvZAkAzAgCBQhBc3NldElkcwUDbmlsBQNuaWwJAMwIAgkA/AcEBRRmYWN0b3J5QWRkcmVzc09yRmFpbAILc3RyaW5nRW50cnkJAMwIAgkBGmtleVBlcmlvZFdpdGhkcmF3YWxBbW91bnRzAQUJbmV3UGVyaW9kCQDMCAIFB0Ftb3VudHMFA25pbAUDbmlsBQNuaWwJAJQKAgUPc2NyaXB0VHJhbnNmZXJzBQ5mYWN0b3J5QWN0aW9ucwkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgFpARBmaW5hbGl6ZVJFQURPTkxZAxhuZXdUcmVhc3VyeVZvbHVtZUluV2F2ZXMXcHdyTWFuYWdlcnNCb251c0luV2F2ZXMgdHJlYXN1cnlWb2x1bWVEaWZmQWxsb2NhdGlvbkNvZWYEE2N1cnJlbnRQZXJpb2RPckZhaWwJARN2YWx1ZU9yRXJyb3JNZXNzYWdlAgkAmggCBRRmYWN0b3J5QWRkcmVzc09yRmFpbAkBEGtleUN1cnJlbnRQZXJpb2QACQEHd3JhcEVycgECDmludmFsaWQgcGVyaW9kBAxwZXJpb2RMZW5ndGgJARN2YWx1ZU9yRXJyb3JNZXNzYWdlAgkAmggCBRRmYWN0b3J5QWRkcmVzc09yRmFpbAkBD2tleVBlcmlvZExlbmd0aAAJAQd3cmFwRXJyAQIVaW52YWxpZCBwZXJpb2QgbGVuZ3RoBBJjdXJyZW50U3RhcnRIZWlnaHQJARN2YWx1ZU9yRXJyb3JNZXNzYWdlAgkAmggCBRRmYWN0b3J5QWRkcmVzc09yRmFpbAkBDmtleVN0YXJ0SGVpZ2h0AQUTY3VycmVudFBlcmlvZE9yRmFpbAkBB3dyYXBFcnIBAhRpbnZhbGlkIHN0YXJ0IGhlaWdodAQSY3VycmVudFByaWNlT3JGYWlsCQETdmFsdWVPckVycm9yTWVzc2FnZQIJAJoIAgUUZmFjdG9yeUFkZHJlc3NPckZhaWwJARFrZXlQcmljZUZvclBlcmlvZAEFE2N1cnJlbnRQZXJpb2RPckZhaWwJAQd3cmFwRXJyAQINaW52YWxpZCBwcmljZQQSbmV4dEJsb2NrVG9Qcm9jZXNzCQETdmFsdWVPckVycm9yTWVzc2FnZQIJAJoIAgUUZmFjdG9yeUFkZHJlc3NPckZhaWwJARVrZXlOZXh0QmxvY2tUb1Byb2Nlc3MACQEHd3JhcEVycgECHWludmFsaWQgbmV4dCBibG9jayB0byBwcm9jZXNzBA9wZXJpb2RFbmRIZWlnaHQJAGUCCQBkAgUSY3VycmVudFN0YXJ0SGVpZ2h0BQxwZXJpb2RMZW5ndGgAAQQGY2hlY2tzCQDMCAIDCQBmAgUSbmV4dEJsb2NrVG9Qcm9jZXNzBQ9wZXJpb2RFbmRIZWlnaHQGCQEIdGhyb3dFcnIBAhJ1bnByb2Nlc3NlZCBibG9ja3MJAMwIAgMJAGcCBRhuZXdUcmVhc3VyeVZvbHVtZUluV2F2ZXMAAAYJAQh0aHJvd0VycgECG2ludmFsaWQgbmV3IHRyZWFzdXJ5IHZvbHVtZQkAzAgCAwkAZwIFF3B3ck1hbmFnZXJzQm9udXNJbldhdmVzAAAGCQEIdGhyb3dFcnIBAhppbnZhbGlkIFBXUiBtYW5hZ2VycyBib251cwkAzAgCAwMJAGcCBSB0cmVhc3VyeVZvbHVtZURpZmZBbGxvY2F0aW9uQ29lZgkBAS0BBQZTQ0FMRTgJAGcCBQZTQ0FMRTgFIHRyZWFzdXJ5Vm9sdW1lRGlmZkFsbG9jYXRpb25Db2VmBwYJAQh0aHJvd0VycgECM2ludmFsaWQgdHJlYXN1cnkgdm9sdW1lIGRpZmYgYWxsb2NhdGlvbiBjb2VmZmljaWVudAUDbmlsAwkAAAIFBmNoZWNrcwUGY2hlY2tzCQCUCgIFA25pbAkBEGZpbmFsaXplSU5URVJOQUwDBRhuZXdUcmVhc3VyeVZvbHVtZUluV2F2ZXMFF3B3ck1hbmFnZXJzQm9udXNJbldhdmVzBSB0cmVhc3VyeVZvbHVtZURpZmZBbGxvY2F0aW9uQ29lZgkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgFpAQZpbnZlc3QBEHVzZXJBZGRyZXNzQnl0ZXMEC2NoZWNrQ2FsbGVyCQELb25seUZhY3RvcnkBBQFpAwkAAAIFC2NoZWNrQ2FsbGVyBQtjaGVja0NhbGxlcgQLdXNlckFkZHJlc3MJAQdBZGRyZXNzAQUQdXNlckFkZHJlc3NCeXRlcwQTY3VycmVudFBlcmlvZE9yRmFpbAkBE3ZhbHVlT3JFcnJvck1lc3NhZ2UCCQCaCAIFFGZhY3RvcnlBZGRyZXNzT3JGYWlsCQEQa2V5Q3VycmVudFBlcmlvZAAJAQd3cmFwRXJyAQIOaW52YWxpZCBwZXJpb2QEEmN1cnJlbnRQcmljZU9yRmFpbAkBE3ZhbHVlT3JFcnJvck1lc3NhZ2UCCQCaCAIFFGZhY3RvcnlBZGRyZXNzT3JGYWlsCQERa2V5UHJpY2VGb3JQZXJpb2QBBRNjdXJyZW50UGVyaW9kT3JGYWlsCQEHd3JhcEVycgECDWludmFsaWQgcHJpY2UEB3BheW1lbnQDCQAAAgkAkAMBCAUBaQhwYXltZW50cwABCQCRAwIIBQFpCHBheW1lbnRzAAAJAQh0aHJvd0VycgECEGludmFsaWQgcGF5bWVudHMEDSR0MDE1Mjg1MTU0NjIDAwkAAAIIBQdwYXltZW50B2Fzc2V0SWQFBHVuaXQJAGYCCAUHcGF5bWVudAZhbW91bnQAAAcJAJQKAggFB3BheW1lbnQGYW1vdW50CAUHcGF5bWVudAdhc3NldElkCQEIdGhyb3dFcnIBAhZpbnZhbGlkIHBheW1lbnQgYW1vdW50BA1wYXltZW50QW1vdW50CAUNJHQwMTUyODUxNTQ2MgJfMQQOcGF5bWVudEFzc2V0SWQIBQ0kdDAxNTI4NTE1NDYyAl8yBA1scEFzc2V0QW1vdW50AwkAZgIFEmN1cnJlbnRQcmljZU9yRmFpbAAACQBrAwUNcGF5bWVudEFtb3VudAUGU0NBTEU4BRJjdXJyZW50UHJpY2VPckZhaWwAAAQIaW52ZXN0ZWQJAQt2YWx1ZU9yRWxzZQIJAJoIAgUUZmFjdG9yeUFkZHJlc3NPckZhaWwJAQtrZXlJbnZlc3RlZAEFBHVuaXQAAAQHYWN0aW9ucwkAzAgCCQEOU2NyaXB0VHJhbnNmZXIDBRltYWluVHJlYXN1cnlBZGRyZXNzT3JGYWlsBQ1wYXltZW50QW1vdW50BQ5wYXltZW50QXNzZXRJZAUDbmlsBA5mYWN0b3J5QWN0aW9ucwkAzAgCCQD8BwQFFGZhY3RvcnlBZGRyZXNzT3JGYWlsAgxpbnRlZ2VyRW50cnkJAMwIAgkBC2tleUludmVzdGVkAQUEdW5pdAkAzAgCCQBkAgUIaW52ZXN0ZWQFDXBheW1lbnRBbW91bnQFA25pbAUDbmlsCQDMCAIJAPwHBAUUZmFjdG9yeUFkZHJlc3NPckZhaWwCB3JlaXNzdWUJAMwIAgUNbHBBc3NldEFtb3VudAUDbmlsBQNuaWwJAMwIAgkA/AcEBRRmYWN0b3J5QWRkcmVzc09yRmFpbAINdHJhbnNmZXJBc3NldAkAzAgCBRB1c2VyQWRkcmVzc0J5dGVzCQDMCAIFDWxwQXNzZXRBbW91bnQJAMwIAgUPbHBBc3NldElkT3JGYWlsBQNuaWwFA25pbAUDbmlsCQCUCgIFB2FjdGlvbnMFDmZhY3RvcnlBY3Rpb25zCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuAWkBCHdpdGhkcmF3ARB1c2VyQWRkcmVzc0J5dGVzBAtjaGVja0NhbGxlcgkBC29ubHlGYWN0b3J5AQUBaQMJAAACBQtjaGVja0NhbGxlcgULY2hlY2tDYWxsZXIEC3VzZXJBZGRyZXNzCQEHQWRkcmVzcwEFEHVzZXJBZGRyZXNzQnl0ZXMEE2N1cnJlbnRQZXJpb2RPckZhaWwJARN2YWx1ZU9yRXJyb3JNZXNzYWdlAgkAmggCBRRmYWN0b3J5QWRkcmVzc09yRmFpbAkBEGtleUN1cnJlbnRQZXJpb2QACQEHd3JhcEVycgECDmludmFsaWQgcGVyaW9kBAdwYXltZW50AwkAAAIJAJADAQgFAWkIcGF5bWVudHMAAQkAkQMCCAUBaQhwYXltZW50cwAACQEIdGhyb3dFcnIBCQEHd3JhcEVycgECEGludmFsaWQgcGF5bWVudHMEDnBheW1lbnRBc3NldElkAwkAAAIIBQdwYXltZW50B2Fzc2V0SWQFD2xwQXNzZXRJZE9yRmFpbAUPbHBBc3NldElkT3JGYWlsCQEIdGhyb3dFcnIBAhVpbnZhbGlkIHBheW1lbnQgYXNzZXQEDXBheW1lbnRBbW91bnQDCQBmAggFB3BheW1lbnQGYW1vdW50AAAIBQdwYXltZW50BmFtb3VudAkBCHRocm93RXJyAQIWaW52YWxpZCBwYXltZW50IGFtb3VudAQKd2l0aGRyYXdhbAkBC3ZhbHVlT3JFbHNlAgkAmggCBRRmYWN0b3J5QWRkcmVzc09yRmFpbAkBDWtleVdpdGhkcmF3YWwAAAAEB2FjdGlvbnMJAMwIAgkBDlNjcmlwdFRyYW5zZmVyAwUUZmFjdG9yeUFkZHJlc3NPckZhaWwFDXBheW1lbnRBbW91bnQFDnBheW1lbnRBc3NldElkBQNuaWwEDmZhY3RvcnlBY3Rpb25zCQDMCAIJAPwHBAUUZmFjdG9yeUFkZHJlc3NPckZhaWwCDGludGVnZXJFbnRyeQkAzAgCCQENa2V5V2l0aGRyYXdhbAAJAMwIAgkAZAIFCndpdGhkcmF3YWwFDXBheW1lbnRBbW91bnQFA25pbAUDbmlsCQDMCAIJAPwHBAUUZmFjdG9yeUFkZHJlc3NPckZhaWwCC3N0cmluZ0VudHJ5CQDMCAIJARRrZXlXaXRoZHJhd2FsUmVxdWVzdAIFC3VzZXJBZGRyZXNzCAUBaQ10cmFuc2FjdGlvbklkCQDMCAIJARZ2YWx1ZVdpdGhkcmF3YWxSZXF1ZXN0BAUHUEVORElORwUNcGF5bWVudEFtb3VudAkAZAIFE2N1cnJlbnRQZXJpb2RPckZhaWwAAQUEdW5pdAUDbmlsBQNuaWwFA25pbAMJAAACBQ5mYWN0b3J5QWN0aW9ucwUOZmFjdG9yeUFjdGlvbnMJAJQKAgUHYWN0aW9ucwUOZmFjdG9yeUFjdGlvbnMJAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4JAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4BaQEOY2FuY2VsV2l0aGRyYXcCEHVzZXJBZGRyZXNzQnl0ZXMEdHhJZAQLY2hlY2tDYWxsZXIJAQtvbmx5RmFjdG9yeQEFAWkDCQAAAgULY2hlY2tDYWxsZXIFC2NoZWNrQ2FsbGVyBAt1c2VyQWRkcmVzcwkBB0FkZHJlc3MBBRB1c2VyQWRkcmVzc0J5dGVzBBd3aXRoZHJhd2FsUmVxdWVzdE9wdGlvbgkBE3ZhbHVlT3JFcnJvck1lc3NhZ2UCCQCdCAIFFGZhY3RvcnlBZGRyZXNzT3JGYWlsCQEUa2V5V2l0aGRyYXdhbFJlcXVlc3QCBQt1c2VyQWRkcmVzcwUEdHhJZAkBB3dyYXBFcnIBAhppbnZhbGlkIHdpdGhkcmF3YWwgcmVxdWVzdAQTY3VycmVudFBlcmlvZE9yRmFpbAkBE3ZhbHVlT3JFcnJvck1lc3NhZ2UCCQCaCAIFFGZhY3RvcnlBZGRyZXNzT3JGYWlsCQEQa2V5Q3VycmVudFBlcmlvZAAJAQd3cmFwRXJyAQIOaW52YWxpZCBwZXJpb2QEDSR0MDE3ODY5MTc5ODIJASFwYXJzZVdpdGhkcmF3YWxSZXF1ZXN0VmFsdWVPckZhaWwBBRd3aXRoZHJhd2FsUmVxdWVzdE9wdGlvbgQGc3RhdHVzCAUNJHQwMTc4NjkxNzk4MgJfMQQNbHBBc3NldEFtb3VudAgFDSR0MDE3ODY5MTc5ODICXzIEDHRhcmdldFBlcmlvZAgFDSR0MDE3ODY5MTc5ODICXzMECWNsYWltVHhJZAgFDSR0MDE3ODY5MTc5ODICXzQEBmNoZWNrcwkAzAgCAwkAAAIFBnN0YXR1cwUHUEVORElORwYJAQh0aHJvd0VycgECIWludmFsaWQgd2l0aGRyYXdhbCByZXF1ZXN0IHN0YXR1cwkAzAgCAwkAZgIFDHRhcmdldFBlcmlvZAUTY3VycmVudFBlcmlvZE9yRmFpbAYJAQh0aHJvd0VycgECIWludmFsaWQgd2l0aGRyYXdhbCByZXF1ZXN0IHBlcmlvZAUDbmlsAwkAAAIFBmNoZWNrcwUGY2hlY2tzBAp3aXRoZHJhd2FsCQETdmFsdWVPckVycm9yTWVzc2FnZQIJAJoIAgUUZmFjdG9yeUFkZHJlc3NPckZhaWwJAQ1rZXlXaXRoZHJhd2FsAAkBB3dyYXBFcnIBAh9pbnZhbGlkIHRvdGFsIHdpdGhkcmF3YWwgYW1vdW50BA5mYWN0b3J5QWN0aW9ucwkAzAgCCQD8BwQFFGZhY3RvcnlBZGRyZXNzT3JGYWlsAgxpbnRlZ2VyRW50cnkJAMwIAgkBDWtleVdpdGhkcmF3YWwACQDMCAIDCQBnAgUKd2l0aGRyYXdhbAUNbHBBc3NldEFtb3VudAkAZQIFCndpdGhkcmF3YWwFDWxwQXNzZXRBbW91bnQJAQh0aHJvd0VycgECGWludmFsaWQgd2l0aGRyYXdhbCBhbW91bnQFA25pbAUDbmlsCQDMCAIJAPwHBAUUZmFjdG9yeUFkZHJlc3NPckZhaWwCC2RlbGV0ZUVudHJ5CQDMCAIJARRrZXlXaXRoZHJhd2FsUmVxdWVzdAIFC3VzZXJBZGRyZXNzBQR0eElkBQNuaWwFA25pbAkAzAgCCQD8BwQFFGZhY3RvcnlBZGRyZXNzT3JGYWlsAg10cmFuc2ZlckFzc2V0CQDMCAIFEHVzZXJBZGRyZXNzQnl0ZXMJAMwIAgUNbHBBc3NldEFtb3VudAkAzAgCBQ9scEFzc2V0SWRPckZhaWwFA25pbAUDbmlsBQNuaWwDCQAAAgUOZmFjdG9yeUFjdGlvbnMFDmZhY3RvcnlBY3Rpb25zCQCUCgIFA25pbAUOZmFjdG9yeUFjdGlvbnMJAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4JAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4JAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4BaQEPY2xhaW1Db2xsYXRlcmFsAhB1c2VyQWRkcmVzc0J5dGVzBHR4SWQEC2NoZWNrQ2FsbGVyCQELb25seUZhY3RvcnkBBQFpAwkAAAIFC2NoZWNrQ2FsbGVyBQtjaGVja0NhbGxlcgQLdXNlckFkZHJlc3MJAQdBZGRyZXNzAQUQdXNlckFkZHJlc3NCeXRlcwQTY3VycmVudFBlcmlvZE9yRmFpbAkBE3ZhbHVlT3JFcnJvck1lc3NhZ2UCCQCaCAIFFGZhY3RvcnlBZGRyZXNzT3JGYWlsCQEQa2V5Q3VycmVudFBlcmlvZAAJAQd3cmFwRXJyAQIOaW52YWxpZCBwZXJpb2QEF3dpdGhkcmF3YWxSZXF1ZXN0T3B0aW9uCQETdmFsdWVPckVycm9yTWVzc2FnZQIJAJ0IAgUUZmFjdG9yeUFkZHJlc3NPckZhaWwJARRrZXlXaXRoZHJhd2FsUmVxdWVzdAIFC3VzZXJBZGRyZXNzBQR0eElkCQEHd3JhcEVycgECGmludmFsaWQgd2l0aGRyYXdhbCByZXF1ZXN0BA0kdDAxOTMxOTE5NDMyCQEhcGFyc2VXaXRoZHJhd2FsUmVxdWVzdFZhbHVlT3JGYWlsAQUXd2l0aGRyYXdhbFJlcXVlc3RPcHRpb24EBnN0YXR1cwgFDSR0MDE5MzE5MTk0MzICXzEEDWxwQXNzZXRBbW91bnQIBQ0kdDAxOTMxOTE5NDMyAl8yBAx0YXJnZXRQZXJpb2QIBQ0kdDAxOTMxOTE5NDMyAl8zBAljbGFpbVR4SWQIBQ0kdDAxOTMxOTE5NDMyAl80AwkAAAIFBnN0YXR1cwUIRklOSVNIRUQJAQh0aHJvd0VycgECIWludmFsaWQgd2l0aGRyYXdhbCByZXF1ZXN0IHN0YXR1cwMJAGYCBQx0YXJnZXRQZXJpb2QFE2N1cnJlbnRQZXJpb2RPckZhaWwJAQh0aHJvd0VycgECIWludmFsaWQgd2l0aGRyYXdhbCByZXF1ZXN0IHBlcmlvZAQLcHJpY2VPckZhaWwJARN2YWx1ZU9yRXJyb3JNZXNzYWdlAgkAmggCBRRmYWN0b3J5QWRkcmVzc09yRmFpbAkBEWtleVByaWNlRm9yUGVyaW9kAQUMdGFyZ2V0UGVyaW9kCQEHd3JhcEVycgECDWludmFsaWQgcHJpY2UEBmFtb3VudAMJAGYCBQtwcmljZU9yRmFpbAAACQBrAwUNbHBBc3NldEFtb3VudAULcHJpY2VPckZhaWwFBlNDQUxFOAAABA5mYWN0b3J5QWN0aW9ucwkAzAgCCQD8BwQFFGZhY3RvcnlBZGRyZXNzT3JGYWlsAgtzdHJpbmdFbnRyeQkAzAgCCQEUa2V5V2l0aGRyYXdhbFJlcXVlc3QCBQt1c2VyQWRkcmVzcwUEdHhJZAkAzAgCCQEWdmFsdWVXaXRoZHJhd2FsUmVxdWVzdAQFCEZJTklTSEVEBQ1scEFzc2V0QW1vdW50BQx0YXJnZXRQZXJpb2QIBQFpDXRyYW5zYWN0aW9uSWQFA25pbAUDbmlsCQDMCAIECmFzc2V0c0xpc3QJANEIAgkAvQkCCQERQGV4dHJOYXRpdmUoMTA1MykCBRRmYWN0b3J5QWRkcmVzc09yRmFpbAkBG2tleVBlcmlvZFdpdGhkcmF3YWxBc3NldElkcwEFE2N1cnJlbnRQZXJpb2RPckZhaWwFA1NFUAAABAthbW91bnRzTGlzdAkA0QgCCQC9CQIJARFAZXh0ck5hdGl2ZSgxMDUzKQIFFGZhY3RvcnlBZGRyZXNzT3JGYWlsCQEaa2V5UGVyaW9kV2l0aGRyYXdhbEFtb3VudHMBBRNjdXJyZW50UGVyaW9kT3JGYWlsBQNTRVAAAAkA/AcEBRRmYWN0b3J5QWRkcmVzc09yRmFpbAIOdHJhbnNmZXJBc3NldHMJAMwIAgUQdXNlckFkZHJlc3NCeXRlcwkAzAgCBQphc3NldHNMaXN0CQDMCAIFC2Ftb3VudHNMaXN0BQNuaWwFA25pbAUDbmlsCQCUCgIFA25pbAUOZmFjdG9yeUFjdGlvbnMJAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4BaQENcHJvY2Vzc0Jsb2NrcwEQdXNlckFkZHJlc3NCeXRlcwQLY2hlY2tDYWxsZXIJAQtvbmx5RmFjdG9yeQEFAWkDCQAAAgULY2hlY2tDYWxsZXIFC2NoZWNrQ2FsbGVyBAt1c2VyQWRkcmVzcwkBB0FkZHJlc3MBBRB1c2VyQWRkcmVzc0J5dGVzBBNjdXJyZW50UGVyaW9kT3JGYWlsCQETdmFsdWVPckVycm9yTWVzc2FnZQIJAJoIAgUUZmFjdG9yeUFkZHJlc3NPckZhaWwJARBrZXlDdXJyZW50UGVyaW9kAAkBB3dyYXBFcnIBAg5pbnZhbGlkIHBlcmlvZAQMcGVyaW9kTGVuZ3RoCQETdmFsdWVPckVycm9yTWVzc2FnZQIJAJoIAgUUZmFjdG9yeUFkZHJlc3NPckZhaWwJAQ9rZXlQZXJpb2RMZW5ndGgACQEHd3JhcEVycgECFWludmFsaWQgcGVyaW9kIGxlbmd0aAQSY3VycmVudFN0YXJ0SGVpZ2h0CQETdmFsdWVPckVycm9yTWVzc2FnZQIJAJoIAgUUZmFjdG9yeUFkZHJlc3NPckZhaWwJAQ5rZXlTdGFydEhlaWdodAEFE2N1cnJlbnRQZXJpb2RPckZhaWwJAQd3cmFwRXJyAQIUaW52YWxpZCBzdGFydCBoZWlnaHQEEmN1cnJlbnRQcmljZU9yRmFpbAkBE3ZhbHVlT3JFcnJvck1lc3NhZ2UCCQCaCAIFFGZhY3RvcnlBZGRyZXNzT3JGYWlsCQERa2V5UHJpY2VGb3JQZXJpb2QBBRNjdXJyZW50UGVyaW9kT3JGYWlsCQEHd3JhcEVycgECDWludmFsaWQgcHJpY2UEEm5leHRCbG9ja1RvUHJvY2VzcwkBE3ZhbHVlT3JFcnJvck1lc3NhZ2UCCQCaCAIFFGZhY3RvcnlBZGRyZXNzT3JGYWlsCQEVa2V5TmV4dEJsb2NrVG9Qcm9jZXNzAAkBB3dyYXBFcnIBAh1pbnZhbGlkIG5leHQgYmxvY2sgdG8gcHJvY2VzcwQPcGVyaW9kRW5kSGVpZ2h0CQBlAgkAZAIFEmN1cnJlbnRTdGFydEhlaWdodAUMcGVyaW9kTGVuZ3RoAAEEHGJsb2Nrc1Byb2Nlc3NpbmdCYXRjaFNpemVNYXgAKAQZYmxvY2tzUHJvY2Vzc2luZ0JhdGNoU2l6ZQMDAwkAZwIFBmhlaWdodAUSbmV4dEJsb2NrVG9Qcm9jZXNzAwkAZwIFEm5leHRCbG9ja1RvUHJvY2VzcwUSY3VycmVudFN0YXJ0SGVpZ2h0BgkAAAIFE2N1cnJlbnRQZXJpb2RPckZhaWwAAAcJAGcCBQ9wZXJpb2RFbmRIZWlnaHQFEm5leHRCbG9ja1RvUHJvY2VzcwcJAJcDAQkAzAgCCQBkAgkAZQIFD3BlcmlvZEVuZEhlaWdodAUSbmV4dEJsb2NrVG9Qcm9jZXNzAAEJAMwIAgUcYmxvY2tzUHJvY2Vzc2luZ0JhdGNoU2l6ZU1heAUDbmlsCQEIdGhyb3dFcnIBCQEHd3JhcEVycgECFGludmFsaWQgdGFyZ2V0IGJsb2NrBBVibG9ja1Byb2Nlc3NpbmdSZXdhcmQJARN2YWx1ZU9yRXJyb3JNZXNzYWdlAgkAmggCBRRmYWN0b3J5QWRkcmVzc09yRmFpbAkBGGtleUJsb2NrUHJvY2Vzc2luZ1Jld2FyZAAJAQd3cmFwRXJyAQIfaW52YWxpZCBibG9jayBwcm9jZXNzaW5nIHJld2FyZAQgYmxvY2tQcm9jZXNzaW5nUmV3YXJkQnlHZW5lcmF0b3IJAGkCBRVibG9ja1Byb2Nlc3NpbmdSZXdhcmQFGWJsb2Nrc1Byb2Nlc3NpbmdCYXRjaFNpemUEKWJsb2NrUHJvY2Vzc2luZ1Jld2FyZEJ5R2VuZXJhdG9yUmVtYWluZGVyCQBlAgUVYmxvY2tQcm9jZXNzaW5nUmV3YXJkCQBoAgUgYmxvY2tQcm9jZXNzaW5nUmV3YXJkQnlHZW5lcmF0b3IFGWJsb2Nrc1Byb2Nlc3NpbmdCYXRjaFNpemUKAQNtYXACA2FjYwNpbmMDCQBnAgUDaW5jBRlibG9ja3NQcm9jZXNzaW5nQmF0Y2hTaXplBQNhY2MEEXRhcmdldEJsb2NrSGVpZ2h0CQBkAgUSbmV4dEJsb2NrVG9Qcm9jZXNzBQNpbmMED3RhcmdldEJsb2NrSW5mbwkBE3ZhbHVlT3JFcnJvck1lc3NhZ2UCCQDtBwEFEXRhcmdldEJsb2NrSGVpZ2h0CQEHd3JhcEVycgECEmludmFsaWQgYmxvY2sgaW5mbwQUdHJlYXN1cnlSZXdhcmRPckZhaWwJARN2YWx1ZU9yRXJyb3JNZXNzYWdlAgkBD3Jld2FyZEZvck9wdGlvbgIIBQ90YXJnZXRCbG9ja0luZm8HcmV3YXJkcwUacHJveHlUcmVhc3VyeUFkZHJlc3NPckZhaWwJAQd3cmFwRXJyAQkArAICAiNpbnZhbGlkIHRyZWFzdXJ5IHJld2FyZCBmb3IgaGVpZ2h0IAkApAMBBRF0YXJnZXRCbG9ja0hlaWdodAQJZ2VuZXJhdG9yCAUPdGFyZ2V0QmxvY2tJbmZvCWdlbmVyYXRvcgQJYXZhaWxhYmxlCQELdmFsdWVPckVsc2UCCQCaCAIFFGZhY3RvcnlBZGRyZXNzT3JGYWlsCQEMa2V5QXZhaWxhYmxlAQUJZ2VuZXJhdG9yAAAEDGNhbGxlclJld2FyZAMJAAACBQNpbmMJAGUCBRlibG9ja3NQcm9jZXNzaW5nQmF0Y2hTaXplAAEJAGQCBSBibG9ja1Byb2Nlc3NpbmdSZXdhcmRCeUdlbmVyYXRvcgUpYmxvY2tQcm9jZXNzaW5nUmV3YXJkQnlHZW5lcmF0b3JSZW1haW5kZXIFIGJsb2NrUHJvY2Vzc2luZ1Jld2FyZEJ5R2VuZXJhdG9yBA1scEFzc2V0QW1vdW50AwkAZgIFEmN1cnJlbnRQcmljZU9yRmFpbAAACQBrAwkAZQIFFHRyZWFzdXJ5UmV3YXJkT3JGYWlsBQxjYWxsZXJSZXdhcmQFBlNDQUxFOAUSY3VycmVudFByaWNlT3JGYWlsAAAEFGZhY3RvcnlBY3Rpb25zU2luZ2xlCQDMCAIJAPwHBAUUZmFjdG9yeUFkZHJlc3NPckZhaWwCC3N0cmluZ0VudHJ5CQDMCAIJARFrZXlCbG9ja1Byb2Nlc3NlZAEFEXRhcmdldEJsb2NrSGVpZ2h0CQDMCAIJALkJAgkAzAgCCQDYBAEIBQFpDXRyYW5zYWN0aW9uSWQJAMwIAgkApAMBBRNjdXJyZW50UGVyaW9kT3JGYWlsCQDMCAIJAKUIAQUJZ2VuZXJhdG9yCQDMCAIJANgEAQUQdXNlckFkZHJlc3NCeXRlcwkAzAgCCQCkAwEFFHRyZWFzdXJ5UmV3YXJkT3JGYWlsCQDMCAIJAKQDAQUMY2FsbGVyUmV3YXJkCQDMCAIJAKQDAQUNbHBBc3NldEFtb3VudAUDbmlsBQNTRVAFA25pbAUDbmlsCQDMCAIJAPwHBAUUZmFjdG9yeUFkZHJlc3NPckZhaWwCDGludGVnZXJFbnRyeQkAzAgCCQEMa2V5QXZhaWxhYmxlAQUJZ2VuZXJhdG9yCQDMCAIJAGQCBQlhdmFpbGFibGUFDWxwQXNzZXRBbW91bnQFA25pbAUDbmlsBQNuaWwDCQAAAgUUZmFjdG9yeUFjdGlvbnNTaW5nbGUFFGZhY3RvcnlBY3Rpb25zU2luZ2xlBA0kdDAyNDAwMjI0MDM1BQNhY2MECmxwQXNzZXRBY2MIBQ0kdDAyNDAwMjI0MDM1Al8xBAlyZXdhcmRBY2MIBQ0kdDAyNDAwMjI0MDM1Al8yCQCUCgIJAGQCBQpscEFzc2V0QWNjBQ1scEFzc2V0QW1vdW50CQBkAgUJcmV3YXJkQWNjBRR0cmVhc3VyeVJld2FyZE9yRmFpbAkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgQEbGlzdAkAzAgCAAAJAMwIAgABCQDMCAIAAgkAzAgCAAMJAMwIAgAECQDMCAIABQkAzAgCAAYJAMwIAgAHCQDMCAIACAkAzAgCAAkJAMwIAgAKCQDMCAIACwkAzAgCAAwJAMwIAgANCQDMCAIADgkAzAgCAA8JAMwIAgAQCQDMCAIAEQkAzAgCABIJAMwIAgATCQDMCAIAFAkAzAgCABUJAMwIAgAWCQDMCAIAFwkAzAgCABgJAMwIAgAZCQDMCAIAGgkAzAgCABsJAMwIAgAcCQDMCAIAHQkAzAgCAB4JAMwIAgAfCQDMCAIAIAkAzAgCACEJAMwIAgAiCQDMCAIAIwkAzAgCACQJAMwIAgAlCQDMCAIAJgkAzAgCACcFA25pbAQNJHQwMjQzMDIyNDM3OAoAAiRsBQRsaXN0CgACJHMJAJADAQUCJGwKAAUkYWNjMAkAlAoCAAAAAAoBBSRmMF8xAgIkYQIkaQMJAGcCBQIkaQUCJHMFAiRhCQEDbWFwAgUCJGEJAJEDAgUCJGwFAiRpCgEFJGYwXzICAiRhAiRpAwkAZwIFAiRpBQIkcwUCJGEJAAIBAhRMaXN0IHNpemUgZXhjZWVkcyA0MAkBBSRmMF8yAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgUFJGFjYzAAAAABAAIAAwAEAAUABgAHAAgACQAKAAsADAANAA4ADwAQABEAEgATABQAFQAWABcAGAAZABoAGwAcAB0AHgAfACAAIQAiACMAJAAlACYAJwAoAwkAAAIFDSR0MDI0MzAyMjQzNzgFDSR0MDI0MzAyMjQzNzgEEXJld2FyZEFtb3VudFRvdGFsCAUNJHQwMjQzMDIyNDM3OAJfMgQSbHBBc3NldEFtb3VudFRvdGFsCAUNJHQwMjQzMDIyNDM3OAJfMQQacmV3YXJkVG9NYWluVHJlYXN1cnlBbW91bnQJAGUCBRFyZXdhcmRBbW91bnRUb3RhbAUVYmxvY2tQcm9jZXNzaW5nUmV3YXJkBAhpbnZlc3RlZAkBC3ZhbHVlT3JFbHNlAgkAmggCBRRmYWN0b3J5QWRkcmVzc09yRmFpbAkBC2tleUludmVzdGVkAQUEdW5pdAAABAdhY3Rpb25zCQDMCAIJAPwHBAUUZmFjdG9yeUFkZHJlc3NPckZhaWwCB3JlaXNzdWUJAMwIAgUSbHBBc3NldEFtb3VudFRvdGFsBQNuaWwFA25pbAkAzAgCCQD8BwQFFGZhY3RvcnlBZGRyZXNzT3JGYWlsAgxpbnRlZ2VyRW50cnkJAMwIAgkBFWtleU5leHRCbG9ja1RvUHJvY2VzcwAJAMwIAgkAZAIFEm5leHRCbG9ja1RvUHJvY2VzcwUZYmxvY2tzUHJvY2Vzc2luZ0JhdGNoU2l6ZQUDbmlsBQNuaWwJAMwIAgkA/AcEBRRmYWN0b3J5QWRkcmVzc09yRmFpbAIZdHJhbnNmZXJGcm9tUHJveHlUcmVhc3VyeQkAzAgCCAUZbWFpblRyZWFzdXJ5QWRkcmVzc09yRmFpbAVieXRlcwkAzAgCBRpyZXdhcmRUb01haW5UcmVhc3VyeUFtb3VudAUDbmlsBQNuaWwJAMwIAgkA/AcEBRRmYWN0b3J5QWRkcmVzc09yRmFpbAIZdHJhbnNmZXJGcm9tUHJveHlUcmVhc3VyeQkAzAgCBRB1c2VyQWRkcmVzc0J5dGVzCQDMCAIFFWJsb2NrUHJvY2Vzc2luZ1Jld2FyZAUDbmlsBQNuaWwJAMwIAgkA/AcEBRRmYWN0b3J5QWRkcmVzc09yRmFpbAIMaW50ZWdlckVudHJ5CQDMCAIJAQtrZXlJbnZlc3RlZAEFBHVuaXQJAMwIAgkAZAIFCGludmVzdGVkBRpyZXdhcmRUb01haW5UcmVhc3VyeUFtb3VudAUDbmlsBQNuaWwFA25pbAMJAAACBQdhY3Rpb25zBQdhY3Rpb25zCQCUCgIFA25pbAUEdW5pdAkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgECdHgBBnZlcmlmeQAECXB1YmxpY0tleQQHJG1hdGNoMAkBGWdldE1hbmFnZXJQdWJsaWNLZXlPclVuaXQAAwkAAQIFByRtYXRjaDACCkJ5dGVWZWN0b3IEA3B1YgUHJG1hdGNoMAUDcHViCAUCdHgPc2VuZGVyUHVibGljS2V5CQD0AwMIBQJ0eAlib2R5Qnl0ZXMJAJEDAggFAnR4BnByb29mcwAABQlwdWJsaWNLZXl3O/N/", "height": 2684188, "applicationStatus": "succeeded", "spentComplexity": 0 } View: original | compacted Prev: CK4sYcu67kpWrryazaT54bguTXR2LWzHnenFWnhhvLGn Next: 5Ckarhp8XofjGwC1C3A8iQ5HomdERTtzi2SvuwSa8tEb Diff:
Old | New | Differences | |
---|---|---|---|
1 | - | {-# STDLIB_VERSION | |
1 | + | {-# STDLIB_VERSION 7 #-} | |
2 | 2 | {-# SCRIPT_TYPE ACCOUNT #-} | |
3 | 3 | {-# CONTENT_TYPE DAPP #-} | |
4 | - | let | |
4 | + | let SEP = "__" | |
5 | 5 | ||
6 | - | let | |
6 | + | let CONTRACT_NAME = "calculator.ride" | |
7 | 7 | ||
8 | - | func wrapErr (msg) = makeString(["voting_verified_v2.ride:", msg], " ") | |
8 | + | let SCALE8 = 100000000 | |
9 | + | ||
10 | + | let PENDING = "PENDING" | |
11 | + | ||
12 | + | let FINISHED = "FINISHED" | |
13 | + | ||
14 | + | let WAVES = "WAVES" | |
15 | + | ||
16 | + | func wrapErr (s) = ((CONTRACT_NAME + ": ") + s) | |
9 | 17 | ||
10 | 18 | ||
11 | - | func throwErr ( | |
19 | + | func throwErr (s) = throw(wrapErr(s)) | |
12 | 20 | ||
13 | 21 | ||
14 | - | func asInt (val) = match val { | |
15 | - | case valInt: Int => | |
16 | - | valInt | |
22 | + | func assetIdToString (assetId) = match assetId { | |
23 | + | case b: ByteVector => | |
24 | + | toBase58String(b) | |
25 | + | case _: Unit => | |
26 | + | WAVES | |
17 | 27 | case _ => | |
18 | - | throw(" | |
28 | + | throw("Match error") | |
19 | 29 | } | |
20 | 30 | ||
21 | 31 | ||
22 | - | func getIntegerOrZero (address,key) = valueOrElse(getInteger(address, key), 0) | |
32 | + | func stringToAssetId (s) = if ((s == WAVES)) | |
33 | + | then unit | |
34 | + | else fromBase58String(s) | |
23 | 35 | ||
24 | 36 | ||
25 | - | func getIntegerOrFail (address,key) = valueOrErrorMessage(getInteger(address, key), wrapErr((key + " is not defined"))) | |
37 | + | func abs (n) = if ((0 > n)) | |
38 | + | then -(n) | |
39 | + | else n | |
26 | 40 | ||
27 | 41 | ||
28 | - | func | |
42 | + | func keyFactoryAddress () = makeString(["%s", "factory"], SEP) | |
29 | 43 | ||
30 | 44 | ||
31 | - | func | |
45 | + | func keyManagerPublicKey () = makeString(["%s", "managerPublicKey"], SEP) | |
32 | 46 | ||
33 | 47 | ||
34 | - | let keyBoostingContract = makeString(["%s", "boostingContract"], separator) | |
35 | - | ||
36 | - | let keyEmissionContract = makeString(["%s", "emissionContract"], separator) | |
37 | - | ||
38 | - | let keyAssetsStoreContract = makeString(["%s", "assetsStoreContract"], separator) | |
39 | - | ||
40 | - | let boostingContract = addressFromStringValue(getStringValue(keyBoostingContract)) | |
41 | - | ||
42 | - | let emissionContract = addressFromStringValue(getStringValue(keyEmissionContract)) | |
43 | - | ||
44 | - | let assetsStoreContract = addressFromStringValue(getStringValue(keyAssetsStoreContract)) | |
45 | - | ||
46 | - | let keyEmissionConfig = makeString(["%s", "config"], separator) | |
47 | - | ||
48 | - | let wxAssetIdStr = split(getStringOrFail(emissionContract, keyEmissionConfig), separator)[1] | |
49 | - | ||
50 | - | let wxAssetId = fromBase58String(wxAssetIdStr) | |
51 | - | ||
52 | - | let keyVotingThresholdAdd = makeString(["%s%s", "votingThreshold", "add"], separator) | |
53 | - | ||
54 | - | let keyVotingThresholdRemove = makeString(["%s%s", "votingThreshold", "remove"], separator) | |
55 | - | ||
56 | - | let keyPeriodLengthRemove = makeString(["%s", "periodLengthRemove"], separator) | |
57 | - | ||
58 | - | let keyMaxPeriodLength = makeString(["%s", "maxPeriodLength"], separator) | |
59 | - | ||
60 | - | let keyMinPeriodLength = makeString(["%s", "minPeriodLength"], separator) | |
61 | - | ||
62 | - | func keyVotingRewardAssetId (assetId,index) = makeString(["%s%s%d", "votingRewardAssetId", assetId, toString(index)], separator) | |
48 | + | func keyLpAssetId () = makeString(["%s", "lpAssetId"], SEP) | |
63 | 49 | ||
64 | 50 | ||
65 | - | func | |
51 | + | func keyProxyTreasuryAddress () = makeString(["%s", "proxyTreasury"], SEP) | |
66 | 52 | ||
67 | 53 | ||
68 | - | func | |
54 | + | func keyMainTreasuryAddress () = makeString(["%s", "mainTreasury"], SEP) | |
69 | 55 | ||
70 | 56 | ||
71 | - | let keyFinalizeCallRewardAmount = makeString(["%s", "finalizeCallRewardAmount"], separator) | |
72 | - | ||
73 | - | let keyMinSuggestRemoveBalance = makeString(["%s", "minSuggestRemoveBalance"], separator) | |
74 | - | ||
75 | - | func keyCurrentIndex (assetId) = makeString(["%s%s", "currentIndex", assetId], separator) | |
57 | + | func keyInvested (assetId) = makeString(["%s%s", "invested", assetIdToString(assetId)], SEP) | |
76 | 58 | ||
77 | 59 | ||
78 | - | func | |
60 | + | func keyDonated (assetId) = makeString(["%s%s", "donated", assetIdToString(assetId)], SEP) | |
79 | 61 | ||
80 | 62 | ||
81 | - | func | |
63 | + | func keyAvailable (userAddress) = makeString(["%s%s", "available", toString(userAddress)], SEP) | |
82 | 64 | ||
83 | 65 | ||
84 | - | func | |
66 | + | func keyClaimed (userAddress) = makeString(["%s%s", "claimed", toString(userAddress)], SEP) | |
85 | 67 | ||
86 | 68 | ||
87 | - | func | |
69 | + | func keyCurrentPeriod () = makeString(["%s", "currentPeriod"], SEP) | |
88 | 70 | ||
89 | 71 | ||
90 | - | let keyFeePerBlock = makeString(["%s", "feePerBlock"], separator) | |
91 | - | ||
92 | - | let feePerBlock = getIntegerOrFail(this, keyFeePerBlock) | |
93 | - | ||
94 | - | let keyMinWxMinForSuggestAddAmountRequired = makeString(["%s", "wxMinForSuggestAddAmountRequired"], separator) | |
95 | - | ||
96 | - | let keyWxForSuggestRemoveAmountRequired = makeString(["%s", "wxForSuggestRemoveAmountRequired"], separator) | |
97 | - | ||
98 | - | func keyVotingInfo (assetId,index) = makeString(["%s%s%d", "votingInfo", assetId, toString(index)], separator) | |
72 | + | func keyPriceForPeriod (period) = makeString(["%s%d", "price", toString(period)], SEP) | |
99 | 73 | ||
100 | 74 | ||
101 | - | func | |
75 | + | func keyStartHeight (period) = makeString(["%s%d", "startHeight", toString(period)], SEP) | |
102 | 76 | ||
103 | 77 | ||
104 | - | func | |
78 | + | func keyPowerManagerBonus (period) = makeString(["%s%d", "powerManagerBonus", toString(period)], SEP) | |
105 | 79 | ||
106 | 80 | ||
107 | - | func getUserGwxAmountAtHeight (userAddress,targetHeight) = { | |
108 | - | let gwxAmount = invoke(boostingContract, "getUserGwxAmountAtHeightREADONLY", [userAddress, targetHeight], nil) | |
109 | - | asInt(gwxAmount) | |
81 | + | func keyPeriodLength () = "%s__periodLength" | |
82 | + | ||
83 | + | ||
84 | + | func keyBlockProcessingReward () = "%s__blockProcessingReward" | |
85 | + | ||
86 | + | ||
87 | + | func keyNextBlockToProcess () = "%s__nextBlockToProcess" | |
88 | + | ||
89 | + | ||
90 | + | func keyBlockProcessed (height) = makeString(["%s%d", "blockProcessed", toString(height)], SEP) | |
91 | + | ||
92 | + | ||
93 | + | func keyWithdrawal () = "%s__withdrawal" | |
94 | + | ||
95 | + | ||
96 | + | func keyWithdrawalRequest (userAddress,txId) = makeString(["%s%s%s", "withdrawal", toString(userAddress), toBase58String(txId)], SEP) | |
97 | + | ||
98 | + | ||
99 | + | func valueWithdrawalRequest (status,lpAssetAmount,targetPeriod,claimTxId) = { | |
100 | + | let claimTxIdStr = match claimTxId { | |
101 | + | case b: ByteVector => | |
102 | + | toBase58String(b) | |
103 | + | case _: Unit => | |
104 | + | "SOON" | |
105 | + | case _ => | |
106 | + | throw("Match error") | |
107 | + | } | |
108 | + | makeString(["%s%d%d%s", status, toString(lpAssetAmount), toString(targetPeriod), claimTxIdStr], SEP) | |
110 | 109 | } | |
111 | 110 | ||
112 | 111 | ||
113 | - | func | |
112 | + | func keyPeriodWithdrawalAssetIds (period) = makeString(["%s%d", "periodReward", toString(period)], SEP) | |
114 | 113 | ||
115 | 114 | ||
116 | - | func | |
115 | + | func keyPeriodWithdrawalAmounts (period) = makeString(["%s%d", "periodRewardAmount", toString(period)], SEP) | |
117 | 116 | ||
118 | 117 | ||
119 | - | func getManagerVaultAddressOrThis () = match getString(keyManagerVaultAddress()) { | |
118 | + | let keyMinHeightForWithdraw = makeString(["%s", "minHeightForWithdraw"], SEP) | |
119 | + | ||
120 | + | let keyMaxHeightForWithdraw = makeString(["%s", "maxHeightForWithdraw"], SEP) | |
121 | + | ||
122 | + | func parseWithdrawalRequestValueOrFail (s) = { | |
123 | + | let parts = split(s, SEP) | |
124 | + | if ((size(parts) == 5)) | |
125 | + | then { | |
126 | + | let status = parts[1] | |
127 | + | let lpAssetAmount = valueOrErrorMessage(parseInt(parts[2]), wrapErr("invalid lpAssetAmount")) | |
128 | + | let targetPeriod = valueOrErrorMessage(parseInt(parts[3]), wrapErr("invalid targetPeriod")) | |
129 | + | let claimTxId = parts[4] | |
130 | + | $Tuple4(status, lpAssetAmount, targetPeriod, claimTxId) | |
131 | + | } | |
132 | + | else throwErr("invalid withdrawal request value") | |
133 | + | } | |
134 | + | ||
135 | + | ||
136 | + | let factoryAddressOption = match getString(this, keyFactoryAddress()) { | |
120 | 137 | case s: String => | |
121 | - | addressFromStringValue(s) | |
138 | + | addressFromString(s) | |
139 | + | case _: Unit => | |
140 | + | unit | |
122 | 141 | case _ => | |
123 | - | this | |
142 | + | throw("Match error") | |
143 | + | } | |
144 | + | ||
145 | + | let factoryAddressOrFail = valueOrErrorMessage(factoryAddressOption, wrapErr("invalid factory address")) | |
146 | + | ||
147 | + | let lpAssetIdOption = match factoryAddressOption { | |
148 | + | case a: Address => | |
149 | + | match getString(a, keyLpAssetId()) { | |
150 | + | case s: String => | |
151 | + | fromBase58String(s) | |
152 | + | case _: Unit => | |
153 | + | unit | |
154 | + | case _ => | |
155 | + | throw("Match error") | |
156 | + | } | |
157 | + | case _: Unit => | |
158 | + | unit | |
159 | + | case _ => | |
160 | + | throw("Match error") | |
161 | + | } | |
162 | + | ||
163 | + | let lpAssetIdOrFail = valueOrErrorMessage(lpAssetIdOption, wrapErr("invalid lpAssetId")) | |
164 | + | ||
165 | + | let proxyTreasuryAddressOption = match factoryAddressOption { | |
166 | + | case a: Address => | |
167 | + | match getString(a, keyProxyTreasuryAddress()) { | |
168 | + | case s: String => | |
169 | + | addressFromString(s) | |
170 | + | case _: Unit => | |
171 | + | unit | |
172 | + | case _ => | |
173 | + | throw("Match error") | |
174 | + | } | |
175 | + | case _: Unit => | |
176 | + | unit | |
177 | + | case _ => | |
178 | + | throw("Match error") | |
179 | + | } | |
180 | + | ||
181 | + | let proxyTreasuryAddressOrFail = valueOrErrorMessage(proxyTreasuryAddressOption, wrapErr("invalid proxy treasury address")) | |
182 | + | ||
183 | + | let mainTreasuryAddressOption = match factoryAddressOption { | |
184 | + | case a: Address => | |
185 | + | match getString(a, keyMainTreasuryAddress()) { | |
186 | + | case s: String => | |
187 | + | addressFromString(s) | |
188 | + | case _: Unit => | |
189 | + | unit | |
190 | + | case _ => | |
191 | + | throw("Match error") | |
192 | + | } | |
193 | + | case _: Unit => | |
194 | + | unit | |
195 | + | case _ => | |
196 | + | throw("Match error") | |
197 | + | } | |
198 | + | ||
199 | + | let mainTreasuryAddressOrFail = valueOrErrorMessage(mainTreasuryAddressOption, wrapErr("invalid main treasury address")) | |
200 | + | ||
201 | + | func getManagerPublicKeyOrUnit () = match factoryAddressOption { | |
202 | + | case fa: Address => | |
203 | + | match getString(fa, keyManagerPublicKey()) { | |
204 | + | case pub: String => | |
205 | + | fromBase58String(pub) | |
206 | + | case _ => | |
207 | + | unit | |
208 | + | } | |
209 | + | case _ => | |
210 | + | unit | |
124 | 211 | } | |
125 | 212 | ||
126 | 213 | ||
127 | - | func managerPublicKeyOrUnit () = { | |
128 | - | let managerVaultAddress = getManagerVaultAddressOrThis() | |
129 | - | match getString(managerVaultAddress, keyManagerPublicKey()) { | |
130 | - | case s: String => | |
131 | - | fromBase58String(s) | |
132 | - | case _: Unit => | |
133 | - | unit | |
134 | - | case _ => | |
135 | - | throw("Match error") | |
136 | - | } | |
214 | + | func onlyAddress (i,address) = if ((i.caller == address)) | |
215 | + | then true | |
216 | + | else throwErr("permission denied") | |
217 | + | ||
218 | + | ||
219 | + | func onlyFactory (i) = onlyAddress(i, factoryAddressOrFail) | |
220 | + | ||
221 | + | ||
222 | + | func rewardForOption (rewards,target) = { | |
223 | + | let s = size(rewards) | |
224 | + | let $t052085233 = rewards[0] | |
225 | + | let a0 = $t052085233._1 | |
226 | + | let r0 = $t052085233._2 | |
227 | + | let $t052365261 = rewards[1] | |
228 | + | let a1 = $t052365261._1 | |
229 | + | let r1 = $t052365261._2 | |
230 | + | let $t052645289 = rewards[2] | |
231 | + | let a2 = $t052645289._1 | |
232 | + | let r2 = $t052645289._2 | |
233 | + | if (if ((s > 0)) | |
234 | + | then (a0 == target) | |
235 | + | else false) | |
236 | + | then r0 | |
237 | + | else if (if ((s > 1)) | |
238 | + | then (a1 == target) | |
239 | + | else false) | |
240 | + | then r1 | |
241 | + | else if (if ((s > 2)) | |
242 | + | then (a2 == target) | |
243 | + | else false) | |
244 | + | then r2 | |
245 | + | else unit | |
137 | 246 | } | |
138 | 247 | ||
139 | 248 | ||
140 | - | func getVotingInfoParts (votingInfo) = { | |
141 | - | let votingInfoParts = split(votingInfo, separator) | |
142 | - | let isRewardExistStr = votingInfoParts[1] | |
143 | - | let isRewardExist = if ((isRewardExistStr == "true")) | |
249 | + | func finalizeINTERNAL (newTreasuryVolumeInWaves,pwrManagersBonusInWaves,treasuryVolumeDiffAllocationCoef) = { | |
250 | + | let donatedWavesAmount = valueOrElse(getInteger(factoryAddressOrFail, keyDonated(unit)), 0) | |
251 | + | let investedWavesAmount = valueOrElse(getInteger(factoryAddressOrFail, keyInvested(unit)), 0) | |
252 | + | let currentTreasuryVolumeInWaves = (donatedWavesAmount + investedWavesAmount) | |
253 | + | let profitRaw = (newTreasuryVolumeInWaves - currentTreasuryVolumeInWaves) | |
254 | + | let pwrManagersBonusAmount = if (if ((profitRaw >= pwrManagersBonusInWaves)) | |
144 | 255 | then true | |
145 | - | else false | |
146 | - | let rewardAssetId = votingInfoParts[2] | |
147 | - | let rewardAmount = parseIntValue(votingInfoParts[3]) | |
148 | - | let votingType = votingInfoParts[4] | |
149 | - | let status = votingInfoParts[5] | |
150 | - | let votingStartHeight = parseIntValue(votingInfoParts[6]) | |
151 | - | let votingEndHeight = parseIntValue(votingInfoParts[7]) | |
152 | - | let votesQuorum = parseIntValue(votingInfoParts[8]) | |
153 | - | let votesFor = parseIntValue(votingInfoParts[9]) | |
154 | - | let votesAgainst = parseIntValue(votingInfoParts[10]) | |
155 | - | $Tuple10(isRewardExist, rewardAssetId, rewardAmount, votingType, status, votingStartHeight, votingEndHeight, votesQuorum, votesFor, votesAgainst) | |
156 | - | } | |
157 | - | ||
158 | - | ||
159 | - | func votingExistChecks (assetId,currentIndex) = { | |
160 | - | let votingInfo = valueOrErrorMessage(getString(keyVotingInfo(assetId, currentIndex)), wrapErr("voting info not found")) | |
161 | - | let votingInfoArray = split(votingInfo, separator) | |
162 | - | let status = votingInfoArray[5] | |
163 | - | let votingEndHeight = valueOrErrorMessage(parseIntValue(votingInfoArray[7]), wrapErr("voting start height not found")) | |
164 | - | let suggestIssuer = valueOrErrorMessage(getString(keySuggestIssuer(assetId, currentIndex)), wrapErr("voting issuer not found")) | |
165 | - | let checks = [if ((status == "inProgress")) | |
256 | + | else (pwrManagersBonusInWaves == 0)) | |
257 | + | then pwrManagersBonusInWaves | |
258 | + | else throwErr("power bonus is more than profit") | |
259 | + | let profit = (profitRaw - pwrManagersBonusAmount) | |
260 | + | let donationPart = if ((currentTreasuryVolumeInWaves > 0)) | |
261 | + | then fraction(donatedWavesAmount, SCALE8, currentTreasuryVolumeInWaves) | |
262 | + | else 0 | |
263 | + | let donationProfitPartRaw = fraction(profit, donationPart, SCALE8) | |
264 | + | let investmentProfitPartRaw = (profit - donationProfitPartRaw) | |
265 | + | let treasuryVolumeDiffAllocationCoefAbs = abs(treasuryVolumeDiffAllocationCoef) | |
266 | + | let amountToDonation = fraction(investmentProfitPartRaw, if ((0 > treasuryVolumeDiffAllocationCoef)) | |
267 | + | then treasuryVolumeDiffAllocationCoefAbs | |
268 | + | else 0, SCALE8) | |
269 | + | let amountToInvestment = fraction(donationProfitPartRaw, if ((treasuryVolumeDiffAllocationCoef > 0)) | |
270 | + | then treasuryVolumeDiffAllocationCoefAbs | |
271 | + | else 0, SCALE8) | |
272 | + | let donationProfitPart = ((donationProfitPartRaw - amountToInvestment) + amountToDonation) | |
273 | + | let investmentProfitPart = ((investmentProfitPartRaw - amountToDonation) + amountToInvestment) | |
274 | + | let donatedWavesAmountNewRaw = (donatedWavesAmount + donationProfitPart) | |
275 | + | let investedWavesAmountNewRaw = (investedWavesAmount + investmentProfitPart) | |
276 | + | let donatedPartDebt = min([0, donatedWavesAmountNewRaw]) | |
277 | + | let investedPartDebt = min([0, investedWavesAmountNewRaw]) | |
278 | + | let donatedWavesAmountNew = (max([0, donatedWavesAmountNewRaw]) + investedPartDebt) | |
279 | + | let investedWavesAmountNew = (max([0, investedWavesAmountNewRaw]) + donatedPartDebt) | |
280 | + | let lpAssetQuantity = valueOrErrorMessage(assetInfo(lpAssetIdOrFail), wrapErr("invalid lpAsset info")).quantity | |
281 | + | let newPrice = fraction(investedWavesAmountNew, SCALE8, lpAssetQuantity) | |
282 | + | let checkIfPriceNotZero = if ((newPrice != 0)) | |
166 | 283 | then true | |
167 | - | else throwErr("no voting in progress"), if ((votingEndHeight > height)) | |
168 | - | then true | |
169 | - | else throwErr("voting expired")] | |
170 | - | if ((checks == checks)) | |
171 | - | then $Tuple3(status, votingEndHeight, suggestIssuer) | |
284 | + | else throwErr("LP price cannot be 0") | |
285 | + | if ((checkIfPriceNotZero == checkIfPriceNotZero)) | |
286 | + | then { | |
287 | + | let lpAssetAmountToBurn = valueOrElse(getInteger(factoryAddressOrFail, keyWithdrawal()), 0) | |
288 | + | let paymentAmountMin = max([0, fraction(lpAssetAmountToBurn, newPrice, SCALE8)]) | |
289 | + | let finalInvestedWavesAmount = (investedWavesAmountNew - paymentAmountMin) | |
290 | + | let lpAssetFinalQuantity = (lpAssetQuantity - lpAssetAmountToBurn) | |
291 | + | $Tuple6(paymentAmountMin, finalInvestedWavesAmount, donatedWavesAmountNew, newPrice, lpAssetAmountToBurn, lpAssetFinalQuantity) | |
292 | + | } | |
172 | 293 | else throw("Strict value is not equal to itself.") | |
173 | 294 | } | |
174 | 295 | ||
175 | 296 | ||
176 | - | func calculateReward (voter,assetId,index) = { | |
177 | - | let voteKey = keyVote(assetId, index, voter) | |
178 | - | let lastVote = valueOrErrorMessage(getString(voteKey), wrapErr("you have not voted")) | |
179 | - | let lastVoteParts = split(lastVote, separator) | |
180 | - | let gwxAmount = parseIntValue(lastVoteParts[2]) | |
181 | - | let votingInfoStr = valueOrErrorMessage(getString(keyVotingInfo(assetId, index)), wrapErr("voting info not found")) | |
182 | - | let votingParts = getVotingInfoParts(votingInfoStr) | |
183 | - | let votesFor = votingParts._9 | |
184 | - | let votesAgainst = votingParts._10 | |
185 | - | let partOfTheTotalVotesX8 = fraction(gwxAmount, MULT8, (votesFor + votesAgainst)) | |
186 | - | let totalVotingReward = valueOrElse(getInteger(keyTotalVotingReward(assetId, index)), 0) | |
187 | - | let voterRewardAmount = fraction(partOfTheTotalVotesX8, totalVotingReward, MULT8, FLOOR) | |
188 | - | voterRewardAmount | |
189 | - | } | |
190 | - | ||
191 | - | ||
192 | 297 | @Callable(i) | |
193 | - | func suggestAdd (assetId,periodLength,assetImage) = { | |
194 | - | let wxPayment = i.payments[0] | |
195 | - | let wxPaymentAssetId = value(wxPayment.assetId) | |
196 | - | let wxPaymentAmount = value(wxPayment.amount) | |
197 | - | let minPeriodLength = getIntegerValue(keyMinPeriodLength) | |
198 | - | let maxPeriodLength = getIntegerValue(keyMaxPeriodLength) | |
199 | - | let tokenIsVerified = { | |
200 | - | let @ = invoke(assetsStoreContract, "isVerifiedREADONLY", [assetId], nil) | |
201 | - | if ($isInstanceOf(@, "Boolean")) | |
202 | - | then @ | |
203 | - | else throw(($getType(@) + " couldn't be cast to Boolean")) | |
204 | - | } | |
205 | - | let checks = [if (if ((periodLength >= minPeriodLength)) | |
206 | - | then (maxPeriodLength >= periodLength) | |
207 | - | else false) | |
208 | - | then true | |
209 | - | else throwErr("invalid periodLength"), if ((tokenIsVerified == false)) | |
210 | - | then true | |
211 | - | else throwErr("token already verified"), if ((wxPaymentAmount > (periodLength * feePerBlock))) | |
212 | - | then true | |
213 | - | else throwErr("not enough wx for given period"), if ((wxPaymentAmount >= getIntegerValue(keyMinWxMinForSuggestAddAmountRequired))) | |
214 | - | then true | |
215 | - | else throwErr("payment less then min for suggest")] | |
216 | - | if ((checks == checks)) | |
298 | + | func claimLP (userAddressBytes) = { | |
299 | + | let checkCaller = onlyFactory(i) | |
300 | + | if ((checkCaller == checkCaller)) | |
217 | 301 | then { | |
218 | - | let currentIndexKey = keyCurrentIndex(assetId) | |
219 | - | let currentIndex = getInteger(currentIndexKey) | |
220 | - | let newIndex = if (isDefined(currentIndex)) | |
221 | - | then (value(currentIndex) + 1) | |
222 | - | else 0 | |
223 | - | let $t087849424 = if ((size(i.payments) > 1)) | |
224 | - | then { | |
225 | - | let votingRewardPayment = i.payments[1] | |
226 | - | let votingRewardPaymentAssetId = toBase58String(value(votingRewardPayment.assetId)) | |
227 | - | let votingRewardPaymentAmount = value(votingRewardPayment.amount) | |
228 | - | $Tuple4(true, votingRewardPaymentAssetId, votingRewardPaymentAmount, [StringEntry(keyVotingRewardAssetId(assetId, newIndex), votingRewardPaymentAssetId), IntegerEntry(keyTotalVotingReward(assetId, newIndex), votingRewardPaymentAmount)]) | |
229 | - | } | |
230 | - | else $Tuple4(false, "EMPTY", 0, nil) | |
231 | - | let isRewardExist = $t087849424._1 | |
232 | - | let rewardAssetId = $t087849424._2 | |
233 | - | let rewardAmount = $t087849424._3 | |
234 | - | let votingRewardActions = $t087849424._4 | |
235 | - | let votesQuorum = valueOrErrorMessage(getInteger(keyVotingThresholdAdd), wrapErr("votingThresholdAdd not set")) | |
236 | - | let votingInfo = votingInfoValue(isRewardExist, rewardAssetId, rewardAmount, "verification", "inProgress", height, (height + periodLength), votesQuorum, 0, 0) | |
237 | - | let finalizeCallRewardAmount = getIntegerValue(keyFinalizeCallRewardAmount) | |
238 | - | let burnWxAmount = (wxPaymentAmount - finalizeCallRewardAmount) | |
239 | - | ([IntegerEntry(currentIndexKey, newIndex), StringEntry(keySuggestIssuer(assetId, newIndex), toString(i.caller)), StringEntry(keyVotingInfo(assetId, newIndex), votingInfo), StringEntry(keyAssetImage(assetId), assetImage), Burn(wxPaymentAssetId, burnWxAmount)] ++ votingRewardActions) | |
302 | + | let userAddress = Address(userAddressBytes) | |
303 | + | let available = valueOrElse(getInteger(factoryAddressOrFail, keyAvailable(userAddress)), 0) | |
304 | + | let claimed = valueOrElse(getInteger(factoryAddressOrFail, keyClaimed(userAddress)), 0) | |
305 | + | let factoryActions = if ((available > 0)) | |
306 | + | then [invoke(factoryAddressOrFail, "transferAsset", [userAddressBytes, available, lpAssetIdOrFail], nil), invoke(factoryAddressOrFail, "integerEntry", [keyAvailable(userAddress), 0], nil), invoke(factoryAddressOrFail, "integerEntry", [keyClaimed(userAddress), (claimed + available)], nil)] | |
307 | + | else throwErr("nothing to claim") | |
308 | + | $Tuple2(nil, factoryActions) | |
240 | 309 | } | |
241 | 310 | else throw("Strict value is not equal to itself.") | |
242 | 311 | } | |
244 | 313 | ||
245 | 314 | ||
246 | 315 | @Callable(i) | |
247 | - | func suggestRemove (assetId) = { | |
248 | - | let gwxAmountAtNow = getUserGwxAmountAtHeight(toString(i.caller), height) | |
249 | - | let minSuggestRemoveBalance = getIntegerValue(keyMinSuggestRemoveBalance) | |
250 | - | let wxPayment = i.payments[0] | |
251 | - | let wxPaymentAssetId = value(wxPayment.assetId) | |
252 | - | let wxPaymentAmount = value(wxPayment.amount) | |
253 | - | let tokenIsVerified = { | |
254 | - | let @ = invoke(assetsStoreContract, "isVerifiedREADONLY", [assetId], nil) | |
255 | - | if ($isInstanceOf(@, "Boolean")) | |
256 | - | then @ | |
257 | - | else throw(($getType(@) + " couldn't be cast to Boolean")) | |
258 | - | } | |
259 | - | let checks = [if (tokenIsVerified) | |
260 | - | then true | |
261 | - | else throwErr("token not verified"), if ((gwxAmountAtNow >= minSuggestRemoveBalance)) | |
262 | - | then true | |
263 | - | else throwErr("not enough gWXes"), if ((wxPaymentAmount >= getIntegerValue(keyWxForSuggestRemoveAmountRequired))) | |
264 | - | then true | |
265 | - | else throwErr("payment less then min for suggest")] | |
266 | - | if ((checks == checks)) | |
316 | + | func finalize (userAddressBytes,newTreasuryVolumeInWaves,pwrManagersBonusInWaves,treasuryVolumeDiffAllocationCoef) = { | |
317 | + | let checkCaller = onlyFactory(i) | |
318 | + | if ((checkCaller == checkCaller)) | |
267 | 319 | then { | |
268 | - | let currentIndexKey = keyCurrentIndex(assetId) | |
269 | - | let currentIndex = getInteger(currentIndexKey) | |
270 | - | let newIndex = if (isDefined(currentIndex)) | |
271 | - | then (value(currentIndex) + 1) | |
272 | - | else 0 | |
273 | - | let periodLength = valueOrErrorMessage(getInteger(keyPeriodLengthRemove), wrapErr("periodLengthRemove not set")) | |
274 | - | let votingEndHeight = (height + periodLength) | |
275 | - | let votesQuorum = valueOrErrorMessage(getInteger(keyVotingThresholdRemove), wrapErr("votingThresholdRemove not set")) | |
276 | - | let votingInfo = votingInfoValue(false, "EMPTY", 0, "deverification", "inProgress", height, (height + periodLength), votesQuorum, 0, 0) | |
277 | - | [IntegerEntry(currentIndexKey, newIndex), StringEntry(keySuggestIssuer(assetId, newIndex), toString(i.caller)), StringEntry(keyVotingInfo(assetId, newIndex), votingInfo)] | |
278 | - | } | |
279 | - | else throw("Strict value is not equal to itself.") | |
280 | - | } | |
281 | - | ||
282 | - | ||
283 | - | ||
284 | - | @Callable(i) | |
285 | - | func vote (assetId,inFavor) = { | |
286 | - | let currentIndexKey = keyCurrentIndex(assetId) | |
287 | - | let currentIndex = valueOrErrorMessage(getInteger(currentIndexKey), wrapErr("voting does not exist")) | |
288 | - | let votingInfo = votingExistChecks(assetId, currentIndex) | |
289 | - | if ((votingInfo == votingInfo)) | |
290 | - | then { | |
291 | - | let currentVotingEndHeight = votingInfo._2 | |
292 | - | let gwxAmountAtEnd = getUserGwxAmountAtHeight(toString(i.caller), currentVotingEndHeight) | |
293 | - | let voteKey = keyVote(assetId, currentIndex, i.caller) | |
294 | - | let checks = [if ((getString(voteKey) == unit)) | |
320 | + | let currentPeriodOrFail = valueOrErrorMessage(getInteger(factoryAddressOrFail, keyCurrentPeriod()), wrapErr("invalid period")) | |
321 | + | let periodLength = valueOrErrorMessage(getInteger(factoryAddressOrFail, keyPeriodLength()), wrapErr("invalid period length")) | |
322 | + | let currentStartHeight = valueOrErrorMessage(getInteger(factoryAddressOrFail, keyStartHeight(currentPeriodOrFail)), wrapErr("invalid start height")) | |
323 | + | let currentPriceOrFail = valueOrErrorMessage(getInteger(factoryAddressOrFail, keyPriceForPeriod(currentPeriodOrFail)), wrapErr("invalid price")) | |
324 | + | let nextBlockToProcess = valueOrErrorMessage(getInteger(factoryAddressOrFail, keyNextBlockToProcess()), wrapErr("invalid next block to process")) | |
325 | + | let periodEndHeight = ((currentStartHeight + periodLength) - 1) | |
326 | + | let checks = [if ((i.originCaller == mainTreasuryAddressOrFail)) | |
295 | 327 | then true | |
296 | - | else throwErr(" | |
328 | + | else throwErr("permission denied"), if ((nextBlockToProcess > periodEndHeight)) | |
297 | 329 | then true | |
298 | - | else throw("You'll not have gWX at the end of voting")] | |
330 | + | else throwErr("unprocessed blocks"), if ((newTreasuryVolumeInWaves >= 0)) | |
331 | + | then true | |
332 | + | else throwErr("invalid new treasury volume"), if ((pwrManagersBonusInWaves >= 0)) | |
333 | + | then true | |
334 | + | else throwErr("invalid PWR managers bonus"), if (if ((treasuryVolumeDiffAllocationCoef >= -(SCALE8))) | |
335 | + | then (SCALE8 >= treasuryVolumeDiffAllocationCoef) | |
336 | + | else false) | |
337 | + | then true | |
338 | + | else throwErr("invalid treasury volume diff allocation coefficient")] | |
299 | 339 | if ((checks == checks)) | |
300 | 340 | then { | |
301 | - | let votingInfoStr = valueOrErrorMessage(getString(keyVotingInfo(assetId, currentIndex)), wrapErr("voting info not found")) | |
302 | - | let votingInfoParts = getVotingInfoParts(votingInfoStr) | |
303 | - | let votesFor = votingInfoParts._9 | |
304 | - | let votesAgainst = votingInfoParts._10 | |
305 | - | let $t01274512906 = if (inFavor) | |
306 | - | then $Tuple2((votesFor + gwxAmountAtEnd), votesAgainst) | |
307 | - | else $Tuple2(votesFor, (votesAgainst + gwxAmountAtEnd)) | |
308 | - | let newVotesFor = $t01274512906._1 | |
309 | - | let newVotesAgainst = $t01274512906._2 | |
310 | - | let newVotingInfoValue = votingInfoValue(votingInfoParts._1, votingInfoParts._2, votingInfoParts._3, votingInfoParts._4, votingInfoParts._5, votingInfoParts._6, votingInfoParts._7, votingInfoParts._8, newVotesFor, newVotesAgainst) | |
311 | - | let votingRewardAction = match getString(keyVotingRewardAssetId(assetId, currentIndex)) { | |
312 | - | case pk: String => | |
313 | - | [StringEntry(keyVotingReward(i.caller, assetId, currentIndex), voteValue(inFavor, gwxAmountAtEnd))] | |
314 | - | case _: Unit => | |
315 | - | nil | |
316 | - | case _ => | |
317 | - | throw("Match error") | |
318 | - | } | |
319 | - | ([StringEntry(voteKey, voteValue(inFavor, gwxAmountAtEnd)), StringEntry(keyVotingInfo(assetId, currentIndex), newVotingInfoValue)] ++ votingRewardAction) | |
341 | + | let $t01078311043 = finalizeINTERNAL(newTreasuryVolumeInWaves, pwrManagersBonusInWaves, treasuryVolumeDiffAllocationCoef) | |
342 | + | let paymentAmountMin = $t01078311043._1 | |
343 | + | let finalInvestedWavesAmount = $t01078311043._2 | |
344 | + | let donatedWavesAmountNew = $t01078311043._3 | |
345 | + | let newPrice = $t01078311043._4 | |
346 | + | let lpAssetAmountToBurn = $t01078311043._5 | |
347 | + | let lpAssetFinalQuantity = $t01078311043._6 | |
348 | + | let newPeriod = (currentPeriodOrFail + 1) | |
349 | + | func addNewAction (actions,payment) = { | |
350 | + | let $t01119111253 = actions | |
351 | + | let scriptTransfers = $t01119111253._1 | |
352 | + | let assetIdsString = $t01119111253._2 | |
353 | + | let amountsString = $t01119111253._3 | |
354 | + | let paymentAmount = payment.amount | |
355 | + | let paymentAssetId = payment.assetId | |
356 | + | let newAssetIdsString = ("%s" + makeString([assetIdsString, assetIdToString(paymentAssetId)], SEP)) | |
357 | + | let newAmountsString = ("%d" + makeString([amountsString, toString(paymentAmount)], SEP)) | |
358 | + | let newScriptTransfer = ScriptTransfer(factoryAddressOrFail, paymentAmount, paymentAssetId) | |
359 | + | $Tuple3((scriptTransfers :+ newScriptTransfer), newAssetIdsString, newAmountsString) | |
360 | + | } | |
361 | + | ||
362 | + | let $t01171311804 = { | |
363 | + | let $l = i.payments | |
364 | + | let $s = size($l) | |
365 | + | let $acc0 = $Tuple3(nil, "", "") | |
366 | + | func $f0_1 ($a,$i) = if (($i >= $s)) | |
367 | + | then $a | |
368 | + | else addNewAction($a, $l[$i]) | |
369 | + | ||
370 | + | func $f0_2 ($a,$i) = if (($i >= $s)) | |
371 | + | then $a | |
372 | + | else throw("List size exceeds 10") | |
373 | + | ||
374 | + | $f0_2($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($acc0, 0), 1), 2), 3), 4), 5), 6), 7), 8), 9), 10) | |
375 | + | } | |
376 | + | let scriptTransfers = $t01171311804._1 | |
377 | + | let AssetIds = $t01171311804._2 | |
378 | + | let Amounts = $t01171311804._3 | |
379 | + | let factoryActions = [invoke(factoryAddressOrFail, "integerEntry", [keyPowerManagerBonus(currentPeriodOrFail), pwrManagersBonusInWaves], nil), invoke(factoryAddressOrFail, "integerEntry", [keyCurrentPeriod(), newPeriod], nil), invoke(factoryAddressOrFail, "integerEntry", [keyPriceForPeriod(newPeriod), newPrice], nil), invoke(factoryAddressOrFail, "integerEntry", [keyStartHeight(newPeriod), (periodEndHeight + 1)], nil), invoke(factoryAddressOrFail, "burn", [lpAssetAmountToBurn], nil), invoke(factoryAddressOrFail, "integerEntry", [keyWithdrawal(), 0], nil), invoke(factoryAddressOrFail, "integerEntry", [keyInvested(unit), finalInvestedWavesAmount], nil), invoke(factoryAddressOrFail, "integerEntry", [keyDonated(unit), donatedWavesAmountNew], nil), invoke(factoryAddressOrFail, "stringEntry", [keyPeriodWithdrawalAssetIds(newPeriod), AssetIds], nil), invoke(factoryAddressOrFail, "stringEntry", [keyPeriodWithdrawalAmounts(newPeriod), Amounts], nil)] | |
380 | + | $Tuple2(scriptTransfers, factoryActions) | |
320 | 381 | } | |
321 | 382 | else throw("Strict value is not equal to itself.") | |
322 | 383 | } | |
326 | 387 | ||
327 | 388 | ||
328 | 389 | @Callable(i) | |
329 | - | func cancelVote (assetId) = { | |
330 | - | let currentIndexKey = keyCurrentIndex(assetId) | |
331 | - | let currentIndex = valueOrErrorMessage(getInteger(currentIndexKey), wrapErr("voting does not exist")) | |
332 | - | let voteKey = keyVote(assetId, currentIndex, i.caller) | |
333 | - | let lastVote = valueOrErrorMessage(getString(voteKey), wrapErr("you have not voted")) | |
334 | - | let lastVoteParts = split(lastVote, separator) | |
335 | - | let inFavor = lastVoteParts[1] | |
336 | - | let gwxAmount = parseIntValue(lastVoteParts[2]) | |
337 | - | let votingInfo = votingExistChecks(assetId, currentIndex) | |
338 | - | if ((votingInfo == votingInfo)) | |
390 | + | func finalizeREADONLY (newTreasuryVolumeInWaves,pwrManagersBonusInWaves,treasuryVolumeDiffAllocationCoef) = { | |
391 | + | let currentPeriodOrFail = valueOrErrorMessage(getInteger(factoryAddressOrFail, keyCurrentPeriod()), wrapErr("invalid period")) | |
392 | + | let periodLength = valueOrErrorMessage(getInteger(factoryAddressOrFail, keyPeriodLength()), wrapErr("invalid period length")) | |
393 | + | let currentStartHeight = valueOrErrorMessage(getInteger(factoryAddressOrFail, keyStartHeight(currentPeriodOrFail)), wrapErr("invalid start height")) | |
394 | + | let currentPriceOrFail = valueOrErrorMessage(getInteger(factoryAddressOrFail, keyPriceForPeriod(currentPeriodOrFail)), wrapErr("invalid price")) | |
395 | + | let nextBlockToProcess = valueOrErrorMessage(getInteger(factoryAddressOrFail, keyNextBlockToProcess()), wrapErr("invalid next block to process")) | |
396 | + | let periodEndHeight = ((currentStartHeight + periodLength) - 1) | |
397 | + | let checks = [if ((nextBlockToProcess > periodEndHeight)) | |
398 | + | then true | |
399 | + | else throwErr("unprocessed blocks"), if ((newTreasuryVolumeInWaves >= 0)) | |
400 | + | then true | |
401 | + | else throwErr("invalid new treasury volume"), if ((pwrManagersBonusInWaves >= 0)) | |
402 | + | then true | |
403 | + | else throwErr("invalid PWR managers bonus"), if (if ((treasuryVolumeDiffAllocationCoef >= -(SCALE8))) | |
404 | + | then (SCALE8 >= treasuryVolumeDiffAllocationCoef) | |
405 | + | else false) | |
406 | + | then true | |
407 | + | else throwErr("invalid treasury volume diff allocation coefficient")] | |
408 | + | if ((checks == checks)) | |
409 | + | then $Tuple2(nil, finalizeINTERNAL(newTreasuryVolumeInWaves, pwrManagersBonusInWaves, treasuryVolumeDiffAllocationCoef)) | |
410 | + | else throw("Strict value is not equal to itself.") | |
411 | + | } | |
412 | + | ||
413 | + | ||
414 | + | ||
415 | + | @Callable(i) | |
416 | + | func invest (userAddressBytes) = { | |
417 | + | let checkCaller = onlyFactory(i) | |
418 | + | if ((checkCaller == checkCaller)) | |
339 | 419 | then { | |
340 | - | let checks = [if (if ((inFavor == "true")) | |
420 | + | let userAddress = Address(userAddressBytes) | |
421 | + | let currentPeriodOrFail = valueOrErrorMessage(getInteger(factoryAddressOrFail, keyCurrentPeriod()), wrapErr("invalid period")) | |
422 | + | let currentPriceOrFail = valueOrErrorMessage(getInteger(factoryAddressOrFail, keyPriceForPeriod(currentPeriodOrFail)), wrapErr("invalid price")) | |
423 | + | let payment = if ((size(i.payments) == 1)) | |
424 | + | then i.payments[0] | |
425 | + | else throwErr("invalid payments") | |
426 | + | let $t01528515462 = if (if ((payment.assetId == unit)) | |
427 | + | then (payment.amount > 0) | |
428 | + | else false) | |
429 | + | then $Tuple2(payment.amount, payment.assetId) | |
430 | + | else throwErr("invalid payment amount") | |
431 | + | let paymentAmount = $t01528515462._1 | |
432 | + | let paymentAssetId = $t01528515462._2 | |
433 | + | let lpAssetAmount = if ((currentPriceOrFail > 0)) | |
434 | + | then fraction(paymentAmount, SCALE8, currentPriceOrFail) | |
435 | + | else 0 | |
436 | + | let invested = valueOrElse(getInteger(factoryAddressOrFail, keyInvested(unit)), 0) | |
437 | + | let actions = [ScriptTransfer(mainTreasuryAddressOrFail, paymentAmount, paymentAssetId)] | |
438 | + | let factoryActions = [invoke(factoryAddressOrFail, "integerEntry", [keyInvested(unit), (invested + paymentAmount)], nil), invoke(factoryAddressOrFail, "reissue", [lpAssetAmount], nil), invoke(factoryAddressOrFail, "transferAsset", [userAddressBytes, lpAssetAmount, lpAssetIdOrFail], nil)] | |
439 | + | $Tuple2(actions, factoryActions) | |
440 | + | } | |
441 | + | else throw("Strict value is not equal to itself.") | |
442 | + | } | |
443 | + | ||
444 | + | ||
445 | + | ||
446 | + | @Callable(i) | |
447 | + | func withdraw (userAddressBytes) = { | |
448 | + | let checkCaller = onlyFactory(i) | |
449 | + | if ((checkCaller == checkCaller)) | |
450 | + | then { | |
451 | + | let userAddress = Address(userAddressBytes) | |
452 | + | let currentPeriodOrFail = valueOrErrorMessage(getInteger(factoryAddressOrFail, keyCurrentPeriod()), wrapErr("invalid period")) | |
453 | + | let payment = if ((size(i.payments) == 1)) | |
454 | + | then i.payments[0] | |
455 | + | else throwErr(wrapErr("invalid payments")) | |
456 | + | let paymentAssetId = if ((payment.assetId == lpAssetIdOrFail)) | |
457 | + | then lpAssetIdOrFail | |
458 | + | else throwErr("invalid payment asset") | |
459 | + | let paymentAmount = if ((payment.amount > 0)) | |
460 | + | then payment.amount | |
461 | + | else throwErr("invalid payment amount") | |
462 | + | let withdrawal = valueOrElse(getInteger(factoryAddressOrFail, keyWithdrawal()), 0) | |
463 | + | let actions = [ScriptTransfer(factoryAddressOrFail, paymentAmount, paymentAssetId)] | |
464 | + | let factoryActions = [invoke(factoryAddressOrFail, "integerEntry", [keyWithdrawal(), (withdrawal + paymentAmount)], nil), invoke(factoryAddressOrFail, "stringEntry", [keyWithdrawalRequest(userAddress, i.transactionId), valueWithdrawalRequest(PENDING, paymentAmount, (currentPeriodOrFail + 1), unit)], nil)] | |
465 | + | if ((factoryActions == factoryActions)) | |
466 | + | then $Tuple2(actions, factoryActions) | |
467 | + | else throw("Strict value is not equal to itself.") | |
468 | + | } | |
469 | + | else throw("Strict value is not equal to itself.") | |
470 | + | } | |
471 | + | ||
472 | + | ||
473 | + | ||
474 | + | @Callable(i) | |
475 | + | func cancelWithdraw (userAddressBytes,txId) = { | |
476 | + | let checkCaller = onlyFactory(i) | |
477 | + | if ((checkCaller == checkCaller)) | |
478 | + | then { | |
479 | + | let userAddress = Address(userAddressBytes) | |
480 | + | let withdrawalRequestOption = valueOrErrorMessage(getString(factoryAddressOrFail, keyWithdrawalRequest(userAddress, txId)), wrapErr("invalid withdrawal request")) | |
481 | + | let currentPeriodOrFail = valueOrErrorMessage(getInteger(factoryAddressOrFail, keyCurrentPeriod()), wrapErr("invalid period")) | |
482 | + | let $t01786917982 = parseWithdrawalRequestValueOrFail(withdrawalRequestOption) | |
483 | + | let status = $t01786917982._1 | |
484 | + | let lpAssetAmount = $t01786917982._2 | |
485 | + | let targetPeriod = $t01786917982._3 | |
486 | + | let claimTxId = $t01786917982._4 | |
487 | + | let checks = [if ((status == PENDING)) | |
341 | 488 | then true | |
342 | - | else ( | |
489 | + | else throwErr("invalid withdrawal request status"), if ((targetPeriod > currentPeriodOrFail)) | |
343 | 490 | then true | |
344 | - | else throwErr("invalid | |
491 | + | else throwErr("invalid withdrawal request period")] | |
345 | 492 | if ((checks == checks)) | |
346 | 493 | then { | |
347 | - | let votingInfoStr = valueOrErrorMessage(getString(keyVotingInfo(assetId, currentIndex)), wrapErr("voting info not found")) | |
348 | - | let votingInfoParts = getVotingInfoParts(votingInfoStr) | |
349 | - | let votesFor = votingInfoParts._9 | |
350 | - | let votesAgainst = votingInfoParts._10 | |
351 | - | let $t01453114692 = if ((inFavor == "true")) | |
352 | - | then $Tuple2((votesFor - gwxAmount), votesAgainst) | |
353 | - | else $Tuple2(votesFor, (votesAgainst - gwxAmount)) | |
354 | - | let newVotesFor = $t01453114692._1 | |
355 | - | let newVotesAgainst = $t01453114692._2 | |
356 | - | let newVotingInfoValue = votingInfoValue(votingInfoParts._1, votingInfoParts._2, votingInfoParts._3, votingInfoParts._4, votingInfoParts._5, votingInfoParts._6, votingInfoParts._7, votingInfoParts._8, newVotesFor, newVotesAgainst) | |
357 | - | [StringEntry(keyVotingInfo(assetId, currentIndex), newVotingInfoValue), DeleteEntry(voteKey), DeleteEntry(keyVotingReward(i.caller, assetId, currentIndex))] | |
494 | + | let withdrawal = valueOrErrorMessage(getInteger(factoryAddressOrFail, keyWithdrawal()), wrapErr("invalid total withdrawal amount")) | |
495 | + | let factoryActions = [invoke(factoryAddressOrFail, "integerEntry", [keyWithdrawal(), if ((withdrawal >= lpAssetAmount)) | |
496 | + | then (withdrawal - lpAssetAmount) | |
497 | + | else throwErr("invalid withdrawal amount")], nil), invoke(factoryAddressOrFail, "deleteEntry", [keyWithdrawalRequest(userAddress, txId)], nil), invoke(factoryAddressOrFail, "transferAsset", [userAddressBytes, lpAssetAmount, lpAssetIdOrFail], nil)] | |
498 | + | if ((factoryActions == factoryActions)) | |
499 | + | then $Tuple2(nil, factoryActions) | |
500 | + | else throw("Strict value is not equal to itself.") | |
358 | 501 | } | |
359 | 502 | else throw("Strict value is not equal to itself.") | |
360 | 503 | } | |
364 | 507 | ||
365 | 508 | ||
366 | 509 | @Callable(i) | |
367 | - | func finalize (assetId) = { | |
368 | - | let currentIndexKey = keyCurrentIndex(assetId) | |
369 | - | let currentIndex = valueOrElse(getInteger(currentIndexKey), 0) | |
370 | - | let votingThresholdAdd = valueOrErrorMessage(getInteger(keyVotingThresholdAdd), wrapErr("votingThresholdAdd not set")) | |
371 | - | let votingThresholdRemove = valueOrErrorMessage(getInteger(keyVotingThresholdRemove), wrapErr("votingThresholdRemove not set")) | |
372 | - | let votingInfoStr = valueOrErrorMessage(getString(keyVotingInfo(assetId, currentIndex)), wrapErr("voting info not found")) | |
373 | - | let votingInfoParts = getVotingInfoParts(votingInfoStr) | |
374 | - | let votingType = votingInfoParts._4 | |
375 | - | let status = votingInfoParts._5 | |
376 | - | let votingEndHeight = votingInfoParts._7 | |
377 | - | let votingQuorum = votingInfoParts._8 | |
378 | - | let votesFor = votingInfoParts._9 | |
379 | - | let votesAgainst = votingInfoParts._10 | |
380 | - | let checks = [if ((status == "inProgress")) | |
381 | - | then true | |
382 | - | else throwErr("voting not in progress"), if ((height >= votingEndHeight)) | |
383 | - | then true | |
384 | - | else throwErr("voting not finished"), if (isDefined(getString(keyAssetImage(assetId)))) | |
385 | - | then true | |
386 | - | else throwErr("asset image not set")] | |
387 | - | if ((checks == checks)) | |
510 | + | func claimCollateral (userAddressBytes,txId) = { | |
511 | + | let checkCaller = onlyFactory(i) | |
512 | + | if ((checkCaller == checkCaller)) | |
388 | 513 | then { | |
389 | - | let votingAccepted = if (if (((votesFor + votesAgainst) >= votingQuorum)) | |
390 | - | then (votesFor > votesAgainst) | |
514 | + | let userAddress = Address(userAddressBytes) | |
515 | + | let currentPeriodOrFail = valueOrErrorMessage(getInteger(factoryAddressOrFail, keyCurrentPeriod()), wrapErr("invalid period")) | |
516 | + | let withdrawalRequestOption = valueOrErrorMessage(getString(factoryAddressOrFail, keyWithdrawalRequest(userAddress, txId)), wrapErr("invalid withdrawal request")) | |
517 | + | let $t01931919432 = parseWithdrawalRequestValueOrFail(withdrawalRequestOption) | |
518 | + | let status = $t01931919432._1 | |
519 | + | let lpAssetAmount = $t01931919432._2 | |
520 | + | let targetPeriod = $t01931919432._3 | |
521 | + | let claimTxId = $t01931919432._4 | |
522 | + | if ((status == FINISHED)) | |
523 | + | then throwErr("invalid withdrawal request status") | |
524 | + | else if ((targetPeriod > currentPeriodOrFail)) | |
525 | + | then throwErr("invalid withdrawal request period") | |
526 | + | else { | |
527 | + | let priceOrFail = valueOrErrorMessage(getInteger(factoryAddressOrFail, keyPriceForPeriod(targetPeriod)), wrapErr("invalid price")) | |
528 | + | let amount = if ((priceOrFail > 0)) | |
529 | + | then fraction(lpAssetAmount, priceOrFail, SCALE8) | |
530 | + | else 0 | |
531 | + | let factoryActions = [invoke(factoryAddressOrFail, "stringEntry", [keyWithdrawalRequest(userAddress, txId), valueWithdrawalRequest(FINISHED, lpAssetAmount, targetPeriod, i.transactionId)], nil), { | |
532 | + | let assetsList = removeByIndex(split_51C(getStringValue(factoryAddressOrFail, keyPeriodWithdrawalAssetIds(currentPeriodOrFail)), SEP), 0) | |
533 | + | let amountsList = removeByIndex(split_51C(getStringValue(factoryAddressOrFail, keyPeriodWithdrawalAmounts(currentPeriodOrFail)), SEP), 0) | |
534 | + | invoke(factoryAddressOrFail, "transferAssets", [userAddressBytes, assetsList, amountsList], nil) | |
535 | + | }] | |
536 | + | $Tuple2(nil, factoryActions) | |
537 | + | } | |
538 | + | } | |
539 | + | else throw("Strict value is not equal to itself.") | |
540 | + | } | |
541 | + | ||
542 | + | ||
543 | + | ||
544 | + | @Callable(i) | |
545 | + | func processBlocks (userAddressBytes) = { | |
546 | + | let checkCaller = onlyFactory(i) | |
547 | + | if ((checkCaller == checkCaller)) | |
548 | + | then { | |
549 | + | let userAddress = Address(userAddressBytes) | |
550 | + | let currentPeriodOrFail = valueOrErrorMessage(getInteger(factoryAddressOrFail, keyCurrentPeriod()), wrapErr("invalid period")) | |
551 | + | let periodLength = valueOrErrorMessage(getInteger(factoryAddressOrFail, keyPeriodLength()), wrapErr("invalid period length")) | |
552 | + | let currentStartHeight = valueOrErrorMessage(getInteger(factoryAddressOrFail, keyStartHeight(currentPeriodOrFail)), wrapErr("invalid start height")) | |
553 | + | let currentPriceOrFail = valueOrErrorMessage(getInteger(factoryAddressOrFail, keyPriceForPeriod(currentPeriodOrFail)), wrapErr("invalid price")) | |
554 | + | let nextBlockToProcess = valueOrErrorMessage(getInteger(factoryAddressOrFail, keyNextBlockToProcess()), wrapErr("invalid next block to process")) | |
555 | + | let periodEndHeight = ((currentStartHeight + periodLength) - 1) | |
556 | + | let blocksProcessingBatchSizeMax = 40 | |
557 | + | let blocksProcessingBatchSize = if (if (if ((height >= nextBlockToProcess)) | |
558 | + | then if ((nextBlockToProcess >= currentStartHeight)) | |
559 | + | then true | |
560 | + | else (currentPeriodOrFail == 0) | |
391 | 561 | else false) | |
392 | - | then true | |
393 | - | else false | |
394 | - | let newStatus = if (votingAccepted) | |
395 | - | then "accepted" | |
396 | - | else "rejected" | |
397 | - | let assetImage = getStringValue(keyAssetImage(assetId)) | |
398 | - | let isVotingAccepted = if (votingAccepted) | |
562 | + | then (periodEndHeight >= nextBlockToProcess) | |
563 | + | else false) | |
564 | + | then min([((periodEndHeight - nextBlockToProcess) + 1), blocksProcessingBatchSizeMax]) | |
565 | + | else throwErr(wrapErr("invalid target block")) | |
566 | + | let blockProcessingReward = valueOrErrorMessage(getInteger(factoryAddressOrFail, keyBlockProcessingReward()), wrapErr("invalid block processing reward")) | |
567 | + | let blockProcessingRewardByGenerator = (blockProcessingReward / blocksProcessingBatchSize) | |
568 | + | let blockProcessingRewardByGeneratorRemainder = (blockProcessingReward - (blockProcessingRewardByGenerator * blocksProcessingBatchSize)) | |
569 | + | func map (acc,inc) = if ((inc >= blocksProcessingBatchSize)) | |
570 | + | then acc | |
571 | + | else { | |
572 | + | let targetBlockHeight = (nextBlockToProcess + inc) | |
573 | + | let targetBlockInfo = valueOrErrorMessage(blockInfoByHeight(targetBlockHeight), wrapErr("invalid block info")) | |
574 | + | let treasuryRewardOrFail = valueOrErrorMessage(rewardForOption(targetBlockInfo.rewards, proxyTreasuryAddressOrFail), wrapErr(("invalid treasury reward for height " + toString(targetBlockHeight)))) | |
575 | + | let generator = targetBlockInfo.generator | |
576 | + | let available = valueOrElse(getInteger(factoryAddressOrFail, keyAvailable(generator)), 0) | |
577 | + | let callerReward = if ((inc == (blocksProcessingBatchSize - 1))) | |
578 | + | then (blockProcessingRewardByGenerator + blockProcessingRewardByGeneratorRemainder) | |
579 | + | else blockProcessingRewardByGenerator | |
580 | + | let lpAssetAmount = if ((currentPriceOrFail > 0)) | |
581 | + | then fraction((treasuryRewardOrFail - callerReward), SCALE8, currentPriceOrFail) | |
582 | + | else 0 | |
583 | + | let factoryActionsSingle = [invoke(factoryAddressOrFail, "stringEntry", [keyBlockProcessed(targetBlockHeight), makeString([toBase58String(i.transactionId), toString(currentPeriodOrFail), toString(generator), toBase58String(userAddressBytes), toString(treasuryRewardOrFail), toString(callerReward), toString(lpAssetAmount)], SEP)], nil), invoke(factoryAddressOrFail, "integerEntry", [keyAvailable(generator), (available + lpAssetAmount)], nil)] | |
584 | + | if ((factoryActionsSingle == factoryActionsSingle)) | |
585 | + | then { | |
586 | + | let $t02400224035 = acc | |
587 | + | let lpAssetAcc = $t02400224035._1 | |
588 | + | let rewardAcc = $t02400224035._2 | |
589 | + | $Tuple2((lpAssetAcc + lpAssetAmount), (rewardAcc + treasuryRewardOrFail)) | |
590 | + | } | |
591 | + | else throw("Strict value is not equal to itself.") | |
592 | + | } | |
593 | + | ||
594 | + | let list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39] | |
595 | + | let $t02430224378 = { | |
596 | + | let $l = list | |
597 | + | let $s = size($l) | |
598 | + | let $acc0 = $Tuple2(0, 0) | |
599 | + | func $f0_1 ($a,$i) = if (($i >= $s)) | |
600 | + | then $a | |
601 | + | else map($a, $l[$i]) | |
602 | + | ||
603 | + | func $f0_2 ($a,$i) = if (($i >= $s)) | |
604 | + | then $a | |
605 | + | else throw("List size exceeds 40") | |
606 | + | ||
607 | + | $f0_2($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($acc0, 0), 1), 2), 3), 4), 5), 6), 7), 8), 9), 10), 11), 12), 13), 14), 15), 16), 17), 18), 19), 20), 21), 22), 23), 24), 25), 26), 27), 28), 29), 30), 31), 32), 33), 34), 35), 36), 37), 38), 39), 40) | |
608 | + | } | |
609 | + | if (($t02430224378 == $t02430224378)) | |
399 | 610 | then { | |
400 | - | let votingAcceptedInvokes = if ((votingType == "verification")) | |
401 | - | then invoke(assetsStoreContract, "createOrUpdate", [assetId, assetImage, true], nil) | |
402 | - | else invoke(assetsStoreContract, "setVerified", [assetId, false], nil) | |
403 | - | votingAcceptedInvokes | |
404 | - | } | |
405 | - | else nil | |
406 | - | if ((isVotingAccepted == isVotingAccepted)) | |
407 | - | then { | |
408 | - | let newVotingInfoValue = votingInfoValue(votingInfoParts._1, votingInfoParts._2, votingInfoParts._3, votingInfoParts._4, newStatus, votingInfoParts._6, votingInfoParts._7, votingInfoParts._8, votingInfoParts._9, votingInfoParts._10) | |
409 | - | let finalizeCallRewardAmount = getIntegerValue(keyFinalizeCallRewardAmount) | |
410 | - | [StringEntry(keyVotingInfo(assetId, currentIndex), newVotingInfoValue), ScriptTransfer(i.caller, finalizeCallRewardAmount, wxAssetId)] | |
611 | + | let rewardAmountTotal = $t02430224378._2 | |
612 | + | let lpAssetAmountTotal = $t02430224378._1 | |
613 | + | let rewardToMainTreasuryAmount = (rewardAmountTotal - blockProcessingReward) | |
614 | + | let invested = valueOrElse(getInteger(factoryAddressOrFail, keyInvested(unit)), 0) | |
615 | + | let actions = [invoke(factoryAddressOrFail, "reissue", [lpAssetAmountTotal], nil), invoke(factoryAddressOrFail, "integerEntry", [keyNextBlockToProcess(), (nextBlockToProcess + blocksProcessingBatchSize)], nil), invoke(factoryAddressOrFail, "transferFromProxyTreasury", [mainTreasuryAddressOrFail.bytes, rewardToMainTreasuryAmount], nil), invoke(factoryAddressOrFail, "transferFromProxyTreasury", [userAddressBytes, blockProcessingReward], nil), invoke(factoryAddressOrFail, "integerEntry", [keyInvested(unit), (invested + rewardToMainTreasuryAmount)], nil)] | |
616 | + | if ((actions == actions)) | |
617 | + | then $Tuple2(nil, unit) | |
618 | + | else throw("Strict value is not equal to itself.") | |
411 | 619 | } | |
412 | 620 | else throw("Strict value is not equal to itself.") | |
413 | 621 | } | |
415 | 623 | } | |
416 | 624 | ||
417 | 625 | ||
418 | - | ||
419 | - | @Callable(i) | |
420 | - | func claimREADONLY (assetId,index,userAddressStr) = { | |
421 | - | let userAddress = valueOrErrorMessage(addressFromString(userAddressStr), wrapErr("invalid address")) | |
422 | - | let votingRewardAssetIdStr = getString(keyVotingRewardAssetId(assetId, index)) | |
423 | - | let rewardAmount = if (isDefined(votingRewardAssetIdStr)) | |
424 | - | then calculateReward(userAddress, assetId, index) | |
425 | - | else 0 | |
426 | - | $Tuple2(nil, rewardAmount) | |
427 | - | } | |
428 | - | ||
429 | - | ||
430 | - | ||
431 | - | @Callable(i) | |
432 | - | func claim (assetId,index) = { | |
433 | - | let callerAddress = i.caller | |
434 | - | let claimHistoryKey = keyClaimHistory(callerAddress, assetId, index) | |
435 | - | let claimHistory = getInteger(claimHistoryKey) | |
436 | - | let checks = [if ((claimHistory == unit)) | |
437 | - | then true | |
438 | - | else throwErr("already claimed")] | |
439 | - | if ((checks == checks)) | |
440 | - | then { | |
441 | - | let rewardAmount = if ((calculateReward(callerAddress, assetId, index) > 0)) | |
442 | - | then calculateReward(callerAddress, assetId, index) | |
443 | - | else throwErr("nothing to claim") | |
444 | - | let votingRewardAssetIdStr = getString(keyVotingRewardAssetId(assetId, index)) | |
445 | - | let rewardAction = if (isDefined(votingRewardAssetIdStr)) | |
446 | - | then { | |
447 | - | let votingRewardAssetId = fromBase58String(value(votingRewardAssetIdStr)) | |
448 | - | [ScriptTransfer(callerAddress, rewardAmount, votingRewardAssetId), IntegerEntry(claimHistoryKey, rewardAmount), DeleteEntry(keyVotingReward(callerAddress, assetId, index))] | |
449 | - | } | |
450 | - | else throwErr("nothing to claim") | |
451 | - | rewardAction | |
452 | - | } | |
453 | - | else throw("Strict value is not equal to itself.") | |
454 | - | } | |
455 | - | ||
456 | - | ||
457 | 626 | @Verifier(tx) | |
458 | 627 | func verify () = { | |
459 | - | let | |
460 | - | case | |
461 | - | | |
462 | - | case _ | |
628 | + | let publicKey = match getManagerPublicKeyOrUnit() { | |
629 | + | case pub: ByteVector => | |
630 | + | pub | |
631 | + | case _ => | |
463 | 632 | tx.senderPublicKey | |
464 | - | case _ => | |
465 | - | throw("Match error") | |
466 | 633 | } | |
467 | - | sigVerify(tx.bodyBytes, tx.proofs[0], | |
634 | + | sigVerify(tx.bodyBytes, tx.proofs[0], publicKey) | |
468 | 635 | } | |
469 | 636 |
Old | New | Differences | |
---|---|---|---|
1 | - | {-# STDLIB_VERSION | |
1 | + | {-# STDLIB_VERSION 7 #-} | |
2 | 2 | {-# SCRIPT_TYPE ACCOUNT #-} | |
3 | 3 | {-# CONTENT_TYPE DAPP #-} | |
4 | - | let | |
4 | + | let SEP = "__" | |
5 | 5 | ||
6 | - | let | |
6 | + | let CONTRACT_NAME = "calculator.ride" | |
7 | 7 | ||
8 | - | func wrapErr (msg) = makeString(["voting_verified_v2.ride:", msg], " ") | |
8 | + | let SCALE8 = 100000000 | |
9 | + | ||
10 | + | let PENDING = "PENDING" | |
11 | + | ||
12 | + | let FINISHED = "FINISHED" | |
13 | + | ||
14 | + | let WAVES = "WAVES" | |
15 | + | ||
16 | + | func wrapErr (s) = ((CONTRACT_NAME + ": ") + s) | |
9 | 17 | ||
10 | 18 | ||
11 | - | func throwErr ( | |
19 | + | func throwErr (s) = throw(wrapErr(s)) | |
12 | 20 | ||
13 | 21 | ||
14 | - | func asInt (val) = match val { | |
15 | - | case valInt: Int => | |
16 | - | valInt | |
22 | + | func assetIdToString (assetId) = match assetId { | |
23 | + | case b: ByteVector => | |
24 | + | toBase58String(b) | |
25 | + | case _: Unit => | |
26 | + | WAVES | |
17 | 27 | case _ => | |
18 | - | throw(" | |
28 | + | throw("Match error") | |
19 | 29 | } | |
20 | 30 | ||
21 | 31 | ||
22 | - | func getIntegerOrZero (address,key) = valueOrElse(getInteger(address, key), 0) | |
32 | + | func stringToAssetId (s) = if ((s == WAVES)) | |
33 | + | then unit | |
34 | + | else fromBase58String(s) | |
23 | 35 | ||
24 | 36 | ||
25 | - | func getIntegerOrFail (address,key) = valueOrErrorMessage(getInteger(address, key), wrapErr((key + " is not defined"))) | |
37 | + | func abs (n) = if ((0 > n)) | |
38 | + | then -(n) | |
39 | + | else n | |
26 | 40 | ||
27 | 41 | ||
28 | - | func | |
42 | + | func keyFactoryAddress () = makeString(["%s", "factory"], SEP) | |
29 | 43 | ||
30 | 44 | ||
31 | - | func | |
45 | + | func keyManagerPublicKey () = makeString(["%s", "managerPublicKey"], SEP) | |
32 | 46 | ||
33 | 47 | ||
34 | - | let keyBoostingContract = makeString(["%s", "boostingContract"], separator) | |
35 | - | ||
36 | - | let keyEmissionContract = makeString(["%s", "emissionContract"], separator) | |
37 | - | ||
38 | - | let keyAssetsStoreContract = makeString(["%s", "assetsStoreContract"], separator) | |
39 | - | ||
40 | - | let boostingContract = addressFromStringValue(getStringValue(keyBoostingContract)) | |
41 | - | ||
42 | - | let emissionContract = addressFromStringValue(getStringValue(keyEmissionContract)) | |
43 | - | ||
44 | - | let assetsStoreContract = addressFromStringValue(getStringValue(keyAssetsStoreContract)) | |
45 | - | ||
46 | - | let keyEmissionConfig = makeString(["%s", "config"], separator) | |
47 | - | ||
48 | - | let wxAssetIdStr = split(getStringOrFail(emissionContract, keyEmissionConfig), separator)[1] | |
49 | - | ||
50 | - | let wxAssetId = fromBase58String(wxAssetIdStr) | |
51 | - | ||
52 | - | let keyVotingThresholdAdd = makeString(["%s%s", "votingThreshold", "add"], separator) | |
53 | - | ||
54 | - | let keyVotingThresholdRemove = makeString(["%s%s", "votingThreshold", "remove"], separator) | |
55 | - | ||
56 | - | let keyPeriodLengthRemove = makeString(["%s", "periodLengthRemove"], separator) | |
57 | - | ||
58 | - | let keyMaxPeriodLength = makeString(["%s", "maxPeriodLength"], separator) | |
59 | - | ||
60 | - | let keyMinPeriodLength = makeString(["%s", "minPeriodLength"], separator) | |
61 | - | ||
62 | - | func keyVotingRewardAssetId (assetId,index) = makeString(["%s%s%d", "votingRewardAssetId", assetId, toString(index)], separator) | |
48 | + | func keyLpAssetId () = makeString(["%s", "lpAssetId"], SEP) | |
63 | 49 | ||
64 | 50 | ||
65 | - | func | |
51 | + | func keyProxyTreasuryAddress () = makeString(["%s", "proxyTreasury"], SEP) | |
66 | 52 | ||
67 | 53 | ||
68 | - | func | |
54 | + | func keyMainTreasuryAddress () = makeString(["%s", "mainTreasury"], SEP) | |
69 | 55 | ||
70 | 56 | ||
71 | - | let keyFinalizeCallRewardAmount = makeString(["%s", "finalizeCallRewardAmount"], separator) | |
72 | - | ||
73 | - | let keyMinSuggestRemoveBalance = makeString(["%s", "minSuggestRemoveBalance"], separator) | |
74 | - | ||
75 | - | func keyCurrentIndex (assetId) = makeString(["%s%s", "currentIndex", assetId], separator) | |
57 | + | func keyInvested (assetId) = makeString(["%s%s", "invested", assetIdToString(assetId)], SEP) | |
76 | 58 | ||
77 | 59 | ||
78 | - | func | |
60 | + | func keyDonated (assetId) = makeString(["%s%s", "donated", assetIdToString(assetId)], SEP) | |
79 | 61 | ||
80 | 62 | ||
81 | - | func | |
63 | + | func keyAvailable (userAddress) = makeString(["%s%s", "available", toString(userAddress)], SEP) | |
82 | 64 | ||
83 | 65 | ||
84 | - | func | |
66 | + | func keyClaimed (userAddress) = makeString(["%s%s", "claimed", toString(userAddress)], SEP) | |
85 | 67 | ||
86 | 68 | ||
87 | - | func | |
69 | + | func keyCurrentPeriod () = makeString(["%s", "currentPeriod"], SEP) | |
88 | 70 | ||
89 | 71 | ||
90 | - | let keyFeePerBlock = makeString(["%s", "feePerBlock"], separator) | |
91 | - | ||
92 | - | let feePerBlock = getIntegerOrFail(this, keyFeePerBlock) | |
93 | - | ||
94 | - | let keyMinWxMinForSuggestAddAmountRequired = makeString(["%s", "wxMinForSuggestAddAmountRequired"], separator) | |
95 | - | ||
96 | - | let keyWxForSuggestRemoveAmountRequired = makeString(["%s", "wxForSuggestRemoveAmountRequired"], separator) | |
97 | - | ||
98 | - | func keyVotingInfo (assetId,index) = makeString(["%s%s%d", "votingInfo", assetId, toString(index)], separator) | |
72 | + | func keyPriceForPeriod (period) = makeString(["%s%d", "price", toString(period)], SEP) | |
99 | 73 | ||
100 | 74 | ||
101 | - | func | |
75 | + | func keyStartHeight (period) = makeString(["%s%d", "startHeight", toString(period)], SEP) | |
102 | 76 | ||
103 | 77 | ||
104 | - | func | |
78 | + | func keyPowerManagerBonus (period) = makeString(["%s%d", "powerManagerBonus", toString(period)], SEP) | |
105 | 79 | ||
106 | 80 | ||
107 | - | func getUserGwxAmountAtHeight (userAddress,targetHeight) = { | |
108 | - | let gwxAmount = invoke(boostingContract, "getUserGwxAmountAtHeightREADONLY", [userAddress, targetHeight], nil) | |
109 | - | asInt(gwxAmount) | |
81 | + | func keyPeriodLength () = "%s__periodLength" | |
82 | + | ||
83 | + | ||
84 | + | func keyBlockProcessingReward () = "%s__blockProcessingReward" | |
85 | + | ||
86 | + | ||
87 | + | func keyNextBlockToProcess () = "%s__nextBlockToProcess" | |
88 | + | ||
89 | + | ||
90 | + | func keyBlockProcessed (height) = makeString(["%s%d", "blockProcessed", toString(height)], SEP) | |
91 | + | ||
92 | + | ||
93 | + | func keyWithdrawal () = "%s__withdrawal" | |
94 | + | ||
95 | + | ||
96 | + | func keyWithdrawalRequest (userAddress,txId) = makeString(["%s%s%s", "withdrawal", toString(userAddress), toBase58String(txId)], SEP) | |
97 | + | ||
98 | + | ||
99 | + | func valueWithdrawalRequest (status,lpAssetAmount,targetPeriod,claimTxId) = { | |
100 | + | let claimTxIdStr = match claimTxId { | |
101 | + | case b: ByteVector => | |
102 | + | toBase58String(b) | |
103 | + | case _: Unit => | |
104 | + | "SOON" | |
105 | + | case _ => | |
106 | + | throw("Match error") | |
107 | + | } | |
108 | + | makeString(["%s%d%d%s", status, toString(lpAssetAmount), toString(targetPeriod), claimTxIdStr], SEP) | |
110 | 109 | } | |
111 | 110 | ||
112 | 111 | ||
113 | - | func | |
112 | + | func keyPeriodWithdrawalAssetIds (period) = makeString(["%s%d", "periodReward", toString(period)], SEP) | |
114 | 113 | ||
115 | 114 | ||
116 | - | func | |
115 | + | func keyPeriodWithdrawalAmounts (period) = makeString(["%s%d", "periodRewardAmount", toString(period)], SEP) | |
117 | 116 | ||
118 | 117 | ||
119 | - | func getManagerVaultAddressOrThis () = match getString(keyManagerVaultAddress()) { | |
118 | + | let keyMinHeightForWithdraw = makeString(["%s", "minHeightForWithdraw"], SEP) | |
119 | + | ||
120 | + | let keyMaxHeightForWithdraw = makeString(["%s", "maxHeightForWithdraw"], SEP) | |
121 | + | ||
122 | + | func parseWithdrawalRequestValueOrFail (s) = { | |
123 | + | let parts = split(s, SEP) | |
124 | + | if ((size(parts) == 5)) | |
125 | + | then { | |
126 | + | let status = parts[1] | |
127 | + | let lpAssetAmount = valueOrErrorMessage(parseInt(parts[2]), wrapErr("invalid lpAssetAmount")) | |
128 | + | let targetPeriod = valueOrErrorMessage(parseInt(parts[3]), wrapErr("invalid targetPeriod")) | |
129 | + | let claimTxId = parts[4] | |
130 | + | $Tuple4(status, lpAssetAmount, targetPeriod, claimTxId) | |
131 | + | } | |
132 | + | else throwErr("invalid withdrawal request value") | |
133 | + | } | |
134 | + | ||
135 | + | ||
136 | + | let factoryAddressOption = match getString(this, keyFactoryAddress()) { | |
120 | 137 | case s: String => | |
121 | - | addressFromStringValue(s) | |
138 | + | addressFromString(s) | |
139 | + | case _: Unit => | |
140 | + | unit | |
122 | 141 | case _ => | |
123 | - | this | |
142 | + | throw("Match error") | |
143 | + | } | |
144 | + | ||
145 | + | let factoryAddressOrFail = valueOrErrorMessage(factoryAddressOption, wrapErr("invalid factory address")) | |
146 | + | ||
147 | + | let lpAssetIdOption = match factoryAddressOption { | |
148 | + | case a: Address => | |
149 | + | match getString(a, keyLpAssetId()) { | |
150 | + | case s: String => | |
151 | + | fromBase58String(s) | |
152 | + | case _: Unit => | |
153 | + | unit | |
154 | + | case _ => | |
155 | + | throw("Match error") | |
156 | + | } | |
157 | + | case _: Unit => | |
158 | + | unit | |
159 | + | case _ => | |
160 | + | throw("Match error") | |
161 | + | } | |
162 | + | ||
163 | + | let lpAssetIdOrFail = valueOrErrorMessage(lpAssetIdOption, wrapErr("invalid lpAssetId")) | |
164 | + | ||
165 | + | let proxyTreasuryAddressOption = match factoryAddressOption { | |
166 | + | case a: Address => | |
167 | + | match getString(a, keyProxyTreasuryAddress()) { | |
168 | + | case s: String => | |
169 | + | addressFromString(s) | |
170 | + | case _: Unit => | |
171 | + | unit | |
172 | + | case _ => | |
173 | + | throw("Match error") | |
174 | + | } | |
175 | + | case _: Unit => | |
176 | + | unit | |
177 | + | case _ => | |
178 | + | throw("Match error") | |
179 | + | } | |
180 | + | ||
181 | + | let proxyTreasuryAddressOrFail = valueOrErrorMessage(proxyTreasuryAddressOption, wrapErr("invalid proxy treasury address")) | |
182 | + | ||
183 | + | let mainTreasuryAddressOption = match factoryAddressOption { | |
184 | + | case a: Address => | |
185 | + | match getString(a, keyMainTreasuryAddress()) { | |
186 | + | case s: String => | |
187 | + | addressFromString(s) | |
188 | + | case _: Unit => | |
189 | + | unit | |
190 | + | case _ => | |
191 | + | throw("Match error") | |
192 | + | } | |
193 | + | case _: Unit => | |
194 | + | unit | |
195 | + | case _ => | |
196 | + | throw("Match error") | |
197 | + | } | |
198 | + | ||
199 | + | let mainTreasuryAddressOrFail = valueOrErrorMessage(mainTreasuryAddressOption, wrapErr("invalid main treasury address")) | |
200 | + | ||
201 | + | func getManagerPublicKeyOrUnit () = match factoryAddressOption { | |
202 | + | case fa: Address => | |
203 | + | match getString(fa, keyManagerPublicKey()) { | |
204 | + | case pub: String => | |
205 | + | fromBase58String(pub) | |
206 | + | case _ => | |
207 | + | unit | |
208 | + | } | |
209 | + | case _ => | |
210 | + | unit | |
124 | 211 | } | |
125 | 212 | ||
126 | 213 | ||
127 | - | func managerPublicKeyOrUnit () = { | |
128 | - | let managerVaultAddress = getManagerVaultAddressOrThis() | |
129 | - | match getString(managerVaultAddress, keyManagerPublicKey()) { | |
130 | - | case s: String => | |
131 | - | fromBase58String(s) | |
132 | - | case _: Unit => | |
133 | - | unit | |
134 | - | case _ => | |
135 | - | throw("Match error") | |
136 | - | } | |
214 | + | func onlyAddress (i,address) = if ((i.caller == address)) | |
215 | + | then true | |
216 | + | else throwErr("permission denied") | |
217 | + | ||
218 | + | ||
219 | + | func onlyFactory (i) = onlyAddress(i, factoryAddressOrFail) | |
220 | + | ||
221 | + | ||
222 | + | func rewardForOption (rewards,target) = { | |
223 | + | let s = size(rewards) | |
224 | + | let $t052085233 = rewards[0] | |
225 | + | let a0 = $t052085233._1 | |
226 | + | let r0 = $t052085233._2 | |
227 | + | let $t052365261 = rewards[1] | |
228 | + | let a1 = $t052365261._1 | |
229 | + | let r1 = $t052365261._2 | |
230 | + | let $t052645289 = rewards[2] | |
231 | + | let a2 = $t052645289._1 | |
232 | + | let r2 = $t052645289._2 | |
233 | + | if (if ((s > 0)) | |
234 | + | then (a0 == target) | |
235 | + | else false) | |
236 | + | then r0 | |
237 | + | else if (if ((s > 1)) | |
238 | + | then (a1 == target) | |
239 | + | else false) | |
240 | + | then r1 | |
241 | + | else if (if ((s > 2)) | |
242 | + | then (a2 == target) | |
243 | + | else false) | |
244 | + | then r2 | |
245 | + | else unit | |
137 | 246 | } | |
138 | 247 | ||
139 | 248 | ||
140 | - | func getVotingInfoParts (votingInfo) = { | |
141 | - | let votingInfoParts = split(votingInfo, separator) | |
142 | - | let isRewardExistStr = votingInfoParts[1] | |
143 | - | let isRewardExist = if ((isRewardExistStr == "true")) | |
249 | + | func finalizeINTERNAL (newTreasuryVolumeInWaves,pwrManagersBonusInWaves,treasuryVolumeDiffAllocationCoef) = { | |
250 | + | let donatedWavesAmount = valueOrElse(getInteger(factoryAddressOrFail, keyDonated(unit)), 0) | |
251 | + | let investedWavesAmount = valueOrElse(getInteger(factoryAddressOrFail, keyInvested(unit)), 0) | |
252 | + | let currentTreasuryVolumeInWaves = (donatedWavesAmount + investedWavesAmount) | |
253 | + | let profitRaw = (newTreasuryVolumeInWaves - currentTreasuryVolumeInWaves) | |
254 | + | let pwrManagersBonusAmount = if (if ((profitRaw >= pwrManagersBonusInWaves)) | |
144 | 255 | then true | |
145 | - | else false | |
146 | - | let rewardAssetId = votingInfoParts[2] | |
147 | - | let rewardAmount = parseIntValue(votingInfoParts[3]) | |
148 | - | let votingType = votingInfoParts[4] | |
149 | - | let status = votingInfoParts[5] | |
150 | - | let votingStartHeight = parseIntValue(votingInfoParts[6]) | |
151 | - | let votingEndHeight = parseIntValue(votingInfoParts[7]) | |
152 | - | let votesQuorum = parseIntValue(votingInfoParts[8]) | |
153 | - | let votesFor = parseIntValue(votingInfoParts[9]) | |
154 | - | let votesAgainst = parseIntValue(votingInfoParts[10]) | |
155 | - | $Tuple10(isRewardExist, rewardAssetId, rewardAmount, votingType, status, votingStartHeight, votingEndHeight, votesQuorum, votesFor, votesAgainst) | |
156 | - | } | |
157 | - | ||
158 | - | ||
159 | - | func votingExistChecks (assetId,currentIndex) = { | |
160 | - | let votingInfo = valueOrErrorMessage(getString(keyVotingInfo(assetId, currentIndex)), wrapErr("voting info not found")) | |
161 | - | let votingInfoArray = split(votingInfo, separator) | |
162 | - | let status = votingInfoArray[5] | |
163 | - | let votingEndHeight = valueOrErrorMessage(parseIntValue(votingInfoArray[7]), wrapErr("voting start height not found")) | |
164 | - | let suggestIssuer = valueOrErrorMessage(getString(keySuggestIssuer(assetId, currentIndex)), wrapErr("voting issuer not found")) | |
165 | - | let checks = [if ((status == "inProgress")) | |
256 | + | else (pwrManagersBonusInWaves == 0)) | |
257 | + | then pwrManagersBonusInWaves | |
258 | + | else throwErr("power bonus is more than profit") | |
259 | + | let profit = (profitRaw - pwrManagersBonusAmount) | |
260 | + | let donationPart = if ((currentTreasuryVolumeInWaves > 0)) | |
261 | + | then fraction(donatedWavesAmount, SCALE8, currentTreasuryVolumeInWaves) | |
262 | + | else 0 | |
263 | + | let donationProfitPartRaw = fraction(profit, donationPart, SCALE8) | |
264 | + | let investmentProfitPartRaw = (profit - donationProfitPartRaw) | |
265 | + | let treasuryVolumeDiffAllocationCoefAbs = abs(treasuryVolumeDiffAllocationCoef) | |
266 | + | let amountToDonation = fraction(investmentProfitPartRaw, if ((0 > treasuryVolumeDiffAllocationCoef)) | |
267 | + | then treasuryVolumeDiffAllocationCoefAbs | |
268 | + | else 0, SCALE8) | |
269 | + | let amountToInvestment = fraction(donationProfitPartRaw, if ((treasuryVolumeDiffAllocationCoef > 0)) | |
270 | + | then treasuryVolumeDiffAllocationCoefAbs | |
271 | + | else 0, SCALE8) | |
272 | + | let donationProfitPart = ((donationProfitPartRaw - amountToInvestment) + amountToDonation) | |
273 | + | let investmentProfitPart = ((investmentProfitPartRaw - amountToDonation) + amountToInvestment) | |
274 | + | let donatedWavesAmountNewRaw = (donatedWavesAmount + donationProfitPart) | |
275 | + | let investedWavesAmountNewRaw = (investedWavesAmount + investmentProfitPart) | |
276 | + | let donatedPartDebt = min([0, donatedWavesAmountNewRaw]) | |
277 | + | let investedPartDebt = min([0, investedWavesAmountNewRaw]) | |
278 | + | let donatedWavesAmountNew = (max([0, donatedWavesAmountNewRaw]) + investedPartDebt) | |
279 | + | let investedWavesAmountNew = (max([0, investedWavesAmountNewRaw]) + donatedPartDebt) | |
280 | + | let lpAssetQuantity = valueOrErrorMessage(assetInfo(lpAssetIdOrFail), wrapErr("invalid lpAsset info")).quantity | |
281 | + | let newPrice = fraction(investedWavesAmountNew, SCALE8, lpAssetQuantity) | |
282 | + | let checkIfPriceNotZero = if ((newPrice != 0)) | |
166 | 283 | then true | |
167 | - | else throwErr("no voting in progress"), if ((votingEndHeight > height)) | |
168 | - | then true | |
169 | - | else throwErr("voting expired")] | |
170 | - | if ((checks == checks)) | |
171 | - | then $Tuple3(status, votingEndHeight, suggestIssuer) | |
284 | + | else throwErr("LP price cannot be 0") | |
285 | + | if ((checkIfPriceNotZero == checkIfPriceNotZero)) | |
286 | + | then { | |
287 | + | let lpAssetAmountToBurn = valueOrElse(getInteger(factoryAddressOrFail, keyWithdrawal()), 0) | |
288 | + | let paymentAmountMin = max([0, fraction(lpAssetAmountToBurn, newPrice, SCALE8)]) | |
289 | + | let finalInvestedWavesAmount = (investedWavesAmountNew - paymentAmountMin) | |
290 | + | let lpAssetFinalQuantity = (lpAssetQuantity - lpAssetAmountToBurn) | |
291 | + | $Tuple6(paymentAmountMin, finalInvestedWavesAmount, donatedWavesAmountNew, newPrice, lpAssetAmountToBurn, lpAssetFinalQuantity) | |
292 | + | } | |
172 | 293 | else throw("Strict value is not equal to itself.") | |
173 | 294 | } | |
174 | 295 | ||
175 | 296 | ||
176 | - | func calculateReward (voter,assetId,index) = { | |
177 | - | let voteKey = keyVote(assetId, index, voter) | |
178 | - | let lastVote = valueOrErrorMessage(getString(voteKey), wrapErr("you have not voted")) | |
179 | - | let lastVoteParts = split(lastVote, separator) | |
180 | - | let gwxAmount = parseIntValue(lastVoteParts[2]) | |
181 | - | let votingInfoStr = valueOrErrorMessage(getString(keyVotingInfo(assetId, index)), wrapErr("voting info not found")) | |
182 | - | let votingParts = getVotingInfoParts(votingInfoStr) | |
183 | - | let votesFor = votingParts._9 | |
184 | - | let votesAgainst = votingParts._10 | |
185 | - | let partOfTheTotalVotesX8 = fraction(gwxAmount, MULT8, (votesFor + votesAgainst)) | |
186 | - | let totalVotingReward = valueOrElse(getInteger(keyTotalVotingReward(assetId, index)), 0) | |
187 | - | let voterRewardAmount = fraction(partOfTheTotalVotesX8, totalVotingReward, MULT8, FLOOR) | |
188 | - | voterRewardAmount | |
189 | - | } | |
190 | - | ||
191 | - | ||
192 | 297 | @Callable(i) | |
193 | - | func suggestAdd (assetId,periodLength,assetImage) = { | |
194 | - | let wxPayment = i.payments[0] | |
195 | - | let wxPaymentAssetId = value(wxPayment.assetId) | |
196 | - | let wxPaymentAmount = value(wxPayment.amount) | |
197 | - | let minPeriodLength = getIntegerValue(keyMinPeriodLength) | |
198 | - | let maxPeriodLength = getIntegerValue(keyMaxPeriodLength) | |
199 | - | let tokenIsVerified = { | |
200 | - | let @ = invoke(assetsStoreContract, "isVerifiedREADONLY", [assetId], nil) | |
201 | - | if ($isInstanceOf(@, "Boolean")) | |
202 | - | then @ | |
203 | - | else throw(($getType(@) + " couldn't be cast to Boolean")) | |
204 | - | } | |
205 | - | let checks = [if (if ((periodLength >= minPeriodLength)) | |
206 | - | then (maxPeriodLength >= periodLength) | |
207 | - | else false) | |
208 | - | then true | |
209 | - | else throwErr("invalid periodLength"), if ((tokenIsVerified == false)) | |
210 | - | then true | |
211 | - | else throwErr("token already verified"), if ((wxPaymentAmount > (periodLength * feePerBlock))) | |
212 | - | then true | |
213 | - | else throwErr("not enough wx for given period"), if ((wxPaymentAmount >= getIntegerValue(keyMinWxMinForSuggestAddAmountRequired))) | |
214 | - | then true | |
215 | - | else throwErr("payment less then min for suggest")] | |
216 | - | if ((checks == checks)) | |
298 | + | func claimLP (userAddressBytes) = { | |
299 | + | let checkCaller = onlyFactory(i) | |
300 | + | if ((checkCaller == checkCaller)) | |
217 | 301 | then { | |
218 | - | let currentIndexKey = keyCurrentIndex(assetId) | |
219 | - | let currentIndex = getInteger(currentIndexKey) | |
220 | - | let newIndex = if (isDefined(currentIndex)) | |
221 | - | then (value(currentIndex) + 1) | |
222 | - | else 0 | |
223 | - | let $t087849424 = if ((size(i.payments) > 1)) | |
224 | - | then { | |
225 | - | let votingRewardPayment = i.payments[1] | |
226 | - | let votingRewardPaymentAssetId = toBase58String(value(votingRewardPayment.assetId)) | |
227 | - | let votingRewardPaymentAmount = value(votingRewardPayment.amount) | |
228 | - | $Tuple4(true, votingRewardPaymentAssetId, votingRewardPaymentAmount, [StringEntry(keyVotingRewardAssetId(assetId, newIndex), votingRewardPaymentAssetId), IntegerEntry(keyTotalVotingReward(assetId, newIndex), votingRewardPaymentAmount)]) | |
229 | - | } | |
230 | - | else $Tuple4(false, "EMPTY", 0, nil) | |
231 | - | let isRewardExist = $t087849424._1 | |
232 | - | let rewardAssetId = $t087849424._2 | |
233 | - | let rewardAmount = $t087849424._3 | |
234 | - | let votingRewardActions = $t087849424._4 | |
235 | - | let votesQuorum = valueOrErrorMessage(getInteger(keyVotingThresholdAdd), wrapErr("votingThresholdAdd not set")) | |
236 | - | let votingInfo = votingInfoValue(isRewardExist, rewardAssetId, rewardAmount, "verification", "inProgress", height, (height + periodLength), votesQuorum, 0, 0) | |
237 | - | let finalizeCallRewardAmount = getIntegerValue(keyFinalizeCallRewardAmount) | |
238 | - | let burnWxAmount = (wxPaymentAmount - finalizeCallRewardAmount) | |
239 | - | ([IntegerEntry(currentIndexKey, newIndex), StringEntry(keySuggestIssuer(assetId, newIndex), toString(i.caller)), StringEntry(keyVotingInfo(assetId, newIndex), votingInfo), StringEntry(keyAssetImage(assetId), assetImage), Burn(wxPaymentAssetId, burnWxAmount)] ++ votingRewardActions) | |
302 | + | let userAddress = Address(userAddressBytes) | |
303 | + | let available = valueOrElse(getInteger(factoryAddressOrFail, keyAvailable(userAddress)), 0) | |
304 | + | let claimed = valueOrElse(getInteger(factoryAddressOrFail, keyClaimed(userAddress)), 0) | |
305 | + | let factoryActions = if ((available > 0)) | |
306 | + | then [invoke(factoryAddressOrFail, "transferAsset", [userAddressBytes, available, lpAssetIdOrFail], nil), invoke(factoryAddressOrFail, "integerEntry", [keyAvailable(userAddress), 0], nil), invoke(factoryAddressOrFail, "integerEntry", [keyClaimed(userAddress), (claimed + available)], nil)] | |
307 | + | else throwErr("nothing to claim") | |
308 | + | $Tuple2(nil, factoryActions) | |
240 | 309 | } | |
241 | 310 | else throw("Strict value is not equal to itself.") | |
242 | 311 | } | |
243 | 312 | ||
244 | 313 | ||
245 | 314 | ||
246 | 315 | @Callable(i) | |
247 | - | func suggestRemove (assetId) = { | |
248 | - | let gwxAmountAtNow = getUserGwxAmountAtHeight(toString(i.caller), height) | |
249 | - | let minSuggestRemoveBalance = getIntegerValue(keyMinSuggestRemoveBalance) | |
250 | - | let wxPayment = i.payments[0] | |
251 | - | let wxPaymentAssetId = value(wxPayment.assetId) | |
252 | - | let wxPaymentAmount = value(wxPayment.amount) | |
253 | - | let tokenIsVerified = { | |
254 | - | let @ = invoke(assetsStoreContract, "isVerifiedREADONLY", [assetId], nil) | |
255 | - | if ($isInstanceOf(@, "Boolean")) | |
256 | - | then @ | |
257 | - | else throw(($getType(@) + " couldn't be cast to Boolean")) | |
258 | - | } | |
259 | - | let checks = [if (tokenIsVerified) | |
260 | - | then true | |
261 | - | else throwErr("token not verified"), if ((gwxAmountAtNow >= minSuggestRemoveBalance)) | |
262 | - | then true | |
263 | - | else throwErr("not enough gWXes"), if ((wxPaymentAmount >= getIntegerValue(keyWxForSuggestRemoveAmountRequired))) | |
264 | - | then true | |
265 | - | else throwErr("payment less then min for suggest")] | |
266 | - | if ((checks == checks)) | |
316 | + | func finalize (userAddressBytes,newTreasuryVolumeInWaves,pwrManagersBonusInWaves,treasuryVolumeDiffAllocationCoef) = { | |
317 | + | let checkCaller = onlyFactory(i) | |
318 | + | if ((checkCaller == checkCaller)) | |
267 | 319 | then { | |
268 | - | let currentIndexKey = keyCurrentIndex(assetId) | |
269 | - | let currentIndex = getInteger(currentIndexKey) | |
270 | - | let newIndex = if (isDefined(currentIndex)) | |
271 | - | then (value(currentIndex) + 1) | |
272 | - | else 0 | |
273 | - | let periodLength = valueOrErrorMessage(getInteger(keyPeriodLengthRemove), wrapErr("periodLengthRemove not set")) | |
274 | - | let votingEndHeight = (height + periodLength) | |
275 | - | let votesQuorum = valueOrErrorMessage(getInteger(keyVotingThresholdRemove), wrapErr("votingThresholdRemove not set")) | |
276 | - | let votingInfo = votingInfoValue(false, "EMPTY", 0, "deverification", "inProgress", height, (height + periodLength), votesQuorum, 0, 0) | |
277 | - | [IntegerEntry(currentIndexKey, newIndex), StringEntry(keySuggestIssuer(assetId, newIndex), toString(i.caller)), StringEntry(keyVotingInfo(assetId, newIndex), votingInfo)] | |
278 | - | } | |
279 | - | else throw("Strict value is not equal to itself.") | |
280 | - | } | |
281 | - | ||
282 | - | ||
283 | - | ||
284 | - | @Callable(i) | |
285 | - | func vote (assetId,inFavor) = { | |
286 | - | let currentIndexKey = keyCurrentIndex(assetId) | |
287 | - | let currentIndex = valueOrErrorMessage(getInteger(currentIndexKey), wrapErr("voting does not exist")) | |
288 | - | let votingInfo = votingExistChecks(assetId, currentIndex) | |
289 | - | if ((votingInfo == votingInfo)) | |
290 | - | then { | |
291 | - | let currentVotingEndHeight = votingInfo._2 | |
292 | - | let gwxAmountAtEnd = getUserGwxAmountAtHeight(toString(i.caller), currentVotingEndHeight) | |
293 | - | let voteKey = keyVote(assetId, currentIndex, i.caller) | |
294 | - | let checks = [if ((getString(voteKey) == unit)) | |
320 | + | let currentPeriodOrFail = valueOrErrorMessage(getInteger(factoryAddressOrFail, keyCurrentPeriod()), wrapErr("invalid period")) | |
321 | + | let periodLength = valueOrErrorMessage(getInteger(factoryAddressOrFail, keyPeriodLength()), wrapErr("invalid period length")) | |
322 | + | let currentStartHeight = valueOrErrorMessage(getInteger(factoryAddressOrFail, keyStartHeight(currentPeriodOrFail)), wrapErr("invalid start height")) | |
323 | + | let currentPriceOrFail = valueOrErrorMessage(getInteger(factoryAddressOrFail, keyPriceForPeriod(currentPeriodOrFail)), wrapErr("invalid price")) | |
324 | + | let nextBlockToProcess = valueOrErrorMessage(getInteger(factoryAddressOrFail, keyNextBlockToProcess()), wrapErr("invalid next block to process")) | |
325 | + | let periodEndHeight = ((currentStartHeight + periodLength) - 1) | |
326 | + | let checks = [if ((i.originCaller == mainTreasuryAddressOrFail)) | |
295 | 327 | then true | |
296 | - | else throwErr(" | |
328 | + | else throwErr("permission denied"), if ((nextBlockToProcess > periodEndHeight)) | |
297 | 329 | then true | |
298 | - | else throw("You'll not have gWX at the end of voting")] | |
330 | + | else throwErr("unprocessed blocks"), if ((newTreasuryVolumeInWaves >= 0)) | |
331 | + | then true | |
332 | + | else throwErr("invalid new treasury volume"), if ((pwrManagersBonusInWaves >= 0)) | |
333 | + | then true | |
334 | + | else throwErr("invalid PWR managers bonus"), if (if ((treasuryVolumeDiffAllocationCoef >= -(SCALE8))) | |
335 | + | then (SCALE8 >= treasuryVolumeDiffAllocationCoef) | |
336 | + | else false) | |
337 | + | then true | |
338 | + | else throwErr("invalid treasury volume diff allocation coefficient")] | |
299 | 339 | if ((checks == checks)) | |
300 | 340 | then { | |
301 | - | let votingInfoStr = valueOrErrorMessage(getString(keyVotingInfo(assetId, currentIndex)), wrapErr("voting info not found")) | |
302 | - | let votingInfoParts = getVotingInfoParts(votingInfoStr) | |
303 | - | let votesFor = votingInfoParts._9 | |
304 | - | let votesAgainst = votingInfoParts._10 | |
305 | - | let $t01274512906 = if (inFavor) | |
306 | - | then $Tuple2((votesFor + gwxAmountAtEnd), votesAgainst) | |
307 | - | else $Tuple2(votesFor, (votesAgainst + gwxAmountAtEnd)) | |
308 | - | let newVotesFor = $t01274512906._1 | |
309 | - | let newVotesAgainst = $t01274512906._2 | |
310 | - | let newVotingInfoValue = votingInfoValue(votingInfoParts._1, votingInfoParts._2, votingInfoParts._3, votingInfoParts._4, votingInfoParts._5, votingInfoParts._6, votingInfoParts._7, votingInfoParts._8, newVotesFor, newVotesAgainst) | |
311 | - | let votingRewardAction = match getString(keyVotingRewardAssetId(assetId, currentIndex)) { | |
312 | - | case pk: String => | |
313 | - | [StringEntry(keyVotingReward(i.caller, assetId, currentIndex), voteValue(inFavor, gwxAmountAtEnd))] | |
314 | - | case _: Unit => | |
315 | - | nil | |
316 | - | case _ => | |
317 | - | throw("Match error") | |
318 | - | } | |
319 | - | ([StringEntry(voteKey, voteValue(inFavor, gwxAmountAtEnd)), StringEntry(keyVotingInfo(assetId, currentIndex), newVotingInfoValue)] ++ votingRewardAction) | |
341 | + | let $t01078311043 = finalizeINTERNAL(newTreasuryVolumeInWaves, pwrManagersBonusInWaves, treasuryVolumeDiffAllocationCoef) | |
342 | + | let paymentAmountMin = $t01078311043._1 | |
343 | + | let finalInvestedWavesAmount = $t01078311043._2 | |
344 | + | let donatedWavesAmountNew = $t01078311043._3 | |
345 | + | let newPrice = $t01078311043._4 | |
346 | + | let lpAssetAmountToBurn = $t01078311043._5 | |
347 | + | let lpAssetFinalQuantity = $t01078311043._6 | |
348 | + | let newPeriod = (currentPeriodOrFail + 1) | |
349 | + | func addNewAction (actions,payment) = { | |
350 | + | let $t01119111253 = actions | |
351 | + | let scriptTransfers = $t01119111253._1 | |
352 | + | let assetIdsString = $t01119111253._2 | |
353 | + | let amountsString = $t01119111253._3 | |
354 | + | let paymentAmount = payment.amount | |
355 | + | let paymentAssetId = payment.assetId | |
356 | + | let newAssetIdsString = ("%s" + makeString([assetIdsString, assetIdToString(paymentAssetId)], SEP)) | |
357 | + | let newAmountsString = ("%d" + makeString([amountsString, toString(paymentAmount)], SEP)) | |
358 | + | let newScriptTransfer = ScriptTransfer(factoryAddressOrFail, paymentAmount, paymentAssetId) | |
359 | + | $Tuple3((scriptTransfers :+ newScriptTransfer), newAssetIdsString, newAmountsString) | |
360 | + | } | |
361 | + | ||
362 | + | let $t01171311804 = { | |
363 | + | let $l = i.payments | |
364 | + | let $s = size($l) | |
365 | + | let $acc0 = $Tuple3(nil, "", "") | |
366 | + | func $f0_1 ($a,$i) = if (($i >= $s)) | |
367 | + | then $a | |
368 | + | else addNewAction($a, $l[$i]) | |
369 | + | ||
370 | + | func $f0_2 ($a,$i) = if (($i >= $s)) | |
371 | + | then $a | |
372 | + | else throw("List size exceeds 10") | |
373 | + | ||
374 | + | $f0_2($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($acc0, 0), 1), 2), 3), 4), 5), 6), 7), 8), 9), 10) | |
375 | + | } | |
376 | + | let scriptTransfers = $t01171311804._1 | |
377 | + | let AssetIds = $t01171311804._2 | |
378 | + | let Amounts = $t01171311804._3 | |
379 | + | let factoryActions = [invoke(factoryAddressOrFail, "integerEntry", [keyPowerManagerBonus(currentPeriodOrFail), pwrManagersBonusInWaves], nil), invoke(factoryAddressOrFail, "integerEntry", [keyCurrentPeriod(), newPeriod], nil), invoke(factoryAddressOrFail, "integerEntry", [keyPriceForPeriod(newPeriod), newPrice], nil), invoke(factoryAddressOrFail, "integerEntry", [keyStartHeight(newPeriod), (periodEndHeight + 1)], nil), invoke(factoryAddressOrFail, "burn", [lpAssetAmountToBurn], nil), invoke(factoryAddressOrFail, "integerEntry", [keyWithdrawal(), 0], nil), invoke(factoryAddressOrFail, "integerEntry", [keyInvested(unit), finalInvestedWavesAmount], nil), invoke(factoryAddressOrFail, "integerEntry", [keyDonated(unit), donatedWavesAmountNew], nil), invoke(factoryAddressOrFail, "stringEntry", [keyPeriodWithdrawalAssetIds(newPeriod), AssetIds], nil), invoke(factoryAddressOrFail, "stringEntry", [keyPeriodWithdrawalAmounts(newPeriod), Amounts], nil)] | |
380 | + | $Tuple2(scriptTransfers, factoryActions) | |
320 | 381 | } | |
321 | 382 | else throw("Strict value is not equal to itself.") | |
322 | 383 | } | |
323 | 384 | else throw("Strict value is not equal to itself.") | |
324 | 385 | } | |
325 | 386 | ||
326 | 387 | ||
327 | 388 | ||
328 | 389 | @Callable(i) | |
329 | - | func cancelVote (assetId) = { | |
330 | - | let currentIndexKey = keyCurrentIndex(assetId) | |
331 | - | let currentIndex = valueOrErrorMessage(getInteger(currentIndexKey), wrapErr("voting does not exist")) | |
332 | - | let voteKey = keyVote(assetId, currentIndex, i.caller) | |
333 | - | let lastVote = valueOrErrorMessage(getString(voteKey), wrapErr("you have not voted")) | |
334 | - | let lastVoteParts = split(lastVote, separator) | |
335 | - | let inFavor = lastVoteParts[1] | |
336 | - | let gwxAmount = parseIntValue(lastVoteParts[2]) | |
337 | - | let votingInfo = votingExistChecks(assetId, currentIndex) | |
338 | - | if ((votingInfo == votingInfo)) | |
390 | + | func finalizeREADONLY (newTreasuryVolumeInWaves,pwrManagersBonusInWaves,treasuryVolumeDiffAllocationCoef) = { | |
391 | + | let currentPeriodOrFail = valueOrErrorMessage(getInteger(factoryAddressOrFail, keyCurrentPeriod()), wrapErr("invalid period")) | |
392 | + | let periodLength = valueOrErrorMessage(getInteger(factoryAddressOrFail, keyPeriodLength()), wrapErr("invalid period length")) | |
393 | + | let currentStartHeight = valueOrErrorMessage(getInteger(factoryAddressOrFail, keyStartHeight(currentPeriodOrFail)), wrapErr("invalid start height")) | |
394 | + | let currentPriceOrFail = valueOrErrorMessage(getInteger(factoryAddressOrFail, keyPriceForPeriod(currentPeriodOrFail)), wrapErr("invalid price")) | |
395 | + | let nextBlockToProcess = valueOrErrorMessage(getInteger(factoryAddressOrFail, keyNextBlockToProcess()), wrapErr("invalid next block to process")) | |
396 | + | let periodEndHeight = ((currentStartHeight + periodLength) - 1) | |
397 | + | let checks = [if ((nextBlockToProcess > periodEndHeight)) | |
398 | + | then true | |
399 | + | else throwErr("unprocessed blocks"), if ((newTreasuryVolumeInWaves >= 0)) | |
400 | + | then true | |
401 | + | else throwErr("invalid new treasury volume"), if ((pwrManagersBonusInWaves >= 0)) | |
402 | + | then true | |
403 | + | else throwErr("invalid PWR managers bonus"), if (if ((treasuryVolumeDiffAllocationCoef >= -(SCALE8))) | |
404 | + | then (SCALE8 >= treasuryVolumeDiffAllocationCoef) | |
405 | + | else false) | |
406 | + | then true | |
407 | + | else throwErr("invalid treasury volume diff allocation coefficient")] | |
408 | + | if ((checks == checks)) | |
409 | + | then $Tuple2(nil, finalizeINTERNAL(newTreasuryVolumeInWaves, pwrManagersBonusInWaves, treasuryVolumeDiffAllocationCoef)) | |
410 | + | else throw("Strict value is not equal to itself.") | |
411 | + | } | |
412 | + | ||
413 | + | ||
414 | + | ||
415 | + | @Callable(i) | |
416 | + | func invest (userAddressBytes) = { | |
417 | + | let checkCaller = onlyFactory(i) | |
418 | + | if ((checkCaller == checkCaller)) | |
339 | 419 | then { | |
340 | - | let checks = [if (if ((inFavor == "true")) | |
420 | + | let userAddress = Address(userAddressBytes) | |
421 | + | let currentPeriodOrFail = valueOrErrorMessage(getInteger(factoryAddressOrFail, keyCurrentPeriod()), wrapErr("invalid period")) | |
422 | + | let currentPriceOrFail = valueOrErrorMessage(getInteger(factoryAddressOrFail, keyPriceForPeriod(currentPeriodOrFail)), wrapErr("invalid price")) | |
423 | + | let payment = if ((size(i.payments) == 1)) | |
424 | + | then i.payments[0] | |
425 | + | else throwErr("invalid payments") | |
426 | + | let $t01528515462 = if (if ((payment.assetId == unit)) | |
427 | + | then (payment.amount > 0) | |
428 | + | else false) | |
429 | + | then $Tuple2(payment.amount, payment.assetId) | |
430 | + | else throwErr("invalid payment amount") | |
431 | + | let paymentAmount = $t01528515462._1 | |
432 | + | let paymentAssetId = $t01528515462._2 | |
433 | + | let lpAssetAmount = if ((currentPriceOrFail > 0)) | |
434 | + | then fraction(paymentAmount, SCALE8, currentPriceOrFail) | |
435 | + | else 0 | |
436 | + | let invested = valueOrElse(getInteger(factoryAddressOrFail, keyInvested(unit)), 0) | |
437 | + | let actions = [ScriptTransfer(mainTreasuryAddressOrFail, paymentAmount, paymentAssetId)] | |
438 | + | let factoryActions = [invoke(factoryAddressOrFail, "integerEntry", [keyInvested(unit), (invested + paymentAmount)], nil), invoke(factoryAddressOrFail, "reissue", [lpAssetAmount], nil), invoke(factoryAddressOrFail, "transferAsset", [userAddressBytes, lpAssetAmount, lpAssetIdOrFail], nil)] | |
439 | + | $Tuple2(actions, factoryActions) | |
440 | + | } | |
441 | + | else throw("Strict value is not equal to itself.") | |
442 | + | } | |
443 | + | ||
444 | + | ||
445 | + | ||
446 | + | @Callable(i) | |
447 | + | func withdraw (userAddressBytes) = { | |
448 | + | let checkCaller = onlyFactory(i) | |
449 | + | if ((checkCaller == checkCaller)) | |
450 | + | then { | |
451 | + | let userAddress = Address(userAddressBytes) | |
452 | + | let currentPeriodOrFail = valueOrErrorMessage(getInteger(factoryAddressOrFail, keyCurrentPeriod()), wrapErr("invalid period")) | |
453 | + | let payment = if ((size(i.payments) == 1)) | |
454 | + | then i.payments[0] | |
455 | + | else throwErr(wrapErr("invalid payments")) | |
456 | + | let paymentAssetId = if ((payment.assetId == lpAssetIdOrFail)) | |
457 | + | then lpAssetIdOrFail | |
458 | + | else throwErr("invalid payment asset") | |
459 | + | let paymentAmount = if ((payment.amount > 0)) | |
460 | + | then payment.amount | |
461 | + | else throwErr("invalid payment amount") | |
462 | + | let withdrawal = valueOrElse(getInteger(factoryAddressOrFail, keyWithdrawal()), 0) | |
463 | + | let actions = [ScriptTransfer(factoryAddressOrFail, paymentAmount, paymentAssetId)] | |
464 | + | let factoryActions = [invoke(factoryAddressOrFail, "integerEntry", [keyWithdrawal(), (withdrawal + paymentAmount)], nil), invoke(factoryAddressOrFail, "stringEntry", [keyWithdrawalRequest(userAddress, i.transactionId), valueWithdrawalRequest(PENDING, paymentAmount, (currentPeriodOrFail + 1), unit)], nil)] | |
465 | + | if ((factoryActions == factoryActions)) | |
466 | + | then $Tuple2(actions, factoryActions) | |
467 | + | else throw("Strict value is not equal to itself.") | |
468 | + | } | |
469 | + | else throw("Strict value is not equal to itself.") | |
470 | + | } | |
471 | + | ||
472 | + | ||
473 | + | ||
474 | + | @Callable(i) | |
475 | + | func cancelWithdraw (userAddressBytes,txId) = { | |
476 | + | let checkCaller = onlyFactory(i) | |
477 | + | if ((checkCaller == checkCaller)) | |
478 | + | then { | |
479 | + | let userAddress = Address(userAddressBytes) | |
480 | + | let withdrawalRequestOption = valueOrErrorMessage(getString(factoryAddressOrFail, keyWithdrawalRequest(userAddress, txId)), wrapErr("invalid withdrawal request")) | |
481 | + | let currentPeriodOrFail = valueOrErrorMessage(getInteger(factoryAddressOrFail, keyCurrentPeriod()), wrapErr("invalid period")) | |
482 | + | let $t01786917982 = parseWithdrawalRequestValueOrFail(withdrawalRequestOption) | |
483 | + | let status = $t01786917982._1 | |
484 | + | let lpAssetAmount = $t01786917982._2 | |
485 | + | let targetPeriod = $t01786917982._3 | |
486 | + | let claimTxId = $t01786917982._4 | |
487 | + | let checks = [if ((status == PENDING)) | |
341 | 488 | then true | |
342 | - | else ( | |
489 | + | else throwErr("invalid withdrawal request status"), if ((targetPeriod > currentPeriodOrFail)) | |
343 | 490 | then true | |
344 | - | else throwErr("invalid | |
491 | + | else throwErr("invalid withdrawal request period")] | |
345 | 492 | if ((checks == checks)) | |
346 | 493 | then { | |
347 | - | let votingInfoStr = valueOrErrorMessage(getString(keyVotingInfo(assetId, currentIndex)), wrapErr("voting info not found")) | |
348 | - | let votingInfoParts = getVotingInfoParts(votingInfoStr) | |
349 | - | let votesFor = votingInfoParts._9 | |
350 | - | let votesAgainst = votingInfoParts._10 | |
351 | - | let $t01453114692 = if ((inFavor == "true")) | |
352 | - | then $Tuple2((votesFor - gwxAmount), votesAgainst) | |
353 | - | else $Tuple2(votesFor, (votesAgainst - gwxAmount)) | |
354 | - | let newVotesFor = $t01453114692._1 | |
355 | - | let newVotesAgainst = $t01453114692._2 | |
356 | - | let newVotingInfoValue = votingInfoValue(votingInfoParts._1, votingInfoParts._2, votingInfoParts._3, votingInfoParts._4, votingInfoParts._5, votingInfoParts._6, votingInfoParts._7, votingInfoParts._8, newVotesFor, newVotesAgainst) | |
357 | - | [StringEntry(keyVotingInfo(assetId, currentIndex), newVotingInfoValue), DeleteEntry(voteKey), DeleteEntry(keyVotingReward(i.caller, assetId, currentIndex))] | |
494 | + | let withdrawal = valueOrErrorMessage(getInteger(factoryAddressOrFail, keyWithdrawal()), wrapErr("invalid total withdrawal amount")) | |
495 | + | let factoryActions = [invoke(factoryAddressOrFail, "integerEntry", [keyWithdrawal(), if ((withdrawal >= lpAssetAmount)) | |
496 | + | then (withdrawal - lpAssetAmount) | |
497 | + | else throwErr("invalid withdrawal amount")], nil), invoke(factoryAddressOrFail, "deleteEntry", [keyWithdrawalRequest(userAddress, txId)], nil), invoke(factoryAddressOrFail, "transferAsset", [userAddressBytes, lpAssetAmount, lpAssetIdOrFail], nil)] | |
498 | + | if ((factoryActions == factoryActions)) | |
499 | + | then $Tuple2(nil, factoryActions) | |
500 | + | else throw("Strict value is not equal to itself.") | |
358 | 501 | } | |
359 | 502 | else throw("Strict value is not equal to itself.") | |
360 | 503 | } | |
361 | 504 | else throw("Strict value is not equal to itself.") | |
362 | 505 | } | |
363 | 506 | ||
364 | 507 | ||
365 | 508 | ||
366 | 509 | @Callable(i) | |
367 | - | func finalize (assetId) = { | |
368 | - | let currentIndexKey = keyCurrentIndex(assetId) | |
369 | - | let currentIndex = valueOrElse(getInteger(currentIndexKey), 0) | |
370 | - | let votingThresholdAdd = valueOrErrorMessage(getInteger(keyVotingThresholdAdd), wrapErr("votingThresholdAdd not set")) | |
371 | - | let votingThresholdRemove = valueOrErrorMessage(getInteger(keyVotingThresholdRemove), wrapErr("votingThresholdRemove not set")) | |
372 | - | let votingInfoStr = valueOrErrorMessage(getString(keyVotingInfo(assetId, currentIndex)), wrapErr("voting info not found")) | |
373 | - | let votingInfoParts = getVotingInfoParts(votingInfoStr) | |
374 | - | let votingType = votingInfoParts._4 | |
375 | - | let status = votingInfoParts._5 | |
376 | - | let votingEndHeight = votingInfoParts._7 | |
377 | - | let votingQuorum = votingInfoParts._8 | |
378 | - | let votesFor = votingInfoParts._9 | |
379 | - | let votesAgainst = votingInfoParts._10 | |
380 | - | let checks = [if ((status == "inProgress")) | |
381 | - | then true | |
382 | - | else throwErr("voting not in progress"), if ((height >= votingEndHeight)) | |
383 | - | then true | |
384 | - | else throwErr("voting not finished"), if (isDefined(getString(keyAssetImage(assetId)))) | |
385 | - | then true | |
386 | - | else throwErr("asset image not set")] | |
387 | - | if ((checks == checks)) | |
510 | + | func claimCollateral (userAddressBytes,txId) = { | |
511 | + | let checkCaller = onlyFactory(i) | |
512 | + | if ((checkCaller == checkCaller)) | |
388 | 513 | then { | |
389 | - | let votingAccepted = if (if (((votesFor + votesAgainst) >= votingQuorum)) | |
390 | - | then (votesFor > votesAgainst) | |
514 | + | let userAddress = Address(userAddressBytes) | |
515 | + | let currentPeriodOrFail = valueOrErrorMessage(getInteger(factoryAddressOrFail, keyCurrentPeriod()), wrapErr("invalid period")) | |
516 | + | let withdrawalRequestOption = valueOrErrorMessage(getString(factoryAddressOrFail, keyWithdrawalRequest(userAddress, txId)), wrapErr("invalid withdrawal request")) | |
517 | + | let $t01931919432 = parseWithdrawalRequestValueOrFail(withdrawalRequestOption) | |
518 | + | let status = $t01931919432._1 | |
519 | + | let lpAssetAmount = $t01931919432._2 | |
520 | + | let targetPeriod = $t01931919432._3 | |
521 | + | let claimTxId = $t01931919432._4 | |
522 | + | if ((status == FINISHED)) | |
523 | + | then throwErr("invalid withdrawal request status") | |
524 | + | else if ((targetPeriod > currentPeriodOrFail)) | |
525 | + | then throwErr("invalid withdrawal request period") | |
526 | + | else { | |
527 | + | let priceOrFail = valueOrErrorMessage(getInteger(factoryAddressOrFail, keyPriceForPeriod(targetPeriod)), wrapErr("invalid price")) | |
528 | + | let amount = if ((priceOrFail > 0)) | |
529 | + | then fraction(lpAssetAmount, priceOrFail, SCALE8) | |
530 | + | else 0 | |
531 | + | let factoryActions = [invoke(factoryAddressOrFail, "stringEntry", [keyWithdrawalRequest(userAddress, txId), valueWithdrawalRequest(FINISHED, lpAssetAmount, targetPeriod, i.transactionId)], nil), { | |
532 | + | let assetsList = removeByIndex(split_51C(getStringValue(factoryAddressOrFail, keyPeriodWithdrawalAssetIds(currentPeriodOrFail)), SEP), 0) | |
533 | + | let amountsList = removeByIndex(split_51C(getStringValue(factoryAddressOrFail, keyPeriodWithdrawalAmounts(currentPeriodOrFail)), SEP), 0) | |
534 | + | invoke(factoryAddressOrFail, "transferAssets", [userAddressBytes, assetsList, amountsList], nil) | |
535 | + | }] | |
536 | + | $Tuple2(nil, factoryActions) | |
537 | + | } | |
538 | + | } | |
539 | + | else throw("Strict value is not equal to itself.") | |
540 | + | } | |
541 | + | ||
542 | + | ||
543 | + | ||
544 | + | @Callable(i) | |
545 | + | func processBlocks (userAddressBytes) = { | |
546 | + | let checkCaller = onlyFactory(i) | |
547 | + | if ((checkCaller == checkCaller)) | |
548 | + | then { | |
549 | + | let userAddress = Address(userAddressBytes) | |
550 | + | let currentPeriodOrFail = valueOrErrorMessage(getInteger(factoryAddressOrFail, keyCurrentPeriod()), wrapErr("invalid period")) | |
551 | + | let periodLength = valueOrErrorMessage(getInteger(factoryAddressOrFail, keyPeriodLength()), wrapErr("invalid period length")) | |
552 | + | let currentStartHeight = valueOrErrorMessage(getInteger(factoryAddressOrFail, keyStartHeight(currentPeriodOrFail)), wrapErr("invalid start height")) | |
553 | + | let currentPriceOrFail = valueOrErrorMessage(getInteger(factoryAddressOrFail, keyPriceForPeriod(currentPeriodOrFail)), wrapErr("invalid price")) | |
554 | + | let nextBlockToProcess = valueOrErrorMessage(getInteger(factoryAddressOrFail, keyNextBlockToProcess()), wrapErr("invalid next block to process")) | |
555 | + | let periodEndHeight = ((currentStartHeight + periodLength) - 1) | |
556 | + | let blocksProcessingBatchSizeMax = 40 | |
557 | + | let blocksProcessingBatchSize = if (if (if ((height >= nextBlockToProcess)) | |
558 | + | then if ((nextBlockToProcess >= currentStartHeight)) | |
559 | + | then true | |
560 | + | else (currentPeriodOrFail == 0) | |
391 | 561 | else false) | |
392 | - | then true | |
393 | - | else false | |
394 | - | let newStatus = if (votingAccepted) | |
395 | - | then "accepted" | |
396 | - | else "rejected" | |
397 | - | let assetImage = getStringValue(keyAssetImage(assetId)) | |
398 | - | let isVotingAccepted = if (votingAccepted) | |
562 | + | then (periodEndHeight >= nextBlockToProcess) | |
563 | + | else false) | |
564 | + | then min([((periodEndHeight - nextBlockToProcess) + 1), blocksProcessingBatchSizeMax]) | |
565 | + | else throwErr(wrapErr("invalid target block")) | |
566 | + | let blockProcessingReward = valueOrErrorMessage(getInteger(factoryAddressOrFail, keyBlockProcessingReward()), wrapErr("invalid block processing reward")) | |
567 | + | let blockProcessingRewardByGenerator = (blockProcessingReward / blocksProcessingBatchSize) | |
568 | + | let blockProcessingRewardByGeneratorRemainder = (blockProcessingReward - (blockProcessingRewardByGenerator * blocksProcessingBatchSize)) | |
569 | + | func map (acc,inc) = if ((inc >= blocksProcessingBatchSize)) | |
570 | + | then acc | |
571 | + | else { | |
572 | + | let targetBlockHeight = (nextBlockToProcess + inc) | |
573 | + | let targetBlockInfo = valueOrErrorMessage(blockInfoByHeight(targetBlockHeight), wrapErr("invalid block info")) | |
574 | + | let treasuryRewardOrFail = valueOrErrorMessage(rewardForOption(targetBlockInfo.rewards, proxyTreasuryAddressOrFail), wrapErr(("invalid treasury reward for height " + toString(targetBlockHeight)))) | |
575 | + | let generator = targetBlockInfo.generator | |
576 | + | let available = valueOrElse(getInteger(factoryAddressOrFail, keyAvailable(generator)), 0) | |
577 | + | let callerReward = if ((inc == (blocksProcessingBatchSize - 1))) | |
578 | + | then (blockProcessingRewardByGenerator + blockProcessingRewardByGeneratorRemainder) | |
579 | + | else blockProcessingRewardByGenerator | |
580 | + | let lpAssetAmount = if ((currentPriceOrFail > 0)) | |
581 | + | then fraction((treasuryRewardOrFail - callerReward), SCALE8, currentPriceOrFail) | |
582 | + | else 0 | |
583 | + | let factoryActionsSingle = [invoke(factoryAddressOrFail, "stringEntry", [keyBlockProcessed(targetBlockHeight), makeString([toBase58String(i.transactionId), toString(currentPeriodOrFail), toString(generator), toBase58String(userAddressBytes), toString(treasuryRewardOrFail), toString(callerReward), toString(lpAssetAmount)], SEP)], nil), invoke(factoryAddressOrFail, "integerEntry", [keyAvailable(generator), (available + lpAssetAmount)], nil)] | |
584 | + | if ((factoryActionsSingle == factoryActionsSingle)) | |
585 | + | then { | |
586 | + | let $t02400224035 = acc | |
587 | + | let lpAssetAcc = $t02400224035._1 | |
588 | + | let rewardAcc = $t02400224035._2 | |
589 | + | $Tuple2((lpAssetAcc + lpAssetAmount), (rewardAcc + treasuryRewardOrFail)) | |
590 | + | } | |
591 | + | else throw("Strict value is not equal to itself.") | |
592 | + | } | |
593 | + | ||
594 | + | let list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39] | |
595 | + | let $t02430224378 = { | |
596 | + | let $l = list | |
597 | + | let $s = size($l) | |
598 | + | let $acc0 = $Tuple2(0, 0) | |
599 | + | func $f0_1 ($a,$i) = if (($i >= $s)) | |
600 | + | then $a | |
601 | + | else map($a, $l[$i]) | |
602 | + | ||
603 | + | func $f0_2 ($a,$i) = if (($i >= $s)) | |
604 | + | then $a | |
605 | + | else throw("List size exceeds 40") | |
606 | + | ||
607 | + | $f0_2($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($acc0, 0), 1), 2), 3), 4), 5), 6), 7), 8), 9), 10), 11), 12), 13), 14), 15), 16), 17), 18), 19), 20), 21), 22), 23), 24), 25), 26), 27), 28), 29), 30), 31), 32), 33), 34), 35), 36), 37), 38), 39), 40) | |
608 | + | } | |
609 | + | if (($t02430224378 == $t02430224378)) | |
399 | 610 | then { | |
400 | - | let votingAcceptedInvokes = if ((votingType == "verification")) | |
401 | - | then invoke(assetsStoreContract, "createOrUpdate", [assetId, assetImage, true], nil) | |
402 | - | else invoke(assetsStoreContract, "setVerified", [assetId, false], nil) | |
403 | - | votingAcceptedInvokes | |
404 | - | } | |
405 | - | else nil | |
406 | - | if ((isVotingAccepted == isVotingAccepted)) | |
407 | - | then { | |
408 | - | let newVotingInfoValue = votingInfoValue(votingInfoParts._1, votingInfoParts._2, votingInfoParts._3, votingInfoParts._4, newStatus, votingInfoParts._6, votingInfoParts._7, votingInfoParts._8, votingInfoParts._9, votingInfoParts._10) | |
409 | - | let finalizeCallRewardAmount = getIntegerValue(keyFinalizeCallRewardAmount) | |
410 | - | [StringEntry(keyVotingInfo(assetId, currentIndex), newVotingInfoValue), ScriptTransfer(i.caller, finalizeCallRewardAmount, wxAssetId)] | |
611 | + | let rewardAmountTotal = $t02430224378._2 | |
612 | + | let lpAssetAmountTotal = $t02430224378._1 | |
613 | + | let rewardToMainTreasuryAmount = (rewardAmountTotal - blockProcessingReward) | |
614 | + | let invested = valueOrElse(getInteger(factoryAddressOrFail, keyInvested(unit)), 0) | |
615 | + | let actions = [invoke(factoryAddressOrFail, "reissue", [lpAssetAmountTotal], nil), invoke(factoryAddressOrFail, "integerEntry", [keyNextBlockToProcess(), (nextBlockToProcess + blocksProcessingBatchSize)], nil), invoke(factoryAddressOrFail, "transferFromProxyTreasury", [mainTreasuryAddressOrFail.bytes, rewardToMainTreasuryAmount], nil), invoke(factoryAddressOrFail, "transferFromProxyTreasury", [userAddressBytes, blockProcessingReward], nil), invoke(factoryAddressOrFail, "integerEntry", [keyInvested(unit), (invested + rewardToMainTreasuryAmount)], nil)] | |
616 | + | if ((actions == actions)) | |
617 | + | then $Tuple2(nil, unit) | |
618 | + | else throw("Strict value is not equal to itself.") | |
411 | 619 | } | |
412 | 620 | else throw("Strict value is not equal to itself.") | |
413 | 621 | } | |
414 | 622 | else throw("Strict value is not equal to itself.") | |
415 | 623 | } | |
416 | 624 | ||
417 | 625 | ||
418 | - | ||
419 | - | @Callable(i) | |
420 | - | func claimREADONLY (assetId,index,userAddressStr) = { | |
421 | - | let userAddress = valueOrErrorMessage(addressFromString(userAddressStr), wrapErr("invalid address")) | |
422 | - | let votingRewardAssetIdStr = getString(keyVotingRewardAssetId(assetId, index)) | |
423 | - | let rewardAmount = if (isDefined(votingRewardAssetIdStr)) | |
424 | - | then calculateReward(userAddress, assetId, index) | |
425 | - | else 0 | |
426 | - | $Tuple2(nil, rewardAmount) | |
427 | - | } | |
428 | - | ||
429 | - | ||
430 | - | ||
431 | - | @Callable(i) | |
432 | - | func claim (assetId,index) = { | |
433 | - | let callerAddress = i.caller | |
434 | - | let claimHistoryKey = keyClaimHistory(callerAddress, assetId, index) | |
435 | - | let claimHistory = getInteger(claimHistoryKey) | |
436 | - | let checks = [if ((claimHistory == unit)) | |
437 | - | then true | |
438 | - | else throwErr("already claimed")] | |
439 | - | if ((checks == checks)) | |
440 | - | then { | |
441 | - | let rewardAmount = if ((calculateReward(callerAddress, assetId, index) > 0)) | |
442 | - | then calculateReward(callerAddress, assetId, index) | |
443 | - | else throwErr("nothing to claim") | |
444 | - | let votingRewardAssetIdStr = getString(keyVotingRewardAssetId(assetId, index)) | |
445 | - | let rewardAction = if (isDefined(votingRewardAssetIdStr)) | |
446 | - | then { | |
447 | - | let votingRewardAssetId = fromBase58String(value(votingRewardAssetIdStr)) | |
448 | - | [ScriptTransfer(callerAddress, rewardAmount, votingRewardAssetId), IntegerEntry(claimHistoryKey, rewardAmount), DeleteEntry(keyVotingReward(callerAddress, assetId, index))] | |
449 | - | } | |
450 | - | else throwErr("nothing to claim") | |
451 | - | rewardAction | |
452 | - | } | |
453 | - | else throw("Strict value is not equal to itself.") | |
454 | - | } | |
455 | - | ||
456 | - | ||
457 | 626 | @Verifier(tx) | |
458 | 627 | func verify () = { | |
459 | - | let | |
460 | - | case | |
461 | - | | |
462 | - | case _ | |
628 | + | let publicKey = match getManagerPublicKeyOrUnit() { | |
629 | + | case pub: ByteVector => | |
630 | + | pub | |
631 | + | case _ => | |
463 | 632 | tx.senderPublicKey | |
464 | - | case _ => | |
465 | - | throw("Match error") | |
466 | 633 | } | |
467 | - | sigVerify(tx.bodyBytes, tx.proofs[0], | |
634 | + | sigVerify(tx.bodyBytes, tx.proofs[0], publicKey) | |
468 | 635 | } | |
469 | 636 |
github/deemru/w8io/169f3d6 86.28 ms ◑