tx · D7pHgkuipvTYqQLUZRxwBgY7SAFDKRocEjRmpGoFDKe7

3PLncXtS1U83D6cQbFD3H8rBHPLgzxSFKZ1:  -0.02400000 Waves

2023.01.08 16:15 [3461520] smart account 3PLncXtS1U83D6cQbFD3H8rBHPLgzxSFKZ1 > SELF 0.00000000 Waves

{ "type": 13, "id": "D7pHgkuipvTYqQLUZRxwBgY7SAFDKRocEjRmpGoFDKe7", "fee": 2400000, "feeAssetId": null, "timestamp": 1673183680531, "version": 2, "chainId": 87, "sender": "3PLncXtS1U83D6cQbFD3H8rBHPLgzxSFKZ1", "senderPublicKey": "BtDpFhgHQKHFVqYTMLReuZzqy94CRPSRkrYcSEVyjt4q", "proofs": [ "2FWjY49gYXqejC8V1sDgjkNivFBqJw5ff2VtGhTWtZnXcDwYorL7W2Lc3f7y7jQg9c4CN1TUhHweHKtqZWDvBPnU" ], "script": "base64:BgJJCAISABIDCgEIEgASAwoBCBIECgIBCBIECgICAhIECgIBCBIECgIBARIECgIICBIECgICAhIECgICAhIDCgEIEgMKAQgSAwoBCEsABWNoYWluCQCwCQEJAMkBAgkAygECCAUEdGhpcwVieXRlcwABAAEAC3VzZG5Bc3NldElkBAckbWF0Y2gwBQVjaGFpbgMJAAACAgFXBQckbWF0Y2gwASC2JinDBPXOU5GkDkt1JC9kjFGx+t+vVCm9SNIdKrKq0QMJAAACAgFUBQckbWF0Y2gwASD3dur394PKZdtuE+4CO89YKZWpwdGN8kvabNgdYoDI3gkAAgECDVVua25vd24gY2hhaW4ADWluY3ViYXRvckFkZHIEByRtYXRjaDAFBWNoYWluAwkAAAICAVcFByRtYXRjaDAJARFAZXh0ck5hdGl2ZSgxMDYyKQECIzNQRWt0VnV4MlJoY2hTTjYzRHNEbzRiNG16NFFxektTZUR2AwkAAAICAVQFByRtYXRjaDAFBHRoaXMJAAIBAg1Vbmtub3duIGNoYWluAAticmVlZGVyQWRkcgQHJG1hdGNoMAUFY2hhaW4DCQAAAgIBVwUHJG1hdGNoMAkBEUBleHRyTmF0aXZlKDEwNjIpAQIjM1BEVnVVNDVIN0VoNWRtdE5iblJOUlN0R3dVTEE3Tlk2SGIDCQAAAgIBVAUHJG1hdGNoMAUEdGhpcwkAAgECDVVua25vd24gY2hhaW4AC2Vjb25vbXlBZGRyBAckbWF0Y2gwBQVjaGFpbgMJAAACAgFXBQckbWF0Y2gwCQERQGV4dHJOYXRpdmUoMTA2MikBAiMzUDJzazFLbmNTeFJhWnM4YjRDV0dQdzJqa3Z2YXY3NHU0RAMJAAACAgFUBQckbWF0Y2gwCQERQGV4dHJOYXRpdmUoMTA2MikBAiMzTjh5NHd4WDNKQzRUZHJDSkJYWDE2U2pXZjZYMjU2aHJlcAkAAgECDVVua25vd24gY2hhaW4AA3B1YgEgT1PiGj9UqNadUk4THUYnVb2zU/Mse8lUVLD/Q8G0FCgACEhFQUxDT1NUAJBOAApMQU5EUFJFRklYAgRMQU5EAApEVUNLUFJFRklYAgREVUNLAA9ERUZBVUxUTE9DQVRJT04CD0FmcmljYV9GX0FmcmljYQAGTlVNUkVTAAYAD0RBSUxZUkVTQllQSUVDRQCA+NIBAAlEQVlNSUxMSVMAgLiZKQARRklWRU1JTlVURVNNSUxMSVMA4KcSABBSRVNPVVJDRVBSSUNFTUlOANXWCQARSW5mcmFVcGdyYWRlQ29zdFMEByRtYXRjaDAFBWNoYWluAwkAAAICAVcFByRtYXRjaDAA0cLCvkYDCQAAAgIBVAUHJG1hdGNoMADQ6ZxaCQACAQINVW5rbm93biBjaGFpbgAVSW5mcmFVcGdyYWRlQ29zdFNVc2RuBAckbWF0Y2gwBQVjaGFpbgMJAAACAgFXBQckbWF0Y2gwAICcnDkDCQAAAgIBVAUHJG1hdGNoMACAn0kJAAIBAg1Vbmtub3duIGNoYWluAAxFWFBNQVRFUklBTFMEByRtYXRjaDAFBWNoYWluAwkAAAICAVcFByRtYXRjaDAAy4DVs8sEAwkAAAICAVQFByRtYXRjaDAA8Zrw7wUJAAIBAg1Vbmtub3duIGNoYWluAAdFWFBVU0ROBAckbWF0Y2gwBQVjaGFpbgMJAAACAgFXBQckbWF0Y2gwAICU69wDAwkAAAICAVQFByRtYXRjaDAAgK3iBAkAAgECDVVua25vd24gY2hhaW4ABU1VTFQ2AMCEPQAFRklWRVgJALYCAQAFAAdUV0VOVFlYCQC2AgEAFAAIVFdFTlRZMlgJALYCAQkAaAIAFAAUAAhUV0VOVFkzWAkAtgIBCQBoAgkAaAIAFAAUABQACFRXRU5UWTRYCQC2AgEJAGgCCQBoAgkAaAIAFAAUABQAFAAIVFdFTlRZNVgJALYCAQkAaAIJAGgCCQBoAgkAaAIAFAAUABQAFAAUAAhtYXRUeXBlcwkAzAgCAgRGdWVsCQDMCAICBU1ldGFsCQDMCAICBVBsYW5rCQDMCAICBUdsYXNzCQDMCAICB1BsYXN0aWMJAMwIAgIHUHJvdGVpbgUDbmlsAApjb250aW5lbnRzCQDMCAICBEFzaWEJAMwIAgIGRXVyb3BlCQDMCAICCEFtZXJpY2FzCQDMCAICB09jZWFuaWEJAMwIAgIGQWZyaWNhBQNuaWwBEmtleU5leHRGcmVlTGFuZE51bQACC25leHRMYW5kTnVtARBrZXlMYW5kVG9Bc3NldElkAQdsYW5kTnVtCQCsAgICDGxhbmRUb0Fzc2V0XwUHbGFuZE51bQEKa2V5TmZ0TmFtZQIHbGFuZE51bQhsYW5kU2l6ZQkArAICCQCsAgIFCkxBTkRQUkVGSVgFB2xhbmROdW0FCGxhbmRTaXplARVrZXlMYW5kQXNzZXRJZFRvT3duZXIBB2Fzc2V0SWQJAKwCAgIJbmZ0T3duZXJfBQdhc3NldElkARBrZXlEdWNrSWRUb093bmVyAQdhc3NldElkCQCsAgICCmR1Y2tPd25lcl8FB2Fzc2V0SWQBFmtleVN0YWtlZFRpbWVCeUFzc2V0SWQBB2Fzc2V0SWQJAKwCAgILc3Rha2VkVGltZV8FB2Fzc2V0SWQBFmtleUluZnJhTGV2ZWxCeUFzc2V0SWQBB2Fzc2V0SWQJAKwCAgILaW5mcmFMZXZlbF8FB2Fzc2V0SWQBHmtleUluZnJhTGV2ZWxCeUFzc2V0SWRBbmRPd25lcgIHYXNzZXRJZAlvd25lckFkZHIJAKwCAgkArAICCQCsAgICHGluZnJhTGV2ZWxCeUFzc2V0SWRBbmRPd25lcl8FB2Fzc2V0SWQCAV8FCW93bmVyQWRkcgEfa2V5UHJlc2FsZUFydEFjdGl2YXRlZEJ5QXNzZXRJZAEHYXNzZXRJZAkArAICAhRwcmVzYWxlQXJ0QWN0aXZhdGVkXwUHYXNzZXRJZAEna2V5UHJlc2FsZUFydEFjdGl2YXRlZEJ5QXNzZXRJZEFuZE93bmVyAgdhc3NldElkCW93bmVyQWRkcgkArAICCQCsAgIJAKwCAgIlcHJlc2FsZUFydEFjdGl2YXRlZEJ5QXNzZXRJZEFuZE93bmVyXwUHYXNzZXRJZAIBXwUJb3duZXJBZGRyARRrZXlTdGFrZWREdWNrQnlPd25lcgEJb3duZXJBZGRyCQCsAgICEnN0YWtlZER1Y2tCeU93bmVyXwUJb3duZXJBZGRyASJrZXlTdGFrZWRUaW1lQnlUeXBlQXNzZXRJZEFuZE93bmVyAwduZnRUeXBlB2Fzc2V0SWQJb3duZXJBZGRyCQCsAgIJAKwCAgkArAICCQCsAgIJAKwCAgIgc3Rha2VkVGltZUJ5VHlwZUFzc2V0SWRBbmRPd25lcl8FB25mdFR5cGUCAV8FB2Fzc2V0SWQCAV8FCW93bmVyQWRkcgERa2V5TGFuZE51bVRvT3duZXIBB2xhbmROdW0JAKwCAgIKbGFuZE93bmVyXwUHbGFuZE51bQERa2V5QmFja3BhY2tCeUR1Y2sBC2R1Y2tBc3NldElkCQCsAgICCWJhY2tQYWNrXwULZHVja0Fzc2V0SWQBD2tleUR1Y2tMb2NhdGlvbgELZHVja0Fzc2V0SWQJAKwCAgINZHVja0xvY2F0aW9uXwULZHVja0Fzc2V0SWQBDWtleUR1Y2tIZWFsdGgBC2R1Y2tBc3NldElkCQCsAgICC2R1Y2tIZWFsdGhfBQtkdWNrQXNzZXRJZAERa2V5UmVzUHJvcG9ydGlvbnMAAhNyZXNUeXBlc1Byb3BvcnRpb25zAApyZWNMYW5kTnVtAAAAC3JlY0xhbmRTaXplAAEAC3JlY1RlcnJhaW5zAAIADHJlY0NvbnRpbmVudAADAA9sb2NJZHhDb250aW5lbnQAAAAKbG9jSWR4VHlwZQABAAhsb2NJZHhJZAACAApicElkeExldmVsAAAACGJwSWR4UmVzAAEACGJwSWR4TWF0AAIACWJwSWR4UHJvZAADAQhhc1N0cmluZwEBdgQHJG1hdGNoMAUBdgMJAAECBQckbWF0Y2gwAgZTdHJpbmcEAXMFByRtYXRjaDAFAXMJAAIBAhhmYWlsIHRvIGNhc3QgaW50byBTdHJpbmcBEmdldE5lZWRlZE1hdGVyaWFscwEFdG90YWwEBXByb3BzCQC1CQIJAQV2YWx1ZQEJAKIIAQkBEWtleVJlc1Byb3BvcnRpb25zAAIBXwMJAQIhPQIJAJADAQUFcHJvcHMFBk5VTVJFUwkAAgECFldyb25nIHByb3BvcnRpb25zIGRhdGEEAXIJAMwIAgkBDXBhcnNlSW50VmFsdWUBCQCRAwIFBXByb3BzAAAJAMwIAgkBDXBhcnNlSW50VmFsdWUBCQCRAwIFBXByb3BzAAEJAMwIAgkBDXBhcnNlSW50VmFsdWUBCQCRAwIFBXByb3BzAAIJAMwIAgkBDXBhcnNlSW50VmFsdWUBCQCRAwIFBXByb3BzAAMJAMwIAgkBDXBhcnNlSW50VmFsdWUBCQCRAwIFBXByb3BzAAQJAMwIAgkBDXBhcnNlSW50VmFsdWUBCQCRAwIFBXByb3BzAAUFA25pbAQDc3VtCQBkAgkAZAIJAGQCCQBkAgkAZAIJAJEDAgUBcgAACQCRAwIFAXIAAQkAkQMCBQFyAAIJAJEDAgUBcgADCQCR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cmVxcwACBQNyZW0CAUMDCQBmAgkAkQMCBQVmcmVxcwADBQNyZW0CAUQDCQBmAgkAkQMCBQVmcmVxcwAEBQNyZW0CAUUCAUYFBmxldHRlcgELZ2V0QmFja3BhY2sBBWJwS2V5BAFwCQC1CQIJAQt2YWx1ZU9yRWxzZQIJAKIIAQUFYnBLZXkCGjA6MF8wXzBfMF8wXzA6MF8wXzBfMF8wXzA6AgE6CQDMCAIJAKQDAQkBC3ZhbHVlT3JFbHNlAgkAtgkBCQCRAwIFAXAFCmJwSWR4TGV2ZWwAAAkAzAgCAwkAAAIJAJADAQkAtQkCCQCRAwIFAXAFCGJwSWR4UmVzAgFfBQZOVU1SRVMJAJEDAgUBcAUIYnBJZHhSZXMCCzBfMF8wXzBfMF8wCQDMCAIDCQAAAgkAkAMBCQC1CQIJAJEDAgUBcAUIYnBJZHhNYXQCAV8FBk5VTVJFUwkAkQMCBQFwBQhicElkeE1hdAILMF8wXzBfMF8wXzAJAMwIAgkAkQMCBQFwBQlicElkeFByb2QFA25pbAEQZXhwZWRpdGlvbkNvbW1vbgUMc2hvdWxkVXNlTWF0BmNhbGxlcgR0eElkB21lc3NhZ2UDc2lnAwkBASEBCQDEEwMFB21lc3NhZ2UFA3NpZwUDcHViCQACAQIYc2lnbmF0dXJlIGRvZXMgbm90IG1hdGNoBAVwYXJ0cwkAtQkCCQCwCQEFB21lc3NhZ2UCATsEAmhwCQC1CQIJAJEDAgkAtQkCCQCRAwIFBXBhcnRzAAACAXwAAAIBXwQFY3VySFAJAQ1wYXJzZUludFZhbHVlAQkAkQMCBQJocAAABAVuZXdIUAkBDXBhcnNlSW50VmFsdWUBCQCRAwIFAmhwAAEECmxvY0FuZFRpbWUJALUJAgkAkQMCBQVwYXJ0cwABAgE6BA50YXJnZXRMb2NhdGlvbgkAtQkCCQCRAwIFCmxvY0FuZFRpbWUAAAIBXwMJAQIhPQIJAJEDAgUOdGFyZ2V0TG9jYXRpb24AAQIBRQkAAgECK2V4cGVkaXRpb24gdGFyZ2V0IGxvY2F0aW9uIHR5cGUgc2hvdWxkIGJlIEUEBHRpbWUJAQ1wYXJzZUludFZhbHVlAQkAkQMCBQpsb2NBbmRUaW1lAAEDAwkAZgIFBHRpbWUJAGQCCAUJbGFzdEJsb2NrCXRpbWVzdGFtcAURRklWRU1JTlVURVNNSUxMSVMGCQBmAgkAZQIIBQlsYXN0QmxvY2sJdGltZXN0YW1wBRFGSVZFTUlOVVRFU01JTExJUwUEdGltZQkAAgECEnNpZ25hdHVyZSBvdXRkYXRlZAQIdXNlckFkZHIJAKUIAQUGY2FsbGVyBAtkdWNrQXNzZXRJZAkBE3ZhbHVlT3JFcnJvck1lc3NhZ2UCCQCiCAEJARRrZXlTdGFrZWREdWNrQnlPd25lcgEFCHVzZXJBZGRyAhxZb3UgZG9uJ3QgaGF2ZSBhIGR1Y2sgc3Rha2VkBAlrZXlIZWFsdGgJAQ1rZXlEdWNrSGVhbHRoAQULZHVja0Fzc2V0SWQEDG9sZEZyb21TdGF0ZQkBC3ZhbHVlT3JFbHNlAgkAnwgBBQlrZXlIZWFsdGgAZAMJAQIhPQIFDG9sZEZyb21TdGF0ZQUFY3VySFAJAAIBCQCsAgIJAKwCAgkArAICAgpvbGRIZWFsdGg9CQCkAwEJAQt2YWx1ZU9yRWxzZQIJAJ8IAQUJa2V5SGVhbHRoAGQCLyBmcm9tIHN0YXRlIGRvZXMgbm90IG1hdGNoIG9uZSBmcm9tIGZsaWdodCBsb2c9CQCkAwEFBWN1ckhQAwkAZwIAAAUFY3VySFAJAAIBAh5Zb3UgY2FuJ3QgZmx5IHdpdGggemVybyBoZWFsdGgDCQBnAgAABQVuZXdIUAkAlAoCCQDNCAIDCQEBIQEFDHNob3VsZFVzZU1hdAkAzAgCCQEOU2NyaXB0VHJhbnNmZXIDBQZjYWxsZXIFB0VYUFVTRE4FC3VzZG5Bc3NldElkBQNuaWwFA25pbAkBDEludGVnZXJFbnRyeQIFCWtleUhlYWx0aAAAAgAEBWJwS2V5CQERa2V5QmFja3BhY2tCeUR1Y2sBBQtkdWNrQXNzZXRJZAQLY3VycmVudFBhY2sJAQtnZXRCYWNrcGFjawEFBWJwS2V5BAVtTGlzdAkAtQkCCQCRAwIFC2N1cnJlbnRQYWNrBQhicElkeE1hdAIBXwQGbmV3TWF0CQC5CQIJARFzdWJ0cmFjdE1hdGVyaWFscwMFDHNob3VsZFVzZU1hdAUFbUxpc3QFDEVYUE1BVEVSSUFMUwIBXwQGYmlnTnVtCQEDYWJzAQkAngMBBQR0eElkBAdmcmVlTnVtCQELdmFsdWVPckVsc2UCCQCfCAEJARJrZXlOZXh0RnJlZUxhbmROdW0AAPUDBAdsYW5kTnVtCQCkAwEFB2ZyZWVOdW0EDGNvbnRpbmVudElkeAkAoAMBCQC7AgIFBmJpZ051bQUFRklWRVgEAWYJAJEDAgUEZnJlcQUMY29udGluZW50SWR4CgEQdGVycmFpbkdlbmVyYXRvcgIDYWNjBGVsZW0JAJQKAgkArAICCQCsAgIJAKwCAgkArAICCQCsAgIIBQNhY2MCXzEJAQdnZW5DaGFyAggFA2FjYwJfMgUBZgkBB2dlbkNoYXICCQC6AgIIBQNhY2MCXzIFB1RXRU5UWVgFAWYJAQdnZW5DaGFyAgkAugICCAUDYWNjAl8yBQhUV0VOVFkyWAUBZgkBB2dlbkNoYXICCQC6AgIIBQNhY2MCXzIFCFRXRU5UWTNYBQFmCQEHZ2VuQ2hhcgIJALoCAggFA2FjYwJfMgUIVFdFTlRZNFgFAWYJALoCAggFA2FjYwJfMgUIVFdFTlRZNVgEAXQKAAIkbAkAzAgCAAEJAMwIAgACCQDMCAIAAwkAzAgCAAQJAMwIAgAFBQNuaWwKAAIkcwkAkAMBBQIkbAoABSRhY2MwCQCUCgICAAkAugICBQZiaWdOdW0FBUZJVkVYCgEFJGYwXzECAiRhAiRpAwkAZwIFAiRpBQIkcwUCJGEJARB0ZXJyYWluR2VuZXJhdG9yAgUCJGEJAJEDAgUCJGwFAiRpCgEFJGYwXzICAiRhAiRpAwkAZwIFAiRpBQIkcwUCJGEJAAIBAhNMaXN0IHNpemUgZXhjZWVkcyA1CQEFJGYwXzICCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECBQUkYWNjMAAAAAEAAgADAAQABQQJY29udGluZW50CQCRAwIFCmNvbnRpbmVudHMFDGNvbnRpbmVudElkeAQFaXNzdWUJAMIIBQkBCmtleU5mdE5hbWUCBQdsYW5kTnVtAgFTCQC5CQIJAMwIAgUHbGFuZE51bQkAzAgCAgFTCQDMCAIIBQF0Al8xCQDMCAIFCWNvbnRpbmVudAUDbmlsAgFfAAEAAAcEB2Fzc2V0SWQJALgIAQUFaXNzdWUEAmlkCQDYBAEFB2Fzc2V0SWQJAJQKAgkAzAgCCQEMSW50ZWdlckVudHJ5AgkBEmtleU5leHRGcmVlTGFuZE51bQAJAGQCBQdmcmVlTnVtAAEJAMwIAgUFaXNzdWUJAMwIAgkBC1N0cmluZ0VudHJ5AgkBEGtleUxhbmRUb0Fzc2V0SWQBBQdsYW5kTnVtBQJpZAkAzAgCCQELU3RyaW5nRW50cnkCCQEVa2V5TGFuZEFzc2V0SWRUb093bmVyAQUCaWQFCHVzZXJBZGRyCQDMCAIJAQtTdHJpbmdFbnRyeQIJARFrZXlMYW5kTnVtVG9Pd25lcgEFB2xhbmROdW0FCHVzZXJBZGRyCQDMCAIJAQxJbnRlZ2VyRW50cnkCCQEWa2V5SW5mcmFMZXZlbEJ5QXNzZXRJZAEFAmlkAAAJAMwIAgkBDEludGVnZXJFbnRyeQIJAR5rZXlJbmZyYUxldmVsQnlBc3NldElkQW5kT3duZXICBQJpZAUIdXNlckFkZHIAAAkAzAgCCQEOU2NyaXB0VHJhbnNmZXIDBQZjYWxsZXIAAQUHYXNzZXRJZAkAzAgCCQELU3RyaW5nRW50cnkCCQEPa2V5RHVja0xvY2F0aW9uAQULZHVja0Fzc2V0SWQJALkJAgkAzAgCBQljb250aW5lbnQJAMwIAgIBTAkAzAgCBQJpZAUDbmlsAgFfCQDMCAIJAQxJbnRlZ2VyRW50cnkCBQlrZXlIZWFsdGgFBW5ld0hQCQDMCAIJAQtTdHJpbmdFbnRyeQIFBWJwS2V5CQC5CQIJAMwIAgkAkQMCBQtjdXJyZW50UGFjawUKYnBJZHhMZXZlbAkAzAgCCQCRAwIFC2N1cnJlbnRQYWNrBQhicElkeFJlcwkAzAgCBQZuZXdNYXQJAMwIAgkAkQMCBQtjdXJyZW50UGFjawUJYnBJZHhQcm9kBQNuaWwCAToFA25pbAUCaWQBDGFwcGx5Qm9udXNlcwELbGFuZEFzc2V0SWQECmluZnJhTGV2ZWwJAQt2YWx1ZU9yRWxzZQIJAJ8IAQkBFmtleUluZnJhTGV2ZWxCeUFzc2V0SWQBBQtsYW5kQXNzZXRJZAAABAxwcmVzYWxlQm9udXMDCQELdmFsdWVPckVsc2UCCQCgCAEJAR9rZXlQcmVzYWxlQXJ0QWN0aXZhdGVkQnlBc3NldElkAQULbGFuZEFzc2V0SWQHAAMAAAkAZAIJAGQCBQ9EQUlMWVJFU0JZUElFQ0UJAGsDBQ9EQUlMWVJFU0JZUElFQ0UFCmluZnJhTGV2ZWwABAkAawMFD0RBSUxZUkVTQllQSUVDRQUMcHJlc2FsZUJvbnVzABQBFGNoZWNrQ2xhaW1Db25kaXRpb25zAQRhZGRyBAtkdWNrQXNzZXRJZAkBE3ZhbHVlT3JFcnJvck1lc3NhZ2UCCQCiCAEJARRrZXlTdGFrZWREdWNrQnlPd25lcgEFBGFkZHICHFlvdSBkb24ndCBoYXZlIGEgZHVjayBzdGFrZWQEC2N1ckxvY2F0aW9uCQELdmFsdWVPckVsc2UCCQCiCAEJAQ9rZXlEdWNrTG9jYXRpb24BBQtkdWNrQXNzZXRJZAUPREVGQVVMVExPQ0FUSU9OBANsb2MJALUJAgkBBXZhbHVlAQULY3VyTG9jYXRpb24CAV8DCQECIT0CCQCRAwIFA2xvYwUKbG9jSWR4VHlwZQIBTAkAAgEJAKwCAgkArAICAhZEdWNrIGxvY2F0aW9uIHR5cGUgaXMgCQCRAwIFA2xvYwUKbG9jSWR4VHlwZQIRLCBidXQgc2hvdWxkIGJlIEwEC2xhbmRBc3NldElkCQCRAwIFA2xvYwUIbG9jSWR4SWQEBWFzc2V0CQEFdmFsdWUBCQDsBwEJANkEAQULbGFuZEFzc2V0SWQEB3RpbWVLZXkJARZrZXlTdGFrZWRUaW1lQnlBc3NldElkAQULbGFuZEFzc2V0SWQECXNhdmVkVGltZQkBE3ZhbHVlT3JFcnJvck1lc3NhZ2UCCQCfCAEFB3RpbWVLZXkJAKwCAgkArAICAgRORlQgCAUFYXNzZXQEbmFtZQIOIGlzIG5vdCBzdGFrZWQEBW93bmVyCQETdmFsdWVPckVycm9yTWVzc2FnZQIJAKIIAQkBFWtleUxhbmRBc3NldElkVG9Pd25lcgEFC2xhbmRBc3NldElkCQCsAgIJAKwCAgIETkZUIAgFBWFzc2V0BG5hbWUCDCBpcyBvcnBoYW5lZAMJAQIhPQIFBW93bmVyBQRhZGRyCQACAQkArAICBQpMQU5EUFJFRklYAg0gaXMgbm90IHlvdXJzBAFkCQC1CQIIBQVhc3NldAtkZXNjcmlwdGlvbgIBXwkAlgoEBQtkdWNrQXNzZXRJZAULbGFuZEFzc2V0SWQFAWQFCXNhdmVkVGltZQEQY2xhaW1SZXNJbnRlcm5hbAIEYWRkcgZhbW91bnQEAWMJARRjaGVja0NsYWltQ29uZGl0aW9ucwEFBGFkZHIECGxhbmRTaXplCQCRAwIIBQFjAl8zBQtyZWNMYW5kU2l6ZQQNdGVycmFpbkNvdW50cwkBDWNvdW50VGVycmFpbnMBCQCRAwIIBQFjAl8zBQtyZWNUZXJyYWlucwQJZGVsdGFUaW1lCQBlAggFCWxhc3RCbG9jawl0aW1lc3RhbXAIBQFjAl80AwkAZgIAAAUJZGVsdGFUaW1lCQACAQkArAICCQCsAgIJAKwCAgImU2F2ZWQgdGltZXN0YW1wIGlzIGluIGZ1dHVyZSwgc2F2ZWQgPSAJAKQDAQgFAWMCXzQCDCwgY3VycmVudCA9IAkApAMBCAUJbGFzdEJsb2NrCXRpbWVzdGFtcAQGcGllY2VzCQEPbnVtUGllY2VzQnlTaXplAQUIbGFuZFNpemUEFmRhaWx5UHJvZHVjdGlvbkJ5UGllY2UJAQxhcHBseUJvbnVzZXMBCAUBYwJfMgQIYXZhaWxSZXMJAGsDBQlkZWx0YVRpbWUJAGgCBRZkYWlseVByb2R1Y3Rpb25CeVBpZWNlBQZwaWVjZXMFCURBWU1JTExJUwMJAGYCBQZhbW91bnQFCGF2YWlsUmVzCQACAQkArAICCQCsAgIJAKwCAgIiTm90IGVub3VnaCByZXNvdXJjZXMsIGF2YWlsYWJsZSA9IAkApAMBBQhhdmFpbFJlcwIOLCByZXF1ZXN0ZWQgPSAJAKQDAQUGYW1vdW50BAxuZXdEZWx0YVRpbWUJAGsDCQBlAgUIYXZhaWxSZXMFBmFtb3VudAUJREFZTUlMTElTCQBoAgUWZGFpbHlQcm9kdWN0aW9uQnlQaWVjZQUGcGllY2VzBAxuZXdUaW1lc3RhbXAJAGUCCAUJbGFzdEJsb2NrCXRpbWVzdGFtcAUMbmV3RGVsdGFUaW1lBAVicEtleQkBEWtleUJhY2twYWNrQnlEdWNrAQgFAWMCXzEEC2N1cnJlbnRQYWNrCQELZ2V0QmFja3BhY2sBBQVicEtleQQKY3VycmVudFJlcwkAtQkCCQCRAwIFC2N1cnJlbnRQYWNrBQhicElkeFJlcwIBXwQFYnBSZXMJAQZhZGRSZXMFBQpjdXJyZW50UmVzBQ10ZXJyYWluQ291bnRzCQBlAgUJZGVsdGFUaW1lBQxuZXdEZWx0YVRpbWUJAGkCBQZwaWVjZXMAGQUWZGFpbHlQcm9kdWN0aW9uQnlQaWVjZQkAlQoDCQDMCAIJAQxJbnRlZ2VyRW50cnkCCQEWa2V5U3Rha2VkVGltZUJ5QXNzZXRJZAEIBQFjAl8yBQxuZXdUaW1lc3RhbXAJAMwIAgkBDEludGVnZXJFbnRyeQIJASJrZXlTdGFrZWRUaW1lQnlUeXBlQXNzZXRJZEFuZE93bmVyAwUKTEFORFBSRUZJWAgFAWMCXzIFBGFkZHIFDG5ld1RpbWVzdGFtcAUDbmlsBQVicEtleQkAzAgCCQCRAwIFC2N1cnJlbnRQYWNrBQpicElkeExldmVsCQDMCAIFBWJwUmVzCQDMCAIJAJEDAgULY3VycmVudFBhY2sFCGJwSWR4TWF0CQDMCAIJAJEDAgULY3VycmVudFBhY2sFCWJwSWR4UHJvZAUDbmlsAQhjbGFpbUFsbAMEYWRkcgtsYW5kQXNzZXRJZAZwaWVjZXMEB3RpbWVLZXkJARZrZXlTdGFrZWRUaW1lQnlBc3NldElkAQULbGFuZEFzc2V0SWQECXNhdmVkVGltZQkBBXZhbHVlAQkAnwgBBQd0aW1lS2V5BAhhdmFpbFJlcwkAaAIJAGsDCQBlAggFCWxhc3RCbG9jawl0aW1lc3RhbXAFCXNhdmVkVGltZQkBDGFwcGx5Qm9udXNlcwEFC2xhbmRBc3NldElkBQlEQVlNSUxMSVMFBnBpZWNlcwkBEGNsYWltUmVzSW50ZXJuYWwCBQRhZGRyBQhhdmFpbFJlcwENdXBJbmZyYUNvbW1vbgMMc2hvdWxkVXNlTWF0BmNhbGxlcg1wYXltZW50QW1vdW50BARhZGRyCQClCAEFBmNhbGxlcgQBYwkBFGNoZWNrQ2xhaW1Db25kaXRpb25zAQUEYWRkcgQGcGllY2VzCQEPbnVtUGllY2VzQnlTaXplAQkAkQMCCAUBYwJfMwULcmVjTGFuZFNpemUECGluZnJhS2V5CQEWa2V5SW5mcmFMZXZlbEJ5QXNzZXRJZAEIBQFjAl8yBAhjdXJMZXZlbAkBC3ZhbHVlT3JFbHNlAgkAnwgBBQhpbmZyYUtleQAAAwkAZwIFCGN1ckxldmVsAAMJAAIBAiZDdXJyZW50bHkgbWF4IGluZnJhc3RydWN0dXJlIGxldmVsID0gMwQIbmV3TGV2ZWwJAGQCBQhjdXJMZXZlbAABBARjb3N0CQBrAwUVSW5mcmFVcGdyYWRlQ29zdFNVc2RuCQBoAgUGcGllY2VzBQhuZXdMZXZlbAAZAwMJAQEhAQUMc2hvdWxkVXNlTWF0CQECIT0CBQ1wYXltZW50QW1vdW50BQRjb3N0BwkAAgEJAKwCAgIbUGF5bWVudCBhdHRhY2hlZCBzaG91bGQgYmUgCQCkAwEFBGNvc3QEBWJwS2V5CQERa2V5QmFja3BhY2tCeUR1Y2sBCAUBYwJfMQQLY3VycmVudFBhY2sJAQtnZXRCYWNrcGFjawEFBWJwS2V5BAVtTGlzdAkAtQkCCQCRAwIFC2N1cnJlbnRQYWNrBQhicElkeE1hdAIBXwQGbmV3TWF0CQC5CQIJARFzdWJ0cmFjdE1hdGVyaWFscwMFDHNob3VsZFVzZU1hdAUFbUxpc3QJAGsDBRFJbmZyYVVwZ3JhZGVDb3N0UwkAaAIFBnBpZWNlcwUIbmV3TGV2ZWwAGQIBXwQLY2xhaW1SZXN1bHQJAQhjbGFpbUFsbAMFBGFkZHIIBQFjAl8yBQZwaWVjZXMJAJQKAgkAzggCCQDMCAIJAQxJbnRlZ2VyRW50cnkCBQhpbmZyYUtleQUIbmV3TGV2ZWwJAMwIAgkBDEludGVnZXJFbnRyeQIJAR5rZXlJbmZyYUxldmVsQnlBc3NldElkQW5kT3duZXICCAUBYwJfMgUEYWRkcgUIbmV3TGV2ZWwJAMwIAgkBC1N0cmluZ0VudHJ5AgUFYnBLZXkJALkJAgkAzAgCCQCRAwIFC2N1cnJlbnRQYWNrBQpicElkeExldmVsCQDMCAIJAJEDAggFC2NsYWltUmVzdWx0Al8zBQhicElkeFJlcwkAzAgCBQZuZXdNYXQJAMwIAgkAkQMCBQtjdXJyZW50UGFjawUJYnBJZHhQcm9kBQNuaWwCAToFA25pbAgFC2NsYWltUmVzdWx0Al8xBQhuZXdMZXZlbAESYWN0aXZhdGVQcmVzYWxlQXJ0AQRhZGRyBAFjCQEUY2hlY2tDbGFpbUNvbmRpdGlvbnMBBQRhZGRyBA1hY3RpdmF0aW9uS2V5CQEfa2V5UHJlc2FsZUFydEFjdGl2YXRlZEJ5QXNzZXRJZAEIBQFjAl8yAwkBC3ZhbHVlT3JFbHNlAgkAoAgBBQ1hY3RpdmF0aW9uS2V5BwkAAgECJVByZXNhbGUgYXJ0aWZhY3QgaXMgYWxyZWFkeSBhY3RpdmF0ZWQDCQBmAgkBDXBhcnNlSW50VmFsdWUBCQCRAwIIBQFjAl8zBQpyZWNMYW5kTnVtAPQDCQACAQkArAICCQCsAgIJAKwCAgUKTEFORFBSRUZJWAIBIAgFAWMCXzICJSBpcyBub3QgZWxpZ2libGUgZm9yIHByZXNhbGUgYXJ0aWZhY3QEBnBpZWNlcwkBD251bVBpZWNlc0J5U2l6ZQEJAJEDAggFAWMCXzMFC3JlY0xhbmRTaXplBAtjbGFpbVJlc3VsdAkBCGNsYWltQWxsAwUEYWRkcggFAWMCXzIFBnBpZWNlcwkAzQgCCQDNCAIJAM0IAggFC2NsYWltUmVzdWx0Al8xCQEMQm9vbGVhbkVudHJ5AgkBH2tleVByZXNhbGVBcnRBY3RpdmF0ZWRCeUFzc2V0SWQBCAUBYwJfMgYJAQxCb29sZWFuRW50cnkCCQEna2V5UHJlc2FsZUFydEFjdGl2YXRlZEJ5QXNzZXRJZEFuZE93bmVyAggFAWMCXzIFBGFkZHIGCQELU3RyaW5nRW50cnkCCAULY2xhaW1SZXN1bHQCXzIJALkJAggFC2NsYWltUmVzdWx0Al8zAgE6DgFpAQlzdGFrZUxhbmQABANwbXQJAQV2YWx1ZQEJAJEDAggFAWkIcGF5bWVudHMAAAQHYXNzZXRJZAkBBXZhbHVlAQgFA3BtdAdhc3NldElkBAdhZGRyZXNzCQClCAEIBQFpBmNhbGxlcgMJAQIhPQIIBQNwbXQGYW1vdW50AAEJAAIBCQCsAgIJAKwCAgIETkZUIAUKTEFORFBSRUZJWAIkIHRva2VuIHNob3VsZCBiZSBhdHRhY2hlZCBhcyBwYXltZW50BAVhc3NldAkBBXZhbHVlAQkA7AcBBQdhc3NldElkAwkBAiE9AggFBWFzc2V0Bmlzc3VlcgUEdGhpcwkAAgECF1Vua25vd24gaXNzdWVyIG9mIHRva2VuAwkBASEBCQEIY29udGFpbnMCCAUFYXNzZXQEbmFtZQUKTEFORFBSRUZJWAkAAgEJAKwCAgkArAICAglPbmx5IE5GVCAFCkxBTkRQUkVGSVgCFCB0b2tlbnMgYXJlIGFjY2VwdGVkBAtsYW5kTnVtU2l6ZQkAsAICCAUFYXNzZXQEbmFtZQAEBAdsYW5kTnVtAwkBCGNvbnRhaW5zAgULbGFuZE51bVNpemUCA1hYTAkAswICBQtsYW5kTnVtU2l6ZQADAwkBCGNvbnRhaW5zAgULbGFuZE51bVNpemUCAlhMCQCzAgIFC2xhbmROdW1TaXplAAIJALMCAgULbGFuZE51bVNpemUAAQQKbGFuZE51bUludAkBE3ZhbHVlT3JFcnJvck1lc3NhZ2UCCQC2CQEFB2xhbmROdW0JAKwCAgIeQ2Fubm90IHBhcnNlIGxhbmQgbnVtYmVyIGZyb20gCAUFYXNzZXQEbmFtZQQLbGFuZEFzc2V0SWQJANgEAQUHYXNzZXRJZAQHdGltZUtleQkBFmtleVN0YWtlZFRpbWVCeUFzc2V0SWQBBQtsYW5kQXNzZXRJZAMJAQlpc0RlZmluZWQBCQCfCAEFB3RpbWVLZXkJAAIBCQCsAgIJAKwCAgIETkZUIAgFBWFzc2V0BG5hbWUCEiBpcyBhbHJlYWR5IHN0YWtlZAQBZAkAtQkCCAUFYXNzZXQLZGVzY3JpcHRpb24CAV8EDXRlcnJhaW5Db3VudHMJAQ1jb3VudFRlcnJhaW5zAQkAkQMCBQFkBQtyZWNUZXJyYWlucwQFcHJvcHMJARF1cGRhdGVQcm9wb3J0aW9ucwMFDXRlcnJhaW5Db3VudHMJAGkCCQEPbnVtUGllY2VzQnlTaXplAQkAkQMCBQFkBQtyZWNMYW5kU2l6ZQAZAAEJAMwIAgkBDEludGVnZXJFbnRyeQIFB3RpbWVLZXkIBQlsYXN0QmxvY2sJdGltZXN0YW1wCQDMCAIJAQxJbnRlZ2VyRW50cnkCCQEia2V5U3Rha2VkVGltZUJ5VHlwZUFzc2V0SWRBbmRPd25lcgMFCkxBTkRQUkVGSVgFC2xhbmRBc3NldElkBQdhZGRyZXNzCAUJbGFzdEJsb2NrCXRpbWVzdGFtcAkAzAgCCQELU3RyaW5nRW50cnkCCQEVa2V5TGFuZEFzc2V0SWRUb093bmVyAQULbGFuZEFzc2V0SWQFB2FkZHJlc3MJAMwIAgkBC1N0cmluZ0VudHJ5AgkBEWtleUxhbmROdW1Ub093bmVyAQUHbGFuZE51bQUHYWRkcmVzcwkAzAgCCQELU3RyaW5nRW50cnkCCQERa2V5UmVzUHJvcG9ydGlvbnMABQVwcm9wcwUDbmlsAWkBC3Vuc3Rha2VMYW5kAQ5sYW5kQXNzZXRJZFN0cgMJAQIhPQIJAJADAQgFAWkIcGF5bWVudHMAAAkAAgECJHVuc3Rha2UgZG9lc24ndCByZXF1aXJlIGFueSBwYXltZW50cwQEYWRkcgkApQgBCAUBaQZjYWxsZXIEAWMJARRjaGVja0NsYWltQ29uZGl0aW9ucwEFBGFkZHIEDXRlcnJhaW5Db3VudHMJAQ1jb3VudFRlcnJhaW5zAQkAkQMCCAUBYwJfMwULcmVjVGVycmFpbnMEBnBpZWNlcwkBD251bVBpZWNlc0J5U2l6ZQEJAJEDAggFAWMCXzMFC3JlY0xhbmRTaXplBAVwcm9wcwkBEXVwZGF0ZVByb3BvcnRpb25zAwUNdGVycmFpbkNvdW50cwkAaQIFBnBpZWNlcwAZAP///////////wEEC2NsYWltUmVzdWx0CQEIY2xhaW1BbGwDBQRhZGRyCAUBYwJfMgUGcGllY2VzCQDMCAIJAQ5TY3JpcHRUcmFuc2ZlcgMIBQFpBmNhbGxlcgABCQDZBAEIBQFjAl8yCQDMCAIJAQtEZWxldGVFbnRyeQEJARZrZXlTdGFrZWRUaW1lQnlBc3NldElkAQgFAWMCXzIJAMwIAgkBC0RlbGV0ZUVudHJ5AQkBImtleVN0YWtlZFRpbWVCeVR5cGVBc3NldElkQW5kT3duZXIDBQpMQU5EUFJFRklYCAUBYwJfMgUEYWRkcgkAzAgCCQELU3RyaW5nRW50cnkCCQERa2V5UmVzUHJvcG9ydGlvbnMABQVwcm9wcwkAzAgCCQELU3RyaW5nRW50cnkCCAULY2xhaW1SZXN1bHQCXzIJALkJAggFC2NsYWltUmVzdWx0Al8zAgE6BQNuaWwBaQEJc3Rha2VEdWNrAAQDcG10CQEFdmFsdWUBCQCRAwIIBQFpCHBheW1lbnRzAAAEB2Fzc2V0SWQJAQV2YWx1ZQEIBQNwbXQHYXNzZXRJZAQHYWRkcmVzcwkApQgBCAUBaQZjYWxsZXIDCQECIT0CCAUDcG10BmFtb3VudAABCQACAQkArAICCQCsAgICBE5GVCAFCkRVQ0tQUkVGSVgCJCB0b2tlbiBzaG91bGQgYmUgYXR0YWNoZWQgYXMgcGF5bWVudAQFYXNzZXQJAQV2YWx1ZQEJAOwHAQUHYXNzZXRJZAMDCQECIT0CCAUFYXNzZXQGaXNzdWVyBQ1pbmN1YmF0b3JBZGRyCQECIT0CCAUFYXNzZXQGaXNzdWVyBQticmVlZGVyQWRkcgcJAAIBCQCsAgIJAKwCAgISVW5rbm93biBpc3N1ZXIgb2YgBQpEVUNLUFJFRklYAgYgdG9rZW4DCQEBIQEJAQhjb250YWlucwIIBQVhc3NldARuYW1lBQpEVUNLUFJFRklYCQACAQkArAICCQCsAgICCU9ubHkgTkZUIAUKRFVDS1BSRUZJWAIUIHRva2VucyBhcmUgYWNjZXB0ZWQECmFzc2V0SWRTdHIJANgEAQUHYXNzZXRJZAQHdGltZUtleQkBFmtleVN0YWtlZFRpbWVCeUFzc2V0SWQBBQphc3NldElkU3RyAwkBCWlzRGVmaW5lZAEJAJ8IAQUHdGltZUtleQkAAgEJAKwCAgkArAICAgRORlQgCAUFYXNzZXQEbmFtZQISIGlzIGFscmVhZHkgc3Rha2VkAwkBCWlzRGVmaW5lZAEJAKIIAQkBFGtleVN0YWtlZER1Y2tCeU93bmVyAQUHYWRkcmVzcwkAAgEJAKwCAgIdWW91IGFscmVhZHkgc3Rha2VkIG9uZSBkdWNrOiAIBQVhc3NldARuYW1lBAZsb2NLZXkJAQ9rZXlEdWNrTG9jYXRpb24BBQphc3NldElkU3RyBAhsb2NhdGlvbgkAoggBBQZsb2NLZXkECWtleUhlYWx0aAkBDWtleUR1Y2tIZWFsdGgBBQphc3NldElkU3RyBAZoZWFsdGgJAJ8IAQUJa2V5SGVhbHRoBAVicEtleQkBEWtleUJhY2twYWNrQnlEdWNrAQUKYXNzZXRJZFN0cgQIYmFja3BhY2sJAKIIAQUFYnBLZXkJAM4IAgkAzAgCCQEMSW50ZWdlckVudHJ5AgUHdGltZUtleQgFCWxhc3RCbG9jawl0aW1lc3RhbXAJAMwIAgkBDEludGVnZXJFbnRyeQIJASJrZXlTdGFrZWRUaW1lQnlUeXBlQXNzZXRJZEFuZE93bmVyAwUKRFVDS1BSRUZJWAkA2AQBBQdhc3NldElkBQdhZGRyZXNzCAUJbGFzdEJsb2NrCXRpbWVzdGFtcAkAzAgCCQELU3RyaW5nRW50cnkCCQEQa2V5RHVja0lkVG9Pd25lcgEFCmFzc2V0SWRTdHIFB2FkZHJlc3MJAMwIAgkBC1N0cmluZ0VudHJ5AgkBFGtleVN0YWtlZER1Y2tCeU93bmVyAQUHYWRkcmVzcwUKYXNzZXRJZFN0cgUDbmlsAwkBCWlzRGVmaW5lZAEFCGxvY2F0aW9uBQNuaWwJAM4IAgkAzAgCCQELU3RyaW5nRW50cnkCBQZsb2NLZXkFD0RFRkFVTFRMT0NBVElPTgUDbmlsAwkBCWlzRGVmaW5lZAEFBmhlYWx0aAUDbmlsCQDOCAIJAMwIAgkBDEludGVnZXJFbnRyeQIFCWtleUhlYWx0aABkBQNuaWwDCQEJaXNEZWZpbmVkAQUIYmFja3BhY2sFA25pbAkAzAgCCQELU3RyaW5nRW50cnkCBQVicEtleQIaMDowXzBfMF8wXzBfMDowXzBfMF8wXzBfMDoFA25pbAFpAQt1bnN0YWtlRHVjawEKYXNzZXRJZFN0cgMJAQIhPQIJAJADAQgFAWkIcGF5bWVudHMAAAkAAgECJHVuc3Rha2UgZG9lc24ndCByZXF1aXJlIGFueSBwYXltZW50cwQHYXNzZXRJZAkA2QQBBQphc3NldElkU3RyBAdhZGRyZXNzCQClCAEIBQFpBmNhbGxlcgQFYXNzZXQJAQV2YWx1ZQEJAOwHAQUHYXNzZXRJZAMDCQECIT0CCAUFYXNzZXQGaXNzdWVyBQ1pbmN1YmF0b3JBZGRyCQECIT0CCAUFYXNzZXQGaXNzdWVyBQticmVlZGVyQWRkcgcJAAIBCQCsAgIJAKwCAgISVW5rbm93biBpc3N1ZXIgb2YgBQpEVUNLUFJFRklYAgYgdG9rZW4DCQEBIQEJAQhjb250YWlucwIIBQVhc3NldARuYW1lBQpEVUNLUFJFRklYCQACAQkArAICCQCsAgICCU9ubHkgTkZUIAUKRFVDS1BSRUZJWAIXIHRva2VucyBjYW4gYmUgdW5zdGFrZWQEB3RpbWVLZXkJARZrZXlTdGFrZWRUaW1lQnlBc3NldElkAQkA2AQBBQdhc3NldElkAwkBASEBCQEJaXNEZWZpbmVkAQUHdGltZUtleQkAAgEJAKwCAgkArAICAgRORlQgCAUFYXNzZXQEbmFtZQIOIGlzIG5vdCBzdGFrZWQDCQEBIQEJAQlpc0RlZmluZWQBCQEUa2V5U3Rha2VkRHVja0J5T3duZXIBBQdhZGRyZXNzCQACAQkArAICCQCsAgICCVRoZSBkdWNrIAgFBWFzc2V0BG5hbWUCDiBpcyBub3Qgc3Rha2VkBAVvd25lcgkBE3ZhbHVlT3JFcnJvck1lc3NhZ2UCCQCiCAEJARBrZXlEdWNrSWRUb093bmVyAQkA2AQBBQdhc3NldElkCQCsAgIJAKwCAgIETkZUIAgFBWFzc2V0BG5hbWUCDCBpcyBvcnBoYW5lZAMJAQIhPQIFBW93bmVyBQdhZGRyZXNzCQACAQIXU3Rha2VkIE5GVCBpcyBub3QgeW91cnMJAMwIAgkBDlNjcmlwdFRyYW5zZmVyAwgFAWkGY2FsbGVyAAEFB2Fzc2V0SWQJAMwIAgkBC0RlbGV0ZUVudHJ5AQUHdGltZUtleQkAzAgCCQELRGVsZXRlRW50cnkBCQEPa2V5RHVja0xvY2F0aW9uAQUKYXNzZXRJZFN0cgkAzAgCCQELRGVsZXRlRW50cnkBCQEQa2V5RHVja0lkVG9Pd25lcgEFCmFzc2V0SWRTdHIJAMwIAgkBC0RlbGV0ZUVudHJ5AQkBImtleVN0YWtlZFRpbWVCeVR5cGVBc3NldElkQW5kT3duZXIDBQpEVUNLUFJFRklYBQphc3NldElkU3RyBQdhZGRyZXNzCQDMCAIJAQtEZWxldGVFbnRyeQEJARRrZXlTdGFrZWREdWNrQnlPd25lcgEFB2FkZHJlc3MFA25pbAFpAQhjbGFpbVJlcwIGYW1vdW50DmxhbmRBc3NldElkU3RyAwkBAiE9AgkAkAMBCAUBaQhwYXltZW50cwAACQACAQIlY2xhaW1SZXMgZG9lc24ndCByZXF1aXJlIGFueSBwYXltZW50cwQEYWRkcgkApQgBCAUBaQxvcmlnaW5DYWxsZXIEBnJlc3VsdAkBEGNsYWltUmVzSW50ZXJuYWwCBQRhZGRyBQZhbW91bnQJAJQKAgkAzQgCCAUGcmVzdWx0Al8xCQELU3RyaW5nRW50cnkCCAUGcmVzdWx0Al8yCQC5CQIIBQZyZXN1bHQCXzMCAToJAJEDAggFBnJlc3VsdAJfMwUIYnBJZHhSZXMBaQEGZmxpZ2h0AgdtZXNzYWdlA3NpZwMJAQEhAQkAxBMDBQdtZXNzYWdlBQNzaWcFA3B1YgkAAgECGHNpZ25hdHVyZSBkb2VzIG5vdCBtYXRjaAMJAQIhPQIJAJADAQgFAWkIcGF5bWVudHMAAAkAAgECI2ZsaWdodCBkb2Vzbid0IHJlcXVpcmUgYW55IHBheW1lbnRzBAVwYXJ0cwkAtQkCCQCwCQEFB21lc3NhZ2UCATsEAmhwCQC1CQIJAJEDAgkAtQkCCQCRAwIFBXBhcnRzAAACAXwAAAIBXwQFY3VySFAJAQ1wYXJzZUludFZhbHVlAQkAkQMCBQJocAAABAVuZXdIUAkBDXBhcnNlSW50VmFsdWUBCQCRAwIFAmhwAAEEDW5ld0xvY0FuZFRpbWUJALUJAgkAkQMCBQVwYXJ0cwABAgE6BAtuZXdMb2NhdGlvbgkAkQMCBQ1uZXdMb2NBbmRUaW1lAAAEBHRpbWUJAQ1wYXJzZUludFZhbHVlAQkAkQMCBQ1uZXdMb2NBbmRUaW1lAAEDAwkAZgIFBHRpbWUJAGQCCAUJbGFzdEJsb2NrCXRpbWVzdGFtcAURRklWRU1JTlVURVNNSUxMSVMGCQBmAgkAZQIIBQlsYXN0QmxvY2sJdGltZXN0YW1wBRFGSVZFTUlOVVRFU01JTExJUwUEdGltZQkAAgECEnNpZ25hdHVyZSBvdXRkYXRlZAQLZHVja0Fzc2V0SWQJARN2YWx1ZU9yRXJyb3JNZXNzYWdlAgkAoggBCQEUa2V5U3Rha2VkRHVja0J5T3duZXIBCQClCAEIBQFpBmNhbGxlcgIcWW91IGRvbid0IGhhdmUgYSBkdWNrIHN0YWtlZAQJa2V5SGVhbHRoCQENa2V5RHVja0hlYWx0aAEFC2R1Y2tBc3NldElkBAxvbGRGcm9tU3RhdGUJAQt2YWx1ZU9yRWxzZQIJAJ8IAQUJa2V5SGVhbHRoAGQDCQECIT0CBQxvbGRGcm9tU3RhdGUFBWN1ckhQCQACAQkArAICCQCsAgIJAKwCAgIKb2xkSGVhbHRoPQkApAMBCQELdmFsdWVPckVsc2UCCQCfCAEFCWtleUhlYWx0aABkAi8gZnJvbSBzdGF0ZSBkb2VzIG5vdCBtYXRjaCBvbmUgZnJvbSBmbGlnaHQgbG9nPQkApAMBBQVjdXJIUAMJAGcCAAAFBWN1ckhQCQACAQIeWW91IGNhbid0IGZseSB3aXRoIHplcm8gaGVhbHRoBAZsb2NLZXkJAQ9rZXlEdWNrTG9jYXRpb24BBQtkdWNrQXNzZXRJZAQLY3VyTG9jYXRpb24JAQt2YWx1ZU9yRWxzZQIJAKIIAQUGbG9jS2V5BQ9ERUZBVUxUTE9DQVRJT04DCQAAAgULbmV3TG9jYXRpb24FC2N1ckxvY2F0aW9uCQACAQIiWW91IGNhbid0IGZseSB0byB0aGUgc2FtZSBsb2NhdGlvbgkAlAoCCQDMCAIJAQtTdHJpbmdFbnRyeQIFBmxvY0tleQMJAGYCBQVuZXdIUAAABQtuZXdMb2NhdGlvbgULY3VyTG9jYXRpb24JAMwIAgkBDEludGVnZXJFbnRyeQIFCWtleUhlYWx0aAUFbmV3SFAFA25pbAUEdW5pdAFpAQlzZXRIZWFsdGgCBmhlYWx0aAtkdWNrQXNzZXRJZAMDCQBmAgAABQZoZWFsdGgGCQBmAgUGaGVhbHRoAGQJAAIBAhpIUCBzaG91bGQgYmUgd2l0aGluIDAuLjEwMAkAzAgCCQEMSW50ZWdlckVudHJ5AgkBDWtleUR1Y2tIZWFsdGgBBQtkdWNrQXNzZXRJZAUGaGVhbHRoBQNuaWwBaQEEaGVhbAIHbWF0VHlwZQZhbW91bnQDAwkAZgIAAAUHbWF0VHlwZQYJAGcCBQdtYXRUeXBlBQZOVU1SRVMJAAIBCQCsAgICElVua25vd24gbWF0ZXJpYWw6IAkApAMBBQdtYXRUeXBlAwkAZwIAAAUGYW1vdW50CQACAQkArAICAhtBbW91bnQgc2hvdWxkIGJlIHBvc2l0aXZlISAJAKQDAQUGYW1vdW50BAtkdWNrQXNzZXRJZAkBE3ZhbHVlT3JFcnJvck1lc3NhZ2UCCQCiCAEJARRrZXlTdGFrZWREdWNrQnlPd25lcgEJAKUIAQgFAWkGY2FsbGVyAhxZb3UgZG9uJ3QgaGF2ZSBhIGR1Y2sgc3Rha2VkBAlrZXlIZWFsdGgJAQ1rZXlEdWNrSGVhbHRoAQULZHVja0Fzc2V0SWQECW9sZEhlYWx0aAkBC3ZhbHVlT3JFbHNlAgkAnwgBBQlrZXlIZWFsdGgAZAMJAGcCBQlvbGRIZWFsdGgAZAkAAgECGkhQIHNob3VsZCBiZSA8IDEwMCB0byBoZWFsBAVicEtleQkBEWtleUJhY2twYWNrQnlEdWNrAQULZHVja0Fzc2V0SWQEC2N1cnJlbnRQYWNrCQELZ2V0QmFja3BhY2sBBQVicEtleQQFbUxpc3QJALUJAgkAkQMCBQtjdXJyZW50UGFjawUIYnBJZHhNYXQCAV8EDWN1cnJlbnRBbW91bnQJAQ1wYXJzZUludFZhbHVlAQkAkQMCBQVtTGlzdAUHbWF0VHlwZQQLZGVsdGFIZWFsdGgJAJcDAQkAzAgCCQBpAgUGYW1vdW50BQhIRUFMQ09TVAkAzAgCCQBlAgBkBQlvbGRIZWFsdGgFA25pbAQLc3BlbmRBbW91bnQJAGgCBQtkZWx0YUhlYWx0aAUISEVBTENPU1QDCQBmAgULc3BlbmRBbW91bnQFDWN1cnJlbnRBbW91bnQJAAIBCQCsAgIJAKwCAgkArAICCQCsAgIJAKwCAgIJWW91IG5lZWQgCQCkAwEFC3NwZW5kQW1vdW50AgQgb2YgCQCRAwIFCG1hdFR5cGVzBQdtYXRUeXBlAiQgdG8gaGVhbCwgYnV0IHlvdSBiYWNrcGFjayBjb250YWlucyAJAKQDAQUNY3VycmVudEFtb3VudAQGbmV3TWF0CQEMc3ViT25lSW5MaXN0AwUFbUxpc3QFB21hdFR5cGUFC3NwZW5kQW1vdW50CQDMCAIJAQxJbnRlZ2VyRW50cnkCBQlrZXlIZWFsdGgJAGQCBQlvbGRIZWFsdGgFC2RlbHRhSGVhbHRoCQDMCAIJAQtTdHJpbmdFbnRyeQIFBWJwS2V5CQC5CQIJAMwIAgkAkQMCBQtjdXJyZW50UGFjawUKYnBJZHhMZXZlbAkAzAgCCQCRAwIFC2N1cnJlbnRQYWNrBQhicElkeFJlcwkAzAgCBQZuZXdNYXQJAMwIAgkAkQMCBQtjdXJyZW50UGFjawUJYnBJZHhQcm9kBQNuaWwCAToFA25pbAFpAQ51cGRhdGVCYWNrcGFjawILZHVja0Fzc2V0SWQHbmV3UGFjawMJAQIhPQIIBQFpBmNhbGxlcgULZWNvbm9teUFkZHIJAAIBAhFwZXJtaXNzaW9uIGRlbmllZAkAlAoCCQDMCAIJAQtTdHJpbmdFbnRyeQIJARFrZXlCYWNrcGFja0J5RHVjawEFC2R1Y2tBc3NldElkBQduZXdQYWNrBQNuaWwFB25ld1BhY2sBaQENZXhwZWRpdGlvbkJ1eQIHbWVzc2FnZQNzaWcDCQECIT0CCQCQAwEIBQFpCHBheW1lbnRzAAEJAAIBAhxFeGFjdGx5IG9uZSBwYXltZW50IHJlcXVpcmVkBANwbXQJAQV2YWx1ZQEJAJEDAggFAWkIcGF5bWVudHMAAAMJAQIhPQIIBQNwbXQHYXNzZXRJZAULdXNkbkFzc2V0SWQJAAIBAhpBbGxvd2VkIFVTRE4gcGF5bWVudCBvbmx5IQMJAQIhPQIIBQNwbXQGYW1vdW50BQdFWFBVU0ROCQACAQkArAICAhtQYXltZW50IGF0dGFjaGVkIHNob3VsZCBiZSAJAKQDAQUHRVhQVVNETgkBEGV4cGVkaXRpb25Db21tb24FBwgFAWkGY2FsbGVyCAUBaQ10cmFuc2FjdGlvbklkBQdtZXNzYWdlBQNzaWcBaQEKZXhwZWRpdGlvbgIHbWVzc2FnZQNzaWcDCQECIT0CCQCQAwEIBQFpCHBheW1lbnRzAAAJAAIBAidleHBlZGl0aW9uIGRvZXNuJ3QgcmVxdWlyZSBhbnkgcGF5bWVudHMJARBleHBlZGl0aW9uQ29tbW9uBQYIBQFpBmNhbGxlcggFAWkNdHJhbnNhY3Rpb25JZAUHbWVzc2FnZQUDc2lnAWkBDHVwZ3JhZGVJbmZyYQESbGFuZEFzc2V0SWRJZ25vcmVkAwkBAiE9AgkAkAMBCAUBaQhwYXltZW50cwAACQACAQIzSW5mcmFzdHJ1Y3R1cmUgdXBncmFkZSBkb2Vzbid0IHJlcXVpcmUgYW55IHBheW1lbnRzCQENdXBJbmZyYUNvbW1vbgMGCAUBaQZjYWxsZXIAAAFpARB1cGdyYWRlSW5mcmFVc2RuARJsYW5kQXNzZXRJZElnbm9yZWQDCQECIT0CCQCQAwEIBQFpCHBheW1lbnRzAAEJAAIBAhxFeGFjdGx5IG9uZSBwYXltZW50IHJlcXVpcmVkBANwbXQJAQV2YWx1ZQEJAJEDAggFAWkIcGF5bWVudHMAAAMJAQIhPQIIBQNwbXQHYXNzZXRJZAULdXNkbkFzc2V0SWQJAAIBAhpBbGxvd2VkIFVTRE4gcGF5bWVudCBvbmx5IQkBDXVwSW5mcmFDb21tb24DBwgFAWkGY2FsbGVyCAUDcG10BmFtb3VudAFpARBhY3RpdmF0ZUFydGlmYWN0AQdhcnROYW1lAwkBAiE9AgkAkAMBCAUBaQhwYXltZW50cwAACQACAQIwQXJ0aWZhY3QgYWN0aXZhdGlvbiBkb2Vzbid0IHJlcXVpcmUgYW55IHBheW1lbnRzBAZyZXN1bHQEByRtYXRjaDAFB2FydE5hbWUDCQAAAgIHUFJFU0FMRQUHJG1hdGNoMAkBEmFjdGl2YXRlUHJlc2FsZUFydAEJAKUIAQgFAWkGY2FsbGVyCQACAQIQVW5rbm93biBhcnRpZmFjdAUGcmVzdWx0ALZTV/A=", "height": 3461520, "applicationStatus": "succeeded", "spentComplexity": 0 } View: original | compacted Prev: HozYJ6nZuPwgCatjc9M6X8sExodgnuHqDapd2vThuT8S Next: 9oK4dAXom2dQLD8AQ3YJqf7Pj2NJdBTgBpXdUdC3Cwna Diff:
OldNewDifferences
5959
6060 let RESOURCEPRICEMIN = 158549
6161
62+let InfraUpgradeCostS = match chain {
63+ case _ =>
64+ if (("W" == $match0))
65+ then 18921595217
66+ else if (("T" == $match0))
67+ then 189215952
68+ else throw("Unknown chain")
69+}
70+
71+let InfraUpgradeCostSUsdn = match chain {
72+ case _ =>
73+ if (("W" == $match0))
74+ then 120000000
75+ else if (("T" == $match0))
76+ then 1200000
77+ else throw("Unknown chain")
78+}
79+
6280 let EXPMATERIALS = match chain {
6381 case _ =>
6482 if (("W" == $match0))
116134 func keyInfraLevelByAssetId (assetId) = ("infraLevel_" + assetId)
117135
118136
137+func keyInfraLevelByAssetIdAndOwner (assetId,ownerAddr) = ((("infraLevelByAssetIdAndOwner_" + assetId) + "_") + ownerAddr)
138+
139+
140+func keyPresaleArtActivatedByAssetId (assetId) = ("presaleArtActivated_" + assetId)
141+
142+
143+func keyPresaleArtActivatedByAssetIdAndOwner (assetId,ownerAddr) = ((("presaleArtActivatedByAssetIdAndOwner_" + assetId) + "_") + ownerAddr)
144+
145+
119146 func keyStakedDuckByOwner (ownerAddr) = ("stakedDuckByOwner_" + ownerAddr)
120147
121148
158185 let bpIdxMat = 2
159186
160187 let bpIdxProd = 3
188+
189+func asString (v) = match v {
190+ case s: String =>
191+ s
192+ case _ =>
193+ throw("fail to cast into String")
194+}
195+
161196
162197 func getNeededMaterials (total) = {
163198 let props = split(value(getString(keyResProportions())), "_")
290325 }
291326
292327
293-func addRes (currentRes,terrainCounts,deltaTime,landSizeIndex) = {
328+func addRes (currentRes,terrainCounts,deltaTime,landSizeIndex,dailyByPieceWithBonuses) = {
294329 func adder (acc,i) = {
295- let resOfType = ((fraction(deltaTime, DAILYRESBYPIECE, DAYMILLIS) * terrainCounts[i]) * landSizeIndex)
330+ let resOfType = ((fraction(deltaTime, dailyByPieceWithBonuses, DAYMILLIS) * terrainCounts[i]) * landSizeIndex)
296331 (acc :+ toString((parseIntValue(currentRes[i]) + resOfType)))
297332 }
298333
375410 else if ((0 >= curHP))
376411 then throw("You can't fly with zero health")
377412 else if ((0 >= newHP))
378- then throw("Your duck health is zero, expedition failed")
413+ then $Tuple2(((if (!(shouldUseMat))
414+ then [ScriptTransfer(caller, EXPUSDN, usdnAssetId)]
415+ else nil) :+ IntegerEntry(keyHealth, 0)), "")
379416 else {
380417 let bpKey = keyBackpackByDuck(duckAssetId)
381418 let currentPack = getBackpack(bpKey)
406443 let issue = Issue(keyNftName(landNum, "S"), makeString([landNum, "S", t._1, continent], "_"), 1, 0, false)
407444 let assetId = calculateAssetId(issue)
408445 let id = toBase58String(assetId)
409- $Tuple2([IntegerEntry(keyNextFreeLandNum(), (freeNum + 1)), issue, StringEntry(keyLandToAssetId(landNum), id), StringEntry(keyLandAssetIdToOwner(id), userAddr), StringEntry(keyLandNumToOwner(landNum), userAddr), IntegerEntry(keyInfraLevelByAssetId(id), 0), ScriptTransfer(caller, 1, assetId), StringEntry(keyDuckLocation(duckAssetId), makeString([continent, "L", id], "_")), IntegerEntry(keyHealth, newHP), StringEntry(bpKey, makeString([currentPack[bpIdxLevel], currentPack[bpIdxRes], newMat, currentPack[bpIdxProd]], ":"))], unit)
446+ $Tuple2([IntegerEntry(keyNextFreeLandNum(), (freeNum + 1)), issue, StringEntry(keyLandToAssetId(landNum), id), StringEntry(keyLandAssetIdToOwner(id), userAddr), StringEntry(keyLandNumToOwner(landNum), userAddr), IntegerEntry(keyInfraLevelByAssetId(id), 0), IntegerEntry(keyInfraLevelByAssetIdAndOwner(id, userAddr), 0), ScriptTransfer(caller, 1, assetId), StringEntry(keyDuckLocation(duckAssetId), makeString([continent, "L", id], "_")), IntegerEntry(keyHealth, newHP), StringEntry(bpKey, makeString([currentPack[bpIdxLevel], currentPack[bpIdxRes], newMat, currentPack[bpIdxProd]], ":"))], id)
410447 }
411448 }
412449 }
413450 }
451+
452+
453+func applyBonuses (landAssetId) = {
454+ let infraLevel = valueOrElse(getInteger(keyInfraLevelByAssetId(landAssetId)), 0)
455+ let presaleBonus = if (valueOrElse(getBoolean(keyPresaleArtActivatedByAssetId(landAssetId)), false))
456+ then 3
457+ else 0
458+ ((DAILYRESBYPIECE + fraction(DAILYRESBYPIECE, infraLevel, 4)) + fraction(DAILYRESBYPIECE, presaleBonus, 20))
459+ }
460+
461+
462+func checkClaimConditions (addr) = {
463+ let duckAssetId = valueOrErrorMessage(getString(keyStakedDuckByOwner(addr)), "You don't have a duck staked")
464+ let curLocation = valueOrElse(getString(keyDuckLocation(duckAssetId)), DEFAULTLOCATION)
465+ let loc = split(value(curLocation), "_")
466+ if ((loc[locIdxType] != "L"))
467+ then throw((("Duck location type is " + loc[locIdxType]) + ", but should be L"))
468+ else {
469+ let landAssetId = loc[locIdxId]
470+ let asset = value(assetInfo(fromBase58String(landAssetId)))
471+ let timeKey = keyStakedTimeByAssetId(landAssetId)
472+ let savedTime = valueOrErrorMessage(getInteger(timeKey), (("NFT " + asset.name) + " is not staked"))
473+ let owner = valueOrErrorMessage(getString(keyLandAssetIdToOwner(landAssetId)), (("NFT " + asset.name) + " is orphaned"))
474+ if ((owner != addr))
475+ then throw((LANDPREFIX + " is not yours"))
476+ else {
477+ let d = split(asset.description, "_")
478+ $Tuple4(duckAssetId, landAssetId, d, savedTime)
479+ }
480+ }
481+ }
482+
483+
484+func claimResInternal (addr,amount) = {
485+ let c = checkClaimConditions(addr)
486+ let landSize = c._3[recLandSize]
487+ let terrainCounts = countTerrains(c._3[recTerrains])
488+ let deltaTime = (lastBlock.timestamp - c._4)
489+ if ((0 > deltaTime))
490+ then throw(((("Saved timestamp is in future, saved = " + toString(c._4)) + ", current = ") + toString(lastBlock.timestamp)))
491+ else {
492+ let pieces = numPiecesBySize(landSize)
493+ let dailyProductionByPiece = applyBonuses(c._2)
494+ let availRes = fraction(deltaTime, (dailyProductionByPiece * pieces), DAYMILLIS)
495+ if ((amount > availRes))
496+ then throw(((("Not enough resources, available = " + toString(availRes)) + ", requested = ") + toString(amount)))
497+ else {
498+ let newDeltaTime = fraction((availRes - amount), DAYMILLIS, (dailyProductionByPiece * pieces))
499+ let newTimestamp = (lastBlock.timestamp - newDeltaTime)
500+ let bpKey = keyBackpackByDuck(c._1)
501+ let currentPack = getBackpack(bpKey)
502+ let currentRes = split(currentPack[bpIdxRes], "_")
503+ let bpRes = addRes(currentRes, terrainCounts, (deltaTime - newDeltaTime), (pieces / 25), dailyProductionByPiece)
504+ $Tuple3([IntegerEntry(keyStakedTimeByAssetId(c._2), newTimestamp), IntegerEntry(keyStakedTimeByTypeAssetIdAndOwner(LANDPREFIX, c._2, addr), newTimestamp)], bpKey, [currentPack[bpIdxLevel], bpRes, currentPack[bpIdxMat], currentPack[bpIdxProd]])
505+ }
506+ }
507+ }
508+
509+
510+func claimAll (addr,landAssetId,pieces) = {
511+ let timeKey = keyStakedTimeByAssetId(landAssetId)
512+ let savedTime = value(getInteger(timeKey))
513+ let availRes = (fraction((lastBlock.timestamp - savedTime), applyBonuses(landAssetId), DAYMILLIS) * pieces)
514+ claimResInternal(addr, availRes)
515+ }
516+
517+
518+func upInfraCommon (shouldUseMat,caller,paymentAmount) = {
519+ let addr = toString(caller)
520+ let c = checkClaimConditions(addr)
521+ let pieces = numPiecesBySize(c._3[recLandSize])
522+ let infraKey = keyInfraLevelByAssetId(c._2)
523+ let curLevel = valueOrElse(getInteger(infraKey), 0)
524+ if ((curLevel >= 3))
525+ then throw("Currently max infrastructure level = 3")
526+ else {
527+ let newLevel = (curLevel + 1)
528+ let cost = fraction(InfraUpgradeCostSUsdn, (pieces * newLevel), 25)
529+ if (if (!(shouldUseMat))
530+ then (paymentAmount != cost)
531+ else false)
532+ then throw(("Payment attached should be " + toString(cost)))
533+ else {
534+ let bpKey = keyBackpackByDuck(c._1)
535+ let currentPack = getBackpack(bpKey)
536+ let mList = split(currentPack[bpIdxMat], "_")
537+ let newMat = makeString(subtractMaterials(shouldUseMat, mList, fraction(InfraUpgradeCostS, (pieces * newLevel), 25)), "_")
538+ let claimResult = claimAll(addr, c._2, pieces)
539+ $Tuple2(([IntegerEntry(infraKey, newLevel), IntegerEntry(keyInfraLevelByAssetIdAndOwner(c._2, addr), newLevel), StringEntry(bpKey, makeString([currentPack[bpIdxLevel], claimResult._3[bpIdxRes], newMat, currentPack[bpIdxProd]], ":"))] ++ claimResult._1), newLevel)
540+ }
541+ }
542+ }
543+
544+
545+func activatePresaleArt (addr) = {
546+ let c = checkClaimConditions(addr)
547+ let activationKey = keyPresaleArtActivatedByAssetId(c._2)
548+ if (valueOrElse(getBoolean(activationKey), false))
549+ then throw("Presale artifact is already activated")
550+ else if ((parseIntValue(c._3[recLandNum]) > 500))
551+ then throw((((LANDPREFIX + " ") + c._2) + " is not eligible for presale artifact"))
552+ else {
553+ let pieces = numPiecesBySize(c._3[recLandSize])
554+ let claimResult = claimAll(addr, c._2, pieces)
555+ (((claimResult._1 :+ BooleanEntry(keyPresaleArtActivatedByAssetId(c._2), true)) :+ BooleanEntry(keyPresaleArtActivatedByAssetIdAndOwner(c._2, addr), true)) :+ StringEntry(claimResult._2, makeString(claimResult._3, ":")))
556+ }
557+ }
414558
415559
416560 @Callable(i)
433577 else if (contains(landNumSize, "XL"))
434578 then dropRight(landNumSize, 2)
435579 else dropRight(landNumSize, 1)
436- if (!(isDefined(parseInt(landNum))))
437- then throw(("Cannot parse land number from " + asset.name))
580+ let landNumInt = valueOrErrorMessage(parseInt(landNum), ("Cannot parse land number from " + asset.name))
581+ let landAssetId = toBase58String(assetId)
582+ let timeKey = keyStakedTimeByAssetId(landAssetId)
583+ if (isDefined(getInteger(timeKey)))
584+ then throw((("NFT " + asset.name) + " is already staked"))
438585 else {
439- let timeKey = keyStakedTimeByAssetId(toBase58String(assetId))
440- if (isDefined(getInteger(timeKey)))
441- then throw((("NFT " + asset.name) + " is already staked"))
442- else {
443- let d = split(asset.description, "_")
444- let terrainCounts = countTerrains(d[recTerrains])
445- let props = updateProportions(terrainCounts, (numPiecesBySize(d[recLandSize]) / 25), 1)
446-[IntegerEntry(timeKey, lastBlock.timestamp), IntegerEntry(keyStakedTimeByTypeAssetIdAndOwner(LANDPREFIX, toBase58String(assetId), address), lastBlock.timestamp), StringEntry(keyLandAssetIdToOwner(toBase58String(assetId)), address), StringEntry(keyLandNumToOwner(landNum), address), StringEntry(keyResProportions(), props)]
447- }
586+ let d = split(asset.description, "_")
587+ let terrainCounts = countTerrains(d[recTerrains])
588+ let props = updateProportions(terrainCounts, (numPiecesBySize(d[recLandSize]) / 25), 1)
589+[IntegerEntry(timeKey, lastBlock.timestamp), IntegerEntry(keyStakedTimeByTypeAssetIdAndOwner(LANDPREFIX, landAssetId, address), lastBlock.timestamp), StringEntry(keyLandAssetIdToOwner(landAssetId), address), StringEntry(keyLandNumToOwner(landNum), address), StringEntry(keyResProportions(), props)]
448590 }
449591 }
450592 }
453595
454596
455597 @Callable(i)
456-func unstakeLand (landAssetId) = if ((size(i.payments) != 0))
598+func unstakeLand (landAssetIdStr) = if ((size(i.payments) != 0))
457599 then throw("unstake doesn't require any payments")
458600 else {
459- let assetId = fromBase58String(landAssetId)
460- let address = toString(i.caller)
461- let asset = value(assetInfo(assetId))
462- if ((asset.issuer != this))
463- then throw("Unknown issuer of token")
464- else if (!(contains(asset.name, LANDPREFIX)))
465- then throw((("Only NFT " + LANDPREFIX) + " tokens can be unstaked"))
466- else {
467- let timeKey = keyStakedTimeByAssetId(landAssetId)
468- if (!(isDefined(timeKey)))
469- then throw((("NFT " + asset.name) + " is not staked"))
470- else {
471- let owner = valueOrErrorMessage(getString(keyLandAssetIdToOwner(landAssetId)), (("NFT " + asset.name) + " is orphaned"))
472- if ((owner != address))
473- then throw("Staked NFT is not yours")
474- else {
475- let d = split(asset.description, "_")
476- let terrainCounts = countTerrains(d[recTerrains])
477- let props = updateProportions(terrainCounts, (numPiecesBySize(d[recLandSize]) / 25), -1)
478-[ScriptTransfer(i.caller, 1, assetId), DeleteEntry(timeKey), DeleteEntry(keyStakedTimeByTypeAssetIdAndOwner(LANDPREFIX, landAssetId, address)), StringEntry(keyResProportions(), props)]
479- }
480- }
481- }
601+ let addr = toString(i.caller)
602+ let c = checkClaimConditions(addr)
603+ let terrainCounts = countTerrains(c._3[recTerrains])
604+ let pieces = numPiecesBySize(c._3[recLandSize])
605+ let props = updateProportions(terrainCounts, (pieces / 25), -1)
606+ let claimResult = claimAll(addr, c._2, pieces)
607+[ScriptTransfer(i.caller, 1, fromBase58String(c._2)), DeleteEntry(keyStakedTimeByAssetId(c._2)), DeleteEntry(keyStakedTimeByTypeAssetIdAndOwner(LANDPREFIX, c._2, addr)), StringEntry(keyResProportions(), props), StringEntry(claimResult._2, makeString(claimResult._3, ":"))]
482608 }
483609
484610
549675 let owner = valueOrErrorMessage(getString(keyDuckIdToOwner(toBase58String(assetId))), (("NFT " + asset.name) + " is orphaned"))
550676 if ((owner != address))
551677 then throw("Staked NFT is not yours")
552- else [ScriptTransfer(i.caller, 1, assetId), DeleteEntry(timeKey), DeleteEntry(keyDuckLocation(assetIdStr)), DeleteEntry(keyStakedTimeByTypeAssetIdAndOwner(DUCKPREFIX, assetIdStr, address)), DeleteEntry(keyStakedDuckByOwner(address))]
678+ else [ScriptTransfer(i.caller, 1, assetId), DeleteEntry(timeKey), DeleteEntry(keyDuckLocation(assetIdStr)), DeleteEntry(keyDuckIdToOwner(assetIdStr)), DeleteEntry(keyStakedTimeByTypeAssetIdAndOwner(DUCKPREFIX, assetIdStr, address)), DeleteEntry(keyStakedDuckByOwner(address))]
553679 }
554680 }
555681 }
557683
558684
559685 @Callable(i)
560-func claimRes (amount,landAssetId) = if ((size(i.payments) != 0))
686+func claimRes (amount,landAssetIdStr) = if ((size(i.payments) != 0))
561687 then throw("claimRes doesn't require any payments")
562688 else {
563- let addr = toString(i.caller)
564- let asset = value(assetInfo(fromBase58String(landAssetId)))
565- if (!(contains(asset.name, LANDPREFIX)))
566- then throw((("NFT " + LANDPREFIX) + " token should be passed as param"))
567- else {
568- let timeKey = keyStakedTimeByAssetId(landAssetId)
569- let savedTime = getInteger(timeKey)
570- if (!(isDefined(savedTime)))
571- then throw((("NFT " + asset.name) + " is not staked"))
572- else {
573- let owner = getStringValue(keyLandAssetIdToOwner(landAssetId))
574- if ((owner != addr))
575- then throw((LANDPREFIX + " is not yours"))
576- else {
577- let d = split(asset.description, "_")
578- let landSize = d[recLandSize]
579- let terrainCounts = countTerrains(d[recTerrains])
580- let duck = getString(keyStakedDuckByOwner(addr))
581- if (!(isDefined(duck)))
582- then throw("You don't have a duck staked")
583- else {
584- let duckAssetIdStr = value(duck)
585- let curLocation = valueOrElse(getString(keyDuckLocation(duckAssetIdStr)), DEFAULTLOCATION)
586- let loc = split(value(curLocation), "_")
587- if ((loc[locIdxType] != "L"))
588- then throw((("Duck location type is " + loc[locIdxType]) + ", but should be L"))
589- else if ((loc[locIdxId] != landAssetId))
590- then throw(((("Duck location id is " + loc[locIdxId]) + ", but should be ") + landAssetId))
591- else {
592- let deltaTime = (lastBlock.timestamp - value(savedTime))
593- if ((0 > deltaTime))
594- then throw(((("Saved timestamp is in future, saved = " + toString(value(savedTime))) + ", current = ") + toString(lastBlock.timestamp)))
595- else {
596- let pieces = numPiecesBySize(landSize)
597- let availRes = (fraction(deltaTime, DAILYRESBYPIECE, DAYMILLIS) * pieces)
598- if ((amount > availRes))
599- then throw(((("Not enough resources, available = " + toString(availRes)) + ", requested = ") + toString(amount)))
600- else {
601- let newDeltaTime = fraction((availRes - amount), DAYMILLIS, (pieces * DAILYRESBYPIECE))
602- let newTimestamp = (lastBlock.timestamp - newDeltaTime)
603- let bpKey = keyBackpackByDuck(duckAssetIdStr)
604- let currentPack = getBackpack(bpKey)
605- let currentRes = split(currentPack[bpIdxRes], "_")
606- let bpRes = addRes(currentRes, terrainCounts, (deltaTime - newDeltaTime), (pieces / 25))
607- let newPack = makeString([currentPack[bpIdxLevel], bpRes, currentPack[bpIdxMat], currentPack[bpIdxProd]], ":")
608- $Tuple2([StringEntry(bpKey, newPack), IntegerEntry(timeKey, newTimestamp), IntegerEntry(keyStakedTimeByTypeAssetIdAndOwner(LANDPREFIX, landAssetId, owner), newTimestamp)], unit)
609- }
610- }
611- }
612- }
613- }
614- }
615- }
689+ let addr = toString(i.originCaller)
690+ let result = claimResInternal(addr, amount)
691+ $Tuple2((result._1 :+ StringEntry(result._2, makeString(result._3, ":"))), result._3[bpIdxRes])
616692 }
617693
618694
725801
726802
727803 @Callable(i)
728-func upgradeInfra (landAssetId) = if ((i.caller != this))
729- then throw("temporary disabled")
730- else if ((size(i.payments) != 0))
731- then throw("Infrastructure upgrading doesn't require any payments")
732- else {
733- let infraKey = keyInfraLevelByAssetId(landAssetId)
734- let curLevel = valueOrElse(getInteger(infraKey), 0)
735- if ((curLevel >= 3))
736- then throw("Currently max infrastructure level is 3")
737- else {
738- let newLevel = (curLevel + 1)
739- $Tuple2([IntegerEntry(infraKey, newLevel)], newLevel)
740- }
741- }
804+func upgradeInfra (landAssetIdIgnored) = if ((size(i.payments) != 0))
805+ then throw("Infrastructure upgrade doesn't require any payments")
806+ else upInfraCommon(true, i.caller, 0)
807+
808+
809+
810+@Callable(i)
811+func upgradeInfraUsdn (landAssetIdIgnored) = if ((size(i.payments) != 1))
812+ then throw("Exactly one payment required")
813+ else {
814+ let pmt = value(i.payments[0])
815+ if ((pmt.assetId != usdnAssetId))
816+ then throw("Allowed USDN payment only!")
817+ else upInfraCommon(false, i.caller, pmt.amount)
818+ }
819+
820+
821+
822+@Callable(i)
823+func activateArtifact (artName) = if ((size(i.payments) != 0))
824+ then throw("Artifact activation doesn't require any payments")
825+ else {
826+ let result = match artName {
827+ case _ =>
828+ if (("PRESALE" == $match0))
829+ then activatePresaleArt(toString(i.caller))
830+ else throw("Unknown artifact")
831+ }
832+ result
833+ }
742834
743835
Full:
OldNewDifferences
11 {-# STDLIB_VERSION 6 #-}
22 {-# SCRIPT_TYPE ACCOUNT #-}
33 {-# CONTENT_TYPE DAPP #-}
44 let chain = toUtf8String(take(drop(this.bytes, 1), 1))
55
66 let usdnAssetId = match chain {
77 case _ =>
88 if (("W" == $match0))
99 then base58'DG2xFkPdDwKUoBkzGAhQtLpSGzfXLiCYPEzeKH2Ad24p'
1010 else if (("T" == $match0))
1111 then base58'HezsdQuRDtzksAYUy97gfhKy7Z1NW2uXYSHA3bgqenNZ'
1212 else throw("Unknown chain")
1313 }
1414
1515 let incubatorAddr = match chain {
1616 case _ =>
1717 if (("W" == $match0))
1818 then addressFromStringValue("3PEktVux2RhchSN63DsDo4b4mz4QqzKSeDv")
1919 else if (("T" == $match0))
2020 then this
2121 else throw("Unknown chain")
2222 }
2323
2424 let breederAddr = match chain {
2525 case _ =>
2626 if (("W" == $match0))
2727 then addressFromStringValue("3PDVuU45H7Eh5dmtNbnRNRStGwULA7NY6Hb")
2828 else if (("T" == $match0))
2929 then this
3030 else throw("Unknown chain")
3131 }
3232
3333 let economyAddr = match chain {
3434 case _ =>
3535 if (("W" == $match0))
3636 then addressFromStringValue("3P2sk1KncSxRaZs8b4CWGPw2jkvvav74u4D")
3737 else if (("T" == $match0))
3838 then addressFromStringValue("3N8y4wxX3JC4TdrCJBXX16SjWf6X256hrep")
3939 else throw("Unknown chain")
4040 }
4141
4242 let pub = base58'6LfPuKJjLgekmncBhMg2LZyMTNVzZBccXR28ySXm9uXD'
4343
4444 let HEALCOST = 10000
4545
4646 let LANDPREFIX = "LAND"
4747
4848 let DUCKPREFIX = "DUCK"
4949
5050 let DEFAULTLOCATION = "Africa_F_Africa"
5151
5252 let NUMRES = 6
5353
5454 let DAILYRESBYPIECE = 3456000
5555
5656 let DAYMILLIS = 86400000
5757
5858 let FIVEMINUTESMILLIS = 300000
5959
6060 let RESOURCEPRICEMIN = 158549
6161
62+let InfraUpgradeCostS = match chain {
63+ case _ =>
64+ if (("W" == $match0))
65+ then 18921595217
66+ else if (("T" == $match0))
67+ then 189215952
68+ else throw("Unknown chain")
69+}
70+
71+let InfraUpgradeCostSUsdn = match chain {
72+ case _ =>
73+ if (("W" == $match0))
74+ then 120000000
75+ else if (("T" == $match0))
76+ then 1200000
77+ else throw("Unknown chain")
78+}
79+
6280 let EXPMATERIALS = match chain {
6381 case _ =>
6482 if (("W" == $match0))
6583 then 157679960139
6684 else if (("T" == $match0))
6785 then 1576799601
6886 else throw("Unknown chain")
6987 }
7088
7189 let EXPUSDN = match chain {
7290 case _ =>
7391 if (("W" == $match0))
7492 then 1000000000
7593 else if (("T" == $match0))
7694 then 10000000
7795 else throw("Unknown chain")
7896 }
7997
8098 let MULT6 = 1000000
8199
82100 let FIVEX = toBigInt(5)
83101
84102 let TWENTYX = toBigInt(20)
85103
86104 let TWENTY2X = toBigInt((20 * 20))
87105
88106 let TWENTY3X = toBigInt(((20 * 20) * 20))
89107
90108 let TWENTY4X = toBigInt((((20 * 20) * 20) * 20))
91109
92110 let TWENTY5X = toBigInt(((((20 * 20) * 20) * 20) * 20))
93111
94112 let matTypes = ["Fuel", "Metal", "Plank", "Glass", "Plastic", "Protein"]
95113
96114 let continents = ["Asia", "Europe", "Americas", "Oceania", "Africa"]
97115
98116 func keyNextFreeLandNum () = "nextLandNum"
99117
100118
101119 func keyLandToAssetId (landNum) = ("landToAsset_" + landNum)
102120
103121
104122 func keyNftName (landNum,landSize) = ((LANDPREFIX + landNum) + landSize)
105123
106124
107125 func keyLandAssetIdToOwner (assetId) = ("nftOwner_" + assetId)
108126
109127
110128 func keyDuckIdToOwner (assetId) = ("duckOwner_" + assetId)
111129
112130
113131 func keyStakedTimeByAssetId (assetId) = ("stakedTime_" + assetId)
114132
115133
116134 func keyInfraLevelByAssetId (assetId) = ("infraLevel_" + assetId)
117135
118136
137+func keyInfraLevelByAssetIdAndOwner (assetId,ownerAddr) = ((("infraLevelByAssetIdAndOwner_" + assetId) + "_") + ownerAddr)
138+
139+
140+func keyPresaleArtActivatedByAssetId (assetId) = ("presaleArtActivated_" + assetId)
141+
142+
143+func keyPresaleArtActivatedByAssetIdAndOwner (assetId,ownerAddr) = ((("presaleArtActivatedByAssetIdAndOwner_" + assetId) + "_") + ownerAddr)
144+
145+
119146 func keyStakedDuckByOwner (ownerAddr) = ("stakedDuckByOwner_" + ownerAddr)
120147
121148
122149 func keyStakedTimeByTypeAssetIdAndOwner (nftType,assetId,ownerAddr) = ((((("stakedTimeByTypeAssetIdAndOwner_" + nftType) + "_") + assetId) + "_") + ownerAddr)
123150
124151
125152 func keyLandNumToOwner (landNum) = ("landOwner_" + landNum)
126153
127154
128155 func keyBackpackByDuck (duckAssetId) = ("backPack_" + duckAssetId)
129156
130157
131158 func keyDuckLocation (duckAssetId) = ("duckLocation_" + duckAssetId)
132159
133160
134161 func keyDuckHealth (duckAssetId) = ("duckHealth_" + duckAssetId)
135162
136163
137164 func keyResProportions () = "resTypesProportions"
138165
139166
140167 let recLandNum = 0
141168
142169 let recLandSize = 1
143170
144171 let recTerrains = 2
145172
146173 let recContinent = 3
147174
148175 let locIdxContinent = 0
149176
150177 let locIdxType = 1
151178
152179 let locIdxId = 2
153180
154181 let bpIdxLevel = 0
155182
156183 let bpIdxRes = 1
157184
158185 let bpIdxMat = 2
159186
160187 let bpIdxProd = 3
188+
189+func asString (v) = match v {
190+ case s: String =>
191+ s
192+ case _ =>
193+ throw("fail to cast into String")
194+}
195+
161196
162197 func getNeededMaterials (total) = {
163198 let props = split(value(getString(keyResProportions())), "_")
164199 if ((size(props) != NUMRES))
165200 then throw("Wrong proportions data")
166201 else {
167202 let r = [parseIntValue(props[0]), parseIntValue(props[1]), parseIntValue(props[2]), parseIntValue(props[3]), parseIntValue(props[4]), parseIntValue(props[5])]
168203 let sum = (((((r[0] + r[1]) + r[2]) + r[3]) + r[4]) + r[5])
169204 if ((0 >= sum))
170205 then throw("No lands staked")
171206 else {
172207 let norm6 = fraction(total, MULT6, sum)
173208 func normalizer (acc,elem) = (acc :+ fraction(elem, norm6, MULT6))
174209
175210 let $l = r
176211 let $s = size($l)
177212 let $acc0 = nil
178213 func $f0_1 ($a,$i) = if (($i >= $s))
179214 then $a
180215 else normalizer($a, $l[$i])
181216
182217 func $f0_2 ($a,$i) = if (($i >= $s))
183218 then $a
184219 else throw("List size exceeds 6")
185220
186221 $f0_2($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($acc0, 0), 1), 2), 3), 4), 5), 6)
187222 }
188223 }
189224 }
190225
191226
192227 func subtractMaterials (shouldUseMat,has,totalNeed) = {
193228 let need = getNeededMaterials(totalNeed)
194229 func subtractor (acc,idx) = {
195230 let result = (parseIntValue(has[idx]) - need[idx])
196231 if ((0 > result))
197232 then throw(((((("Not enough material idx=" + toString(idx)) + ", you have ") + has[idx]) + ", but need ") + toString(need[idx])))
198233 else (acc :+ toString(result))
199234 }
200235
201236 if (shouldUseMat)
202237 then {
203238 let $l = [0, 1, 2, 3, 4, 5]
204239 let $s = size($l)
205240 let $acc0 = nil
206241 func $f0_1 ($a,$i) = if (($i >= $s))
207242 then $a
208243 else subtractor($a, $l[$i])
209244
210245 func $f0_2 ($a,$i) = if (($i >= $s))
211246 then $a
212247 else throw("List size exceeds 6")
213248
214249 $f0_2($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($acc0, 0), 1), 2), 3), 4), 5), 6)
215250 }
216251 else has
217252 }
218253
219254
220255 func updateProportions (terrainCounts,landSizeIndex,sign) = {
221256 let props = split(valueOrElse(getString(keyResProportions()), "0_0_0_0_0_0"), "_")
222257 if ((size(props) != NUMRES))
223258 then throw("Wrong proportions data")
224259 else {
225260 func updater (acc,i) = {
226261 let result = (parseIntValue(props[i]) + ((sign * terrainCounts[i]) * landSizeIndex))
227262 if ((0 > result))
228263 then throw(((((((("Panic! Pieces of type=" + toString(i)) + ", sign=") + toString(sign)) + ", terrainCounts[i]=") + toString(terrainCounts[i])) + ", landSizeIndex=") + toString(landSizeIndex)))
229264 else (acc :+ toString(result))
230265 }
231266
232267 let r = {
233268 let $l = [0, 1, 2, 3, 4, 5]
234269 let $s = size($l)
235270 let $acc0 = nil
236271 func $f0_1 ($a,$i) = if (($i >= $s))
237272 then $a
238273 else updater($a, $l[$i])
239274
240275 func $f0_2 ($a,$i) = if (($i >= $s))
241276 then $a
242277 else throw("List size exceeds 6")
243278
244279 $f0_2($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($acc0, 0), 1), 2), 3), 4), 5), 6)
245280 }
246281 makeString(r, "_")
247282 }
248283 }
249284
250285
251286 func countTerrains (terrains) = [(size(split(terrains, "A")) - 1), (size(split(terrains, "B")) - 1), (size(split(terrains, "C")) - 1), (size(split(terrains, "D")) - 1), (size(split(terrains, "E")) - 1), (size(split(terrains, "F")) - 1)]
252287
253288
254289 func numPiecesBySize (landSize) = match landSize {
255290 case _ =>
256291 if (("S" == $match0))
257292 then 25
258293 else if (("M" == $match0))
259294 then 100
260295 else if (("L" == $match0))
261296 then 225
262297 else if (("XL" == $match0))
263298 then 400
264299 else if (("XXL" == $match0))
265300 then 625
266301 else throw("Unknown land size")
267302 }
268303
269304
270305 func subOneInList (aList,idx,amount) = {
271306 func subber (acc,i) = (acc :+ (if ((i == idx))
272307 then toString((parseIntValue(aList[i]) - amount))
273308 else aList[i]))
274309
275310 let r = {
276311 let $l = [0, 1, 2, 3, 4, 5]
277312 let $s = size($l)
278313 let $acc0 = nil
279314 func $f0_1 ($a,$i) = if (($i >= $s))
280315 then $a
281316 else subber($a, $l[$i])
282317
283318 func $f0_2 ($a,$i) = if (($i >= $s))
284319 then $a
285320 else throw("List size exceeds 6")
286321
287322 $f0_2($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($acc0, 0), 1), 2), 3), 4), 5), 6)
288323 }
289324 makeString(r, "_")
290325 }
291326
292327
293-func addRes (currentRes,terrainCounts,deltaTime,landSizeIndex) = {
328+func addRes (currentRes,terrainCounts,deltaTime,landSizeIndex,dailyByPieceWithBonuses) = {
294329 func adder (acc,i) = {
295- let resOfType = ((fraction(deltaTime, DAILYRESBYPIECE, DAYMILLIS) * terrainCounts[i]) * landSizeIndex)
330+ let resOfType = ((fraction(deltaTime, dailyByPieceWithBonuses, DAYMILLIS) * terrainCounts[i]) * landSizeIndex)
296331 (acc :+ toString((parseIntValue(currentRes[i]) + resOfType)))
297332 }
298333
299334 let r = {
300335 let $l = [0, 1, 2, 3, 4, 5]
301336 let $s = size($l)
302337 let $acc0 = nil
303338 func $f0_1 ($a,$i) = if (($i >= $s))
304339 then $a
305340 else adder($a, $l[$i])
306341
307342 func $f0_2 ($a,$i) = if (($i >= $s))
308343 then $a
309344 else throw("List size exceeds 6")
310345
311346 $f0_2($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($acc0, 0), 1), 2), 3), 4), 5), 6)
312347 }
313348 makeString(r, "_")
314349 }
315350
316351
317352 func abs (x) = if ((x >= toBigInt(0)))
318353 then x
319354 else -(x)
320355
321356
322357 let freq = [[1, 4, 9, 10, 15], [5, 8, 13, 14, 15], [6, 9, 14, 15, 16], [4, 7, 8, 13, 18], [1, 6, 7, 15, 19]]
323358
324359 func genChar (n,freqs) = {
325360 let rem = toInt((n % TWENTYX))
326361 let letter = if ((freqs[0] > rem))
327362 then "A"
328363 else if ((freqs[1] > rem))
329364 then "B"
330365 else if ((freqs[2] > rem))
331366 then "C"
332367 else if ((freqs[3] > rem))
333368 then "D"
334369 else if ((freqs[4] > rem))
335370 then "E"
336371 else "F"
337372 letter
338373 }
339374
340375
341376 func getBackpack (bpKey) = {
342377 let p = split(valueOrElse(getString(bpKey), "0:0_0_0_0_0_0:0_0_0_0_0_0:"), ":")
343378 [toString(valueOrElse(parseInt(p[bpIdxLevel]), 0)), if ((size(split(p[bpIdxRes], "_")) == NUMRES))
344379 then p[bpIdxRes]
345380 else "0_0_0_0_0_0", if ((size(split(p[bpIdxMat], "_")) == NUMRES))
346381 then p[bpIdxMat]
347382 else "0_0_0_0_0_0", p[bpIdxProd]]
348383 }
349384
350385
351386 func expeditionCommon (shouldUseMat,caller,txId,message,sig) = if (!(sigVerify_8Kb(message, sig, pub)))
352387 then throw("signature does not match")
353388 else {
354389 let parts = split(toUtf8String(message), ";")
355390 let hp = split(split(parts[0], "|")[0], "_")
356391 let curHP = parseIntValue(hp[0])
357392 let newHP = parseIntValue(hp[1])
358393 let locAndTime = split(parts[1], ":")
359394 let targetLocation = split(locAndTime[0], "_")
360395 if ((targetLocation[1] != "E"))
361396 then throw("expedition target location type should be E")
362397 else {
363398 let time = parseIntValue(locAndTime[1])
364399 if (if ((time > (lastBlock.timestamp + FIVEMINUTESMILLIS)))
365400 then true
366401 else ((lastBlock.timestamp - FIVEMINUTESMILLIS) > time))
367402 then throw("signature outdated")
368403 else {
369404 let userAddr = toString(caller)
370405 let duckAssetId = valueOrErrorMessage(getString(keyStakedDuckByOwner(userAddr)), "You don't have a duck staked")
371406 let keyHealth = keyDuckHealth(duckAssetId)
372407 let oldFromState = valueOrElse(getInteger(keyHealth), 100)
373408 if ((oldFromState != curHP))
374409 then throw(((("oldHealth=" + toString(valueOrElse(getInteger(keyHealth), 100))) + " from state does not match one from flight log=") + toString(curHP)))
375410 else if ((0 >= curHP))
376411 then throw("You can't fly with zero health")
377412 else if ((0 >= newHP))
378- then throw("Your duck health is zero, expedition failed")
413+ then $Tuple2(((if (!(shouldUseMat))
414+ then [ScriptTransfer(caller, EXPUSDN, usdnAssetId)]
415+ else nil) :+ IntegerEntry(keyHealth, 0)), "")
379416 else {
380417 let bpKey = keyBackpackByDuck(duckAssetId)
381418 let currentPack = getBackpack(bpKey)
382419 let mList = split(currentPack[bpIdxMat], "_")
383420 let newMat = makeString(subtractMaterials(shouldUseMat, mList, EXPMATERIALS), "_")
384421 let bigNum = abs(toBigInt(txId))
385422 let freeNum = valueOrElse(getInteger(keyNextFreeLandNum()), 501)
386423 let landNum = toString(freeNum)
387424 let continentIdx = toInt((bigNum % FIVEX))
388425 let f = freq[continentIdx]
389426 func terrainGenerator (acc,elem) = $Tuple2((((((acc._1 + genChar(acc._2, f)) + genChar((acc._2 / TWENTYX), f)) + genChar((acc._2 / TWENTY2X), f)) + genChar((acc._2 / TWENTY3X), f)) + genChar((acc._2 / TWENTY4X), f)), (acc._2 / TWENTY5X))
390427
391428 let t = {
392429 let $l = [1, 2, 3, 4, 5]
393430 let $s = size($l)
394431 let $acc0 = $Tuple2("", (bigNum / FIVEX))
395432 func $f0_1 ($a,$i) = if (($i >= $s))
396433 then $a
397434 else terrainGenerator($a, $l[$i])
398435
399436 func $f0_2 ($a,$i) = if (($i >= $s))
400437 then $a
401438 else throw("List size exceeds 5")
402439
403440 $f0_2($f0_1($f0_1($f0_1($f0_1($f0_1($acc0, 0), 1), 2), 3), 4), 5)
404441 }
405442 let continent = continents[continentIdx]
406443 let issue = Issue(keyNftName(landNum, "S"), makeString([landNum, "S", t._1, continent], "_"), 1, 0, false)
407444 let assetId = calculateAssetId(issue)
408445 let id = toBase58String(assetId)
409- $Tuple2([IntegerEntry(keyNextFreeLandNum(), (freeNum + 1)), issue, StringEntry(keyLandToAssetId(landNum), id), StringEntry(keyLandAssetIdToOwner(id), userAddr), StringEntry(keyLandNumToOwner(landNum), userAddr), IntegerEntry(keyInfraLevelByAssetId(id), 0), ScriptTransfer(caller, 1, assetId), StringEntry(keyDuckLocation(duckAssetId), makeString([continent, "L", id], "_")), IntegerEntry(keyHealth, newHP), StringEntry(bpKey, makeString([currentPack[bpIdxLevel], currentPack[bpIdxRes], newMat, currentPack[bpIdxProd]], ":"))], unit)
446+ $Tuple2([IntegerEntry(keyNextFreeLandNum(), (freeNum + 1)), issue, StringEntry(keyLandToAssetId(landNum), id), StringEntry(keyLandAssetIdToOwner(id), userAddr), StringEntry(keyLandNumToOwner(landNum), userAddr), IntegerEntry(keyInfraLevelByAssetId(id), 0), IntegerEntry(keyInfraLevelByAssetIdAndOwner(id, userAddr), 0), ScriptTransfer(caller, 1, assetId), StringEntry(keyDuckLocation(duckAssetId), makeString([continent, "L", id], "_")), IntegerEntry(keyHealth, newHP), StringEntry(bpKey, makeString([currentPack[bpIdxLevel], currentPack[bpIdxRes], newMat, currentPack[bpIdxProd]], ":"))], id)
410447 }
411448 }
412449 }
413450 }
451+
452+
453+func applyBonuses (landAssetId) = {
454+ let infraLevel = valueOrElse(getInteger(keyInfraLevelByAssetId(landAssetId)), 0)
455+ let presaleBonus = if (valueOrElse(getBoolean(keyPresaleArtActivatedByAssetId(landAssetId)), false))
456+ then 3
457+ else 0
458+ ((DAILYRESBYPIECE + fraction(DAILYRESBYPIECE, infraLevel, 4)) + fraction(DAILYRESBYPIECE, presaleBonus, 20))
459+ }
460+
461+
462+func checkClaimConditions (addr) = {
463+ let duckAssetId = valueOrErrorMessage(getString(keyStakedDuckByOwner(addr)), "You don't have a duck staked")
464+ let curLocation = valueOrElse(getString(keyDuckLocation(duckAssetId)), DEFAULTLOCATION)
465+ let loc = split(value(curLocation), "_")
466+ if ((loc[locIdxType] != "L"))
467+ then throw((("Duck location type is " + loc[locIdxType]) + ", but should be L"))
468+ else {
469+ let landAssetId = loc[locIdxId]
470+ let asset = value(assetInfo(fromBase58String(landAssetId)))
471+ let timeKey = keyStakedTimeByAssetId(landAssetId)
472+ let savedTime = valueOrErrorMessage(getInteger(timeKey), (("NFT " + asset.name) + " is not staked"))
473+ let owner = valueOrErrorMessage(getString(keyLandAssetIdToOwner(landAssetId)), (("NFT " + asset.name) + " is orphaned"))
474+ if ((owner != addr))
475+ then throw((LANDPREFIX + " is not yours"))
476+ else {
477+ let d = split(asset.description, "_")
478+ $Tuple4(duckAssetId, landAssetId, d, savedTime)
479+ }
480+ }
481+ }
482+
483+
484+func claimResInternal (addr,amount) = {
485+ let c = checkClaimConditions(addr)
486+ let landSize = c._3[recLandSize]
487+ let terrainCounts = countTerrains(c._3[recTerrains])
488+ let deltaTime = (lastBlock.timestamp - c._4)
489+ if ((0 > deltaTime))
490+ then throw(((("Saved timestamp is in future, saved = " + toString(c._4)) + ", current = ") + toString(lastBlock.timestamp)))
491+ else {
492+ let pieces = numPiecesBySize(landSize)
493+ let dailyProductionByPiece = applyBonuses(c._2)
494+ let availRes = fraction(deltaTime, (dailyProductionByPiece * pieces), DAYMILLIS)
495+ if ((amount > availRes))
496+ then throw(((("Not enough resources, available = " + toString(availRes)) + ", requested = ") + toString(amount)))
497+ else {
498+ let newDeltaTime = fraction((availRes - amount), DAYMILLIS, (dailyProductionByPiece * pieces))
499+ let newTimestamp = (lastBlock.timestamp - newDeltaTime)
500+ let bpKey = keyBackpackByDuck(c._1)
501+ let currentPack = getBackpack(bpKey)
502+ let currentRes = split(currentPack[bpIdxRes], "_")
503+ let bpRes = addRes(currentRes, terrainCounts, (deltaTime - newDeltaTime), (pieces / 25), dailyProductionByPiece)
504+ $Tuple3([IntegerEntry(keyStakedTimeByAssetId(c._2), newTimestamp), IntegerEntry(keyStakedTimeByTypeAssetIdAndOwner(LANDPREFIX, c._2, addr), newTimestamp)], bpKey, [currentPack[bpIdxLevel], bpRes, currentPack[bpIdxMat], currentPack[bpIdxProd]])
505+ }
506+ }
507+ }
508+
509+
510+func claimAll (addr,landAssetId,pieces) = {
511+ let timeKey = keyStakedTimeByAssetId(landAssetId)
512+ let savedTime = value(getInteger(timeKey))
513+ let availRes = (fraction((lastBlock.timestamp - savedTime), applyBonuses(landAssetId), DAYMILLIS) * pieces)
514+ claimResInternal(addr, availRes)
515+ }
516+
517+
518+func upInfraCommon (shouldUseMat,caller,paymentAmount) = {
519+ let addr = toString(caller)
520+ let c = checkClaimConditions(addr)
521+ let pieces = numPiecesBySize(c._3[recLandSize])
522+ let infraKey = keyInfraLevelByAssetId(c._2)
523+ let curLevel = valueOrElse(getInteger(infraKey), 0)
524+ if ((curLevel >= 3))
525+ then throw("Currently max infrastructure level = 3")
526+ else {
527+ let newLevel = (curLevel + 1)
528+ let cost = fraction(InfraUpgradeCostSUsdn, (pieces * newLevel), 25)
529+ if (if (!(shouldUseMat))
530+ then (paymentAmount != cost)
531+ else false)
532+ then throw(("Payment attached should be " + toString(cost)))
533+ else {
534+ let bpKey = keyBackpackByDuck(c._1)
535+ let currentPack = getBackpack(bpKey)
536+ let mList = split(currentPack[bpIdxMat], "_")
537+ let newMat = makeString(subtractMaterials(shouldUseMat, mList, fraction(InfraUpgradeCostS, (pieces * newLevel), 25)), "_")
538+ let claimResult = claimAll(addr, c._2, pieces)
539+ $Tuple2(([IntegerEntry(infraKey, newLevel), IntegerEntry(keyInfraLevelByAssetIdAndOwner(c._2, addr), newLevel), StringEntry(bpKey, makeString([currentPack[bpIdxLevel], claimResult._3[bpIdxRes], newMat, currentPack[bpIdxProd]], ":"))] ++ claimResult._1), newLevel)
540+ }
541+ }
542+ }
543+
544+
545+func activatePresaleArt (addr) = {
546+ let c = checkClaimConditions(addr)
547+ let activationKey = keyPresaleArtActivatedByAssetId(c._2)
548+ if (valueOrElse(getBoolean(activationKey), false))
549+ then throw("Presale artifact is already activated")
550+ else if ((parseIntValue(c._3[recLandNum]) > 500))
551+ then throw((((LANDPREFIX + " ") + c._2) + " is not eligible for presale artifact"))
552+ else {
553+ let pieces = numPiecesBySize(c._3[recLandSize])
554+ let claimResult = claimAll(addr, c._2, pieces)
555+ (((claimResult._1 :+ BooleanEntry(keyPresaleArtActivatedByAssetId(c._2), true)) :+ BooleanEntry(keyPresaleArtActivatedByAssetIdAndOwner(c._2, addr), true)) :+ StringEntry(claimResult._2, makeString(claimResult._3, ":")))
556+ }
557+ }
414558
415559
416560 @Callable(i)
417561 func stakeLand () = {
418562 let pmt = value(i.payments[0])
419563 let assetId = value(pmt.assetId)
420564 let address = toString(i.caller)
421565 if ((pmt.amount != 1))
422566 then throw((("NFT " + LANDPREFIX) + " token should be attached as payment"))
423567 else {
424568 let asset = value(assetInfo(assetId))
425569 if ((asset.issuer != this))
426570 then throw("Unknown issuer of token")
427571 else if (!(contains(asset.name, LANDPREFIX)))
428572 then throw((("Only NFT " + LANDPREFIX) + " tokens are accepted"))
429573 else {
430574 let landNumSize = drop(asset.name, 4)
431575 let landNum = if (contains(landNumSize, "XXL"))
432576 then dropRight(landNumSize, 3)
433577 else if (contains(landNumSize, "XL"))
434578 then dropRight(landNumSize, 2)
435579 else dropRight(landNumSize, 1)
436- if (!(isDefined(parseInt(landNum))))
437- then throw(("Cannot parse land number from " + asset.name))
580+ let landNumInt = valueOrErrorMessage(parseInt(landNum), ("Cannot parse land number from " + asset.name))
581+ let landAssetId = toBase58String(assetId)
582+ let timeKey = keyStakedTimeByAssetId(landAssetId)
583+ if (isDefined(getInteger(timeKey)))
584+ then throw((("NFT " + asset.name) + " is already staked"))
438585 else {
439- let timeKey = keyStakedTimeByAssetId(toBase58String(assetId))
440- if (isDefined(getInteger(timeKey)))
441- then throw((("NFT " + asset.name) + " is already staked"))
442- else {
443- let d = split(asset.description, "_")
444- let terrainCounts = countTerrains(d[recTerrains])
445- let props = updateProportions(terrainCounts, (numPiecesBySize(d[recLandSize]) / 25), 1)
446-[IntegerEntry(timeKey, lastBlock.timestamp), IntegerEntry(keyStakedTimeByTypeAssetIdAndOwner(LANDPREFIX, toBase58String(assetId), address), lastBlock.timestamp), StringEntry(keyLandAssetIdToOwner(toBase58String(assetId)), address), StringEntry(keyLandNumToOwner(landNum), address), StringEntry(keyResProportions(), props)]
447- }
586+ let d = split(asset.description, "_")
587+ let terrainCounts = countTerrains(d[recTerrains])
588+ let props = updateProportions(terrainCounts, (numPiecesBySize(d[recLandSize]) / 25), 1)
589+[IntegerEntry(timeKey, lastBlock.timestamp), IntegerEntry(keyStakedTimeByTypeAssetIdAndOwner(LANDPREFIX, landAssetId, address), lastBlock.timestamp), StringEntry(keyLandAssetIdToOwner(landAssetId), address), StringEntry(keyLandNumToOwner(landNum), address), StringEntry(keyResProportions(), props)]
448590 }
449591 }
450592 }
451593 }
452594
453595
454596
455597 @Callable(i)
456-func unstakeLand (landAssetId) = if ((size(i.payments) != 0))
598+func unstakeLand (landAssetIdStr) = if ((size(i.payments) != 0))
457599 then throw("unstake doesn't require any payments")
458600 else {
459- let assetId = fromBase58String(landAssetId)
460- let address = toString(i.caller)
461- let asset = value(assetInfo(assetId))
462- if ((asset.issuer != this))
463- then throw("Unknown issuer of token")
464- else if (!(contains(asset.name, LANDPREFIX)))
465- then throw((("Only NFT " + LANDPREFIX) + " tokens can be unstaked"))
466- else {
467- let timeKey = keyStakedTimeByAssetId(landAssetId)
468- if (!(isDefined(timeKey)))
469- then throw((("NFT " + asset.name) + " is not staked"))
470- else {
471- let owner = valueOrErrorMessage(getString(keyLandAssetIdToOwner(landAssetId)), (("NFT " + asset.name) + " is orphaned"))
472- if ((owner != address))
473- then throw("Staked NFT is not yours")
474- else {
475- let d = split(asset.description, "_")
476- let terrainCounts = countTerrains(d[recTerrains])
477- let props = updateProportions(terrainCounts, (numPiecesBySize(d[recLandSize]) / 25), -1)
478-[ScriptTransfer(i.caller, 1, assetId), DeleteEntry(timeKey), DeleteEntry(keyStakedTimeByTypeAssetIdAndOwner(LANDPREFIX, landAssetId, address)), StringEntry(keyResProportions(), props)]
479- }
480- }
481- }
601+ let addr = toString(i.caller)
602+ let c = checkClaimConditions(addr)
603+ let terrainCounts = countTerrains(c._3[recTerrains])
604+ let pieces = numPiecesBySize(c._3[recLandSize])
605+ let props = updateProportions(terrainCounts, (pieces / 25), -1)
606+ let claimResult = claimAll(addr, c._2, pieces)
607+[ScriptTransfer(i.caller, 1, fromBase58String(c._2)), DeleteEntry(keyStakedTimeByAssetId(c._2)), DeleteEntry(keyStakedTimeByTypeAssetIdAndOwner(LANDPREFIX, c._2, addr)), StringEntry(keyResProportions(), props), StringEntry(claimResult._2, makeString(claimResult._3, ":"))]
482608 }
483609
484610
485611
486612 @Callable(i)
487613 func stakeDuck () = {
488614 let pmt = value(i.payments[0])
489615 let assetId = value(pmt.assetId)
490616 let address = toString(i.caller)
491617 if ((pmt.amount != 1))
492618 then throw((("NFT " + DUCKPREFIX) + " token should be attached as payment"))
493619 else {
494620 let asset = value(assetInfo(assetId))
495621 if (if ((asset.issuer != incubatorAddr))
496622 then (asset.issuer != breederAddr)
497623 else false)
498624 then throw((("Unknown issuer of " + DUCKPREFIX) + " token"))
499625 else if (!(contains(asset.name, DUCKPREFIX)))
500626 then throw((("Only NFT " + DUCKPREFIX) + " tokens are accepted"))
501627 else {
502628 let assetIdStr = toBase58String(assetId)
503629 let timeKey = keyStakedTimeByAssetId(assetIdStr)
504630 if (isDefined(getInteger(timeKey)))
505631 then throw((("NFT " + asset.name) + " is already staked"))
506632 else if (isDefined(getString(keyStakedDuckByOwner(address))))
507633 then throw(("You already staked one duck: " + asset.name))
508634 else {
509635 let locKey = keyDuckLocation(assetIdStr)
510636 let location = getString(locKey)
511637 let keyHealth = keyDuckHealth(assetIdStr)
512638 let health = getInteger(keyHealth)
513639 let bpKey = keyBackpackByDuck(assetIdStr)
514640 let backpack = getString(bpKey)
515641 ([IntegerEntry(timeKey, lastBlock.timestamp), IntegerEntry(keyStakedTimeByTypeAssetIdAndOwner(DUCKPREFIX, toBase58String(assetId), address), lastBlock.timestamp), StringEntry(keyDuckIdToOwner(assetIdStr), address), StringEntry(keyStakedDuckByOwner(address), assetIdStr)] ++ (if (isDefined(location))
516642 then nil
517643 else ([StringEntry(locKey, DEFAULTLOCATION)] ++ (if (isDefined(health))
518644 then nil
519645 else ([IntegerEntry(keyHealth, 100)] ++ (if (isDefined(backpack))
520646 then nil
521647 else [StringEntry(bpKey, "0:0_0_0_0_0_0:0_0_0_0_0_0:")]))))))
522648 }
523649 }
524650 }
525651 }
526652
527653
528654
529655 @Callable(i)
530656 func unstakeDuck (assetIdStr) = if ((size(i.payments) != 0))
531657 then throw("unstake doesn't require any payments")
532658 else {
533659 let assetId = fromBase58String(assetIdStr)
534660 let address = toString(i.caller)
535661 let asset = value(assetInfo(assetId))
536662 if (if ((asset.issuer != incubatorAddr))
537663 then (asset.issuer != breederAddr)
538664 else false)
539665 then throw((("Unknown issuer of " + DUCKPREFIX) + " token"))
540666 else if (!(contains(asset.name, DUCKPREFIX)))
541667 then throw((("Only NFT " + DUCKPREFIX) + " tokens can be unstaked"))
542668 else {
543669 let timeKey = keyStakedTimeByAssetId(toBase58String(assetId))
544670 if (!(isDefined(timeKey)))
545671 then throw((("NFT " + asset.name) + " is not staked"))
546672 else if (!(isDefined(keyStakedDuckByOwner(address))))
547673 then throw((("The duck " + asset.name) + " is not staked"))
548674 else {
549675 let owner = valueOrErrorMessage(getString(keyDuckIdToOwner(toBase58String(assetId))), (("NFT " + asset.name) + " is orphaned"))
550676 if ((owner != address))
551677 then throw("Staked NFT is not yours")
552- else [ScriptTransfer(i.caller, 1, assetId), DeleteEntry(timeKey), DeleteEntry(keyDuckLocation(assetIdStr)), DeleteEntry(keyStakedTimeByTypeAssetIdAndOwner(DUCKPREFIX, assetIdStr, address)), DeleteEntry(keyStakedDuckByOwner(address))]
678+ else [ScriptTransfer(i.caller, 1, assetId), DeleteEntry(timeKey), DeleteEntry(keyDuckLocation(assetIdStr)), DeleteEntry(keyDuckIdToOwner(assetIdStr)), DeleteEntry(keyStakedTimeByTypeAssetIdAndOwner(DUCKPREFIX, assetIdStr, address)), DeleteEntry(keyStakedDuckByOwner(address))]
553679 }
554680 }
555681 }
556682
557683
558684
559685 @Callable(i)
560-func claimRes (amount,landAssetId) = if ((size(i.payments) != 0))
686+func claimRes (amount,landAssetIdStr) = if ((size(i.payments) != 0))
561687 then throw("claimRes doesn't require any payments")
562688 else {
563- let addr = toString(i.caller)
564- let asset = value(assetInfo(fromBase58String(landAssetId)))
565- if (!(contains(asset.name, LANDPREFIX)))
566- then throw((("NFT " + LANDPREFIX) + " token should be passed as param"))
567- else {
568- let timeKey = keyStakedTimeByAssetId(landAssetId)
569- let savedTime = getInteger(timeKey)
570- if (!(isDefined(savedTime)))
571- then throw((("NFT " + asset.name) + " is not staked"))
572- else {
573- let owner = getStringValue(keyLandAssetIdToOwner(landAssetId))
574- if ((owner != addr))
575- then throw((LANDPREFIX + " is not yours"))
576- else {
577- let d = split(asset.description, "_")
578- let landSize = d[recLandSize]
579- let terrainCounts = countTerrains(d[recTerrains])
580- let duck = getString(keyStakedDuckByOwner(addr))
581- if (!(isDefined(duck)))
582- then throw("You don't have a duck staked")
583- else {
584- let duckAssetIdStr = value(duck)
585- let curLocation = valueOrElse(getString(keyDuckLocation(duckAssetIdStr)), DEFAULTLOCATION)
586- let loc = split(value(curLocation), "_")
587- if ((loc[locIdxType] != "L"))
588- then throw((("Duck location type is " + loc[locIdxType]) + ", but should be L"))
589- else if ((loc[locIdxId] != landAssetId))
590- then throw(((("Duck location id is " + loc[locIdxId]) + ", but should be ") + landAssetId))
591- else {
592- let deltaTime = (lastBlock.timestamp - value(savedTime))
593- if ((0 > deltaTime))
594- then throw(((("Saved timestamp is in future, saved = " + toString(value(savedTime))) + ", current = ") + toString(lastBlock.timestamp)))
595- else {
596- let pieces = numPiecesBySize(landSize)
597- let availRes = (fraction(deltaTime, DAILYRESBYPIECE, DAYMILLIS) * pieces)
598- if ((amount > availRes))
599- then throw(((("Not enough resources, available = " + toString(availRes)) + ", requested = ") + toString(amount)))
600- else {
601- let newDeltaTime = fraction((availRes - amount), DAYMILLIS, (pieces * DAILYRESBYPIECE))
602- let newTimestamp = (lastBlock.timestamp - newDeltaTime)
603- let bpKey = keyBackpackByDuck(duckAssetIdStr)
604- let currentPack = getBackpack(bpKey)
605- let currentRes = split(currentPack[bpIdxRes], "_")
606- let bpRes = addRes(currentRes, terrainCounts, (deltaTime - newDeltaTime), (pieces / 25))
607- let newPack = makeString([currentPack[bpIdxLevel], bpRes, currentPack[bpIdxMat], currentPack[bpIdxProd]], ":")
608- $Tuple2([StringEntry(bpKey, newPack), IntegerEntry(timeKey, newTimestamp), IntegerEntry(keyStakedTimeByTypeAssetIdAndOwner(LANDPREFIX, landAssetId, owner), newTimestamp)], unit)
609- }
610- }
611- }
612- }
613- }
614- }
615- }
689+ let addr = toString(i.originCaller)
690+ let result = claimResInternal(addr, amount)
691+ $Tuple2((result._1 :+ StringEntry(result._2, makeString(result._3, ":"))), result._3[bpIdxRes])
616692 }
617693
618694
619695
620696 @Callable(i)
621697 func flight (message,sig) = if (!(sigVerify_8Kb(message, sig, pub)))
622698 then throw("signature does not match")
623699 else if ((size(i.payments) != 0))
624700 then throw("flight doesn't require any payments")
625701 else {
626702 let parts = split(toUtf8String(message), ";")
627703 let hp = split(split(parts[0], "|")[0], "_")
628704 let curHP = parseIntValue(hp[0])
629705 let newHP = parseIntValue(hp[1])
630706 let newLocAndTime = split(parts[1], ":")
631707 let newLocation = newLocAndTime[0]
632708 let time = parseIntValue(newLocAndTime[1])
633709 if (if ((time > (lastBlock.timestamp + FIVEMINUTESMILLIS)))
634710 then true
635711 else ((lastBlock.timestamp - FIVEMINUTESMILLIS) > time))
636712 then throw("signature outdated")
637713 else {
638714 let duckAssetId = valueOrErrorMessage(getString(keyStakedDuckByOwner(toString(i.caller))), "You don't have a duck staked")
639715 let keyHealth = keyDuckHealth(duckAssetId)
640716 let oldFromState = valueOrElse(getInteger(keyHealth), 100)
641717 if ((oldFromState != curHP))
642718 then throw(((("oldHealth=" + toString(valueOrElse(getInteger(keyHealth), 100))) + " from state does not match one from flight log=") + toString(curHP)))
643719 else if ((0 >= curHP))
644720 then throw("You can't fly with zero health")
645721 else {
646722 let locKey = keyDuckLocation(duckAssetId)
647723 let curLocation = valueOrElse(getString(locKey), DEFAULTLOCATION)
648724 if ((newLocation == curLocation))
649725 then throw("You can't fly to the same location")
650726 else $Tuple2([StringEntry(locKey, if ((newHP > 0))
651727 then newLocation
652728 else curLocation), IntegerEntry(keyHealth, newHP)], unit)
653729 }
654730 }
655731 }
656732
657733
658734
659735 @Callable(i)
660736 func setHealth (health,duckAssetId) = if (if ((0 > health))
661737 then true
662738 else (health > 100))
663739 then throw("HP should be within 0..100")
664740 else [IntegerEntry(keyDuckHealth(duckAssetId), health)]
665741
666742
667743
668744 @Callable(i)
669745 func heal (matType,amount) = if (if ((0 > matType))
670746 then true
671747 else (matType >= NUMRES))
672748 then throw(("Unknown material: " + toString(matType)))
673749 else if ((0 >= amount))
674750 then throw(("Amount should be positive! " + toString(amount)))
675751 else {
676752 let duckAssetId = valueOrErrorMessage(getString(keyStakedDuckByOwner(toString(i.caller))), "You don't have a duck staked")
677753 let keyHealth = keyDuckHealth(duckAssetId)
678754 let oldHealth = valueOrElse(getInteger(keyHealth), 100)
679755 if ((oldHealth >= 100))
680756 then throw("HP should be < 100 to heal")
681757 else {
682758 let bpKey = keyBackpackByDuck(duckAssetId)
683759 let currentPack = getBackpack(bpKey)
684760 let mList = split(currentPack[bpIdxMat], "_")
685761 let currentAmount = parseIntValue(mList[matType])
686762 let deltaHealth = min([(amount / HEALCOST), (100 - oldHealth)])
687763 let spendAmount = (deltaHealth * HEALCOST)
688764 if ((spendAmount > currentAmount))
689765 then throw(((((("You need " + toString(spendAmount)) + " of ") + matTypes[matType]) + " to heal, but you backpack contains ") + toString(currentAmount)))
690766 else {
691767 let newMat = subOneInList(mList, matType, spendAmount)
692768 [IntegerEntry(keyHealth, (oldHealth + deltaHealth)), StringEntry(bpKey, makeString([currentPack[bpIdxLevel], currentPack[bpIdxRes], newMat, currentPack[bpIdxProd]], ":"))]
693769 }
694770 }
695771 }
696772
697773
698774
699775 @Callable(i)
700776 func updateBackpack (duckAssetId,newPack) = if ((i.caller != economyAddr))
701777 then throw("permission denied")
702778 else $Tuple2([StringEntry(keyBackpackByDuck(duckAssetId), newPack)], newPack)
703779
704780
705781
706782 @Callable(i)
707783 func expeditionBuy (message,sig) = if ((size(i.payments) != 1))
708784 then throw("Exactly one payment required")
709785 else {
710786 let pmt = value(i.payments[0])
711787 if ((pmt.assetId != usdnAssetId))
712788 then throw("Allowed USDN payment only!")
713789 else if ((pmt.amount != EXPUSDN))
714790 then throw(("Payment attached should be " + toString(EXPUSDN)))
715791 else expeditionCommon(false, i.caller, i.transactionId, message, sig)
716792 }
717793
718794
719795
720796 @Callable(i)
721797 func expedition (message,sig) = if ((size(i.payments) != 0))
722798 then throw("expedition doesn't require any payments")
723799 else expeditionCommon(true, i.caller, i.transactionId, message, sig)
724800
725801
726802
727803 @Callable(i)
728-func upgradeInfra (landAssetId) = if ((i.caller != this))
729- then throw("temporary disabled")
730- else if ((size(i.payments) != 0))
731- then throw("Infrastructure upgrading doesn't require any payments")
732- else {
733- let infraKey = keyInfraLevelByAssetId(landAssetId)
734- let curLevel = valueOrElse(getInteger(infraKey), 0)
735- if ((curLevel >= 3))
736- then throw("Currently max infrastructure level is 3")
737- else {
738- let newLevel = (curLevel + 1)
739- $Tuple2([IntegerEntry(infraKey, newLevel)], newLevel)
740- }
741- }
804+func upgradeInfra (landAssetIdIgnored) = if ((size(i.payments) != 0))
805+ then throw("Infrastructure upgrade doesn't require any payments")
806+ else upInfraCommon(true, i.caller, 0)
807+
808+
809+
810+@Callable(i)
811+func upgradeInfraUsdn (landAssetIdIgnored) = if ((size(i.payments) != 1))
812+ then throw("Exactly one payment required")
813+ else {
814+ let pmt = value(i.payments[0])
815+ if ((pmt.assetId != usdnAssetId))
816+ then throw("Allowed USDN payment only!")
817+ else upInfraCommon(false, i.caller, pmt.amount)
818+ }
819+
820+
821+
822+@Callable(i)
823+func activateArtifact (artName) = if ((size(i.payments) != 0))
824+ then throw("Artifact activation doesn't require any payments")
825+ else {
826+ let result = match artName {
827+ case _ =>
828+ if (("PRESALE" == $match0))
829+ then activatePresaleArt(toString(i.caller))
830+ else throw("Unknown artifact")
831+ }
832+ result
833+ }
742834
743835

github/deemru/w8io/3ef1775 
85.05 ms