tx · 9UucCZm2RobSgxz8zYjLzGz4wUmhKW3PYF516sUWvKab 3P36HH7i6BBuuNp7FHQd6WX8gzfogi778i6: -0.05000000 Waves 2022.10.06 17:10 [3326048] smart account 3P36HH7i6BBuuNp7FHQd6WX8gzfogi778i6 > SELF 0.00000000 Waves
{ "type": 13, "id": "9UucCZm2RobSgxz8zYjLzGz4wUmhKW3PYF516sUWvKab", "fee": 5000000, "feeAssetId": null, "timestamp": 1665065497060, "version": 2, "chainId": 87, "sender": "3P36HH7i6BBuuNp7FHQd6WX8gzfogi778i6", "senderPublicKey": "3xRzVfzGz2mW1Y3XAqQwVN4RiiWSgAHpLFmaF24hhfY9", "proofs": [ "56HRJe1QQGduJcXyVV5mUHqz1je7mnAXbUFChDBx2LMHpyWSBQW9GypQyzop6PMDA1DPZMP1P3qCRUsjskyPcW4m" ], "script": "base64:BgIfCAISAwoBCBIAEgUKAwgIARIGCgQICAEBEgMKAQgSAB8BFHRyeUdldFN0cmluZ0V4dGVybmFsAgdhZGRyZXNzA2tleQQHJG1hdGNoMAkAnQgCBQdhZGRyZXNzBQNrZXkDCQABAgUHJG1hdGNoMAIGU3RyaW5nBAFhBQckbWF0Y2gwBQFhAgABFXRyeUdldEludGVnZXJFeHRlcm5hbAIHYWRkcmVzcwNrZXkEByRtYXRjaDAJAJoIAgUHYWRkcmVzcwUDa2V5AwkAAQIFByRtYXRjaDACA0ludAQBYgUHJG1hdGNoMAUBYgAAAQ10cnlHZXRCb29sZWFuAQNrZXkEByRtYXRjaDAJAKAIAQUDa2V5AwkAAQIFByRtYXRjaDACB0Jvb2xlYW4EAWIFByRtYXRjaDAFAWIHAQx0cnlHZXRTdHJpbmcBA2tleQkBFHRyeUdldFN0cmluZ0V4dGVybmFsAgUEdGhpcwUDa2V5AQlnZXRPcmFjbGUACQEHQWRkcmVzcwEJANkEAQkBDHRyeUdldFN0cmluZwECFHN0YXRpY19vcmFjbGVBZGRyZXNzARBnZXREdWNrbGluZ1ByaWNlAAkBFXRyeUdldEludGVnZXJFeHRlcm5hbAIJAQlnZXRPcmFjbGUAAhRzdGF0aWNfZHVja2xpbmdQcmljZQENZ2V0RWdnQXNzZXRJZAAJANkEAQkBFHRyeUdldFN0cmluZ0V4dGVybmFsAgkBCWdldE9yYWNsZQACEXN0YXRpY19lZ2dBc3NldElkAQ9nZXRTcGljZUFzc2V0SWQACQDZBAEJARR0cnlHZXRTdHJpbmdFeHRlcm5hbAIJAQlnZXRPcmFjbGUAAhNzdGF0aWNfc3BpY2VBc3NldElkARFnZXRSZWJpcnRoQWRkcmVzcwAJAQdBZGRyZXNzAQkA2QQBCQEUdHJ5R2V0U3RyaW5nRXh0ZXJuYWwCCQEJZ2V0T3JhY2xlAAIVc3RhdGljX3JlYmlydGhBZGRyZXNzARNnZXRJbmN1YmF0b3JBZGRyZXNzAAkBB0FkZHJlc3MBCQDZBAEJARR0cnlHZXRTdHJpbmdFeHRlcm5hbAIJAQlnZXRPcmFjbGUAAhdzdGF0aWNfaW5jdWJhdG9yQWRkcmVzcwERZ2V0Q291cG9uc0FkZHJlc3MACQEHQWRkcmVzcwEJANkEAQkBFHRyeUdldFN0cmluZ0V4dGVybmFsAgkBCWdldE9yYWNsZQACFXN0YXRpY19jb3Vwb25zQWRkcmVzcwEOZ2V0QnVybkFkZHJlc3MACQEHQWRkcmVzcwEJANkEAQkBFHRyeUdldFN0cmluZ0V4dGVybmFsAgkBCWdldE9yYWNsZQACEnN0YXRpY19idXJuQWRkcmVzcwANYmFja2VuZFB1YktleQkA2QQBCQERQGV4dHJOYXRpdmUoMTA1MykCCQEJZ2V0T3JhY2xlAAIUc3RhdGljX2JhY2tlbmRQdWJLZXkADURVQ0tMSU5HUFJJQ0UAgMLXLwAWcGVyY2VudEdyb3d0aFByZWNpc2lvbgCAgIT+pt7hEQAVZXhpc3RpbmdEdWNrUHJlY2lzaW9uAICA6YOx3hYAFktHbG9iYWxJc3N1ZWRUaW1lc3RhbXACF2dsb2JhbF9pc3N1ZWRfdGltZXN0YW1wAQxrZXlUb3RhbEZlZWQBCmR1Y2tsaW5nSWQJAKwCAgkArAICAglkdWNrbGluZ18FCmR1Y2tsaW5nSWQCC19mZWVkX3NwaWNlAQ9rZXlBZGRyZXNzTm9uY2UBB2FkZHJlc3MJAKwCAgkArAICAghhZGRyZXNzXwUHYWRkcmVzcwIGX25vbmNlARBrZXlEdWNrbGluZ0xldmVsAQpkdWNrbGluZ0lkCQCsAgIJAKwCAgIJZHVja2xpbmdfBQpkdWNrbGluZ0lkAgZfbGV2ZWwBEGtleUR1Y2tsaW5nR3Jvd24BCmR1Y2tsaW5nSWQJAKwCAgkArAICAglkdWNrbGluZ18FCmR1Y2tsaW5nSWQCBl9ncm93bgESa2V5U3RhcnRQZXJjZW50YWdlAQpkdWNrbGluZ0lkCQCsAgIJAKwCAgIJZHVja2xpbmdfBQpkdWNrbGluZ0lkAhBfc3RhcnRQZXJjZW50YWdlAQhrZXlPd25lcgEKZHVja2xpbmdJZAkArAICCQCsAgICCWR1Y2tsaW5nXwUKZHVja2xpbmdJZAIGX293bmVyAQ5rZXlCbGFja2xpc3RlZAEKZHVja2xpbmdJZAkArAICCQCsAgICCWR1Y2tsaW5nXwUKZHVja2xpbmdJZAIMX2JsYWNrbGlzdGVkAQ10cnlHZXRJbnRlZ2VyAQNrZXkEA3ZhbAQHJG1hdGNoMAkAmggCBQR0aGlzBQNrZXkDCQABAgUHJG1hdGNoMAIDSW50BAFiBQckbWF0Y2gwBQFiAAAFA3ZhbAEHZ2V0Qm9vbAEDa2V5BAckbWF0Y2gwCQCbCAIFBHRoaXMFA2tleQMJAAECBQckbWF0Y2gwAgdCb29sZWFuBAFiBQckbWF0Y2gwBQFiBwEVZ2V0RHVja2xpbmdQZXJjZW50YWdlAQpkdWNrbGluZ0lkBBJwZXJjZW50YWdlRHVja2xpbmcJAQ10cnlHZXRJbnRlZ2VyAQkBEmtleVN0YXJ0UGVyY2VudGFnZQEFCmR1Y2tsaW5nSWQEBGJhc2UDCQECIT0CBRJwZXJjZW50YWdlRHVja2xpbmcAAAUScGVyY2VudGFnZUR1Y2tsaW5nABQJALYCAQkAaAIFBGJhc2UFFWV4aXN0aW5nRHVja1ByZWNpc2lvbgEVZ2V0Q3VycmVudExldmVsQmlnSW50AQpkdWNrbGluZ0lkBAprRHVja0xldmVsCQEQa2V5RHVja2xpbmdMZXZlbAEFCmR1Y2tsaW5nSWQEByRtYXRjaDAJAKIIAQUKa0R1Y2tMZXZlbAMJAAECBQckbWF0Y2gwAgZTdHJpbmcEAXMFByRtYXRjaDAJAKcDAQUBcwQHJG1hdGNoMQkAoAgBCQCsAgIJAKwCAgIJZHVja2xpbmdfBQpkdWNrbGluZ0lkAhFfaXNzdWVkQnlGZWVkQ2FsbAMJAAECBQckbWF0Y2gxAgdCb29sZWFuBAFiBQckbWF0Y2gxAwkAAAIFAWIGCQC2AgEAAAkBFWdldER1Y2tsaW5nUGVyY2VudGFnZQEFCmR1Y2tsaW5nSWQJARVnZXREdWNrbGluZ1BlcmNlbnRhZ2UBBQpkdWNrbGluZ0lkARljYWxjdWxhdGVOZXdEdWNrbGluZ0xldmVsAgpkdWNrbGluZ0lkDXBheW1lbnRBbW91bnQECWN1cnJlbnRUcwgFCWxhc3RCbG9jawl0aW1lc3RhbXAEBmdyb3d0aAkAvAIDCQC2AgEFDXBheW1lbnRBbW91bnQJALYCAQUWcGVyY2VudEdyb3d0aFByZWNpc2lvbgkAtgIBBQ1EVUNLTElOR1BSSUNFBAxjdXJyZW50TGV2ZWwJARVnZXRDdXJyZW50TGV2ZWxCaWdJbnQBBQpkdWNrbGluZ0lkBAhuZXdMZXZlbAkAtwICBQxjdXJyZW50TGV2ZWwFBmdyb3d0aAQGcmVzdWx0CQCmAwEFCG5ld0xldmVsCQCUCgIFBnJlc3VsdAkAzAgCCQCsAgICDWN1cnJlbnRMZXZlbD0JAKYDAQUMY3VycmVudExldmVsCQDMCAIJAKwCAgIJbmV3TGV2ZWw9CQCmAwEFCG5ld0xldmVsCQDMCAIJAKwCAgIHZ3Jvd3RoPQkApgMBBQZncm93dGgFA25pbAEPZ2V0QmFja2VuZFByb29mAw1tYXhGZWVkQW1vdW50CXVzZXJOb25jZQdhZGRyZXNzCQC5CQIJAMwIAgkApAMBBQ1tYXhGZWVkQW1vdW50CQDMCAIJAKQDAQUJdXNlck5vbmNlCQDMCAIFB2FkZHJlc3MFA25pbAIBOwEFYXNJbnQBBXZhbHVlBAckbWF0Y2gwBQV2YWx1ZQMJAAECBQckbWF0Y2gwAgNJbnQEA2ludAUHJG1hdGNoMAUDaW50CQACAQIeQkFJOiB3cm9uZyB0eXBlLCBleHBlY3RlZDogSW50BgFpAQ9jb25maWd1cmVPcmFjbGUBBm9yYWNsZQMJAQIhPQIIBQFpBmNhbGxlcgUEdGhpcwkAAgECD0JDTzogYWRtaW4gb25seQkAzAgCCQELU3RyaW5nRW50cnkCAhRzdGF0aWNfb3JhY2xlQWRkcmVzcwUGb3JhY2xlBQNuaWwBaQELYnV5RHVja2xpbmcABApleGFjdFByaWNlCQEQZ2V0RHVja2xpbmdQcmljZQADCQBmAgCA4esXBQpleGFjdFByaWNlCQACAQIfQkJEOiBJbnZhbGlkIHByaWNlIGZyb20gb3JhY2xlIQQTYW1vdW50UGFpZEJ5Q291cG9ucwkBBWFzSW50AQkA/AcECQERZ2V0Q291cG9uc0FkZHJlc3MAAgp1c2VDb3Vwb25zCQDMCAIFCmV4YWN0UHJpY2UFA25pbAUDbmlsAwkAAAIFE2Ftb3VudFBhaWRCeUNvdXBvbnMFE2Ftb3VudFBhaWRCeUNvdXBvbnMECWxlZnRUb1BheQkAZQIFCmV4YWN0UHJpY2UFE2Ftb3VudFBhaWRCeUNvdXBvbnMEB3BheW1lbnQDCQECIT0CBQlsZWZ0VG9QYXkAAAQMZmlyc3RQYXltZW50CQEFdmFsdWUBCQCRAwIIBQFpCHBheW1lbnRzAAADCQECIT0CCAUMZmlyc3RQYXltZW50B2Fzc2V0SWQJAQ1nZXRFZ2dBc3NldElkAAkAAgEJAKwCAgJBQkJEOiBZb3UgY2FuIGF0dGFjaCBvbmx5IEVHRyB0b2tlbnMgd2l0aCB0aGUgZm9sbG93aW5nIGFzc2V0IGlkOiAJANgEAQkBDWdldEVnZ0Fzc2V0SWQAAwkBAiE9AggFDGZpcnN0UGF5bWVudAZhbW91bnQFCWxlZnRUb1BheQkAAgEJAKwCAgJIQkJEOiBUbyBidXkgYSBwZXJjaCB5b3UgY3VycmVudGx5IG5lZWQgdGhlIGZvbGxvd2luZyBhbW91bnQgb2YgRUdHbGV0czogCQCkAwEFCWxlZnRUb1BheQQIYnVybkNhbGwJAPwHBAkBDmdldEJ1cm5BZGRyZXNzAAIUYnVybkF0dGFjaGVkUGF5bWVudHMFA25pbAkAzAgCCQEPQXR0YWNoZWRQYXltZW50AgkBDWdldEVnZ0Fzc2V0SWQABQlsZWZ0VG9QYXkFA25pbAMJAAACBQhidXJuQ2FsbAUIYnVybkNhbGwFCWxlZnRUb1BheQkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgAAAwkAAAIFB3BheW1lbnQFB3BheW1lbnQED2R1Y2tsaW5nQXNzZXRJZAkA/AcEBQR0aGlzAhFpc3N1ZUZyZWVEdWNrbGluZwkAzAgCCQClCAEIBQFpDG9yaWdpbkNhbGxlcgkAzAgCCQDYBAEIBQFpDXRyYW5zYWN0aW9uSWQJAMwIAgAABQNuaWwFA25pbAMJAAACBQ9kdWNrbGluZ0Fzc2V0SWQFD2R1Y2tsaW5nQXNzZXRJZAUDbmlsCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuAWkBEWlzc3VlRnJlZUR1Y2tsaW5nAwdhZGRyZXNzB3R4SWRTdHIKcGVyY2VudGFnZQMDCQECIT0CCAUBaQZjYWxsZXIFBHRoaXMJAQIhPQIIBQFpBmNhbGxlcgkBEWdldFJlYmlydGhBZGRyZXNzAAcJAAIBAiNCSUZEOiBZb3UgY2FuJ3QgaXNzdWUgZnJlZSBkdWNrbGluZwQFYXNzZXQJAMMIBwIQQkFCWS0xMTExMTExMS1HWgIAAAEAAAcFBHVuaXQFBmhlaWdodAQHYXNzZXRJZAkAuAgBBQVhc3NldAkAlAoCCQDMCAIJAQtTdHJpbmdFbnRyeQIJAKwCAgkArAICCQCsAgIFB2FkZHJlc3MCAV8FB3R4SWRTdHICA19kaQkA2AQBBQdhc3NldElkCQDMCAIJAQxJbnRlZ2VyRW50cnkCAgxzdGF0c19hbW91bnQJAGQCCQENdHJ5R2V0SW50ZWdlcgECDHN0YXRzX2Ftb3VudAABCQDMCAIJAQxCb29sZWFuRW50cnkCCQCsAgIJAKwCAgIJZHVja2xpbmdfCQDYBAEFB2Fzc2V0SWQCEV9pc3N1ZWRCeUZlZWRDYWxsCQAAAggFAWkGY2FsbGVyBQR0aGlzCQDMCAIJAQxJbnRlZ2VyRW50cnkCCQESa2V5U3RhcnRQZXJjZW50YWdlAQkA2AQBBQdhc3NldElkBQpwZXJjZW50YWdlCQDMCAIJAQtTdHJpbmdFbnRyeQIJAQhrZXlPd25lcgEJANgEAQUHYXNzZXRJZAUHYWRkcmVzcwkAzAgCBQVhc3NldAkAzAgCCQEOU2NyaXB0VHJhbnNmZXIDCQEFdmFsdWUBCQCmCAEFB2FkZHJlc3MAAQUHYXNzZXRJZAUDbmlsCQDYBAEFB2Fzc2V0SWQBaQEMZmVlZER1Y2tsaW5nBApkdWNrbGluZ0lkEGJhY2tlbmRTaWduYXR1cmUNbWF4RmVlZEFtb3VudAl1c2VyTm9uY2UEDWFkZHJlc3NTdHJpbmcJAKUIAQgFAWkGY2FsbGVyBAxiYWNrZW5kUHJvb2YJAQ9nZXRCYWNrZW5kUHJvb2YDBQ1tYXhGZWVkQW1vdW50BQl1c2VyTm9uY2UFDWFkZHJlc3NTdHJpbmcEDWtBZGRyZXNzTm9uY2UJAQ9rZXlBZGRyZXNzTm9uY2UBBQ1hZGRyZXNzU3RyaW5nBAxjdXJyZW50Tm9uY2UJAQ10cnlHZXRJbnRlZ2VyAQUNa0FkZHJlc3NOb25jZQQOcmVhbER1Y2tsaW5nSWQDCQEBIQEJAMQTAwkAmwMBBQxiYWNrZW5kUHJvb2YJANkEAQUQYmFja2VuZFNpZ25hdHVyZQUNYmFja2VuZFB1YktleQkAAgECH0JGRDogSW52YWxpZCBwcm9vZiBmcm9tIGJhY2tlbmQDAwkBAiE9AgkAkAMBCAUBaQhwYXltZW50cwABBgkBAiE9AggJAQV2YWx1ZQEJAJEDAggFAWkIcGF5bWVudHMAAAdhc3NldElkCQEPZ2V0U3BpY2VBc3NldElkAAkAAgECLkJGRDogQmFkIHBheW1lbnQgYXR0YWNoZWQgKGFzc2V0W3NdIG9yIGFtb3VudCkDCQEHZ2V0Qm9vbAEJARBrZXlEdWNrbGluZ0dyb3duAQUKZHVja2xpbmdJZAkAAgECHkJGRDogRHVja2xpbmcgaXMgYWxyZWFkeSBncm93bgMJAQIhPQIFCXVzZXJOb25jZQkAZAIFDGN1cnJlbnROb25jZQABCQACAQkArAICCQCsAgIJAKwCAgIaQkZEOiBVc2VyIE5vbmNlIHNob3VsZCBiZSAJAKQDAQUMY3VycmVudE5vbmNlAhUgKyAxLCB3aGlsZSByZWNlaXZlZCAJAKQDAQUJdXNlck5vbmNlBAtibGFja0xpc3RlZAkBDXRyeUdldEJvb2xlYW4BCQEOa2V5QmxhY2tsaXN0ZWQBBQpkdWNrbGluZ0lkAwULYmxhY2tMaXN0ZWQJAAIBAidCRkQ6IENhbiBub3QgZmVlZCBibGFja2xpc3RlZCBkdWNrbGluZyEDCQAAAgUKZHVja2xpbmdJZAIACQACAQIhQkZEOiBQbGVhc2UgYnV5IGEgZHVja2xpbmcgZmlyc3QhBA9kdWNrbGluZ0lkQ2hlY2sJAQV2YWx1ZQEJAOwHAQkA2QQBBQpkdWNrbGluZ0lkAwkBAiE9AgkA8AcCCAUBaQZjYWxsZXIIBQ9kdWNrbGluZ0lkQ2hlY2sCaWQAAQkAAgECKUJGRDogWW91J3JlIG5vdCB0aGUgb3duZXIgb2YgdGhlIGR1Y2tsaW5nAwkBAiE9AggFD2R1Y2tsaW5nSWRDaGVjawZpc3N1ZXIFBHRoaXMJAAIBAiRCRkQ6IENhbnQgZmluZCBkdWNrbGluZyB3aXRoIHN1Y2ggaWQJANgEAQgFD2R1Y2tsaW5nSWRDaGVjawJpZAQOY3VycmVudFBheW1lbnQICQEFdmFsdWUBCQCRAwIIBQFpCHBheW1lbnRzAAAGYW1vdW50BAlrTmV3TGV2ZWwJARBrZXlEdWNrbGluZ0xldmVsAQUOcmVhbER1Y2tsaW5nSWQECmtUb3RhbEZlZWQJAQxrZXlUb3RhbEZlZWQBBQ5yZWFsRHVja2xpbmdJZAQJdG90YWxGZWVkCQENdHJ5R2V0SW50ZWdlcgEFCmtUb3RhbEZlZWQEDGtGZWVkVHhTdGF0cwkArAICCQCsAgIJAKwCAgIJZHVja2xpbmdfBQ5yZWFsRHVja2xpbmdJZAIGX3N0YXRfCQCkAwEIBQlsYXN0QmxvY2sJdGltZXN0YW1wAwkAZgIFDmN1cnJlbnRQYXltZW50BQ1tYXhGZWVkQW1vdW50CQACAQkArAICAj9CRkQ6IENhbm5vdCBmZWVkIGR1Y2tsaW5nIGZvciBzdWNoIGFtb3VudCwgbWF4IGZlZWQgYW1vdW50IGlzOiAJAKQDAQUNbWF4RmVlZEFtb3VudAQQY2FsY3VsYXRlUmVzdWx0cwkBGWNhbGN1bGF0ZU5ld0R1Y2tsaW5nTGV2ZWwCBQ5yZWFsRHVja2xpbmdJZAkAaQIFDmN1cnJlbnRQYXltZW50AGQEDWR1Y2tsaW5nT3duZXIJAQx0cnlHZXRTdHJpbmcBCQEIa2V5T3duZXIBBQ5yZWFsRHVja2xpbmdJZAMJAQIhPQIFDWR1Y2tsaW5nT3duZXIJAKUIAQgFAWkGY2FsbGVyCQDMCAIJAQxCb29sZWFuRW50cnkCCQEOa2V5QmxhY2tsaXN0ZWQBBQ5yZWFsRHVja2xpbmdJZAYFA25pbAkAzAgCCQEMSW50ZWdlckVudHJ5AgUNa0FkZHJlc3NOb25jZQkAZAIFDGN1cnJlbnROb25jZQABCQDMCAIJAQxJbnRlZ2VyRW50cnkCBQprVG90YWxGZWVkCQBkAgUJdG90YWxGZWVkBQ5jdXJyZW50UGF5bWVudAkAzAgCCQEMSW50ZWdlckVudHJ5AgUMa0ZlZWRUeFN0YXRzBQ5jdXJyZW50UGF5bWVudAkAzAgCCQELU3RyaW5nRW50cnkCBQlrTmV3TGV2ZWwIBRBjYWxjdWxhdGVSZXN1bHRzAl8xCQDMCAIJAQtTdHJpbmdFbnRyeQIJAKwCAgUMa0ZlZWRUeFN0YXRzAgZfZGVidWcJALkJAggFEGNhbGN1bGF0ZVJlc3VsdHMCXzICATsFA25pbAFpAQlmaXhMZXZlbHMBC2R1Y2tsaW5nSWRzAwMJAQIhPQIIBQFpD2NhbGxlclB1YmxpY0tleQEg4jCrQaSaRY9uaPb/iI1dy/xcgsAiOBaUksF2pAJAViIJAQIhPQIIBQFpBmNhbGxlcgUEdGhpcwcJAAIBAhNCRkw6IE5vdCBhdXRob3JpemVkBA9kdWNrbGluZ0lkc0xpc3QJAQV2YWx1ZQEJALUJAgULZHVja2xpbmdJZHMCASwKAQhoYW5kbGVJZAIDYWNjAmlkBAprVG90YWxGZWVkCQEMa2V5VG90YWxGZWVkAQUCaWQECXRvdGFsRmVlZAkBDXRyeUdldEludGVnZXIBBQprVG90YWxGZWVkBAlrTmV3TGV2ZWwJARBrZXlEdWNrbGluZ0xldmVsAQUCaWQEDXN0YXJ0aW5nTGV2ZWwEByRtYXRjaDAJAKAIAQkArAICCQCsAgICCWR1Y2tsaW5nXwUCaWQCEV9pc3N1ZWRCeUZlZWRDYWxsAwkAAQIFByRtYXRjaDACB0Jvb2xlYW4EAWIFByRtYXRjaDADCQAAAgUBYgYJALYCAQAACQEVZ2V0RHVja2xpbmdQZXJjZW50YWdlAQUCaWQJARVnZXREdWNrbGluZ1BlcmNlbnRhZ2UBBQJpZAQGZ3Jvd3RoCQC8AgMJALYCAQUJdG90YWxGZWVkCQC2AgEFFnBlcmNlbnRHcm93dGhQcmVjaXNpb24JALYCAQUNRFVDS0xJTkdQUklDRQkAzggCBQNhY2MJAMwIAgkBC1N0cmluZ0VudHJ5AgUJa05ld0xldmVsCQCmAwEJALcCAgUNc3RhcnRpbmdMZXZlbAUGZ3Jvd3RoBQNuaWwKAAIkbAUPZHVja2xpbmdJZHNMaXN0CgACJHMJAJADAQUCJGwKAAUkYWNjMAUDbmlsCgEFJGYwXzECAiRhAiRpAwkAZwIFAiRpBQIkcwUCJGEJAQhoYW5kbGVJZAIFAiRhCQCRAwIFAiRsBQIkaQoBBSRmMF8yAgIkYQIkaQMJAGcCBQIkaQUCJHMFAiRhCQACAQIUTGlzdCBzaXplIGV4Y2VlZHMgMjAJAQUkZjBfMgIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIFBSRhY2MwAAAAAQACAAMABAAFAAYABwAIAAkACgALAAwADQAOAA8AEAARABIAEwAUAWkBFHR1cm5EdWNrbGluZ0ludG9EdWNrAAQHYWRkcmVzcwkApQgBCAUBaQZjYWxsZXIEBHR4SWQJANgEAQgFAWkNdHJhbnNhY3Rpb25JZAQUbGFzdElzc3VlZER1Y2tsaW5nVHMJAQ10cnlHZXRJbnRlZ2VyAQUWS0dsb2JhbElzc3VlZFRpbWVzdGFtcAQLZml2ZU1pbkluTXMJAGgCCQBoAgAFADwA6AcECHRpbWVEaWZmCQBlAggFCWxhc3RCbG9jawl0aW1lc3RhbXAJAGQCBRRsYXN0SXNzdWVkRHVja2xpbmdUcwULZml2ZU1pbkluTXMDAwkAZgIFFGxhc3RJc3N1ZWREdWNrbGluZ1RzAAAJAGYCAAAFCHRpbWVEaWZmBwkAAgEJAKwCAgkArAICAkJCVEQ6IENhbiBpc3N1ZSBkdWNrbGluZ3Mgb25seSBvbmNlIHBlciA1IG1pbnV0ZXMsIHBsZWFzZSB3YWl0IGZvciAJAKQDAQkAawMFCHRpbWVEaWZmAAEA6AcCBSBzZWMuAwkBAiE9AgkAkAMBCAUBaQhwYXltZW50cwABCQACAQIuQlREOiBCYWQgcGF5bWVudCBhdHRhY2hlZCAoYXNzZXRbc10gb3IgYW1vdW50KQQDcG10CQEFdmFsdWUBCQDsBwEJAQV2YWx1ZQEICQEFdmFsdWUBCQCRAwIIBQFpCHBheW1lbnRzAAAHYXNzZXRJZAQLYmxhY2tMaXN0ZWQJAQ10cnlHZXRCb29sZWFuAQkBDmtleUJsYWNrbGlzdGVkAQkA2AQBCAUDcG10AmlkAwULYmxhY2tMaXN0ZWQJAAIBAipCVEQ6IENhbiBub3QgY29udmVydCBibGFja2xpc3RlZCBkdWNrbGluZyEDCQC/AgIJALYCAQUWcGVyY2VudEdyb3d0aFByZWNpc2lvbgkBFWdldEN1cnJlbnRMZXZlbEJpZ0ludAEJANgEAQgFA3BtdAJpZAkAAgECIUJURDogRHVja2xpbmcgaXMgbm90IGdyb3duIHlldC4uLgMJAQIhPQIIBQNwbXQGaXNzdWVyBQR0aGlzCQACAQIqQlREOiBDYW4gdXNlIG9ubHkgZHVja2xpbmdzIGZyb20gdGhpcyBkQXBwBARjYWxsCQD8BwQJARNnZXRJbmN1YmF0b3JBZGRyZXNzAAIRc3RhcnREdWNrSGF0Y2hpbmcJAMwIAgIABQNuaWwFA25pbAMJAAACBQRjYWxsBQRjYWxsBA5rRHVja2xpbmdHcm93bgkBEGtleUR1Y2tsaW5nR3Jvd24BCQDYBAEIBQNwbXQCaWQJAMwIAgkBDEJvb2xlYW5FbnRyeQIFDmtEdWNrbGluZ0dyb3duBgkAzAgCCQEMSW50ZWdlckVudHJ5AgUWS0dsb2JhbElzc3VlZFRpbWVzdGFtcAgFCWxhc3RCbG9jawl0aW1lc3RhbXAFA25pbAkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgECdHgBBnZlcmlmeQAJAPQDAwgFAnR4CWJvZHlCeXRlcwkAkQMCCAUCdHgGcHJvb2ZzAAAIBQJ0eA9zZW5kZXJQdWJsaWNLZXkTsoYI", "height": 3326048, "applicationStatus": "succeeded", "spentComplexity": 0 } View: original | compacted Prev: HVSF1PzT5dhtLTYnTkeeKFRjhmvsWSQnQrwMtnwwPUEY Next: 5HpXcaWVjXEzwbZmmPFuCKkJ1oPtvLzVuFU6QDhVEWv1 Diff:
Old | New | Differences | |
---|---|---|---|
9 | 9 | } | |
10 | 10 | ||
11 | 11 | ||
12 | - | func keyBlacklisted (ducklingId) = (("duckling_" + ducklingId) + "_blacklisted") | |
13 | - | ||
14 | - | ||
15 | 12 | func tryGetIntegerExternal (address,key) = match getInteger(address, key) { | |
16 | 13 | case b: Int => | |
17 | 14 | b | |
18 | 15 | case _ => | |
19 | 16 | 0 | |
17 | + | } | |
18 | + | ||
19 | + | ||
20 | + | func tryGetBoolean (key) = match getBoolean(key) { | |
21 | + | case b: Boolean => | |
22 | + | b | |
23 | + | case _ => | |
24 | + | false | |
20 | 25 | } | |
21 | 26 | ||
22 | 27 | ||
26 | 31 | func getOracle () = Address(fromBase58String(tryGetString("static_oracleAddress"))) | |
27 | 32 | ||
28 | 33 | ||
34 | + | func getDucklingPrice () = tryGetIntegerExternal(getOracle(), "static_ducklingPrice") | |
35 | + | ||
36 | + | ||
37 | + | func getEggAssetId () = fromBase58String(tryGetStringExternal(getOracle(), "static_eggAssetId")) | |
38 | + | ||
39 | + | ||
29 | 40 | func getSpiceAssetId () = fromBase58String(tryGetStringExternal(getOracle(), "static_spiceAssetId")) | |
30 | 41 | ||
31 | 42 | ||
32 | - | func | |
43 | + | func getRebirthAddress () = Address(fromBase58String(tryGetStringExternal(getOracle(), "static_rebirthAddress"))) | |
33 | 44 | ||
34 | 45 | ||
35 | - | let DUCKLINGPRICE = 10000000000 | |
46 | + | func getIncubatorAddress () = Address(fromBase58String(tryGetStringExternal(getOracle(), "static_incubatorAddress"))) | |
47 | + | ||
48 | + | ||
49 | + | func getCouponsAddress () = Address(fromBase58String(tryGetStringExternal(getOracle(), "static_couponsAddress"))) | |
50 | + | ||
51 | + | ||
52 | + | func getBurnAddress () = Address(fromBase58String(tryGetStringExternal(getOracle(), "static_burnAddress"))) | |
53 | + | ||
54 | + | ||
55 | + | let backendPubKey = fromBase58String(getStringValue(getOracle(), "static_backendPubKey")) | |
56 | + | ||
57 | + | let DUCKLINGPRICE = 100000000 | |
58 | + | ||
59 | + | let percentGrowthPrecision = 10000000000000000 | |
60 | + | ||
61 | + | let existingDuckPrecision = 100000000000000 | |
36 | 62 | ||
37 | 63 | let KGlobalIssuedTimestamp = "global_issued_timestamp" | |
38 | 64 | ||
39 | - | func keyTotalFeed (ducklingId) = (("duckling_" + ducklingId) + "_feed") | |
65 | + | func keyTotalFeed (ducklingId) = (("duckling_" + ducklingId) + "_feed_spice") | |
40 | 66 | ||
41 | 67 | ||
42 | - | func | |
68 | + | func keyAddressNonce (address) = (("address_" + address) + "_nonce") | |
43 | 69 | ||
44 | 70 | ||
45 | 71 | func keyDucklingLevel (ducklingId) = (("duckling_" + ducklingId) + "_level") | |
52 | 78 | ||
53 | 79 | ||
54 | 80 | func keyOwner (ducklingId) = (("duckling_" + ducklingId) + "_owner") | |
81 | + | ||
82 | + | ||
83 | + | func keyBlacklisted (ducklingId) = (("duckling_" + ducklingId) + "_blacklisted") | |
55 | 84 | ||
56 | 85 | ||
57 | 86 | func tryGetInteger (key) = { | |
73 | 102 | } | |
74 | 103 | ||
75 | 104 | ||
76 | - | func getCurrentLevelInt (ducklingId) = { | |
105 | + | func getDucklingPercentage (ducklingId) = { | |
106 | + | let percentageDuckling = tryGetInteger(keyStartPercentage(ducklingId)) | |
107 | + | let base = if ((percentageDuckling != 0)) | |
108 | + | then percentageDuckling | |
109 | + | else 20 | |
110 | + | toBigInt((base * existingDuckPrecision)) | |
111 | + | } | |
112 | + | ||
113 | + | ||
114 | + | func getCurrentLevelBigInt (ducklingId) = { | |
77 | 115 | let kDuckLevel = keyDucklingLevel(ducklingId) | |
78 | 116 | match getString(kDuckLevel) { | |
79 | 117 | case s: String => | |
80 | - | parseIntValue(s) | |
81 | - | case u: Unit => | |
82 | - | 0 | |
118 | + | parseBigIntValue(s) | |
83 | 119 | case _ => | |
84 | - | throw("BGCLBI: Weird value detected!") | |
120 | + | match getBoolean((("duckling_" + ducklingId) + "_issuedByFeedCall")) { | |
121 | + | case b: Boolean => | |
122 | + | if ((b == true)) | |
123 | + | then toBigInt(0) | |
124 | + | else getDucklingPercentage(ducklingId) | |
125 | + | case _ => | |
126 | + | getDucklingPercentage(ducklingId) | |
127 | + | } | |
85 | 128 | } | |
86 | - | } | |
87 | - | ||
88 | - | ||
89 | - | func getLastFedTimestamp (ducklingId) = { | |
90 | - | let lastFedTimestamp = tryGetInteger(keyDucklingFedLastTimestamp(ducklingId)) | |
91 | - | if ((lastFedTimestamp > 0)) | |
92 | - | then lastFedTimestamp | |
93 | - | else lastBlock.timestamp | |
94 | 129 | } | |
95 | 130 | ||
96 | 131 | ||
97 | 132 | func calculateNewDucklingLevel (ducklingId,paymentAmount) = { | |
98 | 133 | let currentTs = lastBlock.timestamp | |
99 | - | let lastFedTimestampChecked = getLastFedTimestamp(ducklingId) | |
100 | - | let fedDiff = (currentTs - lastFedTimestampChecked) | |
101 | - | let growth = paymentAmount | |
102 | - | let currentLevel = getCurrentLevelInt(ducklingId) | |
103 | - | let newLevel = (currentLevel + paymentAmount) | |
104 | - | if ((newLevel > DUCKLINGPRICE)) | |
105 | - | then throw((("CBDB: You are feeding your duckling to much! Only " + toString((DUCKLINGPRICE - currentLevel))) + "needed!")) | |
106 | - | else { | |
107 | - | let result = toString(newLevel) | |
108 | - | $Tuple2(result, [("currentLevel=" + toString(currentLevel)), ("newLevel=" + toString(newLevel)), ("growth=" + toString(growth)), ("lastFedTs=" + toString(lastFedTimestampChecked)), ("fedDiff=" + toString(fedDiff))]) | |
109 | - | } | |
134 | + | let growth = fraction(toBigInt(paymentAmount), toBigInt(percentGrowthPrecision), toBigInt(DUCKLINGPRICE)) | |
135 | + | let currentLevel = getCurrentLevelBigInt(ducklingId) | |
136 | + | let newLevel = (currentLevel + growth) | |
137 | + | let result = toString(newLevel) | |
138 | + | $Tuple2(result, [("currentLevel=" + toString(currentLevel)), ("newLevel=" + toString(newLevel)), ("growth=" + toString(growth))]) | |
110 | 139 | } | |
140 | + | ||
141 | + | ||
142 | + | func getBackendProof (maxFeedAmount,userNonce,address) = makeString([toString(maxFeedAmount), toString(userNonce), address], ";") | |
111 | 143 | ||
112 | 144 | ||
113 | 145 | func asInt (value) = match value { | |
115 | 147 | int | |
116 | 148 | case _ => | |
117 | 149 | throw("BAI: wrong type, expected: Int") | |
118 | - | } | |
119 | - | ||
120 | - | ||
121 | - | func tryGetBoolean (key) = match getBoolean(key) { | |
122 | - | case b: Boolean => | |
123 | - | b | |
124 | - | case _ => | |
125 | - | false | |
126 | 150 | } | |
127 | 151 | ||
128 | 152 | ||
134 | 158 | ||
135 | 159 | ||
136 | 160 | @Callable(i) | |
137 | - | func issueDuckling (address,txIdStr) = if ((i.caller != getBreederAddress())) | |
161 | + | func buyDuckling () = { | |
162 | + | let exactPrice = getDucklingPrice() | |
163 | + | if ((50000000 > exactPrice)) | |
164 | + | then throw("BBD: Invalid price from oracle!") | |
165 | + | else { | |
166 | + | let amountPaidByCoupons = asInt(invoke(getCouponsAddress(), "useCoupons", [exactPrice], nil)) | |
167 | + | if ((amountPaidByCoupons == amountPaidByCoupons)) | |
168 | + | then { | |
169 | + | let leftToPay = (exactPrice - amountPaidByCoupons) | |
170 | + | let payment = if ((leftToPay != 0)) | |
171 | + | then { | |
172 | + | let firstPayment = value(i.payments[0]) | |
173 | + | if ((firstPayment.assetId != getEggAssetId())) | |
174 | + | then throw(("BBD: You can attach only EGG tokens with the following asset id: " + toBase58String(getEggAssetId()))) | |
175 | + | else if ((firstPayment.amount != leftToPay)) | |
176 | + | then throw(("BBD: To buy a perch you currently need the following amount of EGGlets: " + toString(leftToPay))) | |
177 | + | else { | |
178 | + | let burnCall = invoke(getBurnAddress(), "burnAttachedPayments", nil, [AttachedPayment(getEggAssetId(), leftToPay)]) | |
179 | + | if ((burnCall == burnCall)) | |
180 | + | then leftToPay | |
181 | + | else throw("Strict value is not equal to itself.") | |
182 | + | } | |
183 | + | } | |
184 | + | else 0 | |
185 | + | if ((payment == payment)) | |
186 | + | then { | |
187 | + | let ducklingAssetId = invoke(this, "issueFreeDuckling", [toString(i.originCaller), toBase58String(i.transactionId), 0], nil) | |
188 | + | if ((ducklingAssetId == ducklingAssetId)) | |
189 | + | then nil | |
190 | + | else throw("Strict value is not equal to itself.") | |
191 | + | } | |
192 | + | else throw("Strict value is not equal to itself.") | |
193 | + | } | |
194 | + | else throw("Strict value is not equal to itself.") | |
195 | + | } | |
196 | + | } | |
197 | + | ||
198 | + | ||
199 | + | ||
200 | + | @Callable(i) | |
201 | + | func issueFreeDuckling (address,txIdStr,percentage) = if (if ((i.caller != this)) | |
202 | + | then (i.caller != getRebirthAddress()) | |
203 | + | else false) | |
138 | 204 | then throw("BIFD: You can't issue free duckling") | |
139 | 205 | else { | |
140 | - | let asset = Issue("BABY-11111111- | |
206 | + | let asset = Issue("BABY-11111111-GZ", "", 1, 0, false, unit, height) | |
141 | 207 | let assetId = calculateAssetId(asset) | |
142 | - | $Tuple2([StringEntry((((address + "_") + txIdStr) + "_di"), toBase58String(assetId)), IntegerEntry("stats_amount", (tryGetInteger("stats_amount") + 1)), BooleanEntry((("duckling_" + toBase58String(assetId)) + "_issuedByFeedCall"), (i.caller == this)), IntegerEntry(keyStartPercentage(toBase58String(assetId)), | |
208 | + | $Tuple2([StringEntry((((address + "_") + txIdStr) + "_di"), toBase58String(assetId)), IntegerEntry("stats_amount", (tryGetInteger("stats_amount") + 1)), BooleanEntry((("duckling_" + toBase58String(assetId)) + "_issuedByFeedCall"), (i.caller == this)), IntegerEntry(keyStartPercentage(toBase58String(assetId)), percentage), StringEntry(keyOwner(toBase58String(assetId)), address), asset, ScriptTransfer(value(addressFromString(address)), 1, assetId)], toBase58String(assetId)) | |
143 | 209 | } | |
144 | 210 | ||
145 | 211 | ||
146 | 212 | ||
147 | 213 | @Callable(i) | |
148 | - | func feedDuckling (ducklingId) = { | |
214 | + | func feedDuckling (ducklingId,backendSignature,maxFeedAmount,userNonce) = { | |
149 | 215 | let addressString = toString(i.caller) | |
150 | - | let | |
151 | - | | |
152 | - | | |
153 | - | | |
154 | - | | |
155 | - | | |
156 | - | | |
157 | - | | |
158 | - | | |
159 | - | | |
160 | - | | |
161 | - | | |
162 | - | | |
163 | - | | |
164 | - | ||
165 | - | ||
166 | - | ||
167 | - | ||
168 | - | ||
169 | - | ||
170 | - | | |
171 | - | | |
172 | - | | |
173 | - | | |
174 | - | | |
175 | - | | |
176 | - | } | |
177 | - | | |
216 | + | let backendProof = getBackendProof(maxFeedAmount, userNonce, addressString) | |
217 | + | let kAddressNonce = keyAddressNonce(addressString) | |
218 | + | let currentNonce = tryGetInteger(kAddressNonce) | |
219 | + | let realDucklingId = if (!(sigVerify_8Kb(toBytes(backendProof), fromBase58String(backendSignature), backendPubKey))) | |
220 | + | then throw("BFD: Invalid proof from backend") | |
221 | + | else if (if ((size(i.payments) != 1)) | |
222 | + | then true | |
223 | + | else (value(i.payments[0]).assetId != getSpiceAssetId())) | |
224 | + | then throw("BFD: Bad payment attached (asset[s] or amount)") | |
225 | + | else if (getBool(keyDucklingGrown(ducklingId))) | |
226 | + | then throw("BFD: Duckling is already grown") | |
227 | + | else if ((userNonce != (currentNonce + 1))) | |
228 | + | then throw(((("BFD: User Nonce should be " + toString(currentNonce)) + " + 1, while received ") + toString(userNonce))) | |
229 | + | else { | |
230 | + | let blackListed = tryGetBoolean(keyBlacklisted(ducklingId)) | |
231 | + | if (blackListed) | |
232 | + | then throw("BFD: Can not feed blacklisted duckling!") | |
233 | + | else if ((ducklingId == "")) | |
234 | + | then throw("BFD: Please buy a duckling first!") | |
235 | + | else { | |
236 | + | let ducklingIdCheck = value(assetInfo(fromBase58String(ducklingId))) | |
237 | + | if ((assetBalance(i.caller, ducklingIdCheck.id) != 1)) | |
238 | + | then throw("BFD: You're not the owner of the duckling") | |
239 | + | else if ((ducklingIdCheck.issuer != this)) | |
240 | + | then throw("BFD: Cant find duckling with such id") | |
241 | + | else toBase58String(ducklingIdCheck.id) | |
242 | + | } | |
243 | + | } | |
178 | 244 | let currentPayment = value(i.payments[0]).amount | |
179 | 245 | let kNewLevel = keyDucklingLevel(realDucklingId) | |
180 | 246 | let kTotalFeed = keyTotalFeed(realDucklingId) | |
181 | 247 | let totalFeed = tryGetInteger(kTotalFeed) | |
182 | 248 | let kFeedTxStats = ((("duckling_" + realDucklingId) + "_stat_") + toString(lastBlock.timestamp)) | |
183 | - | let calculateResults = calculateNewDucklingLevel(realDucklingId, currentPayment) | |
184 | - | [IntegerEntry(kDucklingLastFedTs, lastBlock.timestamp), IntegerEntry(kTotalFeed, (totalFeed + currentPayment)), IntegerEntry(kFeedTxStats, currentPayment), StringEntry(kNewLevel, calculateResults._1), StringEntry((kFeedTxStats + "_debug"), makeString(calculateResults._2, ";"))] | |
249 | + | if ((currentPayment > maxFeedAmount)) | |
250 | + | then throw(("BFD: Cannot feed duckling for such amount, max feed amount is: " + toString(maxFeedAmount))) | |
251 | + | else { | |
252 | + | let calculateResults = calculateNewDucklingLevel(realDucklingId, (currentPayment / 100)) | |
253 | + | let ducklingOwner = tryGetString(keyOwner(realDucklingId)) | |
254 | + | if ((ducklingOwner != toString(i.caller))) | |
255 | + | then [BooleanEntry(keyBlacklisted(realDucklingId), true)] | |
256 | + | else [IntegerEntry(kAddressNonce, (currentNonce + 1)), IntegerEntry(kTotalFeed, (totalFeed + currentPayment)), IntegerEntry(kFeedTxStats, currentPayment), StringEntry(kNewLevel, calculateResults._1), StringEntry((kFeedTxStats + "_debug"), makeString(calculateResults._2, ";"))] | |
257 | + | } | |
185 | 258 | } | |
186 | 259 | ||
187 | 260 | ||
188 | 261 | ||
189 | 262 | @Callable(i) | |
190 | - | func turnDucklingIntoDuck () = if ((i.caller != getBreederAddress())) | |
191 | - | then throw("BIFD: You can't turn duckling") | |
263 | + | func fixLevels (ducklingIds) = if (if ((i.callerPublicKey != base58'GDxBbsDRmeY39quNrDsTXKJzFWbQVtjxHseF4ikxZ7n9')) | |
264 | + | then (i.caller != this) | |
265 | + | else false) | |
266 | + | then throw("BFL: Not authorized") | |
192 | 267 | else { | |
193 | - | let address = toString(i.caller) | |
194 | - | let txId = toBase58String(i.transactionId) | |
195 | - | let lastIssuedDucklingTs = tryGetInteger(KGlobalIssuedTimestamp) | |
196 | - | let fiveMinInMs = ((1 * 60) * 1000) | |
197 | - | let timeDiff = (lastBlock.timestamp - (lastIssuedDucklingTs + fiveMinInMs)) | |
198 | - | if (if ((lastIssuedDucklingTs > 0)) | |
199 | - | then (0 > timeDiff) | |
200 | - | else false) | |
201 | - | then throw((("BTD: Can issue ducklings only once per 1 minutes, please wait for " + toString(fraction(timeDiff, 1, 1000))) + " sec.")) | |
202 | - | else if ((size(i.payments) != 1)) | |
203 | - | then throw("BTD: Bad payment attached (asset[s] or amount)") | |
204 | - | else { | |
205 | - | let pmt = value(assetInfo(value(value(i.payments[0]).assetId))) | |
206 | - | if ((DUCKLINGPRICE > getCurrentLevelInt(toBase58String(pmt.id)))) | |
207 | - | then throw(((("BTD: Duckling is not grown yet..." + toString(getCurrentLevelInt(toBase58String(pmt.id)))) + " ") + toString(DUCKLINGPRICE))) | |
268 | + | let ducklingIdsList = value(split(ducklingIds, ",")) | |
269 | + | func handleId (acc,id) = { | |
270 | + | let kTotalFeed = keyTotalFeed(id) | |
271 | + | let totalFeed = tryGetInteger(kTotalFeed) | |
272 | + | let kNewLevel = keyDucklingLevel(id) | |
273 | + | let startingLevel = match getBoolean((("duckling_" + id) + "_issuedByFeedCall")) { | |
274 | + | case b: Boolean => | |
275 | + | if ((b == true)) | |
276 | + | then toBigInt(0) | |
277 | + | else getDucklingPercentage(id) | |
278 | + | case _ => | |
279 | + | getDucklingPercentage(id) | |
280 | + | } | |
281 | + | let growth = fraction(toBigInt(totalFeed), toBigInt(percentGrowthPrecision), toBigInt(DUCKLINGPRICE)) | |
282 | + | (acc ++ [StringEntry(kNewLevel, toString((startingLevel + growth)))]) | |
283 | + | } | |
284 | + | ||
285 | + | let $l = ducklingIdsList | |
286 | + | let $s = size($l) | |
287 | + | let $acc0 = nil | |
288 | + | func $f0_1 ($a,$i) = if (($i >= $s)) | |
289 | + | then $a | |
290 | + | else handleId($a, $l[$i]) | |
291 | + | ||
292 | + | func $f0_2 ($a,$i) = if (($i >= $s)) | |
293 | + | then $a | |
294 | + | else throw("List size exceeds 20") | |
295 | + | ||
296 | + | $f0_2($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($acc0, 0), 1), 2), 3), 4), 5), 6), 7), 8), 9), 10), 11), 12), 13), 14), 15), 16), 17), 18), 19), 20) | |
297 | + | } | |
298 | + | ||
299 | + | ||
300 | + | ||
301 | + | @Callable(i) | |
302 | + | func turnDucklingIntoDuck () = { | |
303 | + | let address = toString(i.caller) | |
304 | + | let txId = toBase58String(i.transactionId) | |
305 | + | let lastIssuedDucklingTs = tryGetInteger(KGlobalIssuedTimestamp) | |
306 | + | let fiveMinInMs = ((5 * 60) * 1000) | |
307 | + | let timeDiff = (lastBlock.timestamp - (lastIssuedDucklingTs + fiveMinInMs)) | |
308 | + | if (if ((lastIssuedDucklingTs > 0)) | |
309 | + | then (0 > timeDiff) | |
310 | + | else false) | |
311 | + | then throw((("BTD: Can issue ducklings only once per 5 minutes, please wait for " + toString(fraction(timeDiff, 1, 1000))) + " sec.")) | |
312 | + | else if ((size(i.payments) != 1)) | |
313 | + | then throw("BTD: Bad payment attached (asset[s] or amount)") | |
314 | + | else { | |
315 | + | let pmt = value(assetInfo(value(value(i.payments[0]).assetId))) | |
316 | + | let blackListed = tryGetBoolean(keyBlacklisted(toBase58String(pmt.id))) | |
317 | + | if (blackListed) | |
318 | + | then throw("BTD: Can not convert blacklisted duckling!") | |
319 | + | else if ((toBigInt(percentGrowthPrecision) > getCurrentLevelBigInt(toBase58String(pmt.id)))) | |
320 | + | then throw("BTD: Duckling is not grown yet...") | |
208 | 321 | else if ((pmt.issuer != this)) | |
209 | 322 | then throw("BTD: Can use only ducklings from this dApp") | |
210 | 323 | else { | |
211 | - | let kDucklingGrown = keyDucklingGrown(toBase58String(pmt.id)) | |
212 | - | $Tuple2([BooleanEntry(kDucklingGrown, true), IntegerEntry(KGlobalIssuedTimestamp, lastBlock.timestamp)], toBase58String(pmt.id)) | |
324 | + | let call = invoke(getIncubatorAddress(), "startDuckHatching", [""], nil) | |
325 | + | if ((call == call)) | |
326 | + | then { | |
327 | + | let kDucklingGrown = keyDucklingGrown(toBase58String(pmt.id)) | |
328 | + | [BooleanEntry(kDucklingGrown, true), IntegerEntry(KGlobalIssuedTimestamp, lastBlock.timestamp)] | |
329 | + | } | |
330 | + | else throw("Strict value is not equal to itself.") | |
213 | 331 | } | |
214 | - | ||
215 | - | ||
332 | + | } | |
333 | + | } | |
216 | 334 | ||
217 | 335 | ||
218 | 336 | @Verifier(tx) |
Old | New | Differences | |
---|---|---|---|
1 | 1 | {-# STDLIB_VERSION 6 #-} | |
2 | 2 | {-# SCRIPT_TYPE ACCOUNT #-} | |
3 | 3 | {-# CONTENT_TYPE DAPP #-} | |
4 | 4 | func tryGetStringExternal (address,key) = match getString(address, key) { | |
5 | 5 | case a: String => | |
6 | 6 | a | |
7 | 7 | case _ => | |
8 | 8 | "" | |
9 | 9 | } | |
10 | 10 | ||
11 | 11 | ||
12 | - | func keyBlacklisted (ducklingId) = (("duckling_" + ducklingId) + "_blacklisted") | |
13 | - | ||
14 | - | ||
15 | 12 | func tryGetIntegerExternal (address,key) = match getInteger(address, key) { | |
16 | 13 | case b: Int => | |
17 | 14 | b | |
18 | 15 | case _ => | |
19 | 16 | 0 | |
17 | + | } | |
18 | + | ||
19 | + | ||
20 | + | func tryGetBoolean (key) = match getBoolean(key) { | |
21 | + | case b: Boolean => | |
22 | + | b | |
23 | + | case _ => | |
24 | + | false | |
20 | 25 | } | |
21 | 26 | ||
22 | 27 | ||
23 | 28 | func tryGetString (key) = tryGetStringExternal(this, key) | |
24 | 29 | ||
25 | 30 | ||
26 | 31 | func getOracle () = Address(fromBase58String(tryGetString("static_oracleAddress"))) | |
27 | 32 | ||
28 | 33 | ||
34 | + | func getDucklingPrice () = tryGetIntegerExternal(getOracle(), "static_ducklingPrice") | |
35 | + | ||
36 | + | ||
37 | + | func getEggAssetId () = fromBase58String(tryGetStringExternal(getOracle(), "static_eggAssetId")) | |
38 | + | ||
39 | + | ||
29 | 40 | func getSpiceAssetId () = fromBase58String(tryGetStringExternal(getOracle(), "static_spiceAssetId")) | |
30 | 41 | ||
31 | 42 | ||
32 | - | func | |
43 | + | func getRebirthAddress () = Address(fromBase58String(tryGetStringExternal(getOracle(), "static_rebirthAddress"))) | |
33 | 44 | ||
34 | 45 | ||
35 | - | let DUCKLINGPRICE = 10000000000 | |
46 | + | func getIncubatorAddress () = Address(fromBase58String(tryGetStringExternal(getOracle(), "static_incubatorAddress"))) | |
47 | + | ||
48 | + | ||
49 | + | func getCouponsAddress () = Address(fromBase58String(tryGetStringExternal(getOracle(), "static_couponsAddress"))) | |
50 | + | ||
51 | + | ||
52 | + | func getBurnAddress () = Address(fromBase58String(tryGetStringExternal(getOracle(), "static_burnAddress"))) | |
53 | + | ||
54 | + | ||
55 | + | let backendPubKey = fromBase58String(getStringValue(getOracle(), "static_backendPubKey")) | |
56 | + | ||
57 | + | let DUCKLINGPRICE = 100000000 | |
58 | + | ||
59 | + | let percentGrowthPrecision = 10000000000000000 | |
60 | + | ||
61 | + | let existingDuckPrecision = 100000000000000 | |
36 | 62 | ||
37 | 63 | let KGlobalIssuedTimestamp = "global_issued_timestamp" | |
38 | 64 | ||
39 | - | func keyTotalFeed (ducklingId) = (("duckling_" + ducklingId) + "_feed") | |
65 | + | func keyTotalFeed (ducklingId) = (("duckling_" + ducklingId) + "_feed_spice") | |
40 | 66 | ||
41 | 67 | ||
42 | - | func | |
68 | + | func keyAddressNonce (address) = (("address_" + address) + "_nonce") | |
43 | 69 | ||
44 | 70 | ||
45 | 71 | func keyDucklingLevel (ducklingId) = (("duckling_" + ducklingId) + "_level") | |
46 | 72 | ||
47 | 73 | ||
48 | 74 | func keyDucklingGrown (ducklingId) = (("duckling_" + ducklingId) + "_grown") | |
49 | 75 | ||
50 | 76 | ||
51 | 77 | func keyStartPercentage (ducklingId) = (("duckling_" + ducklingId) + "_startPercentage") | |
52 | 78 | ||
53 | 79 | ||
54 | 80 | func keyOwner (ducklingId) = (("duckling_" + ducklingId) + "_owner") | |
81 | + | ||
82 | + | ||
83 | + | func keyBlacklisted (ducklingId) = (("duckling_" + ducklingId) + "_blacklisted") | |
55 | 84 | ||
56 | 85 | ||
57 | 86 | func tryGetInteger (key) = { | |
58 | 87 | let val = match getInteger(this, key) { | |
59 | 88 | case b: Int => | |
60 | 89 | b | |
61 | 90 | case _ => | |
62 | 91 | 0 | |
63 | 92 | } | |
64 | 93 | val | |
65 | 94 | } | |
66 | 95 | ||
67 | 96 | ||
68 | 97 | func getBool (key) = match getBoolean(this, key) { | |
69 | 98 | case b: Boolean => | |
70 | 99 | b | |
71 | 100 | case _ => | |
72 | 101 | false | |
73 | 102 | } | |
74 | 103 | ||
75 | 104 | ||
76 | - | func getCurrentLevelInt (ducklingId) = { | |
105 | + | func getDucklingPercentage (ducklingId) = { | |
106 | + | let percentageDuckling = tryGetInteger(keyStartPercentage(ducklingId)) | |
107 | + | let base = if ((percentageDuckling != 0)) | |
108 | + | then percentageDuckling | |
109 | + | else 20 | |
110 | + | toBigInt((base * existingDuckPrecision)) | |
111 | + | } | |
112 | + | ||
113 | + | ||
114 | + | func getCurrentLevelBigInt (ducklingId) = { | |
77 | 115 | let kDuckLevel = keyDucklingLevel(ducklingId) | |
78 | 116 | match getString(kDuckLevel) { | |
79 | 117 | case s: String => | |
80 | - | parseIntValue(s) | |
81 | - | case u: Unit => | |
82 | - | 0 | |
118 | + | parseBigIntValue(s) | |
83 | 119 | case _ => | |
84 | - | throw("BGCLBI: Weird value detected!") | |
120 | + | match getBoolean((("duckling_" + ducklingId) + "_issuedByFeedCall")) { | |
121 | + | case b: Boolean => | |
122 | + | if ((b == true)) | |
123 | + | then toBigInt(0) | |
124 | + | else getDucklingPercentage(ducklingId) | |
125 | + | case _ => | |
126 | + | getDucklingPercentage(ducklingId) | |
127 | + | } | |
85 | 128 | } | |
86 | - | } | |
87 | - | ||
88 | - | ||
89 | - | func getLastFedTimestamp (ducklingId) = { | |
90 | - | let lastFedTimestamp = tryGetInteger(keyDucklingFedLastTimestamp(ducklingId)) | |
91 | - | if ((lastFedTimestamp > 0)) | |
92 | - | then lastFedTimestamp | |
93 | - | else lastBlock.timestamp | |
94 | 129 | } | |
95 | 130 | ||
96 | 131 | ||
97 | 132 | func calculateNewDucklingLevel (ducklingId,paymentAmount) = { | |
98 | 133 | let currentTs = lastBlock.timestamp | |
99 | - | let lastFedTimestampChecked = getLastFedTimestamp(ducklingId) | |
100 | - | let fedDiff = (currentTs - lastFedTimestampChecked) | |
101 | - | let growth = paymentAmount | |
102 | - | let currentLevel = getCurrentLevelInt(ducklingId) | |
103 | - | let newLevel = (currentLevel + paymentAmount) | |
104 | - | if ((newLevel > DUCKLINGPRICE)) | |
105 | - | then throw((("CBDB: You are feeding your duckling to much! Only " + toString((DUCKLINGPRICE - currentLevel))) + "needed!")) | |
106 | - | else { | |
107 | - | let result = toString(newLevel) | |
108 | - | $Tuple2(result, [("currentLevel=" + toString(currentLevel)), ("newLevel=" + toString(newLevel)), ("growth=" + toString(growth)), ("lastFedTs=" + toString(lastFedTimestampChecked)), ("fedDiff=" + toString(fedDiff))]) | |
109 | - | } | |
134 | + | let growth = fraction(toBigInt(paymentAmount), toBigInt(percentGrowthPrecision), toBigInt(DUCKLINGPRICE)) | |
135 | + | let currentLevel = getCurrentLevelBigInt(ducklingId) | |
136 | + | let newLevel = (currentLevel + growth) | |
137 | + | let result = toString(newLevel) | |
138 | + | $Tuple2(result, [("currentLevel=" + toString(currentLevel)), ("newLevel=" + toString(newLevel)), ("growth=" + toString(growth))]) | |
110 | 139 | } | |
140 | + | ||
141 | + | ||
142 | + | func getBackendProof (maxFeedAmount,userNonce,address) = makeString([toString(maxFeedAmount), toString(userNonce), address], ";") | |
111 | 143 | ||
112 | 144 | ||
113 | 145 | func asInt (value) = match value { | |
114 | 146 | case int: Int => | |
115 | 147 | int | |
116 | 148 | case _ => | |
117 | 149 | throw("BAI: wrong type, expected: Int") | |
118 | - | } | |
119 | - | ||
120 | - | ||
121 | - | func tryGetBoolean (key) = match getBoolean(key) { | |
122 | - | case b: Boolean => | |
123 | - | b | |
124 | - | case _ => | |
125 | - | false | |
126 | 150 | } | |
127 | 151 | ||
128 | 152 | ||
129 | 153 | @Callable(i) | |
130 | 154 | func configureOracle (oracle) = if ((i.caller != this)) | |
131 | 155 | then throw("BCO: admin only") | |
132 | 156 | else [StringEntry("static_oracleAddress", oracle)] | |
133 | 157 | ||
134 | 158 | ||
135 | 159 | ||
136 | 160 | @Callable(i) | |
137 | - | func issueDuckling (address,txIdStr) = if ((i.caller != getBreederAddress())) | |
161 | + | func buyDuckling () = { | |
162 | + | let exactPrice = getDucklingPrice() | |
163 | + | if ((50000000 > exactPrice)) | |
164 | + | then throw("BBD: Invalid price from oracle!") | |
165 | + | else { | |
166 | + | let amountPaidByCoupons = asInt(invoke(getCouponsAddress(), "useCoupons", [exactPrice], nil)) | |
167 | + | if ((amountPaidByCoupons == amountPaidByCoupons)) | |
168 | + | then { | |
169 | + | let leftToPay = (exactPrice - amountPaidByCoupons) | |
170 | + | let payment = if ((leftToPay != 0)) | |
171 | + | then { | |
172 | + | let firstPayment = value(i.payments[0]) | |
173 | + | if ((firstPayment.assetId != getEggAssetId())) | |
174 | + | then throw(("BBD: You can attach only EGG tokens with the following asset id: " + toBase58String(getEggAssetId()))) | |
175 | + | else if ((firstPayment.amount != leftToPay)) | |
176 | + | then throw(("BBD: To buy a perch you currently need the following amount of EGGlets: " + toString(leftToPay))) | |
177 | + | else { | |
178 | + | let burnCall = invoke(getBurnAddress(), "burnAttachedPayments", nil, [AttachedPayment(getEggAssetId(), leftToPay)]) | |
179 | + | if ((burnCall == burnCall)) | |
180 | + | then leftToPay | |
181 | + | else throw("Strict value is not equal to itself.") | |
182 | + | } | |
183 | + | } | |
184 | + | else 0 | |
185 | + | if ((payment == payment)) | |
186 | + | then { | |
187 | + | let ducklingAssetId = invoke(this, "issueFreeDuckling", [toString(i.originCaller), toBase58String(i.transactionId), 0], nil) | |
188 | + | if ((ducklingAssetId == ducklingAssetId)) | |
189 | + | then nil | |
190 | + | else throw("Strict value is not equal to itself.") | |
191 | + | } | |
192 | + | else throw("Strict value is not equal to itself.") | |
193 | + | } | |
194 | + | else throw("Strict value is not equal to itself.") | |
195 | + | } | |
196 | + | } | |
197 | + | ||
198 | + | ||
199 | + | ||
200 | + | @Callable(i) | |
201 | + | func issueFreeDuckling (address,txIdStr,percentage) = if (if ((i.caller != this)) | |
202 | + | then (i.caller != getRebirthAddress()) | |
203 | + | else false) | |
138 | 204 | then throw("BIFD: You can't issue free duckling") | |
139 | 205 | else { | |
140 | - | let asset = Issue("BABY-11111111- | |
206 | + | let asset = Issue("BABY-11111111-GZ", "", 1, 0, false, unit, height) | |
141 | 207 | let assetId = calculateAssetId(asset) | |
142 | - | $Tuple2([StringEntry((((address + "_") + txIdStr) + "_di"), toBase58String(assetId)), IntegerEntry("stats_amount", (tryGetInteger("stats_amount") + 1)), BooleanEntry((("duckling_" + toBase58String(assetId)) + "_issuedByFeedCall"), (i.caller == this)), IntegerEntry(keyStartPercentage(toBase58String(assetId)), | |
208 | + | $Tuple2([StringEntry((((address + "_") + txIdStr) + "_di"), toBase58String(assetId)), IntegerEntry("stats_amount", (tryGetInteger("stats_amount") + 1)), BooleanEntry((("duckling_" + toBase58String(assetId)) + "_issuedByFeedCall"), (i.caller == this)), IntegerEntry(keyStartPercentage(toBase58String(assetId)), percentage), StringEntry(keyOwner(toBase58String(assetId)), address), asset, ScriptTransfer(value(addressFromString(address)), 1, assetId)], toBase58String(assetId)) | |
143 | 209 | } | |
144 | 210 | ||
145 | 211 | ||
146 | 212 | ||
147 | 213 | @Callable(i) | |
148 | - | func feedDuckling (ducklingId) = { | |
214 | + | func feedDuckling (ducklingId,backendSignature,maxFeedAmount,userNonce) = { | |
149 | 215 | let addressString = toString(i.caller) | |
150 | - | let | |
151 | - | | |
152 | - | | |
153 | - | | |
154 | - | | |
155 | - | | |
156 | - | | |
157 | - | | |
158 | - | | |
159 | - | | |
160 | - | | |
161 | - | | |
162 | - | | |
163 | - | | |
164 | - | ||
165 | - | ||
166 | - | ||
167 | - | ||
168 | - | ||
169 | - | ||
170 | - | | |
171 | - | | |
172 | - | | |
173 | - | | |
174 | - | | |
175 | - | | |
176 | - | } | |
177 | - | | |
216 | + | let backendProof = getBackendProof(maxFeedAmount, userNonce, addressString) | |
217 | + | let kAddressNonce = keyAddressNonce(addressString) | |
218 | + | let currentNonce = tryGetInteger(kAddressNonce) | |
219 | + | let realDucklingId = if (!(sigVerify_8Kb(toBytes(backendProof), fromBase58String(backendSignature), backendPubKey))) | |
220 | + | then throw("BFD: Invalid proof from backend") | |
221 | + | else if (if ((size(i.payments) != 1)) | |
222 | + | then true | |
223 | + | else (value(i.payments[0]).assetId != getSpiceAssetId())) | |
224 | + | then throw("BFD: Bad payment attached (asset[s] or amount)") | |
225 | + | else if (getBool(keyDucklingGrown(ducklingId))) | |
226 | + | then throw("BFD: Duckling is already grown") | |
227 | + | else if ((userNonce != (currentNonce + 1))) | |
228 | + | then throw(((("BFD: User Nonce should be " + toString(currentNonce)) + " + 1, while received ") + toString(userNonce))) | |
229 | + | else { | |
230 | + | let blackListed = tryGetBoolean(keyBlacklisted(ducklingId)) | |
231 | + | if (blackListed) | |
232 | + | then throw("BFD: Can not feed blacklisted duckling!") | |
233 | + | else if ((ducklingId == "")) | |
234 | + | then throw("BFD: Please buy a duckling first!") | |
235 | + | else { | |
236 | + | let ducklingIdCheck = value(assetInfo(fromBase58String(ducklingId))) | |
237 | + | if ((assetBalance(i.caller, ducklingIdCheck.id) != 1)) | |
238 | + | then throw("BFD: You're not the owner of the duckling") | |
239 | + | else if ((ducklingIdCheck.issuer != this)) | |
240 | + | then throw("BFD: Cant find duckling with such id") | |
241 | + | else toBase58String(ducklingIdCheck.id) | |
242 | + | } | |
243 | + | } | |
178 | 244 | let currentPayment = value(i.payments[0]).amount | |
179 | 245 | let kNewLevel = keyDucklingLevel(realDucklingId) | |
180 | 246 | let kTotalFeed = keyTotalFeed(realDucklingId) | |
181 | 247 | let totalFeed = tryGetInteger(kTotalFeed) | |
182 | 248 | let kFeedTxStats = ((("duckling_" + realDucklingId) + "_stat_") + toString(lastBlock.timestamp)) | |
183 | - | let calculateResults = calculateNewDucklingLevel(realDucklingId, currentPayment) | |
184 | - | [IntegerEntry(kDucklingLastFedTs, lastBlock.timestamp), IntegerEntry(kTotalFeed, (totalFeed + currentPayment)), IntegerEntry(kFeedTxStats, currentPayment), StringEntry(kNewLevel, calculateResults._1), StringEntry((kFeedTxStats + "_debug"), makeString(calculateResults._2, ";"))] | |
249 | + | if ((currentPayment > maxFeedAmount)) | |
250 | + | then throw(("BFD: Cannot feed duckling for such amount, max feed amount is: " + toString(maxFeedAmount))) | |
251 | + | else { | |
252 | + | let calculateResults = calculateNewDucklingLevel(realDucklingId, (currentPayment / 100)) | |
253 | + | let ducklingOwner = tryGetString(keyOwner(realDucklingId)) | |
254 | + | if ((ducklingOwner != toString(i.caller))) | |
255 | + | then [BooleanEntry(keyBlacklisted(realDucklingId), true)] | |
256 | + | else [IntegerEntry(kAddressNonce, (currentNonce + 1)), IntegerEntry(kTotalFeed, (totalFeed + currentPayment)), IntegerEntry(kFeedTxStats, currentPayment), StringEntry(kNewLevel, calculateResults._1), StringEntry((kFeedTxStats + "_debug"), makeString(calculateResults._2, ";"))] | |
257 | + | } | |
185 | 258 | } | |
186 | 259 | ||
187 | 260 | ||
188 | 261 | ||
189 | 262 | @Callable(i) | |
190 | - | func turnDucklingIntoDuck () = if ((i.caller != getBreederAddress())) | |
191 | - | then throw("BIFD: You can't turn duckling") | |
263 | + | func fixLevels (ducklingIds) = if (if ((i.callerPublicKey != base58'GDxBbsDRmeY39quNrDsTXKJzFWbQVtjxHseF4ikxZ7n9')) | |
264 | + | then (i.caller != this) | |
265 | + | else false) | |
266 | + | then throw("BFL: Not authorized") | |
192 | 267 | else { | |
193 | - | let address = toString(i.caller) | |
194 | - | let txId = toBase58String(i.transactionId) | |
195 | - | let lastIssuedDucklingTs = tryGetInteger(KGlobalIssuedTimestamp) | |
196 | - | let fiveMinInMs = ((1 * 60) * 1000) | |
197 | - | let timeDiff = (lastBlock.timestamp - (lastIssuedDucklingTs + fiveMinInMs)) | |
198 | - | if (if ((lastIssuedDucklingTs > 0)) | |
199 | - | then (0 > timeDiff) | |
200 | - | else false) | |
201 | - | then throw((("BTD: Can issue ducklings only once per 1 minutes, please wait for " + toString(fraction(timeDiff, 1, 1000))) + " sec.")) | |
202 | - | else if ((size(i.payments) != 1)) | |
203 | - | then throw("BTD: Bad payment attached (asset[s] or amount)") | |
204 | - | else { | |
205 | - | let pmt = value(assetInfo(value(value(i.payments[0]).assetId))) | |
206 | - | if ((DUCKLINGPRICE > getCurrentLevelInt(toBase58String(pmt.id)))) | |
207 | - | then throw(((("BTD: Duckling is not grown yet..." + toString(getCurrentLevelInt(toBase58String(pmt.id)))) + " ") + toString(DUCKLINGPRICE))) | |
268 | + | let ducklingIdsList = value(split(ducklingIds, ",")) | |
269 | + | func handleId (acc,id) = { | |
270 | + | let kTotalFeed = keyTotalFeed(id) | |
271 | + | let totalFeed = tryGetInteger(kTotalFeed) | |
272 | + | let kNewLevel = keyDucklingLevel(id) | |
273 | + | let startingLevel = match getBoolean((("duckling_" + id) + "_issuedByFeedCall")) { | |
274 | + | case b: Boolean => | |
275 | + | if ((b == true)) | |
276 | + | then toBigInt(0) | |
277 | + | else getDucklingPercentage(id) | |
278 | + | case _ => | |
279 | + | getDucklingPercentage(id) | |
280 | + | } | |
281 | + | let growth = fraction(toBigInt(totalFeed), toBigInt(percentGrowthPrecision), toBigInt(DUCKLINGPRICE)) | |
282 | + | (acc ++ [StringEntry(kNewLevel, toString((startingLevel + growth)))]) | |
283 | + | } | |
284 | + | ||
285 | + | let $l = ducklingIdsList | |
286 | + | let $s = size($l) | |
287 | + | let $acc0 = nil | |
288 | + | func $f0_1 ($a,$i) = if (($i >= $s)) | |
289 | + | then $a | |
290 | + | else handleId($a, $l[$i]) | |
291 | + | ||
292 | + | func $f0_2 ($a,$i) = if (($i >= $s)) | |
293 | + | then $a | |
294 | + | else throw("List size exceeds 20") | |
295 | + | ||
296 | + | $f0_2($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($acc0, 0), 1), 2), 3), 4), 5), 6), 7), 8), 9), 10), 11), 12), 13), 14), 15), 16), 17), 18), 19), 20) | |
297 | + | } | |
298 | + | ||
299 | + | ||
300 | + | ||
301 | + | @Callable(i) | |
302 | + | func turnDucklingIntoDuck () = { | |
303 | + | let address = toString(i.caller) | |
304 | + | let txId = toBase58String(i.transactionId) | |
305 | + | let lastIssuedDucklingTs = tryGetInteger(KGlobalIssuedTimestamp) | |
306 | + | let fiveMinInMs = ((5 * 60) * 1000) | |
307 | + | let timeDiff = (lastBlock.timestamp - (lastIssuedDucklingTs + fiveMinInMs)) | |
308 | + | if (if ((lastIssuedDucklingTs > 0)) | |
309 | + | then (0 > timeDiff) | |
310 | + | else false) | |
311 | + | then throw((("BTD: Can issue ducklings only once per 5 minutes, please wait for " + toString(fraction(timeDiff, 1, 1000))) + " sec.")) | |
312 | + | else if ((size(i.payments) != 1)) | |
313 | + | then throw("BTD: Bad payment attached (asset[s] or amount)") | |
314 | + | else { | |
315 | + | let pmt = value(assetInfo(value(value(i.payments[0]).assetId))) | |
316 | + | let blackListed = tryGetBoolean(keyBlacklisted(toBase58String(pmt.id))) | |
317 | + | if (blackListed) | |
318 | + | then throw("BTD: Can not convert blacklisted duckling!") | |
319 | + | else if ((toBigInt(percentGrowthPrecision) > getCurrentLevelBigInt(toBase58String(pmt.id)))) | |
320 | + | then throw("BTD: Duckling is not grown yet...") | |
208 | 321 | else if ((pmt.issuer != this)) | |
209 | 322 | then throw("BTD: Can use only ducklings from this dApp") | |
210 | 323 | else { | |
211 | - | let kDucklingGrown = keyDucklingGrown(toBase58String(pmt.id)) | |
212 | - | $Tuple2([BooleanEntry(kDucklingGrown, true), IntegerEntry(KGlobalIssuedTimestamp, lastBlock.timestamp)], toBase58String(pmt.id)) | |
324 | + | let call = invoke(getIncubatorAddress(), "startDuckHatching", [""], nil) | |
325 | + | if ((call == call)) | |
326 | + | then { | |
327 | + | let kDucklingGrown = keyDucklingGrown(toBase58String(pmt.id)) | |
328 | + | [BooleanEntry(kDucklingGrown, true), IntegerEntry(KGlobalIssuedTimestamp, lastBlock.timestamp)] | |
329 | + | } | |
330 | + | else throw("Strict value is not equal to itself.") | |
213 | 331 | } | |
214 | - | ||
215 | - | ||
332 | + | } | |
333 | + | } | |
216 | 334 | ||
217 | 335 | ||
218 | 336 | @Verifier(tx) | |
219 | 337 | func verify () = sigVerify(tx.bodyBytes, tx.proofs[0], tx.senderPublicKey) | |
220 | 338 |
github/deemru/w8io/3ef1775 71.91 ms ◑