tx · 5cHQ5gvQW2PwdcQThd6VuHPW3KjzDKUsERx8Godvi4b4 3N8y4wxX3JC4TdrCJBXX16SjWf6X256hrep: -0.01100000 Waves 2022.12.06 14:57 [2348452] smart account 3N8y4wxX3JC4TdrCJBXX16SjWf6X256hrep > SELF 0.00000000 Waves
{ "type": 13, "id": "5cHQ5gvQW2PwdcQThd6VuHPW3KjzDKUsERx8Godvi4b4", "fee": 1100000, "feeAssetId": null, "timestamp": 1670327876236, "version": 2, "chainId": 84, "sender": "3N8y4wxX3JC4TdrCJBXX16SjWf6X256hrep", "senderPublicKey": "7v5L7QkXxfkirALdyqmox38QCsa9jtfAtgUfHTh34eWq", "proofs": [ "4y81ffZHmoYwSJAS3xm8fjYPCsZnZfW15Ea1pdLFJFBqhpi2zdUbKeHmoAKGVGHv6mPxqCfZ6kiitXNHKZozaQfu" ], "script": "base64:BgIoCAISABIDCgEIEgASAwoBCBIECgIBCBIECgICAhIECgIBCBIECgIICCkAC3VzZG5Bc3NldElkASD3dur394PKZdtuE+4CO89YKZWpwdGN8kvabNgdYoDI3gANaW5jdWJhdG9yQWRkcgUEdGhpcwALYnJlZWRlckFkZHIFBHRoaXMAC2JhY2tFbmRBZGRyCQERQGV4dHJOYXRpdmUoMTA2MikBAiMzTjVTcFgyMVIzUjc1UW80ZWIzTXdGRnZXN1RVenlodmF2dgALZWNvbm9teUFkZHIJARFAZXh0ck5hdGl2ZSgxMDYyKQECIzNOOHk0d3hYM0pDNFRkckNKQlhYMTZTaldmNlgyNTZocmVwAANwdWIBIE9T4ho/VKjWnVJOEx1GJ1W9s1PzLHvJVFSw/0PBtBQoAApMQU5EUFJFRklYAgRMQU5EAApEVUNLUFJFRklYAgREVUNLAA9ERUZBVUxUTE9DQVRJT04CD0FmcmljYV9GX0FmcmljYQAPREFJTFlSRVNCWVBJRUNFAID40gEACURBWU1JTExJUwCAuJkpABFGSVZFTUlOVVRFU01JTExJUwDgpxIBEWtleUFzc2V0SWRUb093bmVyAQdhc3NldElkCQCsAgICCW5mdE93bmVyXwUHYXNzZXRJZAEQa2V5RHVja0lkVG9Pd25lcgEHYXNzZXRJZAkArAICAgpkdWNrT3duZXJfBQdhc3NldElkARZrZXlTdGFrZWRUaW1lQnlBc3NldElkAQdhc3NldElkCQCsAgICC3N0YWtlZFRpbWVfBQdhc3NldElkARRrZXlTdGFrZWREdWNrQnlPd25lcgEJb3duZXJBZGRyCQCsAgICEnN0YWtlZER1Y2tCeU93bmVyXwUJb3duZXJBZGRyASJrZXlTdGFrZWRUaW1lQnlUeXBlQXNzZXRJZEFuZE93bmVyAwduZnRUeXBlB2Fzc2V0SWQJb3duZXJBZGRyCQCsAgIJAKwCAgkArAICCQCsAgIJAKwCAgIgc3Rha2VkVGltZUJ5VHlwZUFzc2V0SWRBbmRPd25lcl8FB25mdFR5cGUCAV8FB2Fzc2V0SWQCAV8FCW93bmVyQWRkcgEOa2V5TGFuZFRvT3duZXIBB2xhbmROdW0JAKwCAgIKbGFuZE93bmVyXwUHbGFuZE51bQERa2V5QmFja3BhY2tCeUR1Y2sBC2R1Y2tBc3NldElkCQCsAgICCWJhY2tQYWNrXwULZHVja0Fzc2V0SWQBD2tleUR1Y2tMb2NhdGlvbgELZHVja0Fzc2V0SWQJAKwCAgINZHVja0xvY2F0aW9uXwULZHVja0Fzc2V0SWQBDWtleUR1Y2tIZWFsdGgBC2R1Y2tBc3NldElkCQCsAgICC2R1Y2tIZWFsdGhfBQtkdWNrQXNzZXRJZAAKcmVjTGFuZE51bQAAAAtyZWNMYW5kU2l6ZQABAAtyZWNUZXJyYWlucwACAAxyZWNDb250aW5lbnQAAwAPbG9jSWR4Q29udGluZW50AAAACmxvY0lkeFR5cGUAAQAIbG9jSWR4SWQAAgAKYnBJZHhMZXZlbAAAAAhicElkeFJlcwABAAhicElkeE1hdAACAAlicElkeFByb2QAAwAEaWR4QQAAAARpZHhCAAEABGlkeEMAAgAEaWR4RAADAARpZHhFAAQABGlkeEYABQENY291bnRUZXJyYWlucwEIdGVycmFpbnMJAMwIAgkAZQIJAJADAQkAtQkCBQh0ZXJyYWlucwIBQQABCQDMCAIJAGUCCQCQAwEJALUJAgUIdGVycmFpbnMCAUIAAQkAzAgCCQBlAgkAkAMBCQC1CQIFCHRlcnJhaW5zAgFDAAEJAMwIAgkAZQIJAJADAQkAtQkCBQh0ZXJyYWlucwIBRAABCQDMCAIJAGUCCQCQAwEJALUJAgUIdGVycmFpbnMCAUUAAQkAzAgCCQBlAgkAkAMBCQC1CQIFCHRlcnJhaW5zAgFGAAEFA25pbAEPbnVtUGllY2VzQnlTaXplAQhsYW5kU2l6ZQQHJG1hdGNoMAUIbGFuZFNpemUDCQAAAgIBUwUHJG1hdGNoMAAZAwkAAAICAU0FByRtYXRjaDAAZAMJAAACAgFMBQckbWF0Y2gwAOEBAwkAAAICAlhMBQckbWF0Y2gwAJADAwkAAAICA1hYTAUHJG1hdGNoMADxBAkAAgECEVVua25vd24gbGFuZCBzaXplAQZhZGRSZXMDCmN1cnJlbnRSZXMNdGVycmFpbkNvdW50cwlkZWx0YVRpbWUKAQVhZGRlcgIDYWNjAWkECXJlc09mVHlwZQkAaAIJAGsDBQlkZWx0YVRpbWUFD0RBSUxZUkVTQllQSUVDRQUJREFZTUlMTElTCQCRAwIFDXRlcnJhaW5Db3VudHMFAWkJAM0IAgUDYWNjCQCkAwEJAGQCCQENcGFyc2VJbnRWYWx1ZQEJAJEDAgUKY3VycmVudFJlcwUBaQUJcmVzT2ZUeXBlBAFyCgACJGwJAMwIAgAACQDMCAIAAQkAzAgCAAIJAMwIAgADCQDMCAIABAkAzAgCAAUFA25pbAoAAiRzCQCQAwEFAiRsCgAFJGFjYzAFA25pbAoBBSRmMF8xAgIkYQIkaQMJAGcCBQIkaQUCJHMFAiRhCQEFYWRkZXICBQIkYQkAkQMCBQIkbAUCJGkKAQUkZjBfMgICJGECJGkDCQBnAgUCJGkFAiRzBQIkYQkAAgECE0xpc3Qgc2l6ZSBleGNlZWRzIDYJAQUkZjBfMgIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIFBSRhY2MwAAAAAQACAAMABAAFAAYJALkJAgUBcgIBXwgBaQEJc3Rha2VMYW5kAAQDcG10CQEFdmFsdWUBCQCRAwIIBQFpCHBheW1lbnRzAAAEB2Fzc2V0SWQJAQV2YWx1ZQEIBQNwbXQHYXNzZXRJZAQHYWRkcmVzcwkApQgBCAUBaQZjYWxsZXIDCQECIT0CCAUDcG10BmFtb3VudAABCQACAQkArAICCQCsAgICBE5GVCAFCkxBTkRQUkVGSVgCJCB0b2tlbiBzaG91bGQgYmUgYXR0YWNoZWQgYXMgcGF5bWVudAQFYXNzZXQJAQV2YWx1ZQEJAOwHAQUHYXNzZXRJZAMJAQIhPQIIBQVhc3NldAZpc3N1ZXIFBHRoaXMJAAIBAhdVbmtub3duIGlzc3VlciBvZiB0b2tlbgMJAQEhAQkBCGNvbnRhaW5zAggFBWFzc2V0BG5hbWUFCkxBTkRQUkVGSVgJAAIBCQCsAgIJAKwCAgIJT25seSBORlQgBQpMQU5EUFJFRklYAhQgdG9rZW5zIGFyZSBhY2NlcHRlZAQLbGFuZE51bVNpemUJALACAggFBWFzc2V0BG5hbWUABAQHbGFuZE51bQMJAQhjb250YWlucwIFC2xhbmROdW1TaXplAgNYWEwJALMCAgULbGFuZE51bVNpemUAAwMJAQhjb250YWlucwIFC2xhbmROdW1TaXplAgJYTAkAswICBQtsYW5kTnVtU2l6ZQACCQCzAgIFC2xhbmROdW1TaXplAAEDCQEBIQEJAQlpc0RlZmluZWQBCQC2CQEFB2xhbmROdW0JAAIBCQCsAgICHkNhbm5vdCBwYXJzZSBsYW5kIG51bWJlciBmcm9tIAgFBWFzc2V0BG5hbWUEB3RpbWVLZXkJARZrZXlTdGFrZWRUaW1lQnlBc3NldElkAQkA2AQBBQdhc3NldElkAwkBCWlzRGVmaW5lZAEJAJ8IAQUHdGltZUtleQkAAgEJAKwCAgkArAICAgRORlQgCAUFYXNzZXQEbmFtZQISIGlzIGFscmVhZHkgc3Rha2VkCQDMCAIJAQxJbnRlZ2VyRW50cnkCBQd0aW1lS2V5CAUJbGFzdEJsb2NrCXRpbWVzdGFtcAkAzAgCCQEMSW50ZWdlckVudHJ5AgkBImtleVN0YWtlZFRpbWVCeVR5cGVBc3NldElkQW5kT3duZXIDBQpMQU5EUFJFRklYCQDYBAEFB2Fzc2V0SWQFB2FkZHJlc3MIBQlsYXN0QmxvY2sJdGltZXN0YW1wCQDMCAIJAQtTdHJpbmdFbnRyeQIJARFrZXlBc3NldElkVG9Pd25lcgEJANgEAQUHYXNzZXRJZAUHYWRkcmVzcwkAzAgCCQELU3RyaW5nRW50cnkCCQEOa2V5TGFuZFRvT3duZXIBBQdsYW5kTnVtBQdhZGRyZXNzBQNuaWwBaQELdW5zdGFrZUxhbmQBC2xhbmRBc3NldElkAwkBAiE9AgkAkAMBCAUBaQhwYXltZW50cwAACQACAQIkdW5zdGFrZSBkb2Vzbid0IHJlcXVpcmUgYW55IHBheW1lbnRzBAdhc3NldElkCQDZBAEFC2xhbmRBc3NldElkBAdhZGRyZXNzCQClCAEIBQFpBmNhbGxlcgQFYXNzZXQJAQV2YWx1ZQEJAOwHAQUHYXNzZXRJZAMJAQIhPQIIBQVhc3NldAZpc3N1ZXIFBHRoaXMJAAIBAhdVbmtub3duIGlzc3VlciBvZiB0b2tlbgMJAQEhAQkBCGNvbnRhaW5zAggFBWFzc2V0BG5hbWUFCkxBTkRQUkVGSVgJAAIBCQCsAgIJAKwCAgIJT25seSBORlQgBQpMQU5EUFJFRklYAhcgdG9rZW5zIGNhbiBiZSB1bnN0YWtlZAQHdGltZUtleQkBFmtleVN0YWtlZFRpbWVCeUFzc2V0SWQBBQtsYW5kQXNzZXRJZAMJAQEhAQkBCWlzRGVmaW5lZAEFB3RpbWVLZXkJAAIBCQCsAgIJAKwCAgIETkZUIAgFBWFzc2V0BG5hbWUCDiBpcyBub3Qgc3Rha2VkBAVvd25lcgkBE3ZhbHVlT3JFcnJvck1lc3NhZ2UCCQCiCAEJARFrZXlBc3NldElkVG9Pd25lcgEFC2xhbmRBc3NldElkCQCsAgIJAKwCAgIETkZUIAgFBWFzc2V0BG5hbWUCDCBpcyBvcnBoYW5lZAMJAQIhPQIFBW93bmVyBQdhZGRyZXNzCQACAQIXU3Rha2VkIE5GVCBpcyBub3QgeW91cnMJAMwIAgkBDlNjcmlwdFRyYW5zZmVyAwgFAWkGY2FsbGVyAAEFB2Fzc2V0SWQJAMwIAgkBC0RlbGV0ZUVudHJ5AQUHdGltZUtleQkAzAgCCQELRGVsZXRlRW50cnkBCQEia2V5U3Rha2VkVGltZUJ5VHlwZUFzc2V0SWRBbmRPd25lcgMFCkxBTkRQUkVGSVgFC2xhbmRBc3NldElkBQdhZGRyZXNzBQNuaWwBaQEJc3Rha2VEdWNrAAQDcG10CQEFdmFsdWUBCQCRAwIIBQFpCHBheW1lbnRzAAAEB2Fzc2V0SWQJAQV2YWx1ZQEIBQNwbXQHYXNzZXRJZAQHYWRkcmVzcwkApQgBCAUBaQZjYWxsZXIDCQECIT0CCAUDcG10BmFtb3VudAABCQACAQkArAICCQCsAgICBE5GVCAFCkRVQ0tQUkVGSVgCJCB0b2tlbiBzaG91bGQgYmUgYXR0YWNoZWQgYXMgcGF5bWVudAQFYXNzZXQJAQV2YWx1ZQEJAOwHAQUHYXNzZXRJZAMDCQECIT0CCAUFYXNzZXQGaXNzdWVyBQ1pbmN1YmF0b3JBZGRyCQECIT0CCAUFYXNzZXQGaXNzdWVyBQticmVlZGVyQWRkcgcJAAIBCQCsAgIJAKwCAgISVW5rbm93biBpc3N1ZXIgb2YgBQpEVUNLUFJFRklYAgYgdG9rZW4DCQEBIQEJAQhjb250YWlucwIIBQVhc3NldARuYW1lBQpEVUNLUFJFRklYCQACAQkArAICCQCsAgICCU9ubHkgTkZUIAUKRFVDS1BSRUZJWAIUIHRva2VucyBhcmUgYWNjZXB0ZWQECmFzc2V0SWRTdHIJANgEAQUHYXNzZXRJZAQHdGltZUtleQkBFmtleVN0YWtlZFRpbWVCeUFzc2V0SWQBBQphc3NldElkU3RyAwkBCWlzRGVmaW5lZAEJAJ8IAQUHdGltZUtleQkAAgEJAKwCAgkArAICAgRORlQgCAUFYXNzZXQEbmFtZQISIGlzIGFscmVhZHkgc3Rha2VkAwkBCWlzRGVmaW5lZAEJAKIIAQkBFGtleVN0YWtlZER1Y2tCeU93bmVyAQUHYWRkcmVzcwkAAgEJAKwCAgIdWW91IGFscmVhZHkgc3Rha2VkIG9uZSBkdWNrOiAIBQVhc3NldARuYW1lBAZsb2NLZXkJAQ9rZXlEdWNrTG9jYXRpb24BBQphc3NldElkU3RyBAhsb2NhdGlvbgkAoggBBQZsb2NLZXkECWtleUhlYWx0aAkBDWtleUR1Y2tIZWFsdGgBBQphc3NldElkU3RyBAZoZWFsdGgJAJ8IAQUJa2V5SGVhbHRoBAVicEtleQkBEWtleUJhY2twYWNrQnlEdWNrAQUKYXNzZXRJZFN0cgQIYmFja3BhY2sJAKIIAQUFYnBLZXkJAM4IAgkAzAgCCQEMSW50ZWdlckVudHJ5AgUHdGltZUtleQgFCWxhc3RCbG9jawl0aW1lc3RhbXAJAMwIAgkBDEludGVnZXJFbnRyeQIJASJrZXlTdGFrZWRUaW1lQnlUeXBlQXNzZXRJZEFuZE93bmVyAwUKRFVDS1BSRUZJWAkA2AQBBQdhc3NldElkBQdhZGRyZXNzCAUJbGFzdEJsb2NrCXRpbWVzdGFtcAkAzAgCCQELU3RyaW5nRW50cnkCCQEQa2V5RHVja0lkVG9Pd25lcgEFCmFzc2V0SWRTdHIFB2FkZHJlc3MJAMwIAgkBC1N0cmluZ0VudHJ5AgkBFGtleVN0YWtlZER1Y2tCeU93bmVyAQUHYWRkcmVzcwUKYXNzZXRJZFN0cgUDbmlsAwkBCWlzRGVmaW5lZAEFCGxvY2F0aW9uBQNuaWwJAM4IAgkAzAgCCQELU3RyaW5nRW50cnkCBQZsb2NLZXkFD0RFRkFVTFRMT0NBVElPTgUDbmlsAwkBCWlzRGVmaW5lZAEFBmhlYWx0aAUDbmlsCQDOCAIJAMwIAgkBDEludGVnZXJFbnRyeQIFCWtleUhlYWx0aABkBQNuaWwDCQEJaXNEZWZpbmVkAQUIYmFja3BhY2sFA25pbAkAzAgCCQELU3RyaW5nRW50cnkCBQVicEtleQIPMDowXzBfMF8wXzBfMDo6BQNuaWwBaQELdW5zdGFrZUR1Y2sBCmFzc2V0SWRTdHIDCQECIT0CCQCQAwEIBQFpCHBheW1lbnRzAAAJAAIBAiR1bnN0YWtlIGRvZXNuJ3QgcmVxdWlyZSBhbnkgcGF5bWVudHMEB2Fzc2V0SWQJANkEAQUKYXNzZXRJZFN0cgQHYWRkcmVzcwkApQgBCAUBaQZjYWxsZXIEBWFzc2V0CQEFdmFsdWUBCQDsBwEFB2Fzc2V0SWQDAwkBAiE9AggFBWFzc2V0Bmlzc3VlcgUNaW5jdWJhdG9yQWRkcgkBAiE9AggFBWFzc2V0Bmlzc3VlcgULYnJlZWRlckFkZHIHCQACAQkArAICCQCsAgICElVua25vd24gaXNzdWVyIG9mIAUKRFVDS1BSRUZJWAIGIHRva2VuAwkBASEBCQEIY29udGFpbnMCCAUFYXNzZXQEbmFtZQUKRFVDS1BSRUZJWAkAAgEJAKwCAgkArAICAglPbmx5IE5GVCAFCkRVQ0tQUkVGSVgCFyB0b2tlbnMgY2FuIGJlIHVuc3Rha2VkBAd0aW1lS2V5CQEWa2V5U3Rha2VkVGltZUJ5QXNzZXRJZAEJANgEAQUHYXNzZXRJZAMJAQEhAQkBCWlzRGVmaW5lZAEFB3RpbWVLZXkJAAIBCQCsAgIJAKwCAgIETkZUIAgFBWFzc2V0BG5hbWUCDiBpcyBub3Qgc3Rha2VkAwkBASEBCQEJaXNEZWZpbmVkAQkBFGtleVN0YWtlZER1Y2tCeU93bmVyAQUHYWRkcmVzcwkAAgEJAKwCAgkArAICAglUaGUgZHVjayAIBQVhc3NldARuYW1lAg4gaXMgbm90IHN0YWtlZAQFb3duZXIJARN2YWx1ZU9yRXJyb3JNZXNzYWdlAgkAoggBCQEQa2V5RHVja0lkVG9Pd25lcgEJANgEAQUHYXNzZXRJZAkArAICCQCsAgICBE5GVCAIBQVhc3NldARuYW1lAgwgaXMgb3JwaGFuZWQDCQECIT0CBQVvd25lcgUHYWRkcmVzcwkAAgECF1N0YWtlZCBORlQgaXMgbm90IHlvdXJzCQDMCAIJAQ5TY3JpcHRUcmFuc2ZlcgMIBQFpBmNhbGxlcgABBQdhc3NldElkCQDMCAIJAQtEZWxldGVFbnRyeQEFB3RpbWVLZXkJAMwIAgkBC0RlbGV0ZUVudHJ5AQkBD2tleUR1Y2tMb2NhdGlvbgEFCmFzc2V0SWRTdHIJAMwIAgkBC0RlbGV0ZUVudHJ5AQkBImtleVN0YWtlZFRpbWVCeVR5cGVBc3NldElkQW5kT3duZXIDBQpEVUNLUFJFRklYBQphc3NldElkU3RyBQdhZGRyZXNzCQDMCAIJAQtEZWxldGVFbnRyeQEJARRrZXlTdGFrZWREdWNrQnlPd25lcgEFB2FkZHJlc3MFA25pbAFpAQhjbGFpbVJlcwIGYW1vdW50C2xhbmRBc3NldElkAwkBAiE9AgkAkAMBCAUBaQhwYXltZW50cwAACQACAQIlY2xhaW1SZXMgZG9lc24ndCByZXF1aXJlIGFueSBwYXltZW50cwQEYWRkcgkApQgBCAUBaQZjYWxsZXIEBWFzc2V0CQEFdmFsdWUBCQDsBwEJANkEAQULbGFuZEFzc2V0SWQDCQEBIQEJAQhjb250YWlucwIIBQVhc3NldARuYW1lBQpMQU5EUFJFRklYCQACAQkArAICCQCsAgICBE5GVCAFCkxBTkRQUkVGSVgCICB0b2tlbiBzaG91bGQgYmUgcGFzc2VkIGFzIHBhcmFtBAd0aW1lS2V5CQEWa2V5U3Rha2VkVGltZUJ5QXNzZXRJZAEFC2xhbmRBc3NldElkBAlzYXZlZFRpbWUJAJ8IAQUHdGltZUtleQMJAQEhAQkBCWlzRGVmaW5lZAEFCXNhdmVkVGltZQkAAgEJAKwCAgkArAICAgRORlQgCAUFYXNzZXQEbmFtZQIOIGlzIG5vdCBzdGFrZWQEBW93bmVyCQERQGV4dHJOYXRpdmUoMTA1OCkBCQERa2V5QXNzZXRJZFRvT3duZXIBBQtsYW5kQXNzZXRJZAMJAQIhPQIFBW93bmVyBQRhZGRyCQACAQkArAICBQpMQU5EUFJFRklYAg0gaXMgbm90IHlvdXJzBAFkCQC1CQIIBQVhc3NldAtkZXNjcmlwdGlvbgIBXwQIbGFuZFNpemUJAJEDAgUBZAULcmVjTGFuZFNpemUEDXRlcnJhaW5Db3VudHMJAQ1jb3VudFRlcnJhaW5zAQkAkQMCBQFkBQtyZWNUZXJyYWlucwQEZHVjawkAoggBCQEUa2V5U3Rha2VkRHVja0J5T3duZXIBBQRhZGRyAwkBASEBCQEJaXNEZWZpbmVkAQUEZHVjawkAAgECHFlvdSBkb24ndCBoYXZlIGEgZHVjayBzdGFrZWQEDmR1Y2tBc3NldElkU3RyCQEFdmFsdWUBBQRkdWNrBAtjdXJMb2NhdGlvbgkBC3ZhbHVlT3JFbHNlAgkAoggBCQEPa2V5RHVja0xvY2F0aW9uAQUOZHVja0Fzc2V0SWRTdHIFD0RFRkFVTFRMT0NBVElPTgQDbG9jCQC1CQIJAQV2YWx1ZQEFC2N1ckxvY2F0aW9uAgFfAwkBAiE9AgkAkQMCBQNsb2MFCmxvY0lkeFR5cGUCAUwJAAIBCQCsAgIJAKwCAgIWRHVjayBsb2NhdGlvbiB0eXBlIGlzIAkAkQMCBQNsb2MFCmxvY0lkeFR5cGUCESwgYnV0IHNob3VsZCBiZSBMAwkBAiE9AgkAkQMCBQNsb2MFCGxvY0lkeElkBQtsYW5kQXNzZXRJZAkAAgEJAKwCAgkArAICCQCsAgICFER1Y2sgbG9jYXRpb24gaWQgaXMgCQCRAwIFA2xvYwUIbG9jSWR4SWQCECwgYnV0IHNob3VsZCBiZSAFC2xhbmRBc3NldElkBAlkZWx0YVRpbWUJAGUCCAUJbGFzdEJsb2NrCXRpbWVzdGFtcAkBBXZhbHVlAQUJc2F2ZWRUaW1lAwkAZgIAAAUJZGVsdGFUaW1lCQACAQkArAICCQCsAgIJAKwCAgImU2F2ZWQgdGltZXN0YW1wIGlzIGluIGZ1dHVyZSwgc2F2ZWQgPSAJAKQDAQkBBXZhbHVlAQUJc2F2ZWRUaW1lAgwsIGN1cnJlbnQgPSAJAKQDAQgFCWxhc3RCbG9jawl0aW1lc3RhbXAEBnBpZWNlcwkBD251bVBpZWNlc0J5U2l6ZQEFCGxhbmRTaXplBAhhdmFpbFJlcwkAaAIJAGsDBQlkZWx0YVRpbWUFD0RBSUxZUkVTQllQSUVDRQUJREFZTUlMTElTBQZwaWVjZXMDCQBmAgUGYW1vdW50BQhhdmFpbFJlcwkAAgEJAKwCAgkArAICCQCsAgICIk5vdCBlbm91Z2ggcmVzb3VyY2VzLCBhdmFpbGFibGUgPSAJAKQDAQUIYXZhaWxSZXMCDiwgcmVxdWVzdGVkID0gCQCkAwEFBmFtb3VudAQMbmV3RGVsdGFUaW1lCQBrAwkAZQIFCGF2YWlsUmVzBQZhbW91bnQFCURBWU1JTExJUwkAaAIFBnBpZWNlcwUPREFJTFlSRVNCWVBJRUNFBAxuZXdUaW1lc3RhbXAJAGUCCAUJbGFzdEJsb2NrCXRpbWVzdGFtcAUMbmV3RGVsdGFUaW1lBAVicEtleQkBEWtleUJhY2twYWNrQnlEdWNrAQUOZHVja0Fzc2V0SWRTdHIEC2N1cnJlbnRQYWNrCQC1CQIJAQt2YWx1ZU9yRWxzZQIJAKIIAQUFYnBLZXkCDzA6MF8wXzBfMF8wXzA6OgIBOgQKY3VycmVudFJlcwkAtQkCCQCRAwIFC2N1cnJlbnRQYWNrBQhicElkeFJlcwIBXwQFYnBSZXMJAQZhZGRSZXMDBQpjdXJyZW50UmVzBQ10ZXJyYWluQ291bnRzCQBlAgUJZGVsdGFUaW1lBQxuZXdEZWx0YVRpbWUEB25ld1BhY2sJALkJAgkAzAgCCQCRAwIFC2N1cnJlbnRQYWNrBQpicElkeExldmVsCQDMCAIFBWJwUmVzCQDMCAIJAJEDAgULY3VycmVudFBhY2sFCGJwSWR4TWF0CQDMCAIJAJEDAgULY3VycmVudFBhY2sFCWJwSWR4UHJvZAUDbmlsAgE6CQCUCgIJAMwIAgkBC1N0cmluZ0VudHJ5AgUFYnBLZXkFB25ld1BhY2sJAMwIAgkBDEludGVnZXJFbnRyeQIFB3RpbWVLZXkFDG5ld1RpbWVzdGFtcAkAzAgCCQEMSW50ZWdlckVudHJ5AgkBImtleVN0YWtlZFRpbWVCeVR5cGVBc3NldElkQW5kT3duZXIDBQpMQU5EUFJFRklYBQtsYW5kQXNzZXRJZAUFb3duZXIFDG5ld1RpbWVzdGFtcAUDbmlsBQR1bml0AWkBBmZsaWdodAIHbWVzc2FnZQNzaWcDCQEBIQEJAMQTAwUHbWVzc2FnZQUDc2lnBQNwdWIJAAIBAhhzaWduYXR1cmUgZG9lcyBub3QgbWF0Y2gDCQECIT0CCQCQAwEIBQFpCHBheW1lbnRzAAAJAAIBAiNmbGlnaHQgZG9lc24ndCByZXF1aXJlIGFueSBwYXltZW50cwQFcGFydHMJALUJAgkAsAkBBQdtZXNzYWdlAgE7BAJocAkAtQkCCQCRAwIJALUJAgkAkQMCBQVwYXJ0cwAAAgF8AAACAV8EBWN1ckhQCQENcGFyc2VJbnRWYWx1ZQEJAJEDAgUCaHAAAQQFbmV3SFAJAQ1wYXJzZUludFZhbHVlAQkAkQMCBQJocAABBA1uZXdMb2NBbmRUaW1lCQC1CQIJAJEDAgUFcGFydHMAAQIBOgQLbmV3TG9jYXRpb24JAJEDAgUNbmV3TG9jQW5kVGltZQAABAR0aW1lCQENcGFyc2VJbnRWYWx1ZQEJAJEDAgUNbmV3TG9jQW5kVGltZQABAwMJAGYCBQR0aW1lCAUJbGFzdEJsb2NrCXRpbWVzdGFtcAYJAGYCCQBlAggFCWxhc3RCbG9jawl0aW1lc3RhbXAFEUZJVkVNSU5VVEVTTUlMTElTBQR0aW1lCQACAQISc2lnbmF0dXJlIG91dGRhdGVkBAtkdWNrQXNzZXRJZAkBE3ZhbHVlT3JFcnJvck1lc3NhZ2UCCQCiCAEJARRrZXlTdGFrZWREdWNrQnlPd25lcgEJAKUIAQgFAWkGY2FsbGVyAhxZb3UgZG9uJ3QgaGF2ZSBhIGR1Y2sgc3Rha2VkBAlrZXlIZWFsdGgJAQ1rZXlEdWNrSGVhbHRoAQULZHVja0Fzc2V0SWQEDG9sZEZyb21TdGF0ZQkBC3ZhbHVlT3JFbHNlAgkAnwgBBQlrZXlIZWFsdGgAZAMJAQIhPQIFDG9sZEZyb21TdGF0ZQUFY3VySFAJAAIBCQCsAgIJAKwCAgkArAICAgpvbGRIZWFsdGg9CQCkAwEJAQt2YWx1ZU9yRWxzZQIJAJ8IAQUJa2V5SGVhbHRoAGQCLyBmcm9tIHN0YXRlIGRvZXMgbm90IG1hdGNoIG9uZSBmcm9tIGZsaWdodCBsb2c9CQCkAwEFBWN1ckhQBAZsb2NLZXkJAQ9rZXlEdWNrTG9jYXRpb24BBQtkdWNrQXNzZXRJZAQLY3VyTG9jYXRpb24JAQt2YWx1ZU9yRWxzZQIJAKIIAQUGbG9jS2V5BQ9ERUZBVUxUTE9DQVRJT04DCQAAAgULbmV3TG9jYXRpb24FC2N1ckxvY2F0aW9uCQACAQIiWW91IGNhbid0IGZseSB0byB0aGUgc2FtZSBsb2NhdGlvbgkAlAoCCQDMCAIJAQtTdHJpbmdFbnRyeQIFBmxvY0tleQMJAGYCBQVuZXdIUAAABQtuZXdMb2NhdGlvbgULY3VyTG9jYXRpb24JAMwIAgkBDEludGVnZXJFbnRyeQIFCWtleUhlYWx0aAUFbmV3SFAFA25pbAUEdW5pdAFpAQlzZXRIZWFsdGgCBmhlYWx0aAtkdWNrQXNzZXRJZAQOZHVja0Fzc2V0SWRTdHIJARN2YWx1ZU9yRXJyb3JNZXNzYWdlAgkAoggBCQEUa2V5U3Rha2VkRHVja0J5T3duZXIBCQClCAEIBQFpBmNhbGxlcgIcWW91IGRvbid0IGhhdmUgYSBkdWNrIHN0YWtlZAMDCQBmAgAABQZoZWFsdGgGCQBmAgUGaGVhbHRoAGQJAAIBAhpIUCBzaG91bGQgYmUgd2l0aGluIDAuLjEwMAkAzAgCCQEMSW50ZWdlckVudHJ5AgkBDWtleUR1Y2tIZWFsdGgBBQtkdWNrQXNzZXRJZAUGaGVhbHRoBQNuaWwBaQEOdXBkYXRlQmFja3BhY2sCC2R1Y2tBc3NldElkB25ld1BhY2sDCQECIT0CCAUBaQZjYWxsZXIFC2Vjb25vbXlBZGRyCQACAQIRcGVybWlzc2lvbiBkZW5pZWQJAJQKAgkAzAgCCQELU3RyaW5nRW50cnkCCQERa2V5QmFja3BhY2tCeUR1Y2sBBQtkdWNrQXNzZXRJZAUHbmV3UGFjawUDbmlsBQduZXdQYWNrALPSIhU=", "height": 2348452, "applicationStatus": "succeeded", "spentComplexity": 0 } View: original | compacted Prev: 7N2EXuiBVFiZofNKHDCSv4C4rwMCm9toECHF4eYEZgiZ Next: Et3s8ZDJ4U9GWbFSwaeomM5N4PJeGvvhmB7wLvbJs2D Diff:
Old | New | Differences | |
---|---|---|---|
1 | 1 | {-# STDLIB_VERSION 6 #-} | |
2 | 2 | {-# SCRIPT_TYPE ACCOUNT #-} | |
3 | 3 | {-# CONTENT_TYPE DAPP #-} | |
4 | - | let | |
4 | + | let usdnAssetId = base58'HezsdQuRDtzksAYUy97gfhKy7Z1NW2uXYSHA3bgqenNZ' | |
5 | 5 | ||
6 | - | let | |
6 | + | let incubatorAddr = this | |
7 | 7 | ||
8 | - | let | |
8 | + | let breederAddr = this | |
9 | 9 | ||
10 | 10 | let backEndAddr = addressFromStringValue("3N5SpX21R3R75Qo4eb3MwFFvW7TUzyhvavv") | |
11 | 11 | ||
12 | - | let stakingContract = addressFromStringValue("3NDCyBG5q85JuaRiigUeEtainyjCQT3XpZm") | |
12 | + | let economyAddr = addressFromStringValue("3N8y4wxX3JC4TdrCJBXX16SjWf6X256hrep") | |
13 | + | ||
14 | + | let pub = base58'6LfPuKJjLgekmncBhMg2LZyMTNVzZBccXR28ySXm9uXD' | |
15 | + | ||
16 | + | let LANDPREFIX = "LAND" | |
17 | + | ||
18 | + | let DUCKPREFIX = "DUCK" | |
13 | 19 | ||
14 | 20 | let DEFAULTLOCATION = "Africa_F_Africa" | |
15 | 21 | ||
16 | - | let | |
22 | + | let DAILYRESBYPIECE = 3456000 | |
17 | 23 | ||
18 | - | let | |
24 | + | let DAYMILLIS = 86400000 | |
19 | 25 | ||
20 | - | let | |
26 | + | let FIVEMINUTESMILLIS = 300000 | |
21 | 27 | ||
22 | - | let continents = ["Americas", "Europe", "Asia", "Africa", "Oceania"] | |
23 | - | ||
24 | - | func keyFactoryWarehouseByIdAndType (factoryId,resType) = ((("factoryWhByContinentAndRes_" + factoryId) + "_") + toString(resType)) | |
28 | + | func keyAssetIdToOwner (assetId) = ("nftOwner_" + assetId) | |
25 | 29 | ||
26 | 30 | ||
27 | - | func | |
31 | + | func keyDuckIdToOwner (assetId) = ("duckOwner_" + assetId) | |
28 | 32 | ||
29 | 33 | ||
30 | 34 | func keyStakedTimeByAssetId (assetId) = ("stakedTime_" + assetId) | |
33 | 37 | func keyStakedDuckByOwner (ownerAddr) = ("stakedDuckByOwner_" + ownerAddr) | |
34 | 38 | ||
35 | 39 | ||
40 | + | func keyStakedTimeByTypeAssetIdAndOwner (nftType,assetId,ownerAddr) = ((((("stakedTimeByTypeAssetIdAndOwner_" + nftType) + "_") + assetId) + "_") + ownerAddr) | |
41 | + | ||
42 | + | ||
43 | + | func keyLandToOwner (landNum) = ("landOwner_" + landNum) | |
44 | + | ||
45 | + | ||
36 | 46 | func keyBackpackByDuck (duckAssetId) = ("backPack_" + duckAssetId) | |
37 | 47 | ||
38 | 48 | ||
39 | 49 | func keyDuckLocation (duckAssetId) = ("duckLocation_" + duckAssetId) | |
40 | 50 | ||
41 | 51 | ||
42 | - | ||
52 | + | func keyDuckHealth (duckAssetId) = ("duckHealth_" + duckAssetId) | |
43 | 53 | ||
44 | - | let idxQuantity = 1 | |
45 | 54 | ||
46 | - | let idxPrice = 2 | |
55 | + | let recLandNum = 0 | |
56 | + | ||
57 | + | let recLandSize = 1 | |
58 | + | ||
59 | + | let recTerrains = 2 | |
60 | + | ||
61 | + | let recContinent = 3 | |
47 | 62 | ||
48 | 63 | let locIdxContinent = 0 | |
49 | 64 | ||
59 | 74 | ||
60 | 75 | let bpIdxProd = 3 | |
61 | 76 | ||
62 | - | func asString (v) = match v { | |
63 | - | case s: String => | |
64 | - | s | |
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)] | |
90 | + | ||
91 | + | ||
92 | + | func numPiecesBySize (landSize) = match landSize { | |
65 | 93 | case _ => | |
66 | - | throw("fail to cast into String") | |
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") | |
67 | 105 | } | |
68 | 106 | ||
69 | 107 | ||
70 | - | func subRes (resList,resType,amount) = { | |
71 | - | func subber (acc,i) = (acc :+ (if ((i == resType)) | |
72 | - | then toString((parseIntValue(resList[i]) - amount)) | |
73 | - | else resList[i])) | |
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))) | |
112 | + | } | |
74 | 113 | ||
75 | 114 | let r = { | |
76 | 115 | let $l = [0, 1, 2, 3, 4, 5] | |
78 | 117 | let $acc0 = nil | |
79 | 118 | func $f0_1 ($a,$i) = if (($i >= $s)) | |
80 | 119 | then $a | |
81 | - | else | |
120 | + | else adder($a, $l[$i]) | |
82 | 121 | ||
83 | 122 | func $f0_2 ($a,$i) = if (($i >= $s)) | |
84 | 123 | then $a | |
91 | 130 | ||
92 | 131 | ||
93 | 132 | @Callable(i) | |
94 | - | func | |
95 | - | | |
96 | - | | |
97 | - | | |
98 | - | | |
99 | - | then throw((" | |
133 | + | func stakeLand () = { | |
134 | + | let pmt = value(i.payments[0]) | |
135 | + | let assetId = value(pmt.assetId) | |
136 | + | let address = toString(i.caller) | |
137 | + | if ((pmt.amount != 1)) | |
138 | + | then throw((("NFT " + LANDPREFIX) + " token should be attached as payment")) | |
100 | 139 | else { | |
101 | - | let duckAssetId = valueOrErrorMessage(getString(stakingContract, keyStakedDuckByOwner(toString(i.caller))), "You don't have a duck staked") | |
102 | - | if ((size(i.payments) != 0)) | |
103 | - | then throw("sellResources doesn't require any payments") | |
140 | + | let asset = value(assetInfo(assetId)) | |
141 | + | if ((asset.issuer != this)) | |
142 | + | then throw("Unknown issuer of token") | |
143 | + | else if (!(contains(asset.name, LANDPREFIX))) | |
144 | + | then throw((("Only NFT " + LANDPREFIX) + " tokens are accepted")) | |
145 | + | else { | |
146 | + | let landNumSize = drop(asset.name, 4) | |
147 | + | let landNum = if (contains(landNumSize, "XXL")) | |
148 | + | then dropRight(landNumSize, 3) | |
149 | + | else if (contains(landNumSize, "XL")) | |
150 | + | then dropRight(landNumSize, 2) | |
151 | + | else dropRight(landNumSize, 1) | |
152 | + | if (!(isDefined(parseInt(landNum)))) | |
153 | + | then throw(("Cannot parse land number from " + asset.name)) | |
154 | + | else { | |
155 | + | let timeKey = keyStakedTimeByAssetId(toBase58String(assetId)) | |
156 | + | if (isDefined(getInteger(timeKey))) | |
157 | + | then throw((("NFT " + asset.name) + " is already staked")) | |
158 | + | else [IntegerEntry(timeKey, lastBlock.timestamp), IntegerEntry(keyStakedTimeByTypeAssetIdAndOwner(LANDPREFIX, toBase58String(assetId), address), lastBlock.timestamp), StringEntry(keyAssetIdToOwner(toBase58String(assetId)), address), StringEntry(keyLandToOwner(landNum), address)] | |
159 | + | } | |
160 | + | } | |
161 | + | } | |
162 | + | } | |
163 | + | ||
164 | + | ||
165 | + | ||
166 | + | @Callable(i) | |
167 | + | func unstakeLand (landAssetId) = if ((size(i.payments) != 0)) | |
168 | + | then throw("unstake doesn't require any payments") | |
169 | + | else { | |
170 | + | let assetId = fromBase58String(landAssetId) | |
171 | + | let address = toString(i.caller) | |
172 | + | let asset = value(assetInfo(assetId)) | |
173 | + | if ((asset.issuer != this)) | |
174 | + | then throw("Unknown issuer of token") | |
175 | + | else if (!(contains(asset.name, LANDPREFIX))) | |
176 | + | then throw((("Only NFT " + LANDPREFIX) + " tokens can be unstaked")) | |
104 | 177 | else { | |
105 | - | let | |
106 | - | if (( | |
107 | - | then throw((" | |
178 | + | let timeKey = keyStakedTimeByAssetId(landAssetId) | |
179 | + | if (!(isDefined(timeKey))) | |
180 | + | then throw((("NFT " + asset.name) + " is not staked")) | |
108 | 181 | else { | |
109 | - | let bpKey = keyBackpackByDuck(duckAssetId) | |
110 | - | let currentPack = split(valueOrElse(getString(stakingContract, bpKey), "0:0_0_0_0_0_0::"), ":") | |
111 | - | let resList = split(currentPack[bpIdxRes], "_") | |
112 | - | let currentRes = parseIntValue(resList[resType]) | |
113 | - | if ((amount > currentRes)) | |
114 | - | then throw(((((("You have " + toString(currentRes)) + " of ") + resTypes[resType]) + " in backpack, but tried to sell ") + toString(amount))) | |
182 | + | let owner = valueOrErrorMessage(getString(keyAssetIdToOwner(landAssetId)), (("NFT " + asset.name) + " is orphaned")) | |
183 | + | if ((owner != address)) | |
184 | + | then throw("Staked NFT is not yours") | |
185 | + | else [ScriptTransfer(i.caller, 1, assetId), DeleteEntry(timeKey), DeleteEntry(keyStakedTimeByTypeAssetIdAndOwner(LANDPREFIX, landAssetId, address))] | |
186 | + | } | |
187 | + | } | |
188 | + | } | |
189 | + | ||
190 | + | ||
191 | + | ||
192 | + | @Callable(i) | |
193 | + | func stakeDuck () = { | |
194 | + | let pmt = value(i.payments[0]) | |
195 | + | let assetId = value(pmt.assetId) | |
196 | + | let address = toString(i.caller) | |
197 | + | if ((pmt.amount != 1)) | |
198 | + | then throw((("NFT " + DUCKPREFIX) + " token should be attached as payment")) | |
199 | + | else { | |
200 | + | let asset = value(assetInfo(assetId)) | |
201 | + | if (if ((asset.issuer != incubatorAddr)) | |
202 | + | then (asset.issuer != breederAddr) | |
203 | + | else false) | |
204 | + | then throw((("Unknown issuer of " + DUCKPREFIX) + " token")) | |
205 | + | else if (!(contains(asset.name, DUCKPREFIX))) | |
206 | + | then throw((("Only NFT " + DUCKPREFIX) + " tokens are accepted")) | |
207 | + | else { | |
208 | + | let assetIdStr = toBase58String(assetId) | |
209 | + | let timeKey = keyStakedTimeByAssetId(assetIdStr) | |
210 | + | if (isDefined(getInteger(timeKey))) | |
211 | + | then throw((("NFT " + asset.name) + " is already staked")) | |
212 | + | else if (isDefined(getString(keyStakedDuckByOwner(address)))) | |
213 | + | then throw(("You already staked one duck: " + asset.name)) | |
115 | 214 | else { | |
116 | - | let whKey = keyFactoryWarehouseByIdAndType(curLocation[locIdxId], resType) | |
117 | - | let w0 = valueOrElse(getInteger(whKey), 0) | |
118 | - | let r0 = if ((w0 > FACTORYMAXWAREHOUSE)) | |
119 | - | then 0 | |
120 | - | else if (((w0 + amount) > FACTORYMAXWAREHOUSE)) | |
121 | - | then (FACTORYMAXWAREHOUSE - w0) | |
122 | - | else amount | |
123 | - | let usdnReceived = (fraction(r0, ((2 * MULT6) - fraction((w0 + (r0 / 2)), MULT6, FACTORYMAXWAREHOUSE)), MULT8) + ((amount - r0) / 100)) | |
124 | - | let bpRes = subRes(resList, resType, amount) | |
125 | - | let newPack = makeString([currentPack[bpIdxLevel], bpRes, currentPack[bpIdxMat], currentPack[bpIdxProd]], ":") | |
126 | - | let result = asString(invoke(stakingContract, "updateBackpack", [duckAssetId, newPack], nil)) | |
127 | - | $Tuple2([IntegerEntry(whKey, (w0 + amount)), ScriptTransfer(i.caller, usdnReceived, usdnAssetId)], result) | |
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::")])))))) | |
128 | 228 | } | |
229 | + | } | |
230 | + | } | |
231 | + | } | |
232 | + | ||
233 | + | ||
234 | + | ||
235 | + | @Callable(i) | |
236 | + | func unstakeDuck (assetIdStr) = if ((size(i.payments) != 0)) | |
237 | + | then throw("unstake doesn't require any payments") | |
238 | + | else { | |
239 | + | let assetId = fromBase58String(assetIdStr) | |
240 | + | let address = toString(i.caller) | |
241 | + | let asset = value(assetInfo(assetId)) | |
242 | + | if (if ((asset.issuer != incubatorAddr)) | |
243 | + | then (asset.issuer != breederAddr) | |
244 | + | else false) | |
245 | + | then throw((("Unknown issuer of " + DUCKPREFIX) + " token")) | |
246 | + | else if (!(contains(asset.name, DUCKPREFIX))) | |
247 | + | then throw((("Only NFT " + DUCKPREFIX) + " tokens can be unstaked")) | |
248 | + | else { | |
249 | + | let timeKey = keyStakedTimeByAssetId(toBase58String(assetId)) | |
250 | + | if (!(isDefined(timeKey))) | |
251 | + | then throw((("NFT " + asset.name) + " is not staked")) | |
252 | + | else if (!(isDefined(keyStakedDuckByOwner(address)))) | |
253 | + | then throw((("The duck " + asset.name) + " is not staked")) | |
254 | + | else { | |
255 | + | let owner = valueOrErrorMessage(getString(keyDuckIdToOwner(toBase58String(assetId))), (("NFT " + asset.name) + " is orphaned")) | |
256 | + | if ((owner != address)) | |
257 | + | then throw("Staked NFT is not yours") | |
258 | + | else [ScriptTransfer(i.caller, 1, assetId), DeleteEntry(timeKey), DeleteEntry(keyDuckLocation(assetIdStr)), DeleteEntry(keyStakedTimeByTypeAssetIdAndOwner(DUCKPREFIX, assetIdStr, address)), DeleteEntry(keyStakedDuckByOwner(address))] | |
259 | + | } | |
260 | + | } | |
261 | + | } | |
262 | + | ||
263 | + | ||
264 | + | ||
265 | + | @Callable(i) | |
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") | |
331 | + | else { | |
332 | + | let parts = split(toUtf8String(message), ";") | |
333 | + | let hp = split(split(parts[0], "|")[0], "_") | |
334 | + | let curHP = parseIntValue(hp[1]) | |
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)) | |
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 { | |
350 | + | let locKey = keyDuckLocation(duckAssetId) | |
351 | + | let curLocation = valueOrElse(getString(locKey), DEFAULTLOCATION) | |
352 | + | if ((newLocation == curLocation)) | |
353 | + | then throw("You can't fly to the same location") | |
354 | + | else $Tuple2([StringEntry(locKey, if ((newHP > 0)) | |
355 | + | then newLocation | |
356 | + | else curLocation), IntegerEntry(keyHealth, newHP)], unit) | |
129 | 357 | } | |
130 | 358 | } | |
131 | 359 | } | |
360 | + | ||
361 | + | ||
362 | + | ||
363 | + | @Callable(i) | |
364 | + | func setHealth (health,duckAssetId) = { | |
365 | + | let duckAssetIdStr = valueOrErrorMessage(getString(keyStakedDuckByOwner(toString(i.caller))), "You don't have a duck staked") | |
366 | + | if (if ((0 > health)) | |
367 | + | then true | |
368 | + | else (health > 100)) | |
369 | + | then throw("HP should be within 0..100") | |
370 | + | else [IntegerEntry(keyDuckHealth(duckAssetId), health)] | |
371 | + | } | |
372 | + | ||
373 | + | ||
374 | + | ||
375 | + | @Callable(i) | |
376 | + | func updateBackpack (duckAssetId,newPack) = if ((i.caller != economyAddr)) | |
377 | + | then throw("permission denied") | |
378 | + | else $Tuple2([StringEntry(keyBackpackByDuck(duckAssetId), newPack)], newPack) | |
132 | 379 | ||
133 | 380 |
Old | New | Differences | |
---|---|---|---|
1 | 1 | {-# STDLIB_VERSION 6 #-} | |
2 | 2 | {-# SCRIPT_TYPE ACCOUNT #-} | |
3 | 3 | {-# CONTENT_TYPE DAPP #-} | |
4 | - | let | |
4 | + | let usdnAssetId = base58'HezsdQuRDtzksAYUy97gfhKy7Z1NW2uXYSHA3bgqenNZ' | |
5 | 5 | ||
6 | - | let | |
6 | + | let incubatorAddr = this | |
7 | 7 | ||
8 | - | let | |
8 | + | let breederAddr = this | |
9 | 9 | ||
10 | 10 | let backEndAddr = addressFromStringValue("3N5SpX21R3R75Qo4eb3MwFFvW7TUzyhvavv") | |
11 | 11 | ||
12 | - | let stakingContract = addressFromStringValue("3NDCyBG5q85JuaRiigUeEtainyjCQT3XpZm") | |
12 | + | let economyAddr = addressFromStringValue("3N8y4wxX3JC4TdrCJBXX16SjWf6X256hrep") | |
13 | + | ||
14 | + | let pub = base58'6LfPuKJjLgekmncBhMg2LZyMTNVzZBccXR28ySXm9uXD' | |
15 | + | ||
16 | + | let LANDPREFIX = "LAND" | |
17 | + | ||
18 | + | let DUCKPREFIX = "DUCK" | |
13 | 19 | ||
14 | 20 | let DEFAULTLOCATION = "Africa_F_Africa" | |
15 | 21 | ||
16 | - | let | |
22 | + | let DAILYRESBYPIECE = 3456000 | |
17 | 23 | ||
18 | - | let | |
24 | + | let DAYMILLIS = 86400000 | |
19 | 25 | ||
20 | - | let | |
26 | + | let FIVEMINUTESMILLIS = 300000 | |
21 | 27 | ||
22 | - | let continents = ["Americas", "Europe", "Asia", "Africa", "Oceania"] | |
23 | - | ||
24 | - | func keyFactoryWarehouseByIdAndType (factoryId,resType) = ((("factoryWhByContinentAndRes_" + factoryId) + "_") + toString(resType)) | |
28 | + | func keyAssetIdToOwner (assetId) = ("nftOwner_" + assetId) | |
25 | 29 | ||
26 | 30 | ||
27 | - | func | |
31 | + | func keyDuckIdToOwner (assetId) = ("duckOwner_" + assetId) | |
28 | 32 | ||
29 | 33 | ||
30 | 34 | func keyStakedTimeByAssetId (assetId) = ("stakedTime_" + assetId) | |
31 | 35 | ||
32 | 36 | ||
33 | 37 | func keyStakedDuckByOwner (ownerAddr) = ("stakedDuckByOwner_" + ownerAddr) | |
34 | 38 | ||
35 | 39 | ||
40 | + | func keyStakedTimeByTypeAssetIdAndOwner (nftType,assetId,ownerAddr) = ((((("stakedTimeByTypeAssetIdAndOwner_" + nftType) + "_") + assetId) + "_") + ownerAddr) | |
41 | + | ||
42 | + | ||
43 | + | func keyLandToOwner (landNum) = ("landOwner_" + landNum) | |
44 | + | ||
45 | + | ||
36 | 46 | func keyBackpackByDuck (duckAssetId) = ("backPack_" + duckAssetId) | |
37 | 47 | ||
38 | 48 | ||
39 | 49 | func keyDuckLocation (duckAssetId) = ("duckLocation_" + duckAssetId) | |
40 | 50 | ||
41 | 51 | ||
42 | - | ||
52 | + | func keyDuckHealth (duckAssetId) = ("duckHealth_" + duckAssetId) | |
43 | 53 | ||
44 | - | let idxQuantity = 1 | |
45 | 54 | ||
46 | - | let idxPrice = 2 | |
55 | + | let recLandNum = 0 | |
56 | + | ||
57 | + | let recLandSize = 1 | |
58 | + | ||
59 | + | let recTerrains = 2 | |
60 | + | ||
61 | + | let recContinent = 3 | |
47 | 62 | ||
48 | 63 | let locIdxContinent = 0 | |
49 | 64 | ||
50 | 65 | let locIdxType = 1 | |
51 | 66 | ||
52 | 67 | let locIdxId = 2 | |
53 | 68 | ||
54 | 69 | let bpIdxLevel = 0 | |
55 | 70 | ||
56 | 71 | let bpIdxRes = 1 | |
57 | 72 | ||
58 | 73 | let bpIdxMat = 2 | |
59 | 74 | ||
60 | 75 | let bpIdxProd = 3 | |
61 | 76 | ||
62 | - | func asString (v) = match v { | |
63 | - | case s: String => | |
64 | - | s | |
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)] | |
90 | + | ||
91 | + | ||
92 | + | func numPiecesBySize (landSize) = match landSize { | |
65 | 93 | case _ => | |
66 | - | throw("fail to cast into String") | |
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") | |
67 | 105 | } | |
68 | 106 | ||
69 | 107 | ||
70 | - | func subRes (resList,resType,amount) = { | |
71 | - | func subber (acc,i) = (acc :+ (if ((i == resType)) | |
72 | - | then toString((parseIntValue(resList[i]) - amount)) | |
73 | - | else resList[i])) | |
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))) | |
112 | + | } | |
74 | 113 | ||
75 | 114 | let r = { | |
76 | 115 | let $l = [0, 1, 2, 3, 4, 5] | |
77 | 116 | let $s = size($l) | |
78 | 117 | let $acc0 = nil | |
79 | 118 | func $f0_1 ($a,$i) = if (($i >= $s)) | |
80 | 119 | then $a | |
81 | - | else | |
120 | + | else adder($a, $l[$i]) | |
82 | 121 | ||
83 | 122 | func $f0_2 ($a,$i) = if (($i >= $s)) | |
84 | 123 | then $a | |
85 | 124 | else throw("List size exceeds 6") | |
86 | 125 | ||
87 | 126 | $f0_2($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($acc0, 0), 1), 2), 3), 4), 5), 6) | |
88 | 127 | } | |
89 | 128 | makeString(r, "_") | |
90 | 129 | } | |
91 | 130 | ||
92 | 131 | ||
93 | 132 | @Callable(i) | |
94 | - | func | |
95 | - | | |
96 | - | | |
97 | - | | |
98 | - | | |
99 | - | then throw((" | |
133 | + | func stakeLand () = { | |
134 | + | let pmt = value(i.payments[0]) | |
135 | + | let assetId = value(pmt.assetId) | |
136 | + | let address = toString(i.caller) | |
137 | + | if ((pmt.amount != 1)) | |
138 | + | then throw((("NFT " + LANDPREFIX) + " token should be attached as payment")) | |
100 | 139 | else { | |
101 | - | let duckAssetId = valueOrErrorMessage(getString(stakingContract, keyStakedDuckByOwner(toString(i.caller))), "You don't have a duck staked") | |
102 | - | if ((size(i.payments) != 0)) | |
103 | - | then throw("sellResources doesn't require any payments") | |
140 | + | let asset = value(assetInfo(assetId)) | |
141 | + | if ((asset.issuer != this)) | |
142 | + | then throw("Unknown issuer of token") | |
143 | + | else if (!(contains(asset.name, LANDPREFIX))) | |
144 | + | then throw((("Only NFT " + LANDPREFIX) + " tokens are accepted")) | |
145 | + | else { | |
146 | + | let landNumSize = drop(asset.name, 4) | |
147 | + | let landNum = if (contains(landNumSize, "XXL")) | |
148 | + | then dropRight(landNumSize, 3) | |
149 | + | else if (contains(landNumSize, "XL")) | |
150 | + | then dropRight(landNumSize, 2) | |
151 | + | else dropRight(landNumSize, 1) | |
152 | + | if (!(isDefined(parseInt(landNum)))) | |
153 | + | then throw(("Cannot parse land number from " + asset.name)) | |
154 | + | else { | |
155 | + | let timeKey = keyStakedTimeByAssetId(toBase58String(assetId)) | |
156 | + | if (isDefined(getInteger(timeKey))) | |
157 | + | then throw((("NFT " + asset.name) + " is already staked")) | |
158 | + | else [IntegerEntry(timeKey, lastBlock.timestamp), IntegerEntry(keyStakedTimeByTypeAssetIdAndOwner(LANDPREFIX, toBase58String(assetId), address), lastBlock.timestamp), StringEntry(keyAssetIdToOwner(toBase58String(assetId)), address), StringEntry(keyLandToOwner(landNum), address)] | |
159 | + | } | |
160 | + | } | |
161 | + | } | |
162 | + | } | |
163 | + | ||
164 | + | ||
165 | + | ||
166 | + | @Callable(i) | |
167 | + | func unstakeLand (landAssetId) = if ((size(i.payments) != 0)) | |
168 | + | then throw("unstake doesn't require any payments") | |
169 | + | else { | |
170 | + | let assetId = fromBase58String(landAssetId) | |
171 | + | let address = toString(i.caller) | |
172 | + | let asset = value(assetInfo(assetId)) | |
173 | + | if ((asset.issuer != this)) | |
174 | + | then throw("Unknown issuer of token") | |
175 | + | else if (!(contains(asset.name, LANDPREFIX))) | |
176 | + | then throw((("Only NFT " + LANDPREFIX) + " tokens can be unstaked")) | |
104 | 177 | else { | |
105 | - | let | |
106 | - | if (( | |
107 | - | then throw((" | |
178 | + | let timeKey = keyStakedTimeByAssetId(landAssetId) | |
179 | + | if (!(isDefined(timeKey))) | |
180 | + | then throw((("NFT " + asset.name) + " is not staked")) | |
108 | 181 | else { | |
109 | - | let bpKey = keyBackpackByDuck(duckAssetId) | |
110 | - | let currentPack = split(valueOrElse(getString(stakingContract, bpKey), "0:0_0_0_0_0_0::"), ":") | |
111 | - | let resList = split(currentPack[bpIdxRes], "_") | |
112 | - | let currentRes = parseIntValue(resList[resType]) | |
113 | - | if ((amount > currentRes)) | |
114 | - | then throw(((((("You have " + toString(currentRes)) + " of ") + resTypes[resType]) + " in backpack, but tried to sell ") + toString(amount))) | |
182 | + | let owner = valueOrErrorMessage(getString(keyAssetIdToOwner(landAssetId)), (("NFT " + asset.name) + " is orphaned")) | |
183 | + | if ((owner != address)) | |
184 | + | then throw("Staked NFT is not yours") | |
185 | + | else [ScriptTransfer(i.caller, 1, assetId), DeleteEntry(timeKey), DeleteEntry(keyStakedTimeByTypeAssetIdAndOwner(LANDPREFIX, landAssetId, address))] | |
186 | + | } | |
187 | + | } | |
188 | + | } | |
189 | + | ||
190 | + | ||
191 | + | ||
192 | + | @Callable(i) | |
193 | + | func stakeDuck () = { | |
194 | + | let pmt = value(i.payments[0]) | |
195 | + | let assetId = value(pmt.assetId) | |
196 | + | let address = toString(i.caller) | |
197 | + | if ((pmt.amount != 1)) | |
198 | + | then throw((("NFT " + DUCKPREFIX) + " token should be attached as payment")) | |
199 | + | else { | |
200 | + | let asset = value(assetInfo(assetId)) | |
201 | + | if (if ((asset.issuer != incubatorAddr)) | |
202 | + | then (asset.issuer != breederAddr) | |
203 | + | else false) | |
204 | + | then throw((("Unknown issuer of " + DUCKPREFIX) + " token")) | |
205 | + | else if (!(contains(asset.name, DUCKPREFIX))) | |
206 | + | then throw((("Only NFT " + DUCKPREFIX) + " tokens are accepted")) | |
207 | + | else { | |
208 | + | let assetIdStr = toBase58String(assetId) | |
209 | + | let timeKey = keyStakedTimeByAssetId(assetIdStr) | |
210 | + | if (isDefined(getInteger(timeKey))) | |
211 | + | then throw((("NFT " + asset.name) + " is already staked")) | |
212 | + | else if (isDefined(getString(keyStakedDuckByOwner(address)))) | |
213 | + | then throw(("You already staked one duck: " + asset.name)) | |
115 | 214 | else { | |
116 | - | let whKey = keyFactoryWarehouseByIdAndType(curLocation[locIdxId], resType) | |
117 | - | let w0 = valueOrElse(getInteger(whKey), 0) | |
118 | - | let r0 = if ((w0 > FACTORYMAXWAREHOUSE)) | |
119 | - | then 0 | |
120 | - | else if (((w0 + amount) > FACTORYMAXWAREHOUSE)) | |
121 | - | then (FACTORYMAXWAREHOUSE - w0) | |
122 | - | else amount | |
123 | - | let usdnReceived = (fraction(r0, ((2 * MULT6) - fraction((w0 + (r0 / 2)), MULT6, FACTORYMAXWAREHOUSE)), MULT8) + ((amount - r0) / 100)) | |
124 | - | let bpRes = subRes(resList, resType, amount) | |
125 | - | let newPack = makeString([currentPack[bpIdxLevel], bpRes, currentPack[bpIdxMat], currentPack[bpIdxProd]], ":") | |
126 | - | let result = asString(invoke(stakingContract, "updateBackpack", [duckAssetId, newPack], nil)) | |
127 | - | $Tuple2([IntegerEntry(whKey, (w0 + amount)), ScriptTransfer(i.caller, usdnReceived, usdnAssetId)], result) | |
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::")])))))) | |
128 | 228 | } | |
229 | + | } | |
230 | + | } | |
231 | + | } | |
232 | + | ||
233 | + | ||
234 | + | ||
235 | + | @Callable(i) | |
236 | + | func unstakeDuck (assetIdStr) = if ((size(i.payments) != 0)) | |
237 | + | then throw("unstake doesn't require any payments") | |
238 | + | else { | |
239 | + | let assetId = fromBase58String(assetIdStr) | |
240 | + | let address = toString(i.caller) | |
241 | + | let asset = value(assetInfo(assetId)) | |
242 | + | if (if ((asset.issuer != incubatorAddr)) | |
243 | + | then (asset.issuer != breederAddr) | |
244 | + | else false) | |
245 | + | then throw((("Unknown issuer of " + DUCKPREFIX) + " token")) | |
246 | + | else if (!(contains(asset.name, DUCKPREFIX))) | |
247 | + | then throw((("Only NFT " + DUCKPREFIX) + " tokens can be unstaked")) | |
248 | + | else { | |
249 | + | let timeKey = keyStakedTimeByAssetId(toBase58String(assetId)) | |
250 | + | if (!(isDefined(timeKey))) | |
251 | + | then throw((("NFT " + asset.name) + " is not staked")) | |
252 | + | else if (!(isDefined(keyStakedDuckByOwner(address)))) | |
253 | + | then throw((("The duck " + asset.name) + " is not staked")) | |
254 | + | else { | |
255 | + | let owner = valueOrErrorMessage(getString(keyDuckIdToOwner(toBase58String(assetId))), (("NFT " + asset.name) + " is orphaned")) | |
256 | + | if ((owner != address)) | |
257 | + | then throw("Staked NFT is not yours") | |
258 | + | else [ScriptTransfer(i.caller, 1, assetId), DeleteEntry(timeKey), DeleteEntry(keyDuckLocation(assetIdStr)), DeleteEntry(keyStakedTimeByTypeAssetIdAndOwner(DUCKPREFIX, assetIdStr, address)), DeleteEntry(keyStakedDuckByOwner(address))] | |
259 | + | } | |
260 | + | } | |
261 | + | } | |
262 | + | ||
263 | + | ||
264 | + | ||
265 | + | @Callable(i) | |
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") | |
331 | + | else { | |
332 | + | let parts = split(toUtf8String(message), ";") | |
333 | + | let hp = split(split(parts[0], "|")[0], "_") | |
334 | + | let curHP = parseIntValue(hp[1]) | |
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)) | |
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 { | |
350 | + | let locKey = keyDuckLocation(duckAssetId) | |
351 | + | let curLocation = valueOrElse(getString(locKey), DEFAULTLOCATION) | |
352 | + | if ((newLocation == curLocation)) | |
353 | + | then throw("You can't fly to the same location") | |
354 | + | else $Tuple2([StringEntry(locKey, if ((newHP > 0)) | |
355 | + | then newLocation | |
356 | + | else curLocation), IntegerEntry(keyHealth, newHP)], unit) | |
129 | 357 | } | |
130 | 358 | } | |
131 | 359 | } | |
360 | + | ||
361 | + | ||
362 | + | ||
363 | + | @Callable(i) | |
364 | + | func setHealth (health,duckAssetId) = { | |
365 | + | let duckAssetIdStr = valueOrErrorMessage(getString(keyStakedDuckByOwner(toString(i.caller))), "You don't have a duck staked") | |
366 | + | if (if ((0 > health)) | |
367 | + | then true | |
368 | + | else (health > 100)) | |
369 | + | then throw("HP should be within 0..100") | |
370 | + | else [IntegerEntry(keyDuckHealth(duckAssetId), health)] | |
371 | + | } | |
372 | + | ||
373 | + | ||
374 | + | ||
375 | + | @Callable(i) | |
376 | + | func updateBackpack (duckAssetId,newPack) = if ((i.caller != economyAddr)) | |
377 | + | then throw("permission denied") | |
378 | + | else $Tuple2([StringEntry(keyBackpackByDuck(duckAssetId), newPack)], newPack) | |
132 | 379 | ||
133 | 380 |
github/deemru/w8io/873ac7e 55.12 ms ◑![]()