tx · 5kfJvsuTRGQiSUYy5ExUkBEfomfMLhcmgBQQ7NwiKRUS

3NDCyBG5q85JuaRiigUeEtainyjCQT3XpZm:  -0.02300000 Waves

2023.01.02 18:16 [2387518] smart account 3NDCyBG5q85JuaRiigUeEtainyjCQT3XpZm > SELF 0.00000000 Waves

{ "type": 13, "id": "5kfJvsuTRGQiSUYy5ExUkBEfomfMLhcmgBQQ7NwiKRUS", "fee": 2300000, "feeAssetId": null, "timestamp": 1672672610641, "version": 2, "chainId": 84, "sender": "3NDCyBG5q85JuaRiigUeEtainyjCQT3XpZm", "senderPublicKey": "EVooykMNV691Venwp1dHUTBd7KWequzUcda57Wd3LQEX", "proofs": [ "2SGYEd9nmaqmoMyB6Uo2KHTUBpNUSvAoyewApaSgXiNeWnkCVkwZRmnF1sai4JteeJD32RhH18NNAo88CaRminNJ" ], "script": "base64:BgJECAISABIDCgEIEgASAwoBCBIECgIBCBIECgICAhIECgIBCBIECgIBARIECgIICBIECgICAhIECgICAhIDCgEIEgMKAQhDAAVjaGFpbgkAsAkBCQDJAQIJAMoBAggFBHRoaXMFYnl0ZXMAAQABAAt1c2RuQXNzZXRJZAQHJG1hdGNoMAUFY2hhaW4DCQAAAgIBVwUHJG1hdGNoMAEgtiYpwwT1zlORpA5LdSQvZIxRsfrfr1QpvUjSHSqyqtEDCQAAAgIBVAUHJG1hdGNoMAEg93bq9/eDymXbbhPuAjvPWCmVqcHRjfJL2mzYHWKAyN4JAAIBAg1Vbmtub3duIGNoYWluAA1pbmN1YmF0b3JBZGRyBAckbWF0Y2gwBQVjaGFpbgMJAAACAgFXBQckbWF0Y2gwCQERQGV4dHJOYXRpdmUoMTA2MikBAiMzUEVrdFZ1eDJSaGNoU042M0RzRG80YjRtejRRcXpLU2VEdgMJAAACAgFUBQckbWF0Y2gwBQR0aGlzCQACAQINVW5rbm93biBjaGFpbgALYnJlZWRlckFkZHIEByRtYXRjaDAFBWNoYWluAwkAAAICAVcFByRtYXRjaDAJARFAZXh0ck5hdGl2ZSgxMDYyKQECIzNQRFZ1VTQ1SDdFaDVkbXROYm5STlJTdEd3VUxBN05ZNkhiAwkAAAICAVQFByRtYXRjaDAFBHRoaXMJAAIBAg1Vbmtub3duIGNoYWluAAtlY29ub215QWRkcgQHJG1hdGNoMAUFY2hhaW4DCQAAAgIBVwUHJG1hdGNoMAkBEUBleHRyTmF0aXZlKDEwNjIpAQIjM1Ayc2sxS25jU3hSYVpzOGI0Q1dHUHcyamt2dmF2NzR1NEQDCQAAAgIBVAUHJG1hdGNoMAkBEUBleHRyTmF0aXZlKDEwNjIpAQIjM044eTR3eFgzSkM0VGRyQ0pCWFgxNlNqV2Y2WDI1NmhyZXAJAAIBAg1Vbmtub3duIGNoYWluAANwdWIBIE9T4ho/VKjWnVJOEx1GJ1W9s1PzLHvJVFSw/0PBtBQoAAhIRUFMQ09TVACQTgAKTEFORFBSRUZJWAIETEFORAAKRFVDS1BSRUZJWAIERFVDSwAPREVGQVVMVExPQ0FUSU9OAg9BZnJpY2FfRl9BZnJpY2EABk5VTVJFUwAGAA9EQUlMWVJFU0JZUElFQ0UAgPjSAQAJREFZTUlMTElTAIC4mSkAEUZJVkVNSU5VVEVTTUlMTElTAOCnEgAQUkVTT1VSQ0VQUklDRU1JTgDV1gkAEUluZnJhVXBncmFkZUNvc3RTAIDu9eySAQAVSW5mcmFVcGdyYWRlQ29zdFNVc2RuAIDlmncADEVYUE1BVEVSSUFMUwQHJG1hdGNoMAUFY2hhaW4DCQAAAgIBVwUHJG1hdGNoMADLgNWzywQDCQAAAgIBVAUHJG1hdGNoMADxmvDvBQkAAgECDVVua25vd24gY2hhaW4AB0VYUFVTRE4EByRtYXRjaDAFBWNoYWluAwkAAAICAVcFByRtYXRjaDAAgJTr3AMDCQAAAgIBVAUHJG1hdGNoMACAreIECQACAQINVW5rbm93biBjaGFpbgAFTVVMVDYAwIQ9AAVGSVZFWAkAtgIBAAUAB1RXRU5UWVgJALYCAQAUAAhUV0VOVFkyWAkAtgIBCQBoAgAUABQACFRXRU5UWTNYCQC2AgEJAGgCCQBoAgAUABQAFAAIVFdFTlRZNFgJALYCAQkAaAIJAGgCCQBoAgAUABQAFAAUAAhUV0VOVFk1WAkAtgIBCQBoAgkAaAIJAGgCCQBoAgAUABQAFAAUABQACG1hdFR5cGVzCQDMCAICBEZ1ZWwJAMwIAgIFTWV0YWwJAMwIAgIFUGxhbmsJAMwIAgIFR2xhc3MJAMwIAgIHUGxhc3RpYwkAzAgCAgdQcm90ZWluBQNuaWwACmNvbnRpbmVudHMJAMwIAgIEQXNpYQkAzAgCAgZFdXJvcGUJAMwIAgIIQW1lcmljYXMJAMwIAgIHT2NlYW5pYQkAzAgCAgZBZnJpY2EFA25pbAESa2V5TmV4dEZyZWVMYW5kTnVtAAILbmV4dExhbmROdW0BEGtleUxhbmRUb0Fzc2V0SWQBB2xhbmROdW0JAKwCAgIMbGFuZFRvQXNzZXRfBQdsYW5kTnVtAQprZXlOZnROYW1lAgdsYW5kTnVtCGxhbmRTaXplCQCsAgIJAKwCAgUKTEFORFBSRUZJWAUHbGFuZE51bQUIbGFuZFNpemUBFWtleUxhbmRBc3NldElkVG9Pd25lcgEHYXNzZXRJZAkArAICAgluZnRPd25lcl8FB2Fzc2V0SWQBEGtleUR1Y2tJZFRvT3duZXIBB2Fzc2V0SWQJAKwCAgIKZHVja093bmVyXwUHYXNzZXRJZAEWa2V5U3Rha2VkVGltZUJ5QXNzZXRJZAEHYXNzZXRJZAkArAICAgtzdGFrZWRUaW1lXwUHYXNzZXRJZAEWa2V5SW5mcmFMZXZlbEJ5QXNzZXRJZAEHYXNzZXRJZAkArAICAgtpbmZyYUxldmVsXwUHYXNzZXRJZAEUa2V5U3Rha2VkRHVja0J5T3duZXIBCW93bmVyQWRkcgkArAICAhJzdGFrZWREdWNrQnlPd25lcl8FCW93bmVyQWRkcgEia2V5U3Rha2VkVGltZUJ5VHlwZUFzc2V0SWRBbmRPd25lcgMHbmZ0VHlwZQdhc3NldElkCW93bmVyQWRkcgkArAICCQCsAgIJAKwCAgkArAICCQCsAgICIHN0YWtlZFRpbWVCeVR5cGVBc3NldElkQW5kT3duZXJfBQduZnRUeXBlAgFfBQdhc3NldElkAgFfBQlvd25lckFkZHIBEWtleUxhbmROdW1Ub093bmVyAQdsYW5kTnVtCQCsAgICCmxhbmRPd25lcl8FB2xhbmROdW0BEWtleUJhY2twYWNrQnlEdWNrAQtkdWNrQXNzZXRJZAkArAICAgliYWNrUGFja18FC2R1Y2tBc3NldElkAQ9rZXlEdWNrTG9jYXRpb24BC2R1Y2tBc3NldElkCQCsAgICDWR1Y2tMb2NhdGlvbl8FC2R1Y2tBc3NldElkAQ1rZXlEdWNrSGVhbHRoAQtkdWNrQXNzZXRJZAkArAICAgtkdWNrSGVhbHRoXwULZHVja0Fzc2V0SWQBEWtleVJlc1Byb3BvcnRpb25zAAITcmVzVHlwZXNQcm9wb3J0aW9ucwAKcmVjTGFuZE51bQAAAAtyZWNMYW5kU2l6ZQABAAtyZWNUZXJyYWlucwACAAxyZWNDb250aW5lbnQAAwAPbG9jSWR4Q29udGluZW50AAAACmxvY0lkeFR5cGUAAQAIbG9jSWR4SWQAAgAKYnBJZHhMZXZlbAAAAAhicElkeFJlcwABAAhicElkeE1hdAACAAlicElkeFByb2QAAwEIYXNTdHJpbmcBAXYEByRtYXRjaDAFAXYDCQABAgUHJG1hdGNoMAIGU3RyaW5nBAFzBQckbWF0Y2gwBQFzCQACAQIYZmFpbCB0byBjYXN0IGludG8gU3RyaW5nARJnZXROZWVkZWRNYXRlcmlhbHMBBXRvdGFsBAVwcm9wcwkAtQkCCQEFdmFsdWUBCQCiCAEJARFrZXlSZXNQcm9wb3J0aW9ucwACAV8DCQECIT0CCQCQAwEFBXByb3BzBQZOVU1SRVMJAAIBAhZXcm9uZyBwcm9wb3J0aW9ucyBkYXRhBAFyCQDMCAIJAQ1wYXJzZUludFZhbHVlAQkAkQMCBQVwcm9wcwAACQDMCAIJAQ1wYXJzZUludFZhbHVlAQkAkQMCBQVwcm9wcwABCQDMCAIJAQ1wYXJzZUludFZhbHVlAQkAkQMCBQVwcm9wcwACCQDMCAIJAQ1wYXJzZUludFZhbHVlAQkAkQMCBQVwcm9wcwADCQDMCAIJAQ1wYXJzZUludFZhbHVlAQkAkQMCBQVwcm9wcwAECQDMCAIJAQ1wYXJzZUludFZhbHVlAQkAkQMCBQVwcm9wcwAFBQNuaWwEA3N1bQkAZAIJAGQCCQBkAgkAZAIJAGQCCQCRAwIFAXIAAAkAkQMCBQFyAAEJAJEDAgUBcgACCQCRAwIFAXIAAwkAkQMCBQFyAAQJAJEDAgUBcgAFAwkAZwIAAAUDc3VtCQACAQIPTm8gbGFuZHMgc3Rha2VkBAVub3JtNgkAawMFBXRvdGFsBQVNVUxUNgUDc3VtCgEKbm9ybWFsaXplcgIDYWNjBGVsZW0JAM0IAgUDYWNjCQBrAwUEZWxlbQUFbm9ybTYFBU1VTFQ2CgACJGwFAXIKAAIkcwkAkAMBBQIkbAoABSRhY2MwBQNuaWwKAQUkZjBfMQICJGECJGkDCQBnAgUCJGkFAiRzBQIkYQkBCm5vcm1hbGl6ZXICBQIkYQkAkQMCBQIkbAUCJGkKAQUkZjBfMgICJGECJGkDCQBnAgUCJGkFAiRzBQIkYQkAAgECE0xpc3Qgc2l6ZSBleGNlZWRzIDYJAQUkZjBfMgIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIFBSRhY2MwAAAAAQACAAMABAAFAAYBEXN1YnRyYWN0TWF0ZXJpYWxzAwxzaG91bGRVc2VNYXQDaGFzCXRvdGFsTmVlZAQEbmVlZAkBEmdldE5lZWRlZE1hdGVyaWFscwEFCXRvdGFsTmVlZAoBCnN1YnRyYWN0b3ICA2FjYwNpZHgEBnJlc3VsdAkAZQIJAQ1wYXJzZUludFZhbHVlAQkAkQMCBQNoYXMFA2lkeAkAkQMCBQRuZWVkBQNpZHgDCQBmAgAABQZyZXN1bHQJAAIBCQCsAgIJAKwCAgkArAICCQCsAgIJAKwCAgIYTm90IGVub3VnaCBtYXRlcmlhbCBpZHg9CQCkAwEFA2lkeAILLCB5b3UgaGF2ZSAJAJEDAgUDaGFzBQNpZHgCCywgYnV0IG5lZWQgCQCkAwEJAJEDAgUEbmVlZAUDaWR4CQDNCAIFA2FjYwkApAMBBQZyZXN1bHQDBQxzaG91bGRVc2VNYXQKAAIkbAkAzAgCAAAJAMwIAgABCQDMCAIAAgkAzAgCAAMJAMwIAgAECQDMCAIABQUDbmlsCgACJHMJAJADAQUCJGwKAAUkYWNjMAUDbmlsCgEFJGYwXzECAiRhAiRpAwkAZwIFAiRpBQIkcwUCJGEJAQpzdWJ0cmFjdG9yAgUCJGEJAJEDAgUCJGwFAiRpCgEFJGYwXzICAiRhAiRpAwkAZwIFAiRpBQIkcwUCJGEJAAIBAhNMaXN0IHNpemUgZXhjZWVkcyA2CQEFJGYwXzICCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECBQUkYWNjMAAAAAEAAgADAAQABQAGBQNoYXMBEXVwZGF0ZVByb3BvcnRpb25zAw10ZXJyYWluQ291bnRzDWxhbmRTaXplSW5kZXgEc2lnbgQFcHJvcHMJALUJAgkBC3ZhbHVlT3JFbHNlAgkAoggBCQERa2V5UmVzUHJvcG9ydGlvbnMAAgswXzBfMF8wXzBfMAIBXwMJAQIhPQIJAJADAQUFcHJvcHMFBk5VTVJFUwkAAgECFldyb25nIHByb3BvcnRpb25zIGRhdGEKAQd1cGRhdGVyAgNhY2MBaQQGcmVzdWx0CQBkAgkBDXBhcnNlSW50VmFsdWUBCQCRAwIFBXByb3BzBQFpCQBoAgkAaAIFBHNpZ24JAJEDAgUNdGVycmFpbkNvdW50cwUBaQUNbGFuZFNpemVJbmRleAMJAGYCAAAFBnJlc3VsdAkAAgEJAKwCAgkArAICCQCsAgIJAKwCAgkArAICCQCsAgIJAKwCAgIWUGFuaWMhIFBpZWNlcyBvZiB0eXBlPQkApAMBBQFpAgcsIHNpZ249CQCkAwEFBHNpZ24CFCwgIHRlcnJhaW5Db3VudHNbaV09CQCkAwEJAJEDAgUNdGVycmFpbkNvdW50cwUBaQIQLCBsYW5kU2l6ZUluZGV4PQkApAMBBQ1sYW5kU2l6ZUluZGV4CQDNCAIFA2FjYwkApAMBBQZyZXN1bHQEAXIKAAIkbAkAzAgCAAAJAMwIAgABCQDMCAIAAgkAzAgCAAMJAMwIAgAECQDMCAIABQUDbmlsCgACJHMJAJADAQUCJGwKAAUkYWNjMAUDbmlsCgEFJGYwXzECAiRhAiRpAwkAZwIFAiRpBQIkcwUCJGEJAQd1cGRhdGVyAgUCJGEJAJEDAgUCJGwFAiRpCgEFJGYwXzICAiRhAiRpAwkAZwIFAiRpBQIkcwUCJGEJAAIBAhNMaXN0IHNpemUgZXhjZWVkcyA2CQEFJGYwXzICCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECBQUkYWNjMAAAAAEAAgADAAQABQAGCQC5CQIFAXICAV8BDWNvdW50VGVycmFpbnMBCHRlcnJhaW5zCQDMCAIJAGUCCQCQAwEJALUJAgUIdGVycmFpbnMCAUEAAQkAzAgCCQBlAgkAkAMBCQC1CQIFCHRlcnJhaW5zAgFCAAEJAMwIAgkAZQIJAJADAQkAtQkCBQh0ZXJyYWlucwIBQwABCQDMCAIJAGUCCQCQAwEJALUJAgUIdGVycmFpbnMCAUQAAQkAzAgCCQBlAgkAkAMBCQC1CQIFCHRlcnJhaW5zAgFFAAEJAMwIAgkAZQIJAJADAQkAtQkCBQh0ZXJyYWlucwIBRgABBQNuaWwBD251bVBpZWNlc0J5U2l6ZQEIbGFuZFNpemUEByRtYXRjaDAFCGxhbmRTaXplAwkAAAICAVMFByRtYXRjaDAAGQMJAAACAgFNBQckbWF0Y2gwAGQDCQAAAgIBTAUHJG1hdGNoMADhAQMJAAACAgJYTAUHJG1hdGNoMACQAwMJAAACAgNYWEwFByRtYXRjaDAA8QQJAAIBAhFVbmtub3duIGxhbmQgc2l6ZQEMc3ViT25lSW5MaXN0AwVhTGlzdANpZHgGYW1vdW50CgEGc3ViYmVyAgNhY2MBaQkAzQgCBQNhY2MDCQAAAgUBaQUDaWR4CQCkAwEJAGUCCQENcGFyc2VJbnRWYWx1ZQEJAJEDAgUFYUxpc3QFAWkFBmFtb3VudAkAkQMCBQVhTGlzdAUBaQQBcgoAAiRsCQDMCAIAAAkAzAgCAAEJAMwIAgACCQDMCAIAAwkAzAgCAAQJAMwIAgAFBQNuaWwKAAIkcwkAkAMBBQIkbAoABSRhY2MwBQNuaWwKAQUkZjBfMQICJGECJGkDCQBnAgUCJGkFAiRzBQIkYQkBBnN1YmJlcgIFAiRhCQCRAwIFAiRsBQIkaQoBBSRmMF8yAgIkYQIkaQMJAGcCBQIkaQUCJHMFAiRhCQACAQITTGlzdCBzaXplIGV4Y2VlZHMgNgkBBSRmMF8yAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgUFJGFjYzAAAAABAAIAAwAEAAUABgkAuQkCBQFyAgFfAQZhZGRSZXMECmN1cnJlbnRSZXMNdGVycmFpbkNvdW50cwlkZWx0YVRpbWUNbGFuZFNpemVJbmRleAoBBWFkZGVyAgNhY2MBaQQJcmVzT2ZUeXBlCQBoAgkAaAIJAGsDBQlkZWx0YVRpbWUFD0RBSUxZUkVTQllQSUVDRQUJREFZTUlMTElTCQCRAwIFDXRlcnJhaW5Db3VudHMFAWkFDWxhbmRTaXplSW5kZXgJAM0IAgUDYWNjCQCkAwEJAGQCCQENcGFyc2VJbnRWYWx1ZQEJAJEDAgUKY3VycmVudFJlcwUBaQUJcmVzT2ZUeXBlBAFyCgACJGwJAMwIAgAACQDMCAIAAQkAzAgCAAIJAMwIAgADCQDMCAIABAkAzAgCAAUFA25pbAoAAiRzCQCQAwEFAiRsCgAFJGFjYzAFA25pbAoBBSRmMF8xAgIkYQIkaQMJAGcCBQIkaQUCJHMFAiRhCQEFYWRkZXICBQIkYQkAkQMCBQIkbAUCJGkKAQUkZjBfMgICJGECJGkDCQBnAgUCJGkFAiRzBQIkYQkAAgECE0xpc3Qgc2l6ZSBleGNlZWRzIDYJAQUkZjBfMgIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIFBSRhY2MwAAAAAQACAAMABAAFAAYJALkJAgUBcgIBXwEDYWJzAQF4AwkAwAICBQF4CQC2AgEAAAUBeAkAvgIBBQF4AARmcmVxCQDMCAIJAMwIAgABCQDMCAIABAkAzAgCAAkJAMwIAgAKCQDMCAIADwUDbmlsCQDMCAIJAMwIAgAFCQDMCAIACAkAzAgCAA0JAMwIAgAOCQDMCAIADwUDbmlsCQDMCAIJAMwIAgAGCQDMCAIACQkAzAgCAA4JAMwIAgAPCQDMCAIAEAUDbmlsCQDMCAIJAMwIAgAECQDMCAIABwkAzAgCAAgJAMwIAgANCQDMCAIAEgUDbmlsCQDMCAIJAMwIAgABCQDMCAIABgkAzAgCAAcJAMwIAgAPCQDMCAIAEwUDbmlsBQNuaWwBB2dlbkNoYXICAW4FZnJlcXMEA3JlbQkAoAMBCQC7AgIFAW4FB1RXRU5UWVgEBmxldHRlcgMJAGYCCQCRAwIFBWZyZXFzAAAFA3JlbQIBQQMJAGYCCQCRAwIFBWZyZXFzAAEFA3JlbQIBQgMJAGYCCQCRAwIFBWZyZXFzAAIFA3JlbQIBQwMJAGYCCQCRAwIFBWZyZXFzAAMFA3JlbQIBRAMJAGYCCQCRAwIFBWZyZXFzAAQFA3JlbQIBRQIBRgUGbGV0dGVyAQtnZXRCYWNrcGFjawEFYnBLZXkEAXAJALUJAgkBC3ZhbHVlT3JFbHNlAgkAoggBBQVicEtleQIaMDowXzBfMF8wXzBfMDowXzBfMF8wXzBfMDoCAToJAMwIAgkApAMBCQELdmFsdWVPckVsc2UCCQC2CQEJAJEDAgUBcAUKYnBJZHhMZXZlbAAACQDMCAIDCQAAAgkAkAMBCQC1CQIJAJEDAgUBcAUIYnBJZHhSZXMCAV8FBk5VTVJFUwkAkQMCBQFwBQhicElkeFJlcwILMF8wXzBfMF8wXzAJAMwIAgMJAAACCQCQAwEJALUJAgkAkQMCBQFwBQhicElkeE1hdAIBXwUGTlVNUkVTCQCRAwIFAXAFCGJwSWR4TWF0AgswXzBfMF8wXzBfMAkAzAgCCQCRAwIFAXAFCWJwSWR4UHJvZAUDbmlsARBleHBlZGl0aW9uQ29tbW9uBQxzaG91bGRVc2VNYXQGY2FsbGVyBHR4SWQHbWVzc2FnZQNzaWcDCQEBIQEJAMQTAwUHbWVzc2FnZQUDc2lnBQNwdWIJAAIBAhhzaWduYXR1cmUgZG9lcyBub3QgbWF0Y2gEBXBhcnRzCQC1CQIJALAJAQUHbWVzc2FnZQIBOwQCaHAJALUJAgkAkQMCCQC1CQIJAJEDAgUFcGFydHMAAAIBfAAAAgFfBAVjdXJIUAkBDXBhcnNlSW50VmFsdWUBCQCRAwIFAmhwAAAEBW5ld0hQCQENcGFyc2VJbnRWYWx1ZQEJAJEDAgUCaHAAAQQKbG9jQW5kVGltZQkAtQkCCQCRAwIFBXBhcnRzAAECAToEDnRhcmdldExvY2F0aW9uCQC1CQIJAJEDAgUKbG9jQW5kVGltZQAAAgFfAwkBAiE9AgkAkQMCBQ50YXJnZXRMb2NhdGlvbgABAgFFCQACAQIrZXhwZWRpdGlvbiB0YXJnZXQgbG9jYXRpb24gdHlwZSBzaG91bGQgYmUgRQQEdGltZQkBDXBhcnNlSW50VmFsdWUBCQCRAwIFCmxvY0FuZFRpbWUAAQMDCQBmAgUEdGltZQkAZAIIBQlsYXN0QmxvY2sJdGltZXN0YW1wBRFGSVZFTUlOVVRFU01JTExJUwYJAGYCCQBlAggFCWxhc3RCbG9jawl0aW1lc3RhbXAFEUZJVkVNSU5VVEVTTUlMTElTBQR0aW1lCQACAQISc2lnbmF0dXJlIG91dGRhdGVkBAh1c2VyQWRkcgkApQgBBQZjYWxsZXIEC2R1Y2tBc3NldElkCQETdmFsdWVPckVycm9yTWVzc2FnZQIJAKIIAQkBFGtleVN0YWtlZER1Y2tCeU93bmVyAQUIdXNlckFkZHICHFlvdSBkb24ndCBoYXZlIGEgZHVjayBzdGFrZWQECWtleUhlYWx0aAkBDWtleUR1Y2tIZWFsdGgBBQtkdWNrQXNzZXRJZAQMb2xkRnJvbVN0YXRlCQELdmFsdWVPckVsc2UCCQCfCAEFCWtleUhlYWx0aABkAwkBAiE9AgUMb2xkRnJvbVN0YXRlBQVjdXJIUAkAAgEJAKwCAgkArAICCQCsAgICCm9sZEhlYWx0aD0JAKQDAQkBC3ZhbHVlT3JFbHNlAgkAnwgBBQlrZXlIZWFsdGgAZAIvIGZyb20gc3RhdGUgZG9lcyBub3QgbWF0Y2ggb25lIGZyb20gZmxpZ2h0IGxvZz0JAKQDAQUFY3VySFADCQBnAgAABQVjdXJIUAkAAgECHllvdSBjYW4ndCBmbHkgd2l0aCB6ZXJvIGhlYWx0aAMJAGcCAAAFBW5ld0hQCQACAQIrWW91ciBkdWNrIGhlYWx0aCBpcyB6ZXJvLCBleHBlZGl0aW9uIGZhaWxlZAQFYnBLZXkJARFrZXlCYWNrcGFja0J5RHVjawEFC2R1Y2tBc3NldElkBAtjdXJyZW50UGFjawkBC2dldEJhY2twYWNrAQUFYnBLZXkEBW1MaXN0CQC1CQIJAJEDAgULY3VycmVudFBhY2sFCGJwSWR4TWF0AgFfBAZuZXdNYXQJALkJAgkBEXN1YnRyYWN0TWF0ZXJpYWxzAwUMc2hvdWxkVXNlTWF0BQVtTGlzdAUMRVhQTUFURVJJQUxTAgFfBAZiaWdOdW0JAQNhYnMBCQCeAwEFBHR4SWQEB2ZyZWVOdW0JAQt2YWx1ZU9yRWxzZQIJAJ8IAQkBEmtleU5leHRGcmVlTGFuZE51bQAA9QMEB2xhbmROdW0JAKQDAQUHZnJlZU51bQQMY29udGluZW50SWR4CQCgAwEJALsCAgUGYmlnTnVtBQVGSVZFWAQBZgkAkQMCBQRmcmVxBQxjb250aW5lbnRJZHgKARB0ZXJyYWluR2VuZXJhdG9yAgNhY2MEZWxlbQkAlAoCCQCsAgIJAKwCAgkArAICCQCsAgIJAKwCAggFA2FjYwJfMQkBB2dlbkNoYXICCAUDYWNjAl8yBQFmCQEHZ2VuQ2hhcgIJALoCAggFA2FjYwJfMgUHVFdFTlRZWAUBZgkBB2dlbkNoYXICCQC6AgIIBQNhY2MCXzIFCFRXRU5UWTJYBQFmCQEHZ2VuQ2hhcgIJALoCAggFA2FjYwJfMgUIVFdFTlRZM1gFAWYJAQdnZW5DaGFyAgkAugICCAUDYWNjAl8yBQhUV0VOVFk0WAUBZgkAugICCAUDYWNjAl8yBQhUV0VOVFk1WAQBdAoAAiRsCQDMCAIAAQkAzAgCAAIJAMwIAgADCQDMCAIABAkAzAgCAAUFA25pbAoAAiRzCQCQAwEFAiRsCgAFJGFjYzAJAJQKAgIACQC6AgIFBmJpZ051bQUFRklWRVgKAQUkZjBfMQICJGECJGkDCQBnAgUCJGkFAiRzBQIkYQkBEHRlcnJhaW5HZW5lcmF0b3ICBQIkYQkAkQMCBQIkbAUCJGkKAQUkZjBfMgICJGECJGkDCQBnAgUCJGkFAiRzBQIkYQkAAgECE0xpc3Qgc2l6ZSBleGNlZWRzIDUJAQUkZjBfMgIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIFBSRhY2MwAAAAAQACAAMABAAFBAljb250aW5lbnQJAJEDAgUKY29udGluZW50cwUMY29udGluZW50SWR4BAVpc3N1ZQkAwggFCQEKa2V5TmZ0TmFtZQIFB2xhbmROdW0CAVMJALkJAgkAzAgCBQdsYW5kTnVtCQDMCAICAVMJAMwIAggFAXQCXzEJAMwIAgUJY29udGluZW50BQNuaWwCAV8AAQAABwQHYXNzZXRJZAkAuAgBBQVpc3N1ZQQCaWQJANgEAQUHYXNzZXRJZAkAlAoCCQDMCAIJAQxJbnRlZ2VyRW50cnkCCQESa2V5TmV4dEZyZWVMYW5kTnVtAAkAZAIFB2ZyZWVOdW0AAQkAzAgCBQVpc3N1ZQkAzAgCCQELU3RyaW5nRW50cnkCCQEQa2V5TGFuZFRvQXNzZXRJZAEFB2xhbmROdW0FAmlkCQDMCAIJAQtTdHJpbmdFbnRyeQIJARVrZXlMYW5kQXNzZXRJZFRvT3duZXIBBQJpZAUIdXNlckFkZHIJAMwIAgkBC1N0cmluZ0VudHJ5AgkBEWtleUxhbmROdW1Ub093bmVyAQUHbGFuZE51bQUIdXNlckFkZHIJAMwIAgkBDEludGVnZXJFbnRyeQIJARZrZXlJbmZyYUxldmVsQnlBc3NldElkAQUCaWQAAAkAzAgCCQEOU2NyaXB0VHJhbnNmZXIDBQZjYWxsZXIAAQUHYXNzZXRJZAkAzAgCCQELU3RyaW5nRW50cnkCCQEPa2V5RHVja0xvY2F0aW9uAQULZHVja0Fzc2V0SWQJALkJAgkAzAgCBQljb250aW5lbnQJAMwIAgIBTAkAzAgCBQJpZAUDbmlsAgFfCQDMCAIJAQxJbnRlZ2VyRW50cnkCBQlrZXlIZWFsdGgFBW5ld0hQCQDMCAIJAQtTdHJpbmdFbnRyeQIFBWJwS2V5CQC5CQIJAMwIAgkAkQMCBQtjdXJyZW50UGFjawUKYnBJZHhMZXZlbAkAzAgCCQCRAwIFC2N1cnJlbnRQYWNrBQhicElkeFJlcwkAzAgCBQZuZXdNYXQJAMwIAgkAkQMCBQtjdXJyZW50UGFjawUJYnBJZHhQcm9kBQNuaWwCAToFA25pbAUEdW5pdAENdXBJbmZyYUNvbW1vbgMMc2hvdWxkVXNlTWF0BmNhbGxlcgtsYW5kQXNzZXRJZAQEYWRkcgkApQgBBQZjYWxsZXIECGluZnJhS2V5CQEWa2V5SW5mcmFMZXZlbEJ5QXNzZXRJZAEFC2xhbmRBc3NldElkBAhjdXJMZXZlbAkBC3ZhbHVlT3JFbHNlAgkAnwgBBQhpbmZyYUtleQAAAwkAZwIFCGN1ckxldmVsAAMJAAIBAiZDdXJyZW50bHkgbWF4IGluZnJhc3RydWN0dXJlIGxldmVsID0gMwQIbmV3TGV2ZWwJAGQCBQhjdXJMZXZlbAABBAVhc3NldAkBBXZhbHVlAQkA7AcBCQDZBAEFC2xhbmRBc3NldElkAwkBASEBCQEIY29udGFpbnMCCAUFYXNzZXQEbmFtZQUKTEFORFBSRUZJWAkAAgEJAKwCAgkArAICAgRORlQgBQpMQU5EUFJFRklYAiAgdG9rZW4gc2hvdWxkIGJlIHBhc3NlZCBhcyBwYXJhbQQHdGltZUtleQkBFmtleVN0YWtlZFRpbWVCeUFzc2V0SWQBBQtsYW5kQXNzZXRJZAQFb3duZXIJARFAZXh0ck5hdGl2ZSgxMDU4KQEJARVrZXlMYW5kQXNzZXRJZFRvT3duZXIBBQtsYW5kQXNzZXRJZAMJAQIhPQIFBW93bmVyBQRhZGRyCQACAQkArAICBQpMQU5EUFJFRklYAg0gaXMgbm90IHlvdXJzBAFkCQC1CQIIBQVhc3NldAtkZXNjcmlwdGlvbgIBXwQIbGFuZFNpemUJAJEDAgUBZAULcmVjTGFuZFNpemUEBnBpZWNlcwkBD251bVBpZWNlc0J5U2l6ZQEFCGxhbmRTaXplBAtkdWNrQXNzZXRJZAkBE3ZhbHVlT3JFcnJvck1lc3NhZ2UCCQCiCAEJARRrZXlTdGFrZWREdWNrQnlPd25lcgEFBGFkZHICHFlvdSBkb24ndCBoYXZlIGEgZHVjayBzdGFrZWQEC2N1ckxvY2F0aW9uCQELdmFsdWVPckVsc2UCCQCiCAEJAQ9rZXlEdWNrTG9jYXRpb24BBQtkdWNrQXNzZXRJZAUPREVGQVVMVExPQ0FUSU9OBANsb2MJALUJAgkBBXZhbHVlAQULY3VyTG9jYXRpb24CAV8DCQECIT0CCQCRAwIFA2xvYwUKbG9jSWR4VHlwZQIBTAkAAgEJAKwCAgkArAICAhZEdWNrIGxvY2F0aW9uIHR5cGUgaXMgCQCRAwIFA2xvYwUKbG9jSWR4VHlwZQIRLCBidXQgc2hvdWxkIGJlIEwDCQECIT0CCQCRAwIFA2xvYwUIbG9jSWR4SWQFC2xhbmRBc3NldElkCQACAQkArAICCQCsAgIJAKwCAgIURHVjayBsb2NhdGlvbiBpZCBpcyAJAJEDAgUDbG9jBQhsb2NJZHhJZAIQLCBidXQgc2hvdWxkIGJlIAULbGFuZEFzc2V0SWQEBWJwS2V5CQERa2V5QmFja3BhY2tCeUR1Y2sBBQtkdWNrQXNzZXRJZAQLY3VycmVudFBhY2sJAQtnZXRCYWNrcGFjawEFBWJwS2V5BAVtTGlzdAkAtQkCCQCRAwIFC2N1cnJlbnRQYWNrBQhicElkeE1hdAIBXwQGbmV3TWF0CQC5CQIJARFzdWJ0cmFjdE1hdGVyaWFscwMFDHNob3VsZFVzZU1hdAUFbUxpc3QJAGsDBRFJbmZyYVVwZ3JhZGVDb3N0UwUGcGllY2VzABkCAV8ECXNhdmVkVGltZQkBE3ZhbHVlT3JFcnJvck1lc3NhZ2UCCQCfCAEFB3RpbWVLZXkJAKwCAgkArAICAgRORlQgCAUFYXNzZXQEbmFtZQIOIGlzIG5vdCBzdGFrZWQECWRlbHRhVGltZQkAZQIIBQlsYXN0QmxvY2sJdGltZXN0YW1wBQlzYXZlZFRpbWUDCQBmAgAABQlkZWx0YVRpbWUJAAIBCQCsAgIJAKwCAgkArAICAiZTYXZlZCB0aW1lc3RhbXAgaXMgaW4gZnV0dXJlLCBzYXZlZCA9IAkApAMBBQlzYXZlZFRpbWUCDCwgY3VycmVudCA9IAkApAMBCAUJbGFzdEJsb2NrCXRpbWVzdGFtcAQIYXZhaWxSZXMJAGgCCQBrAwUJZGVsdGFUaW1lBQ9EQUlMWVJFU0JZUElFQ0UFCURBWU1JTExJUwUGcGllY2VzBAZuZXdSZXMJAQhhc1N0cmluZwEJAPwHBAUEdGhpcwIIY2xhaW1SZXMJAMwIAgUIYXZhaWxSZXMJAMwIAgULbGFuZEFzc2V0SWQFA25pbAUDbmlsCQCUCgIJAMwIAgkBDEludGVnZXJFbnRyeQIFCGluZnJhS2V5BQhuZXdMZXZlbAkAzAgCCQELU3RyaW5nRW50cnkCBQVicEtleQkAuQkCCQDMCAIJAJEDAgULY3VycmVudFBhY2sFCmJwSWR4TGV2ZWwJAMwIAgUGbmV3UmVzCQDMCAIFBm5ld01hdAkAzAgCCQCRAwIFC2N1cnJlbnRQYWNrBQlicElkeFByb2QFA25pbAIBOgUDbmlsBQhuZXdMZXZlbA0BaQEJc3Rha2VMYW5kAAQDcG10CQEFdmFsdWUBCQCRAwIIBQFpCHBheW1lbnRzAAAEB2Fzc2V0SWQJAQV2YWx1ZQEIBQNwbXQHYXNzZXRJZAQHYWRkcmVzcwkApQgBCAUBaQZjYWxsZXIDCQECIT0CCAUDcG10BmFtb3VudAABCQACAQkArAICCQCsAgICBE5GVCAFCkxBTkRQUkVGSVgCJCB0b2tlbiBzaG91bGQgYmUgYXR0YWNoZWQgYXMgcGF5bWVudAQFYXNzZXQJAQV2YWx1ZQEJAOwHAQUHYXNzZXRJZAMJAQIhPQIIBQVhc3NldAZpc3N1ZXIFBHRoaXMJAAIBAhdVbmtub3duIGlzc3VlciBvZiB0b2tlbgMJAQEhAQkBCGNvbnRhaW5zAggFBWFzc2V0BG5hbWUFCkxBTkRQUkVGSVgJAAIBCQCsAgIJAKwCAgIJT25seSBORlQgBQpMQU5EUFJFRklYAhQgdG9rZW5zIGFyZSBhY2NlcHRlZAQLbGFuZE51bVNpemUJALACAggFBWFzc2V0BG5hbWUABAQHbGFuZE51bQMJAQhjb250YWlucwIFC2xhbmROdW1TaXplAgNYWEwJALMCAgULbGFuZE51bVNpemUAAwMJAQhjb250YWlucwIFC2xhbmROdW1TaXplAgJYTAkAswICBQtsYW5kTnVtU2l6ZQACCQCzAgIFC2xhbmROdW1TaXplAAEDCQEBIQEJAQlpc0RlZmluZWQBCQC2CQEFB2xhbmROdW0JAAIBCQCsAgICHkNhbm5vdCBwYXJzZSBsYW5kIG51bWJlciBmcm9tIAgFBWFzc2V0BG5hbWUEB3RpbWVLZXkJARZrZXlTdGFrZWRUaW1lQnlBc3NldElkAQkA2AQBBQdhc3NldElkAwkBCWlzRGVmaW5lZAEJAJ8IAQUHdGltZUtleQkAAgEJAKwCAgkArAICAgRORlQgCAUFYXNzZXQEbmFtZQISIGlzIGFscmVhZHkgc3Rha2VkBAFkCQC1CQIIBQVhc3NldAtkZXNjcmlwdGlvbgIBXwQNdGVycmFpbkNvdW50cwkBDWNvdW50VGVycmFpbnMBCQCRAwIFAWQFC3JlY1RlcnJhaW5zBAVwcm9wcwkBEXVwZGF0ZVByb3BvcnRpb25zAwUNdGVycmFpbkNvdW50cwkAaQIJAQ9udW1QaWVjZXNCeVNpemUBCQCRAwIFAWQFC3JlY0xhbmRTaXplABkAAQkAzAgCCQEMSW50ZWdlckVudHJ5AgUHdGltZUtleQgFCWxhc3RCbG9jawl0aW1lc3RhbXAJAMwIAgkBDEludGVnZXJFbnRyeQIJASJrZXlTdGFrZWRUaW1lQnlUeXBlQXNzZXRJZEFuZE93bmVyAwUKTEFORFBSRUZJWAkA2AQBBQdhc3NldElkBQdhZGRyZXNzCAUJbGFzdEJsb2NrCXRpbWVzdGFtcAkAzAgCCQELU3RyaW5nRW50cnkCCQEVa2V5TGFuZEFzc2V0SWRUb093bmVyAQkA2AQBBQdhc3NldElkBQdhZGRyZXNzCQDMCAIJAQtTdHJpbmdFbnRyeQIJARFrZXlMYW5kTnVtVG9Pd25lcgEFB2xhbmROdW0FB2FkZHJlc3MJAMwIAgkBC1N0cmluZ0VudHJ5AgkBEWtleVJlc1Byb3BvcnRpb25zAAUFcHJvcHMFA25pbAFpAQt1bnN0YWtlTGFuZAELbGFuZEFzc2V0SWQDCQECIT0CCQCQAwEIBQFpCHBheW1lbnRzAAAJAAIBAiR1bnN0YWtlIGRvZXNuJ3QgcmVxdWlyZSBhbnkgcGF5bWVudHMEB2Fzc2V0SWQJANkEAQULbGFuZEFzc2V0SWQEB2FkZHJlc3MJAKUIAQgFAWkGY2FsbGVyBAVhc3NldAkBBXZhbHVlAQkA7AcBBQdhc3NldElkAwkBAiE9AggFBWFzc2V0Bmlzc3VlcgUEdGhpcwkAAgECF1Vua25vd24gaXNzdWVyIG9mIHRva2VuAwkBASEBCQEIY29udGFpbnMCCAUFYXNzZXQEbmFtZQUKTEFORFBSRUZJWAkAAgEJAKwCAgkArAICAglPbmx5IE5GVCAFCkxBTkRQUkVGSVgCFyB0b2tlbnMgY2FuIGJlIHVuc3Rha2VkBAd0aW1lS2V5CQEWa2V5U3Rha2VkVGltZUJ5QXNzZXRJZAEFC2xhbmRBc3NldElkAwkBASEBCQEJaXNEZWZpbmVkAQUHdGltZUtleQkAAgEJAKwCAgkArAICAgRORlQgCAUFYXNzZXQEbmFtZQIOIGlzIG5vdCBzdGFrZWQEBW93bmVyCQETdmFsdWVPckVycm9yTWVzc2FnZQIJAKIIAQkBFWtleUxhbmRBc3NldElkVG9Pd25lcgEFC2xhbmRBc3NldElkCQCsAgIJAKwCAgIETkZUIAgFBWFzc2V0BG5hbWUCDCBpcyBvcnBoYW5lZAMJAQIhPQIFBW93bmVyBQdhZGRyZXNzCQACAQIXU3Rha2VkIE5GVCBpcyBub3QgeW91cnMEAWQJALUJAggFBWFzc2V0C2Rlc2NyaXB0aW9uAgFfBA10ZXJyYWluQ291bnRzCQENY291bnRUZXJyYWlucwEJAJEDAgUBZAULcmVjVGVycmFpbnMEBXByb3BzCQERdXBkYXRlUHJvcG9ydGlvbnMDBQ10ZXJyYWluQ291bnRzCQBpAgkBD251bVBpZWNlc0J5U2l6ZQEJAJEDAgUBZAULcmVjTGFuZFNpemUAGQD///////////8BCQDMCAIJAQ5TY3JpcHRUcmFuc2ZlcgMIBQFpBmNhbGxlcgABBQdhc3NldElkCQDMCAIJAQtEZWxldGVFbnRyeQEFB3RpbWVLZXkJAMwIAgkBC0RlbGV0ZUVudHJ5AQkBImtleVN0YWtlZFRpbWVCeVR5cGVBc3NldElkQW5kT3duZXIDBQpMQU5EUFJFRklYBQtsYW5kQXNzZXRJZAUHYWRkcmVzcwkAzAgCCQELU3RyaW5nRW50cnkCCQERa2V5UmVzUHJvcG9ydGlvbnMABQVwcm9wcwUDbmlsAWkBCXN0YWtlRHVjawAEA3BtdAkBBXZhbHVlAQkAkQMCCAUBaQhwYXltZW50cwAABAdhc3NldElkCQEFdmFsdWUBCAUDcG10B2Fzc2V0SWQEB2FkZHJlc3MJAKUIAQgFAWkGY2FsbGVyAwkBAiE9AggFA3BtdAZhbW91bnQAAQkAAgEJAKwCAgkArAICAgRORlQgBQpEVUNLUFJFRklYAiQgdG9rZW4gc2hvdWxkIGJlIGF0dGFjaGVkIGFzIHBheW1lbnQEBWFzc2V0CQEFdmFsdWUBCQDsBwEFB2Fzc2V0SWQDAwkBAiE9AggFBWFzc2V0Bmlzc3VlcgUNaW5jdWJhdG9yQWRkcgkBAiE9AggFBWFzc2V0Bmlzc3VlcgULYnJlZWRlckFkZHIHCQACAQkArAICCQCsAgICElVua25vd24gaXNzdWVyIG9mIAUKRFVDS1BSRUZJWAIGIHRva2VuAwkBASEBCQEIY29udGFpbnMCCAUFYXNzZXQEbmFtZQUKRFVDS1BSRUZJWAkAAgEJAKwCAgkArAICAglPbmx5IE5GVCAFCkRVQ0tQUkVGSVgCFCB0b2tlbnMgYXJlIGFjY2VwdGVkBAphc3NldElkU3RyCQDYBAEFB2Fzc2V0SWQEB3RpbWVLZXkJARZrZXlTdGFrZWRUaW1lQnlBc3NldElkAQUKYXNzZXRJZFN0cgMJAQlpc0RlZmluZWQBCQCfCAEFB3RpbWVLZXkJAAIBCQCsAgIJAKwCAgIETkZUIAgFBWFzc2V0BG5hbWUCEiBpcyBhbHJlYWR5IHN0YWtlZAMJAQlpc0RlZmluZWQBCQCiCAEJARRrZXlTdGFrZWREdWNrQnlPd25lcgEFB2FkZHJlc3MJAAIBCQCsAgICHVlvdSBhbHJlYWR5IHN0YWtlZCBvbmUgZHVjazogCAUFYXNzZXQEbmFtZQQGbG9jS2V5CQEPa2V5RHVja0xvY2F0aW9uAQUKYXNzZXRJZFN0cgQIbG9jYXRpb24JAKIIAQUGbG9jS2V5BAlrZXlIZWFsdGgJAQ1rZXlEdWNrSGVhbHRoAQUKYXNzZXRJZFN0cgQGaGVhbHRoCQCfCAEFCWtleUhlYWx0aAQFYnBLZXkJARFrZXlCYWNrcGFja0J5RHVjawEFCmFzc2V0SWRTdHIECGJhY2twYWNrCQCiCAEFBWJwS2V5CQDOCAIJAMwIAgkBDEludGVnZXJFbnRyeQIFB3RpbWVLZXkIBQlsYXN0QmxvY2sJdGltZXN0YW1wCQDMCAIJAQxJbnRlZ2VyRW50cnkCCQEia2V5U3Rha2VkVGltZUJ5VHlwZUFzc2V0SWRBbmRPd25lcgMFCkRVQ0tQUkVGSVgJANgEAQUHYXNzZXRJZAUHYWRkcmVzcwgFCWxhc3RCbG9jawl0aW1lc3RhbXAJAMwIAgkBC1N0cmluZ0VudHJ5AgkBEGtleUR1Y2tJZFRvT3duZXIBBQphc3NldElkU3RyBQdhZGRyZXNzCQDMCAIJAQtTdHJpbmdFbnRyeQIJARRrZXlTdGFrZWREdWNrQnlPd25lcgEFB2FkZHJlc3MFCmFzc2V0SWRTdHIFA25pbAMJAQlpc0RlZmluZWQBBQhsb2NhdGlvbgUDbmlsCQDOCAIJAMwIAgkBC1N0cmluZ0VudHJ5AgUGbG9jS2V5BQ9ERUZBVUxUTE9DQVRJT04FA25pbAMJAQlpc0RlZmluZWQBBQZoZWFsdGgFA25pbAkAzggCCQDMCAIJAQxJbnRlZ2VyRW50cnkCBQlrZXlIZWFsdGgAZAUDbmlsAwkBCWlzRGVmaW5lZAEFCGJhY2twYWNrBQNuaWwJAMwIAgkBC1N0cmluZ0VudHJ5AgUFYnBLZXkCGjA6MF8wXzBfMF8wXzA6MF8wXzBfMF8wXzA6BQNuaWwBaQELdW5zdGFrZUR1Y2sBCmFzc2V0SWRTdHIDCQECIT0CCQCQAwEIBQFpCHBheW1lbnRzAAAJAAIBAiR1bnN0YWtlIGRvZXNuJ3QgcmVxdWlyZSBhbnkgcGF5bWVudHMEB2Fzc2V0SWQJANkEAQUKYXNzZXRJZFN0cgQHYWRkcmVzcwkApQgBCAUBaQZjYWxsZXIEBWFzc2V0CQEFdmFsdWUBCQDsBwEFB2Fzc2V0SWQDAwkBAiE9AggFBWFzc2V0Bmlzc3VlcgUNaW5jdWJhdG9yQWRkcgkBAiE9AggFBWFzc2V0Bmlzc3VlcgULYnJlZWRlckFkZHIHCQACAQkArAICCQCsAgICElVua25vd24gaXNzdWVyIG9mIAUKRFVDS1BSRUZJWAIGIHRva2VuAwkBASEBCQEIY29udGFpbnMCCAUFYXNzZXQEbmFtZQUKRFVDS1BSRUZJWAkAAgEJAKwCAgkArAICAglPbmx5IE5GVCAFCkRVQ0tQUkVGSVgCFyB0b2tlbnMgY2FuIGJlIHVuc3Rha2VkBAd0aW1lS2V5CQEWa2V5U3Rha2VkVGltZUJ5QXNzZXRJZAEJANgEAQUHYXNzZXRJZAMJAQEhAQkBCWlzRGVmaW5lZAEFB3RpbWVLZXkJAAIBCQCsAgIJAKwCAgIETkZUIAgFBWFzc2V0BG5hbWUCDiBpcyBub3Qgc3Rha2VkAwkBASEBCQEJaXNEZWZpbmVkAQkBFGtleVN0YWtlZER1Y2tCeU93bmVyAQUHYWRkcmVzcwkAAgEJAKwCAgkArAICAglUaGUgZHVjayAIBQVhc3NldARuYW1lAg4gaXMgbm90IHN0YWtlZAQFb3duZXIJARN2YWx1ZU9yRXJyb3JNZXNzYWdlAgkAoggBCQEQa2V5RHVja0lkVG9Pd25lcgEJANgEAQUHYXNzZXRJZAkArAICCQCsAgICBE5GVCAIBQVhc3NldARuYW1lAgwgaXMgb3JwaGFuZWQDCQECIT0CBQVvd25lcgUHYWRkcmVzcwkAAgECF1N0YWtlZCBORlQgaXMgbm90IHlvdXJzCQDMCAIJAQ5TY3JpcHRUcmFuc2ZlcgMIBQFpBmNhbGxlcgABBQdhc3NldElkCQDMCAIJAQtEZWxldGVFbnRyeQEFB3RpbWVLZXkJAMwIAgkBC0RlbGV0ZUVudHJ5AQkBD2tleUR1Y2tMb2NhdGlvbgEFCmFzc2V0SWRTdHIJAMwIAgkBC0RlbGV0ZUVudHJ5AQkBImtleVN0YWtlZFRpbWVCeVR5cGVBc3NldElkQW5kT3duZXIDBQpEVUNLUFJFRklYBQphc3NldElkU3RyBQdhZGRyZXNzCQDMCAIJAQtEZWxldGVFbnRyeQEJARRrZXlTdGFrZWREdWNrQnlPd25lcgEFB2FkZHJlc3MFA25pbAFpAQhjbGFpbVJlcwIGYW1vdW50C2xhbmRBc3NldElkAwkBAiE9AgkAkAMBCAUBaQhwYXltZW50cwAACQACAQIlY2xhaW1SZXMgZG9lc24ndCByZXF1aXJlIGFueSBwYXltZW50cwQEYWRkcgkApQgBCAUBaQZjYWxsZXIEBWFzc2V0CQEFdmFsdWUBCQDsBwEJANkEAQULbGFuZEFzc2V0SWQDCQEBIQEJAQhjb250YWlucwIIBQVhc3NldARuYW1lBQpMQU5EUFJFRklYCQACAQkArAICCQCsAgICBE5GVCAFCkxBTkRQUkVGSVgCICB0b2tlbiBzaG91bGQgYmUgcGFzc2VkIGFzIHBhcmFtBAd0aW1lS2V5CQEWa2V5U3Rha2VkVGltZUJ5QXNzZXRJZAEFC2xhbmRBc3NldElkBAVvd25lcgkBEUBleHRyTmF0aXZlKDEwNTgpAQkBFWtleUxhbmRBc3NldElkVG9Pd25lcgEFC2xhbmRBc3NldElkAwkBAiE9AgUFb3duZXIFBGFkZHIJAAIBCQCsAgIFCkxBTkRQUkVGSVgCDSBpcyBub3QgeW91cnMEAWQJALUJAggFBWFzc2V0C2Rlc2NyaXB0aW9uAgFfBAhsYW5kU2l6ZQkAkQMCBQFkBQtyZWNMYW5kU2l6ZQQNdGVycmFpbkNvdW50cwkBDWNvdW50VGVycmFpbnMBCQCRAwIFAWQFC3JlY1RlcnJhaW5zBAtkdWNrQXNzZXRJZAkBE3ZhbHVlT3JFcnJvck1lc3NhZ2UCCQCiCAEJARRrZXlTdGFrZWREdWNrQnlPd25lcgEFBGFkZHICHFlvdSBkb24ndCBoYXZlIGEgZHVjayBzdGFrZWQEC2N1ckxvY2F0aW9uCQELdmFsdWVPckVsc2UCCQCiCAEJAQ9rZXlEdWNrTG9jYXRpb24BBQtkdWNrQXNzZXRJZAUPREVGQVVMVExPQ0FUSU9OBANsb2MJALUJAgkBBXZhbHVlAQULY3VyTG9jYXRpb24CAV8DCQECIT0CCQCRAwIFA2xvYwUKbG9jSWR4VHlwZQIBTAkAAgEJAKwCAgkArAICAhZEdWNrIGxvY2F0aW9uIHR5cGUgaXMgCQCRAwIFA2xvYwUKbG9jSWR4VHlwZQIRLCBidXQgc2hvdWxkIGJlIEwDCQECIT0CCQCRAwIFA2xvYwUIbG9jSWR4SWQFC2xhbmRBc3NldElkCQACAQkArAICCQCsAgIJAKwCAgIURHVjayBsb2NhdGlvbiBpZCBpcyAJAJEDAgUDbG9jBQhsb2NJZHhJZAIQLCBidXQgc2hvdWxkIGJlIAULbGFuZEFzc2V0SWQECXNhdmVkVGltZQkBE3ZhbHVlT3JFcnJvck1lc3NhZ2UCCQCfCAEFB3RpbWVLZXkJAKwCAgkArAICAgRORlQgCAUFYXNzZXQEbmFtZQIOIGlzIG5vdCBzdGFrZWQECWRlbHRhVGltZQkAZQIIBQlsYXN0QmxvY2sJdGltZXN0YW1wBQlzYXZlZFRpbWUDCQBmAgAABQlkZWx0YVRpbWUJAAIBCQCsAgIJAKwCAgkArAICAiZTYXZlZCB0aW1lc3RhbXAgaXMgaW4gZnV0dXJlLCBzYXZlZCA9IAkApAMBBQlzYXZlZFRpbWUCDCwgY3VycmVudCA9IAkApAMBCAUJbGFzdEJsb2NrCXRpbWVzdGFtcAQGcGllY2VzCQEPbnVtUGllY2VzQnlTaXplAQUIbGFuZFNpemUECmluZnJhTGV2ZWwJAQt2YWx1ZU9yRWxzZQIJAJ8IAQkBFmtleUluZnJhTGV2ZWxCeUFzc2V0SWQBBQtsYW5kQXNzZXRJZAAABAhhdmFpbFJlcwkAaAIJAGsDCQBkAgUJZGVsdGFUaW1lCQBrAwUJZGVsdGFUaW1lBQppbmZyYUxldmVsAAQFD0RBSUxZUkVTQllQSUVDRQUJREFZTUlMTElTBQZwaWVjZXMDCQBmAgUGYW1vdW50BQhhdmFpbFJlcwkAAgEJAKwCAgkArAICCQCsAgICIk5vdCBlbm91Z2ggcmVzb3VyY2VzLCBhdmFpbGFibGUgPSAJAKQDAQUIYXZhaWxSZXMCDiwgcmVxdWVzdGVkID0gCQCkAwEFBmFtb3VudAQMbmV3RGVsdGFUaW1lCQBrAwkAZQIFCGF2YWlsUmVzBQZhbW91bnQFCURBWU1JTExJUwkAaAIFBnBpZWNlcwUPREFJTFlSRVNCWVBJRUNFBAxuZXdUaW1lc3RhbXAJAGUCCAUJbGFzdEJsb2NrCXRpbWVzdGFtcAUMbmV3RGVsdGFUaW1lBAVicEtleQkBEWtleUJhY2twYWNrQnlEdWNrAQULZHVja0Fzc2V0SWQEC2N1cnJlbnRQYWNrCQELZ2V0QmFja3BhY2sBBQVicEtleQQKY3VycmVudFJlcwkAtQkCCQCRAwIFC2N1cnJlbnRQYWNrBQhicElkeFJlcwIBXwQFYnBSZXMJAQZhZGRSZXMEBQpjdXJyZW50UmVzBQ10ZXJyYWluQ291bnRzCQBkAgkAZQIFCWRlbHRhVGltZQUMbmV3RGVsdGFUaW1lCQBrAwkAZQIFCWRlbHRhVGltZQUMbmV3RGVsdGFUaW1lBQppbmZyYUxldmVsAAQJAGkCBQZwaWVjZXMAGQQHbmV3UGFjawkAuQkCCQDMCAIJAJEDAgULY3VycmVudFBhY2sFCmJwSWR4TGV2ZWwJAMwIAgUFYnBSZXMJAMwIAgkAkQMCBQtjdXJyZW50UGFjawUIYnBJZHhNYXQJAMwIAgkAkQMCBQtjdXJyZW50UGFjawUJYnBJZHhQcm9kBQNuaWwCAToJAJQKAgkAzAgCCQELU3RyaW5nRW50cnkCBQVicEtleQUHbmV3UGFjawkAzAgCCQEMSW50ZWdlckVudHJ5AgUHdGltZUtleQUMbmV3VGltZXN0YW1wCQDMCAIJAQxJbnRlZ2VyRW50cnkCCQEia2V5U3Rha2VkVGltZUJ5VHlwZUFzc2V0SWRBbmRPd25lcgMFCkxBTkRQUkVGSVgFC2xhbmRBc3NldElkBQVvd25lcgUMbmV3VGltZXN0YW1wBQNuaWwFBWJwUmVzAWkBBmZsaWdodAIHbWVzc2FnZQNzaWcDCQEBIQEJAMQTAwUHbWVzc2FnZQUDc2lnBQNwdWIJAAIBAhhzaWduYXR1cmUgZG9lcyBub3QgbWF0Y2gDCQECIT0CCQCQAwEIBQFpCHBheW1lbnRzAAAJAAIBAiNmbGlnaHQgZG9lc24ndCByZXF1aXJlIGFueSBwYXltZW50cwQFcGFydHMJALUJAgkAsAkBBQdtZXNzYWdlAgE7BAJocAkAtQkCCQCRAwIJALUJAgkAkQMCBQVwYXJ0cwAAAgF8AAACAV8EBWN1ckhQCQENcGFyc2VJbnRWYWx1ZQEJAJEDAgUCaHAAAAQFbmV3SFAJAQ1wYXJzZUludFZhbHVlAQkAkQMCBQJocAABBA1uZXdMb2NBbmRUaW1lCQC1CQIJAJEDAgUFcGFydHMAAQIBOgQLbmV3TG9jYXRpb24JAJEDAgUNbmV3TG9jQW5kVGltZQAABAR0aW1lCQENcGFyc2VJbnRWYWx1ZQEJAJEDAgUNbmV3TG9jQW5kVGltZQABAwMJAGYCBQR0aW1lCQBkAggFCWxhc3RCbG9jawl0aW1lc3RhbXAFEUZJVkVNSU5VVEVTTUlMTElTBgkAZgIJAGUCCAUJbGFzdEJsb2NrCXRpbWVzdGFtcAURRklWRU1JTlVURVNNSUxMSVMFBHRpbWUJAAIBAhJzaWduYXR1cmUgb3V0ZGF0ZWQEC2R1Y2tBc3NldElkCQETdmFsdWVPckVycm9yTWVzc2FnZQIJAKIIAQkBFGtleVN0YWtlZER1Y2tCeU93bmVyAQkApQgBCAUBaQZjYWxsZXICHFlvdSBkb24ndCBoYXZlIGEgZHVjayBzdGFrZWQECWtleUhlYWx0aAkBDWtleUR1Y2tIZWFsdGgBBQtkdWNrQXNzZXRJZAQMb2xkRnJvbVN0YXRlCQELdmFsdWVPckVsc2UCCQCfCAEFCWtleUhlYWx0aABkAwkBAiE9AgUMb2xkRnJvbVN0YXRlBQVjdXJIUAkAAgEJAKwCAgkArAICCQCsAgICCm9sZEhlYWx0aD0JAKQDAQkBC3ZhbHVlT3JFbHNlAgkAnwgBBQlrZXlIZWFsdGgAZAIvIGZyb20gc3RhdGUgZG9lcyBub3QgbWF0Y2ggb25lIGZyb20gZmxpZ2h0IGxvZz0JAKQDAQUFY3VySFADCQBnAgAABQVjdXJIUAkAAgECHllvdSBjYW4ndCBmbHkgd2l0aCB6ZXJvIGhlYWx0aAQGbG9jS2V5CQEPa2V5RHVja0xvY2F0aW9uAQULZHVja0Fzc2V0SWQEC2N1ckxvY2F0aW9uCQELdmFsdWVPckVsc2UCCQCiCAEFBmxvY0tleQUPREVGQVVMVExPQ0FUSU9OAwkAAAIFC25ld0xvY2F0aW9uBQtjdXJMb2NhdGlvbgkAAgECIllvdSBjYW4ndCBmbHkgdG8gdGhlIHNhbWUgbG9jYXRpb24JAJQKAgkAzAgCCQELU3RyaW5nRW50cnkCBQZsb2NLZXkDCQBmAgUFbmV3SFAAAAULbmV3TG9jYXRpb24FC2N1ckxvY2F0aW9uCQDMCAIJAQxJbnRlZ2VyRW50cnkCBQlrZXlIZWFsdGgFBW5ld0hQBQNuaWwFBHVuaXQBaQEJc2V0SGVhbHRoAgZoZWFsdGgLZHVja0Fzc2V0SWQDAwkAZgIAAAUGaGVhbHRoBgkAZgIFBmhlYWx0aABkCQACAQIaSFAgc2hvdWxkIGJlIHdpdGhpbiAwLi4xMDAJAMwIAgkBDEludGVnZXJFbnRyeQIJAQ1rZXlEdWNrSGVhbHRoAQULZHVja0Fzc2V0SWQFBmhlYWx0aAUDbmlsAWkBBGhlYWwCB21hdFR5cGUGYW1vdW50AwMJAGYCAAAFB21hdFR5cGUGCQBnAgUHbWF0VHlwZQUGTlVNUkVTCQACAQkArAICAhJVbmtub3duIG1hdGVyaWFsOiAJAKQDAQUHbWF0VHlwZQMJAGcCAAAFBmFtb3VudAkAAgEJAKwCAgIbQW1vdW50IHNob3VsZCBiZSBwb3NpdGl2ZSEgCQCkAwEFBmFtb3VudAQLZHVja0Fzc2V0SWQJARN2YWx1ZU9yRXJyb3JNZXNzYWdlAgkAoggBCQEUa2V5U3Rha2VkRHVja0J5T3duZXIBCQClCAEIBQFpBmNhbGxlcgIcWW91IGRvbid0IGhhdmUgYSBkdWNrIHN0YWtlZAQJa2V5SGVhbHRoCQENa2V5RHVja0hlYWx0aAEFC2R1Y2tBc3NldElkBAlvbGRIZWFsdGgJAQt2YWx1ZU9yRWxzZQIJAJ8IAQUJa2V5SGVhbHRoAGQDCQBnAgUJb2xkSGVhbHRoAGQJAAIBAhpIUCBzaG91bGQgYmUgPCAxMDAgdG8gaGVhbAQFYnBLZXkJARFrZXlCYWNrcGFja0J5RHVjawEFC2R1Y2tBc3NldElkBAtjdXJyZW50UGFjawkBC2dldEJhY2twYWNrAQUFYnBLZXkEBW1MaXN0CQC1CQIJAJEDAgULY3VycmVudFBhY2sFCGJwSWR4TWF0AgFfBA1jdXJyZW50QW1vdW50CQENcGFyc2VJbnRWYWx1ZQEJAJEDAgUFbUxpc3QFB21hdFR5cGUEC2RlbHRhSGVhbHRoCQCXAwEJAMwIAgkAaQIFBmFtb3VudAUISEVBTENPU1QJAMwIAgkAZQIAZAUJb2xkSGVhbHRoBQNuaWwEC3NwZW5kQW1vdW50CQBoAgULZGVsdGFIZWFsdGgFCEhFQUxDT1NUAwkAZgIFC3NwZW5kQW1vdW50BQ1jdXJyZW50QW1vdW50CQACAQkArAICCQCsAgIJAKwCAgkArAICCQCsAgICCVlvdSBuZWVkIAkApAMBBQtzcGVuZEFtb3VudAIEIG9mIAkAkQMCBQhtYXRUeXBlcwUHbWF0VHlwZQIkIHRvIGhlYWwsIGJ1dCB5b3UgYmFja3BhY2sgY29udGFpbnMgCQCkAwEFDWN1cnJlbnRBbW91bnQEBm5ld01hdAkBDHN1Yk9uZUluTGlzdAMFBW1MaXN0BQdtYXRUeXBlBQtzcGVuZEFtb3VudAkAzAgCCQEMSW50ZWdlckVudHJ5AgUJa2V5SGVhbHRoCQBkAgUJb2xkSGVhbHRoBQtkZWx0YUhlYWx0aAkAzAgCCQELU3RyaW5nRW50cnkCBQVicEtleQkAuQkCCQDMCAIJAJEDAgULY3VycmVudFBhY2sFCmJwSWR4TGV2ZWwJAMwIAgkAkQMCBQtjdXJyZW50UGFjawUIYnBJZHhSZXMJAMwIAgUGbmV3TWF0CQDMCAIJAJEDAgULY3VycmVudFBhY2sFCWJwSWR4UHJvZAUDbmlsAgE6BQNuaWwBaQEOdXBkYXRlQmFja3BhY2sCC2R1Y2tBc3NldElkB25ld1BhY2sDCQECIT0CCAUBaQZjYWxsZXIFC2Vjb25vbXlBZGRyCQACAQIRcGVybWlzc2lvbiBkZW5pZWQJAJQKAgkAzAgCCQELU3RyaW5nRW50cnkCCQERa2V5QmFja3BhY2tCeUR1Y2sBBQtkdWNrQXNzZXRJZAUHbmV3UGFjawUDbmlsBQduZXdQYWNrAWkBDWV4cGVkaXRpb25CdXkCB21lc3NhZ2UDc2lnAwkBAiE9AgkAkAMBCAUBaQhwYXltZW50cwABCQACAQIcRXhhY3RseSBvbmUgcGF5bWVudCByZXF1aXJlZAQDcG10CQEFdmFsdWUBCQCRAwIIBQFpCHBheW1lbnRzAAADCQECIT0CCAUDcG10B2Fzc2V0SWQFC3VzZG5Bc3NldElkCQACAQIaQWxsb3dlZCBVU0ROIHBheW1lbnQgb25seSEDCQECIT0CCAUDcG10BmFtb3VudAUHRVhQVVNETgkAAgEJAKwCAgIbUGF5bWVudCBhdHRhY2hlZCBzaG91bGQgYmUgCQCkAwEFB0VYUFVTRE4JARBleHBlZGl0aW9uQ29tbW9uBQcIBQFpBmNhbGxlcggFAWkNdHJhbnNhY3Rpb25JZAUHbWVzc2FnZQUDc2lnAWkBCmV4cGVkaXRpb24CB21lc3NhZ2UDc2lnAwkBAiE9AgkAkAMBCAUBaQhwYXltZW50cwAACQACAQInZXhwZWRpdGlvbiBkb2Vzbid0IHJlcXVpcmUgYW55IHBheW1lbnRzCQEQZXhwZWRpdGlvbkNvbW1vbgUGCAUBaQZjYWxsZXIIBQFpDXRyYW5zYWN0aW9uSWQFB21lc3NhZ2UFA3NpZwFpAQx1cGdyYWRlSW5mcmEBC2xhbmRBc3NldElkAwkBAiE9AgkAkAMBCAUBaQhwYXltZW50cwAACQACAQIzSW5mcmFzdHJ1Y3R1cmUgdXBncmFkZSBkb2Vzbid0IHJlcXVpcmUgYW55IHBheW1lbnRzCQENdXBJbmZyYUNvbW1vbgMGCAUBaQZjYWxsZXIFC2xhbmRBc3NldElkAWkBEHVwZ3JhZGVJbmZyYVVzZG4BC2xhbmRBc3NldElkAwkBAiE9AgkAkAMBCAUBaQhwYXltZW50cwABCQACAQIcRXhhY3RseSBvbmUgcGF5bWVudCByZXF1aXJlZAQDcG10CQEFdmFsdWUBCQCRAwIIBQFpCHBheW1lbnRzAAADCQECIT0CCAUDcG10B2Fzc2V0SWQFC3VzZG5Bc3NldElkCQACAQIaQWxsb3dlZCBVU0ROIHBheW1lbnQgb25seSEEBWFzc2V0CQEFdmFsdWUBCQDsBwEJANkEAQULbGFuZEFzc2V0SWQEAWQJALUJAggFBWFzc2V0C2Rlc2NyaXB0aW9uAgFfBARjb3N0CQBrAwUVSW5mcmFVcGdyYWRlQ29zdFNVc2RuCQEPbnVtUGllY2VzQnlTaXplAQkAkQMCBQFkBQtyZWNMYW5kU2l6ZQAZAwkBAiE9AggFA3BtdAZhbW91bnQFBGNvc3QJAAIBCQCsAgICG1BheW1lbnQgYXR0YWNoZWQgc2hvdWxkIGJlIAkApAMBBQRjb3N0CQENdXBJbmZyYUNvbW1vbgMHCAUBaQZjYWxsZXIFC2xhbmRBc3NldElkAFhAVSM=", "height": 2387518, "applicationStatus": "succeeded", "spentComplexity": 0 } View: original | compacted Prev: FVPTsqkfzMa29uMcm5zNdYgHeNyUGzfkJjonJEk5CMie Next: FrgbE6ccFhyawHJuqex1RvdBfxSSsCF9n38o364EiH9F Diff:
OldNewDifferences
5858 let FIVEMINUTESMILLIS = 300000
5959
6060 let RESOURCEPRICEMIN = 158549
61+
62+let InfraUpgradeCostS = 39420000000
63+
64+let InfraUpgradeCostSUsdn = 250000000
6165
6266 let EXPMATERIALS = match chain {
6367 case _ =>
158162 let bpIdxMat = 2
159163
160164 let bpIdxProd = 3
165+
166+func asString (v) = match v {
167+ case s: String =>
168+ s
169+ case _ =>
170+ throw("fail to cast into String")
171+}
172+
161173
162174 func getNeededMaterials (total) = {
163175 let props = split(value(getString(keyResProportions())), "_")
413425 }
414426
415427
428+func upInfraCommon (shouldUseMat,caller,landAssetId) = {
429+ let addr = toString(caller)
430+ let infraKey = keyInfraLevelByAssetId(landAssetId)
431+ let curLevel = valueOrElse(getInteger(infraKey), 0)
432+ if ((curLevel >= 3))
433+ then throw("Currently max infrastructure level = 3")
434+ else {
435+ let newLevel = (curLevel + 1)
436+ let asset = value(assetInfo(fromBase58String(landAssetId)))
437+ if (!(contains(asset.name, LANDPREFIX)))
438+ then throw((("NFT " + LANDPREFIX) + " token should be passed as param"))
439+ else {
440+ let timeKey = keyStakedTimeByAssetId(landAssetId)
441+ let owner = getStringValue(keyLandAssetIdToOwner(landAssetId))
442+ if ((owner != addr))
443+ then throw((LANDPREFIX + " is not yours"))
444+ else {
445+ let d = split(asset.description, "_")
446+ let landSize = d[recLandSize]
447+ let pieces = numPiecesBySize(landSize)
448+ let duckAssetId = valueOrErrorMessage(getString(keyStakedDuckByOwner(addr)), "You don't have a duck staked")
449+ let curLocation = valueOrElse(getString(keyDuckLocation(duckAssetId)), DEFAULTLOCATION)
450+ let loc = split(value(curLocation), "_")
451+ if ((loc[locIdxType] != "L"))
452+ then throw((("Duck location type is " + loc[locIdxType]) + ", but should be L"))
453+ else if ((loc[locIdxId] != landAssetId))
454+ then throw(((("Duck location id is " + loc[locIdxId]) + ", but should be ") + landAssetId))
455+ else {
456+ let bpKey = keyBackpackByDuck(duckAssetId)
457+ let currentPack = getBackpack(bpKey)
458+ let mList = split(currentPack[bpIdxMat], "_")
459+ let newMat = makeString(subtractMaterials(shouldUseMat, mList, fraction(InfraUpgradeCostS, pieces, 25)), "_")
460+ let savedTime = valueOrErrorMessage(getInteger(timeKey), (("NFT " + asset.name) + " is not staked"))
461+ let deltaTime = (lastBlock.timestamp - savedTime)
462+ if ((0 > deltaTime))
463+ then throw(((("Saved timestamp is in future, saved = " + toString(savedTime)) + ", current = ") + toString(lastBlock.timestamp)))
464+ else {
465+ let availRes = (fraction(deltaTime, DAILYRESBYPIECE, DAYMILLIS) * pieces)
466+ let newRes = asString(invoke(this, "claimRes", [availRes, landAssetId], nil))
467+ $Tuple2([IntegerEntry(infraKey, newLevel), StringEntry(bpKey, makeString([currentPack[bpIdxLevel], newRes, newMat, currentPack[bpIdxProd]], ":"))], newLevel)
468+ }
469+ }
470+ }
471+ }
472+ }
473+ }
474+
475+
416476 @Callable(i)
417477 func stakeLand () = {
418478 let pmt = value(i.payments[0])
566626 then throw((("NFT " + LANDPREFIX) + " token should be passed as param"))
567627 else {
568628 let timeKey = keyStakedTimeByAssetId(landAssetId)
569- let savedTime = getInteger(timeKey)
570- if (!(isDefined(savedTime)))
571- then throw((("NFT " + asset.name) + " is not staked"))
629+ let owner = getStringValue(keyLandAssetIdToOwner(landAssetId))
630+ if ((owner != addr))
631+ then throw((LANDPREFIX + " is not yours"))
572632 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))
633+ let d = split(asset.description, "_")
634+ let landSize = d[recLandSize]
635+ let terrainCounts = countTerrains(d[recTerrains])
636+ let duckAssetId = valueOrErrorMessage(getString(keyStakedDuckByOwner(addr)), "You don't have a duck staked")
637+ let curLocation = valueOrElse(getString(keyDuckLocation(duckAssetId)), DEFAULTLOCATION)
638+ let loc = split(value(curLocation), "_")
639+ if ((loc[locIdxType] != "L"))
640+ then throw((("Duck location type is " + loc[locIdxType]) + ", but should be L"))
641+ else if ((loc[locIdxId] != landAssetId))
642+ then throw(((("Duck location id is " + loc[locIdxId]) + ", but should be ") + landAssetId))
643+ else {
644+ let savedTime = valueOrErrorMessage(getInteger(timeKey), (("NFT " + asset.name) + " is not staked"))
645+ let deltaTime = (lastBlock.timestamp - savedTime)
646+ if ((0 > deltaTime))
647+ then throw(((("Saved timestamp is in future, saved = " + toString(savedTime)) + ", current = ") + toString(lastBlock.timestamp)))
648+ else {
649+ let pieces = numPiecesBySize(landSize)
650+ let infraLevel = valueOrElse(getInteger(keyInfraLevelByAssetId(landAssetId)), 0)
651+ let availRes = (fraction((deltaTime + fraction(deltaTime, infraLevel, 4)), DAILYRESBYPIECE, DAYMILLIS) * pieces)
652+ if ((amount > availRes))
653+ then throw(((("Not enough resources, available = " + toString(availRes)) + ", requested = ") + toString(amount)))
591654 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- }
655+ let newDeltaTime = fraction((availRes - amount), DAYMILLIS, (pieces * DAILYRESBYPIECE))
656+ let newTimestamp = (lastBlock.timestamp - newDeltaTime)
657+ let bpKey = keyBackpackByDuck(duckAssetId)
658+ let currentPack = getBackpack(bpKey)
659+ let currentRes = split(currentPack[bpIdxRes], "_")
660+ let bpRes = addRes(currentRes, terrainCounts, ((deltaTime - newDeltaTime) + fraction((deltaTime - newDeltaTime), infraLevel, 4)), (pieces / 25))
661+ let newPack = makeString([currentPack[bpIdxLevel], bpRes, currentPack[bpIdxMat], currentPack[bpIdxProd]], ":")
662+ $Tuple2([StringEntry(bpKey, newPack), IntegerEntry(timeKey, newTimestamp), IntegerEntry(keyStakedTimeByTypeAssetIdAndOwner(LANDPREFIX, landAssetId, owner), newTimestamp)], bpRes)
611663 }
612- }
613- }
664+ }
665+ }
614666 }
615667 }
616668 }
725777
726778
727779 @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- }
780+func upgradeInfra (landAssetId) = if ((size(i.payments) != 0))
781+ then throw("Infrastructure upgrade doesn't require any payments")
782+ else upInfraCommon(true, i.caller, landAssetId)
783+
784+
785+
786+@Callable(i)
787+func upgradeInfraUsdn (landAssetId) = if ((size(i.payments) != 1))
788+ then throw("Exactly one payment required")
789+ else {
790+ let pmt = value(i.payments[0])
791+ if ((pmt.assetId != usdnAssetId))
792+ then throw("Allowed USDN payment only!")
793+ else {
794+ let asset = value(assetInfo(fromBase58String(landAssetId)))
795+ let d = split(asset.description, "_")
796+ let cost = fraction(InfraUpgradeCostSUsdn, numPiecesBySize(d[recLandSize]), 25)
797+ if ((pmt.amount != cost))
798+ then throw(("Payment attached should be " + toString(cost)))
799+ else upInfraCommon(false, i.caller, landAssetId)
800+ }
801+ }
742802
743803
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
61+
62+let InfraUpgradeCostS = 39420000000
63+
64+let InfraUpgradeCostSUsdn = 250000000
6165
6266 let EXPMATERIALS = match chain {
6367 case _ =>
6468 if (("W" == $match0))
6569 then 157679960139
6670 else if (("T" == $match0))
6771 then 1576799601
6872 else throw("Unknown chain")
6973 }
7074
7175 let EXPUSDN = match chain {
7276 case _ =>
7377 if (("W" == $match0))
7478 then 1000000000
7579 else if (("T" == $match0))
7680 then 10000000
7781 else throw("Unknown chain")
7882 }
7983
8084 let MULT6 = 1000000
8185
8286 let FIVEX = toBigInt(5)
8387
8488 let TWENTYX = toBigInt(20)
8589
8690 let TWENTY2X = toBigInt((20 * 20))
8791
8892 let TWENTY3X = toBigInt(((20 * 20) * 20))
8993
9094 let TWENTY4X = toBigInt((((20 * 20) * 20) * 20))
9195
9296 let TWENTY5X = toBigInt(((((20 * 20) * 20) * 20) * 20))
9397
9498 let matTypes = ["Fuel", "Metal", "Plank", "Glass", "Plastic", "Protein"]
9599
96100 let continents = ["Asia", "Europe", "Americas", "Oceania", "Africa"]
97101
98102 func keyNextFreeLandNum () = "nextLandNum"
99103
100104
101105 func keyLandToAssetId (landNum) = ("landToAsset_" + landNum)
102106
103107
104108 func keyNftName (landNum,landSize) = ((LANDPREFIX + landNum) + landSize)
105109
106110
107111 func keyLandAssetIdToOwner (assetId) = ("nftOwner_" + assetId)
108112
109113
110114 func keyDuckIdToOwner (assetId) = ("duckOwner_" + assetId)
111115
112116
113117 func keyStakedTimeByAssetId (assetId) = ("stakedTime_" + assetId)
114118
115119
116120 func keyInfraLevelByAssetId (assetId) = ("infraLevel_" + assetId)
117121
118122
119123 func keyStakedDuckByOwner (ownerAddr) = ("stakedDuckByOwner_" + ownerAddr)
120124
121125
122126 func keyStakedTimeByTypeAssetIdAndOwner (nftType,assetId,ownerAddr) = ((((("stakedTimeByTypeAssetIdAndOwner_" + nftType) + "_") + assetId) + "_") + ownerAddr)
123127
124128
125129 func keyLandNumToOwner (landNum) = ("landOwner_" + landNum)
126130
127131
128132 func keyBackpackByDuck (duckAssetId) = ("backPack_" + duckAssetId)
129133
130134
131135 func keyDuckLocation (duckAssetId) = ("duckLocation_" + duckAssetId)
132136
133137
134138 func keyDuckHealth (duckAssetId) = ("duckHealth_" + duckAssetId)
135139
136140
137141 func keyResProportions () = "resTypesProportions"
138142
139143
140144 let recLandNum = 0
141145
142146 let recLandSize = 1
143147
144148 let recTerrains = 2
145149
146150 let recContinent = 3
147151
148152 let locIdxContinent = 0
149153
150154 let locIdxType = 1
151155
152156 let locIdxId = 2
153157
154158 let bpIdxLevel = 0
155159
156160 let bpIdxRes = 1
157161
158162 let bpIdxMat = 2
159163
160164 let bpIdxProd = 3
165+
166+func asString (v) = match v {
167+ case s: String =>
168+ s
169+ case _ =>
170+ throw("fail to cast into String")
171+}
172+
161173
162174 func getNeededMaterials (total) = {
163175 let props = split(value(getString(keyResProportions())), "_")
164176 if ((size(props) != NUMRES))
165177 then throw("Wrong proportions data")
166178 else {
167179 let r = [parseIntValue(props[0]), parseIntValue(props[1]), parseIntValue(props[2]), parseIntValue(props[3]), parseIntValue(props[4]), parseIntValue(props[5])]
168180 let sum = (((((r[0] + r[1]) + r[2]) + r[3]) + r[4]) + r[5])
169181 if ((0 >= sum))
170182 then throw("No lands staked")
171183 else {
172184 let norm6 = fraction(total, MULT6, sum)
173185 func normalizer (acc,elem) = (acc :+ fraction(elem, norm6, MULT6))
174186
175187 let $l = r
176188 let $s = size($l)
177189 let $acc0 = nil
178190 func $f0_1 ($a,$i) = if (($i >= $s))
179191 then $a
180192 else normalizer($a, $l[$i])
181193
182194 func $f0_2 ($a,$i) = if (($i >= $s))
183195 then $a
184196 else throw("List size exceeds 6")
185197
186198 $f0_2($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($acc0, 0), 1), 2), 3), 4), 5), 6)
187199 }
188200 }
189201 }
190202
191203
192204 func subtractMaterials (shouldUseMat,has,totalNeed) = {
193205 let need = getNeededMaterials(totalNeed)
194206 func subtractor (acc,idx) = {
195207 let result = (parseIntValue(has[idx]) - need[idx])
196208 if ((0 > result))
197209 then throw(((((("Not enough material idx=" + toString(idx)) + ", you have ") + has[idx]) + ", but need ") + toString(need[idx])))
198210 else (acc :+ toString(result))
199211 }
200212
201213 if (shouldUseMat)
202214 then {
203215 let $l = [0, 1, 2, 3, 4, 5]
204216 let $s = size($l)
205217 let $acc0 = nil
206218 func $f0_1 ($a,$i) = if (($i >= $s))
207219 then $a
208220 else subtractor($a, $l[$i])
209221
210222 func $f0_2 ($a,$i) = if (($i >= $s))
211223 then $a
212224 else throw("List size exceeds 6")
213225
214226 $f0_2($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($acc0, 0), 1), 2), 3), 4), 5), 6)
215227 }
216228 else has
217229 }
218230
219231
220232 func updateProportions (terrainCounts,landSizeIndex,sign) = {
221233 let props = split(valueOrElse(getString(keyResProportions()), "0_0_0_0_0_0"), "_")
222234 if ((size(props) != NUMRES))
223235 then throw("Wrong proportions data")
224236 else {
225237 func updater (acc,i) = {
226238 let result = (parseIntValue(props[i]) + ((sign * terrainCounts[i]) * landSizeIndex))
227239 if ((0 > result))
228240 then throw(((((((("Panic! Pieces of type=" + toString(i)) + ", sign=") + toString(sign)) + ", terrainCounts[i]=") + toString(terrainCounts[i])) + ", landSizeIndex=") + toString(landSizeIndex)))
229241 else (acc :+ toString(result))
230242 }
231243
232244 let r = {
233245 let $l = [0, 1, 2, 3, 4, 5]
234246 let $s = size($l)
235247 let $acc0 = nil
236248 func $f0_1 ($a,$i) = if (($i >= $s))
237249 then $a
238250 else updater($a, $l[$i])
239251
240252 func $f0_2 ($a,$i) = if (($i >= $s))
241253 then $a
242254 else throw("List size exceeds 6")
243255
244256 $f0_2($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($acc0, 0), 1), 2), 3), 4), 5), 6)
245257 }
246258 makeString(r, "_")
247259 }
248260 }
249261
250262
251263 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)]
252264
253265
254266 func numPiecesBySize (landSize) = match landSize {
255267 case _ =>
256268 if (("S" == $match0))
257269 then 25
258270 else if (("M" == $match0))
259271 then 100
260272 else if (("L" == $match0))
261273 then 225
262274 else if (("XL" == $match0))
263275 then 400
264276 else if (("XXL" == $match0))
265277 then 625
266278 else throw("Unknown land size")
267279 }
268280
269281
270282 func subOneInList (aList,idx,amount) = {
271283 func subber (acc,i) = (acc :+ (if ((i == idx))
272284 then toString((parseIntValue(aList[i]) - amount))
273285 else aList[i]))
274286
275287 let r = {
276288 let $l = [0, 1, 2, 3, 4, 5]
277289 let $s = size($l)
278290 let $acc0 = nil
279291 func $f0_1 ($a,$i) = if (($i >= $s))
280292 then $a
281293 else subber($a, $l[$i])
282294
283295 func $f0_2 ($a,$i) = if (($i >= $s))
284296 then $a
285297 else throw("List size exceeds 6")
286298
287299 $f0_2($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($acc0, 0), 1), 2), 3), 4), 5), 6)
288300 }
289301 makeString(r, "_")
290302 }
291303
292304
293305 func addRes (currentRes,terrainCounts,deltaTime,landSizeIndex) = {
294306 func adder (acc,i) = {
295307 let resOfType = ((fraction(deltaTime, DAILYRESBYPIECE, DAYMILLIS) * terrainCounts[i]) * landSizeIndex)
296308 (acc :+ toString((parseIntValue(currentRes[i]) + resOfType)))
297309 }
298310
299311 let r = {
300312 let $l = [0, 1, 2, 3, 4, 5]
301313 let $s = size($l)
302314 let $acc0 = nil
303315 func $f0_1 ($a,$i) = if (($i >= $s))
304316 then $a
305317 else adder($a, $l[$i])
306318
307319 func $f0_2 ($a,$i) = if (($i >= $s))
308320 then $a
309321 else throw("List size exceeds 6")
310322
311323 $f0_2($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($acc0, 0), 1), 2), 3), 4), 5), 6)
312324 }
313325 makeString(r, "_")
314326 }
315327
316328
317329 func abs (x) = if ((x >= toBigInt(0)))
318330 then x
319331 else -(x)
320332
321333
322334 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]]
323335
324336 func genChar (n,freqs) = {
325337 let rem = toInt((n % TWENTYX))
326338 let letter = if ((freqs[0] > rem))
327339 then "A"
328340 else if ((freqs[1] > rem))
329341 then "B"
330342 else if ((freqs[2] > rem))
331343 then "C"
332344 else if ((freqs[3] > rem))
333345 then "D"
334346 else if ((freqs[4] > rem))
335347 then "E"
336348 else "F"
337349 letter
338350 }
339351
340352
341353 func getBackpack (bpKey) = {
342354 let p = split(valueOrElse(getString(bpKey), "0:0_0_0_0_0_0:0_0_0_0_0_0:"), ":")
343355 [toString(valueOrElse(parseInt(p[bpIdxLevel]), 0)), if ((size(split(p[bpIdxRes], "_")) == NUMRES))
344356 then p[bpIdxRes]
345357 else "0_0_0_0_0_0", if ((size(split(p[bpIdxMat], "_")) == NUMRES))
346358 then p[bpIdxMat]
347359 else "0_0_0_0_0_0", p[bpIdxProd]]
348360 }
349361
350362
351363 func expeditionCommon (shouldUseMat,caller,txId,message,sig) = if (!(sigVerify_8Kb(message, sig, pub)))
352364 then throw("signature does not match")
353365 else {
354366 let parts = split(toUtf8String(message), ";")
355367 let hp = split(split(parts[0], "|")[0], "_")
356368 let curHP = parseIntValue(hp[0])
357369 let newHP = parseIntValue(hp[1])
358370 let locAndTime = split(parts[1], ":")
359371 let targetLocation = split(locAndTime[0], "_")
360372 if ((targetLocation[1] != "E"))
361373 then throw("expedition target location type should be E")
362374 else {
363375 let time = parseIntValue(locAndTime[1])
364376 if (if ((time > (lastBlock.timestamp + FIVEMINUTESMILLIS)))
365377 then true
366378 else ((lastBlock.timestamp - FIVEMINUTESMILLIS) > time))
367379 then throw("signature outdated")
368380 else {
369381 let userAddr = toString(caller)
370382 let duckAssetId = valueOrErrorMessage(getString(keyStakedDuckByOwner(userAddr)), "You don't have a duck staked")
371383 let keyHealth = keyDuckHealth(duckAssetId)
372384 let oldFromState = valueOrElse(getInteger(keyHealth), 100)
373385 if ((oldFromState != curHP))
374386 then throw(((("oldHealth=" + toString(valueOrElse(getInteger(keyHealth), 100))) + " from state does not match one from flight log=") + toString(curHP)))
375387 else if ((0 >= curHP))
376388 then throw("You can't fly with zero health")
377389 else if ((0 >= newHP))
378390 then throw("Your duck health is zero, expedition failed")
379391 else {
380392 let bpKey = keyBackpackByDuck(duckAssetId)
381393 let currentPack = getBackpack(bpKey)
382394 let mList = split(currentPack[bpIdxMat], "_")
383395 let newMat = makeString(subtractMaterials(shouldUseMat, mList, EXPMATERIALS), "_")
384396 let bigNum = abs(toBigInt(txId))
385397 let freeNum = valueOrElse(getInteger(keyNextFreeLandNum()), 501)
386398 let landNum = toString(freeNum)
387399 let continentIdx = toInt((bigNum % FIVEX))
388400 let f = freq[continentIdx]
389401 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))
390402
391403 let t = {
392404 let $l = [1, 2, 3, 4, 5]
393405 let $s = size($l)
394406 let $acc0 = $Tuple2("", (bigNum / FIVEX))
395407 func $f0_1 ($a,$i) = if (($i >= $s))
396408 then $a
397409 else terrainGenerator($a, $l[$i])
398410
399411 func $f0_2 ($a,$i) = if (($i >= $s))
400412 then $a
401413 else throw("List size exceeds 5")
402414
403415 $f0_2($f0_1($f0_1($f0_1($f0_1($f0_1($acc0, 0), 1), 2), 3), 4), 5)
404416 }
405417 let continent = continents[continentIdx]
406418 let issue = Issue(keyNftName(landNum, "S"), makeString([landNum, "S", t._1, continent], "_"), 1, 0, false)
407419 let assetId = calculateAssetId(issue)
408420 let id = toBase58String(assetId)
409421 $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)
410422 }
411423 }
412424 }
413425 }
414426
415427
428+func upInfraCommon (shouldUseMat,caller,landAssetId) = {
429+ let addr = toString(caller)
430+ let infraKey = keyInfraLevelByAssetId(landAssetId)
431+ let curLevel = valueOrElse(getInteger(infraKey), 0)
432+ if ((curLevel >= 3))
433+ then throw("Currently max infrastructure level = 3")
434+ else {
435+ let newLevel = (curLevel + 1)
436+ let asset = value(assetInfo(fromBase58String(landAssetId)))
437+ if (!(contains(asset.name, LANDPREFIX)))
438+ then throw((("NFT " + LANDPREFIX) + " token should be passed as param"))
439+ else {
440+ let timeKey = keyStakedTimeByAssetId(landAssetId)
441+ let owner = getStringValue(keyLandAssetIdToOwner(landAssetId))
442+ if ((owner != addr))
443+ then throw((LANDPREFIX + " is not yours"))
444+ else {
445+ let d = split(asset.description, "_")
446+ let landSize = d[recLandSize]
447+ let pieces = numPiecesBySize(landSize)
448+ let duckAssetId = valueOrErrorMessage(getString(keyStakedDuckByOwner(addr)), "You don't have a duck staked")
449+ let curLocation = valueOrElse(getString(keyDuckLocation(duckAssetId)), DEFAULTLOCATION)
450+ let loc = split(value(curLocation), "_")
451+ if ((loc[locIdxType] != "L"))
452+ then throw((("Duck location type is " + loc[locIdxType]) + ", but should be L"))
453+ else if ((loc[locIdxId] != landAssetId))
454+ then throw(((("Duck location id is " + loc[locIdxId]) + ", but should be ") + landAssetId))
455+ else {
456+ let bpKey = keyBackpackByDuck(duckAssetId)
457+ let currentPack = getBackpack(bpKey)
458+ let mList = split(currentPack[bpIdxMat], "_")
459+ let newMat = makeString(subtractMaterials(shouldUseMat, mList, fraction(InfraUpgradeCostS, pieces, 25)), "_")
460+ let savedTime = valueOrErrorMessage(getInteger(timeKey), (("NFT " + asset.name) + " is not staked"))
461+ let deltaTime = (lastBlock.timestamp - savedTime)
462+ if ((0 > deltaTime))
463+ then throw(((("Saved timestamp is in future, saved = " + toString(savedTime)) + ", current = ") + toString(lastBlock.timestamp)))
464+ else {
465+ let availRes = (fraction(deltaTime, DAILYRESBYPIECE, DAYMILLIS) * pieces)
466+ let newRes = asString(invoke(this, "claimRes", [availRes, landAssetId], nil))
467+ $Tuple2([IntegerEntry(infraKey, newLevel), StringEntry(bpKey, makeString([currentPack[bpIdxLevel], newRes, newMat, currentPack[bpIdxProd]], ":"))], newLevel)
468+ }
469+ }
470+ }
471+ }
472+ }
473+ }
474+
475+
416476 @Callable(i)
417477 func stakeLand () = {
418478 let pmt = value(i.payments[0])
419479 let assetId = value(pmt.assetId)
420480 let address = toString(i.caller)
421481 if ((pmt.amount != 1))
422482 then throw((("NFT " + LANDPREFIX) + " token should be attached as payment"))
423483 else {
424484 let asset = value(assetInfo(assetId))
425485 if ((asset.issuer != this))
426486 then throw("Unknown issuer of token")
427487 else if (!(contains(asset.name, LANDPREFIX)))
428488 then throw((("Only NFT " + LANDPREFIX) + " tokens are accepted"))
429489 else {
430490 let landNumSize = drop(asset.name, 4)
431491 let landNum = if (contains(landNumSize, "XXL"))
432492 then dropRight(landNumSize, 3)
433493 else if (contains(landNumSize, "XL"))
434494 then dropRight(landNumSize, 2)
435495 else dropRight(landNumSize, 1)
436496 if (!(isDefined(parseInt(landNum))))
437497 then throw(("Cannot parse land number from " + asset.name))
438498 else {
439499 let timeKey = keyStakedTimeByAssetId(toBase58String(assetId))
440500 if (isDefined(getInteger(timeKey)))
441501 then throw((("NFT " + asset.name) + " is already staked"))
442502 else {
443503 let d = split(asset.description, "_")
444504 let terrainCounts = countTerrains(d[recTerrains])
445505 let props = updateProportions(terrainCounts, (numPiecesBySize(d[recLandSize]) / 25), 1)
446506 [IntegerEntry(timeKey, lastBlock.timestamp), IntegerEntry(keyStakedTimeByTypeAssetIdAndOwner(LANDPREFIX, toBase58String(assetId), address), lastBlock.timestamp), StringEntry(keyLandAssetIdToOwner(toBase58String(assetId)), address), StringEntry(keyLandNumToOwner(landNum), address), StringEntry(keyResProportions(), props)]
447507 }
448508 }
449509 }
450510 }
451511 }
452512
453513
454514
455515 @Callable(i)
456516 func unstakeLand (landAssetId) = if ((size(i.payments) != 0))
457517 then throw("unstake doesn't require any payments")
458518 else {
459519 let assetId = fromBase58String(landAssetId)
460520 let address = toString(i.caller)
461521 let asset = value(assetInfo(assetId))
462522 if ((asset.issuer != this))
463523 then throw("Unknown issuer of token")
464524 else if (!(contains(asset.name, LANDPREFIX)))
465525 then throw((("Only NFT " + LANDPREFIX) + " tokens can be unstaked"))
466526 else {
467527 let timeKey = keyStakedTimeByAssetId(landAssetId)
468528 if (!(isDefined(timeKey)))
469529 then throw((("NFT " + asset.name) + " is not staked"))
470530 else {
471531 let owner = valueOrErrorMessage(getString(keyLandAssetIdToOwner(landAssetId)), (("NFT " + asset.name) + " is orphaned"))
472532 if ((owner != address))
473533 then throw("Staked NFT is not yours")
474534 else {
475535 let d = split(asset.description, "_")
476536 let terrainCounts = countTerrains(d[recTerrains])
477537 let props = updateProportions(terrainCounts, (numPiecesBySize(d[recLandSize]) / 25), -1)
478538 [ScriptTransfer(i.caller, 1, assetId), DeleteEntry(timeKey), DeleteEntry(keyStakedTimeByTypeAssetIdAndOwner(LANDPREFIX, landAssetId, address)), StringEntry(keyResProportions(), props)]
479539 }
480540 }
481541 }
482542 }
483543
484544
485545
486546 @Callable(i)
487547 func stakeDuck () = {
488548 let pmt = value(i.payments[0])
489549 let assetId = value(pmt.assetId)
490550 let address = toString(i.caller)
491551 if ((pmt.amount != 1))
492552 then throw((("NFT " + DUCKPREFIX) + " token should be attached as payment"))
493553 else {
494554 let asset = value(assetInfo(assetId))
495555 if (if ((asset.issuer != incubatorAddr))
496556 then (asset.issuer != breederAddr)
497557 else false)
498558 then throw((("Unknown issuer of " + DUCKPREFIX) + " token"))
499559 else if (!(contains(asset.name, DUCKPREFIX)))
500560 then throw((("Only NFT " + DUCKPREFIX) + " tokens are accepted"))
501561 else {
502562 let assetIdStr = toBase58String(assetId)
503563 let timeKey = keyStakedTimeByAssetId(assetIdStr)
504564 if (isDefined(getInteger(timeKey)))
505565 then throw((("NFT " + asset.name) + " is already staked"))
506566 else if (isDefined(getString(keyStakedDuckByOwner(address))))
507567 then throw(("You already staked one duck: " + asset.name))
508568 else {
509569 let locKey = keyDuckLocation(assetIdStr)
510570 let location = getString(locKey)
511571 let keyHealth = keyDuckHealth(assetIdStr)
512572 let health = getInteger(keyHealth)
513573 let bpKey = keyBackpackByDuck(assetIdStr)
514574 let backpack = getString(bpKey)
515575 ([IntegerEntry(timeKey, lastBlock.timestamp), IntegerEntry(keyStakedTimeByTypeAssetIdAndOwner(DUCKPREFIX, toBase58String(assetId), address), lastBlock.timestamp), StringEntry(keyDuckIdToOwner(assetIdStr), address), StringEntry(keyStakedDuckByOwner(address), assetIdStr)] ++ (if (isDefined(location))
516576 then nil
517577 else ([StringEntry(locKey, DEFAULTLOCATION)] ++ (if (isDefined(health))
518578 then nil
519579 else ([IntegerEntry(keyHealth, 100)] ++ (if (isDefined(backpack))
520580 then nil
521581 else [StringEntry(bpKey, "0:0_0_0_0_0_0:0_0_0_0_0_0:")]))))))
522582 }
523583 }
524584 }
525585 }
526586
527587
528588
529589 @Callable(i)
530590 func unstakeDuck (assetIdStr) = if ((size(i.payments) != 0))
531591 then throw("unstake doesn't require any payments")
532592 else {
533593 let assetId = fromBase58String(assetIdStr)
534594 let address = toString(i.caller)
535595 let asset = value(assetInfo(assetId))
536596 if (if ((asset.issuer != incubatorAddr))
537597 then (asset.issuer != breederAddr)
538598 else false)
539599 then throw((("Unknown issuer of " + DUCKPREFIX) + " token"))
540600 else if (!(contains(asset.name, DUCKPREFIX)))
541601 then throw((("Only NFT " + DUCKPREFIX) + " tokens can be unstaked"))
542602 else {
543603 let timeKey = keyStakedTimeByAssetId(toBase58String(assetId))
544604 if (!(isDefined(timeKey)))
545605 then throw((("NFT " + asset.name) + " is not staked"))
546606 else if (!(isDefined(keyStakedDuckByOwner(address))))
547607 then throw((("The duck " + asset.name) + " is not staked"))
548608 else {
549609 let owner = valueOrErrorMessage(getString(keyDuckIdToOwner(toBase58String(assetId))), (("NFT " + asset.name) + " is orphaned"))
550610 if ((owner != address))
551611 then throw("Staked NFT is not yours")
552612 else [ScriptTransfer(i.caller, 1, assetId), DeleteEntry(timeKey), DeleteEntry(keyDuckLocation(assetIdStr)), DeleteEntry(keyStakedTimeByTypeAssetIdAndOwner(DUCKPREFIX, assetIdStr, address)), DeleteEntry(keyStakedDuckByOwner(address))]
553613 }
554614 }
555615 }
556616
557617
558618
559619 @Callable(i)
560620 func claimRes (amount,landAssetId) = if ((size(i.payments) != 0))
561621 then throw("claimRes doesn't require any payments")
562622 else {
563623 let addr = toString(i.caller)
564624 let asset = value(assetInfo(fromBase58String(landAssetId)))
565625 if (!(contains(asset.name, LANDPREFIX)))
566626 then throw((("NFT " + LANDPREFIX) + " token should be passed as param"))
567627 else {
568628 let timeKey = keyStakedTimeByAssetId(landAssetId)
569- let savedTime = getInteger(timeKey)
570- if (!(isDefined(savedTime)))
571- then throw((("NFT " + asset.name) + " is not staked"))
629+ let owner = getStringValue(keyLandAssetIdToOwner(landAssetId))
630+ if ((owner != addr))
631+ then throw((LANDPREFIX + " is not yours"))
572632 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))
633+ let d = split(asset.description, "_")
634+ let landSize = d[recLandSize]
635+ let terrainCounts = countTerrains(d[recTerrains])
636+ let duckAssetId = valueOrErrorMessage(getString(keyStakedDuckByOwner(addr)), "You don't have a duck staked")
637+ let curLocation = valueOrElse(getString(keyDuckLocation(duckAssetId)), DEFAULTLOCATION)
638+ let loc = split(value(curLocation), "_")
639+ if ((loc[locIdxType] != "L"))
640+ then throw((("Duck location type is " + loc[locIdxType]) + ", but should be L"))
641+ else if ((loc[locIdxId] != landAssetId))
642+ then throw(((("Duck location id is " + loc[locIdxId]) + ", but should be ") + landAssetId))
643+ else {
644+ let savedTime = valueOrErrorMessage(getInteger(timeKey), (("NFT " + asset.name) + " is not staked"))
645+ let deltaTime = (lastBlock.timestamp - savedTime)
646+ if ((0 > deltaTime))
647+ then throw(((("Saved timestamp is in future, saved = " + toString(savedTime)) + ", current = ") + toString(lastBlock.timestamp)))
648+ else {
649+ let pieces = numPiecesBySize(landSize)
650+ let infraLevel = valueOrElse(getInteger(keyInfraLevelByAssetId(landAssetId)), 0)
651+ let availRes = (fraction((deltaTime + fraction(deltaTime, infraLevel, 4)), DAILYRESBYPIECE, DAYMILLIS) * pieces)
652+ if ((amount > availRes))
653+ then throw(((("Not enough resources, available = " + toString(availRes)) + ", requested = ") + toString(amount)))
591654 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- }
655+ let newDeltaTime = fraction((availRes - amount), DAYMILLIS, (pieces * DAILYRESBYPIECE))
656+ let newTimestamp = (lastBlock.timestamp - newDeltaTime)
657+ let bpKey = keyBackpackByDuck(duckAssetId)
658+ let currentPack = getBackpack(bpKey)
659+ let currentRes = split(currentPack[bpIdxRes], "_")
660+ let bpRes = addRes(currentRes, terrainCounts, ((deltaTime - newDeltaTime) + fraction((deltaTime - newDeltaTime), infraLevel, 4)), (pieces / 25))
661+ let newPack = makeString([currentPack[bpIdxLevel], bpRes, currentPack[bpIdxMat], currentPack[bpIdxProd]], ":")
662+ $Tuple2([StringEntry(bpKey, newPack), IntegerEntry(timeKey, newTimestamp), IntegerEntry(keyStakedTimeByTypeAssetIdAndOwner(LANDPREFIX, landAssetId, owner), newTimestamp)], bpRes)
611663 }
612- }
613- }
664+ }
665+ }
614666 }
615667 }
616668 }
617669
618670
619671
620672 @Callable(i)
621673 func flight (message,sig) = if (!(sigVerify_8Kb(message, sig, pub)))
622674 then throw("signature does not match")
623675 else if ((size(i.payments) != 0))
624676 then throw("flight doesn't require any payments")
625677 else {
626678 let parts = split(toUtf8String(message), ";")
627679 let hp = split(split(parts[0], "|")[0], "_")
628680 let curHP = parseIntValue(hp[0])
629681 let newHP = parseIntValue(hp[1])
630682 let newLocAndTime = split(parts[1], ":")
631683 let newLocation = newLocAndTime[0]
632684 let time = parseIntValue(newLocAndTime[1])
633685 if (if ((time > (lastBlock.timestamp + FIVEMINUTESMILLIS)))
634686 then true
635687 else ((lastBlock.timestamp - FIVEMINUTESMILLIS) > time))
636688 then throw("signature outdated")
637689 else {
638690 let duckAssetId = valueOrErrorMessage(getString(keyStakedDuckByOwner(toString(i.caller))), "You don't have a duck staked")
639691 let keyHealth = keyDuckHealth(duckAssetId)
640692 let oldFromState = valueOrElse(getInteger(keyHealth), 100)
641693 if ((oldFromState != curHP))
642694 then throw(((("oldHealth=" + toString(valueOrElse(getInteger(keyHealth), 100))) + " from state does not match one from flight log=") + toString(curHP)))
643695 else if ((0 >= curHP))
644696 then throw("You can't fly with zero health")
645697 else {
646698 let locKey = keyDuckLocation(duckAssetId)
647699 let curLocation = valueOrElse(getString(locKey), DEFAULTLOCATION)
648700 if ((newLocation == curLocation))
649701 then throw("You can't fly to the same location")
650702 else $Tuple2([StringEntry(locKey, if ((newHP > 0))
651703 then newLocation
652704 else curLocation), IntegerEntry(keyHealth, newHP)], unit)
653705 }
654706 }
655707 }
656708
657709
658710
659711 @Callable(i)
660712 func setHealth (health,duckAssetId) = if (if ((0 > health))
661713 then true
662714 else (health > 100))
663715 then throw("HP should be within 0..100")
664716 else [IntegerEntry(keyDuckHealth(duckAssetId), health)]
665717
666718
667719
668720 @Callable(i)
669721 func heal (matType,amount) = if (if ((0 > matType))
670722 then true
671723 else (matType >= NUMRES))
672724 then throw(("Unknown material: " + toString(matType)))
673725 else if ((0 >= amount))
674726 then throw(("Amount should be positive! " + toString(amount)))
675727 else {
676728 let duckAssetId = valueOrErrorMessage(getString(keyStakedDuckByOwner(toString(i.caller))), "You don't have a duck staked")
677729 let keyHealth = keyDuckHealth(duckAssetId)
678730 let oldHealth = valueOrElse(getInteger(keyHealth), 100)
679731 if ((oldHealth >= 100))
680732 then throw("HP should be < 100 to heal")
681733 else {
682734 let bpKey = keyBackpackByDuck(duckAssetId)
683735 let currentPack = getBackpack(bpKey)
684736 let mList = split(currentPack[bpIdxMat], "_")
685737 let currentAmount = parseIntValue(mList[matType])
686738 let deltaHealth = min([(amount / HEALCOST), (100 - oldHealth)])
687739 let spendAmount = (deltaHealth * HEALCOST)
688740 if ((spendAmount > currentAmount))
689741 then throw(((((("You need " + toString(spendAmount)) + " of ") + matTypes[matType]) + " to heal, but you backpack contains ") + toString(currentAmount)))
690742 else {
691743 let newMat = subOneInList(mList, matType, spendAmount)
692744 [IntegerEntry(keyHealth, (oldHealth + deltaHealth)), StringEntry(bpKey, makeString([currentPack[bpIdxLevel], currentPack[bpIdxRes], newMat, currentPack[bpIdxProd]], ":"))]
693745 }
694746 }
695747 }
696748
697749
698750
699751 @Callable(i)
700752 func updateBackpack (duckAssetId,newPack) = if ((i.caller != economyAddr))
701753 then throw("permission denied")
702754 else $Tuple2([StringEntry(keyBackpackByDuck(duckAssetId), newPack)], newPack)
703755
704756
705757
706758 @Callable(i)
707759 func expeditionBuy (message,sig) = if ((size(i.payments) != 1))
708760 then throw("Exactly one payment required")
709761 else {
710762 let pmt = value(i.payments[0])
711763 if ((pmt.assetId != usdnAssetId))
712764 then throw("Allowed USDN payment only!")
713765 else if ((pmt.amount != EXPUSDN))
714766 then throw(("Payment attached should be " + toString(EXPUSDN)))
715767 else expeditionCommon(false, i.caller, i.transactionId, message, sig)
716768 }
717769
718770
719771
720772 @Callable(i)
721773 func expedition (message,sig) = if ((size(i.payments) != 0))
722774 then throw("expedition doesn't require any payments")
723775 else expeditionCommon(true, i.caller, i.transactionId, message, sig)
724776
725777
726778
727779 @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- }
780+func upgradeInfra (landAssetId) = if ((size(i.payments) != 0))
781+ then throw("Infrastructure upgrade doesn't require any payments")
782+ else upInfraCommon(true, i.caller, landAssetId)
783+
784+
785+
786+@Callable(i)
787+func upgradeInfraUsdn (landAssetId) = if ((size(i.payments) != 1))
788+ then throw("Exactly one payment required")
789+ else {
790+ let pmt = value(i.payments[0])
791+ if ((pmt.assetId != usdnAssetId))
792+ then throw("Allowed USDN payment only!")
793+ else {
794+ let asset = value(assetInfo(fromBase58String(landAssetId)))
795+ let d = split(asset.description, "_")
796+ let cost = fraction(InfraUpgradeCostSUsdn, numPiecesBySize(d[recLandSize]), 25)
797+ if ((pmt.amount != cost))
798+ then throw(("Payment attached should be " + toString(cost)))
799+ else upInfraCommon(false, i.caller, landAssetId)
800+ }
801+ }
742802
743803

github/deemru/w8io/169f3d6 
92.66 ms