tx · Ffohhby5ENVZRFcPY7CgTsbpYXna4tK6gmK5iRWsc4L8 3PLncXtS1U83D6cQbFD3H8rBHPLgzxSFKZ1: -0.01100000 Waves 2022.12.12 02:55 [3421834] smart account 3PLncXtS1U83D6cQbFD3H8rBHPLgzxSFKZ1 > SELF 0.00000000 Waves
{ "type": 13, "id": "Ffohhby5ENVZRFcPY7CgTsbpYXna4tK6gmK5iRWsc4L8", "fee": 1100000, "feeAssetId": null, "timestamp": 1670802937689, "version": 2, "chainId": 87, "sender": "3PLncXtS1U83D6cQbFD3H8rBHPLgzxSFKZ1", "senderPublicKey": "BtDpFhgHQKHFVqYTMLReuZzqy94CRPSRkrYcSEVyjt4q", "proofs": [ "3QrUt7W5fifpHYnNcJhRqNqvFVzxPa6rbCzWtJgaBRV4gaY1h9gn9PaWr97DQCnbvJFjGdTYGUTGUg8tG52VCdQo" ], "script": "base64:BgIoCAISABIDCgEIEgASAwoBCBIECgIBCBIECgICAhIECgIBCBIECgIICCkAC3VzZG5Bc3NldElkASC2JinDBPXOU5GkDkt1JC9kjFGx+t+vVCm9SNIdKrKq0QANaW5jdWJhdG9yQWRkcgkBEUBleHRyTmF0aXZlKDEwNjIpAQIjM1BFa3RWdXgyUmhjaFNONjNEc0RvNGI0bXo0UXF6S1NlRHYAC2JyZWVkZXJBZGRyCQERQGV4dHJOYXRpdmUoMTA2MikBAiMzUERWdVU0NUg3RWg1ZG10TmJuUk5SU3RHd1VMQTdOWTZIYgALYmFja0VuZEFkZHIJARFAZXh0ck5hdGl2ZSgxMDYyKQECIzNQQktldTc4eGROeWFLdzVrMlVhNm5wcWU4TVJRZWdvcTE3AAtlY29ub215QWRkcgkBEUBleHRyTmF0aXZlKDEwNjIpAQIjM1Ayc2sxS25jU3hSYVpzOGI0Q1dHUHcyamt2dmF2NzR1NEQAA3B1YgEgT1PiGj9UqNadUk4THUYnVb2zU/Mse8lUVLD/Q8G0FCgACkxBTkRQUkVGSVgCBExBTkQACkRVQ0tQUkVGSVgCBERVQ0sAD0RFRkFVTFRMT0NBVElPTgIPQWZyaWNhX0ZfQWZyaWNhAA9EQUlMWVJFU0JZUElFQ0UAgPjSAQAJREFZTUlMTElTAIC4mSkAEUZJVkVNSU5VVEVTTUlMTElTAOCnEgERa2V5QXNzZXRJZFRvT3duZXIBB2Fzc2V0SWQJAKwCAgIJbmZ0T3duZXJfBQdhc3NldElkARBrZXlEdWNrSWRUb093bmVyAQdhc3NldElkCQCsAgICCmR1Y2tPd25lcl8FB2Fzc2V0SWQBFmtleVN0YWtlZFRpbWVCeUFzc2V0SWQBB2Fzc2V0SWQJAKwCAgILc3Rha2VkVGltZV8FB2Fzc2V0SWQBFGtleVN0YWtlZER1Y2tCeU93bmVyAQlvd25lckFkZHIJAKwCAgISc3Rha2VkRHVja0J5T3duZXJfBQlvd25lckFkZHIBImtleVN0YWtlZFRpbWVCeVR5cGVBc3NldElkQW5kT3duZXIDB25mdFR5cGUHYXNzZXRJZAlvd25lckFkZHIJAKwCAgkArAICCQCsAgIJAKwCAgkArAICAiBzdGFrZWRUaW1lQnlUeXBlQXNzZXRJZEFuZE93bmVyXwUHbmZ0VHlwZQIBXwUHYXNzZXRJZAIBXwUJb3duZXJBZGRyAQ5rZXlMYW5kVG9Pd25lcgEHbGFuZE51bQkArAICAgpsYW5kT3duZXJfBQdsYW5kTnVtARFrZXlCYWNrcGFja0J5RHVjawELZHVja0Fzc2V0SWQJAKwCAgIJYmFja1BhY2tfBQtkdWNrQXNzZXRJZAEPa2V5RHVja0xvY2F0aW9uAQtkdWNrQXNzZXRJZAkArAICAg1kdWNrTG9jYXRpb25fBQtkdWNrQXNzZXRJZAENa2V5RHVja0hlYWx0aAELZHVja0Fzc2V0SWQJAKwCAgILZHVja0hlYWx0aF8FC2R1Y2tBc3NldElkAApyZWNMYW5kTnVtAAAAC3JlY0xhbmRTaXplAAEAC3JlY1RlcnJhaW5zAAIADHJlY0NvbnRpbmVudAADAA9sb2NJZHhDb250aW5lbnQAAAAKbG9jSWR4VHlwZQABAAhsb2NJZHhJZAACAApicElkeExldmVsAAAACGJwSWR4UmVzAAEACGJwSWR4TWF0AAIACWJwSWR4UHJvZAADAARpZHhBAAAABGlkeEIAAQAEaWR4QwACAARpZHhEAAMABGlkeEUABAAEaWR4RgAFAQ1jb3VudFRlcnJhaW5zAQh0ZXJyYWlucwkAzAgCCQBlAgkAkAMBCQC1CQIFCHRlcnJhaW5zAgFBAAEJAMwIAgkAZQIJAJADAQkAtQkCBQh0ZXJyYWlucwIBQgABCQDMCAIJAGUCCQCQAwEJALUJAgUIdGVycmFpbnMCAUMAAQkAzAgCCQBlAgkAkAMBCQC1CQIFCHRlcnJhaW5zAgFEAAEJAMwIAgkAZQIJAJADAQkAtQkCBQh0ZXJyYWlucwIBRQABCQDMCAIJAGUCCQCQAwEJALUJAgUIdGVycmFpbnMCAUYAAQUDbmlsAQ9udW1QaWVjZXNCeVNpemUBCGxhbmRTaXplBAckbWF0Y2gwBQhsYW5kU2l6ZQMJAAACAgFTBQckbWF0Y2gwABkDCQAAAgIBTQUHJG1hdGNoMABkAwkAAAICAUwFByRtYXRjaDAA4QEDCQAAAgICWEwFByRtYXRjaDAAkAMDCQAAAgIDWFhMBQckbWF0Y2gwAPEECQACAQIRVW5rbm93biBsYW5kIHNpemUBBmFkZFJlcwMKY3VycmVudFJlcw10ZXJyYWluQ291bnRzCWRlbHRhVGltZQoBBWFkZGVyAgNhY2MBaQQJcmVzT2ZUeXBlCQBoAgkAawMFCWRlbHRhVGltZQUPREFJTFlSRVNCWVBJRUNFBQlEQVlNSUxMSVMJAJEDAgUNdGVycmFpbkNvdW50cwUBaQkAzQgCBQNhY2MJAKQDAQkAZAIJAQ1wYXJzZUludFZhbHVlAQkAkQMCBQpjdXJyZW50UmVzBQFpBQlyZXNPZlR5cGUEAXIKAAIkbAkAzAgCAAAJAMwIAgABCQDMCAIAAgkAzAgCAAMJAMwIAgAECQDMCAIABQUDbmlsCgACJHMJAJADAQUCJGwKAAUkYWNjMAUDbmlsCgEFJGYwXzECAiRhAiRpAwkAZwIFAiRpBQIkcwUCJGEJAQVhZGRlcgIFAiRhCQCRAwIFAiRsBQIkaQoBBSRmMF8yAgIkYQIkaQMJAGcCBQIkaQUCJHMFAiRhCQACAQITTGlzdCBzaXplIGV4Y2VlZHMgNgkBBSRmMF8yAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgUFJGFjYzAAAAABAAIAAwAEAAUABgkAuQkCBQFyAgFfCAFpAQlzdGFrZUxhbmQABANwbXQJAQV2YWx1ZQEJAJEDAggFAWkIcGF5bWVudHMAAAQHYXNzZXRJZAkBBXZhbHVlAQgFA3BtdAdhc3NldElkBAdhZGRyZXNzCQClCAEIBQFpBmNhbGxlcgMJAQIhPQIIBQNwbXQGYW1vdW50AAEJAAIBCQCsAgIJAKwCAgIETkZUIAUKTEFORFBSRUZJWAIkIHRva2VuIHNob3VsZCBiZSBhdHRhY2hlZCBhcyBwYXltZW50BAVhc3NldAkBBXZhbHVlAQkA7AcBBQdhc3NldElkAwkBAiE9AggFBWFzc2V0Bmlzc3VlcgUEdGhpcwkAAgECF1Vua25vd24gaXNzdWVyIG9mIHRva2VuAwkBASEBCQEIY29udGFpbnMCCAUFYXNzZXQEbmFtZQUKTEFORFBSRUZJWAkAAgEJAKwCAgkArAICAglPbmx5IE5GVCAFCkxBTkRQUkVGSVgCFCB0b2tlbnMgYXJlIGFjY2VwdGVkBAtsYW5kTnVtU2l6ZQkAsAICCAUFYXNzZXQEbmFtZQAEBAdsYW5kTnVtAwkBCGNvbnRhaW5zAgULbGFuZE51bVNpemUCA1hYTAkAswICBQtsYW5kTnVtU2l6ZQADAwkBCGNvbnRhaW5zAgULbGFuZE51bVNpemUCAlhMCQCzAgIFC2xhbmROdW1TaXplAAIJALMCAgULbGFuZE51bVNpemUAAQMJAQEhAQkBCWlzRGVmaW5lZAEJALYJAQUHbGFuZE51bQkAAgEJAKwCAgIeQ2Fubm90IHBhcnNlIGxhbmQgbnVtYmVyIGZyb20gCAUFYXNzZXQEbmFtZQQHdGltZUtleQkBFmtleVN0YWtlZFRpbWVCeUFzc2V0SWQBCQDYBAEFB2Fzc2V0SWQDCQEJaXNEZWZpbmVkAQkAnwgBBQd0aW1lS2V5CQACAQkArAICCQCsAgICBE5GVCAIBQVhc3NldARuYW1lAhIgaXMgYWxyZWFkeSBzdGFrZWQJAMwIAgkBDEludGVnZXJFbnRyeQIFB3RpbWVLZXkIBQlsYXN0QmxvY2sJdGltZXN0YW1wCQDMCAIJAQxJbnRlZ2VyRW50cnkCCQEia2V5U3Rha2VkVGltZUJ5VHlwZUFzc2V0SWRBbmRPd25lcgMFCkxBTkRQUkVGSVgJANgEAQUHYXNzZXRJZAUHYWRkcmVzcwgFCWxhc3RCbG9jawl0aW1lc3RhbXAJAMwIAgkBC1N0cmluZ0VudHJ5AgkBEWtleUFzc2V0SWRUb093bmVyAQkA2AQBBQdhc3NldElkBQdhZGRyZXNzCQDMCAIJAQtTdHJpbmdFbnRyeQIJAQ5rZXlMYW5kVG9Pd25lcgEFB2xhbmROdW0FB2FkZHJlc3MFA25pbAFpAQt1bnN0YWtlTGFuZAELbGFuZEFzc2V0SWQDCQECIT0CCQCQAwEIBQFpCHBheW1lbnRzAAAJAAIBAiR1bnN0YWtlIGRvZXNuJ3QgcmVxdWlyZSBhbnkgcGF5bWVudHMEB2Fzc2V0SWQJANkEAQULbGFuZEFzc2V0SWQEB2FkZHJlc3MJAKUIAQgFAWkGY2FsbGVyBAVhc3NldAkBBXZhbHVlAQkA7AcBBQdhc3NldElkAwkBAiE9AggFBWFzc2V0Bmlzc3VlcgUEdGhpcwkAAgECF1Vua25vd24gaXNzdWVyIG9mIHRva2VuAwkBASEBCQEIY29udGFpbnMCCAUFYXNzZXQEbmFtZQUKTEFORFBSRUZJWAkAAgEJAKwCAgkArAICAglPbmx5IE5GVCAFCkxBTkRQUkVGSVgCFyB0b2tlbnMgY2FuIGJlIHVuc3Rha2VkBAd0aW1lS2V5CQEWa2V5U3Rha2VkVGltZUJ5QXNzZXRJZAEFC2xhbmRBc3NldElkAwkBASEBCQEJaXNEZWZpbmVkAQUHdGltZUtleQkAAgEJAKwCAgkArAICAgRORlQgCAUFYXNzZXQEbmFtZQIOIGlzIG5vdCBzdGFrZWQEBW93bmVyCQETdmFsdWVPckVycm9yTWVzc2FnZQIJAKIIAQkBEWtleUFzc2V0SWRUb093bmVyAQULbGFuZEFzc2V0SWQJAKwCAgkArAICAgRORlQgCAUFYXNzZXQEbmFtZQIMIGlzIG9ycGhhbmVkAwkBAiE9AgUFb3duZXIFB2FkZHJlc3MJAAIBAhdTdGFrZWQgTkZUIGlzIG5vdCB5b3VycwkAzAgCCQEOU2NyaXB0VHJhbnNmZXIDCAUBaQZjYWxsZXIAAQUHYXNzZXRJZAkAzAgCCQELRGVsZXRlRW50cnkBBQd0aW1lS2V5CQDMCAIJAQtEZWxldGVFbnRyeQEJASJrZXlTdGFrZWRUaW1lQnlUeXBlQXNzZXRJZEFuZE93bmVyAwUKTEFORFBSRUZJWAULbGFuZEFzc2V0SWQFB2FkZHJlc3MFA25pbAFpAQlzdGFrZUR1Y2sABANwbXQJAQV2YWx1ZQEJAJEDAggFAWkIcGF5bWVudHMAAAQHYXNzZXRJZAkBBXZhbHVlAQgFA3BtdAdhc3NldElkBAdhZGRyZXNzCQClCAEIBQFpBmNhbGxlcgMJAQIhPQIIBQNwbXQGYW1vdW50AAEJAAIBCQCsAgIJAKwCAgIETkZUIAUKRFVDS1BSRUZJWAIkIHRva2VuIHNob3VsZCBiZSBhdHRhY2hlZCBhcyBwYXltZW50BAVhc3NldAkBBXZhbHVlAQkA7AcBBQdhc3NldElkAwMJAQIhPQIIBQVhc3NldAZpc3N1ZXIFDWluY3ViYXRvckFkZHIJAQIhPQIIBQVhc3NldAZpc3N1ZXIFC2JyZWVkZXJBZGRyBwkAAgEJAKwCAgkArAICAhJVbmtub3duIGlzc3VlciBvZiAFCkRVQ0tQUkVGSVgCBiB0b2tlbgMJAQEhAQkBCGNvbnRhaW5zAggFBWFzc2V0BG5hbWUFCkRVQ0tQUkVGSVgJAAIBCQCsAgIJAKwCAgIJT25seSBORlQgBQpEVUNLUFJFRklYAhQgdG9rZW5zIGFyZSBhY2NlcHRlZAQKYXNzZXRJZFN0cgkA2AQBBQdhc3NldElkBAd0aW1lS2V5CQEWa2V5U3Rha2VkVGltZUJ5QXNzZXRJZAEFCmFzc2V0SWRTdHIDCQEJaXNEZWZpbmVkAQkAnwgBBQd0aW1lS2V5CQACAQkArAICCQCsAgICBE5GVCAIBQVhc3NldARuYW1lAhIgaXMgYWxyZWFkeSBzdGFrZWQDCQEJaXNEZWZpbmVkAQkAoggBCQEUa2V5U3Rha2VkRHVja0J5T3duZXIBBQdhZGRyZXNzCQACAQkArAICAh1Zb3UgYWxyZWFkeSBzdGFrZWQgb25lIGR1Y2s6IAgFBWFzc2V0BG5hbWUEBmxvY0tleQkBD2tleUR1Y2tMb2NhdGlvbgEFCmFzc2V0SWRTdHIECGxvY2F0aW9uCQCiCAEFBmxvY0tleQQJa2V5SGVhbHRoCQENa2V5RHVja0hlYWx0aAEFCmFzc2V0SWRTdHIEBmhlYWx0aAkAnwgBBQlrZXlIZWFsdGgEBWJwS2V5CQERa2V5QmFja3BhY2tCeUR1Y2sBBQphc3NldElkU3RyBAhiYWNrcGFjawkAoggBBQVicEtleQkAzggCCQDMCAIJAQxJbnRlZ2VyRW50cnkCBQd0aW1lS2V5CAUJbGFzdEJsb2NrCXRpbWVzdGFtcAkAzAgCCQEMSW50ZWdlckVudHJ5AgkBImtleVN0YWtlZFRpbWVCeVR5cGVBc3NldElkQW5kT3duZXIDBQpEVUNLUFJFRklYCQDYBAEFB2Fzc2V0SWQFB2FkZHJlc3MIBQlsYXN0QmxvY2sJdGltZXN0YW1wCQDMCAIJAQtTdHJpbmdFbnRyeQIJARBrZXlEdWNrSWRUb093bmVyAQUKYXNzZXRJZFN0cgUHYWRkcmVzcwkAzAgCCQELU3RyaW5nRW50cnkCCQEUa2V5U3Rha2VkRHVja0J5T3duZXIBBQdhZGRyZXNzBQphc3NldElkU3RyBQNuaWwDCQEJaXNEZWZpbmVkAQUIbG9jYXRpb24FA25pbAkAzggCCQDMCAIJAQtTdHJpbmdFbnRyeQIFBmxvY0tleQUPREVGQVVMVExPQ0FUSU9OBQNuaWwDCQEJaXNEZWZpbmVkAQUGaGVhbHRoBQNuaWwJAM4IAgkAzAgCCQEMSW50ZWdlckVudHJ5AgUJa2V5SGVhbHRoAGQFA25pbAMJAQlpc0RlZmluZWQBBQhiYWNrcGFjawUDbmlsCQDMCAIJAQtTdHJpbmdFbnRyeQIFBWJwS2V5Ag8wOjBfMF8wXzBfMF8wOjoFA25pbAFpAQt1bnN0YWtlRHVjawEKYXNzZXRJZFN0cgMJAQIhPQIJAJADAQgFAWkIcGF5bWVudHMAAAkAAgECJHVuc3Rha2UgZG9lc24ndCByZXF1aXJlIGFueSBwYXltZW50cwQHYXNzZXRJZAkA2QQBBQphc3NldElkU3RyBAdhZGRyZXNzCQClCAEIBQFpBmNhbGxlcgQFYXNzZXQJAQV2YWx1ZQEJAOwHAQUHYXNzZXRJZAMDCQECIT0CCAUFYXNzZXQGaXNzdWVyBQ1pbmN1YmF0b3JBZGRyCQECIT0CCAUFYXNzZXQGaXNzdWVyBQticmVlZGVyQWRkcgcJAAIBCQCsAgIJAKwCAgISVW5rbm93biBpc3N1ZXIgb2YgBQpEVUNLUFJFRklYAgYgdG9rZW4DCQEBIQEJAQhjb250YWlucwIIBQVhc3NldARuYW1lBQpEVUNLUFJFRklYCQACAQkArAICCQCsAgICCU9ubHkgTkZUIAUKRFVDS1BSRUZJWAIXIHRva2VucyBjYW4gYmUgdW5zdGFrZWQEB3RpbWVLZXkJARZrZXlTdGFrZWRUaW1lQnlBc3NldElkAQkA2AQBBQdhc3NldElkAwkBASEBCQEJaXNEZWZpbmVkAQUHdGltZUtleQkAAgEJAKwCAgkArAICAgRORlQgCAUFYXNzZXQEbmFtZQIOIGlzIG5vdCBzdGFrZWQDCQEBIQEJAQlpc0RlZmluZWQBCQEUa2V5U3Rha2VkRHVja0J5T3duZXIBBQdhZGRyZXNzCQACAQkArAICCQCsAgICCVRoZSBkdWNrIAgFBWFzc2V0BG5hbWUCDiBpcyBub3Qgc3Rha2VkBAVvd25lcgkBE3ZhbHVlT3JFcnJvck1lc3NhZ2UCCQCiCAEJARBrZXlEdWNrSWRUb093bmVyAQkA2AQBBQdhc3NldElkCQCsAgIJAKwCAgIETkZUIAgFBWFzc2V0BG5hbWUCDCBpcyBvcnBoYW5lZAMJAQIhPQIFBW93bmVyBQdhZGRyZXNzCQACAQIXU3Rha2VkIE5GVCBpcyBub3QgeW91cnMJAMwIAgkBDlNjcmlwdFRyYW5zZmVyAwgFAWkGY2FsbGVyAAEFB2Fzc2V0SWQJAMwIAgkBC0RlbGV0ZUVudHJ5AQUHdGltZUtleQkAzAgCCQELRGVsZXRlRW50cnkBCQEPa2V5RHVja0xvY2F0aW9uAQUKYXNzZXRJZFN0cgkAzAgCCQELRGVsZXRlRW50cnkBCQEia2V5U3Rha2VkVGltZUJ5VHlwZUFzc2V0SWRBbmRPd25lcgMFCkRVQ0tQUkVGSVgFCmFzc2V0SWRTdHIFB2FkZHJlc3MJAMwIAgkBC0RlbGV0ZUVudHJ5AQkBFGtleVN0YWtlZER1Y2tCeU93bmVyAQUHYWRkcmVzcwUDbmlsAWkBCGNsYWltUmVzAgZhbW91bnQLbGFuZEFzc2V0SWQDCQECIT0CCQCQAwEIBQFpCHBheW1lbnRzAAAJAAIBAiVjbGFpbVJlcyBkb2Vzbid0IHJlcXVpcmUgYW55IHBheW1lbnRzBARhZGRyCQClCAEIBQFpBmNhbGxlcgQFYXNzZXQJAQV2YWx1ZQEJAOwHAQkA2QQBBQtsYW5kQXNzZXRJZAMJAQEhAQkBCGNvbnRhaW5zAggFBWFzc2V0BG5hbWUFCkxBTkRQUkVGSVgJAAIBCQCsAgIJAKwCAgIETkZUIAUKTEFORFBSRUZJWAIgIHRva2VuIHNob3VsZCBiZSBwYXNzZWQgYXMgcGFyYW0EB3RpbWVLZXkJARZrZXlTdGFrZWRUaW1lQnlBc3NldElkAQULbGFuZEFzc2V0SWQECXNhdmVkVGltZQkAnwgBBQd0aW1lS2V5AwkBASEBCQEJaXNEZWZpbmVkAQUJc2F2ZWRUaW1lCQACAQkArAICCQCsAgICBE5GVCAIBQVhc3NldARuYW1lAg4gaXMgbm90IHN0YWtlZAQFb3duZXIJARFAZXh0ck5hdGl2ZSgxMDU4KQEJARFrZXlBc3NldElkVG9Pd25lcgEFC2xhbmRBc3NldElkAwkBAiE9AgUFb3duZXIFBGFkZHIJAAIBCQCsAgIFCkxBTkRQUkVGSVgCDSBpcyBub3QgeW91cnMEAWQJALUJAggFBWFzc2V0C2Rlc2NyaXB0aW9uAgFfBAhsYW5kU2l6ZQkAkQMCBQFkBQtyZWNMYW5kU2l6ZQQNdGVycmFpbkNvdW50cwkBDWNvdW50VGVycmFpbnMBCQCRAwIFAWQFC3JlY1RlcnJhaW5zBARkdWNrCQCiCAEJARRrZXlTdGFrZWREdWNrQnlPd25lcgEFBGFkZHIDCQEBIQEJAQlpc0RlZmluZWQBBQRkdWNrCQACAQIcWW91IGRvbid0IGhhdmUgYSBkdWNrIHN0YWtlZAQOZHVja0Fzc2V0SWRTdHIJAQV2YWx1ZQEFBGR1Y2sEC2N1ckxvY2F0aW9uCQELdmFsdWVPckVsc2UCCQCiCAEJAQ9rZXlEdWNrTG9jYXRpb24BBQ5kdWNrQXNzZXRJZFN0cgUPREVGQVVMVExPQ0FUSU9OBANsb2MJALUJAgkBBXZhbHVlAQULY3VyTG9jYXRpb24CAV8DCQECIT0CCQCRAwIFA2xvYwUKbG9jSWR4VHlwZQIBTAkAAgEJAKwCAgkArAICAhZEdWNrIGxvY2F0aW9uIHR5cGUgaXMgCQCRAwIFA2xvYwUKbG9jSWR4VHlwZQIRLCBidXQgc2hvdWxkIGJlIEwDCQECIT0CCQCRAwIFA2xvYwUIbG9jSWR4SWQFC2xhbmRBc3NldElkCQACAQkArAICCQCsAgIJAKwCAgIURHVjayBsb2NhdGlvbiBpZCBpcyAJAJEDAgUDbG9jBQhsb2NJZHhJZAIQLCBidXQgc2hvdWxkIGJlIAULbGFuZEFzc2V0SWQECWRlbHRhVGltZQkAZQIIBQlsYXN0QmxvY2sJdGltZXN0YW1wCQEFdmFsdWUBBQlzYXZlZFRpbWUDCQBmAgAABQlkZWx0YVRpbWUJAAIBCQCsAgIJAKwCAgkArAICAiZTYXZlZCB0aW1lc3RhbXAgaXMgaW4gZnV0dXJlLCBzYXZlZCA9IAkApAMBCQEFdmFsdWUBBQlzYXZlZFRpbWUCDCwgY3VycmVudCA9IAkApAMBCAUJbGFzdEJsb2NrCXRpbWVzdGFtcAQGcGllY2VzCQEPbnVtUGllY2VzQnlTaXplAQUIbGFuZFNpemUECGF2YWlsUmVzCQBoAgkAawMFCWRlbHRhVGltZQUPREFJTFlSRVNCWVBJRUNFBQlEQVlNSUxMSVMFBnBpZWNlcwMJAGYCBQZhbW91bnQFCGF2YWlsUmVzCQACAQkArAICCQCsAgIJAKwCAgIiTm90IGVub3VnaCByZXNvdXJjZXMsIGF2YWlsYWJsZSA9IAkApAMBBQhhdmFpbFJlcwIOLCByZXF1ZXN0ZWQgPSAJAKQDAQUGYW1vdW50BAxuZXdEZWx0YVRpbWUJAGsDCQBlAgUIYXZhaWxSZXMFBmFtb3VudAUJREFZTUlMTElTCQBoAgUGcGllY2VzBQ9EQUlMWVJFU0JZUElFQ0UEDG5ld1RpbWVzdGFtcAkAZQIIBQlsYXN0QmxvY2sJdGltZXN0YW1wBQxuZXdEZWx0YVRpbWUEBWJwS2V5CQERa2V5QmFja3BhY2tCeUR1Y2sBBQ5kdWNrQXNzZXRJZFN0cgQLY3VycmVudFBhY2sJALUJAgkBC3ZhbHVlT3JFbHNlAgkAoggBBQVicEtleQIPMDowXzBfMF8wXzBfMDo6AgE6BApjdXJyZW50UmVzCQC1CQIJAJEDAgULY3VycmVudFBhY2sFCGJwSWR4UmVzAgFfBAVicFJlcwkBBmFkZFJlcwMFCmN1cnJlbnRSZXMFDXRlcnJhaW5Db3VudHMJAGUCBQlkZWx0YVRpbWUFDG5ld0RlbHRhVGltZQQHbmV3UGFjawkAuQkCCQDMCAIJAJEDAgULY3VycmVudFBhY2sFCmJwSWR4TGV2ZWwJAMwIAgUFYnBSZXMJAMwIAgkAkQMCBQtjdXJyZW50UGFjawUIYnBJZHhNYXQJAMwIAgkAkQMCBQtjdXJyZW50UGFjawUJYnBJZHhQcm9kBQNuaWwCAToJAJQKAgkAzAgCCQELU3RyaW5nRW50cnkCBQVicEtleQUHbmV3UGFjawkAzAgCCQEMSW50ZWdlckVudHJ5AgUHdGltZUtleQUMbmV3VGltZXN0YW1wCQDMCAIJAQxJbnRlZ2VyRW50cnkCCQEia2V5U3Rha2VkVGltZUJ5VHlwZUFzc2V0SWRBbmRPd25lcgMFCkxBTkRQUkVGSVgFC2xhbmRBc3NldElkBQVvd25lcgUMbmV3VGltZXN0YW1wBQNuaWwFBHVuaXQBaQEGZmxpZ2h0AgdtZXNzYWdlA3NpZwMJAQEhAQkAxBMDBQdtZXNzYWdlBQNzaWcFA3B1YgkAAgECGHNpZ25hdHVyZSBkb2VzIG5vdCBtYXRjaAMJAQIhPQIJAJADAQgFAWkIcGF5bWVudHMAAAkAAgECI2ZsaWdodCBkb2Vzbid0IHJlcXVpcmUgYW55IHBheW1lbnRzBAVwYXJ0cwkAtQkCCQCwCQEFB21lc3NhZ2UCATsEAmhwCQC1CQIJAJEDAgkAtQkCCQCRAwIFBXBhcnRzAAACAXwAAAIBXwQFY3VySFAJAQ1wYXJzZUludFZhbHVlAQkAkQMCBQJocAAABAVuZXdIUAkBDXBhcnNlSW50VmFsdWUBCQCRAwIFAmhwAAEEDW5ld0xvY0FuZFRpbWUJALUJAgkAkQMCBQVwYXJ0cwABAgE6BAtuZXdMb2NhdGlvbgkAkQMCBQ1uZXdMb2NBbmRUaW1lAAAEBHRpbWUJAQ1wYXJzZUludFZhbHVlAQkAkQMCBQ1uZXdMb2NBbmRUaW1lAAEDAwkAZgIFBHRpbWUJAGQCCAUJbGFzdEJsb2NrCXRpbWVzdGFtcAURRklWRU1JTlVURVNNSUxMSVMGCQBmAgkAZQIIBQlsYXN0QmxvY2sJdGltZXN0YW1wBRFGSVZFTUlOVVRFU01JTExJUwUEdGltZQkAAgECEnNpZ25hdHVyZSBvdXRkYXRlZAQLZHVja0Fzc2V0SWQJARN2YWx1ZU9yRXJyb3JNZXNzYWdlAgkAoggBCQEUa2V5U3Rha2VkRHVja0J5T3duZXIBCQClCAEIBQFpBmNhbGxlcgIcWW91IGRvbid0IGhhdmUgYSBkdWNrIHN0YWtlZAQJa2V5SGVhbHRoCQENa2V5RHVja0hlYWx0aAEFC2R1Y2tBc3NldElkBAxvbGRGcm9tU3RhdGUJAQt2YWx1ZU9yRWxzZQIJAJ8IAQUJa2V5SGVhbHRoAGQDCQECIT0CBQxvbGRGcm9tU3RhdGUFBWN1ckhQCQACAQkArAICCQCsAgIJAKwCAgIKb2xkSGVhbHRoPQkApAMBCQELdmFsdWVPckVsc2UCCQCfCAEFCWtleUhlYWx0aABkAi8gZnJvbSBzdGF0ZSBkb2VzIG5vdCBtYXRjaCBvbmUgZnJvbSBmbGlnaHQgbG9nPQkApAMBBQVjdXJIUAMJAGcCAAAFBWN1ckhQCQACAQIeWW91IGNhbid0IGZseSB3aXRoIHplcm8gaGVhbHRoBAZsb2NLZXkJAQ9rZXlEdWNrTG9jYXRpb24BBQtkdWNrQXNzZXRJZAQLY3VyTG9jYXRpb24JAQt2YWx1ZU9yRWxzZQIJAKIIAQUGbG9jS2V5BQ9ERUZBVUxUTE9DQVRJT04DCQAAAgULbmV3TG9jYXRpb24FC2N1ckxvY2F0aW9uCQACAQIiWW91IGNhbid0IGZseSB0byB0aGUgc2FtZSBsb2NhdGlvbgkAlAoCCQDMCAIJAQtTdHJpbmdFbnRyeQIFBmxvY0tleQMJAGYCBQVuZXdIUAAABQtuZXdMb2NhdGlvbgULY3VyTG9jYXRpb24JAMwIAgkBDEludGVnZXJFbnRyeQIFCWtleUhlYWx0aAUFbmV3SFAFA25pbAUEdW5pdAFpAQlzZXRIZWFsdGgCBmhlYWx0aAtkdWNrQXNzZXRJZAQOZHVja0Fzc2V0SWRTdHIJARN2YWx1ZU9yRXJyb3JNZXNzYWdlAgkAoggBCQEUa2V5U3Rha2VkRHVja0J5T3duZXIBCQClCAEIBQFpBmNhbGxlcgIcWW91IGRvbid0IGhhdmUgYSBkdWNrIHN0YWtlZAMDCQBmAgAABQZoZWFsdGgGCQBmAgUGaGVhbHRoAGQJAAIBAhpIUCBzaG91bGQgYmUgd2l0aGluIDAuLjEwMAkAzAgCCQEMSW50ZWdlckVudHJ5AgkBDWtleUR1Y2tIZWFsdGgBBQtkdWNrQXNzZXRJZAUGaGVhbHRoBQNuaWwBaQEOdXBkYXRlQmFja3BhY2sCC2R1Y2tBc3NldElkB25ld1BhY2sDCQECIT0CCAUBaQZjYWxsZXIFC2Vjb25vbXlBZGRyCQACAQIRcGVybWlzc2lvbiBkZW5pZWQJAJQKAgkAzAgCCQELU3RyaW5nRW50cnkCCQERa2V5QmFja3BhY2tCeUR1Y2sBBQtkdWNrQXNzZXRJZAUHbmV3UGFjawUDbmlsBQduZXdQYWNrAGTcKf4=", "height": 3421834, "applicationStatus": "succeeded", "spentComplexity": 0 } View: original | compacted Prev: AUF3WKjnuuh9u7oXNsaqw8mm1s7GhVRDSPcFvNdUKUvs Next: ADE5WWDHcWwh1kxhCyQq9Tt32t4RRHVEJ35yQUdUquxT Diff:
Old | New | Differences | |
---|---|---|---|
1 | 1 | {-# STDLIB_VERSION 6 #-} | |
2 | 2 | {-# SCRIPT_TYPE ACCOUNT #-} | |
3 | 3 | {-# CONTENT_TYPE DAPP #-} | |
4 | - | let DEFAULTPRICES = 250000000 | |
5 | - | ||
6 | - | let DEFAULTPRICEM = 1000000000 | |
7 | - | ||
8 | - | let DEFAULTPRICEL = 2250000000 | |
9 | - | ||
10 | - | let DEFAULTPRICEXL = 4000000000 | |
11 | - | ||
12 | 4 | let usdnAssetId = base58'DG2xFkPdDwKUoBkzGAhQtLpSGzfXLiCYPEzeKH2Ad24p' | |
13 | 5 | ||
14 | 6 | let incubatorAddr = addressFromStringValue("3PEktVux2RhchSN63DsDo4b4mz4QqzKSeDv") | |
17 | 9 | ||
18 | 10 | let backEndAddr = addressFromStringValue("3PBKeu78xdNyaKw5k2Ua6npqe8MRQegoq17") | |
19 | 11 | ||
12 | + | let economyAddr = addressFromStringValue("3P2sk1KncSxRaZs8b4CWGPw2jkvvav74u4D") | |
13 | + | ||
14 | + | let pub = base58'6LfPuKJjLgekmncBhMg2LZyMTNVzZBccXR28ySXm9uXD' | |
15 | + | ||
20 | 16 | let LANDPREFIX = "LAND" | |
21 | 17 | ||
22 | 18 | let DUCKPREFIX = "DUCK" | |
23 | 19 | ||
24 | - | let | |
20 | + | let DEFAULTLOCATION = "Africa_F_Africa" | |
25 | 21 | ||
26 | - | let | |
22 | + | let DAILYRESBYPIECE = 3456000 | |
27 | 23 | ||
28 | - | let | |
24 | + | let DAYMILLIS = 86400000 | |
29 | 25 | ||
30 | - | let LENXL = "xlLen" | |
31 | - | ||
32 | - | let MAPS = "sMap" | |
33 | - | ||
34 | - | let MAPM = "mMap" | |
35 | - | ||
36 | - | let MAPL = "lMap" | |
37 | - | ||
38 | - | let MAPXL = "xlMap" | |
39 | - | ||
40 | - | func keyPaymentRequired (landSize) = ("paymentRequired_" + landSize) | |
41 | - | ||
42 | - | ||
43 | - | func keyLandToAssetId (landNum) = ("landToAsset_" + landNum) | |
44 | - | ||
26 | + | let FIVEMINUTESMILLIS = 300000 | |
45 | 27 | ||
46 | 28 | func keyAssetIdToOwner (assetId) = ("nftOwner_" + assetId) | |
47 | 29 | ||
58 | 40 | func keyStakedTimeByTypeAssetIdAndOwner (nftType,assetId,ownerAddr) = ((((("stakedTimeByTypeAssetIdAndOwner_" + nftType) + "_") + assetId) + "_") + ownerAddr) | |
59 | 41 | ||
60 | 42 | ||
61 | - | func keyFromContinentByAssetId (assetId) = ("fromContinent_" + assetId) | |
62 | - | ||
63 | - | ||
64 | - | func keyToContinentByAssetId (assetId) = ("toContinent_" + assetId) | |
65 | - | ||
66 | - | ||
67 | - | func keyWindsByAssetId (assetId) = ("winds_" + assetId) | |
68 | - | ||
69 | - | ||
70 | - | func keyObstaclesByAssetId (assetId) = ("obstacles_" + assetId) | |
71 | - | ||
72 | - | ||
73 | - | func keyMovesByAssetId (assetId) = ("userMoves_" + assetId) | |
74 | - | ||
75 | - | ||
76 | 43 | func keyLandToOwner (landNum) = ("landOwner_" + landNum) | |
77 | 44 | ||
78 | 45 | ||
79 | - | func | |
46 | + | func keyBackpackByDuck (duckAssetId) = ("backPack_" + duckAssetId) | |
80 | 47 | ||
81 | 48 | ||
82 | - | func | |
49 | + | func keyDuckLocation (duckAssetId) = ("duckLocation_" + duckAssetId) | |
83 | 50 | ||
84 | 51 | ||
85 | - | func | |
52 | + | func keyDuckHealth (duckAssetId) = ("duckHealth_" + duckAssetId) | |
86 | 53 | ||
87 | 54 | ||
88 | 55 | let recLandNum = 0 | |
93 | 60 | ||
94 | 61 | let recContinent = 3 | |
95 | 62 | ||
96 | - | func abs (x) = if ((x >= 0)) | |
97 | - | then x | |
98 | - | else -(x) | |
63 | + | let locIdxContinent = 0 | |
64 | + | ||
65 | + | let locIdxType = 1 | |
66 | + | ||
67 | + | let locIdxId = 2 | |
68 | + | ||
69 | + | let bpIdxLevel = 0 | |
70 | + | ||
71 | + | let bpIdxRes = 1 | |
72 | + | ||
73 | + | let bpIdxMat = 2 | |
74 | + | ||
75 | + | let bpIdxProd = 3 | |
76 | + | ||
77 | + | let idxA = 0 | |
78 | + | ||
79 | + | let idxB = 1 | |
80 | + | ||
81 | + | let idxC = 2 | |
82 | + | ||
83 | + | let idxD = 3 | |
84 | + | ||
85 | + | let idxE = 4 | |
86 | + | ||
87 | + | let idxF = 5 | |
88 | + | ||
89 | + | 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)] | |
99 | 90 | ||
100 | 91 | ||
101 | - | func buyInternal (pmt,txId,caller,landSize,lenKey,mapKey,defaultPrice) = if ((pmt.assetId != usdnAssetId)) | |
102 | - | then throw("Allowed USDN payment only!") | |
103 | - | else { | |
104 | - | let pmtReq = valueOrElse(getInteger(keyPaymentRequired(landSize)), defaultPrice) | |
105 | - | if ((pmtReq > pmt.amount)) | |
106 | - | then throw(("Payment attached should be at least " + toString(pmtReq))) | |
107 | - | else { | |
108 | - | let len = valueOrErrorMessage(getInteger(lenKey), "initPresale should be called first") | |
109 | - | if ((len == 0)) | |
110 | - | then throw((("All " + landSize) + " lands are sold out")) | |
111 | - | else { | |
112 | - | let random = abs(toInt((toBigInt(txId) % toBigInt(len)))) | |
113 | - | let lands = split_51C(valueOrErrorMessage(getString(mapKey), (mapKey + " is not initialized")), "_") | |
114 | - | let landNum = lands[random] | |
115 | - | let nft = valueOrErrorMessage(getString(keyLandToAssetId(landNum)), (keyNftName(landNum, landSize) + " does not exist")) | |
116 | - | [IntegerEntry(lenKey, (len - 1)), StringEntry(mapKey, makeString_11C(removeByIndex(lands, random), "_")), ScriptTransfer(caller, 1, fromBase58String(nft)), StringEntry(keyAssetIdToOwner(nft), toString(caller)), StringEntry(keyLandToOwner(landNum), toString(caller))] | |
117 | - | } | |
118 | - | } | |
92 | + | func numPiecesBySize (landSize) = match landSize { | |
93 | + | case _ => | |
94 | + | if (("S" == $match0)) | |
95 | + | then 25 | |
96 | + | else if (("M" == $match0)) | |
97 | + | then 100 | |
98 | + | else if (("L" == $match0)) | |
99 | + | then 225 | |
100 | + | else if (("XL" == $match0)) | |
101 | + | then 400 | |
102 | + | else if (("XXL" == $match0)) | |
103 | + | then 625 | |
104 | + | else throw("Unknown land size") | |
105 | + | } | |
106 | + | ||
107 | + | ||
108 | + | func addRes (currentRes,terrainCounts,deltaTime) = { | |
109 | + | func adder (acc,i) = { | |
110 | + | let resOfType = (fraction(deltaTime, DAILYRESBYPIECE, DAYMILLIS) * terrainCounts[i]) | |
111 | + | (acc :+ toString((parseIntValue(currentRes[i]) + resOfType))) | |
119 | 112 | } | |
113 | + | ||
114 | + | let r = { | |
115 | + | let $l = [0, 1, 2, 3, 4, 5] | |
116 | + | let $s = size($l) | |
117 | + | let $acc0 = nil | |
118 | + | func $f0_1 ($a,$i) = if (($i >= $s)) | |
119 | + | then $a | |
120 | + | else adder($a, $l[$i]) | |
121 | + | ||
122 | + | func $f0_2 ($a,$i) = if (($i >= $s)) | |
123 | + | then $a | |
124 | + | else throw("List size exceeds 6") | |
125 | + | ||
126 | + | $f0_2($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($acc0, 0), 1), 2), 3), 4), 5), 6) | |
127 | + | } | |
128 | + | makeString(r, "_") | |
129 | + | } | |
120 | 130 | ||
121 | 131 | ||
122 | 132 | @Callable(i) | |
154 | 164 | ||
155 | 165 | ||
156 | 166 | @Callable(i) | |
157 | - | func updateStakedLand (assetIdStr,newTimestamp) = if ((i.caller != backEndAddr)) | |
158 | - | then throw("permission denied") | |
159 | - | else if ((size(i.payments) != 0)) | |
160 | - | then throw("updateStakedLand doesn't require any payments") | |
161 | - | else { | |
162 | - | let assetId = fromBase58String(assetIdStr) | |
163 | - | let asset = value(assetInfo(assetId)) | |
164 | - | if ((asset.issuer != this)) | |
165 | - | then throw("Unknown issuer of token") | |
166 | - | else if (!(contains(asset.name, LANDPREFIX))) | |
167 | - | then throw((("Only NFT " + LANDPREFIX) + " tokens are accepted")) | |
168 | - | else { | |
169 | - | let timeKey = keyStakedTimeByAssetId(toBase58String(assetId)) | |
170 | - | if (!(isDefined(getInteger(timeKey)))) | |
171 | - | then throw((("NFT " + asset.name) + " is not staked")) | |
172 | - | else { | |
173 | - | let ownerAddr = getStringValue(keyAssetIdToOwner(assetIdStr)) | |
174 | - | [IntegerEntry(timeKey, newTimestamp), IntegerEntry(keyStakedTimeByTypeAssetIdAndOwner(LANDPREFIX, assetIdStr, ownerAddr), newTimestamp)] | |
175 | - | } | |
176 | - | } | |
177 | - | } | |
178 | - | ||
179 | - | ||
180 | - | ||
181 | - | @Callable(i) | |
182 | - | func unstakeLand (assetIdStr) = if ((size(i.payments) != 0)) | |
167 | + | func unstakeLand (landAssetId) = if ((size(i.payments) != 0)) | |
183 | 168 | then throw("unstake doesn't require any payments") | |
184 | 169 | else { | |
185 | - | let assetId = fromBase58String( | |
170 | + | let assetId = fromBase58String(landAssetId) | |
186 | 171 | let address = toString(i.caller) | |
187 | 172 | let asset = value(assetInfo(assetId)) | |
188 | 173 | if ((asset.issuer != this)) | |
190 | 175 | else if (!(contains(asset.name, LANDPREFIX))) | |
191 | 176 | then throw((("Only NFT " + LANDPREFIX) + " tokens can be unstaked")) | |
192 | 177 | else { | |
193 | - | let timeKey = keyStakedTimeByAssetId( | |
178 | + | let timeKey = keyStakedTimeByAssetId(landAssetId) | |
194 | 179 | if (!(isDefined(timeKey))) | |
195 | 180 | then throw((("NFT " + asset.name) + " is not staked")) | |
196 | 181 | else { | |
197 | - | let owner = valueOrErrorMessage(getString(keyAssetIdToOwner( | |
182 | + | let owner = valueOrErrorMessage(getString(keyAssetIdToOwner(landAssetId)), (("NFT " + asset.name) + " is orphaned")) | |
198 | 183 | if ((owner != address)) | |
199 | 184 | then throw("Staked NFT is not yours") | |
200 | - | else [ScriptTransfer(i.caller, 1, assetId), DeleteEntry(timeKey), DeleteEntry(keyStakedTimeByTypeAssetIdAndOwner(LANDPREFIX, | |
185 | + | else [ScriptTransfer(i.caller, 1, assetId), DeleteEntry(timeKey), DeleteEntry(keyStakedTimeByTypeAssetIdAndOwner(LANDPREFIX, landAssetId, address))] | |
201 | 186 | } | |
202 | 187 | } | |
203 | 188 | } | |
226 | 211 | then throw((("NFT " + asset.name) + " is already staked")) | |
227 | 212 | else if (isDefined(getString(keyStakedDuckByOwner(address)))) | |
228 | 213 | then throw(("You already staked one duck: " + asset.name)) | |
229 | - | else [IntegerEntry(timeKey, lastBlock.timestamp), IntegerEntry(keyStakedTimeByTypeAssetIdAndOwner(DUCKPREFIX, toBase58String(assetId), address), lastBlock.timestamp), StringEntry(keyDuckIdToOwner(assetIdStr), address), StringEntry(keyStakedDuckByOwner(address), assetIdStr)] | |
214 | + | else { | |
215 | + | let locKey = keyDuckLocation(assetIdStr) | |
216 | + | let location = getString(locKey) | |
217 | + | let keyHealth = keyDuckHealth(assetIdStr) | |
218 | + | let health = getInteger(keyHealth) | |
219 | + | let bpKey = keyBackpackByDuck(assetIdStr) | |
220 | + | let backpack = getString(bpKey) | |
221 | + | ([IntegerEntry(timeKey, lastBlock.timestamp), IntegerEntry(keyStakedTimeByTypeAssetIdAndOwner(DUCKPREFIX, toBase58String(assetId), address), lastBlock.timestamp), StringEntry(keyDuckIdToOwner(assetIdStr), address), StringEntry(keyStakedDuckByOwner(address), assetIdStr)] ++ (if (isDefined(location)) | |
222 | + | then nil | |
223 | + | else ([StringEntry(locKey, DEFAULTLOCATION)] ++ (if (isDefined(health)) | |
224 | + | then nil | |
225 | + | else ([IntegerEntry(keyHealth, 100)] ++ (if (isDefined(backpack)) | |
226 | + | then nil | |
227 | + | else [StringEntry(bpKey, "0:0_0_0_0_0_0::")])))))) | |
228 | + | } | |
230 | 229 | } | |
231 | 230 | } | |
232 | 231 | } | |
256 | 255 | let owner = valueOrErrorMessage(getString(keyDuckIdToOwner(toBase58String(assetId))), (("NFT " + asset.name) + " is orphaned")) | |
257 | 256 | if ((owner != address)) | |
258 | 257 | then throw("Staked NFT is not yours") | |
259 | - | else [ScriptTransfer(i.caller, 1, assetId), DeleteEntry(timeKey), DeleteEntry(keyStakedTimeByTypeAssetIdAndOwner(DUCKPREFIX, assetIdStr, address)), DeleteEntry(keyStakedDuckByOwner(address))] | |
258 | + | else [ScriptTransfer(i.caller, 1, assetId), DeleteEntry(timeKey), DeleteEntry(keyDuckLocation(assetIdStr)), DeleteEntry(keyStakedTimeByTypeAssetIdAndOwner(DUCKPREFIX, assetIdStr, address)), DeleteEntry(keyStakedDuckByOwner(address))] | |
260 | 259 | } | |
261 | 260 | } | |
262 | 261 | } | |
264 | 263 | ||
265 | 264 | ||
266 | 265 | @Callable(i) | |
267 | - | func buyLand (landSize) = if ((i.caller != this)) | |
268 | - | then throw("Permission denied") | |
269 | - | else if ((size(i.payments) != 1)) | |
270 | - | then throw("Exactly one payment required") | |
266 | + | func claimRes (amount,landAssetId) = if ((size(i.payments) != 0)) | |
267 | + | then throw("claimRes doesn't require any payments") | |
268 | + | else { | |
269 | + | let addr = toString(i.caller) | |
270 | + | let asset = value(assetInfo(fromBase58String(landAssetId))) | |
271 | + | if (!(contains(asset.name, LANDPREFIX))) | |
272 | + | then throw((("NFT " + LANDPREFIX) + " token should be passed as param")) | |
273 | + | else { | |
274 | + | let timeKey = keyStakedTimeByAssetId(landAssetId) | |
275 | + | let savedTime = getInteger(timeKey) | |
276 | + | if (!(isDefined(savedTime))) | |
277 | + | then throw((("NFT " + asset.name) + " is not staked")) | |
278 | + | else { | |
279 | + | let owner = getStringValue(keyAssetIdToOwner(landAssetId)) | |
280 | + | if ((owner != addr)) | |
281 | + | then throw((LANDPREFIX + " is not yours")) | |
282 | + | else { | |
283 | + | let d = split(asset.description, "_") | |
284 | + | let landSize = d[recLandSize] | |
285 | + | let terrainCounts = countTerrains(d[recTerrains]) | |
286 | + | let duck = getString(keyStakedDuckByOwner(addr)) | |
287 | + | if (!(isDefined(duck))) | |
288 | + | then throw("You don't have a duck staked") | |
289 | + | else { | |
290 | + | let duckAssetIdStr = value(duck) | |
291 | + | let curLocation = valueOrElse(getString(keyDuckLocation(duckAssetIdStr)), DEFAULTLOCATION) | |
292 | + | let loc = split(value(curLocation), "_") | |
293 | + | if ((loc[locIdxType] != "L")) | |
294 | + | then throw((("Duck location type is " + loc[locIdxType]) + ", but should be L")) | |
295 | + | else if ((loc[locIdxId] != landAssetId)) | |
296 | + | then throw(((("Duck location id is " + loc[locIdxId]) + ", but should be ") + landAssetId)) | |
297 | + | else { | |
298 | + | let deltaTime = (lastBlock.timestamp - value(savedTime)) | |
299 | + | if ((0 > deltaTime)) | |
300 | + | then throw(((("Saved timestamp is in future, saved = " + toString(value(savedTime))) + ", current = ") + toString(lastBlock.timestamp))) | |
301 | + | else { | |
302 | + | let pieces = numPiecesBySize(landSize) | |
303 | + | let availRes = (fraction(deltaTime, DAILYRESBYPIECE, DAYMILLIS) * pieces) | |
304 | + | if ((amount > availRes)) | |
305 | + | then throw(((("Not enough resources, available = " + toString(availRes)) + ", requested = ") + toString(amount))) | |
306 | + | else { | |
307 | + | let newDeltaTime = fraction((availRes - amount), DAYMILLIS, (pieces * DAILYRESBYPIECE)) | |
308 | + | let newTimestamp = (lastBlock.timestamp - newDeltaTime) | |
309 | + | let bpKey = keyBackpackByDuck(duckAssetIdStr) | |
310 | + | let currentPack = split(valueOrElse(getString(bpKey), "0:0_0_0_0_0_0::"), ":") | |
311 | + | let currentRes = split(currentPack[bpIdxRes], "_") | |
312 | + | let bpRes = addRes(currentRes, terrainCounts, (deltaTime - newDeltaTime)) | |
313 | + | let newPack = makeString([currentPack[bpIdxLevel], bpRes, currentPack[bpIdxMat], currentPack[bpIdxProd]], ":") | |
314 | + | $Tuple2([StringEntry(bpKey, newPack), IntegerEntry(timeKey, newTimestamp), IntegerEntry(keyStakedTimeByTypeAssetIdAndOwner(LANDPREFIX, landAssetId, owner), newTimestamp)], unit) | |
315 | + | } | |
316 | + | } | |
317 | + | } | |
318 | + | } | |
319 | + | } | |
320 | + | } | |
321 | + | } | |
322 | + | } | |
323 | + | ||
324 | + | ||
325 | + | ||
326 | + | @Callable(i) | |
327 | + | func flight (message,sig) = if (!(sigVerify_8Kb(message, sig, pub))) | |
328 | + | then throw("signature does not match") | |
329 | + | else if ((size(i.payments) != 0)) | |
330 | + | then throw("flight doesn't require any payments") | |
271 | 331 | else { | |
272 | - | let pmt = value(i.payments[0]) | |
273 | - | let actions = match landSize { | |
274 | - | case _ => | |
275 | - | if (("S" == $match0)) | |
276 | - | then buyInternal(pmt, i.transactionId, i.caller, landSize, LENS, MAPS, DEFAULTPRICES) | |
277 | - | else if (("M" == $match0)) | |
278 | - | then buyInternal(pmt, i.transactionId, i.caller, landSize, LENM, MAPM, DEFAULTPRICEM) | |
279 | - | else if (("L" == $match0)) | |
280 | - | then buyInternal(pmt, i.transactionId, i.caller, landSize, LENL, MAPL, DEFAULTPRICEL) | |
281 | - | else if (("XL" == $match0)) | |
282 | - | then buyInternal(pmt, i.transactionId, i.caller, landSize, LENXL, MAPXL, DEFAULTPRICEXL) | |
283 | - | else throw("Unknown land size") | |
332 | + | let parts = split(toUtf8String(message), ";") | |
333 | + | let hp = split(split(parts[0], "|")[0], "_") | |
334 | + | let curHP = parseIntValue(hp[0]) | |
335 | + | let newHP = parseIntValue(hp[1]) | |
336 | + | let newLocAndTime = split(parts[1], ":") | |
337 | + | let newLocation = newLocAndTime[0] | |
338 | + | let time = parseIntValue(newLocAndTime[1]) | |
339 | + | if (if ((time > (lastBlock.timestamp + FIVEMINUTESMILLIS))) | |
340 | + | then true | |
341 | + | else ((lastBlock.timestamp - FIVEMINUTESMILLIS) > time)) | |
342 | + | then throw("signature outdated") | |
343 | + | else { | |
344 | + | let duckAssetId = valueOrErrorMessage(getString(keyStakedDuckByOwner(toString(i.caller))), "You don't have a duck staked") | |
345 | + | let keyHealth = keyDuckHealth(duckAssetId) | |
346 | + | let oldFromState = valueOrElse(getInteger(keyHealth), 100) | |
347 | + | if ((oldFromState != curHP)) | |
348 | + | then throw(((("oldHealth=" + toString(valueOrElse(getInteger(keyHealth), 100))) + " from state does not match one from flight log=") + toString(curHP))) | |
349 | + | else if ((0 >= curHP)) | |
350 | + | then throw("You can't fly with zero health") | |
351 | + | else { | |
352 | + | let locKey = keyDuckLocation(duckAssetId) | |
353 | + | let curLocation = valueOrElse(getString(locKey), DEFAULTLOCATION) | |
354 | + | if ((newLocation == curLocation)) | |
355 | + | then throw("You can't fly to the same location") | |
356 | + | else $Tuple2([StringEntry(locKey, if ((newHP > 0)) | |
357 | + | then newLocation | |
358 | + | else curLocation), IntegerEntry(keyHealth, newHP)], unit) | |
359 | + | } | |
360 | + | } | |
284 | 361 | } | |
285 | - | $Tuple2(actions, unit) | |
286 | - | } | |
362 | + | ||
363 | + | ||
364 | + | ||
365 | + | @Callable(i) | |
366 | + | func setHealth (health,duckAssetId) = { | |
367 | + | let duckAssetIdStr = valueOrErrorMessage(getString(keyStakedDuckByOwner(toString(i.caller))), "You don't have a duck staked") | |
368 | + | if (if ((0 > health)) | |
369 | + | then true | |
370 | + | else (health > 100)) | |
371 | + | then throw("HP should be within 0..100") | |
372 | + | else [IntegerEntry(keyDuckHealth(duckAssetId), health)] | |
373 | + | } | |
374 | + | ||
375 | + | ||
376 | + | ||
377 | + | @Callable(i) | |
378 | + | func updateBackpack (duckAssetId,newPack) = if ((i.caller != economyAddr)) | |
379 | + | then throw("permission denied") | |
380 | + | else $Tuple2([StringEntry(keyBackpackByDuck(duckAssetId), newPack)], newPack) | |
287 | 381 | ||
288 | 382 |
Old | New | Differences | |
---|---|---|---|
1 | 1 | {-# STDLIB_VERSION 6 #-} | |
2 | 2 | {-# SCRIPT_TYPE ACCOUNT #-} | |
3 | 3 | {-# CONTENT_TYPE DAPP #-} | |
4 | - | let DEFAULTPRICES = 250000000 | |
5 | - | ||
6 | - | let DEFAULTPRICEM = 1000000000 | |
7 | - | ||
8 | - | let DEFAULTPRICEL = 2250000000 | |
9 | - | ||
10 | - | let DEFAULTPRICEXL = 4000000000 | |
11 | - | ||
12 | 4 | let usdnAssetId = base58'DG2xFkPdDwKUoBkzGAhQtLpSGzfXLiCYPEzeKH2Ad24p' | |
13 | 5 | ||
14 | 6 | let incubatorAddr = addressFromStringValue("3PEktVux2RhchSN63DsDo4b4mz4QqzKSeDv") | |
15 | 7 | ||
16 | 8 | let breederAddr = addressFromStringValue("3PDVuU45H7Eh5dmtNbnRNRStGwULA7NY6Hb") | |
17 | 9 | ||
18 | 10 | let backEndAddr = addressFromStringValue("3PBKeu78xdNyaKw5k2Ua6npqe8MRQegoq17") | |
19 | 11 | ||
12 | + | let economyAddr = addressFromStringValue("3P2sk1KncSxRaZs8b4CWGPw2jkvvav74u4D") | |
13 | + | ||
14 | + | let pub = base58'6LfPuKJjLgekmncBhMg2LZyMTNVzZBccXR28ySXm9uXD' | |
15 | + | ||
20 | 16 | let LANDPREFIX = "LAND" | |
21 | 17 | ||
22 | 18 | let DUCKPREFIX = "DUCK" | |
23 | 19 | ||
24 | - | let | |
20 | + | let DEFAULTLOCATION = "Africa_F_Africa" | |
25 | 21 | ||
26 | - | let | |
22 | + | let DAILYRESBYPIECE = 3456000 | |
27 | 23 | ||
28 | - | let | |
24 | + | let DAYMILLIS = 86400000 | |
29 | 25 | ||
30 | - | let LENXL = "xlLen" | |
31 | - | ||
32 | - | let MAPS = "sMap" | |
33 | - | ||
34 | - | let MAPM = "mMap" | |
35 | - | ||
36 | - | let MAPL = "lMap" | |
37 | - | ||
38 | - | let MAPXL = "xlMap" | |
39 | - | ||
40 | - | func keyPaymentRequired (landSize) = ("paymentRequired_" + landSize) | |
41 | - | ||
42 | - | ||
43 | - | func keyLandToAssetId (landNum) = ("landToAsset_" + landNum) | |
44 | - | ||
26 | + | let FIVEMINUTESMILLIS = 300000 | |
45 | 27 | ||
46 | 28 | func keyAssetIdToOwner (assetId) = ("nftOwner_" + assetId) | |
47 | 29 | ||
48 | 30 | ||
49 | 31 | func keyDuckIdToOwner (assetId) = ("duckOwner_" + assetId) | |
50 | 32 | ||
51 | 33 | ||
52 | 34 | func keyStakedTimeByAssetId (assetId) = ("stakedTime_" + assetId) | |
53 | 35 | ||
54 | 36 | ||
55 | 37 | func keyStakedDuckByOwner (ownerAddr) = ("stakedDuckByOwner_" + ownerAddr) | |
56 | 38 | ||
57 | 39 | ||
58 | 40 | func keyStakedTimeByTypeAssetIdAndOwner (nftType,assetId,ownerAddr) = ((((("stakedTimeByTypeAssetIdAndOwner_" + nftType) + "_") + assetId) + "_") + ownerAddr) | |
59 | 41 | ||
60 | 42 | ||
61 | - | func keyFromContinentByAssetId (assetId) = ("fromContinent_" + assetId) | |
62 | - | ||
63 | - | ||
64 | - | func keyToContinentByAssetId (assetId) = ("toContinent_" + assetId) | |
65 | - | ||
66 | - | ||
67 | - | func keyWindsByAssetId (assetId) = ("winds_" + assetId) | |
68 | - | ||
69 | - | ||
70 | - | func keyObstaclesByAssetId (assetId) = ("obstacles_" + assetId) | |
71 | - | ||
72 | - | ||
73 | - | func keyMovesByAssetId (assetId) = ("userMoves_" + assetId) | |
74 | - | ||
75 | - | ||
76 | 43 | func keyLandToOwner (landNum) = ("landOwner_" + landNum) | |
77 | 44 | ||
78 | 45 | ||
79 | - | func | |
46 | + | func keyBackpackByDuck (duckAssetId) = ("backPack_" + duckAssetId) | |
80 | 47 | ||
81 | 48 | ||
82 | - | func | |
49 | + | func keyDuckLocation (duckAssetId) = ("duckLocation_" + duckAssetId) | |
83 | 50 | ||
84 | 51 | ||
85 | - | func | |
52 | + | func keyDuckHealth (duckAssetId) = ("duckHealth_" + duckAssetId) | |
86 | 53 | ||
87 | 54 | ||
88 | 55 | let recLandNum = 0 | |
89 | 56 | ||
90 | 57 | let recLandSize = 1 | |
91 | 58 | ||
92 | 59 | let recTerrains = 2 | |
93 | 60 | ||
94 | 61 | let recContinent = 3 | |
95 | 62 | ||
96 | - | func abs (x) = if ((x >= 0)) | |
97 | - | then x | |
98 | - | else -(x) | |
63 | + | let locIdxContinent = 0 | |
64 | + | ||
65 | + | let locIdxType = 1 | |
66 | + | ||
67 | + | let locIdxId = 2 | |
68 | + | ||
69 | + | let bpIdxLevel = 0 | |
70 | + | ||
71 | + | let bpIdxRes = 1 | |
72 | + | ||
73 | + | let bpIdxMat = 2 | |
74 | + | ||
75 | + | let bpIdxProd = 3 | |
76 | + | ||
77 | + | let idxA = 0 | |
78 | + | ||
79 | + | let idxB = 1 | |
80 | + | ||
81 | + | let idxC = 2 | |
82 | + | ||
83 | + | let idxD = 3 | |
84 | + | ||
85 | + | let idxE = 4 | |
86 | + | ||
87 | + | let idxF = 5 | |
88 | + | ||
89 | + | 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)] | |
99 | 90 | ||
100 | 91 | ||
101 | - | func buyInternal (pmt,txId,caller,landSize,lenKey,mapKey,defaultPrice) = if ((pmt.assetId != usdnAssetId)) | |
102 | - | then throw("Allowed USDN payment only!") | |
103 | - | else { | |
104 | - | let pmtReq = valueOrElse(getInteger(keyPaymentRequired(landSize)), defaultPrice) | |
105 | - | if ((pmtReq > pmt.amount)) | |
106 | - | then throw(("Payment attached should be at least " + toString(pmtReq))) | |
107 | - | else { | |
108 | - | let len = valueOrErrorMessage(getInteger(lenKey), "initPresale should be called first") | |
109 | - | if ((len == 0)) | |
110 | - | then throw((("All " + landSize) + " lands are sold out")) | |
111 | - | else { | |
112 | - | let random = abs(toInt((toBigInt(txId) % toBigInt(len)))) | |
113 | - | let lands = split_51C(valueOrErrorMessage(getString(mapKey), (mapKey + " is not initialized")), "_") | |
114 | - | let landNum = lands[random] | |
115 | - | let nft = valueOrErrorMessage(getString(keyLandToAssetId(landNum)), (keyNftName(landNum, landSize) + " does not exist")) | |
116 | - | [IntegerEntry(lenKey, (len - 1)), StringEntry(mapKey, makeString_11C(removeByIndex(lands, random), "_")), ScriptTransfer(caller, 1, fromBase58String(nft)), StringEntry(keyAssetIdToOwner(nft), toString(caller)), StringEntry(keyLandToOwner(landNum), toString(caller))] | |
117 | - | } | |
118 | - | } | |
92 | + | func numPiecesBySize (landSize) = match landSize { | |
93 | + | case _ => | |
94 | + | if (("S" == $match0)) | |
95 | + | then 25 | |
96 | + | else if (("M" == $match0)) | |
97 | + | then 100 | |
98 | + | else if (("L" == $match0)) | |
99 | + | then 225 | |
100 | + | else if (("XL" == $match0)) | |
101 | + | then 400 | |
102 | + | else if (("XXL" == $match0)) | |
103 | + | then 625 | |
104 | + | else throw("Unknown land size") | |
105 | + | } | |
106 | + | ||
107 | + | ||
108 | + | func addRes (currentRes,terrainCounts,deltaTime) = { | |
109 | + | func adder (acc,i) = { | |
110 | + | let resOfType = (fraction(deltaTime, DAILYRESBYPIECE, DAYMILLIS) * terrainCounts[i]) | |
111 | + | (acc :+ toString((parseIntValue(currentRes[i]) + resOfType))) | |
119 | 112 | } | |
113 | + | ||
114 | + | let r = { | |
115 | + | let $l = [0, 1, 2, 3, 4, 5] | |
116 | + | let $s = size($l) | |
117 | + | let $acc0 = nil | |
118 | + | func $f0_1 ($a,$i) = if (($i >= $s)) | |
119 | + | then $a | |
120 | + | else adder($a, $l[$i]) | |
121 | + | ||
122 | + | func $f0_2 ($a,$i) = if (($i >= $s)) | |
123 | + | then $a | |
124 | + | else throw("List size exceeds 6") | |
125 | + | ||
126 | + | $f0_2($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($acc0, 0), 1), 2), 3), 4), 5), 6) | |
127 | + | } | |
128 | + | makeString(r, "_") | |
129 | + | } | |
120 | 130 | ||
121 | 131 | ||
122 | 132 | @Callable(i) | |
123 | 133 | func stakeLand () = { | |
124 | 134 | let pmt = value(i.payments[0]) | |
125 | 135 | let assetId = value(pmt.assetId) | |
126 | 136 | let address = toString(i.caller) | |
127 | 137 | if ((pmt.amount != 1)) | |
128 | 138 | then throw((("NFT " + LANDPREFIX) + " token should be attached as payment")) | |
129 | 139 | else { | |
130 | 140 | let asset = value(assetInfo(assetId)) | |
131 | 141 | if ((asset.issuer != this)) | |
132 | 142 | then throw("Unknown issuer of token") | |
133 | 143 | else if (!(contains(asset.name, LANDPREFIX))) | |
134 | 144 | then throw((("Only NFT " + LANDPREFIX) + " tokens are accepted")) | |
135 | 145 | else { | |
136 | 146 | let landNumSize = drop(asset.name, 4) | |
137 | 147 | let landNum = if (contains(landNumSize, "XXL")) | |
138 | 148 | then dropRight(landNumSize, 3) | |
139 | 149 | else if (contains(landNumSize, "XL")) | |
140 | 150 | then dropRight(landNumSize, 2) | |
141 | 151 | else dropRight(landNumSize, 1) | |
142 | 152 | if (!(isDefined(parseInt(landNum)))) | |
143 | 153 | then throw(("Cannot parse land number from " + asset.name)) | |
144 | 154 | else { | |
145 | 155 | let timeKey = keyStakedTimeByAssetId(toBase58String(assetId)) | |
146 | 156 | if (isDefined(getInteger(timeKey))) | |
147 | 157 | then throw((("NFT " + asset.name) + " is already staked")) | |
148 | 158 | else [IntegerEntry(timeKey, lastBlock.timestamp), IntegerEntry(keyStakedTimeByTypeAssetIdAndOwner(LANDPREFIX, toBase58String(assetId), address), lastBlock.timestamp), StringEntry(keyAssetIdToOwner(toBase58String(assetId)), address), StringEntry(keyLandToOwner(landNum), address)] | |
149 | 159 | } | |
150 | 160 | } | |
151 | 161 | } | |
152 | 162 | } | |
153 | 163 | ||
154 | 164 | ||
155 | 165 | ||
156 | 166 | @Callable(i) | |
157 | - | func updateStakedLand (assetIdStr,newTimestamp) = if ((i.caller != backEndAddr)) | |
158 | - | then throw("permission denied") | |
159 | - | else if ((size(i.payments) != 0)) | |
160 | - | then throw("updateStakedLand doesn't require any payments") | |
161 | - | else { | |
162 | - | let assetId = fromBase58String(assetIdStr) | |
163 | - | let asset = value(assetInfo(assetId)) | |
164 | - | if ((asset.issuer != this)) | |
165 | - | then throw("Unknown issuer of token") | |
166 | - | else if (!(contains(asset.name, LANDPREFIX))) | |
167 | - | then throw((("Only NFT " + LANDPREFIX) + " tokens are accepted")) | |
168 | - | else { | |
169 | - | let timeKey = keyStakedTimeByAssetId(toBase58String(assetId)) | |
170 | - | if (!(isDefined(getInteger(timeKey)))) | |
171 | - | then throw((("NFT " + asset.name) + " is not staked")) | |
172 | - | else { | |
173 | - | let ownerAddr = getStringValue(keyAssetIdToOwner(assetIdStr)) | |
174 | - | [IntegerEntry(timeKey, newTimestamp), IntegerEntry(keyStakedTimeByTypeAssetIdAndOwner(LANDPREFIX, assetIdStr, ownerAddr), newTimestamp)] | |
175 | - | } | |
176 | - | } | |
177 | - | } | |
178 | - | ||
179 | - | ||
180 | - | ||
181 | - | @Callable(i) | |
182 | - | func unstakeLand (assetIdStr) = if ((size(i.payments) != 0)) | |
167 | + | func unstakeLand (landAssetId) = if ((size(i.payments) != 0)) | |
183 | 168 | then throw("unstake doesn't require any payments") | |
184 | 169 | else { | |
185 | - | let assetId = fromBase58String( | |
170 | + | let assetId = fromBase58String(landAssetId) | |
186 | 171 | let address = toString(i.caller) | |
187 | 172 | let asset = value(assetInfo(assetId)) | |
188 | 173 | if ((asset.issuer != this)) | |
189 | 174 | then throw("Unknown issuer of token") | |
190 | 175 | else if (!(contains(asset.name, LANDPREFIX))) | |
191 | 176 | then throw((("Only NFT " + LANDPREFIX) + " tokens can be unstaked")) | |
192 | 177 | else { | |
193 | - | let timeKey = keyStakedTimeByAssetId( | |
178 | + | let timeKey = keyStakedTimeByAssetId(landAssetId) | |
194 | 179 | if (!(isDefined(timeKey))) | |
195 | 180 | then throw((("NFT " + asset.name) + " is not staked")) | |
196 | 181 | else { | |
197 | - | let owner = valueOrErrorMessage(getString(keyAssetIdToOwner( | |
182 | + | let owner = valueOrErrorMessage(getString(keyAssetIdToOwner(landAssetId)), (("NFT " + asset.name) + " is orphaned")) | |
198 | 183 | if ((owner != address)) | |
199 | 184 | then throw("Staked NFT is not yours") | |
200 | - | else [ScriptTransfer(i.caller, 1, assetId), DeleteEntry(timeKey), DeleteEntry(keyStakedTimeByTypeAssetIdAndOwner(LANDPREFIX, | |
185 | + | else [ScriptTransfer(i.caller, 1, assetId), DeleteEntry(timeKey), DeleteEntry(keyStakedTimeByTypeAssetIdAndOwner(LANDPREFIX, landAssetId, address))] | |
201 | 186 | } | |
202 | 187 | } | |
203 | 188 | } | |
204 | 189 | ||
205 | 190 | ||
206 | 191 | ||
207 | 192 | @Callable(i) | |
208 | 193 | func stakeDuck () = { | |
209 | 194 | let pmt = value(i.payments[0]) | |
210 | 195 | let assetId = value(pmt.assetId) | |
211 | 196 | let address = toString(i.caller) | |
212 | 197 | if ((pmt.amount != 1)) | |
213 | 198 | then throw((("NFT " + DUCKPREFIX) + " token should be attached as payment")) | |
214 | 199 | else { | |
215 | 200 | let asset = value(assetInfo(assetId)) | |
216 | 201 | if (if ((asset.issuer != incubatorAddr)) | |
217 | 202 | then (asset.issuer != breederAddr) | |
218 | 203 | else false) | |
219 | 204 | then throw((("Unknown issuer of " + DUCKPREFIX) + " token")) | |
220 | 205 | else if (!(contains(asset.name, DUCKPREFIX))) | |
221 | 206 | then throw((("Only NFT " + DUCKPREFIX) + " tokens are accepted")) | |
222 | 207 | else { | |
223 | 208 | let assetIdStr = toBase58String(assetId) | |
224 | 209 | let timeKey = keyStakedTimeByAssetId(assetIdStr) | |
225 | 210 | if (isDefined(getInteger(timeKey))) | |
226 | 211 | then throw((("NFT " + asset.name) + " is already staked")) | |
227 | 212 | else if (isDefined(getString(keyStakedDuckByOwner(address)))) | |
228 | 213 | then throw(("You already staked one duck: " + asset.name)) | |
229 | - | else [IntegerEntry(timeKey, lastBlock.timestamp), IntegerEntry(keyStakedTimeByTypeAssetIdAndOwner(DUCKPREFIX, toBase58String(assetId), address), lastBlock.timestamp), StringEntry(keyDuckIdToOwner(assetIdStr), address), StringEntry(keyStakedDuckByOwner(address), assetIdStr)] | |
214 | + | else { | |
215 | + | let locKey = keyDuckLocation(assetIdStr) | |
216 | + | let location = getString(locKey) | |
217 | + | let keyHealth = keyDuckHealth(assetIdStr) | |
218 | + | let health = getInteger(keyHealth) | |
219 | + | let bpKey = keyBackpackByDuck(assetIdStr) | |
220 | + | let backpack = getString(bpKey) | |
221 | + | ([IntegerEntry(timeKey, lastBlock.timestamp), IntegerEntry(keyStakedTimeByTypeAssetIdAndOwner(DUCKPREFIX, toBase58String(assetId), address), lastBlock.timestamp), StringEntry(keyDuckIdToOwner(assetIdStr), address), StringEntry(keyStakedDuckByOwner(address), assetIdStr)] ++ (if (isDefined(location)) | |
222 | + | then nil | |
223 | + | else ([StringEntry(locKey, DEFAULTLOCATION)] ++ (if (isDefined(health)) | |
224 | + | then nil | |
225 | + | else ([IntegerEntry(keyHealth, 100)] ++ (if (isDefined(backpack)) | |
226 | + | then nil | |
227 | + | else [StringEntry(bpKey, "0:0_0_0_0_0_0::")])))))) | |
228 | + | } | |
230 | 229 | } | |
231 | 230 | } | |
232 | 231 | } | |
233 | 232 | ||
234 | 233 | ||
235 | 234 | ||
236 | 235 | @Callable(i) | |
237 | 236 | func unstakeDuck (assetIdStr) = if ((size(i.payments) != 0)) | |
238 | 237 | then throw("unstake doesn't require any payments") | |
239 | 238 | else { | |
240 | 239 | let assetId = fromBase58String(assetIdStr) | |
241 | 240 | let address = toString(i.caller) | |
242 | 241 | let asset = value(assetInfo(assetId)) | |
243 | 242 | if (if ((asset.issuer != incubatorAddr)) | |
244 | 243 | then (asset.issuer != breederAddr) | |
245 | 244 | else false) | |
246 | 245 | then throw((("Unknown issuer of " + DUCKPREFIX) + " token")) | |
247 | 246 | else if (!(contains(asset.name, DUCKPREFIX))) | |
248 | 247 | then throw((("Only NFT " + DUCKPREFIX) + " tokens can be unstaked")) | |
249 | 248 | else { | |
250 | 249 | let timeKey = keyStakedTimeByAssetId(toBase58String(assetId)) | |
251 | 250 | if (!(isDefined(timeKey))) | |
252 | 251 | then throw((("NFT " + asset.name) + " is not staked")) | |
253 | 252 | else if (!(isDefined(keyStakedDuckByOwner(address)))) | |
254 | 253 | then throw((("The duck " + asset.name) + " is not staked")) | |
255 | 254 | else { | |
256 | 255 | let owner = valueOrErrorMessage(getString(keyDuckIdToOwner(toBase58String(assetId))), (("NFT " + asset.name) + " is orphaned")) | |
257 | 256 | if ((owner != address)) | |
258 | 257 | then throw("Staked NFT is not yours") | |
259 | - | else [ScriptTransfer(i.caller, 1, assetId), DeleteEntry(timeKey), DeleteEntry(keyStakedTimeByTypeAssetIdAndOwner(DUCKPREFIX, assetIdStr, address)), DeleteEntry(keyStakedDuckByOwner(address))] | |
258 | + | else [ScriptTransfer(i.caller, 1, assetId), DeleteEntry(timeKey), DeleteEntry(keyDuckLocation(assetIdStr)), DeleteEntry(keyStakedTimeByTypeAssetIdAndOwner(DUCKPREFIX, assetIdStr, address)), DeleteEntry(keyStakedDuckByOwner(address))] | |
260 | 259 | } | |
261 | 260 | } | |
262 | 261 | } | |
263 | 262 | ||
264 | 263 | ||
265 | 264 | ||
266 | 265 | @Callable(i) | |
267 | - | func buyLand (landSize) = if ((i.caller != this)) | |
268 | - | then throw("Permission denied") | |
269 | - | else if ((size(i.payments) != 1)) | |
270 | - | then throw("Exactly one payment required") | |
266 | + | func claimRes (amount,landAssetId) = if ((size(i.payments) != 0)) | |
267 | + | then throw("claimRes doesn't require any payments") | |
268 | + | else { | |
269 | + | let addr = toString(i.caller) | |
270 | + | let asset = value(assetInfo(fromBase58String(landAssetId))) | |
271 | + | if (!(contains(asset.name, LANDPREFIX))) | |
272 | + | then throw((("NFT " + LANDPREFIX) + " token should be passed as param")) | |
273 | + | else { | |
274 | + | let timeKey = keyStakedTimeByAssetId(landAssetId) | |
275 | + | let savedTime = getInteger(timeKey) | |
276 | + | if (!(isDefined(savedTime))) | |
277 | + | then throw((("NFT " + asset.name) + " is not staked")) | |
278 | + | else { | |
279 | + | let owner = getStringValue(keyAssetIdToOwner(landAssetId)) | |
280 | + | if ((owner != addr)) | |
281 | + | then throw((LANDPREFIX + " is not yours")) | |
282 | + | else { | |
283 | + | let d = split(asset.description, "_") | |
284 | + | let landSize = d[recLandSize] | |
285 | + | let terrainCounts = countTerrains(d[recTerrains]) | |
286 | + | let duck = getString(keyStakedDuckByOwner(addr)) | |
287 | + | if (!(isDefined(duck))) | |
288 | + | then throw("You don't have a duck staked") | |
289 | + | else { | |
290 | + | let duckAssetIdStr = value(duck) | |
291 | + | let curLocation = valueOrElse(getString(keyDuckLocation(duckAssetIdStr)), DEFAULTLOCATION) | |
292 | + | let loc = split(value(curLocation), "_") | |
293 | + | if ((loc[locIdxType] != "L")) | |
294 | + | then throw((("Duck location type is " + loc[locIdxType]) + ", but should be L")) | |
295 | + | else if ((loc[locIdxId] != landAssetId)) | |
296 | + | then throw(((("Duck location id is " + loc[locIdxId]) + ", but should be ") + landAssetId)) | |
297 | + | else { | |
298 | + | let deltaTime = (lastBlock.timestamp - value(savedTime)) | |
299 | + | if ((0 > deltaTime)) | |
300 | + | then throw(((("Saved timestamp is in future, saved = " + toString(value(savedTime))) + ", current = ") + toString(lastBlock.timestamp))) | |
301 | + | else { | |
302 | + | let pieces = numPiecesBySize(landSize) | |
303 | + | let availRes = (fraction(deltaTime, DAILYRESBYPIECE, DAYMILLIS) * pieces) | |
304 | + | if ((amount > availRes)) | |
305 | + | then throw(((("Not enough resources, available = " + toString(availRes)) + ", requested = ") + toString(amount))) | |
306 | + | else { | |
307 | + | let newDeltaTime = fraction((availRes - amount), DAYMILLIS, (pieces * DAILYRESBYPIECE)) | |
308 | + | let newTimestamp = (lastBlock.timestamp - newDeltaTime) | |
309 | + | let bpKey = keyBackpackByDuck(duckAssetIdStr) | |
310 | + | let currentPack = split(valueOrElse(getString(bpKey), "0:0_0_0_0_0_0::"), ":") | |
311 | + | let currentRes = split(currentPack[bpIdxRes], "_") | |
312 | + | let bpRes = addRes(currentRes, terrainCounts, (deltaTime - newDeltaTime)) | |
313 | + | let newPack = makeString([currentPack[bpIdxLevel], bpRes, currentPack[bpIdxMat], currentPack[bpIdxProd]], ":") | |
314 | + | $Tuple2([StringEntry(bpKey, newPack), IntegerEntry(timeKey, newTimestamp), IntegerEntry(keyStakedTimeByTypeAssetIdAndOwner(LANDPREFIX, landAssetId, owner), newTimestamp)], unit) | |
315 | + | } | |
316 | + | } | |
317 | + | } | |
318 | + | } | |
319 | + | } | |
320 | + | } | |
321 | + | } | |
322 | + | } | |
323 | + | ||
324 | + | ||
325 | + | ||
326 | + | @Callable(i) | |
327 | + | func flight (message,sig) = if (!(sigVerify_8Kb(message, sig, pub))) | |
328 | + | then throw("signature does not match") | |
329 | + | else if ((size(i.payments) != 0)) | |
330 | + | then throw("flight doesn't require any payments") | |
271 | 331 | else { | |
272 | - | let pmt = value(i.payments[0]) | |
273 | - | let actions = match landSize { | |
274 | - | case _ => | |
275 | - | if (("S" == $match0)) | |
276 | - | then buyInternal(pmt, i.transactionId, i.caller, landSize, LENS, MAPS, DEFAULTPRICES) | |
277 | - | else if (("M" == $match0)) | |
278 | - | then buyInternal(pmt, i.transactionId, i.caller, landSize, LENM, MAPM, DEFAULTPRICEM) | |
279 | - | else if (("L" == $match0)) | |
280 | - | then buyInternal(pmt, i.transactionId, i.caller, landSize, LENL, MAPL, DEFAULTPRICEL) | |
281 | - | else if (("XL" == $match0)) | |
282 | - | then buyInternal(pmt, i.transactionId, i.caller, landSize, LENXL, MAPXL, DEFAULTPRICEXL) | |
283 | - | else throw("Unknown land size") | |
332 | + | let parts = split(toUtf8String(message), ";") | |
333 | + | let hp = split(split(parts[0], "|")[0], "_") | |
334 | + | let curHP = parseIntValue(hp[0]) | |
335 | + | let newHP = parseIntValue(hp[1]) | |
336 | + | let newLocAndTime = split(parts[1], ":") | |
337 | + | let newLocation = newLocAndTime[0] | |
338 | + | let time = parseIntValue(newLocAndTime[1]) | |
339 | + | if (if ((time > (lastBlock.timestamp + FIVEMINUTESMILLIS))) | |
340 | + | then true | |
341 | + | else ((lastBlock.timestamp - FIVEMINUTESMILLIS) > time)) | |
342 | + | then throw("signature outdated") | |
343 | + | else { | |
344 | + | let duckAssetId = valueOrErrorMessage(getString(keyStakedDuckByOwner(toString(i.caller))), "You don't have a duck staked") | |
345 | + | let keyHealth = keyDuckHealth(duckAssetId) | |
346 | + | let oldFromState = valueOrElse(getInteger(keyHealth), 100) | |
347 | + | if ((oldFromState != curHP)) | |
348 | + | then throw(((("oldHealth=" + toString(valueOrElse(getInteger(keyHealth), 100))) + " from state does not match one from flight log=") + toString(curHP))) | |
349 | + | else if ((0 >= curHP)) | |
350 | + | then throw("You can't fly with zero health") | |
351 | + | else { | |
352 | + | let locKey = keyDuckLocation(duckAssetId) | |
353 | + | let curLocation = valueOrElse(getString(locKey), DEFAULTLOCATION) | |
354 | + | if ((newLocation == curLocation)) | |
355 | + | then throw("You can't fly to the same location") | |
356 | + | else $Tuple2([StringEntry(locKey, if ((newHP > 0)) | |
357 | + | then newLocation | |
358 | + | else curLocation), IntegerEntry(keyHealth, newHP)], unit) | |
359 | + | } | |
360 | + | } | |
284 | 361 | } | |
285 | - | $Tuple2(actions, unit) | |
286 | - | } | |
362 | + | ||
363 | + | ||
364 | + | ||
365 | + | @Callable(i) | |
366 | + | func setHealth (health,duckAssetId) = { | |
367 | + | let duckAssetIdStr = valueOrErrorMessage(getString(keyStakedDuckByOwner(toString(i.caller))), "You don't have a duck staked") | |
368 | + | if (if ((0 > health)) | |
369 | + | then true | |
370 | + | else (health > 100)) | |
371 | + | then throw("HP should be within 0..100") | |
372 | + | else [IntegerEntry(keyDuckHealth(duckAssetId), health)] | |
373 | + | } | |
374 | + | ||
375 | + | ||
376 | + | ||
377 | + | @Callable(i) | |
378 | + | func updateBackpack (duckAssetId,newPack) = if ((i.caller != economyAddr)) | |
379 | + | then throw("permission denied") | |
380 | + | else $Tuple2([StringEntry(keyBackpackByDuck(duckAssetId), newPack)], newPack) | |
287 | 381 | ||
288 | 382 |
github/deemru/w8io/0e76f2f 191.61 ms ◑![]()