diff --git a/crates/algokit_test_artifacts/contracts/extra_pages_test/application.arc56.json b/crates/algokit_test_artifacts/contracts/extra_pages_test/application.arc56.json new file mode 100644 index 000000000..c067544db --- /dev/null +++ b/crates/algokit_test_artifacts/contracts/extra_pages_test/application.arc56.json @@ -0,0 +1,120 @@ +{ + "arcs": [ + 22, + 28 + ], + "bareActions": { + "call": [ + "UpdateApplication" + ], + "create": [ + "NoOp" + ] + }, + "methods": [ + { + "actions": { + "call": [ + "NoOp" + ], + "create": [] + }, + "args": [ + { + "type": "string", + "name": "name" + } + ], + "name": "hello", + "returns": { + "type": "string" + }, + "events": [], + "readonly": false, + "recommendations": {} + } + ], + "name": "HelloWorld", + "state": { + "keys": { + "box": {}, + "global": {}, + "local": {} + }, + "maps": { + "box": {}, + "global": {}, + "local": {} + }, + "schema": { + "global": { + "bytes": 0, + "ints": 0 + }, + "local": { + "bytes": 0, + "ints": 0 + } + } + }, + "structs": {}, + "byteCode": { + "approval": "CiADAQAAMRtBADKABAK+zhE2GgCOAQACI0MxGRREMRhENhoBVwIAiAAvSRUWVwYCTFCABBUffHVMULAiQ4EEIzEZjgIACQADQv/NMRgURCJDMRhEiAASIkOKAQGAB0hlbGxvLCCL/1CJigAAJESJ", + "clear": "CoEBQw==" + }, + "compilerInfo": { + "compiler": "puya", + "compilerVersion": { + "major": 4, + "minor": 3, + "patch": 3 + } + }, + "events": [], + "networks": {}, + "source": { + "approval": "I3ByYWdtYSB2ZXJzaW9uIDEwCiNwcmFnbWEgdHlwZXRyYWNrIGZhbHNlCgovLyBhbGdvcHkuYXJjNC5BUkM0Q29udHJhY3QuYXBwcm92YWxfcHJvZ3JhbSgpIC0+IHVpbnQ2NDoKbWFpbjoKICAgIGludGNibG9jayAxIDAgVE1QTF9VUERBVEFCTEUKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo1CiAgICAvLyBjbGFzcyBIZWxsb1dvcmxkKEFSQzRDb250cmFjdCk6CiAgICB0eG4gTnVtQXBwQXJncwogICAgYnogbWFpbl9iYXJlX3JvdXRpbmdANgogICAgcHVzaGJ5dGVzIDB4MDJiZWNlMTEgLy8gbWV0aG9kICJoZWxsbyhzdHJpbmcpc3RyaW5nIgogICAgdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMAogICAgbWF0Y2ggbWFpbl9oZWxsb19yb3V0ZUAzCgptYWluX2FmdGVyX2lmX2Vsc2VAMTE6CiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6NQogICAgLy8gY2xhc3MgSGVsbG9Xb3JsZChBUkM0Q29udHJhY3QpOgogICAgaW50Y18xIC8vIDAKICAgIHJldHVybgoKbWFpbl9oZWxsb19yb3V0ZUAzOgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjYKICAgIC8vIEBhYmltZXRob2QoKQogICAgdHhuIE9uQ29tcGxldGlvbgogICAgIQogICAgYXNzZXJ0IC8vIE9uQ29tcGxldGlvbiBpcyBub3QgTm9PcAogICAgdHhuIEFwcGxpY2F0aW9uSUQKICAgIGFzc2VydCAvLyBjYW4gb25seSBjYWxsIHdoZW4gbm90IGNyZWF0aW5nCiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6NQogICAgLy8gY2xhc3MgSGVsbG9Xb3JsZChBUkM0Q29udHJhY3QpOgogICAgdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMQogICAgZXh0cmFjdCAyIDAKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo2CiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIGNhbGxzdWIgaGVsbG8KICAgIGR1cAogICAgbGVuCiAgICBpdG9iCiAgICBleHRyYWN0IDYgMgogICAgc3dhcAogICAgY29uY2F0CiAgICBwdXNoYnl0ZXMgMHgxNTFmN2M3NQogICAgc3dhcAogICAgY29uY2F0CiAgICBsb2cKICAgIGludGNfMCAvLyAxCiAgICByZXR1cm4KCm1haW5fYmFyZV9yb3V0aW5nQDY6CiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6NQogICAgLy8gY2xhc3MgSGVsbG9Xb3JsZChBUkM0Q29udHJhY3QpOgogICAgcHVzaGludCA0IC8vIDQKICAgIGludGNfMSAvLyAwCiAgICB0eG4gT25Db21wbGV0aW9uCiAgICBtYXRjaCBtYWluX3VwZGF0ZUA3IG1haW5fX19hbGdvcHlfZGVmYXVsdF9jcmVhdGVAOAogICAgYiBtYWluX2FmdGVyX2lmX2Vsc2VAMTEKCm1haW5fX19hbGdvcHlfZGVmYXVsdF9jcmVhdGVAODoKICAgIHR4biBBcHBsaWNhdGlvbklECiAgICAhCiAgICBhc3NlcnQgLy8gY2FuIG9ubHkgY2FsbCB3aGVuIGNyZWF0aW5nCiAgICBpbnRjXzAgLy8gMQogICAgcmV0dXJuCgptYWluX3VwZGF0ZUA3OgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjIwNgogICAgLy8gQGJhcmVtZXRob2QoYWxsb3dfYWN0aW9ucz1bIlVwZGF0ZUFwcGxpY2F0aW9uIl0pCiAgICB0eG4gQXBwbGljYXRpb25JRAogICAgYXNzZXJ0IC8vIGNhbiBvbmx5IGNhbGwgd2hlbiBub3QgY3JlYXRpbmcKICAgIGNhbGxzdWIgdXBkYXRlCiAgICBpbnRjXzAgLy8gMQogICAgcmV0dXJuCgoKLy8gc21hcnRfY29udHJhY3RzLmhlbGxvX3dvcmxkLmNvbnRyYWN0LkhlbGxvV29ybGQuaGVsbG8obmFtZTogYnl0ZXMpIC0+IGJ5dGVzOgpoZWxsbzoKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo2LTcKICAgIC8vIEBhYmltZXRob2QoKQogICAgLy8gZGVmIGhlbGxvKHNlbGYsIG5hbWU6IFN0cmluZykgLT4gU3RyaW5nOgogICAgcHJvdG8gMSAxCiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6OAogICAgLy8gcmV0dXJuICJIZWxsbywgIiArIG5hbWUKICAgIHB1c2hieXRlcyAiSGVsbG8sICIKICAgIGZyYW1lX2RpZyAtMQogICAgY29uY2F0CiAgICByZXRzdWIKCgovLyBzbWFydF9jb250cmFjdHMuaGVsbG9fd29ybGQuY29udHJhY3QuSGVsbG9Xb3JsZC51cGRhdGUoKSAtPiB2b2lkOgp1cGRhdGU6CiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6MjA2LTIwNwogICAgLy8gQGJhcmVtZXRob2QoYWxsb3dfYWN0aW9ucz1bIlVwZGF0ZUFwcGxpY2F0aW9uIl0pCiAgICAvLyBkZWYgdXBkYXRlKHNlbGYpIC0+IE5vbmU6CiAgICBwcm90byAwIDAKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToyMDgKICAgIC8vIGFzc2VydCBUZW1wbGF0ZVZhcltib29sXSgiVVBEQVRBQkxFIiksICJDaGVjayBhcHAgaXMgdXBkYXRhYmxlIgogICAgaW50Y18yIC8vIFRNUExfVVBEQVRBQkxFCiAgICBhc3NlcnQgLy8gQ2hlY2sgYXBwIGlzIHVwZGF0YWJsZQogICAgcmV0c3ViCg==", + "clear": "I3ByYWdtYSB2ZXJzaW9uIDEwCiNwcmFnbWEgdHlwZXRyYWNrIGZhbHNlCgovLyBhbGdvcHkuYXJjNC5BUkM0Q29udHJhY3QuY2xlYXJfc3RhdGVfcHJvZ3JhbSgpIC0+IHVpbnQ2NDoKbWFpbjoKICAgIHB1c2hpbnQgMSAvLyAxCiAgICByZXR1cm4K" + }, + "sourceInfo": { + "approval": { + "pcOffsetMethod": "cblocks", + "sourceInfo": [ + { + "pc": [ + 103 + ], + "errorMessage": "Check app is updatable" + }, + { + "pc": [ + 23 + ], + "errorMessage": "OnCompletion is not NoOp" + }, + { + "pc": [ + 72 + ], + "errorMessage": "can only call when creating" + }, + { + "pc": [ + 26, + 77 + ], + "errorMessage": "can only call when not creating" + } + ] + }, + "clear": { + "pcOffsetMethod": "none", + "sourceInfo": [] + } + }, + "templateVariables": { + "UPDATABLE": { + "type": "AVMUint64" + } + } +} \ No newline at end of file diff --git a/crates/algokit_test_artifacts/contracts/extra_pages_test/large.arc56.json b/crates/algokit_test_artifacts/contracts/extra_pages_test/large.arc56.json new file mode 100644 index 000000000..9229fb409 --- /dev/null +++ b/crates/algokit_test_artifacts/contracts/extra_pages_test/large.arc56.json @@ -0,0 +1,1247 @@ +{ + "name": "HelloWorld", + "structs": {}, + "methods": [ + { + "name": "hello", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello2", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello3", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello4", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello5", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello6", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello7", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello8", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello9", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello10", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello11", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello12", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello13", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello14", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello15", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello16", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello17", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello18", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello19", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello20", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello21", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello22", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello23", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello24", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello25", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello26", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello27", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello28", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello29", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello30", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello31", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello32", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello33", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello34", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello35", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello36", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello37", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello38", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello39", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello40", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello41", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello42", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello43", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello44", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello45", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello46", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello47", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello48", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello49", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "hello50", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + } + ], + "arcs": [ + 22, + 28 + ], + "networks": {}, + "state": { + "schema": { + "global": { + "ints": 0, + "bytes": 0 + }, + "local": { + "ints": 0, + "bytes": 0 + } + }, + "keys": { + "global": {}, + "local": {}, + "box": {} + }, + "maps": { + "global": {}, + "local": {}, + "box": {} + } + }, + "bareActions": { + "create": [ + "NoOp" + ], + "call": [ + "UpdateApplication" + ] + }, + "sourceInfo": { + "approval": { + "sourceInfo": [ + { + "pc": [ + 2296 + ], + "errorMessage": "Check app is updatable" + }, + { + "pc": [ + 367, + 397, + 427, + 457, + 487, + 517, + 547, + 577, + 607, + 637, + 667, + 697, + 727, + 757, + 787, + 817, + 847, + 877, + 907, + 937, + 967, + 997, + 1027, + 1057, + 1087, + 1117, + 1147, + 1177, + 1207, + 1237, + 1267, + 1297, + 1327, + 1357, + 1387, + 1417, + 1447, + 1477, + 1507, + 1537, + 1567, + 1597, + 1627, + 1657, + 1687, + 1717, + 1747, + 1777, + 1807, + 1837 + ], + "errorMessage": "OnCompletion is not NoOp" + }, + { + "pc": [ + 1881 + ], + "errorMessage": "can only call when creating" + }, + { + "pc": [ + 370, + 400, + 430, + 460, + 490, + 520, + 550, + 580, + 610, + 640, + 670, + 700, + 730, + 760, + 790, + 820, + 850, + 880, + 910, + 940, + 970, + 1000, + 1030, + 1060, + 1090, + 1120, + 1150, + 1180, + 1210, + 1240, + 1270, + 1300, + 1330, + 1360, + 1390, + 1420, + 1450, + 1480, + 1510, + 1540, + 1570, + 1600, + 1630, + 1660, + 1690, + 1720, + 1750, + 1780, + 1810, + 1840, + 1886 + ], + "errorMessage": "can only call when not creating" + } + ], + "pcOffsetMethod": "cblocks" + }, + "clear": { + "sourceInfo": [], + "pcOffsetMethod": "none" + } + }, + "source": { + "approval": "I3ByYWdtYSB2ZXJzaW9uIDEwCiNwcmFnbWEgdHlwZXRyYWNrIGZhbHNlCgovLyBhbGdvcHkuYXJjNC5BUkM0Q29udHJhY3QuYXBwcm92YWxfcHJvZ3JhbSgpIC0+IHVpbnQ2NDoKbWFpbjoKICAgIGludGNibG9jayAxIDAgVE1QTF9VUERBVEFCTEUKICAgIGJ5dGVjYmxvY2sgMHgxNTFmN2M3NSAiSGVsbG8sICIKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo1CiAgICAvLyBjbGFzcyBIZWxsb1dvcmxkKEFSQzRDb250cmFjdCk6CiAgICB0eG4gTnVtQXBwQXJncwogICAgYnogbWFpbl9iYXJlX3JvdXRpbmdANTUKICAgIHB1c2hieXRlc3MgMHgwMmJlY2UxMSAweDZmOTc3NWMzIDB4MWM2NTk2YTkgMHgwYjBjYzI3OSAweGQ4ZjMxYTY2IDB4ZDBjYzQzODYgMHgzZmUxYmZiYSAweGFiN2JiZjQwIDB4ZjY2YzE2NDkgMHg0MmYxYWI4ZCAweDY2YWNmMTk4IDB4ZWNlYzFkNTkgMHhmNjA5Njk2MCAweDA5ZTc5MjAzIDB4MDNhMTZiZjEgMHg1NDIwMWY4OCAweDEzMjdkZWFiIDB4ZjhmNWM0ODUgMHhjMzg0MjU1MyAweGVmNWY4OTc2IDB4MjgwM2UxOTcgMHgyODA3MmE4NiAweDlhNjA2OTI5IDB4YTU2NTBhZTQgMHg5MWQ5YjJiMyAweGVlODdhYmYzIDB4Mjc3OTAwNzIgMHg3ZmZjZDQxNCAweGJjMzRjZTcyIDB4ZWVhOTU3YTkgMHgxOTY1MjI3NyAweDQzNDNlMWRmIDB4MmE1ZjhjNGIgMHgzZGViZjliOCAweGYwOTI1Njg4IDB4MDNlZjE3OWYgMHhiMDk5MGMwZSAweGM2ODZiZmMxIDB4NTg4MmM2OTYgMHg0YTRjZjljZSAweGY4N2Q5M2MzIDB4ZjNmNWM4YzMgMHg5OTQyZTY4YSAweDE1MzVkYjZjIDB4ZjY5NDYwOGQgMHhkMWYzOGI4NSAweDljNTQ1MmFhIDB4MGY5OGQxYWQgMHgxYjYyMjQxMyAweGE5ZTRiYzFkIC8vIG1ldGhvZCAiaGVsbG8oc3RyaW5nKXN0cmluZyIsIG1ldGhvZCAiaGVsbG8yKHN0cmluZylzdHJpbmciLCBtZXRob2QgImhlbGxvMyhzdHJpbmcpc3RyaW5nIiwgbWV0aG9kICJoZWxsbzQoc3RyaW5nKXN0cmluZyIsIG1ldGhvZCAiaGVsbG81KHN0cmluZylzdHJpbmciLCBtZXRob2QgImhlbGxvNihzdHJpbmcpc3RyaW5nIiwgbWV0aG9kICJoZWxsbzcoc3RyaW5nKXN0cmluZyIsIG1ldGhvZCAiaGVsbG84KHN0cmluZylzdHJpbmciLCBtZXRob2QgImhlbGxvOShzdHJpbmcpc3RyaW5nIiwgbWV0aG9kICJoZWxsbzEwKHN0cmluZylzdHJpbmciLCBtZXRob2QgImhlbGxvMTEoc3RyaW5nKXN0cmluZyIsIG1ldGhvZCAiaGVsbG8xMihzdHJpbmcpc3RyaW5nIiwgbWV0aG9kICJoZWxsbzEzKHN0cmluZylzdHJpbmciLCBtZXRob2QgImhlbGxvMTQoc3RyaW5nKXN0cmluZyIsIG1ldGhvZCAiaGVsbG8xNShzdHJpbmcpc3RyaW5nIiwgbWV0aG9kICJoZWxsbzE2KHN0cmluZylzdHJpbmciLCBtZXRob2QgImhlbGxvMTcoc3RyaW5nKXN0cmluZyIsIG1ldGhvZCAiaGVsbG8xOChzdHJpbmcpc3RyaW5nIiwgbWV0aG9kICJoZWxsbzE5KHN0cmluZylzdHJpbmciLCBtZXRob2QgImhlbGxvMjAoc3RyaW5nKXN0cmluZyIsIG1ldGhvZCAiaGVsbG8yMShzdHJpbmcpc3RyaW5nIiwgbWV0aG9kICJoZWxsbzIyKHN0cmluZylzdHJpbmciLCBtZXRob2QgImhlbGxvMjMoc3RyaW5nKXN0cmluZyIsIG1ldGhvZCAiaGVsbG8yNChzdHJpbmcpc3RyaW5nIiwgbWV0aG9kICJoZWxsbzI1KHN0cmluZylzdHJpbmciLCBtZXRob2QgImhlbGxvMjYoc3RyaW5nKXN0cmluZyIsIG1ldGhvZCAiaGVsbG8yNyhzdHJpbmcpc3RyaW5nIiwgbWV0aG9kICJoZWxsbzI4KHN0cmluZylzdHJpbmciLCBtZXRob2QgImhlbGxvMjkoc3RyaW5nKXN0cmluZyIsIG1ldGhvZCAiaGVsbG8zMChzdHJpbmcpc3RyaW5nIiwgbWV0aG9kICJoZWxsbzMxKHN0cmluZylzdHJpbmciLCBtZXRob2QgImhlbGxvMzIoc3RyaW5nKXN0cmluZyIsIG1ldGhvZCAiaGVsbG8zMyhzdHJpbmcpc3RyaW5nIiwgbWV0aG9kICJoZWxsbzM0KHN0cmluZylzdHJpbmciLCBtZXRob2QgImhlbGxvMzUoc3RyaW5nKXN0cmluZyIsIG1ldGhvZCAiaGVsbG8zNihzdHJpbmcpc3RyaW5nIiwgbWV0aG9kICJoZWxsbzM3KHN0cmluZylzdHJpbmciLCBtZXRob2QgImhlbGxvMzgoc3RyaW5nKXN0cmluZyIsIG1ldGhvZCAiaGVsbG8zOShzdHJpbmcpc3RyaW5nIiwgbWV0aG9kICJoZWxsbzQwKHN0cmluZylzdHJpbmciLCBtZXRob2QgImhlbGxvNDEoc3RyaW5nKXN0cmluZyIsIG1ldGhvZCAiaGVsbG80MihzdHJpbmcpc3RyaW5nIiwgbWV0aG9kICJoZWxsbzQzKHN0cmluZylzdHJpbmciLCBtZXRob2QgImhlbGxvNDQoc3RyaW5nKXN0cmluZyIsIG1ldGhvZCAiaGVsbG80NShzdHJpbmcpc3RyaW5nIiwgbWV0aG9kICJoZWxsbzQ2KHN0cmluZylzdHJpbmciLCBtZXRob2QgImhlbGxvNDcoc3RyaW5nKXN0cmluZyIsIG1ldGhvZCAiaGVsbG80OChzdHJpbmcpc3RyaW5nIiwgbWV0aG9kICJoZWxsbzQ5KHN0cmluZylzdHJpbmciLCBtZXRob2QgImhlbGxvNTAoc3RyaW5nKXN0cmluZyIKICAgIHR4bmEgQXBwbGljYXRpb25BcmdzIDAKICAgIG1hdGNoIG1haW5faGVsbG9fcm91dGVAMyBtYWluX2hlbGxvMl9yb3V0ZUA0IG1haW5faGVsbG8zX3JvdXRlQDUgbWFpbl9oZWxsbzRfcm91dGVANiBtYWluX2hlbGxvNV9yb3V0ZUA3IG1haW5faGVsbG82X3JvdXRlQDggbWFpbl9oZWxsbzdfcm91dGVAOSBtYWluX2hlbGxvOF9yb3V0ZUAxMCBtYWluX2hlbGxvOV9yb3V0ZUAxMSBtYWluX2hlbGxvMTBfcm91dGVAMTIgbWFpbl9oZWxsbzExX3JvdXRlQDEzIG1haW5faGVsbG8xMl9yb3V0ZUAxNCBtYWluX2hlbGxvMTNfcm91dGVAMTUgbWFpbl9oZWxsbzE0X3JvdXRlQDE2IG1haW5faGVsbG8xNV9yb3V0ZUAxNyBtYWluX2hlbGxvMTZfcm91dGVAMTggbWFpbl9oZWxsbzE3X3JvdXRlQDE5IG1haW5faGVsbG8xOF9yb3V0ZUAyMCBtYWluX2hlbGxvMTlfcm91dGVAMjEgbWFpbl9oZWxsbzIwX3JvdXRlQDIyIG1haW5faGVsbG8yMV9yb3V0ZUAyMyBtYWluX2hlbGxvMjJfcm91dGVAMjQgbWFpbl9oZWxsbzIzX3JvdXRlQDI1IG1haW5faGVsbG8yNF9yb3V0ZUAyNiBtYWluX2hlbGxvMjVfcm91dGVAMjcgbWFpbl9oZWxsbzI2X3JvdXRlQDI4IG1haW5faGVsbG8yN19yb3V0ZUAyOSBtYWluX2hlbGxvMjhfcm91dGVAMzAgbWFpbl9oZWxsbzI5X3JvdXRlQDMxIG1haW5faGVsbG8zMF9yb3V0ZUAzMiBtYWluX2hlbGxvMzFfcm91dGVAMzMgbWFpbl9oZWxsbzMyX3JvdXRlQDM0IG1haW5faGVsbG8zM19yb3V0ZUAzNSBtYWluX2hlbGxvMzRfcm91dGVAMzYgbWFpbl9oZWxsbzM1X3JvdXRlQDM3IG1haW5faGVsbG8zNl9yb3V0ZUAzOCBtYWluX2hlbGxvMzdfcm91dGVAMzkgbWFpbl9oZWxsbzM4X3JvdXRlQDQwIG1haW5faGVsbG8zOV9yb3V0ZUA0MSBtYWluX2hlbGxvNDBfcm91dGVANDIgbWFpbl9oZWxsbzQxX3JvdXRlQDQzIG1haW5faGVsbG80Ml9yb3V0ZUA0NCBtYWluX2hlbGxvNDNfcm91dGVANDUgbWFpbl9oZWxsbzQ0X3JvdXRlQDQ2IG1haW5faGVsbG80NV9yb3V0ZUA0NyBtYWluX2hlbGxvNDZfcm91dGVANDggbWFpbl9oZWxsbzQ3X3JvdXRlQDQ5IG1haW5faGVsbG80OF9yb3V0ZUA1MCBtYWluX2hlbGxvNDlfcm91dGVANTEgbWFpbl9oZWxsbzUwX3JvdXRlQDUyCgptYWluX2FmdGVyX2lmX2Vsc2VANjA6CiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6NQogICAgLy8gY2xhc3MgSGVsbG9Xb3JsZChBUkM0Q29udHJhY3QpOgogICAgaW50Y18xIC8vIDAKICAgIHJldHVybgoKbWFpbl9oZWxsbzUwX3JvdXRlQDUyOgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjIwMgogICAgLy8gQGFiaW1ldGhvZCgpCiAgICB0eG4gT25Db21wbGV0aW9uCiAgICAhCiAgICBhc3NlcnQgLy8gT25Db21wbGV0aW9uIGlzIG5vdCBOb09wCiAgICB0eG4gQXBwbGljYXRpb25JRAogICAgYXNzZXJ0IC8vIGNhbiBvbmx5IGNhbGwgd2hlbiBub3QgY3JlYXRpbmcKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo1CiAgICAvLyBjbGFzcyBIZWxsb1dvcmxkKEFSQzRDb250cmFjdCk6CiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAxCiAgICBleHRyYWN0IDIgMAogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjIwMgogICAgLy8gQGFiaW1ldGhvZCgpCiAgICBjYWxsc3ViIGhlbGxvNTAKICAgIGR1cAogICAgbGVuCiAgICBpdG9iCiAgICBleHRyYWN0IDYgMgogICAgc3dhcAogICAgY29uY2F0CiAgICBieXRlY18wIC8vIDB4MTUxZjdjNzUKICAgIHN3YXAKICAgIGNvbmNhdAogICAgbG9nCiAgICBpbnRjXzAgLy8gMQogICAgcmV0dXJuCgptYWluX2hlbGxvNDlfcm91dGVANTE6CiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6MTk4CiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIHR4biBPbkNvbXBsZXRpb24KICAgICEKICAgIGFzc2VydCAvLyBPbkNvbXBsZXRpb24gaXMgbm90IE5vT3AKICAgIHR4biBBcHBsaWNhdGlvbklECiAgICBhc3NlcnQgLy8gY2FuIG9ubHkgY2FsbCB3aGVuIG5vdCBjcmVhdGluZwogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjUKICAgIC8vIGNsYXNzIEhlbGxvV29ybGQoQVJDNENvbnRyYWN0KToKICAgIHR4bmEgQXBwbGljYXRpb25BcmdzIDEKICAgIGV4dHJhY3QgMiAwCiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6MTk4CiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIGNhbGxzdWIgaGVsbG80OQogICAgZHVwCiAgICBsZW4KICAgIGl0b2IKICAgIGV4dHJhY3QgNiAyCiAgICBzd2FwCiAgICBjb25jYXQKICAgIGJ5dGVjXzAgLy8gMHgxNTFmN2M3NQogICAgc3dhcAogICAgY29uY2F0CiAgICBsb2cKICAgIGludGNfMCAvLyAxCiAgICByZXR1cm4KCm1haW5faGVsbG80OF9yb3V0ZUA1MDoKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToxOTQKICAgIC8vIEBhYmltZXRob2QoKQogICAgdHhuIE9uQ29tcGxldGlvbgogICAgIQogICAgYXNzZXJ0IC8vIE9uQ29tcGxldGlvbiBpcyBub3QgTm9PcAogICAgdHhuIEFwcGxpY2F0aW9uSUQKICAgIGFzc2VydCAvLyBjYW4gb25seSBjYWxsIHdoZW4gbm90IGNyZWF0aW5nCiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6NQogICAgLy8gY2xhc3MgSGVsbG9Xb3JsZChBUkM0Q29udHJhY3QpOgogICAgdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMQogICAgZXh0cmFjdCAyIDAKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToxOTQKICAgIC8vIEBhYmltZXRob2QoKQogICAgY2FsbHN1YiBoZWxsbzQ4CiAgICBkdXAKICAgIGxlbgogICAgaXRvYgogICAgZXh0cmFjdCA2IDIKICAgIHN3YXAKICAgIGNvbmNhdAogICAgYnl0ZWNfMCAvLyAweDE1MWY3Yzc1CiAgICBzd2FwCiAgICBjb25jYXQKICAgIGxvZwogICAgaW50Y18wIC8vIDEKICAgIHJldHVybgoKbWFpbl9oZWxsbzQ3X3JvdXRlQDQ5OgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjE5MAogICAgLy8gQGFiaW1ldGhvZCgpCiAgICB0eG4gT25Db21wbGV0aW9uCiAgICAhCiAgICBhc3NlcnQgLy8gT25Db21wbGV0aW9uIGlzIG5vdCBOb09wCiAgICB0eG4gQXBwbGljYXRpb25JRAogICAgYXNzZXJ0IC8vIGNhbiBvbmx5IGNhbGwgd2hlbiBub3QgY3JlYXRpbmcKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo1CiAgICAvLyBjbGFzcyBIZWxsb1dvcmxkKEFSQzRDb250cmFjdCk6CiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAxCiAgICBleHRyYWN0IDIgMAogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjE5MAogICAgLy8gQGFiaW1ldGhvZCgpCiAgICBjYWxsc3ViIGhlbGxvNDcKICAgIGR1cAogICAgbGVuCiAgICBpdG9iCiAgICBleHRyYWN0IDYgMgogICAgc3dhcAogICAgY29uY2F0CiAgICBieXRlY18wIC8vIDB4MTUxZjdjNzUKICAgIHN3YXAKICAgIGNvbmNhdAogICAgbG9nCiAgICBpbnRjXzAgLy8gMQogICAgcmV0dXJuCgptYWluX2hlbGxvNDZfcm91dGVANDg6CiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6MTg2CiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIHR4biBPbkNvbXBsZXRpb24KICAgICEKICAgIGFzc2VydCAvLyBPbkNvbXBsZXRpb24gaXMgbm90IE5vT3AKICAgIHR4biBBcHBsaWNhdGlvbklECiAgICBhc3NlcnQgLy8gY2FuIG9ubHkgY2FsbCB3aGVuIG5vdCBjcmVhdGluZwogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjUKICAgIC8vIGNsYXNzIEhlbGxvV29ybGQoQVJDNENvbnRyYWN0KToKICAgIHR4bmEgQXBwbGljYXRpb25BcmdzIDEKICAgIGV4dHJhY3QgMiAwCiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6MTg2CiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIGNhbGxzdWIgaGVsbG80NgogICAgZHVwCiAgICBsZW4KICAgIGl0b2IKICAgIGV4dHJhY3QgNiAyCiAgICBzd2FwCiAgICBjb25jYXQKICAgIGJ5dGVjXzAgLy8gMHgxNTFmN2M3NQogICAgc3dhcAogICAgY29uY2F0CiAgICBsb2cKICAgIGludGNfMCAvLyAxCiAgICByZXR1cm4KCm1haW5faGVsbG80NV9yb3V0ZUA0NzoKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToxODIKICAgIC8vIEBhYmltZXRob2QoKQogICAgdHhuIE9uQ29tcGxldGlvbgogICAgIQogICAgYXNzZXJ0IC8vIE9uQ29tcGxldGlvbiBpcyBub3QgTm9PcAogICAgdHhuIEFwcGxpY2F0aW9uSUQKICAgIGFzc2VydCAvLyBjYW4gb25seSBjYWxsIHdoZW4gbm90IGNyZWF0aW5nCiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6NQogICAgLy8gY2xhc3MgSGVsbG9Xb3JsZChBUkM0Q29udHJhY3QpOgogICAgdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMQogICAgZXh0cmFjdCAyIDAKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToxODIKICAgIC8vIEBhYmltZXRob2QoKQogICAgY2FsbHN1YiBoZWxsbzQ1CiAgICBkdXAKICAgIGxlbgogICAgaXRvYgogICAgZXh0cmFjdCA2IDIKICAgIHN3YXAKICAgIGNvbmNhdAogICAgYnl0ZWNfMCAvLyAweDE1MWY3Yzc1CiAgICBzd2FwCiAgICBjb25jYXQKICAgIGxvZwogICAgaW50Y18wIC8vIDEKICAgIHJldHVybgoKbWFpbl9oZWxsbzQ0X3JvdXRlQDQ2OgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjE3OAogICAgLy8gQGFiaW1ldGhvZCgpCiAgICB0eG4gT25Db21wbGV0aW9uCiAgICAhCiAgICBhc3NlcnQgLy8gT25Db21wbGV0aW9uIGlzIG5vdCBOb09wCiAgICB0eG4gQXBwbGljYXRpb25JRAogICAgYXNzZXJ0IC8vIGNhbiBvbmx5IGNhbGwgd2hlbiBub3QgY3JlYXRpbmcKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo1CiAgICAvLyBjbGFzcyBIZWxsb1dvcmxkKEFSQzRDb250cmFjdCk6CiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAxCiAgICBleHRyYWN0IDIgMAogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjE3OAogICAgLy8gQGFiaW1ldGhvZCgpCiAgICBjYWxsc3ViIGhlbGxvNDQKICAgIGR1cAogICAgbGVuCiAgICBpdG9iCiAgICBleHRyYWN0IDYgMgogICAgc3dhcAogICAgY29uY2F0CiAgICBieXRlY18wIC8vIDB4MTUxZjdjNzUKICAgIHN3YXAKICAgIGNvbmNhdAogICAgbG9nCiAgICBpbnRjXzAgLy8gMQogICAgcmV0dXJuCgptYWluX2hlbGxvNDNfcm91dGVANDU6CiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6MTc0CiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIHR4biBPbkNvbXBsZXRpb24KICAgICEKICAgIGFzc2VydCAvLyBPbkNvbXBsZXRpb24gaXMgbm90IE5vT3AKICAgIHR4biBBcHBsaWNhdGlvbklECiAgICBhc3NlcnQgLy8gY2FuIG9ubHkgY2FsbCB3aGVuIG5vdCBjcmVhdGluZwogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjUKICAgIC8vIGNsYXNzIEhlbGxvV29ybGQoQVJDNENvbnRyYWN0KToKICAgIHR4bmEgQXBwbGljYXRpb25BcmdzIDEKICAgIGV4dHJhY3QgMiAwCiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6MTc0CiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIGNhbGxzdWIgaGVsbG80MwogICAgZHVwCiAgICBsZW4KICAgIGl0b2IKICAgIGV4dHJhY3QgNiAyCiAgICBzd2FwCiAgICBjb25jYXQKICAgIGJ5dGVjXzAgLy8gMHgxNTFmN2M3NQogICAgc3dhcAogICAgY29uY2F0CiAgICBsb2cKICAgIGludGNfMCAvLyAxCiAgICByZXR1cm4KCm1haW5faGVsbG80Ml9yb3V0ZUA0NDoKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToxNzAKICAgIC8vIEBhYmltZXRob2QoKQogICAgdHhuIE9uQ29tcGxldGlvbgogICAgIQogICAgYXNzZXJ0IC8vIE9uQ29tcGxldGlvbiBpcyBub3QgTm9PcAogICAgdHhuIEFwcGxpY2F0aW9uSUQKICAgIGFzc2VydCAvLyBjYW4gb25seSBjYWxsIHdoZW4gbm90IGNyZWF0aW5nCiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6NQogICAgLy8gY2xhc3MgSGVsbG9Xb3JsZChBUkM0Q29udHJhY3QpOgogICAgdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMQogICAgZXh0cmFjdCAyIDAKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToxNzAKICAgIC8vIEBhYmltZXRob2QoKQogICAgY2FsbHN1YiBoZWxsbzQyCiAgICBkdXAKICAgIGxlbgogICAgaXRvYgogICAgZXh0cmFjdCA2IDIKICAgIHN3YXAKICAgIGNvbmNhdAogICAgYnl0ZWNfMCAvLyAweDE1MWY3Yzc1CiAgICBzd2FwCiAgICBjb25jYXQKICAgIGxvZwogICAgaW50Y18wIC8vIDEKICAgIHJldHVybgoKbWFpbl9oZWxsbzQxX3JvdXRlQDQzOgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjE2NgogICAgLy8gQGFiaW1ldGhvZCgpCiAgICB0eG4gT25Db21wbGV0aW9uCiAgICAhCiAgICBhc3NlcnQgLy8gT25Db21wbGV0aW9uIGlzIG5vdCBOb09wCiAgICB0eG4gQXBwbGljYXRpb25JRAogICAgYXNzZXJ0IC8vIGNhbiBvbmx5IGNhbGwgd2hlbiBub3QgY3JlYXRpbmcKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo1CiAgICAvLyBjbGFzcyBIZWxsb1dvcmxkKEFSQzRDb250cmFjdCk6CiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAxCiAgICBleHRyYWN0IDIgMAogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjE2NgogICAgLy8gQGFiaW1ldGhvZCgpCiAgICBjYWxsc3ViIGhlbGxvNDEKICAgIGR1cAogICAgbGVuCiAgICBpdG9iCiAgICBleHRyYWN0IDYgMgogICAgc3dhcAogICAgY29uY2F0CiAgICBieXRlY18wIC8vIDB4MTUxZjdjNzUKICAgIHN3YXAKICAgIGNvbmNhdAogICAgbG9nCiAgICBpbnRjXzAgLy8gMQogICAgcmV0dXJuCgptYWluX2hlbGxvNDBfcm91dGVANDI6CiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6MTYyCiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIHR4biBPbkNvbXBsZXRpb24KICAgICEKICAgIGFzc2VydCAvLyBPbkNvbXBsZXRpb24gaXMgbm90IE5vT3AKICAgIHR4biBBcHBsaWNhdGlvbklECiAgICBhc3NlcnQgLy8gY2FuIG9ubHkgY2FsbCB3aGVuIG5vdCBjcmVhdGluZwogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjUKICAgIC8vIGNsYXNzIEhlbGxvV29ybGQoQVJDNENvbnRyYWN0KToKICAgIHR4bmEgQXBwbGljYXRpb25BcmdzIDEKICAgIGV4dHJhY3QgMiAwCiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6MTYyCiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIGNhbGxzdWIgaGVsbG80MAogICAgZHVwCiAgICBsZW4KICAgIGl0b2IKICAgIGV4dHJhY3QgNiAyCiAgICBzd2FwCiAgICBjb25jYXQKICAgIGJ5dGVjXzAgLy8gMHgxNTFmN2M3NQogICAgc3dhcAogICAgY29uY2F0CiAgICBsb2cKICAgIGludGNfMCAvLyAxCiAgICByZXR1cm4KCm1haW5faGVsbG8zOV9yb3V0ZUA0MToKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToxNTgKICAgIC8vIEBhYmltZXRob2QoKQogICAgdHhuIE9uQ29tcGxldGlvbgogICAgIQogICAgYXNzZXJ0IC8vIE9uQ29tcGxldGlvbiBpcyBub3QgTm9PcAogICAgdHhuIEFwcGxpY2F0aW9uSUQKICAgIGFzc2VydCAvLyBjYW4gb25seSBjYWxsIHdoZW4gbm90IGNyZWF0aW5nCiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6NQogICAgLy8gY2xhc3MgSGVsbG9Xb3JsZChBUkM0Q29udHJhY3QpOgogICAgdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMQogICAgZXh0cmFjdCAyIDAKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToxNTgKICAgIC8vIEBhYmltZXRob2QoKQogICAgY2FsbHN1YiBoZWxsbzM5CiAgICBkdXAKICAgIGxlbgogICAgaXRvYgogICAgZXh0cmFjdCA2IDIKICAgIHN3YXAKICAgIGNvbmNhdAogICAgYnl0ZWNfMCAvLyAweDE1MWY3Yzc1CiAgICBzd2FwCiAgICBjb25jYXQKICAgIGxvZwogICAgaW50Y18wIC8vIDEKICAgIHJldHVybgoKbWFpbl9oZWxsbzM4X3JvdXRlQDQwOgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjE1NAogICAgLy8gQGFiaW1ldGhvZCgpCiAgICB0eG4gT25Db21wbGV0aW9uCiAgICAhCiAgICBhc3NlcnQgLy8gT25Db21wbGV0aW9uIGlzIG5vdCBOb09wCiAgICB0eG4gQXBwbGljYXRpb25JRAogICAgYXNzZXJ0IC8vIGNhbiBvbmx5IGNhbGwgd2hlbiBub3QgY3JlYXRpbmcKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo1CiAgICAvLyBjbGFzcyBIZWxsb1dvcmxkKEFSQzRDb250cmFjdCk6CiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAxCiAgICBleHRyYWN0IDIgMAogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjE1NAogICAgLy8gQGFiaW1ldGhvZCgpCiAgICBjYWxsc3ViIGhlbGxvMzgKICAgIGR1cAogICAgbGVuCiAgICBpdG9iCiAgICBleHRyYWN0IDYgMgogICAgc3dhcAogICAgY29uY2F0CiAgICBieXRlY18wIC8vIDB4MTUxZjdjNzUKICAgIHN3YXAKICAgIGNvbmNhdAogICAgbG9nCiAgICBpbnRjXzAgLy8gMQogICAgcmV0dXJuCgptYWluX2hlbGxvMzdfcm91dGVAMzk6CiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6MTUwCiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIHR4biBPbkNvbXBsZXRpb24KICAgICEKICAgIGFzc2VydCAvLyBPbkNvbXBsZXRpb24gaXMgbm90IE5vT3AKICAgIHR4biBBcHBsaWNhdGlvbklECiAgICBhc3NlcnQgLy8gY2FuIG9ubHkgY2FsbCB3aGVuIG5vdCBjcmVhdGluZwogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjUKICAgIC8vIGNsYXNzIEhlbGxvV29ybGQoQVJDNENvbnRyYWN0KToKICAgIHR4bmEgQXBwbGljYXRpb25BcmdzIDEKICAgIGV4dHJhY3QgMiAwCiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6MTUwCiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIGNhbGxzdWIgaGVsbG8zNwogICAgZHVwCiAgICBsZW4KICAgIGl0b2IKICAgIGV4dHJhY3QgNiAyCiAgICBzd2FwCiAgICBjb25jYXQKICAgIGJ5dGVjXzAgLy8gMHgxNTFmN2M3NQogICAgc3dhcAogICAgY29uY2F0CiAgICBsb2cKICAgIGludGNfMCAvLyAxCiAgICByZXR1cm4KCm1haW5faGVsbG8zNl9yb3V0ZUAzODoKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToxNDYKICAgIC8vIEBhYmltZXRob2QoKQogICAgdHhuIE9uQ29tcGxldGlvbgogICAgIQogICAgYXNzZXJ0IC8vIE9uQ29tcGxldGlvbiBpcyBub3QgTm9PcAogICAgdHhuIEFwcGxpY2F0aW9uSUQKICAgIGFzc2VydCAvLyBjYW4gb25seSBjYWxsIHdoZW4gbm90IGNyZWF0aW5nCiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6NQogICAgLy8gY2xhc3MgSGVsbG9Xb3JsZChBUkM0Q29udHJhY3QpOgogICAgdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMQogICAgZXh0cmFjdCAyIDAKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToxNDYKICAgIC8vIEBhYmltZXRob2QoKQogICAgY2FsbHN1YiBoZWxsbzM2CiAgICBkdXAKICAgIGxlbgogICAgaXRvYgogICAgZXh0cmFjdCA2IDIKICAgIHN3YXAKICAgIGNvbmNhdAogICAgYnl0ZWNfMCAvLyAweDE1MWY3Yzc1CiAgICBzd2FwCiAgICBjb25jYXQKICAgIGxvZwogICAgaW50Y18wIC8vIDEKICAgIHJldHVybgoKbWFpbl9oZWxsbzM1X3JvdXRlQDM3OgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjE0MgogICAgLy8gQGFiaW1ldGhvZCgpCiAgICB0eG4gT25Db21wbGV0aW9uCiAgICAhCiAgICBhc3NlcnQgLy8gT25Db21wbGV0aW9uIGlzIG5vdCBOb09wCiAgICB0eG4gQXBwbGljYXRpb25JRAogICAgYXNzZXJ0IC8vIGNhbiBvbmx5IGNhbGwgd2hlbiBub3QgY3JlYXRpbmcKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo1CiAgICAvLyBjbGFzcyBIZWxsb1dvcmxkKEFSQzRDb250cmFjdCk6CiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAxCiAgICBleHRyYWN0IDIgMAogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjE0MgogICAgLy8gQGFiaW1ldGhvZCgpCiAgICBjYWxsc3ViIGhlbGxvMzUKICAgIGR1cAogICAgbGVuCiAgICBpdG9iCiAgICBleHRyYWN0IDYgMgogICAgc3dhcAogICAgY29uY2F0CiAgICBieXRlY18wIC8vIDB4MTUxZjdjNzUKICAgIHN3YXAKICAgIGNvbmNhdAogICAgbG9nCiAgICBpbnRjXzAgLy8gMQogICAgcmV0dXJuCgptYWluX2hlbGxvMzRfcm91dGVAMzY6CiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6MTM4CiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIHR4biBPbkNvbXBsZXRpb24KICAgICEKICAgIGFzc2VydCAvLyBPbkNvbXBsZXRpb24gaXMgbm90IE5vT3AKICAgIHR4biBBcHBsaWNhdGlvbklECiAgICBhc3NlcnQgLy8gY2FuIG9ubHkgY2FsbCB3aGVuIG5vdCBjcmVhdGluZwogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjUKICAgIC8vIGNsYXNzIEhlbGxvV29ybGQoQVJDNENvbnRyYWN0KToKICAgIHR4bmEgQXBwbGljYXRpb25BcmdzIDEKICAgIGV4dHJhY3QgMiAwCiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6MTM4CiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIGNhbGxzdWIgaGVsbG8zNAogICAgZHVwCiAgICBsZW4KICAgIGl0b2IKICAgIGV4dHJhY3QgNiAyCiAgICBzd2FwCiAgICBjb25jYXQKICAgIGJ5dGVjXzAgLy8gMHgxNTFmN2M3NQogICAgc3dhcAogICAgY29uY2F0CiAgICBsb2cKICAgIGludGNfMCAvLyAxCiAgICByZXR1cm4KCm1haW5faGVsbG8zM19yb3V0ZUAzNToKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToxMzQKICAgIC8vIEBhYmltZXRob2QoKQogICAgdHhuIE9uQ29tcGxldGlvbgogICAgIQogICAgYXNzZXJ0IC8vIE9uQ29tcGxldGlvbiBpcyBub3QgTm9PcAogICAgdHhuIEFwcGxpY2F0aW9uSUQKICAgIGFzc2VydCAvLyBjYW4gb25seSBjYWxsIHdoZW4gbm90IGNyZWF0aW5nCiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6NQogICAgLy8gY2xhc3MgSGVsbG9Xb3JsZChBUkM0Q29udHJhY3QpOgogICAgdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMQogICAgZXh0cmFjdCAyIDAKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToxMzQKICAgIC8vIEBhYmltZXRob2QoKQogICAgY2FsbHN1YiBoZWxsbzMzCiAgICBkdXAKICAgIGxlbgogICAgaXRvYgogICAgZXh0cmFjdCA2IDIKICAgIHN3YXAKICAgIGNvbmNhdAogICAgYnl0ZWNfMCAvLyAweDE1MWY3Yzc1CiAgICBzd2FwCiAgICBjb25jYXQKICAgIGxvZwogICAgaW50Y18wIC8vIDEKICAgIHJldHVybgoKbWFpbl9oZWxsbzMyX3JvdXRlQDM0OgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjEzMAogICAgLy8gQGFiaW1ldGhvZCgpCiAgICB0eG4gT25Db21wbGV0aW9uCiAgICAhCiAgICBhc3NlcnQgLy8gT25Db21wbGV0aW9uIGlzIG5vdCBOb09wCiAgICB0eG4gQXBwbGljYXRpb25JRAogICAgYXNzZXJ0IC8vIGNhbiBvbmx5IGNhbGwgd2hlbiBub3QgY3JlYXRpbmcKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo1CiAgICAvLyBjbGFzcyBIZWxsb1dvcmxkKEFSQzRDb250cmFjdCk6CiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAxCiAgICBleHRyYWN0IDIgMAogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjEzMAogICAgLy8gQGFiaW1ldGhvZCgpCiAgICBjYWxsc3ViIGhlbGxvMzIKICAgIGR1cAogICAgbGVuCiAgICBpdG9iCiAgICBleHRyYWN0IDYgMgogICAgc3dhcAogICAgY29uY2F0CiAgICBieXRlY18wIC8vIDB4MTUxZjdjNzUKICAgIHN3YXAKICAgIGNvbmNhdAogICAgbG9nCiAgICBpbnRjXzAgLy8gMQogICAgcmV0dXJuCgptYWluX2hlbGxvMzFfcm91dGVAMzM6CiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6MTI2CiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIHR4biBPbkNvbXBsZXRpb24KICAgICEKICAgIGFzc2VydCAvLyBPbkNvbXBsZXRpb24gaXMgbm90IE5vT3AKICAgIHR4biBBcHBsaWNhdGlvbklECiAgICBhc3NlcnQgLy8gY2FuIG9ubHkgY2FsbCB3aGVuIG5vdCBjcmVhdGluZwogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjUKICAgIC8vIGNsYXNzIEhlbGxvV29ybGQoQVJDNENvbnRyYWN0KToKICAgIHR4bmEgQXBwbGljYXRpb25BcmdzIDEKICAgIGV4dHJhY3QgMiAwCiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6MTI2CiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIGNhbGxzdWIgaGVsbG8zMQogICAgZHVwCiAgICBsZW4KICAgIGl0b2IKICAgIGV4dHJhY3QgNiAyCiAgICBzd2FwCiAgICBjb25jYXQKICAgIGJ5dGVjXzAgLy8gMHgxNTFmN2M3NQogICAgc3dhcAogICAgY29uY2F0CiAgICBsb2cKICAgIGludGNfMCAvLyAxCiAgICByZXR1cm4KCm1haW5faGVsbG8zMF9yb3V0ZUAzMjoKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToxMjIKICAgIC8vIEBhYmltZXRob2QoKQogICAgdHhuIE9uQ29tcGxldGlvbgogICAgIQogICAgYXNzZXJ0IC8vIE9uQ29tcGxldGlvbiBpcyBub3QgTm9PcAogICAgdHhuIEFwcGxpY2F0aW9uSUQKICAgIGFzc2VydCAvLyBjYW4gb25seSBjYWxsIHdoZW4gbm90IGNyZWF0aW5nCiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6NQogICAgLy8gY2xhc3MgSGVsbG9Xb3JsZChBUkM0Q29udHJhY3QpOgogICAgdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMQogICAgZXh0cmFjdCAyIDAKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToxMjIKICAgIC8vIEBhYmltZXRob2QoKQogICAgY2FsbHN1YiBoZWxsbzMwCiAgICBkdXAKICAgIGxlbgogICAgaXRvYgogICAgZXh0cmFjdCA2IDIKICAgIHN3YXAKICAgIGNvbmNhdAogICAgYnl0ZWNfMCAvLyAweDE1MWY3Yzc1CiAgICBzd2FwCiAgICBjb25jYXQKICAgIGxvZwogICAgaW50Y18wIC8vIDEKICAgIHJldHVybgoKbWFpbl9oZWxsbzI5X3JvdXRlQDMxOgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjExOAogICAgLy8gQGFiaW1ldGhvZCgpCiAgICB0eG4gT25Db21wbGV0aW9uCiAgICAhCiAgICBhc3NlcnQgLy8gT25Db21wbGV0aW9uIGlzIG5vdCBOb09wCiAgICB0eG4gQXBwbGljYXRpb25JRAogICAgYXNzZXJ0IC8vIGNhbiBvbmx5IGNhbGwgd2hlbiBub3QgY3JlYXRpbmcKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo1CiAgICAvLyBjbGFzcyBIZWxsb1dvcmxkKEFSQzRDb250cmFjdCk6CiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAxCiAgICBleHRyYWN0IDIgMAogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjExOAogICAgLy8gQGFiaW1ldGhvZCgpCiAgICBjYWxsc3ViIGhlbGxvMjkKICAgIGR1cAogICAgbGVuCiAgICBpdG9iCiAgICBleHRyYWN0IDYgMgogICAgc3dhcAogICAgY29uY2F0CiAgICBieXRlY18wIC8vIDB4MTUxZjdjNzUKICAgIHN3YXAKICAgIGNvbmNhdAogICAgbG9nCiAgICBpbnRjXzAgLy8gMQogICAgcmV0dXJuCgptYWluX2hlbGxvMjhfcm91dGVAMzA6CiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6MTE0CiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIHR4biBPbkNvbXBsZXRpb24KICAgICEKICAgIGFzc2VydCAvLyBPbkNvbXBsZXRpb24gaXMgbm90IE5vT3AKICAgIHR4biBBcHBsaWNhdGlvbklECiAgICBhc3NlcnQgLy8gY2FuIG9ubHkgY2FsbCB3aGVuIG5vdCBjcmVhdGluZwogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjUKICAgIC8vIGNsYXNzIEhlbGxvV29ybGQoQVJDNENvbnRyYWN0KToKICAgIHR4bmEgQXBwbGljYXRpb25BcmdzIDEKICAgIGV4dHJhY3QgMiAwCiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6MTE0CiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIGNhbGxzdWIgaGVsbG8yOAogICAgZHVwCiAgICBsZW4KICAgIGl0b2IKICAgIGV4dHJhY3QgNiAyCiAgICBzd2FwCiAgICBjb25jYXQKICAgIGJ5dGVjXzAgLy8gMHgxNTFmN2M3NQogICAgc3dhcAogICAgY29uY2F0CiAgICBsb2cKICAgIGludGNfMCAvLyAxCiAgICByZXR1cm4KCm1haW5faGVsbG8yN19yb3V0ZUAyOToKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToxMTAKICAgIC8vIEBhYmltZXRob2QoKQogICAgdHhuIE9uQ29tcGxldGlvbgogICAgIQogICAgYXNzZXJ0IC8vIE9uQ29tcGxldGlvbiBpcyBub3QgTm9PcAogICAgdHhuIEFwcGxpY2F0aW9uSUQKICAgIGFzc2VydCAvLyBjYW4gb25seSBjYWxsIHdoZW4gbm90IGNyZWF0aW5nCiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6NQogICAgLy8gY2xhc3MgSGVsbG9Xb3JsZChBUkM0Q29udHJhY3QpOgogICAgdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMQogICAgZXh0cmFjdCAyIDAKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToxMTAKICAgIC8vIEBhYmltZXRob2QoKQogICAgY2FsbHN1YiBoZWxsbzI3CiAgICBkdXAKICAgIGxlbgogICAgaXRvYgogICAgZXh0cmFjdCA2IDIKICAgIHN3YXAKICAgIGNvbmNhdAogICAgYnl0ZWNfMCAvLyAweDE1MWY3Yzc1CiAgICBzd2FwCiAgICBjb25jYXQKICAgIGxvZwogICAgaW50Y18wIC8vIDEKICAgIHJldHVybgoKbWFpbl9oZWxsbzI2X3JvdXRlQDI4OgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjEwNgogICAgLy8gQGFiaW1ldGhvZCgpCiAgICB0eG4gT25Db21wbGV0aW9uCiAgICAhCiAgICBhc3NlcnQgLy8gT25Db21wbGV0aW9uIGlzIG5vdCBOb09wCiAgICB0eG4gQXBwbGljYXRpb25JRAogICAgYXNzZXJ0IC8vIGNhbiBvbmx5IGNhbGwgd2hlbiBub3QgY3JlYXRpbmcKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo1CiAgICAvLyBjbGFzcyBIZWxsb1dvcmxkKEFSQzRDb250cmFjdCk6CiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAxCiAgICBleHRyYWN0IDIgMAogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjEwNgogICAgLy8gQGFiaW1ldGhvZCgpCiAgICBjYWxsc3ViIGhlbGxvMjYKICAgIGR1cAogICAgbGVuCiAgICBpdG9iCiAgICBleHRyYWN0IDYgMgogICAgc3dhcAogICAgY29uY2F0CiAgICBieXRlY18wIC8vIDB4MTUxZjdjNzUKICAgIHN3YXAKICAgIGNvbmNhdAogICAgbG9nCiAgICBpbnRjXzAgLy8gMQogICAgcmV0dXJuCgptYWluX2hlbGxvMjVfcm91dGVAMjc6CiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6MTAyCiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIHR4biBPbkNvbXBsZXRpb24KICAgICEKICAgIGFzc2VydCAvLyBPbkNvbXBsZXRpb24gaXMgbm90IE5vT3AKICAgIHR4biBBcHBsaWNhdGlvbklECiAgICBhc3NlcnQgLy8gY2FuIG9ubHkgY2FsbCB3aGVuIG5vdCBjcmVhdGluZwogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjUKICAgIC8vIGNsYXNzIEhlbGxvV29ybGQoQVJDNENvbnRyYWN0KToKICAgIHR4bmEgQXBwbGljYXRpb25BcmdzIDEKICAgIGV4dHJhY3QgMiAwCiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6MTAyCiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIGNhbGxzdWIgaGVsbG8yNQogICAgZHVwCiAgICBsZW4KICAgIGl0b2IKICAgIGV4dHJhY3QgNiAyCiAgICBzd2FwCiAgICBjb25jYXQKICAgIGJ5dGVjXzAgLy8gMHgxNTFmN2M3NQogICAgc3dhcAogICAgY29uY2F0CiAgICBsb2cKICAgIGludGNfMCAvLyAxCiAgICByZXR1cm4KCm1haW5faGVsbG8yNF9yb3V0ZUAyNjoKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo5OAogICAgLy8gQGFiaW1ldGhvZCgpCiAgICB0eG4gT25Db21wbGV0aW9uCiAgICAhCiAgICBhc3NlcnQgLy8gT25Db21wbGV0aW9uIGlzIG5vdCBOb09wCiAgICB0eG4gQXBwbGljYXRpb25JRAogICAgYXNzZXJ0IC8vIGNhbiBvbmx5IGNhbGwgd2hlbiBub3QgY3JlYXRpbmcKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo1CiAgICAvLyBjbGFzcyBIZWxsb1dvcmxkKEFSQzRDb250cmFjdCk6CiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAxCiAgICBleHRyYWN0IDIgMAogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5Ojk4CiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIGNhbGxzdWIgaGVsbG8yNAogICAgZHVwCiAgICBsZW4KICAgIGl0b2IKICAgIGV4dHJhY3QgNiAyCiAgICBzd2FwCiAgICBjb25jYXQKICAgIGJ5dGVjXzAgLy8gMHgxNTFmN2M3NQogICAgc3dhcAogICAgY29uY2F0CiAgICBsb2cKICAgIGludGNfMCAvLyAxCiAgICByZXR1cm4KCm1haW5faGVsbG8yM19yb3V0ZUAyNToKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo5NAogICAgLy8gQGFiaW1ldGhvZCgpCiAgICB0eG4gT25Db21wbGV0aW9uCiAgICAhCiAgICBhc3NlcnQgLy8gT25Db21wbGV0aW9uIGlzIG5vdCBOb09wCiAgICB0eG4gQXBwbGljYXRpb25JRAogICAgYXNzZXJ0IC8vIGNhbiBvbmx5IGNhbGwgd2hlbiBub3QgY3JlYXRpbmcKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo1CiAgICAvLyBjbGFzcyBIZWxsb1dvcmxkKEFSQzRDb250cmFjdCk6CiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAxCiAgICBleHRyYWN0IDIgMAogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5Ojk0CiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIGNhbGxzdWIgaGVsbG8yMwogICAgZHVwCiAgICBsZW4KICAgIGl0b2IKICAgIGV4dHJhY3QgNiAyCiAgICBzd2FwCiAgICBjb25jYXQKICAgIGJ5dGVjXzAgLy8gMHgxNTFmN2M3NQogICAgc3dhcAogICAgY29uY2F0CiAgICBsb2cKICAgIGludGNfMCAvLyAxCiAgICByZXR1cm4KCm1haW5faGVsbG8yMl9yb3V0ZUAyNDoKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo5MAogICAgLy8gQGFiaW1ldGhvZCgpCiAgICB0eG4gT25Db21wbGV0aW9uCiAgICAhCiAgICBhc3NlcnQgLy8gT25Db21wbGV0aW9uIGlzIG5vdCBOb09wCiAgICB0eG4gQXBwbGljYXRpb25JRAogICAgYXNzZXJ0IC8vIGNhbiBvbmx5IGNhbGwgd2hlbiBub3QgY3JlYXRpbmcKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo1CiAgICAvLyBjbGFzcyBIZWxsb1dvcmxkKEFSQzRDb250cmFjdCk6CiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAxCiAgICBleHRyYWN0IDIgMAogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjkwCiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIGNhbGxzdWIgaGVsbG8yMgogICAgZHVwCiAgICBsZW4KICAgIGl0b2IKICAgIGV4dHJhY3QgNiAyCiAgICBzd2FwCiAgICBjb25jYXQKICAgIGJ5dGVjXzAgLy8gMHgxNTFmN2M3NQogICAgc3dhcAogICAgY29uY2F0CiAgICBsb2cKICAgIGludGNfMCAvLyAxCiAgICByZXR1cm4KCm1haW5faGVsbG8yMV9yb3V0ZUAyMzoKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo4NgogICAgLy8gQGFiaW1ldGhvZCgpCiAgICB0eG4gT25Db21wbGV0aW9uCiAgICAhCiAgICBhc3NlcnQgLy8gT25Db21wbGV0aW9uIGlzIG5vdCBOb09wCiAgICB0eG4gQXBwbGljYXRpb25JRAogICAgYXNzZXJ0IC8vIGNhbiBvbmx5IGNhbGwgd2hlbiBub3QgY3JlYXRpbmcKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo1CiAgICAvLyBjbGFzcyBIZWxsb1dvcmxkKEFSQzRDb250cmFjdCk6CiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAxCiAgICBleHRyYWN0IDIgMAogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5Ojg2CiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIGNhbGxzdWIgaGVsbG8yMQogICAgZHVwCiAgICBsZW4KICAgIGl0b2IKICAgIGV4dHJhY3QgNiAyCiAgICBzd2FwCiAgICBjb25jYXQKICAgIGJ5dGVjXzAgLy8gMHgxNTFmN2M3NQogICAgc3dhcAogICAgY29uY2F0CiAgICBsb2cKICAgIGludGNfMCAvLyAxCiAgICByZXR1cm4KCm1haW5faGVsbG8yMF9yb3V0ZUAyMjoKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo4MgogICAgLy8gQGFiaW1ldGhvZCgpCiAgICB0eG4gT25Db21wbGV0aW9uCiAgICAhCiAgICBhc3NlcnQgLy8gT25Db21wbGV0aW9uIGlzIG5vdCBOb09wCiAgICB0eG4gQXBwbGljYXRpb25JRAogICAgYXNzZXJ0IC8vIGNhbiBvbmx5IGNhbGwgd2hlbiBub3QgY3JlYXRpbmcKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo1CiAgICAvLyBjbGFzcyBIZWxsb1dvcmxkKEFSQzRDb250cmFjdCk6CiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAxCiAgICBleHRyYWN0IDIgMAogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjgyCiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIGNhbGxzdWIgaGVsbG8yMAogICAgZHVwCiAgICBsZW4KICAgIGl0b2IKICAgIGV4dHJhY3QgNiAyCiAgICBzd2FwCiAgICBjb25jYXQKICAgIGJ5dGVjXzAgLy8gMHgxNTFmN2M3NQogICAgc3dhcAogICAgY29uY2F0CiAgICBsb2cKICAgIGludGNfMCAvLyAxCiAgICByZXR1cm4KCm1haW5faGVsbG8xOV9yb3V0ZUAyMToKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo3OAogICAgLy8gQGFiaW1ldGhvZCgpCiAgICB0eG4gT25Db21wbGV0aW9uCiAgICAhCiAgICBhc3NlcnQgLy8gT25Db21wbGV0aW9uIGlzIG5vdCBOb09wCiAgICB0eG4gQXBwbGljYXRpb25JRAogICAgYXNzZXJ0IC8vIGNhbiBvbmx5IGNhbGwgd2hlbiBub3QgY3JlYXRpbmcKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo1CiAgICAvLyBjbGFzcyBIZWxsb1dvcmxkKEFSQzRDb250cmFjdCk6CiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAxCiAgICBleHRyYWN0IDIgMAogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5Ojc4CiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIGNhbGxzdWIgaGVsbG8xOQogICAgZHVwCiAgICBsZW4KICAgIGl0b2IKICAgIGV4dHJhY3QgNiAyCiAgICBzd2FwCiAgICBjb25jYXQKICAgIGJ5dGVjXzAgLy8gMHgxNTFmN2M3NQogICAgc3dhcAogICAgY29uY2F0CiAgICBsb2cKICAgIGludGNfMCAvLyAxCiAgICByZXR1cm4KCm1haW5faGVsbG8xOF9yb3V0ZUAyMDoKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo3NAogICAgLy8gQGFiaW1ldGhvZCgpCiAgICB0eG4gT25Db21wbGV0aW9uCiAgICAhCiAgICBhc3NlcnQgLy8gT25Db21wbGV0aW9uIGlzIG5vdCBOb09wCiAgICB0eG4gQXBwbGljYXRpb25JRAogICAgYXNzZXJ0IC8vIGNhbiBvbmx5IGNhbGwgd2hlbiBub3QgY3JlYXRpbmcKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo1CiAgICAvLyBjbGFzcyBIZWxsb1dvcmxkKEFSQzRDb250cmFjdCk6CiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAxCiAgICBleHRyYWN0IDIgMAogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5Ojc0CiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIGNhbGxzdWIgaGVsbG8xOAogICAgZHVwCiAgICBsZW4KICAgIGl0b2IKICAgIGV4dHJhY3QgNiAyCiAgICBzd2FwCiAgICBjb25jYXQKICAgIGJ5dGVjXzAgLy8gMHgxNTFmN2M3NQogICAgc3dhcAogICAgY29uY2F0CiAgICBsb2cKICAgIGludGNfMCAvLyAxCiAgICByZXR1cm4KCm1haW5faGVsbG8xN19yb3V0ZUAxOToKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo3MAogICAgLy8gQGFiaW1ldGhvZCgpCiAgICB0eG4gT25Db21wbGV0aW9uCiAgICAhCiAgICBhc3NlcnQgLy8gT25Db21wbGV0aW9uIGlzIG5vdCBOb09wCiAgICB0eG4gQXBwbGljYXRpb25JRAogICAgYXNzZXJ0IC8vIGNhbiBvbmx5IGNhbGwgd2hlbiBub3QgY3JlYXRpbmcKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo1CiAgICAvLyBjbGFzcyBIZWxsb1dvcmxkKEFSQzRDb250cmFjdCk6CiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAxCiAgICBleHRyYWN0IDIgMAogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjcwCiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIGNhbGxzdWIgaGVsbG8xNwogICAgZHVwCiAgICBsZW4KICAgIGl0b2IKICAgIGV4dHJhY3QgNiAyCiAgICBzd2FwCiAgICBjb25jYXQKICAgIGJ5dGVjXzAgLy8gMHgxNTFmN2M3NQogICAgc3dhcAogICAgY29uY2F0CiAgICBsb2cKICAgIGludGNfMCAvLyAxCiAgICByZXR1cm4KCm1haW5faGVsbG8xNl9yb3V0ZUAxODoKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo2NgogICAgLy8gQGFiaW1ldGhvZCgpCiAgICB0eG4gT25Db21wbGV0aW9uCiAgICAhCiAgICBhc3NlcnQgLy8gT25Db21wbGV0aW9uIGlzIG5vdCBOb09wCiAgICB0eG4gQXBwbGljYXRpb25JRAogICAgYXNzZXJ0IC8vIGNhbiBvbmx5IGNhbGwgd2hlbiBub3QgY3JlYXRpbmcKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo1CiAgICAvLyBjbGFzcyBIZWxsb1dvcmxkKEFSQzRDb250cmFjdCk6CiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAxCiAgICBleHRyYWN0IDIgMAogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjY2CiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIGNhbGxzdWIgaGVsbG8xNgogICAgZHVwCiAgICBsZW4KICAgIGl0b2IKICAgIGV4dHJhY3QgNiAyCiAgICBzd2FwCiAgICBjb25jYXQKICAgIGJ5dGVjXzAgLy8gMHgxNTFmN2M3NQogICAgc3dhcAogICAgY29uY2F0CiAgICBsb2cKICAgIGludGNfMCAvLyAxCiAgICByZXR1cm4KCm1haW5faGVsbG8xNV9yb3V0ZUAxNzoKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo2MgogICAgLy8gQGFiaW1ldGhvZCgpCiAgICB0eG4gT25Db21wbGV0aW9uCiAgICAhCiAgICBhc3NlcnQgLy8gT25Db21wbGV0aW9uIGlzIG5vdCBOb09wCiAgICB0eG4gQXBwbGljYXRpb25JRAogICAgYXNzZXJ0IC8vIGNhbiBvbmx5IGNhbGwgd2hlbiBub3QgY3JlYXRpbmcKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo1CiAgICAvLyBjbGFzcyBIZWxsb1dvcmxkKEFSQzRDb250cmFjdCk6CiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAxCiAgICBleHRyYWN0IDIgMAogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjYyCiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIGNhbGxzdWIgaGVsbG8xNQogICAgZHVwCiAgICBsZW4KICAgIGl0b2IKICAgIGV4dHJhY3QgNiAyCiAgICBzd2FwCiAgICBjb25jYXQKICAgIGJ5dGVjXzAgLy8gMHgxNTFmN2M3NQogICAgc3dhcAogICAgY29uY2F0CiAgICBsb2cKICAgIGludGNfMCAvLyAxCiAgICByZXR1cm4KCm1haW5faGVsbG8xNF9yb3V0ZUAxNjoKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo1OAogICAgLy8gQGFiaW1ldGhvZCgpCiAgICB0eG4gT25Db21wbGV0aW9uCiAgICAhCiAgICBhc3NlcnQgLy8gT25Db21wbGV0aW9uIGlzIG5vdCBOb09wCiAgICB0eG4gQXBwbGljYXRpb25JRAogICAgYXNzZXJ0IC8vIGNhbiBvbmx5IGNhbGwgd2hlbiBub3QgY3JlYXRpbmcKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo1CiAgICAvLyBjbGFzcyBIZWxsb1dvcmxkKEFSQzRDb250cmFjdCk6CiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAxCiAgICBleHRyYWN0IDIgMAogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjU4CiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIGNhbGxzdWIgaGVsbG8xNAogICAgZHVwCiAgICBsZW4KICAgIGl0b2IKICAgIGV4dHJhY3QgNiAyCiAgICBzd2FwCiAgICBjb25jYXQKICAgIGJ5dGVjXzAgLy8gMHgxNTFmN2M3NQogICAgc3dhcAogICAgY29uY2F0CiAgICBsb2cKICAgIGludGNfMCAvLyAxCiAgICByZXR1cm4KCm1haW5faGVsbG8xM19yb3V0ZUAxNToKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo1NAogICAgLy8gQGFiaW1ldGhvZCgpCiAgICB0eG4gT25Db21wbGV0aW9uCiAgICAhCiAgICBhc3NlcnQgLy8gT25Db21wbGV0aW9uIGlzIG5vdCBOb09wCiAgICB0eG4gQXBwbGljYXRpb25JRAogICAgYXNzZXJ0IC8vIGNhbiBvbmx5IGNhbGwgd2hlbiBub3QgY3JlYXRpbmcKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo1CiAgICAvLyBjbGFzcyBIZWxsb1dvcmxkKEFSQzRDb250cmFjdCk6CiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAxCiAgICBleHRyYWN0IDIgMAogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjU0CiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIGNhbGxzdWIgaGVsbG8xMwogICAgZHVwCiAgICBsZW4KICAgIGl0b2IKICAgIGV4dHJhY3QgNiAyCiAgICBzd2FwCiAgICBjb25jYXQKICAgIGJ5dGVjXzAgLy8gMHgxNTFmN2M3NQogICAgc3dhcAogICAgY29uY2F0CiAgICBsb2cKICAgIGludGNfMCAvLyAxCiAgICByZXR1cm4KCm1haW5faGVsbG8xMl9yb3V0ZUAxNDoKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo1MAogICAgLy8gQGFiaW1ldGhvZCgpCiAgICB0eG4gT25Db21wbGV0aW9uCiAgICAhCiAgICBhc3NlcnQgLy8gT25Db21wbGV0aW9uIGlzIG5vdCBOb09wCiAgICB0eG4gQXBwbGljYXRpb25JRAogICAgYXNzZXJ0IC8vIGNhbiBvbmx5IGNhbGwgd2hlbiBub3QgY3JlYXRpbmcKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo1CiAgICAvLyBjbGFzcyBIZWxsb1dvcmxkKEFSQzRDb250cmFjdCk6CiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAxCiAgICBleHRyYWN0IDIgMAogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjUwCiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIGNhbGxzdWIgaGVsbG8xMgogICAgZHVwCiAgICBsZW4KICAgIGl0b2IKICAgIGV4dHJhY3QgNiAyCiAgICBzd2FwCiAgICBjb25jYXQKICAgIGJ5dGVjXzAgLy8gMHgxNTFmN2M3NQogICAgc3dhcAogICAgY29uY2F0CiAgICBsb2cKICAgIGludGNfMCAvLyAxCiAgICByZXR1cm4KCm1haW5faGVsbG8xMV9yb3V0ZUAxMzoKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo0NgogICAgLy8gQGFiaW1ldGhvZCgpCiAgICB0eG4gT25Db21wbGV0aW9uCiAgICAhCiAgICBhc3NlcnQgLy8gT25Db21wbGV0aW9uIGlzIG5vdCBOb09wCiAgICB0eG4gQXBwbGljYXRpb25JRAogICAgYXNzZXJ0IC8vIGNhbiBvbmx5IGNhbGwgd2hlbiBub3QgY3JlYXRpbmcKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo1CiAgICAvLyBjbGFzcyBIZWxsb1dvcmxkKEFSQzRDb250cmFjdCk6CiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAxCiAgICBleHRyYWN0IDIgMAogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjQ2CiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIGNhbGxzdWIgaGVsbG8xMQogICAgZHVwCiAgICBsZW4KICAgIGl0b2IKICAgIGV4dHJhY3QgNiAyCiAgICBzd2FwCiAgICBjb25jYXQKICAgIGJ5dGVjXzAgLy8gMHgxNTFmN2M3NQogICAgc3dhcAogICAgY29uY2F0CiAgICBsb2cKICAgIGludGNfMCAvLyAxCiAgICByZXR1cm4KCm1haW5faGVsbG8xMF9yb3V0ZUAxMjoKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo0MgogICAgLy8gQGFiaW1ldGhvZCgpCiAgICB0eG4gT25Db21wbGV0aW9uCiAgICAhCiAgICBhc3NlcnQgLy8gT25Db21wbGV0aW9uIGlzIG5vdCBOb09wCiAgICB0eG4gQXBwbGljYXRpb25JRAogICAgYXNzZXJ0IC8vIGNhbiBvbmx5IGNhbGwgd2hlbiBub3QgY3JlYXRpbmcKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo1CiAgICAvLyBjbGFzcyBIZWxsb1dvcmxkKEFSQzRDb250cmFjdCk6CiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAxCiAgICBleHRyYWN0IDIgMAogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjQyCiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIGNhbGxzdWIgaGVsbG8xMAogICAgZHVwCiAgICBsZW4KICAgIGl0b2IKICAgIGV4dHJhY3QgNiAyCiAgICBzd2FwCiAgICBjb25jYXQKICAgIGJ5dGVjXzAgLy8gMHgxNTFmN2M3NQogICAgc3dhcAogICAgY29uY2F0CiAgICBsb2cKICAgIGludGNfMCAvLyAxCiAgICByZXR1cm4KCm1haW5faGVsbG85X3JvdXRlQDExOgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjM4CiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIHR4biBPbkNvbXBsZXRpb24KICAgICEKICAgIGFzc2VydCAvLyBPbkNvbXBsZXRpb24gaXMgbm90IE5vT3AKICAgIHR4biBBcHBsaWNhdGlvbklECiAgICBhc3NlcnQgLy8gY2FuIG9ubHkgY2FsbCB3aGVuIG5vdCBjcmVhdGluZwogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjUKICAgIC8vIGNsYXNzIEhlbGxvV29ybGQoQVJDNENvbnRyYWN0KToKICAgIHR4bmEgQXBwbGljYXRpb25BcmdzIDEKICAgIGV4dHJhY3QgMiAwCiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6MzgKICAgIC8vIEBhYmltZXRob2QoKQogICAgY2FsbHN1YiBoZWxsbzkKICAgIGR1cAogICAgbGVuCiAgICBpdG9iCiAgICBleHRyYWN0IDYgMgogICAgc3dhcAogICAgY29uY2F0CiAgICBieXRlY18wIC8vIDB4MTUxZjdjNzUKICAgIHN3YXAKICAgIGNvbmNhdAogICAgbG9nCiAgICBpbnRjXzAgLy8gMQogICAgcmV0dXJuCgptYWluX2hlbGxvOF9yb3V0ZUAxMDoKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTozNAogICAgLy8gQGFiaW1ldGhvZCgpCiAgICB0eG4gT25Db21wbGV0aW9uCiAgICAhCiAgICBhc3NlcnQgLy8gT25Db21wbGV0aW9uIGlzIG5vdCBOb09wCiAgICB0eG4gQXBwbGljYXRpb25JRAogICAgYXNzZXJ0IC8vIGNhbiBvbmx5IGNhbGwgd2hlbiBub3QgY3JlYXRpbmcKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo1CiAgICAvLyBjbGFzcyBIZWxsb1dvcmxkKEFSQzRDb250cmFjdCk6CiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAxCiAgICBleHRyYWN0IDIgMAogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjM0CiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIGNhbGxzdWIgaGVsbG84CiAgICBkdXAKICAgIGxlbgogICAgaXRvYgogICAgZXh0cmFjdCA2IDIKICAgIHN3YXAKICAgIGNvbmNhdAogICAgYnl0ZWNfMCAvLyAweDE1MWY3Yzc1CiAgICBzd2FwCiAgICBjb25jYXQKICAgIGxvZwogICAgaW50Y18wIC8vIDEKICAgIHJldHVybgoKbWFpbl9oZWxsbzdfcm91dGVAOToKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTozMAogICAgLy8gQGFiaW1ldGhvZCgpCiAgICB0eG4gT25Db21wbGV0aW9uCiAgICAhCiAgICBhc3NlcnQgLy8gT25Db21wbGV0aW9uIGlzIG5vdCBOb09wCiAgICB0eG4gQXBwbGljYXRpb25JRAogICAgYXNzZXJ0IC8vIGNhbiBvbmx5IGNhbGwgd2hlbiBub3QgY3JlYXRpbmcKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo1CiAgICAvLyBjbGFzcyBIZWxsb1dvcmxkKEFSQzRDb250cmFjdCk6CiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAxCiAgICBleHRyYWN0IDIgMAogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjMwCiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIGNhbGxzdWIgaGVsbG83CiAgICBkdXAKICAgIGxlbgogICAgaXRvYgogICAgZXh0cmFjdCA2IDIKICAgIHN3YXAKICAgIGNvbmNhdAogICAgYnl0ZWNfMCAvLyAweDE1MWY3Yzc1CiAgICBzd2FwCiAgICBjb25jYXQKICAgIGxvZwogICAgaW50Y18wIC8vIDEKICAgIHJldHVybgoKbWFpbl9oZWxsbzZfcm91dGVAODoKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToyNgogICAgLy8gQGFiaW1ldGhvZCgpCiAgICB0eG4gT25Db21wbGV0aW9uCiAgICAhCiAgICBhc3NlcnQgLy8gT25Db21wbGV0aW9uIGlzIG5vdCBOb09wCiAgICB0eG4gQXBwbGljYXRpb25JRAogICAgYXNzZXJ0IC8vIGNhbiBvbmx5IGNhbGwgd2hlbiBub3QgY3JlYXRpbmcKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo1CiAgICAvLyBjbGFzcyBIZWxsb1dvcmxkKEFSQzRDb250cmFjdCk6CiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAxCiAgICBleHRyYWN0IDIgMAogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjI2CiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIGNhbGxzdWIgaGVsbG82CiAgICBkdXAKICAgIGxlbgogICAgaXRvYgogICAgZXh0cmFjdCA2IDIKICAgIHN3YXAKICAgIGNvbmNhdAogICAgYnl0ZWNfMCAvLyAweDE1MWY3Yzc1CiAgICBzd2FwCiAgICBjb25jYXQKICAgIGxvZwogICAgaW50Y18wIC8vIDEKICAgIHJldHVybgoKbWFpbl9oZWxsbzVfcm91dGVANzoKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToyMgogICAgLy8gQGFiaW1ldGhvZCgpCiAgICB0eG4gT25Db21wbGV0aW9uCiAgICAhCiAgICBhc3NlcnQgLy8gT25Db21wbGV0aW9uIGlzIG5vdCBOb09wCiAgICB0eG4gQXBwbGljYXRpb25JRAogICAgYXNzZXJ0IC8vIGNhbiBvbmx5IGNhbGwgd2hlbiBub3QgY3JlYXRpbmcKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo1CiAgICAvLyBjbGFzcyBIZWxsb1dvcmxkKEFSQzRDb250cmFjdCk6CiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAxCiAgICBleHRyYWN0IDIgMAogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjIyCiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIGNhbGxzdWIgaGVsbG81CiAgICBkdXAKICAgIGxlbgogICAgaXRvYgogICAgZXh0cmFjdCA2IDIKICAgIHN3YXAKICAgIGNvbmNhdAogICAgYnl0ZWNfMCAvLyAweDE1MWY3Yzc1CiAgICBzd2FwCiAgICBjb25jYXQKICAgIGxvZwogICAgaW50Y18wIC8vIDEKICAgIHJldHVybgoKbWFpbl9oZWxsbzRfcm91dGVANjoKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToxOAogICAgLy8gQGFiaW1ldGhvZCgpCiAgICB0eG4gT25Db21wbGV0aW9uCiAgICAhCiAgICBhc3NlcnQgLy8gT25Db21wbGV0aW9uIGlzIG5vdCBOb09wCiAgICB0eG4gQXBwbGljYXRpb25JRAogICAgYXNzZXJ0IC8vIGNhbiBvbmx5IGNhbGwgd2hlbiBub3QgY3JlYXRpbmcKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo1CiAgICAvLyBjbGFzcyBIZWxsb1dvcmxkKEFSQzRDb250cmFjdCk6CiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAxCiAgICBleHRyYWN0IDIgMAogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjE4CiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIGNhbGxzdWIgaGVsbG80CiAgICBkdXAKICAgIGxlbgogICAgaXRvYgogICAgZXh0cmFjdCA2IDIKICAgIHN3YXAKICAgIGNvbmNhdAogICAgYnl0ZWNfMCAvLyAweDE1MWY3Yzc1CiAgICBzd2FwCiAgICBjb25jYXQKICAgIGxvZwogICAgaW50Y18wIC8vIDEKICAgIHJldHVybgoKbWFpbl9oZWxsbzNfcm91dGVANToKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToxNAogICAgLy8gQGFiaW1ldGhvZCgpCiAgICB0eG4gT25Db21wbGV0aW9uCiAgICAhCiAgICBhc3NlcnQgLy8gT25Db21wbGV0aW9uIGlzIG5vdCBOb09wCiAgICB0eG4gQXBwbGljYXRpb25JRAogICAgYXNzZXJ0IC8vIGNhbiBvbmx5IGNhbGwgd2hlbiBub3QgY3JlYXRpbmcKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo1CiAgICAvLyBjbGFzcyBIZWxsb1dvcmxkKEFSQzRDb250cmFjdCk6CiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAxCiAgICBleHRyYWN0IDIgMAogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjE0CiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIGNhbGxzdWIgaGVsbG8zCiAgICBkdXAKICAgIGxlbgogICAgaXRvYgogICAgZXh0cmFjdCA2IDIKICAgIHN3YXAKICAgIGNvbmNhdAogICAgYnl0ZWNfMCAvLyAweDE1MWY3Yzc1CiAgICBzd2FwCiAgICBjb25jYXQKICAgIGxvZwogICAgaW50Y18wIC8vIDEKICAgIHJldHVybgoKbWFpbl9oZWxsbzJfcm91dGVANDoKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToxMAogICAgLy8gQGFiaW1ldGhvZCgpCiAgICB0eG4gT25Db21wbGV0aW9uCiAgICAhCiAgICBhc3NlcnQgLy8gT25Db21wbGV0aW9uIGlzIG5vdCBOb09wCiAgICB0eG4gQXBwbGljYXRpb25JRAogICAgYXNzZXJ0IC8vIGNhbiBvbmx5IGNhbGwgd2hlbiBub3QgY3JlYXRpbmcKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo1CiAgICAvLyBjbGFzcyBIZWxsb1dvcmxkKEFSQzRDb250cmFjdCk6CiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAxCiAgICBleHRyYWN0IDIgMAogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjEwCiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIGNhbGxzdWIgaGVsbG8yCiAgICBkdXAKICAgIGxlbgogICAgaXRvYgogICAgZXh0cmFjdCA2IDIKICAgIHN3YXAKICAgIGNvbmNhdAogICAgYnl0ZWNfMCAvLyAweDE1MWY3Yzc1CiAgICBzd2FwCiAgICBjb25jYXQKICAgIGxvZwogICAgaW50Y18wIC8vIDEKICAgIHJldHVybgoKbWFpbl9oZWxsb19yb3V0ZUAzOgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjYKICAgIC8vIEBhYmltZXRob2QoKQogICAgdHhuIE9uQ29tcGxldGlvbgogICAgIQogICAgYXNzZXJ0IC8vIE9uQ29tcGxldGlvbiBpcyBub3QgTm9PcAogICAgdHhuIEFwcGxpY2F0aW9uSUQKICAgIGFzc2VydCAvLyBjYW4gb25seSBjYWxsIHdoZW4gbm90IGNyZWF0aW5nCiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6NQogICAgLy8gY2xhc3MgSGVsbG9Xb3JsZChBUkM0Q29udHJhY3QpOgogICAgdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMQogICAgZXh0cmFjdCAyIDAKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo2CiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIGNhbGxzdWIgaGVsbG8KICAgIGR1cAogICAgbGVuCiAgICBpdG9iCiAgICBleHRyYWN0IDYgMgogICAgc3dhcAogICAgY29uY2F0CiAgICBieXRlY18wIC8vIDB4MTUxZjdjNzUKICAgIHN3YXAKICAgIGNvbmNhdAogICAgbG9nCiAgICBpbnRjXzAgLy8gMQogICAgcmV0dXJuCgptYWluX2JhcmVfcm91dGluZ0A1NToKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo1CiAgICAvLyBjbGFzcyBIZWxsb1dvcmxkKEFSQzRDb250cmFjdCk6CiAgICBwdXNoaW50IDQgLy8gNAogICAgaW50Y18xIC8vIDAKICAgIHR4biBPbkNvbXBsZXRpb24KICAgIG1hdGNoIG1haW5fdXBkYXRlQDU2IG1haW5fX19hbGdvcHlfZGVmYXVsdF9jcmVhdGVANTcKICAgIGIgbWFpbl9hZnRlcl9pZl9lbHNlQDYwCgptYWluX19fYWxnb3B5X2RlZmF1bHRfY3JlYXRlQDU3OgogICAgdHhuIEFwcGxpY2F0aW9uSUQKICAgICEKICAgIGFzc2VydCAvLyBjYW4gb25seSBjYWxsIHdoZW4gY3JlYXRpbmcKICAgIGludGNfMCAvLyAxCiAgICByZXR1cm4KCm1haW5fdXBkYXRlQDU2OgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjIwNgogICAgLy8gQGJhcmVtZXRob2QoYWxsb3dfYWN0aW9ucz1bIlVwZGF0ZUFwcGxpY2F0aW9uIl0pCiAgICB0eG4gQXBwbGljYXRpb25JRAogICAgYXNzZXJ0IC8vIGNhbiBvbmx5IGNhbGwgd2hlbiBub3QgY3JlYXRpbmcKICAgIGNhbGxzdWIgdXBkYXRlCiAgICBpbnRjXzAgLy8gMQogICAgcmV0dXJuCgoKLy8gc21hcnRfY29udHJhY3RzLmhlbGxvX3dvcmxkLmNvbnRyYWN0LkhlbGxvV29ybGQuaGVsbG8obmFtZTogYnl0ZXMpIC0+IGJ5dGVzOgpoZWxsbzoKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo2LTcKICAgIC8vIEBhYmltZXRob2QoKQogICAgLy8gZGVmIGhlbGxvKHNlbGYsIG5hbWU6IFN0cmluZykgLT4gU3RyaW5nOgogICAgcHJvdG8gMSAxCiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6OAogICAgLy8gcmV0dXJuICJIZWxsbywgIiArIG5hbWUKICAgIGJ5dGVjXzEgLy8gIkhlbGxvLCAiCiAgICBmcmFtZV9kaWcgLTEKICAgIGNvbmNhdAogICAgcmV0c3ViCgoKLy8gc21hcnRfY29udHJhY3RzLmhlbGxvX3dvcmxkLmNvbnRyYWN0LkhlbGxvV29ybGQuaGVsbG8yKG5hbWU6IGJ5dGVzKSAtPiBieXRlczoKaGVsbG8yOgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjEwLTExCiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIC8vIGRlZiBoZWxsbzIoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToxMgogICAgLy8gcmV0dXJuICJIZWxsbywgIiArIG5hbWUKICAgIGJ5dGVjXzEgLy8gIkhlbGxvLCAiCiAgICBmcmFtZV9kaWcgLTEKICAgIGNvbmNhdAogICAgcmV0c3ViCgoKLy8gc21hcnRfY29udHJhY3RzLmhlbGxvX3dvcmxkLmNvbnRyYWN0LkhlbGxvV29ybGQuaGVsbG8zKG5hbWU6IGJ5dGVzKSAtPiBieXRlczoKaGVsbG8zOgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjE0LTE1CiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIC8vIGRlZiBoZWxsbzMoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToxNgogICAgLy8gcmV0dXJuICJIZWxsbywgIiArIG5hbWUKICAgIGJ5dGVjXzEgLy8gIkhlbGxvLCAiCiAgICBmcmFtZV9kaWcgLTEKICAgIGNvbmNhdAogICAgcmV0c3ViCgoKLy8gc21hcnRfY29udHJhY3RzLmhlbGxvX3dvcmxkLmNvbnRyYWN0LkhlbGxvV29ybGQuaGVsbG80KG5hbWU6IGJ5dGVzKSAtPiBieXRlczoKaGVsbG80OgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjE4LTE5CiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIC8vIGRlZiBoZWxsbzQoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToyMAogICAgLy8gcmV0dXJuICJIZWxsbywgIiArIG5hbWUKICAgIGJ5dGVjXzEgLy8gIkhlbGxvLCAiCiAgICBmcmFtZV9kaWcgLTEKICAgIGNvbmNhdAogICAgcmV0c3ViCgoKLy8gc21hcnRfY29udHJhY3RzLmhlbGxvX3dvcmxkLmNvbnRyYWN0LkhlbGxvV29ybGQuaGVsbG81KG5hbWU6IGJ5dGVzKSAtPiBieXRlczoKaGVsbG81OgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjIyLTIzCiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIC8vIGRlZiBoZWxsbzUoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToyNAogICAgLy8gcmV0dXJuICJIZWxsbywgIiArIG5hbWUKICAgIGJ5dGVjXzEgLy8gIkhlbGxvLCAiCiAgICBmcmFtZV9kaWcgLTEKICAgIGNvbmNhdAogICAgcmV0c3ViCgoKLy8gc21hcnRfY29udHJhY3RzLmhlbGxvX3dvcmxkLmNvbnRyYWN0LkhlbGxvV29ybGQuaGVsbG82KG5hbWU6IGJ5dGVzKSAtPiBieXRlczoKaGVsbG82OgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjI2LTI3CiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIC8vIGRlZiBoZWxsbzYoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToyOAogICAgLy8gcmV0dXJuICJIZWxsbywgIiArIG5hbWUKICAgIGJ5dGVjXzEgLy8gIkhlbGxvLCAiCiAgICBmcmFtZV9kaWcgLTEKICAgIGNvbmNhdAogICAgcmV0c3ViCgoKLy8gc21hcnRfY29udHJhY3RzLmhlbGxvX3dvcmxkLmNvbnRyYWN0LkhlbGxvV29ybGQuaGVsbG83KG5hbWU6IGJ5dGVzKSAtPiBieXRlczoKaGVsbG83OgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjMwLTMxCiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIC8vIGRlZiBoZWxsbzcoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTozMgogICAgLy8gcmV0dXJuICJIZWxsbywgIiArIG5hbWUKICAgIGJ5dGVjXzEgLy8gIkhlbGxvLCAiCiAgICBmcmFtZV9kaWcgLTEKICAgIGNvbmNhdAogICAgcmV0c3ViCgoKLy8gc21hcnRfY29udHJhY3RzLmhlbGxvX3dvcmxkLmNvbnRyYWN0LkhlbGxvV29ybGQuaGVsbG84KG5hbWU6IGJ5dGVzKSAtPiBieXRlczoKaGVsbG84OgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjM0LTM1CiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIC8vIGRlZiBoZWxsbzgoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTozNgogICAgLy8gcmV0dXJuICJIZWxsbywgIiArIG5hbWUKICAgIGJ5dGVjXzEgLy8gIkhlbGxvLCAiCiAgICBmcmFtZV9kaWcgLTEKICAgIGNvbmNhdAogICAgcmV0c3ViCgoKLy8gc21hcnRfY29udHJhY3RzLmhlbGxvX3dvcmxkLmNvbnRyYWN0LkhlbGxvV29ybGQuaGVsbG85KG5hbWU6IGJ5dGVzKSAtPiBieXRlczoKaGVsbG85OgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjM4LTM5CiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIC8vIGRlZiBoZWxsbzkoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo0MAogICAgLy8gcmV0dXJuICJIZWxsbywgIiArIG5hbWUKICAgIGJ5dGVjXzEgLy8gIkhlbGxvLCAiCiAgICBmcmFtZV9kaWcgLTEKICAgIGNvbmNhdAogICAgcmV0c3ViCgoKLy8gc21hcnRfY29udHJhY3RzLmhlbGxvX3dvcmxkLmNvbnRyYWN0LkhlbGxvV29ybGQuaGVsbG8xMChuYW1lOiBieXRlcykgLT4gYnl0ZXM6CmhlbGxvMTA6CiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6NDItNDMKICAgIC8vIEBhYmltZXRob2QoKQogICAgLy8gZGVmIGhlbGxvMTAoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo0NAogICAgLy8gcmV0dXJuICJIZWxsbywgIiArIG5hbWUKICAgIGJ5dGVjXzEgLy8gIkhlbGxvLCAiCiAgICBmcmFtZV9kaWcgLTEKICAgIGNvbmNhdAogICAgcmV0c3ViCgoKLy8gc21hcnRfY29udHJhY3RzLmhlbGxvX3dvcmxkLmNvbnRyYWN0LkhlbGxvV29ybGQuaGVsbG8xMShuYW1lOiBieXRlcykgLT4gYnl0ZXM6CmhlbGxvMTE6CiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6NDYtNDcKICAgIC8vIEBhYmltZXRob2QoKQogICAgLy8gZGVmIGhlbGxvMTEoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo0OAogICAgLy8gcmV0dXJuICJIZWxsbywgIiArIG5hbWUKICAgIGJ5dGVjXzEgLy8gIkhlbGxvLCAiCiAgICBmcmFtZV9kaWcgLTEKICAgIGNvbmNhdAogICAgcmV0c3ViCgoKLy8gc21hcnRfY29udHJhY3RzLmhlbGxvX3dvcmxkLmNvbnRyYWN0LkhlbGxvV29ybGQuaGVsbG8xMihuYW1lOiBieXRlcykgLT4gYnl0ZXM6CmhlbGxvMTI6CiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6NTAtNTEKICAgIC8vIEBhYmltZXRob2QoKQogICAgLy8gZGVmIGhlbGxvMTIoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo1MgogICAgLy8gcmV0dXJuICJIZWxsbywgIiArIG5hbWUKICAgIGJ5dGVjXzEgLy8gIkhlbGxvLCAiCiAgICBmcmFtZV9kaWcgLTEKICAgIGNvbmNhdAogICAgcmV0c3ViCgoKLy8gc21hcnRfY29udHJhY3RzLmhlbGxvX3dvcmxkLmNvbnRyYWN0LkhlbGxvV29ybGQuaGVsbG8xMyhuYW1lOiBieXRlcykgLT4gYnl0ZXM6CmhlbGxvMTM6CiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6NTQtNTUKICAgIC8vIEBhYmltZXRob2QoKQogICAgLy8gZGVmIGhlbGxvMTMoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo1NgogICAgLy8gcmV0dXJuICJIZWxsbywgIiArIG5hbWUKICAgIGJ5dGVjXzEgLy8gIkhlbGxvLCAiCiAgICBmcmFtZV9kaWcgLTEKICAgIGNvbmNhdAogICAgcmV0c3ViCgoKLy8gc21hcnRfY29udHJhY3RzLmhlbGxvX3dvcmxkLmNvbnRyYWN0LkhlbGxvV29ybGQuaGVsbG8xNChuYW1lOiBieXRlcykgLT4gYnl0ZXM6CmhlbGxvMTQ6CiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6NTgtNTkKICAgIC8vIEBhYmltZXRob2QoKQogICAgLy8gZGVmIGhlbGxvMTQoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo2MAogICAgLy8gcmV0dXJuICJIZWxsbywgIiArIG5hbWUKICAgIGJ5dGVjXzEgLy8gIkhlbGxvLCAiCiAgICBmcmFtZV9kaWcgLTEKICAgIGNvbmNhdAogICAgcmV0c3ViCgoKLy8gc21hcnRfY29udHJhY3RzLmhlbGxvX3dvcmxkLmNvbnRyYWN0LkhlbGxvV29ybGQuaGVsbG8xNShuYW1lOiBieXRlcykgLT4gYnl0ZXM6CmhlbGxvMTU6CiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6NjItNjMKICAgIC8vIEBhYmltZXRob2QoKQogICAgLy8gZGVmIGhlbGxvMTUoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo2NAogICAgLy8gcmV0dXJuICJIZWxsbywgIiArIG5hbWUKICAgIGJ5dGVjXzEgLy8gIkhlbGxvLCAiCiAgICBmcmFtZV9kaWcgLTEKICAgIGNvbmNhdAogICAgcmV0c3ViCgoKLy8gc21hcnRfY29udHJhY3RzLmhlbGxvX3dvcmxkLmNvbnRyYWN0LkhlbGxvV29ybGQuaGVsbG8xNihuYW1lOiBieXRlcykgLT4gYnl0ZXM6CmhlbGxvMTY6CiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6NjYtNjcKICAgIC8vIEBhYmltZXRob2QoKQogICAgLy8gZGVmIGhlbGxvMTYoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo2OAogICAgLy8gcmV0dXJuICJIZWxsbywgIiArIG5hbWUKICAgIGJ5dGVjXzEgLy8gIkhlbGxvLCAiCiAgICBmcmFtZV9kaWcgLTEKICAgIGNvbmNhdAogICAgcmV0c3ViCgoKLy8gc21hcnRfY29udHJhY3RzLmhlbGxvX3dvcmxkLmNvbnRyYWN0LkhlbGxvV29ybGQuaGVsbG8xNyhuYW1lOiBieXRlcykgLT4gYnl0ZXM6CmhlbGxvMTc6CiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6NzAtNzEKICAgIC8vIEBhYmltZXRob2QoKQogICAgLy8gZGVmIGhlbGxvMTcoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo3MgogICAgLy8gcmV0dXJuICJIZWxsbywgIiArIG5hbWUKICAgIGJ5dGVjXzEgLy8gIkhlbGxvLCAiCiAgICBmcmFtZV9kaWcgLTEKICAgIGNvbmNhdAogICAgcmV0c3ViCgoKLy8gc21hcnRfY29udHJhY3RzLmhlbGxvX3dvcmxkLmNvbnRyYWN0LkhlbGxvV29ybGQuaGVsbG8xOChuYW1lOiBieXRlcykgLT4gYnl0ZXM6CmhlbGxvMTg6CiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6NzQtNzUKICAgIC8vIEBhYmltZXRob2QoKQogICAgLy8gZGVmIGhlbGxvMTgoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo3NgogICAgLy8gcmV0dXJuICJIZWxsbywgIiArIG5hbWUKICAgIGJ5dGVjXzEgLy8gIkhlbGxvLCAiCiAgICBmcmFtZV9kaWcgLTEKICAgIGNvbmNhdAogICAgcmV0c3ViCgoKLy8gc21hcnRfY29udHJhY3RzLmhlbGxvX3dvcmxkLmNvbnRyYWN0LkhlbGxvV29ybGQuaGVsbG8xOShuYW1lOiBieXRlcykgLT4gYnl0ZXM6CmhlbGxvMTk6CiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6NzgtNzkKICAgIC8vIEBhYmltZXRob2QoKQogICAgLy8gZGVmIGhlbGxvMTkoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo4MAogICAgLy8gcmV0dXJuICJIZWxsbywgIiArIG5hbWUKICAgIGJ5dGVjXzEgLy8gIkhlbGxvLCAiCiAgICBmcmFtZV9kaWcgLTEKICAgIGNvbmNhdAogICAgcmV0c3ViCgoKLy8gc21hcnRfY29udHJhY3RzLmhlbGxvX3dvcmxkLmNvbnRyYWN0LkhlbGxvV29ybGQuaGVsbG8yMChuYW1lOiBieXRlcykgLT4gYnl0ZXM6CmhlbGxvMjA6CiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6ODItODMKICAgIC8vIEBhYmltZXRob2QoKQogICAgLy8gZGVmIGhlbGxvMjAoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo4NAogICAgLy8gcmV0dXJuICJIZWxsbywgIiArIG5hbWUKICAgIGJ5dGVjXzEgLy8gIkhlbGxvLCAiCiAgICBmcmFtZV9kaWcgLTEKICAgIGNvbmNhdAogICAgcmV0c3ViCgoKLy8gc21hcnRfY29udHJhY3RzLmhlbGxvX3dvcmxkLmNvbnRyYWN0LkhlbGxvV29ybGQuaGVsbG8yMShuYW1lOiBieXRlcykgLT4gYnl0ZXM6CmhlbGxvMjE6CiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6ODYtODcKICAgIC8vIEBhYmltZXRob2QoKQogICAgLy8gZGVmIGhlbGxvMjEoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo4OAogICAgLy8gcmV0dXJuICJIZWxsbywgIiArIG5hbWUKICAgIGJ5dGVjXzEgLy8gIkhlbGxvLCAiCiAgICBmcmFtZV9kaWcgLTEKICAgIGNvbmNhdAogICAgcmV0c3ViCgoKLy8gc21hcnRfY29udHJhY3RzLmhlbGxvX3dvcmxkLmNvbnRyYWN0LkhlbGxvV29ybGQuaGVsbG8yMihuYW1lOiBieXRlcykgLT4gYnl0ZXM6CmhlbGxvMjI6CiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6OTAtOTEKICAgIC8vIEBhYmltZXRob2QoKQogICAgLy8gZGVmIGhlbGxvMjIoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo5MgogICAgLy8gcmV0dXJuICJIZWxsbywgIiArIG5hbWUKICAgIGJ5dGVjXzEgLy8gIkhlbGxvLCAiCiAgICBmcmFtZV9kaWcgLTEKICAgIGNvbmNhdAogICAgcmV0c3ViCgoKLy8gc21hcnRfY29udHJhY3RzLmhlbGxvX3dvcmxkLmNvbnRyYWN0LkhlbGxvV29ybGQuaGVsbG8yMyhuYW1lOiBieXRlcykgLT4gYnl0ZXM6CmhlbGxvMjM6CiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6OTQtOTUKICAgIC8vIEBhYmltZXRob2QoKQogICAgLy8gZGVmIGhlbGxvMjMoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo5NgogICAgLy8gcmV0dXJuICJIZWxsbywgIiArIG5hbWUKICAgIGJ5dGVjXzEgLy8gIkhlbGxvLCAiCiAgICBmcmFtZV9kaWcgLTEKICAgIGNvbmNhdAogICAgcmV0c3ViCgoKLy8gc21hcnRfY29udHJhY3RzLmhlbGxvX3dvcmxkLmNvbnRyYWN0LkhlbGxvV29ybGQuaGVsbG8yNChuYW1lOiBieXRlcykgLT4gYnl0ZXM6CmhlbGxvMjQ6CiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6OTgtOTkKICAgIC8vIEBhYmltZXRob2QoKQogICAgLy8gZGVmIGhlbGxvMjQoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToxMDAKICAgIC8vIHJldHVybiAiSGVsbG8sICIgKyBuYW1lCiAgICBieXRlY18xIC8vICJIZWxsbywgIgogICAgZnJhbWVfZGlnIC0xCiAgICBjb25jYXQKICAgIHJldHN1YgoKCi8vIHNtYXJ0X2NvbnRyYWN0cy5oZWxsb193b3JsZC5jb250cmFjdC5IZWxsb1dvcmxkLmhlbGxvMjUobmFtZTogYnl0ZXMpIC0+IGJ5dGVzOgpoZWxsbzI1OgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjEwMi0xMDMKICAgIC8vIEBhYmltZXRob2QoKQogICAgLy8gZGVmIGhlbGxvMjUoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToxMDQKICAgIC8vIHJldHVybiAiSGVsbG8sICIgKyBuYW1lCiAgICBieXRlY18xIC8vICJIZWxsbywgIgogICAgZnJhbWVfZGlnIC0xCiAgICBjb25jYXQKICAgIHJldHN1YgoKCi8vIHNtYXJ0X2NvbnRyYWN0cy5oZWxsb193b3JsZC5jb250cmFjdC5IZWxsb1dvcmxkLmhlbGxvMjYobmFtZTogYnl0ZXMpIC0+IGJ5dGVzOgpoZWxsbzI2OgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjEwNi0xMDcKICAgIC8vIEBhYmltZXRob2QoKQogICAgLy8gZGVmIGhlbGxvMjYoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToxMDgKICAgIC8vIHJldHVybiAiSGVsbG8sICIgKyBuYW1lCiAgICBieXRlY18xIC8vICJIZWxsbywgIgogICAgZnJhbWVfZGlnIC0xCiAgICBjb25jYXQKICAgIHJldHN1YgoKCi8vIHNtYXJ0X2NvbnRyYWN0cy5oZWxsb193b3JsZC5jb250cmFjdC5IZWxsb1dvcmxkLmhlbGxvMjcobmFtZTogYnl0ZXMpIC0+IGJ5dGVzOgpoZWxsbzI3OgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjExMC0xMTEKICAgIC8vIEBhYmltZXRob2QoKQogICAgLy8gZGVmIGhlbGxvMjcoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToxMTIKICAgIC8vIHJldHVybiAiSGVsbG8sICIgKyBuYW1lCiAgICBieXRlY18xIC8vICJIZWxsbywgIgogICAgZnJhbWVfZGlnIC0xCiAgICBjb25jYXQKICAgIHJldHN1YgoKCi8vIHNtYXJ0X2NvbnRyYWN0cy5oZWxsb193b3JsZC5jb250cmFjdC5IZWxsb1dvcmxkLmhlbGxvMjgobmFtZTogYnl0ZXMpIC0+IGJ5dGVzOgpoZWxsbzI4OgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjExNC0xMTUKICAgIC8vIEBhYmltZXRob2QoKQogICAgLy8gZGVmIGhlbGxvMjgoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToxMTYKICAgIC8vIHJldHVybiAiSGVsbG8sICIgKyBuYW1lCiAgICBieXRlY18xIC8vICJIZWxsbywgIgogICAgZnJhbWVfZGlnIC0xCiAgICBjb25jYXQKICAgIHJldHN1YgoKCi8vIHNtYXJ0X2NvbnRyYWN0cy5oZWxsb193b3JsZC5jb250cmFjdC5IZWxsb1dvcmxkLmhlbGxvMjkobmFtZTogYnl0ZXMpIC0+IGJ5dGVzOgpoZWxsbzI5OgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjExOC0xMTkKICAgIC8vIEBhYmltZXRob2QoKQogICAgLy8gZGVmIGhlbGxvMjkoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToxMjAKICAgIC8vIHJldHVybiAiSGVsbG8sICIgKyBuYW1lCiAgICBieXRlY18xIC8vICJIZWxsbywgIgogICAgZnJhbWVfZGlnIC0xCiAgICBjb25jYXQKICAgIHJldHN1YgoKCi8vIHNtYXJ0X2NvbnRyYWN0cy5oZWxsb193b3JsZC5jb250cmFjdC5IZWxsb1dvcmxkLmhlbGxvMzAobmFtZTogYnl0ZXMpIC0+IGJ5dGVzOgpoZWxsbzMwOgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjEyMi0xMjMKICAgIC8vIEBhYmltZXRob2QoKQogICAgLy8gZGVmIGhlbGxvMzAoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToxMjQKICAgIC8vIHJldHVybiAiSGVsbG8sICIgKyBuYW1lCiAgICBieXRlY18xIC8vICJIZWxsbywgIgogICAgZnJhbWVfZGlnIC0xCiAgICBjb25jYXQKICAgIHJldHN1YgoKCi8vIHNtYXJ0X2NvbnRyYWN0cy5oZWxsb193b3JsZC5jb250cmFjdC5IZWxsb1dvcmxkLmhlbGxvMzEobmFtZTogYnl0ZXMpIC0+IGJ5dGVzOgpoZWxsbzMxOgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjEyNi0xMjcKICAgIC8vIEBhYmltZXRob2QoKQogICAgLy8gZGVmIGhlbGxvMzEoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToxMjgKICAgIC8vIHJldHVybiAiSGVsbG8sICIgKyBuYW1lCiAgICBieXRlY18xIC8vICJIZWxsbywgIgogICAgZnJhbWVfZGlnIC0xCiAgICBjb25jYXQKICAgIHJldHN1YgoKCi8vIHNtYXJ0X2NvbnRyYWN0cy5oZWxsb193b3JsZC5jb250cmFjdC5IZWxsb1dvcmxkLmhlbGxvMzIobmFtZTogYnl0ZXMpIC0+IGJ5dGVzOgpoZWxsbzMyOgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjEzMC0xMzEKICAgIC8vIEBhYmltZXRob2QoKQogICAgLy8gZGVmIGhlbGxvMzIoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToxMzIKICAgIC8vIHJldHVybiAiSGVsbG8sICIgKyBuYW1lCiAgICBieXRlY18xIC8vICJIZWxsbywgIgogICAgZnJhbWVfZGlnIC0xCiAgICBjb25jYXQKICAgIHJldHN1YgoKCi8vIHNtYXJ0X2NvbnRyYWN0cy5oZWxsb193b3JsZC5jb250cmFjdC5IZWxsb1dvcmxkLmhlbGxvMzMobmFtZTogYnl0ZXMpIC0+IGJ5dGVzOgpoZWxsbzMzOgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjEzNC0xMzUKICAgIC8vIEBhYmltZXRob2QoKQogICAgLy8gZGVmIGhlbGxvMzMoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToxMzYKICAgIC8vIHJldHVybiAiSGVsbG8sICIgKyBuYW1lCiAgICBieXRlY18xIC8vICJIZWxsbywgIgogICAgZnJhbWVfZGlnIC0xCiAgICBjb25jYXQKICAgIHJldHN1YgoKCi8vIHNtYXJ0X2NvbnRyYWN0cy5oZWxsb193b3JsZC5jb250cmFjdC5IZWxsb1dvcmxkLmhlbGxvMzQobmFtZTogYnl0ZXMpIC0+IGJ5dGVzOgpoZWxsbzM0OgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjEzOC0xMzkKICAgIC8vIEBhYmltZXRob2QoKQogICAgLy8gZGVmIGhlbGxvMzQoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToxNDAKICAgIC8vIHJldHVybiAiSGVsbG8sICIgKyBuYW1lCiAgICBieXRlY18xIC8vICJIZWxsbywgIgogICAgZnJhbWVfZGlnIC0xCiAgICBjb25jYXQKICAgIHJldHN1YgoKCi8vIHNtYXJ0X2NvbnRyYWN0cy5oZWxsb193b3JsZC5jb250cmFjdC5IZWxsb1dvcmxkLmhlbGxvMzUobmFtZTogYnl0ZXMpIC0+IGJ5dGVzOgpoZWxsbzM1OgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjE0Mi0xNDMKICAgIC8vIEBhYmltZXRob2QoKQogICAgLy8gZGVmIGhlbGxvMzUoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToxNDQKICAgIC8vIHJldHVybiAiSGVsbG8sICIgKyBuYW1lCiAgICBieXRlY18xIC8vICJIZWxsbywgIgogICAgZnJhbWVfZGlnIC0xCiAgICBjb25jYXQKICAgIHJldHN1YgoKCi8vIHNtYXJ0X2NvbnRyYWN0cy5oZWxsb193b3JsZC5jb250cmFjdC5IZWxsb1dvcmxkLmhlbGxvMzYobmFtZTogYnl0ZXMpIC0+IGJ5dGVzOgpoZWxsbzM2OgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjE0Ni0xNDcKICAgIC8vIEBhYmltZXRob2QoKQogICAgLy8gZGVmIGhlbGxvMzYoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToxNDgKICAgIC8vIHJldHVybiAiSGVsbG8sICIgKyBuYW1lCiAgICBieXRlY18xIC8vICJIZWxsbywgIgogICAgZnJhbWVfZGlnIC0xCiAgICBjb25jYXQKICAgIHJldHN1YgoKCi8vIHNtYXJ0X2NvbnRyYWN0cy5oZWxsb193b3JsZC5jb250cmFjdC5IZWxsb1dvcmxkLmhlbGxvMzcobmFtZTogYnl0ZXMpIC0+IGJ5dGVzOgpoZWxsbzM3OgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjE1MC0xNTEKICAgIC8vIEBhYmltZXRob2QoKQogICAgLy8gZGVmIGhlbGxvMzcoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToxNTIKICAgIC8vIHJldHVybiAiSGVsbG8sICIgKyBuYW1lCiAgICBieXRlY18xIC8vICJIZWxsbywgIgogICAgZnJhbWVfZGlnIC0xCiAgICBjb25jYXQKICAgIHJldHN1YgoKCi8vIHNtYXJ0X2NvbnRyYWN0cy5oZWxsb193b3JsZC5jb250cmFjdC5IZWxsb1dvcmxkLmhlbGxvMzgobmFtZTogYnl0ZXMpIC0+IGJ5dGVzOgpoZWxsbzM4OgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjE1NC0xNTUKICAgIC8vIEBhYmltZXRob2QoKQogICAgLy8gZGVmIGhlbGxvMzgoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToxNTYKICAgIC8vIHJldHVybiAiSGVsbG8sICIgKyBuYW1lCiAgICBieXRlY18xIC8vICJIZWxsbywgIgogICAgZnJhbWVfZGlnIC0xCiAgICBjb25jYXQKICAgIHJldHN1YgoKCi8vIHNtYXJ0X2NvbnRyYWN0cy5oZWxsb193b3JsZC5jb250cmFjdC5IZWxsb1dvcmxkLmhlbGxvMzkobmFtZTogYnl0ZXMpIC0+IGJ5dGVzOgpoZWxsbzM5OgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjE1OC0xNTkKICAgIC8vIEBhYmltZXRob2QoKQogICAgLy8gZGVmIGhlbGxvMzkoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToxNjAKICAgIC8vIHJldHVybiAiSGVsbG8sICIgKyBuYW1lCiAgICBieXRlY18xIC8vICJIZWxsbywgIgogICAgZnJhbWVfZGlnIC0xCiAgICBjb25jYXQKICAgIHJldHN1YgoKCi8vIHNtYXJ0X2NvbnRyYWN0cy5oZWxsb193b3JsZC5jb250cmFjdC5IZWxsb1dvcmxkLmhlbGxvNDAobmFtZTogYnl0ZXMpIC0+IGJ5dGVzOgpoZWxsbzQwOgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjE2Mi0xNjMKICAgIC8vIEBhYmltZXRob2QoKQogICAgLy8gZGVmIGhlbGxvNDAoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToxNjQKICAgIC8vIHJldHVybiAiSGVsbG8sICIgKyBuYW1lCiAgICBieXRlY18xIC8vICJIZWxsbywgIgogICAgZnJhbWVfZGlnIC0xCiAgICBjb25jYXQKICAgIHJldHN1YgoKCi8vIHNtYXJ0X2NvbnRyYWN0cy5oZWxsb193b3JsZC5jb250cmFjdC5IZWxsb1dvcmxkLmhlbGxvNDEobmFtZTogYnl0ZXMpIC0+IGJ5dGVzOgpoZWxsbzQxOgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjE2Ni0xNjcKICAgIC8vIEBhYmltZXRob2QoKQogICAgLy8gZGVmIGhlbGxvNDEoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToxNjgKICAgIC8vIHJldHVybiAiSGVsbG8sICIgKyBuYW1lCiAgICBieXRlY18xIC8vICJIZWxsbywgIgogICAgZnJhbWVfZGlnIC0xCiAgICBjb25jYXQKICAgIHJldHN1YgoKCi8vIHNtYXJ0X2NvbnRyYWN0cy5oZWxsb193b3JsZC5jb250cmFjdC5IZWxsb1dvcmxkLmhlbGxvNDIobmFtZTogYnl0ZXMpIC0+IGJ5dGVzOgpoZWxsbzQyOgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjE3MC0xNzEKICAgIC8vIEBhYmltZXRob2QoKQogICAgLy8gZGVmIGhlbGxvNDIoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToxNzIKICAgIC8vIHJldHVybiAiSGVsbG8sICIgKyBuYW1lCiAgICBieXRlY18xIC8vICJIZWxsbywgIgogICAgZnJhbWVfZGlnIC0xCiAgICBjb25jYXQKICAgIHJldHN1YgoKCi8vIHNtYXJ0X2NvbnRyYWN0cy5oZWxsb193b3JsZC5jb250cmFjdC5IZWxsb1dvcmxkLmhlbGxvNDMobmFtZTogYnl0ZXMpIC0+IGJ5dGVzOgpoZWxsbzQzOgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjE3NC0xNzUKICAgIC8vIEBhYmltZXRob2QoKQogICAgLy8gZGVmIGhlbGxvNDMoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToxNzYKICAgIC8vIHJldHVybiAiSGVsbG8sICIgKyBuYW1lCiAgICBieXRlY18xIC8vICJIZWxsbywgIgogICAgZnJhbWVfZGlnIC0xCiAgICBjb25jYXQKICAgIHJldHN1YgoKCi8vIHNtYXJ0X2NvbnRyYWN0cy5oZWxsb193b3JsZC5jb250cmFjdC5IZWxsb1dvcmxkLmhlbGxvNDQobmFtZTogYnl0ZXMpIC0+IGJ5dGVzOgpoZWxsbzQ0OgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjE3OC0xNzkKICAgIC8vIEBhYmltZXRob2QoKQogICAgLy8gZGVmIGhlbGxvNDQoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToxODAKICAgIC8vIHJldHVybiAiSGVsbG8sICIgKyBuYW1lCiAgICBieXRlY18xIC8vICJIZWxsbywgIgogICAgZnJhbWVfZGlnIC0xCiAgICBjb25jYXQKICAgIHJldHN1YgoKCi8vIHNtYXJ0X2NvbnRyYWN0cy5oZWxsb193b3JsZC5jb250cmFjdC5IZWxsb1dvcmxkLmhlbGxvNDUobmFtZTogYnl0ZXMpIC0+IGJ5dGVzOgpoZWxsbzQ1OgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjE4Mi0xODMKICAgIC8vIEBhYmltZXRob2QoKQogICAgLy8gZGVmIGhlbGxvNDUoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToxODQKICAgIC8vIHJldHVybiAiSGVsbG8sICIgKyBuYW1lCiAgICBieXRlY18xIC8vICJIZWxsbywgIgogICAgZnJhbWVfZGlnIC0xCiAgICBjb25jYXQKICAgIHJldHN1YgoKCi8vIHNtYXJ0X2NvbnRyYWN0cy5oZWxsb193b3JsZC5jb250cmFjdC5IZWxsb1dvcmxkLmhlbGxvNDYobmFtZTogYnl0ZXMpIC0+IGJ5dGVzOgpoZWxsbzQ2OgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjE4Ni0xODcKICAgIC8vIEBhYmltZXRob2QoKQogICAgLy8gZGVmIGhlbGxvNDYoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToxODgKICAgIC8vIHJldHVybiAiSGVsbG8sICIgKyBuYW1lCiAgICBieXRlY18xIC8vICJIZWxsbywgIgogICAgZnJhbWVfZGlnIC0xCiAgICBjb25jYXQKICAgIHJldHN1YgoKCi8vIHNtYXJ0X2NvbnRyYWN0cy5oZWxsb193b3JsZC5jb250cmFjdC5IZWxsb1dvcmxkLmhlbGxvNDcobmFtZTogYnl0ZXMpIC0+IGJ5dGVzOgpoZWxsbzQ3OgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjE5MC0xOTEKICAgIC8vIEBhYmltZXRob2QoKQogICAgLy8gZGVmIGhlbGxvNDcoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToxOTIKICAgIC8vIHJldHVybiAiSGVsbG8sICIgKyBuYW1lCiAgICBieXRlY18xIC8vICJIZWxsbywgIgogICAgZnJhbWVfZGlnIC0xCiAgICBjb25jYXQKICAgIHJldHN1YgoKCi8vIHNtYXJ0X2NvbnRyYWN0cy5oZWxsb193b3JsZC5jb250cmFjdC5IZWxsb1dvcmxkLmhlbGxvNDgobmFtZTogYnl0ZXMpIC0+IGJ5dGVzOgpoZWxsbzQ4OgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjE5NC0xOTUKICAgIC8vIEBhYmltZXRob2QoKQogICAgLy8gZGVmIGhlbGxvNDgoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToxOTYKICAgIC8vIHJldHVybiAiSGVsbG8sICIgKyBuYW1lCiAgICBieXRlY18xIC8vICJIZWxsbywgIgogICAgZnJhbWVfZGlnIC0xCiAgICBjb25jYXQKICAgIHJldHN1YgoKCi8vIHNtYXJ0X2NvbnRyYWN0cy5oZWxsb193b3JsZC5jb250cmFjdC5IZWxsb1dvcmxkLmhlbGxvNDkobmFtZTogYnl0ZXMpIC0+IGJ5dGVzOgpoZWxsbzQ5OgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjE5OC0xOTkKICAgIC8vIEBhYmltZXRob2QoKQogICAgLy8gZGVmIGhlbGxvNDkoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToyMDAKICAgIC8vIHJldHVybiAiSGVsbG8sICIgKyBuYW1lCiAgICBieXRlY18xIC8vICJIZWxsbywgIgogICAgZnJhbWVfZGlnIC0xCiAgICBjb25jYXQKICAgIHJldHN1YgoKCi8vIHNtYXJ0X2NvbnRyYWN0cy5oZWxsb193b3JsZC5jb250cmFjdC5IZWxsb1dvcmxkLmhlbGxvNTAobmFtZTogYnl0ZXMpIC0+IGJ5dGVzOgpoZWxsbzUwOgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjIwMi0yMDMKICAgIC8vIEBhYmltZXRob2QoKQogICAgLy8gZGVmIGhlbGxvNTAoc2VsZiwgbmFtZTogU3RyaW5nKSAtPiBTdHJpbmc6CiAgICBwcm90byAxIDEKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToyMDQKICAgIC8vIHJldHVybiAiSGVsbG8sICIgKyBuYW1lCiAgICBieXRlY18xIC8vICJIZWxsbywgIgogICAgZnJhbWVfZGlnIC0xCiAgICBjb25jYXQKICAgIHJldHN1YgoKCi8vIHNtYXJ0X2NvbnRyYWN0cy5oZWxsb193b3JsZC5jb250cmFjdC5IZWxsb1dvcmxkLnVwZGF0ZSgpIC0+IHZvaWQ6CnVwZGF0ZToKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToyMDYtMjA3CiAgICAvLyBAYmFyZW1ldGhvZChhbGxvd19hY3Rpb25zPVsiVXBkYXRlQXBwbGljYXRpb24iXSkKICAgIC8vIGRlZiB1cGRhdGUoc2VsZikgLT4gTm9uZToKICAgIHByb3RvIDAgMAogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjIwOAogICAgLy8gYXNzZXJ0IFRlbXBsYXRlVmFyW2Jvb2xdKCJVUERBVEFCTEUiKSwgIkNoZWNrIGFwcCBpcyB1cGRhdGFibGUiCiAgICBpbnRjXzIgLy8gVE1QTF9VUERBVEFCTEUKICAgIGFzc2VydCAvLyBDaGVjayBhcHAgaXMgdXBkYXRhYmxlCiAgICByZXRzdWIK", + "clear": "I3ByYWdtYSB2ZXJzaW9uIDEwCiNwcmFnbWEgdHlwZXRyYWNrIGZhbHNlCgovLyBhbGdvcHkuYXJjNC5BUkM0Q29udHJhY3QuY2xlYXJfc3RhdGVfcHJvZ3JhbSgpIC0+IHVpbnQ2NDoKbWFpbjoKICAgIHB1c2hpbnQgMSAvLyAxCiAgICByZXR1cm4K" + }, + "byteCode": { + "approval": "CiADAQAAJgIEFR98dQdIZWxsbywgMRtBB0OCMgQCvs4RBG+XdcMEHGWWqQQLDMJ5BNjzGmYE0MxDhgQ/4b+6BKt7v0AE9mwWSQRC8auNBGas8ZgE7OwdWQT2CWlgBAnnkgMEA6Fr8QRUIB+IBBMn3qsE+PXEhQTDhCVTBO9fiXYEKAPhlwQoByqGBJpgaSkEpWUK5ASR2bKzBO6Hq/MEJ3kAcgR//NQUBLw0znIE7qlXqQQZZSJ3BEND4d8EKl+MSwQ96/m4BPCSVogEA+8XnwSwmQwOBMaGv8EEWILGlgRKTPnOBPh9k8ME8/XIwwSZQuaKBBU122wE9pRgjQTR84uFBJxUUqoED5jRrQQbYiQTBKnkvB02GgCOMgXABaIFhAVmBUgFKgUMBO4E0ASyBJQEdgRYBDoEHAP+A+ADwgOkA4YDaANKAywDDgLwAtICtAKWAngCWgI8Ah4CAAHiAcQBpgGIAWoBTAEuARAA8gDUALYAmAB6AFwAPgAgAAIjQzEZFEQxGEQ2GgFXAgCIB3BJFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIB0pJFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIByRJFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIBv5JFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIBthJFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIBrJJFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIBoxJFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIBmZJFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIBkBJFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIBhpJFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIBfRJFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIBc5JFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIBahJFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIBYJJFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIBVxJFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIBTZJFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIBRBJFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIBOpJFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIBMRJFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIBJ5JFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIBHhJFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIBFJJFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIBCxJFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIBAZJFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIA+BJFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIA7pJFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIA5RJFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIA25JFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIA0hJFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIAyJJFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIAvxJFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIAtZJFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIArBJFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIAopJFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIAmRJFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIAj5JFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIAhhJFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIAfJJFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIAcxJFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIAaZJFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIAYBJFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIAVpJFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIATRJFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIAQ5JFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIAOhJFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIAMJJFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIAJxJFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIAHZJFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIAFBJFRZXBgJMUChMULAiQzEZFEQxGEQ2GgFXAgCIACpJFRZXBgJMUChMULAiQ4EEIzEZjgIACQADQvoUMRgURCJDMRhEiAGSIkOKAQEpi/9QiYoBASmL/1CJigEBKYv/UImKAQEpi/9QiYoBASmL/1CJigEBKYv/UImKAQEpi/9QiYoBASmL/1CJigEBKYv/UImKAQEpi/9QiYoBASmL/1CJigEBKYv/UImKAQEpi/9QiYoBASmL/1CJigEBKYv/UImKAQEpi/9QiYoBASmL/1CJigEBKYv/UImKAQEpi/9QiYoBASmL/1CJigEBKYv/UImKAQEpi/9QiYoBASmL/1CJigEBKYv/UImKAQEpi/9QiYoBASmL/1CJigEBKYv/UImKAQEpi/9QiYoBASmL/1CJigEBKYv/UImKAQEpi/9QiYoBASmL/1CJigEBKYv/UImKAQEpi/9QiYoBASmL/1CJigEBKYv/UImKAQEpi/9QiYoBASmL/1CJigEBKYv/UImKAQEpi/9QiYoBASmL/1CJigEBKYv/UImKAQEpi/9QiYoBASmL/1CJigEBKYv/UImKAQEpi/9QiYoBASmL/1CJigEBKYv/UImKAQEpi/9QiYoBASmL/1CJigAAJESJ", + "clear": "CoEBQw==" + }, + "compilerInfo": { + "compiler": "puya", + "compilerVersion": { + "major": 4, + "minor": 3, + "patch": 3 + } + }, + "events": [], + "templateVariables": { + "UPDATABLE": { + "type": "AVMUint64" + } + } +} diff --git a/crates/algokit_test_artifacts/contracts/extra_pages_test/small.arc56.json b/crates/algokit_test_artifacts/contracts/extra_pages_test/small.arc56.json new file mode 100644 index 000000000..973467f43 --- /dev/null +++ b/crates/algokit_test_artifacts/contracts/extra_pages_test/small.arc56.json @@ -0,0 +1,120 @@ +{ + "name": "HelloWorld", + "structs": {}, + "methods": [ + { + "name": "hello", + "args": [ + { + "type": "string", + "name": "name" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + } + ], + "arcs": [ + 22, + 28 + ], + "networks": {}, + "state": { + "schema": { + "global": { + "ints": 0, + "bytes": 0 + }, + "local": { + "ints": 0, + "bytes": 0 + } + }, + "keys": { + "global": {}, + "local": {}, + "box": {} + }, + "maps": { + "global": {}, + "local": {}, + "box": {} + } + }, + "bareActions": { + "create": [ + "NoOp" + ], + "call": [ + "UpdateApplication" + ] + }, + "sourceInfo": { + "approval": { + "sourceInfo": [ + { + "pc": [ + 103 + ], + "errorMessage": "Check app is updatable" + }, + { + "pc": [ + 23 + ], + "errorMessage": "OnCompletion is not NoOp" + }, + { + "pc": [ + 72 + ], + "errorMessage": "can only call when creating" + }, + { + "pc": [ + 26, + 77 + ], + "errorMessage": "can only call when not creating" + } + ], + "pcOffsetMethod": "cblocks" + }, + "clear": { + "sourceInfo": [], + "pcOffsetMethod": "none" + } + }, + "source": { + "approval": "I3ByYWdtYSB2ZXJzaW9uIDEwCiNwcmFnbWEgdHlwZXRyYWNrIGZhbHNlCgovLyBhbGdvcHkuYXJjNC5BUkM0Q29udHJhY3QuYXBwcm92YWxfcHJvZ3JhbSgpIC0+IHVpbnQ2NDoKbWFpbjoKICAgIGludGNibG9jayAxIDAgVE1QTF9VUERBVEFCTEUKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo1CiAgICAvLyBjbGFzcyBIZWxsb1dvcmxkKEFSQzRDb250cmFjdCk6CiAgICB0eG4gTnVtQXBwQXJncwogICAgYnogbWFpbl9iYXJlX3JvdXRpbmdANgogICAgcHVzaGJ5dGVzIDB4MDJiZWNlMTEgLy8gbWV0aG9kICJoZWxsbyhzdHJpbmcpc3RyaW5nIgogICAgdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMAogICAgbWF0Y2ggbWFpbl9oZWxsb19yb3V0ZUAzCgptYWluX2FmdGVyX2lmX2Vsc2VAMTE6CiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6NQogICAgLy8gY2xhc3MgSGVsbG9Xb3JsZChBUkM0Q29udHJhY3QpOgogICAgaW50Y18xIC8vIDAKICAgIHJldHVybgoKbWFpbl9oZWxsb19yb3V0ZUAzOgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjYKICAgIC8vIEBhYmltZXRob2QoKQogICAgdHhuIE9uQ29tcGxldGlvbgogICAgIQogICAgYXNzZXJ0IC8vIE9uQ29tcGxldGlvbiBpcyBub3QgTm9PcAogICAgdHhuIEFwcGxpY2F0aW9uSUQKICAgIGFzc2VydCAvLyBjYW4gb25seSBjYWxsIHdoZW4gbm90IGNyZWF0aW5nCiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6NQogICAgLy8gY2xhc3MgSGVsbG9Xb3JsZChBUkM0Q29udHJhY3QpOgogICAgdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMQogICAgZXh0cmFjdCAyIDAKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo2CiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIGNhbGxzdWIgaGVsbG8KICAgIGR1cAogICAgbGVuCiAgICBpdG9iCiAgICBleHRyYWN0IDYgMgogICAgc3dhcAogICAgY29uY2F0CiAgICBwdXNoYnl0ZXMgMHgxNTFmN2M3NQogICAgc3dhcAogICAgY29uY2F0CiAgICBsb2cKICAgIGludGNfMCAvLyAxCiAgICByZXR1cm4KCm1haW5fYmFyZV9yb3V0aW5nQDY6CiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6NQogICAgLy8gY2xhc3MgSGVsbG9Xb3JsZChBUkM0Q29udHJhY3QpOgogICAgcHVzaGludCA0IC8vIDQKICAgIGludGNfMSAvLyAwCiAgICB0eG4gT25Db21wbGV0aW9uCiAgICBtYXRjaCBtYWluX3VwZGF0ZUA3IG1haW5fX19hbGdvcHlfZGVmYXVsdF9jcmVhdGVAOAogICAgYiBtYWluX2FmdGVyX2lmX2Vsc2VAMTEKCm1haW5fX19hbGdvcHlfZGVmYXVsdF9jcmVhdGVAODoKICAgIHR4biBBcHBsaWNhdGlvbklECiAgICAhCiAgICBhc3NlcnQgLy8gY2FuIG9ubHkgY2FsbCB3aGVuIGNyZWF0aW5nCiAgICBpbnRjXzAgLy8gMQogICAgcmV0dXJuCgptYWluX3VwZGF0ZUA3OgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjIwNgogICAgLy8gQGJhcmVtZXRob2QoYWxsb3dfYWN0aW9ucz1bIlVwZGF0ZUFwcGxpY2F0aW9uIl0pCiAgICB0eG4gQXBwbGljYXRpb25JRAogICAgYXNzZXJ0IC8vIGNhbiBvbmx5IGNhbGwgd2hlbiBub3QgY3JlYXRpbmcKICAgIGNhbGxzdWIgdXBkYXRlCiAgICBpbnRjXzAgLy8gMQogICAgcmV0dXJuCgoKLy8gc21hcnRfY29udHJhY3RzLmhlbGxvX3dvcmxkLmNvbnRyYWN0LkhlbGxvV29ybGQuaGVsbG8obmFtZTogYnl0ZXMpIC0+IGJ5dGVzOgpoZWxsbzoKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo2LTcKICAgIC8vIEBhYmltZXRob2QoKQogICAgLy8gZGVmIGhlbGxvKHNlbGYsIG5hbWU6IFN0cmluZykgLT4gU3RyaW5nOgogICAgcHJvdG8gMSAxCiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6OAogICAgLy8gcmV0dXJuICJIZWxsbywgIiArIG5hbWUKICAgIHB1c2hieXRlcyAiSGVsbG8sICIKICAgIGZyYW1lX2RpZyAtMQogICAgY29uY2F0CiAgICByZXRzdWIKCgovLyBzbWFydF9jb250cmFjdHMuaGVsbG9fd29ybGQuY29udHJhY3QuSGVsbG9Xb3JsZC51cGRhdGUoKSAtPiB2b2lkOgp1cGRhdGU6CiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6MjA2LTIwNwogICAgLy8gQGJhcmVtZXRob2QoYWxsb3dfYWN0aW9ucz1bIlVwZGF0ZUFwcGxpY2F0aW9uIl0pCiAgICAvLyBkZWYgdXBkYXRlKHNlbGYpIC0+IE5vbmU6CiAgICBwcm90byAwIDAKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weToyMDgKICAgIC8vIGFzc2VydCBUZW1wbGF0ZVZhcltib29sXSgiVVBEQVRBQkxFIiksICJDaGVjayBhcHAgaXMgdXBkYXRhYmxlIgogICAgaW50Y18yIC8vIFRNUExfVVBEQVRBQkxFCiAgICBhc3NlcnQgLy8gQ2hlY2sgYXBwIGlzIHVwZGF0YWJsZQogICAgcmV0c3ViCg==", + "clear": "I3ByYWdtYSB2ZXJzaW9uIDEwCiNwcmFnbWEgdHlwZXRyYWNrIGZhbHNlCgovLyBhbGdvcHkuYXJjNC5BUkM0Q29udHJhY3QuY2xlYXJfc3RhdGVfcHJvZ3JhbSgpIC0+IHVpbnQ2NDoKbWFpbjoKICAgIHB1c2hpbnQgMSAvLyAxCiAgICByZXR1cm4K" + }, + "byteCode": { + "approval": "CiADAQAAMRtBADKABAK+zhE2GgCOAQACI0MxGRREMRhENhoBVwIAiAAvSRUWVwYCTFCABBUffHVMULAiQ4EEIzEZjgIACQADQv/NMRgURCJDMRhEiAASIkOKAQGAB0hlbGxvLCCL/1CJigAAJESJ", + "clear": "CoEBQw==" + }, + "compilerInfo": { + "compiler": "puya", + "compilerVersion": { + "major": 4, + "minor": 3, + "patch": 3 + } + }, + "events": [], + "templateVariables": { + "UPDATABLE": { + "type": "AVMUint64" + } + } +} diff --git a/crates/algokit_test_artifacts/contracts/state_contract/state.arc56.json b/crates/algokit_test_artifacts/contracts/state_contract/state.arc56.json new file mode 100644 index 000000000..00886938e --- /dev/null +++ b/crates/algokit_test_artifacts/contracts/state_contract/state.arc56.json @@ -0,0 +1,714 @@ +{ + "name": "State", + "structs": { + "Input": [ + { + "name": "name", + "type": "string" + }, + { + "name": "age", + "type": "uint64" + } + ], + "Output": [ + { + "name": "message", + "type": "string" + }, + { + "name": "result", + "type": "uint64" + } + ] + }, + "methods": [ + { + "name": "create_abi", + "args": [ + { + "type": "string", + "name": "input" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [ + "NoOp" + ], + "call": [] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "update_abi", + "args": [ + { + "type": "string", + "name": "input" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "UpdateApplication" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "delete_abi", + "args": [ + { + "type": "string", + "name": "input" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "DeleteApplication" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "opt_in", + "args": [], + "returns": { + "type": "void" + }, + "actions": { + "create": [], + "call": [ + "OptIn" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "error", + "args": [], + "returns": { + "type": "void" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": true, + "events": [], + "recommendations": {} + }, + { + "name": "call_abi", + "args": [ + { + "type": "string", + "name": "value" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": true, + "events": [], + "recommendations": {} + }, + { + "name": "call_abi_txn", + "args": [ + { + "type": "pay", + "name": "txn" + }, + { + "type": "string", + "name": "value" + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": true, + "events": [], + "recommendations": {} + }, + { + "name": "call_with_references", + "args": [ + { + "type": "asset", + "name": "asset" + }, + { + "type": "account", + "name": "account" + }, + { + "type": "application", + "name": "application" + } + ], + "returns": { + "type": "uint64" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "default_value", + "args": [ + { + "type": "string", + "name": "arg_with_default", + "defaultValue": { + "source": "literal", + "data": "AA1kZWZhdWx0IHZhbHVl", + "type": "string" + } + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": true, + "events": [], + "recommendations": {} + }, + { + "name": "default_value_int", + "args": [ + { + "type": "uint64", + "name": "arg_with_default", + "defaultValue": { + "source": "literal", + "data": "AAAAAAAAAHs=", + "type": "uint64" + } + } + ], + "returns": { + "type": "uint64" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": true, + "events": [], + "recommendations": {} + }, + { + "name": "default_value_from_abi", + "args": [ + { + "type": "string", + "name": "arg_with_default", + "defaultValue": { + "source": "literal", + "data": "AA1kZWZhdWx0IHZhbHVl", + "type": "string" + } + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": true, + "events": [], + "recommendations": {} + }, + { + "name": "default_value_from_global_state", + "args": [ + { + "type": "uint64", + "name": "arg_with_default", + "defaultValue": { + "source": "global", + "data": "aW50MQ==", + "type": "AVMString" + } + } + ], + "returns": { + "type": "uint64" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": true, + "events": [], + "recommendations": {} + }, + { + "name": "default_value_from_local_state", + "args": [ + { + "type": "string", + "name": "arg_with_default", + "defaultValue": { + "source": "local", + "data": "bG9jYWxfYnl0ZXMx", + "type": "AVMString" + } + } + ], + "returns": { + "type": "string" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": true, + "events": [], + "recommendations": {} + }, + { + "name": "structs", + "args": [ + { + "type": "(string,uint64)", + "struct": "Input", + "name": "name_age" + } + ], + "returns": { + "type": "(string,uint64)", + "struct": "Output" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "set_global", + "args": [ + { + "type": "uint64", + "name": "int1" + }, + { + "type": "uint64", + "name": "int2" + }, + { + "type": "string", + "name": "bytes1" + }, + { + "type": "byte[4]", + "name": "bytes2" + } + ], + "returns": { + "type": "void" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "set_local", + "args": [ + { + "type": "uint64", + "name": "int1" + }, + { + "type": "uint64", + "name": "int2" + }, + { + "type": "string", + "name": "bytes1" + }, + { + "type": "byte[4]", + "name": "bytes2" + } + ], + "returns": { + "type": "void" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + }, + { + "name": "set_box", + "args": [ + { + "type": "byte[4]", + "name": "name" + }, + { + "type": "string", + "name": "value" + } + ], + "returns": { + "type": "void" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + }, + "readonly": false, + "events": [], + "recommendations": {} + } + ], + "arcs": [ + 22, + 28 + ], + "networks": {}, + "state": { + "schema": { + "global": { + "ints": 3, + "bytes": 3 + }, + "local": { + "ints": 2, + "bytes": 3 + } + }, + "keys": { + "global": { + "value": { + "keyType": "AVMString", + "valueType": "AVMUint64", + "key": "dmFsdWU=" + }, + "bytes1": { + "keyType": "AVMString", + "valueType": "AVMBytes", + "key": "Ynl0ZXMx" + }, + "bytes2": { + "keyType": "AVMString", + "valueType": "AVMBytes", + "key": "Ynl0ZXMy" + }, + "bytesNotInSnakeCase": { + "keyType": "AVMString", + "valueType": "AVMBytes", + "key": "Ynl0ZXNOb3RJblNuYWtlQ2FzZQ==" + }, + "int1": { + "keyType": "AVMString", + "valueType": "AVMUint64", + "key": "aW50MQ==" + }, + "int2": { + "keyType": "AVMString", + "valueType": "AVMUint64", + "key": "aW50Mg==" + } + }, + "local": { + "local_bytes1": { + "keyType": "AVMString", + "valueType": "AVMBytes", + "key": "bG9jYWxfYnl0ZXMx" + }, + "local_bytes2": { + "keyType": "AVMString", + "valueType": "AVMBytes", + "key": "bG9jYWxfYnl0ZXMy" + }, + "localBytesNotInSnakeCase": { + "keyType": "AVMString", + "valueType": "AVMBytes", + "key": "bG9jYWxCeXRlc05vdEluU25ha2VDYXNl" + }, + "local_int1": { + "keyType": "AVMString", + "valueType": "AVMUint64", + "key": "bG9jYWxfaW50MQ==" + }, + "local_int2": { + "keyType": "AVMString", + "valueType": "AVMUint64", + "key": "bG9jYWxfaW50Mg==" + } + }, + "box": { + "boxNotInSnakeCase": { + "keyType": "AVMBytes", + "valueType": "string", + "key": "YQ==" + } + } + }, + "maps": { + "global": {}, + "local": {}, + "box": { + "box": { + "keyType": "byte[4]", + "valueType": "string", + "prefix": "" + }, + "boxMapNotInSnakeCase": { + "keyType": "byte[4]", + "valueType": "string", + "prefix": "Yg==" + } + } + } + }, + "bareActions": { + "create": [ + "NoOp", + "OptIn" + ], + "call": [ + "DeleteApplication", + "UpdateApplication" + ] + }, + "sourceInfo": { + "approval": { + "sourceInfo": [ + { + "pc": [ + 620, + 927 + ], + "errorMessage": "Check app is deletable" + }, + { + "pc": [ + 609, + 918 + ], + "errorMessage": "Check app is updatable" + }, + { + "pc": [ + 426 + ], + "errorMessage": "Deliberate error" + }, + { + "pc": [ + 764 + ], + "errorMessage": "Index access is out of bounds" + }, + { + "pc": [ + 442 + ], + "errorMessage": "OnCompletion is not DeleteApplication" + }, + { + "pc": [ + 136, + 154, + 183, + 212, + 231, + 253, + 268, + 287, + 302, + 317, + 352, + 392, + 422, + 504 + ], + "errorMessage": "OnCompletion is not NoOp" + }, + { + "pc": [ + 431 + ], + "errorMessage": "OnCompletion is not OptIn" + }, + { + "pc": [ + 474 + ], + "errorMessage": "OnCompletion is not UpdateApplication" + }, + { + "pc": [ + 671 + ], + "errorMessage": "account not provided" + }, + { + "pc": [ + 674 + ], + "errorMessage": "application not provided" + }, + { + "pc": [ + 665 + ], + "errorMessage": "asset not provided" + }, + { + "pc": [ + 508, + 570 + ], + "errorMessage": "can only call when creating" + }, + { + "pc": [ + 139, + 157, + 186, + 215, + 234, + 256, + 271, + 290, + 305, + 320, + 355, + 395, + 425, + 434, + 445, + 477, + 553, + 561 + ], + "errorMessage": "can only call when not creating" + }, + { + "pc": [ + 365 + ], + "errorMessage": "transaction type is pay" + }, + { + "pc": [ + 940 + ], + "errorMessage": "unauthorized" + } + ], + "pcOffsetMethod": "cblocks" + }, + "clear": { + "sourceInfo": [], + "pcOffsetMethod": "none" + } + }, + "source": { + "approval": "I3ByYWdtYSB2ZXJzaW9uIDEwCiNwcmFnbWEgdHlwZXRyYWNrIGZhbHNlCgovLyBleGFtcGxlcy5zbWFydF9jb250cmFjdHMuc3RhdGUuY29udHJhY3QuU3RhdGUuX19hbGdvcHlfZW50cnlwb2ludF93aXRoX2luaXQoKSAtPiB1aW50NjQ6Cm1haW46CiAgICBpbnRjYmxvY2sgMSAwIFRNUExfVVBEQVRBQkxFIFRNUExfREVMRVRBQkxFIFRNUExfVkFMVUUKICAgIGJ5dGVjYmxvY2sgMHgxNTFmN2M3NSAiSGVsbG8sICIgIiIKICAgIC8vIGV4YW1wbGVzL3NtYXJ0X2NvbnRyYWN0cy9zdGF0ZS9jb250cmFjdC5weTozNAogICAgLy8gY2xhc3MgU3RhdGUoRXhhbXBsZUFSQzRDb250cmFjdCk6CiAgICB0eG4gTnVtQXBwQXJncwogICAgYnogbWFpbl9iYXJlX3JvdXRpbmdAMjIKICAgIHB1c2hieXRlc3MgMHg5ZDUyMzA0MCAweDNjYTVjZWI3IDB4MjcxYjRlZTkgMHgzMGM2ZDU4YSAweDQ0ZDBkYTBkIDB4ZjE3ZTgwYTUgMHgwYTkyYTgxZSAweGZlZmRmMTFlIDB4NTc0YjU1YzggMHgzNjAzNjJlOSAweDQ2ZDIxMWEzIDB4MGNmY2JiMDAgMHhkMGYwYmFmOCAweDI0NmJlYjgzIDB4YTRjZjhkZWEgMHhjZWMyODM0YSAweGE0YjRhMjMwIC8vIG1ldGhvZCAiY3JlYXRlX2FiaShzdHJpbmcpc3RyaW5nIiwgbWV0aG9kICJ1cGRhdGVfYWJpKHN0cmluZylzdHJpbmciLCBtZXRob2QgImRlbGV0ZV9hYmkoc3RyaW5nKXN0cmluZyIsIG1ldGhvZCAib3B0X2luKCl2b2lkIiwgbWV0aG9kICJlcnJvcigpdm9pZCIsIG1ldGhvZCAiY2FsbF9hYmkoc3RyaW5nKXN0cmluZyIsIG1ldGhvZCAiY2FsbF9hYmlfdHhuKHBheSxzdHJpbmcpc3RyaW5nIiwgbWV0aG9kICJjYWxsX3dpdGhfcmVmZXJlbmNlcyhhc3NldCxhY2NvdW50LGFwcGxpY2F0aW9uKXVpbnQ2NCIsIG1ldGhvZCAiZGVmYXVsdF92YWx1ZShzdHJpbmcpc3RyaW5nIiwgbWV0aG9kICJkZWZhdWx0X3ZhbHVlX2ludCh1aW50NjQpdWludDY0IiwgbWV0aG9kICJkZWZhdWx0X3ZhbHVlX2Zyb21fYWJpKHN0cmluZylzdHJpbmciLCBtZXRob2QgImRlZmF1bHRfdmFsdWVfZnJvbV9nbG9iYWxfc3RhdGUodWludDY0KXVpbnQ2NCIsIG1ldGhvZCAiZGVmYXVsdF92YWx1ZV9mcm9tX2xvY2FsX3N0YXRlKHN0cmluZylzdHJpbmciLCBtZXRob2QgInN0cnVjdHMoKHN0cmluZyx1aW50NjQpKShzdHJpbmcsdWludDY0KSIsIG1ldGhvZCAic2V0X2dsb2JhbCh1aW50NjQsdWludDY0LHN0cmluZyxieXRlWzRdKXZvaWQiLCBtZXRob2QgInNldF9sb2NhbCh1aW50NjQsdWludDY0LHN0cmluZyxieXRlWzRdKXZvaWQiLCBtZXRob2QgInNldF9ib3goYnl0ZVs0XSxzdHJpbmcpdm9pZCIKICAgIHR4bmEgQXBwbGljYXRpb25BcmdzIDAKICAgIG1hdGNoIG1haW5fY3JlYXRlX2FiaV9yb3V0ZUA1IG1haW5fdXBkYXRlX2FiaV9yb3V0ZUA2IG1haW5fZGVsZXRlX2FiaV9yb3V0ZUA3IG1haW5fb3B0X2luX3JvdXRlQDggbWFpbl9lcnJvcl9yb3V0ZUA5IG1haW5fY2FsbF9hYmlfcm91dGVAMTAgbWFpbl9jYWxsX2FiaV90eG5fcm91dGVAMTEgbWFpbl9jYWxsX3dpdGhfcmVmZXJlbmNlc19yb3V0ZUAxMiBtYWluX2RlZmF1bHRfdmFsdWVfcm91dGVAMTMgbWFpbl9kZWZhdWx0X3ZhbHVlX2ludF9yb3V0ZUAxNCBtYWluX2RlZmF1bHRfdmFsdWVfZnJvbV9hYmlfcm91dGVAMTUgbWFpbl9kZWZhdWx0X3ZhbHVlX2Zyb21fZ2xvYmFsX3N0YXRlX3JvdXRlQDE2IG1haW5fZGVmYXVsdF92YWx1ZV9mcm9tX2xvY2FsX3N0YXRlX3JvdXRlQDE3IG1haW5fc3RydWN0c19yb3V0ZUAxOCBtYWluX3NldF9nbG9iYWxfcm91dGVAMTkgbWFpbl9zZXRfbG9jYWxfcm91dGVAMjAgbWFpbl9zZXRfYm94X3JvdXRlQDIxCgptYWluX2FmdGVyX2lmX2Vsc2VAMjY6CiAgICAvLyBleGFtcGxlcy9zbWFydF9jb250cmFjdHMvc3RhdGUvY29udHJhY3QucHk6MzQKICAgIC8vIGNsYXNzIFN0YXRlKEV4YW1wbGVBUkM0Q29udHJhY3QpOgogICAgaW50Y18xIC8vIDAKICAgIHJldHVybgoKbWFpbl9zZXRfYm94X3JvdXRlQDIxOgogICAgLy8gZXhhbXBsZXMvc21hcnRfY29udHJhY3RzL3N0YXRlL2NvbnRyYWN0LnB5OjE0NwogICAgLy8gQGFyYzQuYWJpbWV0aG9kCiAgICB0eG4gT25Db21wbGV0aW9uCiAgICAhCiAgICBhc3NlcnQgLy8gT25Db21wbGV0aW9uIGlzIG5vdCBOb09wCiAgICB0eG4gQXBwbGljYXRpb25JRAogICAgYXNzZXJ0IC8vIGNhbiBvbmx5IGNhbGwgd2hlbiBub3QgY3JlYXRpbmcKICAgIC8vIGV4YW1wbGVzL3NtYXJ0X2NvbnRyYWN0cy9zdGF0ZS9jb250cmFjdC5weTozNAogICAgLy8gY2xhc3MgU3RhdGUoRXhhbXBsZUFSQzRDb250cmFjdCk6CiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAxCiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAyCiAgICAvLyBleGFtcGxlcy9zbWFydF9jb250cmFjdHMvc3RhdGUvY29udHJhY3QucHk6MTQ3CiAgICAvLyBAYXJjNC5hYmltZXRob2QKICAgIGNhbGxzdWIgc2V0X2JveAogICAgaW50Y18wIC8vIDEKICAgIHJldHVybgoKbWFpbl9zZXRfbG9jYWxfcm91dGVAMjA6CiAgICAvLyBleGFtcGxlcy9zbWFydF9jb250cmFjdHMvc3RhdGUvY29udHJhY3QucHk6MTM4CiAgICAvLyBAYXJjNC5hYmltZXRob2QKICAgIHR4biBPbkNvbXBsZXRpb24KICAgICEKICAgIGFzc2VydCAvLyBPbkNvbXBsZXRpb24gaXMgbm90IE5vT3AKICAgIHR4biBBcHBsaWNhdGlvbklECiAgICBhc3NlcnQgLy8gY2FuIG9ubHkgY2FsbCB3aGVuIG5vdCBjcmVhdGluZwogICAgLy8gZXhhbXBsZXMvc21hcnRfY29udHJhY3RzL3N0YXRlL2NvbnRyYWN0LnB5OjM0CiAgICAvLyBjbGFzcyBTdGF0ZShFeGFtcGxlQVJDNENvbnRyYWN0KToKICAgIHR4bmEgQXBwbGljYXRpb25BcmdzIDEKICAgIGJ0b2kKICAgIHR4bmEgQXBwbGljYXRpb25BcmdzIDIKICAgIGJ0b2kKICAgIHR4bmEgQXBwbGljYXRpb25BcmdzIDMKICAgIGV4dHJhY3QgMiAwCiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyA0CiAgICAvLyBleGFtcGxlcy9zbWFydF9jb250cmFjdHMvc3RhdGUvY29udHJhY3QucHk6MTM4CiAgICAvLyBAYXJjNC5hYmltZXRob2QKICAgIGNhbGxzdWIgc2V0X2xvY2FsCiAgICBpbnRjXzAgLy8gMQogICAgcmV0dXJuCgptYWluX3NldF9nbG9iYWxfcm91dGVAMTk6CiAgICAvLyBleGFtcGxlcy9zbWFydF9jb250cmFjdHMvc3RhdGUvY29udHJhY3QucHk6MTI5CiAgICAvLyBAYXJjNC5hYmltZXRob2QKICAgIHR4biBPbkNvbXBsZXRpb24KICAgICEKICAgIGFzc2VydCAvLyBPbkNvbXBsZXRpb24gaXMgbm90IE5vT3AKICAgIHR4biBBcHBsaWNhdGlvbklECiAgICBhc3NlcnQgLy8gY2FuIG9ubHkgY2FsbCB3aGVuIG5vdCBjcmVhdGluZwogICAgLy8gZXhhbXBsZXMvc21hcnRfY29udHJhY3RzL3N0YXRlL2NvbnRyYWN0LnB5OjM0CiAgICAvLyBjbGFzcyBTdGF0ZShFeGFtcGxlQVJDNENvbnRyYWN0KToKICAgIHR4bmEgQXBwbGljYXRpb25BcmdzIDEKICAgIGJ0b2kKICAgIHR4bmEgQXBwbGljYXRpb25BcmdzIDIKICAgIGJ0b2kKICAgIHR4bmEgQXBwbGljYXRpb25BcmdzIDMKICAgIGV4dHJhY3QgMiAwCiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyA0CiAgICAvLyBleGFtcGxlcy9zbWFydF9jb250cmFjdHMvc3RhdGUvY29udHJhY3QucHk6MTI5CiAgICAvLyBAYXJjNC5hYmltZXRob2QKICAgIGNhbGxzdWIgc2V0X2dsb2JhbAogICAgaW50Y18wIC8vIDEKICAgIHJldHVybgoKbWFpbl9zdHJ1Y3RzX3JvdXRlQDE4OgogICAgLy8gZXhhbXBsZXMvc21hcnRfY29udHJhY3RzL3N0YXRlL2NvbnRyYWN0LnB5OjEyNQogICAgLy8gQGFyYzQuYWJpbWV0aG9kCiAgICB0eG4gT25Db21wbGV0aW9uCiAgICAhCiAgICBhc3NlcnQgLy8gT25Db21wbGV0aW9uIGlzIG5vdCBOb09wCiAgICB0eG4gQXBwbGljYXRpb25JRAogICAgYXNzZXJ0IC8vIGNhbiBvbmx5IGNhbGwgd2hlbiBub3QgY3JlYXRpbmcKICAgIC8vIGV4YW1wbGVzL3NtYXJ0X2NvbnRyYWN0cy9zdGF0ZS9jb250cmFjdC5weTozNAogICAgLy8gY2xhc3MgU3RhdGUoRXhhbXBsZUFSQzRDb250cmFjdCk6CiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAxCiAgICAvLyBleGFtcGxlcy9zbWFydF9jb250cmFjdHMvc3RhdGUvY29udHJhY3QucHk6MTI1CiAgICAvLyBAYXJjNC5hYmltZXRob2QKICAgIGNhbGxzdWIgc3RydWN0cwogICAgYnl0ZWNfMCAvLyAweDE1MWY3Yzc1CiAgICBzd2FwCiAgICBjb25jYXQKICAgIGxvZwogICAgaW50Y18wIC8vIDEKICAgIHJldHVybgoKbWFpbl9kZWZhdWx0X3ZhbHVlX2Zyb21fbG9jYWxfc3RhdGVfcm91dGVAMTc6CiAgICAvLyBleGFtcGxlcy9zbWFydF9jb250cmFjdHMvc3RhdGUvY29udHJhY3QucHk6MTIxCiAgICAvLyBAYXJjNC5hYmltZXRob2QocmVhZG9ubHk9VHJ1ZSwgZGVmYXVsdF9hcmdzPXsiYXJnX3dpdGhfZGVmYXVsdCI6ICJsb2NhbF9ieXRlczEifSkKICAgIHR4biBPbkNvbXBsZXRpb24KICAgICEKICAgIGFzc2VydCAvLyBPbkNvbXBsZXRpb24gaXMgbm90IE5vT3AKICAgIHR4biBBcHBsaWNhdGlvbklECiAgICBhc3NlcnQgLy8gY2FuIG9ubHkgY2FsbCB3aGVuIG5vdCBjcmVhdGluZwogICAgLy8gZXhhbXBsZXMvc21hcnRfY29udHJhY3RzL3N0YXRlL2NvbnRyYWN0LnB5OjM0CiAgICAvLyBjbGFzcyBTdGF0ZShFeGFtcGxlQVJDNENvbnRyYWN0KToKICAgIHR4bmEgQXBwbGljYXRpb25BcmdzIDEKICAgIGV4dHJhY3QgMiAwCiAgICAvLyBleGFtcGxlcy9zbWFydF9jb250cmFjdHMvc3RhdGUvY29udHJhY3QucHk6MTIxCiAgICAvLyBAYXJjNC5hYmltZXRob2QocmVhZG9ubHk9VHJ1ZSwgZGVmYXVsdF9hcmdzPXsiYXJnX3dpdGhfZGVmYXVsdCI6ICJsb2NhbF9ieXRlczEifSkKICAgIGNhbGxzdWIgZGVmYXVsdF92YWx1ZV9mcm9tX2xvY2FsX3N0YXRlCiAgICBieXRlY18wIC8vIDB4MTUxZjdjNzUKICAgIHN3YXAKICAgIGNvbmNhdAogICAgbG9nCiAgICBpbnRjXzAgLy8gMQogICAgcmV0dXJuCgptYWluX2RlZmF1bHRfdmFsdWVfZnJvbV9nbG9iYWxfc3RhdGVfcm91dGUAMTY6CiAgICAvLyBleGFtcGxlcy9zbWFydF9jb250cmFjdHMvc3RhdGUvY29udHJhY3QucHk6MTE3CiAgICAvLyBAYXJjNC5hYmltZXRob2QocmVhZG9ubHk9VHJ1ZSwgZGVmYXVsdF9hcmdzPXsiYXJnX3dpdGhfZGVmYXVsdCI6ICJpbnQxIn0pCiAgICB0eG4gT25Db21wbGV0aW9uCiAgICAhCiAgICBhc3NlcnQgLy8gT25Db21wbGV0aW9uIGlzIG5vdCBOb09wCiAgICB0eG4gQXBwbGljYXRpb25JRAogICAgYXNzZXJ0IC8vIGNhbiBvbmx5IGNhbGwgd2hlbiBub3QgY3JlYXRpbmcKICAgIGJ5dGVjXzAgLy8gMHgxNTFmN2M3NQogICAgLy8gZXhhbXBsZXMvc21hcnRfY29udHJhY3RzL3N0YXRlL2NvbnRyYWN0LnB5OjM0CiAgICAvLyBjbGFzcyBTdGF0ZShFeGFtcGxlQVJDNENvbnRyYWN0KToKICAgIHR4bmEgQXBwbGljYXRpb25BcmdzIDEKICAgIC8vIGV4YW1wbGVzL3NtYXJ0X2NvbnRyYWN0cy9zdGF0ZS9jb250cmFjdC5weToxMTcKICAgIC8vIEBhcmM0LmFiaW1ldGhvZChyZWFkb25seT1UcnVlLCBkZWZhdWx0X2FyZ3M9eyJhcmdfd2l0aF9kZWZhdWx0IjogImludDEifSkKICAgIGNvbmNhdAogICAgbG9nCiAgICBpbnRjXzAgLy8gMQogICAgcmV0dXJuCgptYWluX2RlZmF1bHRfdmFsdWVfZnJvbV9hYmlfcm91dGUAMTU6CiAgICAvLyBleGFtcGxlcy9zbWFydF9jb250cmFjdHMvc3RhdGUvY29udHJhY3QucHk6MTEzCiAgICAvLyBAYXJjNC5hYmltZXRob2QocmVhZG9ubHk9VHJ1ZSwgZGVmYXVsdF9hcmdzPXsiYXJnX3dpdGhfZGVmYXVsdCI6IGFyYzQuU3RyaW5nKCJkZWZhdWx0IHZhbHVlIil9KQogICAgdHhuIE9uQ29tcGxldGlvbgogICAgIQogICAgYXNzZXJ0IC8vIE9uQ29tcGxldGlvbiBpcyBub3QgTm9PcAogICAgdHhuIEFwcGxpY2F0aW9uSUQKICAgIGFzc2VydCAvLyBjYW4gb25seSBjYWxsIHdoZW4gbm90IGNyZWF0aW5nCiAgICAvLyBleGFtcGxlcy9zbWFydF9jb250cmFjdHMvY29udHJhY3QucHk6MzQKICAgIC8vIGNsYXNzIFN0YXRlKEV4YW1wbGVBUkM0Q29udHJhY3QpOgogICAgdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMQogICAgLy8gZXhhbXBsZXMvc21hcnRfY29udHJhY3RzL3N0YXRlL2NvbnRyYWN0LnB5OjExMwogICAgLy8gQGFyYzQuYWJpbWV0aG9kKHJlYWRvbmx5PVRydWUsIGRlZmF1bHRfYXJncz17ImFyZ193aXRoX2RlZmF1bHQiOiBhcmM0LlN0cmluZygiZGVmYXVsdCB2YWx1ZSIpfSkKICAgIGNhbGxzdWIgZGVmYXVsdF92YWx1ZV9mcm9tX2FiaQogICAgYnl0ZWNfMCAvLyAweDE1MWY3Yzc1CiAgICBzd2FwCiAgICBjb25jYXQKICAgIGxvZwogICAgaW50Y18wIC8vIDEKICAgIHJldHVybgoKbWFpbl9kZWZhdWx0X3ZhbHVlX2ludF9yb3V0ZUAxNDoKICAgIC8vIGV4YW1wbGVzL3NtYXJ0X2NvbnRyYWN0cy9zdGF0ZS9jb250cmFjdC5weToxMDkKICAgIC8vIEBhcmM0LmFiaW1ldGhvZChyZWFkb25seT1UcnVlLCBkZWZhdWx0X2FyZ3M9eyJhcmdfd2l0aF9kZWZhdWx0IjogYXJjNC5VSW50NjQoMTIzKX0pCiAgICB0eG4gT25Db21wbGV0aW9uCiAgICAhCiAgICBhc3NlcnQgLy8gT25Db21wbGV0aW9uIGlzIG5vdCBOb09wCiAgICB0eG4gQXBwbGljYXRpb25JRAogICAgYXNzZXJ0IC8vIGNhbiBvbmx5IGNhbGwgd2hlbiBub3QgY3JlYXRpbmcKICAgIGJ5dGVjXzAgLy8gMHgxNTFmN2M3NQogICAgLy8gZXhhbXBsZXMvc21hcnRfY29udHJhY3RzL3N0YXRlL2NvbnRyYWN0LnB5OjM0CiAgICAvLyBjbGFzcyBTdGF0ZShFeGFtcGxlQVJDNENvbnRyYWN0KToKICAgIHR4bmEgQXBwbGljYXRpb25BcmdzIDEKICAgIC8vIGV4YW1wbGVzL3NtYXJ0X2NvbnRyYWN0cy9zdGF0ZS9jb250cmFjdC5weToxMDkKICAgIC8vIEBhcmM0LmFiaW1ldGhvZChyZWFkb25seT1UcnVlLCBkZWZhdWx0X2FyZ3M9eyJhcmdfd2l0aF9kZWZhdWx0IjogYXJjNC5VSW50NjQoMTIzKX0pCiAgICBjb25jYXQKICAgIGxvZwogICAgaW50Y18wIC8vIDEKICAgIHJldHVybgoKbWFpbl9kZWZhdWx0X3ZhbHVlX3JvdXRlQDEzOgogICAgLy8gZXhhbXBsZXMvc21hcnRfY29udHJhY3RzL3N0YXRlL2NvbnRyYWN0LnB5OjEwNQogICAgLy8gQGFyYzQuYWJpbWV0aG9kKHJlYWRvbmx5PVRydWUsIGRlZmF1bHRfYXJncz17ImFyZ193aXRoX2RlZmF1bHQiOiBhcmM0LlN0cmluZygiZGVmYXVsdCB2YWx1ZSIpfSkKICAgIHR4biBPbkNvbXBsZXRpb24KICAgICEKICAgIGFzc2VydCAvLyBPbkNvbXBsZXRpb24gaXMgbm90IE5vT3AKICAgIHR4biBBcHBsaWNhdGlvbklECiAgICBhc3NlcnQgLy8gY2FuIG9ubHkgY2FsbCB3aGVuIG5vdCBjcmVhdGluZwogICAgYnl0ZWNfMCAvLyAweDE1MWY3Yzc1CiAgICAvLyBleGFtcGxlcy9zbWFydF9jb250cmFjdHMvc3RhdGUvY29udHJhY3QucHk6MzQKICAgIC8vIGNsYXNzIFN0YXRlKEV4YW1wbGVBUkM0Q29udHJhY3QpOgogICAgdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMQogICAgLy8gZXhhbXBsZXMvc21hcnRfY29udHJhY3RzL3N0YXRlL2NvbnRyYWN0LnB5OjEwNQogICAgLy8gQGFyYzQuYWJpbWV0aG9kKHJlYWRvbmx5PVRydWUsIGRlZmF1bHRfYXJncz17ImFyZ193aXRoX2RlZmF1bHQiOiBhcmM0LlN0cmluZygiZGVmYXVsdCB2YWx1ZSIpfSkKICAgIGNvbmNhdAogICAgbG9nCiAgICBpbnRjXzAgLy8gMQogICAgcmV0dXJuCgptYWluX2NhbGxfd2l0aF9yZWZlcmVuY2VzX3JvdXRlQDEyOgogICAgLy8gZXhhbXBsZXMvc21hcnRfY29udHJhY3RzL3N0YXRlL2NvbnRyYWN0LnB5Ojk4CiAgICAvLyBAYXJjNC5hYmltZXRob2QKICAgIHR4biBPbkNvbXBsZXRpb24KICAgICEKICAgIGFzc2VydCAvLyBPbkNvbXBsZXRpb24gaXMgbm90IE5vT3AKICAgIHR4biBBcHBsaWNhdGlvbklECiAgICBhc3NlcnQgLy8gY2FuIG9ubHkgY2FsbCB3aGVuIG5vdCBjcmVhdGluZwogICAgLy8gZXhhbXBsZXMvc21hcnRfY29udHJhY3RzL3N0YXRlL2NvbnRyYWN0LnB5OjM0CiAgICAvLyBjbGFzcyBTdGF0ZShFeGFtcGxlQVJDNENvbnRyYWN0KToKICAgIHR4bmEgQXBwbGljYXRpb25BcmdzIDEKICAgIGJ0b2kKICAgIHR4bmFzIEFzc2V0cwogICAgdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMgogICAgYnRvaQogICAgdHhuYXMgQWNjb3VudHMKICAgIHR4bmEgQXBwbGljYXRpb25BcmdzIDMKICAgIGJ0b2kKICAgIHR4bmFzIEFwcGxpY2F0aW9ucwogICAgLy8gZXhhbXBsZXMvc21hcnRfY29udHJhY3RzL3N0YXRlL2NvbnRyYWN0LnB5Ojk4CiAgICAvLyBAYXJjNC5hYmltZXRob2QKICAgIGNhbGxzdWIgY2FsbF93aXRoX3JlZmVyZW5jZXMKICAgIGl0b2IKICAgIGJ5dGVjXzAgLy8gMHgxNTFmN2M3NQogICAgc3dhcAogICAgY29uY2F0CiAgICBsb2cKICAgIGludGNfMCAvLyAxCiAgICByZXR1cm4KCm1haW5fY2FsbF9hYmlfdHhuX3JvdXRlQDExOgogICAgLy8gZXhhbXBsZXMvc21hcnRfY29udHJhY3RzL3N0YXRlL2NvbnRyYWN0LnB5Ojk0CiAgICAvLyBAYXJjNC5hYmltZXRob2QocmVhZG9ubHk9VHJ1ZSkKICAgIHR4biBPbkNvbXBsZXRpb24KICAgICEKICAgIGFzc2VydCAvLyBPbkNvbXBsZXRpb24gaXMgbm90IE5vT3AKICAgIHR4biBBcHBsaWNhdGlvbklECiAgICBhc3NlcnQgLy8gY2FuIG9ubHkgY2FsbCB3aGVuIG5vdCBjcmVhdGluZwogICAgLy8gZXhhbXBsZXMvc21hcnRfY29udHJhY3RzL3N0YXRlL2NvbnRyYWN0LnB5OjM0CiAgICAvLyBjbGFzcyBTdGF0ZShFeGFtcGxlQVJDNENvbnRyYWN0KToKICAgIHR4biBHcm91cEluZGV4CiAgICBpbnRjXzAgLy8gMQogICAgLQogICAgZHVwCiAgICBndHhucyBUeXBlRW51bQogICAgaW50Y18wIC8vIHBheQogICAgPT0KICAgIGFzc2VydCAvLyB0cmFuc2FjdGlvbiB0eXBlIGlzIHBheQogICAgdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMQogICAgZXh0cmFjdCAyIDAKICAgIC8vIGV4YW1wbGVzL3NtYXJ0X2NvbnRyYWN0cy9zdGF0ZS9jb250cmFjdC5weTo5NAogICAgLy8gQGFyYzQuYWJpbWV0aG9kKHJlYWRvbmx5PVRydWUpCiAgICBjYWxsc3ViIGNhbGxfYWJpX3R4bgogICAgZHVwCiAgICBsZW4KICAgIGl0b2IKICAgIGV4dHJhY3QgNiAyCiAgICBzd2FwCiAgICBjb25jYXQKICAgIGJ5dGVjXzAgLy8gMHgxNTFmN2M3NQogICAgc3dhcAogICAgY29uY2F0CiAgICBsb2cKICAgIGludGNfMCAvLyAxCiAgICByZXR1cm4KCm1haW5fY2FsbF9hYmlfcm91dGVAMTA6CiAgICAvLyBleGFtcGxlcy9zbWFydF9jb250cmFjdHMvc3RhdGUvY29udHJhY3QucHk6OTAKICAgIC8vIEBhcmM0LmFiaW1ldGhvZChyZWFkb25seT1UcnVlKQogICAgdHhuIE9uQ29tcGxldGlvbgogICAgIQogICAgYXNzZXJ0IC8vIE9uQ29tcGxldGlvbiBpcyBub3QgTm9PcAogICAgdHhuIEFwcGxpY2F0aW9uSUQKICAgIGFzc2VydCAvLyBjYW4gb25seSBjYWxsIHdoZW4gbm90IGNyZWF0aW5nCiAgICAvLyBleGFtcGxlcy9zbWFydF9jb250cmFjdHMvc3RhdGUvY29udHJhY3QucHk6MzQKICAgIC8vIGNsYXNzIFN0YXRlKEV4YW1wbGVBUkM0Q29udHJhY3QpOgogICAgdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMQogICAgZXh0cmFjdCAyIDAKICAgIC8vIGV4YW1wbGVzL3NtYXJ0X2NvbnRyYWN0cy9zdGF0ZS9jb250cmFjdC5weTo5MAogICAgLy8gQGFyYzQuYWJpbWV0aG9kKHJlYWRvbmx5PVRydWUpCiAgICBjYWxsc3ViIGNhbGxfYWJpCiAgICBkdXAKICAgIGxlbgogICAgaXRvYgogICAgZXh0cmFjdCA2IDIKICAgIHN3YXAKICAgIGNvbmNhdAogICAgYnl0ZWNfMCAvLyAweDE1MWY3Yzc1CiAgICBzd2FwCiAgICBjb25jYXQKICAgIGxvZwogICAgaW50Y18wIC8vIDEKICAgIHJldHVybgoKbWFpbl9lcnJvcl9yb3V0ZUA5OgogICAgLy8gZXhhbXBsZXMvc21hcnRfY29udHJhY3RzL3N0YXRlL2NvbnRyYWN0LnB5Ojg2CiAgICAvLyBAYXJjNC5hYmltZXRob2QocmVhZG9ubHk9VHJ1ZSkKICAgIHR4biBPbkNvbXBsZXRpb24KICAgICEKICAgIGFzc2VydCAvLyBPbkNvbXBsZXRpb24gaXMgbm90IE5vT3AKICAgIHR4biBBcHBsaWNhdGlvbklECiAgICBhc3NlcnQgLy8gY2FuIG9ubHkgY2FsbCB3aGVuIG5vdCBjcmVhdGluZwogICAgLy8gZXhhbXBsZXMvc21hcnRfY29udHJhY3RzL3N0YXRlL2NvbnRyYWN0LnB5Ojg4CiAgICAvLyBhc3NlcnQgRmFsc2UsICJEZWxpYmVyYXRlIGVycm9yIiAgIyBub3FhOiBQVDAxNSwgQjAxMQogICAgZXJyIC8vIERlbGliZXJhdGUgZXJyb3IKCm1haW5fb3B0X2luX3JvdXRlQDg6CiAgICAvLyBleGFtcGxlcy9zbWFydF9jb250cmFjdHMvc3RhdGUvY29udHJhY3QucHk6ODIKICAgIC8vIEBhcmM0LmFiaW1ldGhvZChhbGxvd19hY3Rpb25zPVsiT3B0SW4iXSkKICAgIHR4biBPbkNvbXBsZXRpb24KICAgIGludGNfMCAvLyBPcHRJbgogICAgPT0KICAgIGFzc2VydCAvLyBPbkNvbXBsZXRpb24gaXMgbm90IE9wdEluCiAgICB0eG4gQXBwbGljYXRpb25JRAogICAgYXNzZXJ0IC8vIGNhbiBvbmx5IGNhbGwgd2hlbiBub3QgY3JlYXRpbmcKICAgIGludGNfMCAvLyAxCiAgICByZXR1cm4KCm1haW5fZGVsZXRlX2FiaV9yb3V0ZUA3OgogICAgLy8gZXhhbXBsZXMvc21hcnRfY29udHJhY3RzL3N0YXRlL2NvbnRyYWN0LnB5Ojc2CiAgICAvLyBAYXJjNC5hYmltZXRob2QoYWxsb3dfYWN0aW9ucz1bIkRlbGV0ZUFwcGxpY2F0aW9uIl0pCiAgICB0eG4gT25Db21wbGV0aW9uCiAgICBwdXNoaW50IDUgLy8gRGVsZXRlQXBwbGljYXRpb24KICAgID09CiAgICBhc3NlcnQgLy8gT25Db21wbGV0aW9uIGlzIG5vdCBEZWxldGVBcHBsaWNhdGlvbgogICAgdHhuIEFwcGxpY2F0aW9uSUQKICAgIGFzc2VydCAvLyBjYW4gb25seSBjYWxsIHdoZW4gbm90IGNyZWF0aW5nCiAgICAvLyBleGFtcGxlcy9zbWFydF9jb250cmFjdHMvc3RhdGUvY29udHJhY3QucHk6MzQKICAgIC8vIGNsYXNzIFN0YXRlKEV4YW1wbGVBUkM0Q29udHJhY3QpOgogICAgdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMQogICAgZXh0cmFjdCAyIDAKICAgIC8vIGV4YW1wbGVzL3NtYXJ0X2NvbnRyYWN0cy9zdGF0ZS9jb250cmFjdC5weTo3NgogICAgLy8gQGFyYzQuYWJpbWV0aG9kKGFsbG93X2FjdGlvbnM9WyJEZWxldGVBcHBsaWNhdGlvbiJdKQogICAgY2FsbHN1YiBkZWxldGVfYWJpCiAgICBkdXAKICAgIGxlbgogICAgaXRvYgogICAgZXh0cmFjdCA2IDIKICAgIHN3YXAKICAgIGNvbmNhdAogICAgYnl0ZWNfMCAvLyAweDE1MWY3Yzc1CiAgICBzd2FwCiAgICBjb25jYXQKICAgIGxvZwogICAgaW50Y18wIC8vIDEKICAgIHJldHVybgoKbWFpbl91cGRhdGVfYWJpX3JvdXRlQDY6CiAgICAvLyBleGFtcGxlcy9zbWFydF9jb250cmFjdHMvc3RhdGUvY29udHJhY3QucHk6NzAKICAgIC8vIEBhcmM0LmFiaW1ldGhvZChhbGxvd19hY3Rpb25zPVsiVXBkYXRlQXBwbGljYXRpb24iXSkKICAgIHR4biBPbkNvbXBsZXRpb24KICAgIHB1c2hpbnQgNCAvLyBVcGRhdGVBcHBsaWNhdGlvbgogICAgPT0KICAgIGFzc2VydCAvLyBPbkNvbXBsZXRpb24gaXMgbm90IFVwZGF0ZUFwcGxpY2F0aW9uCiAgICB0eG4gQXBwbGljYXRpb25JRAogICAgYXNzZXJ0IC8vIGNhbiBvbmx5IGNhbGwgd2hlbiBub3QgY3JlYXRpbmcKICAgIC8vIGV4YW1wbGVzL3NtYXJ0X2NvbnRyYWN0cy9zdGF0ZS9jb250cmFjdC5weTozNAogICAgLy8gY2xhc3MgU3RhdGUoRXhhbXBsZUFSQzRDb250cmFjdCk6CiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAxCiAgICBleHRyYWN0IDIgMAogICAgLy8gZXhhbXBsZXMvc21hcnRfY29udHJhY3RzL3N0YXRlL2NvbnRyYWN0LnB5OjcwCiAgICAvLyBAYXJjNC5hYmltZXRob2QoYWxsb3dfYWN0aW9ucz1bIlVwZGF0ZUFwcGxpY2F0aW9uIl0pCiAgICBjYWxsc3ViIHVwZGF0ZV9hYmkKICAgIGR1cAogICAgbGVuCiAgICBpdG9iCiAgICBleHRyYWN0IDYgMgogICAgc3dhcAogICAgY29uY2F0CiAgICBieXRlY18wIC8vIDB4MTUxZjdjNzUKICAgIHN3YXAKICAgIGNvbmNhdAogICAgbG9nCiAgICBpbnRjXzAgLy8gMQogICAgcmV0dXJuCgptYWluX2NyZWF0ZV9hYmlfcm91dGVANToKICAgIC8vIGV4YW1wbGVzL3NtYXJ0X2NvbnRyYWN0cy9zdGF0ZS9jb250cmFjdC5weTo2NQogICAgLy8gQGFyYzQuYWJpbWV0aG9kKGNyZWF0ZT0icmVxdWlyZSIpCiAgICB0eG4gT25Db21wbGV0aW9uCiAgICAhCiAgICBhc3NlcnQgLy8gT25Db21wbGV0aW9uIGlzIG5vdCBOb09wCiAgICB0eG4gQXBwbGljYXRpb25JRAogICAgIQogICAgYXNzZXJ0IC8vIGNhbiBvbmx5IGNhbGwgd2hlbiBjcmVhdGluZwogICAgLy8gZXhhbXBsZXMvc21hcnRfY29udHJhY3RzL3N0YXRlL2NvbnRyYWN0LnB5OjM0CiAgICAvLyBjbGFzcyBTdGF0ZShFeGFtcGxlQVJDNENvbnRyYWN0KToKICAgIHR4bmEgQXBwbGljYXRpb25BcmdzIDEKICAgIGV4dHJhY3QgMiAwCiAgICAvLyBleGFtcGxlcy9zbWFydF9jb250cmFjdHMvc3RhdGUvY29udHJhY3QucHk6NjUKICAgIC8vIEBhcmM0LmFiaW1ldGhvZChjcmVhdGU9InJlcXVpcmUiKQogICAgY2FsbHN1YiBjcmVhdGVfYWJpCiAgICBkdXAKICAgIGxlbgogICAgaXRvYgogICAgZXh0cmFjdCA2IDIKICAgIHN3YXAKICAgIGNvbmNhdAogICAgYnl0ZWNfMCAvLyAweDE1MWY3Yzc1CiAgICBzd2FwCiAgICBjb25jYXQKICAgIGxvZwogICAgaW50Y18wIC8vIDEKICAgIHJldHVybgoKbWFpbl9iYXJlX3JvdXRpbmdAMjI6CiAgICAvLyBleGFtcGxlcy9zbWFydF9jb250cmFjdHMvc3RhdGUvY29udHJhY3QucHk6MzQKICAgIC8vIGNsYXNzIFN0YXRlKEV4YW1wbGVBUkM0Q29udHJhY3QpOgogICAgdHhuIE9uQ29tcGxldGlvbgogICAgc3dpdGNoIG1haW5fY3JlYXRlQDIzIG1haW5fY3JlYXRlQDIzIG1haW5fYWZ0ZXJfaWZfZWxzZUAyNiBtYWluX2FmdGVyX2lmX2Vsc2VAMjYgbWFpbl91cGRhdGVAMjQgbWFpbl9kZWxldGVAMjUKICAgIGIgbWFpbl9hZnRlcl9pZl9lbHNlQDI2CgptYWluX2RlbGV0ZUAyNToKICAgIC8vIGV4YW1wbGVzL3NtYXJ0X2NvbnRyYWN0cy9iYXNlL2NvbnRyYWN0LnB5OjMwCiAgICAvLyBAYXJjNC5iYXJlbWV0aG9kKGFsbG93X2FjdGlvbnM9WyJEZWxldGVBcHBsaWNhdGlvbiJdKQogICAgdHhuIEFwcGxpY2F0aW9uSUQKICAgIGFzc2VydCAvLyBjYW4gb25seSBjYWxsIHdoZW4gbm90IGNyZWF0aW5nCiAgICBjYWxsc3ViIGRlbGV0ZQogICAgaW50Y18wIC8vIDEKICAgIHJldHVybgoKbWFpbl91cGRhdGVAMjQ6CiAgICAvLyBleGFtcGxlcy9zbWFydF9jb250cmFjdHMvYmFzZS9jb250cmFjdC5weToyMwogICAgLy8gQGFyYzQuYmFyZW1ldGhvZChhbGxvd19hY3Rpb25zPVsiVXBkYXRlQXBwbGljYXRpb24iXSkKICAgIHR4biBBcHBsaWNhdGlvbklECiAgICBhc3NlcnQgLy8gY2FuIG9ubHkgY2FsbCB3aGVuIG5vdCBjcmVhdGluZwogICAgY2FsbHN1YiB1cGRhdGUKICAgIGludGNfMCAvLyAxCiAgICByZXR1cm4KCm1haW5fY3JlYXRlQDIzOgogICAgLy8gZXhhbXBsZXMvc21hcnRfY29udHJhY3RzL3N0YXRlL2NvbnRyYWN0LnB5OjYwCiAgICAvLyBAYXJjNC5iYXJlbWV0aG9kKGNyZWF0ZT0icmVxdWlyZSIsIGFsbG93X2FjdGlvbnM9WyJOb09wIiwgIk9wdEluIl0pCiAgICB0eG4gQXBwbGljYXRpb25JRAogICAgIQogICAgYXNzZXJ0IC8vIGNhbiBvbmx5IGNhbGwgd2hlbiBjcmVhdGluZwogICAgY2FsbHN1YiBjcmVhdGUKICAgIGludGNfMCAvLyAxCiAgICByZXR1cm4KCgovLyBleGFtcGxlcy5zbWFydF9jb250cmFjdHMuc3RhdGUuY29udHJhY3QuU3RhdGUuY3JlYXRlKCkgLT4gdm9pZDoKY3JlYXRlOgogICAgLy8gZXhhbXBsZXMvc21hcnRfY29udHJhY3RzL3N0YXRlL2NvbnRyYWN0LnB5OjYwLTYxCiAgICAvLyBAYXJjNC5iYXJlbWV0aG9kKGNyZWF0ZT0icmVxdWlyZSIsIGFsbG93X2FjdGlvbnM9WyJOb09wIiwgIk9wdEluIl0pCiAgICAvLyBkZWYgY3JlYXRlKHNlbGYpIC0+IE5vbmU6CiAgICBwcm90byAwIDAKICAgIC8vIGV4YW1wbGVzL3NtYXJ0X2NvbnRyYWN0cy9zdGF0ZS9jb250cmFjdC5weTo2MgogICAgLy8gc2VsZi5hdXRob3JpemVfY3JlYXRvcigpCiAgICBjYWxsc3ViIGF1dGhvcml6ZV9jcmVhdG9yCiAgICAvLyBleGFtcGxlcy9zbWFydF9jb250cmFjdHMvc3RhdGUvY29udHJhY3QucHk6NjMKICAgIC8vIHNlbGYudmFsdWUgPSBUZW1wbGF0ZVZhcltVSW50NjRdKFZBTFVFX1RFTVBMQVRFX05BTUUpCiAgICBwdXNoYnl0ZXMgInZhbHVlIgogICAgaW50YyA0IC8vIFRNUExfVkFMVUUKICAgIGFwcF9nbG9iYWxfcHV0CiAgICByZXRzdWIKCgovLyBleGFtcGxlcy5zbWFydF9jb250cmFjdHMuc3RhdGUuY29udHJhY3QuU3RhdGUuY3JlYXRlX2FiaShpbnB1dDogYnl0ZXMpIC0+IGJ5dGVzOgpjcmVhdGVfYWJpOgogICAgLy8gZXhhbXBsZXMvc21hcnRfY29udHJhY3RzL3N0YXRlL2NvbnRyYWN0LnB5OjY1LTY2CiAgICAvLyBAYXJjNC5hYmltZXRob2QoY3JlYXRlPSJyZXF1aXJlIikKICAgIC8vIGRlZiBjcmVhdGVfYWJpKHNlbGYsIGlucHV0OiBTdHJpbmcpIC0+IFN0cmluZzogICMgbm9xYTogQTAwMgogICAgcHJvdG8gMSAxCiAgICAvLyBleGFtcGxlcy9zbWFydF9jb250cmFjdHMvc3RhdGUvY29udHJhY3QucHk6NjcKICAgIC8vIHNlbGYuYXV0aG9yaXplX2NyZWF0b3IoKQogICAgY2FsbHN1YiBhdXRob3JpemVfY3JlYXRvcgogICAgLy8gZXhhbXBsZXMvc21hcnRfY29udHJhY3RzL3N0YXRlL2NvbnRyYWN0LnB5OjY4CiAgICAvLyByZXR1cm4gaW5wdXQKICAgIGZyYW1lX2RpZyAtMQogICAgcmV0c3ViCgoKLy8gZXhhbXBsZXMuc21hcnRfY29udHJhY3RzLnN0YXRlLmNvbnRyYWN0LlN0YXRlLnVwZGF0ZV9hYmkoaW5wdXQ6IGJ5dGVzKSAtPiBieXRlczoKdXBkYXRlX2FiaToKICAgIC8vIGV4YW1wbGVzL3NtYXJ0X2NvbnRyYWN0cy9zdGF0ZS9jb250cmFjdC5weTo3MC03MQogICAgLy8gQGFyYzQuYWJpbWV0aG9kKGFsbG93X2FjdGlvbnM9WyJVcGRhdGVBcHBsaWNhdGlvbiJdKQogICAgLy8gZGVmIHVwZGF0ZV9hYmkoc2VsZiwgaW5wdXQ6IFN0cmluZykgLT4gU3RyaW5nOiAgIyBub3FhOiBBMDAyCiAgICBwcm90byAxIDEKICAgIC8vIGV4YW1wbGVzL3NtYXJ0X2NvbnRyYWN0cy9zdGF0ZS9jb250cmFjdC5weTo3MgogICAgLy8gc2VsZi5hdXRob3JpemVfY3JlYXRvcigpCiAgICBjYWxsc3ViIGF1dGhvcml6ZV9jcmVhdG9yCiAgICAvLyBhc3NlcnQgVGVtcGxhdGVWYXJbYm9vbF0oVVBEQVRBQkxFX1RFTVBMQVRFX05BTUUpLCAiQ2hlY2sgYXBwIGlzIHVwZGF0YWJsZSIKICAgIGludGNfMiAvLyBUTVBMX1VQREFUQUJMRQogICAgYXNzZXJ0IC8vIENoZWNrIGFwcCBpcyB1cGRhdGFibGUKICAgIC8vIGV4YW1wbGVzL3NtYXJ0X2NvbnRyYWN0cy9zdGF0ZS9jb250cmFjdC5weTo3NAogICAgLy8gcmV0dXJuIGlucHV0CiAgICBmcmFtZV9kaWcgLTEKICAgIHJldHN1YgoKCi8vIGV4YW1wbGVzLnNtYXJ0X2NvbnRyYWN0cy5zdGF0ZS5jb250cmFjdC5TdGF0ZS5kZWxldGVfYWJpKGlucHV0OiBieXRlcykgLT4gYnl0ZXM6CmRlbGV0ZV9hYmk6CiAgICAvLyBleGFtcGxlcy9zbWFydF9jb250cmFjdHMvc3RhdGUvY29udHJhY3QucHk6NzYtNzcKICAgIC8vIEBhcmM0LmFiaW1ldGhvZChhbGxvd19hY3Rpb25zPVsiRGVsZXRlQXBwbGljYXRpb24iXSkKICAgIC8vIGRlZiBkZWxldGVfYWJpKHNlbGYsIGlucHV0OiBTdHJpbmcpIC0+IFN0cmluZzogICMgbm9xYTogQTAwMgogICAgcHJvdG8gMSAxCiAgICAvLyBleGFtcGxlcy9zbWFydF9jb250cmFjdHMvc3RhdGUvY29udHJhY3QucHk6NzgKICAgIC8vIHNlbGYuYXV0aG9yaXplX2NyZWF0b3IoKQogICAgY2FsbHN1YiBhdXRob3JpemVfY3JlYXRvcgogICAgLy8gYXNzZXJ0IFRlbXBsYXRlVmFyW2Jvb2xdKERFTEVUQUJMRV9URU1QTEFURV9OQU1FKSwgIkNoZWNrIGFwcCBpcyBkZWxldGFibGUiCiAgICBpbnRjXzMgLy8gVE1QTF9ERUxFVEFCTEUKICAgIGFzc2VydCAvLyBDaGVjayBhcHAgaXMgZGVsZXRhYmxlCiAgICAvLyBleGFtcGxlcy9zbWFydF9jb250cmFjdHMvc3RhdGUvY29udHJhY3QucHk6ODAKICAgIC8vIHJldHVybiBpbnB1dAogICAgZnJhbWVfZGlnIC0xCiAgICByZXRzdWIKCgovLyBleGFtcGxlcy5zbWFydF9jb250cmFjdHMuYmFzZS5jb250cmFjdC5JbW11dGFiaWxpdHlDb250cm9sQVJDNENvbnRyYWN0LnVwZGF0ZSgpIC0+IHZvaWQ6CnVwZGF0ZToKICAgIC8vIGV4YW1wbGVzL3NtYXJ0X2NvbnRyYWN0cy9iYXNlL2NvbnRyYWN0LnB5OjIzLTI0CiAgICAvLyBAYXJjNC5iYXJlbWV0aG9kKGFsbG93X2FjdGlvbnM9WyJVcGRhdGVBcHBsaWNhdGlvbiJdKQogICAgLy8gZGVmIHVwZGF0ZShzZWxmKSAtPiBOb25lOgogICAgcHJvdG8gMCAwCiAgICAvLyBleGFtcGxlcy9zbWFydF9jb250cmFjdHMuYmFzZS9jb250cmFjdC5weToyNQogICAgLy8gYXNzZXJ0IFRlbXBsYXRlVmFyW2Jvb2xdKFVQREFUQUJMRV9URU1QTEFURV9OQU1FKSwgIkNoZWNrIGFwcCBpcyB1cGRhdGFibGUiCiAgICBpbnRjXzIgLy8gVE1QTF9VUERBVEFCTEUKICAgIGFzc2VydCAvLyBDaGVjayBhcHAgaXMgdXBkYXRhYmxlCiAgICAvLyBleGFtcGxlcy9zbWFydF9jb250cmFjdHMuYmFzZS9jb250cmFjdC5weToyNgogICAgLy8gc2VsZi5hdXRob3JpemVfY3JlYXRvcigpCiAgICBjYWxsc3ViIGF1dGhvcml6ZV9jcmVhdG9yCiAgICByZXRzdWIKCgovLyBleGFtcGxlcy5zbWFydF9jb250cmFjdHMuYmFzZS5jb250cmFjdC5QZXJtYW5lbmNlQ29udHJvbEFSQzRDb250cmFjdC5kZWxldGUoKSAtPiB2b2lkOgpkZWxldGU6CiAgICAvLyBleGFtcGxlcy9zbWFydF9jb250cmFjdHMuYmFzZS9jb250cmFjdC5weTozMC0zMQogICAgLy8gQGFyYzQuYmFyZW1ldGhvZChhbGxvd19hY3Rpb25zPVsiRGVsZXRlQXBwbGljYXRpb24iXSkKICAgIC8vIGRlZiBkZWxldGUoc2VsZikgLT4gTm9uZToKICAgIHByb3RvIDAgMAogICAgLy8gZXhhbXBsZXMvc21hcnRfY29udHJhY3RzL2Jhc2UvY29udHJhY3QucHk6MzIKICAgIC8vIGFzc2VydCBUZW1wbGF0ZVZhcltib29sXShERUxFVEFCTEVfVEVNUExBVEVfTkFNRSksICJDaGVjayBhcHAgaXMgZGVsZXRhYmxlIgogICAgaW50Y18zIC8vIFRNUExfREVMRVRBQkxFCiAgICBhc3NlcnQgLy8gQ2hlY2sgYXBwIGlzIGRlbGV0YWJsZQogICAgLy8gZXhhbXBsZXMvc21hcnRfY29udHJhY3RzL2Jhc2UvY29udHJhY3QucHk6MzMKICAgIC8vIHNlbGYuYXV0aG9yaXplX2NyZWF0b3IoKQogICAgY2FsbHN1YiBhdXRob3JpemVfY3JlYXRvcgogICAgcmV0c3ViCgoKLy8gZXhhbXBsZXMuc21hcnRfY29udHJhY3RzLmJhc2UuY29udHJhY3QuQmFzZUFSQzRDb250cmFjdC5hdXRob3JpemVfY3JlYXRvciAoKSAtPiB2b2lkOgphdXRob3JpemVfY3JlYXRvcjoKICAgIC8vIGV4YW1wbGVzL3NtYXJ0X2NvbnRyYWN0cy9iYXNlL2NvbnRyYWN0LnB5OjgtOQogICAgLy8gQHN1YnJvdXRpbmUKICAgIC8vIGRlZiBhdXRob3JpemVfY3JlYXRvcihzZWxmKSAtPiBOb25lOgogICAgcHJvdG8gMCAwCiAgICAvLyBleGFtcGxlcy9zbWFydF9jb250cmFjdHMuYmFzZS9jb250cmFjdC5weToxMAogICAgLy8gYXNzZXJ0IFR4bi5zZW5kZXIgPT0gR2xvYmFsLmNyZWF0b3JfYWRkcmVzcywgInVuYXV0aG9yaXplZCIKICAgIHR4biBTZW5kZXIKICAgIGdsb2JhbCBDcmVhdG9yQWRkcmVzcwogICAgPT0KICAgIGFzc2VydCAvLyB1bmF1dGhvcml6ZWQKICAgIHJldHVybgoKCi8vIGV4YW1wbGVzLnNtYXJ0X2NvbnRyYWN0cy5iYXNlLmNvbnRyYWN0LkJhc2VBUkM0Q29udHJhY3QuaXRvYShpOiB1aW50NjQpIC0+IGJ5dGVzOwppdG9hOgogICAgLy8gZXhhbXBsZXMvc21hcnRfY29udHJhY3RzL2Jhc2UvY29udHJhY3QucHk6MTItMTMKICAgIC8vIEBzdWJyb3V0aW5lCiAgICAvLyBkZWYgaXRvYShzZWxmLCBpOiBVSW50NjQpIC0+IFN0cmluZzoKICAgIHByb3RvIDEgMQogICAgYnl0ZWNfMiAvLyAiIgogICAgLy8gZXhhbXBsZXMvc21hcnRfY29udHJhY3RzL2Jhc2UvY29udHJhY3QucHk6MTQKICAgIC8vIGlmIGkgPT0gVUludDY0KDApOgogICAgZnJhbWVfZGlnIC0xCiAgICBibnogaXRvYV9lbHNlX2JvZHlAMgogICAgLy8gZXhhbXBsZXMvc21hcnRfY29udHJhY3RzL2Jhc2UvY29udHJhY3QucHk6MTUKICAgIC8vIHJldHVybiBTdHJpbmcoIjAiKQogICAgcHVzaGJ5dGVzICIwIgogICAgc3dhcAogICAgcmV0c3ViCgppdG9hX2Vsc2VfYm9keUAyOgogICAgLy8gZXhhbXBsZXMvc21hcnRfY29udHJhY3RzL2Jhc2UvY29udHJhY3QucHk6MTcKICAgIC8vIHJldHVybiAoc2VsZi5pdG9hKGkgLy8gVUludDY0KDEwKSkgaWYgKGkgLy8gVUludDY0KDEwKSkgPiBVSW50NjQoMCkgZWxzZSBTdHJpbmcoIiIpKSArIFN0cmluZy5mcm9tX2J5dGVzKAogICAgZnJhbWVfZGlnIC0xCiAgICBwdXNoaW50IDEwIC8vIDEwCiAgICAvCiAgICBkdXAKICAgIGZyYW1lX2J1cnkgMAogICAgYnogaXRvYV90ZXJuYXJ5X2ZhbHNlQDQKICAgIGZyYW1lX2RpZyAwCiAgICBjYWxsc3ViIGl0b2EKCml0b2FfdGVybmFyeV9tZXJnZUA1OgogICAgLy8gZXhhbXBsZXMvc21hcnRfY29udHJhY3RzL2Jhc2UvY29udHJhY3QucHk6MTgKICAgIC8vIFN0cmluZygiMDEyMzQ1Njc4OSIpLmJ5dGVzW2kgJSBVSW50NjQoMTApXQogICAgZnJhbWVfZGlnIC0xCiAgICBwdXNoaW50IDEwIC8vIDEwCiAgICAlCiAgICBwdXNoYnl0ZXMgIjAxMjM0NTY3ODkiCiAgICBzd2FwCiAgICBpbnRjXzAgLy8gMQogICAgZXh0cmFjdDMKICAgIC8vIGV4YW1wbGVzL3NtYXJ0X2NvbnRyYWN0cy9iYXNlL2NvbnRyYWN0LnB5OjE3LTE5CiAgICAvLyByZXR1cm4gKHNlbGYuaXRvYShpIC8vIFVJbnQ2NCgxMCkpIGlmIChpIC8vIFVJbnQ2NCgxMCkpID4gVUludDY0KDApIGVsc2UgU3RyaW5nKCIiKSkgKyBTdHJpbmcuZnJvbV9ieXRlcygKICAgIC8vICAgICBTdHJpbmcoIjAxMjM0NTY3ODkiKS5ieXRlc1tpICUgVUludDY0KDEwKV0KICAgIC8vICkKICAgIGNvbmNhdAogICAgc3dhcAogICAgcmV0c3ViCgppdG9hX3Rlcm5hcnlfZmFsc2VANAogICAgLy8gZXhhbXBsZXMvc21hcnRfY29udHJhY3RzL2Jhc2UvY29udHJhY3QucHk6MTcKICAgIC8vIHJldHVybiAoc2VsZi5pdG9hKGkgLy8gVUludDY0KDEwKSkgaWYgKGkgLy8gVUludDY0KDEwKSkgPiBVSW50NjQoMCkgZWxzZSBTdHJpbmcoIiIpKSArIFN0cmluZy5mcm9tX2J5dGVzKAogICAgYnl0ZWNfMiAvLyAiIgogICAgYiBpdG9hX3Rlcm5hcnlfbWVyZ2VANSg=", + "clear": "I3ByYWdtYSB2ZXJzaW9uIDEwCiNwcmFnbWEgdHlwZXRyYWNrIGZhbHNlCgovLyBhbGdvcHkuYXJjNC5BUkM0Q29udHJhY3QuY2xlYXJfc3RhdGVfcHJvZ3JhbSgpIC0+IHVpbnQ2NDoKbWFpbjoKICAgIHB1c2hpbnQgMSAvLyAxCiAgICByZXR1cm4K" + }, + "byteCode": { + "approval": "CiAFAQAAAAAmAwQVH3x1B0hlbGxvLCAAMRtBAg+CEQSdUjBABDylzrcEJxtO6QQwxtWKBETQ2g0E8X6ApQQKkqgeBP798R4EV0tVyAQ2A2LpBEbSEaMEDPy7AATQ8Lr4BCRr64MEpM+N6gTOwoNKBKS0ojA2GgCOEQFyAVIBMgEoASABAgDaALcAqACZAIYAdwBhAE4AMQAUAAIjQzEZFEQxGEQ2GgE2GgKIAvAiQzEZFEQxGEQ2GgEXNhoCFzYaA1cCADYaBIgChyJDMRkURDEYRDYaARc2GgIXNhoDVwIANhoEiAI+IkMxGRREMRhENhoBiAH/KExQsCJDMRkURDEYRDYaAVcCAIgByyhMULAiQzEZFEQxGEQoNhoBULAiQzEZFEQxGEQ2GgGIAY8oTFCwIkMxGRREMRhEKDYaAVCwIkMxGRREMRhEKDYaAVCwIkMxGRREMRhENhoBF8AwNhoCF8AcNhoDF8AyiAE+FihMULAiQzEZFEQxGEQxFiIJSTgQIhJENhoBVwIAiAEBSRUWVwYCTFAoTFCwIkMxGRREMRhENhoBVwIAiADbSRUWVwYCTFAoTFCwIkMxGRREMRhEADEZIhJEMRhEIkMxGYEFEkQxGEQ2GgFXAgCIAJ5JFRZXBgJMUChMULAiQzEZgQQSRDEYRDYaAVcCAIgAc0kVFlcGAkxQKExQsCJDMRkURDEYFEQ2GgFXAgCIAEtJFRZXBgJMUChMULAiQzEZjQYAEwAT/l/+XwALAANC/lwxGESIAW4iQzEYRIgBXSJDMRgURIgAAiJDigAAiAFegAV2YWx1ZSEEZ4mKAQGIAU2L/4mKAQGIAUQkRIv/iYoBAYgBOSVEi/+JigEBKYv/UImKAgGL/jgIiAEsgAVTZW50IExQgAIuIFCL/1CJigMBi/1Ei/4yAxNEi/9EIomKAQGL/1cCAIAFQUJJLCBMUEkVFlcGAkxQiYoBAYANTG9jYWwgc3RhdGUsIIv/UEkVFlcGAkxQiYoBAYv/I1mL/xWL/04CUlcCAClMUEkVFlcGAkxQi/9XAggXgQILFoACAApMUExQiYoEAIAEaW50MYv8Z4AEaW50Mov9Z4AGYnl0ZXMxi/5ngAZieXRlczKL/2eJigQAMQCACmxvY2FsX2ludDGL/GYxAIAKbG9jYWxfaW50Mov9ZjEAgAxsb2NhbF9ieXRlczGL/mYxAIAMbG9jYWxfYnl0ZXMyi/9miYoCAIv+vEiL/ov/v4mKAAAkRIgAComKAAAlRIgAAYmKAAAxADIJEkSJigEBKov/QAAFgAEwTImL/4EKCkmMAEEAHIsAiP/ii/+BChiACjAxMjM0NTY3ODlMIlhQTIkqQv/l", + "clear": "CoEBQw==" + }, + "compilerInfo": { + "compiler": "puya", + "compilerVersion": { + "major": 4, + "minor": 3, + "patch": 3 + } + }, + "events": [], + "templateVariables": { + "UPDATABLE": { + "type": "AVMUint64" + }, + "DELETABLE": { + "type": "AVMUint64" + }, + "VALUE": { + "type": "AVMUint64" + } + } +} diff --git a/crates/algokit_test_artifacts/contracts/testing_app_arc56/app_spec.arc56.json b/crates/algokit_test_artifacts/contracts/testing_app_arc56/app_spec.arc56.json new file mode 100644 index 000000000..664c68ab0 --- /dev/null +++ b/crates/algokit_test_artifacts/contracts/testing_app_arc56/app_spec.arc56.json @@ -0,0 +1,681 @@ +{ + "name": "Templates", + "desc": "", + "methods": [ + { + "name": "tmpl", + "args": [], + "returns": { + "type": "void" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + } + }, + { + "name": "specificLengthTemplateVar", + "args": [], + "returns": { + "type": "void" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + } + }, + { + "name": "throwError", + "args": [], + "returns": { + "type": "void" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + } + }, + { + "name": "itobTemplateVar", + "args": [], + "returns": { + "type": "byte[]" + }, + "actions": { + "create": [], + "call": [ + "NoOp" + ] + } + }, + { + "name": "createApplication", + "args": [], + "returns": { + "type": "void" + }, + "actions": { + "create": [ + "NoOp" + ], + "call": [] + } + } + ], + "arcs": [ + 4, + 56 + ], + "structs": {}, + "state": { + "schema": { + "global": { + "bytes": 0, + "ints": 0 + }, + "local": { + "bytes": 0, + "ints": 0 + } + }, + "keys": { + "global": {}, + "local": {}, + "box": {} + }, + "maps": { + "global": {}, + "local": {}, + "box": {} + } + }, + "bareActions": { + "create": [], + "call": [] + }, + "sourceInfo": { + "approval": { + "sourceInfo": [ + { + "teal": 15, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:3", + "pc": [ + 1, + 2 + ] + }, + { + "teal": 16, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:3", + "pc": [ + 3 + ] + }, + { + "teal": 17, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:3", + "pc": [ + 4, + 5 + ] + }, + { + "teal": 18, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:3", + "pc": [ + 6 + ] + }, + { + "teal": 19, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:3", + "pc": [ + 7, + 8 + ] + }, + { + "teal": 20, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:3", + "pc": [ + 9 + ] + }, + { + "teal": 21, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:3", + "pc": [ + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35 + ] + }, + { + "teal": 25, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:3", + "errorMessage": "The requested action is not implemented in this contract. Are you using the correct OnComplete? Did you set your app ID?", + "pc": [ + 36 + ] + }, + { + "teal": 30, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:12", + "pc": [ + 37, + 38, + 39 + ] + }, + { + "teal": 31, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:12", + "pc": [ + 40 + ] + }, + { + "teal": 32, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:12", + "pc": [ + 41 + ] + }, + { + "teal": 36, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:12", + "pc": [ + 42, + 43, + 44 + ] + }, + { + "teal": 40, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:13", + "pc": [ + 45 + ] + }, + { + "teal": 41, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:13", + "pc": [ + 46 + ] + }, + { + "teal": 45, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:14", + "pc": [ + 47 + ] + }, + { + "teal": 46, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:14", + "pc": [ + 48 + ] + }, + { + "teal": 47, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:12", + "pc": [ + 49 + ] + }, + { + "teal": 52, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:17", + "pc": [ + 50, + 51, + 52 + ] + }, + { + "teal": 53, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:17", + "pc": [ + 53 + ] + }, + { + "teal": 54, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:17", + "pc": [ + 54 + ] + }, + { + "teal": 58, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:17", + "pc": [ + 55, + 56, + 57 + ] + }, + { + "teal": 62, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:18", + "pc": [ + 58 + ] + }, + { + "teal": 63, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:18", + "pc": [ + 59 + ] + }, + { + "teal": 64, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:18", + "pc": [ + 60 + ] + }, + { + "teal": 65, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:18", + "pc": [ + 61 + ] + }, + { + "teal": 66, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:17", + "pc": [ + 62 + ] + }, + { + "teal": 71, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:21", + "pc": [ + 63, + 64, + 65 + ] + }, + { + "teal": 72, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:21", + "pc": [ + 66 + ] + }, + { + "teal": 73, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:21", + "pc": [ + 67 + ] + }, + { + "teal": 77, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:21", + "pc": [ + 68, + 69, + 70 + ] + }, + { + "teal": 80, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:22", + "errorMessage": "this is an error", + "pc": [ + 71 + ] + }, + { + "teal": 81, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:21", + "pc": [ + 72 + ] + }, + { + "teal": 86, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:25", + "pc": [ + 73, + 74, + 75, + 76, + 77, + 78 + ] + }, + { + "teal": 89, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:25", + "pc": [ + 79, + 80, + 81 + ] + }, + { + "teal": 90, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:25", + "pc": [ + 82 + ] + }, + { + "teal": 91, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:25", + "pc": [ + 83 + ] + }, + { + "teal": 92, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:25", + "pc": [ + 84 + ] + }, + { + "teal": 93, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:25", + "pc": [ + 85, + 86, + 87 + ] + }, + { + "teal": 94, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:25", + "pc": [ + 88 + ] + }, + { + "teal": 95, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:25", + "pc": [ + 89 + ] + }, + { + "teal": 96, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:25", + "pc": [ + 90 + ] + }, + { + "teal": 97, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:25", + "pc": [ + 91 + ] + }, + { + "teal": 98, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:25", + "pc": [ + 92 + ] + }, + { + "teal": 99, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:25", + "pc": [ + 93 + ] + }, + { + "teal": 103, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:25", + "pc": [ + 94, + 95, + 96 + ] + }, + { + "teal": 107, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:26", + "pc": [ + 97 + ] + }, + { + "teal": 108, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:26", + "pc": [ + 98 + ] + }, + { + "teal": 109, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:25", + "pc": [ + 99 + ] + }, + { + "teal": 112, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:3", + "pc": [ + 100 + ] + }, + { + "teal": 113, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:3", + "pc": [ + 101 + ] + }, + { + "teal": 116, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:3", + "pc": [ + 102, + 103, + 104, + 105, + 106, + 107 + ] + }, + { + "teal": 117, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:3", + "pc": [ + 108, + 109, + 110 + ] + }, + { + "teal": 118, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:3", + "pc": [ + 111, + 112, + 113, + 114 + ] + }, + { + "teal": 121, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:3", + "errorMessage": "this contract does not implement the given ABI method for create NoOp", + "pc": [ + 115 + ] + }, + { + "teal": 124, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:3", + "pc": [ + 116, + 117, + 118, + 119, + 120, + 121 + ] + }, + { + "teal": 125, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:3", + "pc": [ + 122, + 123, + 124, + 125, + 126, + 127 + ] + }, + { + "teal": 126, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:3", + "pc": [ + 128, + 129, + 130, + 131, + 132, + 133 + ] + }, + { + "teal": 127, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:3", + "pc": [ + 134, + 135, + 136, + 137, + 138, + 139 + ] + }, + { + "teal": 128, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:3", + "pc": [ + 140, + 141, + 142 + ] + }, + { + "teal": 129, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:3", + "pc": [ + 143, + 144, + 145, + 146, + 147, + 148, + 149, + 150, + 151, + 152 + ] + }, + { + "teal": 132, + "source": "tests/example-contracts/arc56_templates/templates.algo.ts:3", + "errorMessage": "this contract does not implement the given ABI method for call NoOp", + "pc": [ + 153 + ] + } + ], + "pcOffsetMethod": "cblocks" + }, + "clear": { + "sourceInfo": [], + "pcOffsetMethod": "none" + } + }, + "source": { + "approval": "I3ByYWdtYSB2ZXJzaW9uIDEwCmludGNibG9jayAxIFRNUExfdWludDY0VG1wbFZhcgpieXRlY2Jsb2NrIFRNUExfYnl0ZXNUbXBsVmFyIFRNUExfYnl0ZXM2NFRtcGxWYXIgVE1QTF9ieXRlczMyVG1wbFZhcgoKLy8gVGhpcyBURUFMIHdhcyBnZW5lcmF0ZWQgYnkgVEVBTFNjcmlwdCB2MC4xMDUuMwovLyBodHRwczovL2dpdGh1Yi5jb20vYWxnb3JhbmRmb3VuZGF0aW9uL1RFQUxTY3JpcHQKCi8vIFRoaXMgY29udHJhY3QgaXMgY29tcGxpYW50IHdpdGggYW5kL29yIGltcGxlbWVudHMgdGhlIGZvbGxvd2luZyBBUkNzOiBbIEFSQzQgXQoKLy8gVGhlIGZvbGxvd2luZyB0ZW4gbGluZXMgb2YgVEVBTCBoYW5kbGUgaW5pdGlhbCBwcm9ncmFtIGZsb3cKLy8gVGhpcyBwYXR0ZXJuIGlzIHVzZWQgdG8gbWFrZSBpdCBlYXN5IGZvciBhbnlvbmUgdG8gcGFyc2UgdGhlIHN0YXJ0IG9mIHRoZSBwcm9ncmFtIGFuZCBkZXRlcm1pbmUgaWYgYSBzcGVjaWZpYyBhY3Rpb24gaXMgYWxsb3dlZAovLyBIZXJlLCBhY3Rpb24gcmVmZXJzIHRvIHRoZSBPbkNvbXBsZXRlIGluIGNvbWJpbmF0aW9uIHdpdGggd2hldGhlciB0aGUgYXBwIGlzIGJlaW5nIGNyZWF0ZWQgb3IgY2FsbGVkCi8vIEV2ZXJ5IHBvc3NpYmxlIGFjdGlvbiBmb3IgdGhpcyBjb250cmFjdCBpcyByZXByZXNlbnRlZCBpbiB0aGUgc3dpdGNoIHN0YXRlbWVudAovLyBJZiB0aGUgYWN0aW9uIGlzIG5vdCBpbXBsZW1lbnRlZCBpbiB0aGUgY29udHJhY3QsIGl0cyByZXNwZWN0aXZlIGJyYW5jaCB3aWxsIGJlICIqTk9UX0lNUExFTUVOVEVEIiB3aGljaCBqdXN0IGNvbnRhaW5zICJlcnIiCnR4biBBcHBsaWNhdGlvbklECiEKcHVzaGludCA2CioKdHhuIE9uQ29tcGxldGlvbgorCnN3aXRjaCAqY2FsbF9Ob09wICpOT1RfSU1QTEVNRU5URUQgKk5PVF9JTVBMRU1FTlRFRCAqTk9UX0lNUExFTUVOVEVEICpOT1RfSU1QTEVNRU5URUQgKk5PVF9JTVBMRU1FTlRFRCAqY3JlYXRlX05vT3AgKk5PVF9JTVBMRU1FTlRFRCAqTk9UX0lNUExFTUVOVEVEICpOT1RfSU1QTEVNRU5URUQgKk5PVF9JTVBMRU1FTlRFRCAqTk9UX0lNUExFTUVOVEVECgoqTk9UX0lNUExFTUVOVEVEOgoJLy8gVGhlIHJlcXVlc3RlZCBhY3Rpb24gaXMgbm90IGltcGxlbWVudGVkIGluIHRoaXMgY29udHJhY3QuIEFyZSB5b3UgdXNpbmcgdGhlIGNvcnJlY3QgT25Db21wbGV0ZT8gRGlkIHlvdSBzZXQgeW91ciBhcHAgSUQ/CgllcnIKCi8vIHRtcGwoKXZvaWQKKmFiaV9yb3V0ZV90bXBsOgoJLy8gZXhlY3V0ZSB0bXBsKCl2b2lkCgljYWxsc3ViIHRtcGwKCWludGMgMCAvLyAxCglyZXR1cm4KCi8vIHRtcGwoKTogdm9pZAp0bXBsOgoJcHJvdG8gMCAwCgoJLy8gdGVzdHMvZXhhbXBsZS1jb250cmFjdHMvYXJjNTZfdGVtcGxhdGVzL3RlbXBsYXRlcy5hbGdvLnRzOjEzCgkvLyBsb2codGhpcy5ieXRlc1RtcGxWYXIpCglieXRlYyAwIC8vIFRNUExfYnl0ZXNUbXBsVmFyCglsb2cKCgkvLyB0ZXN0cy9leGFtcGxlLWNvbnRyYWN0cy9hcmM1Nl90ZW1wbGF0ZXMvdGVtcGxhdGVzLmFsZ28udHM6MTQKCS8vIGFzc2VydCh0aGlzLnVpbnQ2NFRtcGxWYXIpCglpbnRjIDEgLy8gVE1QTF91aW50NjRUbXBsVmFyCglhc3NlcnQKCXJldHN1YgoKLy8gc3BlY2lmaWNMZW5ndGhUZW1wbGF0ZVZhcigpdm9pZAoqYWJpX3JvdXRlX3NwZWNpZmljTGVuZ3RoVGVtcGxhdGVWYXI6CgkvLyBleGVjdXRlIHNwZWNpZmljTGVuZ3RoVGVtcGxhdGVWYXIoKXZvaWQKCWNhbGxzdWIgc3BlY2lmaWNMZW5ndGhUZW1wbGF0ZVZhcgoJaW50YyAwIC8vIDEKCXJldHVybgoKLy8gc3BlY2lmaWNMZW5ndGhUZW1wbGF0ZVZhcigpOiB2b2lkCnNwZWNpZmljTGVuZ3RoVGVtcGxhdGVWYXI6Cglwcm90byAwIDAKCgkvLyB0ZXN0cy9leGFtcGxlLWNvbnRyYWN0cy9hcmM1Nl90ZW1wbGF0ZXMvdGVtcGxhdGVzLmFsZ28udHM6MTgKCS8vIGVkMjU1MTlWZXJpZnlCYXJlKHRoaXMuYnl0ZXNUbXBsVmFyLCB0aGlzLmJ5dGVzNjRUbXBsVmFyLCB0aGlzLmJ5dGVzMzJUbXBsVmFyKQoJYnl0ZWMgMCAvLyBUTVBMX2J5dGVzVG1wbFZhcgoJYnl0ZWMgMSAvLyBUTVBMX2J5dGVzNjRUbXBsVmFyCglieXRlYyAyIC8vIFRNUExfYnl0ZXMzMlRtcGxWYXIKCWVkMjU1MTl2ZXJpZnlfYmFyZQoJcmV0c3ViCgovLyB0aHJvd0Vycm9yKCl2b2lkCiphYmlfcm91dGVfdGhyb3dFcnJvcjoKCS8vIGV4ZWN1dGUgdGhyb3dFcnJvcigpdm9pZAoJY2FsbHN1YiB0aHJvd0Vycm9yCglpbnRjIDAgLy8gMQoJcmV0dXJuCgovLyB0aHJvd0Vycm9yKCk6IHZvaWQKdGhyb3dFcnJvcjoKCXByb3RvIDAgMAoKCS8vIHRoaXMgaXMgYW4gZXJyb3IKCWVycgoJcmV0c3ViCgovLyBpdG9iVGVtcGxhdGVWYXIoKWJ5dGVbXQoqYWJpX3JvdXRlX2l0b2JUZW1wbGF0ZVZhcjoKCS8vIFRoZSBBQkkgcmV0dXJuIHByZWZpeAoJcHVzaGJ5dGVzIDB4MTUxZjdjNzUKCgkvLyBleGVjdXRlIGl0b2JUZW1wbGF0ZVZhcigpYnl0ZVtdCgljYWxsc3ViIGl0b2JUZW1wbGF0ZVZhcgoJZHVwCglsZW4KCWl0b2IKCWV4dHJhY3QgNiAyCglzd2FwCgljb25jYXQKCWNvbmNhdAoJbG9nCglpbnRjIDAgLy8gMQoJcmV0dXJuCgovLyBpdG9iVGVtcGxhdGVWYXIoKTogYnl0ZXMKaXRvYlRlbXBsYXRlVmFyOgoJcHJvdG8gMCAxCgoJLy8gdGVzdHMvZXhhbXBsZS1jb250cmFjdHMvYXJjNTZfdGVtcGxhdGVzL3RlbXBsYXRlcy5hbGdvLnRzOjI2CgkvLyByZXR1cm4gaXRvYih0aGlzLnVpbnQ2NFRtcGxWYXIpCglpbnRjIDEgLy8gVE1QTF91aW50NjRUbXBsVmFyCglpdG9iCglyZXRzdWIKCiphYmlfcm91dGVfY3JlYXRlQXBwbGljYXRpb246CglpbnRjIDAgLy8gMQoJcmV0dXJuCgoqY3JlYXRlX05vT3A6CglwdXNoYnl0ZXMgMHhiODQ0N2IzNiAvLyBtZXRob2QgImNyZWF0ZUFwcGxpY2F0aW9uKCl2b2lkIgoJdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMAoJbWF0Y2ggKmFiaV9yb3V0ZV9jcmVhdGVBcHBsaWNhdGlvbgoKCS8vIHRoaXMgY29udHJhY3QgZG9lcyBub3QgaW1wbGVtZW50IHRoZSBnaXZlbiBBQkkgbWV0aG9kIGZvciBjcmVhdGUgTm9PcAoJZXJyCgoqY2FsbF9Ob09wOgoJcHVzaGJ5dGVzIDB4OWE3MWQyYjQgLy8gbWV0aG9kICJ0bXBsKCl2b2lkIgoJcHVzaGJ5dGVzIDB4ZGY0ZDVjM2IgLy8gbWV0aG9kICJzcGVjaWZpY0xlbmd0aFRlbXBsYXRlVmFyKCl2b2lkIgoJcHVzaGJ5dGVzIDB4M2Q4NzBkODcgLy8gbWV0aG9kICJ0aHJvd0Vycm9yKCl2b2lkIgoJcHVzaGJ5dGVzIDB4YmMwYjE3MDYgLy8gbWV0aG9kICJpdG9iVGVtcGxhdGVWYXIoKWJ5dGVbXSIKCXR4bmEgQXBwbGljYXRpb25BcmdzIDAKCW1hdGNoICphYmlfcm91dGVfdG1wbCAqYWJpX3JvdXRlX3NwZWNpZmljTGVuZ3RoVGVtcGxhdGVWYXIgKmFiaV9yb3V0ZV90aHJvd0Vycm9yICphYmlfcm91dGVfaXRvYlRlbXBsYXRlVmFyCgoJLy8gdGhpcyBjb250cmFjdCBkb2VzIG5vdCBpbXBsZW1lbnQgdGhlIGdpdmVuIEFCSSBtZXRob2QgZm9yIGNhbGwgTm9PcAoJZXJy", + "clear": "I3ByYWdtYSB2ZXJzaW9uIDEw" + }, + "templateVariables": { + "bytesTmplVar": { + "type": "byte[]" + }, + "uint64TmplVar": { + "type": "uint64" + }, + "bytes32TmplVar": { + "type": "byte[32]" + }, + "bytes64TmplVar": { + "type": "byte[64]" + } + }, + "scratchVariables": { + "bytesTmplVar": { + "type": "byte[]", + "slot": 200 + }, + "uint64TmplVar": { + "type": "uint64", + "slot": 201 + }, + "bytes32TmplVar": { + "type": "byte[32]", + "slot": 202 + }, + "bytes64TmplVar": { + "type": "byte[64]", + "slot": 203 + } + }, + "compilerInfo": { + "compiler": "algod", + "compilerVersion": { + "major": 3, + "minor": 26, + "patch": 0, + "commitHash": "0d10b244" + } + } +} diff --git a/crates/algokit_test_artifacts/src/lib.rs b/crates/algokit_test_artifacts/src/lib.rs index 53775f9e8..68a6a50fb 100644 --- a/crates/algokit_test_artifacts/src/lib.rs +++ b/crates/algokit_test_artifacts/src/lib.rs @@ -180,6 +180,31 @@ pub mod testing_app_puya { include_str!("../contracts/testing_app_puya/application.arc56.json"); } +/// Testing app ARC56 templates (control-template capable) +pub mod testing_app_arc56_templates { + /// ARC56 app spec used in template-var/error mapping tests + pub const APP_SPEC_ARC56: &str = + include_str!("../contracts/testing_app_arc56/app_spec.arc56.json"); +} +/// Extra pages test contract artifacts +pub mod extra_pages_test { + /// Aggregate application (ARC56) used by extra pages tests + pub const APPLICATION_ARC56: &str = + include_str!("../contracts/extra_pages_test/application.arc56.json"); + + /// Small program variant (ARC56) + pub const SMALL_ARC56: &str = include_str!("../contracts/extra_pages_test/small.arc56.json"); + + /// Large program variant (ARC56) + pub const LARGE_ARC56: &str = include_str!("../contracts/extra_pages_test/large.arc56.json"); +} + +/// State contract artifacts (control-aware spec) +pub mod state_contract { + /// State contract (ARC56) with UPDATABLE/DELETABLE/VALUE template variables + pub const STATE_ARC56: &str = include_str!("../contracts/state_contract/state.arc56.json"); +} + /// Resource population contract artifacts pub mod resource_population { /// Resource population testing contract (ARC32) targeting AVM V8 diff --git a/crates/algokit_utils/src/applications/app_client/compilation.rs b/crates/algokit_utils/src/applications/app_client/compilation.rs index f6503a5d4..560d9d4e4 100644 --- a/crates/algokit_utils/src/applications/app_client/compilation.rs +++ b/crates/algokit_utils/src/applications/app_client/compilation.rs @@ -6,28 +6,22 @@ use crate::{ config::{AppCompiledEventData, EventData}, }; +use crate::clients::app_manager::{CompiledPrograms, CompiledTeal}; + impl AppClient { /// Compile the application's approval and clear programs with optional template parameters. pub async fn compile( &self, compilation_params: &CompilationParams, - ) -> Result<(Vec, Vec), AppClientError> { + ) -> Result { let approval = self.compile_approval(compilation_params).await?; let clear = self.compile_clear(compilation_params).await?; // Emit AppCompiled event when debug flag is enabled if Config::debug() { let app_name = self.app_name.clone(); - let approval_map = self - .algorand() - .app() - .get_compilation_result(&String::from_utf8_lossy(&approval)) - .and_then(|c| c.source_map); - let clear_map = self - .algorand() - .app() - .get_compilation_result(&String::from_utf8_lossy(&clear)) - .and_then(|c| c.source_map); + let approval_map = approval.source_map.clone(); + let clear_map = clear.source_map.clone(); let event = AppCompiledEventData { app_name, @@ -39,13 +33,13 @@ impl AppClient { .await; } - Ok((approval, clear)) + Ok(CompiledPrograms { approval, clear }) } async fn compile_approval( &self, compilation_params: &CompilationParams, - ) -> Result, AppClientError> { + ) -> Result { let source = self.app_spec .source @@ -83,14 +77,13 @@ impl AppClient { .await .map_err(|e| AppClientError::AppManagerError { source: e })?; - // Return TEAL source bytes (TransactionSender will pull compiled bytes from cache) - Ok(compiled.teal.into_bytes()) + Ok(compiled) } async fn compile_clear( &self, compilation_params: &CompilationParams, - ) -> Result, AppClientError> { + ) -> Result { let source = self.app_spec .source @@ -114,6 +107,6 @@ impl AppClient { .await .map_err(|e| AppClientError::AppManagerError { source: e })?; - Ok(compiled.teal.into_bytes()) + Ok(compiled) } } diff --git a/crates/algokit_utils/src/applications/app_client/error.rs b/crates/algokit_utils/src/applications/app_client/error.rs index d7dfd3948..c343c3e75 100644 --- a/crates/algokit_utils/src/applications/app_client/error.rs +++ b/crates/algokit_utils/src/applications/app_client/error.rs @@ -1,3 +1,4 @@ +use crate::applications::app_client::types::LogicError; use crate::clients::app_manager::AppManagerError; use crate::clients::client_manager::ClientManagerError; use crate::transactions::TransactionSenderError; @@ -34,7 +35,7 @@ pub enum AppClientError { #[snafu(display("{message}"))] LogicError { message: String, - logic: Box, + logic: Box, }, #[snafu(display("Transact error: {source}"))] TransactError { source: AlgoKitTransactError }, diff --git a/crates/algokit_utils/src/applications/app_client/mod.rs b/crates/algokit_utils/src/applications/app_client/mod.rs index 0397a985a..58db3058c 100644 --- a/crates/algokit_utils/src/applications/app_client/mod.rs +++ b/crates/algokit_utils/src/applications/app_client/mod.rs @@ -48,7 +48,7 @@ type BoxNameFilter = Box bool>; pub struct AppClient { app_id: u64, app_spec: Arc56Contract, - algorand: AlgorandClient, + algorand: Arc, default_sender: Option, default_signer: Option>, source_maps: Option, @@ -77,7 +77,7 @@ impl AppClient { /// or the network's genesis hash present in the node's suggested params. pub async fn from_network( app_spec: Arc56Contract, - algorand: AlgorandClient, + algorand: Arc, app_name: Option, default_sender: Option, default_signer: Option>, @@ -124,7 +124,7 @@ impl AppClient { creator_address: &str, app_name: &str, app_spec: Arc56Contract, - algorand: AlgorandClient, + algorand: Arc, default_sender: Option, default_signer: Option>, source_maps: Option, diff --git a/crates/algokit_utils/src/applications/app_client/params_builder.rs b/crates/algokit_utils/src/applications/app_client/params_builder.rs index e163607d7..3cab0bc7a 100644 --- a/crates/algokit_utils/src/applications/app_client/params_builder.rs +++ b/crates/algokit_utils/src/applications/app_client/params_builder.rs @@ -3,7 +3,9 @@ use super::types::{ AppClientBareCallParams, AppClientMethodCallParams, CompilationParams, FundAppAccountParams, }; use crate::AppClientError; +use crate::applications::app_client::utils::parse_account_refs_to_addresses; use crate::clients::app_manager::AppState; +use crate::clients::app_manager::CompiledPrograms; use crate::transactions::{ AppCallMethodCallParams, AppCallParams, AppDeleteMethodCallParams, AppDeleteParams, AppMethodCallArg, AppUpdateMethodCallParams, AppUpdateParams, PaymentParams, @@ -100,9 +102,7 @@ impl<'app_client> ParamsBuilder<'app_client> { app_id: self.client.app_id, method: abi_method, args: resolved_args, - account_references: super::utils::parse_account_refs_to_addresses( - ¶ms.account_references, - )?, + account_references: parse_account_refs_to_addresses(¶ms.account_references)?, app_references: params.app_references.clone(), asset_references: params.asset_references.clone(), box_references: params.box_references.clone(), @@ -114,11 +114,10 @@ impl<'app_client> ParamsBuilder<'app_client> { &self, params: AppClientMethodCallParams, compilation_params: Option, - ) -> Result { + ) -> Result<(AppUpdateMethodCallParams, CompiledPrograms), AppClientError> { // Compile programs (and populate AppManager cache/source maps) let compilation_params = compilation_params.unwrap_or_default(); - let (approval_program, clear_state_program) = - self.client.compile(&compilation_params).await?; + let compiled = self.client.compile(&compilation_params).await?; let abi_method = self.get_abi_method(¶ms.method)?; let sender = self.client.get_sender_address(¶ms.sender)?.as_str(); @@ -126,7 +125,7 @@ impl<'app_client> ParamsBuilder<'app_client> { .resolve_args(&abi_method, ¶ms.args, &sender) .await?; - Ok(AppUpdateMethodCallParams { + let update_params = AppUpdateMethodCallParams { sender: self.client.get_sender_address(¶ms.sender)?, signer: self .client @@ -143,15 +142,15 @@ impl<'app_client> ParamsBuilder<'app_client> { app_id: self.client.app_id, method: abi_method, args: resolved_args, - account_references: super::utils::parse_account_refs_to_addresses( - ¶ms.account_references, - )?, + account_references: parse_account_refs_to_addresses(¶ms.account_references)?, app_references: params.app_references.clone(), asset_references: params.asset_references.clone(), box_references: params.box_references.clone(), - approval_program, - clear_state_program, - }) + approval_program: compiled.approval.compiled_base64_to_bytes.clone(), + clear_state_program: compiled.clear.compiled_base64_to_bytes.clone(), + }; + + Ok((update_params, compiled)) } /// Build parameters for funding the application's account. @@ -210,9 +209,7 @@ impl<'app_client> ParamsBuilder<'app_client> { app_id: self.client.app_id, method: abi_method, args: resolved_args, - account_references: super::utils::parse_account_refs_to_addresses( - ¶ms.account_references, - )?, + account_references: parse_account_refs_to_addresses(¶ms.account_references)?, app_references: params.app_references.clone(), asset_references: params.asset_references.clone(), box_references: params.box_references.clone(), @@ -458,9 +455,7 @@ impl BareParamsBuilder<'_> { last_valid_round: params.last_valid_round, app_id: self.client.app_id, args: params.args, - account_references: super::utils::parse_account_refs_to_addresses( - ¶ms.account_references, - )?, + account_references: parse_account_refs_to_addresses(¶ms.account_references)?, app_references: params.app_references, asset_references: params.asset_references, box_references: params.box_references, @@ -480,13 +475,12 @@ impl BareParamsBuilder<'_> { &self, params: AppClientBareCallParams, compilation_params: Option, - ) -> Result { + ) -> Result<(AppUpdateParams, CompiledPrograms), AppClientError> { // Compile programs (and populate AppManager cache/source maps) let compilation_params = compilation_params.unwrap_or_default(); - let (approval_program, clear_state_program) = - self.client.compile(&compilation_params).await?; + let compiled = self.client.compile(&compilation_params).await?; - Ok(AppUpdateParams { + let update_params = AppUpdateParams { sender: self.client.get_sender_address(¶ms.sender)?, signer: self .client @@ -502,15 +496,15 @@ impl BareParamsBuilder<'_> { last_valid_round: params.last_valid_round, app_id: self.client.app_id, args: params.args, - account_references: super::utils::parse_account_refs_to_addresses( - ¶ms.account_references, - )?, + account_references: parse_account_refs_to_addresses(¶ms.account_references)?, app_references: params.app_references, asset_references: params.asset_references, box_references: params.box_references, - approval_program, - clear_state_program, - }) + approval_program: compiled.approval.compiled_base64_to_bytes.clone(), + clear_state_program: compiled.clear.compiled_base64_to_bytes.clone(), + }; + + Ok((update_params, compiled)) } fn build_bare_app_call_params( @@ -535,9 +529,7 @@ impl BareParamsBuilder<'_> { app_id: self.client.app_id, on_complete, args: params.args, - account_references: super::utils::parse_account_refs_to_addresses( - ¶ms.account_references, - )?, + account_references: parse_account_refs_to_addresses(¶ms.account_references)?, app_references: params.app_references, asset_references: params.asset_references, box_references: params.box_references, diff --git a/crates/algokit_utils/src/applications/app_client/sender.rs b/crates/algokit_utils/src/applications/app_client/sender.rs index 68bd44ad4..50e42cb4e 100644 --- a/crates/algokit_utils/src/applications/app_client/sender.rs +++ b/crates/algokit_utils/src/applications/app_client/sender.rs @@ -1,6 +1,7 @@ +use crate::applications::app_client::utils::transform_transaction_error; use crate::transactions::SendTransactionResult; use crate::transactions::composer::SimulateParams; -use crate::{AppClientError, SendAppCallResult, SendParams}; +use crate::{AppClientError, SendAppCallResult, SendAppUpdateResult, SendParams}; use algokit_transact::{MAX_SIMULATE_OPCODE_BUDGET, OnApplicationComplete}; use super::types::{AppClientBareCallParams, AppClientMethodCallParams, CompilationParams}; @@ -111,7 +112,7 @@ impl<'app_client> TransactionSender<'app_client> { .send() .app_call_method_call(method_params, send_params) .await - .map_err(|e| super::utils::transform_transaction_error(self.client, e, false)) + .map_err(|e| transform_transaction_error(self.client, e, false)) } } @@ -128,7 +129,7 @@ impl<'app_client> TransactionSender<'app_client> { .send() .app_call_method_call(method_params, send_params) .await - .map_err(|e| super::utils::transform_transaction_error(self.client, e, false)) + .map_err(|e| transform_transaction_error(self.client, e, false)) } /// Execute an ABI method call with CloseOut on-complete action. @@ -144,7 +145,7 @@ impl<'app_client> TransactionSender<'app_client> { .send() .app_call_method_call(method_params, send_params) .await - .map_err(|e| super::utils::transform_transaction_error(self.client, e, false)) + .map_err(|e| transform_transaction_error(self.client, e, false)) } /// Execute an ABI method call with Delete on-complete action. @@ -160,7 +161,7 @@ impl<'app_client> TransactionSender<'app_client> { .send() .app_delete_method_call(delete_params, send_params) .await - .map_err(|e| super::utils::transform_transaction_error(self.client, e, false)) + .map_err(|e| transform_transaction_error(self.client, e, false)) } /// Update the application using an ABI method call. @@ -169,19 +170,27 @@ impl<'app_client> TransactionSender<'app_client> { params: AppClientMethodCallParams, compilation_params: Option, send_params: Option, - ) -> Result { - let update_params = self + ) -> Result { + let (update_params, compiled) = self .client .params() .update(params, compilation_params) .await?; - self.client - .algorand + let mut result = self + .client + .algorand() .send() .app_update_method_call(update_params, send_params) .await - .map_err(|e| super::utils::transform_transaction_error(self.client, e, false)) + .map_err(|e| transform_transaction_error(self.client, e, false))?; + + result.compiled_approval = Some(compiled.approval.compiled_base64_to_bytes.clone()); + result.compiled_clear = Some(compiled.clear.compiled_base64_to_bytes.clone()); + result.approval_source_map = compiled.approval.source_map.clone(); + result.clear_source_map = compiled.clear.source_map.clone(); + + Ok(result) } /// Send payment to fund the application's account. @@ -197,7 +206,7 @@ impl<'app_client> TransactionSender<'app_client> { .send() .payment(payment, send_params) .await - .map_err(|e| super::utils::transform_transaction_error(self.client, e, false)) + .map_err(|e| transform_transaction_error(self.client, e, false)) } } @@ -215,7 +224,7 @@ impl BareTransactionSender<'_> { .send() .app_call(params, send_params) .await - .map_err(|e| super::utils::transform_transaction_error(self.client, e, false)) + .map_err(|e| transform_transaction_error(self.client, e, false)) } /// Execute a bare application call with OptIn on-complete action. @@ -230,7 +239,7 @@ impl BareTransactionSender<'_> { .send() .app_call(app_call, send_params) .await - .map_err(|e| super::utils::transform_transaction_error(self.client, e, false)) + .map_err(|e| transform_transaction_error(self.client, e, false)) } /// Execute a bare application call with CloseOut on-complete action. @@ -245,7 +254,7 @@ impl BareTransactionSender<'_> { .send() .app_call(app_call, send_params) .await - .map_err(|e| super::utils::transform_transaction_error(self.client, e, false)) + .map_err(|e| transform_transaction_error(self.client, e, false)) } /// Execute a bare application call with Delete on-complete action. @@ -260,7 +269,7 @@ impl BareTransactionSender<'_> { .send() .app_delete(delete_params, send_params) .await - .map_err(|e| super::utils::transform_transaction_error(self.client, e, false)) + .map_err(|e| transform_transaction_error(self.client, e, false)) } /// Execute a bare application call with ClearState on-complete action. @@ -275,7 +284,7 @@ impl BareTransactionSender<'_> { .send() .app_call(app_call, send_params) .await - .map_err(|e| super::utils::transform_transaction_error(self.client, e, true)) + .map_err(|e| transform_transaction_error(self.client, e, true)) } /// Update the application using a bare application call. @@ -284,8 +293,8 @@ impl BareTransactionSender<'_> { params: AppClientBareCallParams, compilation_params: Option, send_params: Option, - ) -> Result { - let update_params = self + ) -> Result { + let (update_params, _compiled) = self .client .params() .bare() @@ -297,6 +306,6 @@ impl BareTransactionSender<'_> { .send() .app_update(update_params, send_params) .await - .map_err(|e| super::utils::transform_transaction_error(self.client, e, false)) + .map_err(|e| transform_transaction_error(self.client, e, false)) } } diff --git a/crates/algokit_utils/src/applications/app_client/transaction_builder.rs b/crates/algokit_utils/src/applications/app_client/transaction_builder.rs index ce0f2c2db..0e99a3186 100644 --- a/crates/algokit_utils/src/applications/app_client/transaction_builder.rs +++ b/crates/algokit_utils/src/applications/app_client/transaction_builder.rs @@ -108,14 +108,14 @@ impl TransactionBuilder<'_> { params: AppClientMethodCallParams, compilation_params: Option, ) -> Result { - let params = self + let (params, _compiled) = self .client .params() .update(params, compilation_params) .await?; let trasactions = self .client - .algorand + .algorand() .create() .app_update_method_call(params) .map_err(|e| AppClientError::ComposerError { source: e }) @@ -216,14 +216,14 @@ impl BareTransactionBuilder<'_> { params: AppClientBareCallParams, compilation_params: Option, ) -> Result { - let params: crate::AppUpdateParams = self + let (params, _compiled) = self .client .params() .bare() .update(params, compilation_params) .await?; self.client - .algorand + .algorand() .create() .app_update(params) .map_err(|e| AppClientError::ComposerError { source: e }) diff --git a/crates/algokit_utils/src/applications/app_client/types.rs b/crates/algokit_utils/src/applications/app_client/types.rs index ad9f25175..f705c1ad8 100644 --- a/crates/algokit_utils/src/applications/app_client/types.rs +++ b/crates/algokit_utils/src/applications/app_client/types.rs @@ -17,17 +17,11 @@ pub struct AppSourceMaps { } /// Parameters required to construct an AppClient instance. -// Important: do NOT derive Clone for this struct while it contains `AlgorandClient`. -// `AlgorandClient` is intentionally non-Clone: it owns live HTTP clients, internal caches, -// and shared mutable state (e.g., signer registry via Arc>). Forcing Clone here -// would either require making `AlgorandClient` Clone or wrapping it in Arc implicitly, -// which encourages accidental copying of a process-wide client and confusing ownership/ -// lifetime semantics. If you need to share the client, wrap it in Arc at the call site -// and pass that explicitly, rather than deriving Clone on this params type. +#[derive(Clone)] pub struct AppClientParams { pub app_id: u64, pub app_spec: Arc56Contract, - pub algorand: AlgorandClient, + pub algorand: Arc, pub app_name: Option, pub default_sender: Option, pub default_signer: Option>, @@ -41,7 +35,7 @@ pub struct FundAppAccountParams { pub amount: u64, pub sender: Option, #[debug(skip)] - pub signer: Option>, + pub signer: Option>, pub rekey_to: Option, pub note: Option>, pub lease: Option<[u8; 32]>, @@ -61,7 +55,7 @@ pub struct AppClientMethodCallParams { pub args: Vec, pub sender: Option, #[debug(skip)] - pub signer: Option>, + pub signer: Option>, pub rekey_to: Option, pub note: Option>, pub lease: Option<[u8; 32]>, @@ -83,7 +77,7 @@ pub struct AppClientBareCallParams { pub args: Option>>, pub sender: Option, #[debug(skip)] - pub signer: Option>, + pub signer: Option>, pub rekey_to: Option, pub note: Option>, pub lease: Option<[u8; 32]>, diff --git a/crates/algokit_utils/src/applications/app_client/utils.rs b/crates/algokit_utils/src/applications/app_client/utils.rs index 65ac9c069..db2d0b5fe 100644 --- a/crates/algokit_utils/src/applications/app_client/utils.rs +++ b/crates/algokit_utils/src/applications/app_client/utils.rs @@ -1,7 +1,7 @@ use super::AppClient; use super::error_transformation::extract_logic_error_data; -use crate::AppClientError; use crate::transactions::TransactionSenderError; +use crate::{AppClientError, TransactionResultError}; use std::str::FromStr; fn contains_logic_error(s: &str) -> bool { @@ -23,7 +23,7 @@ pub fn transform_transaction_error( return AppClientError::TransactionSenderError { source: err }; } } - let tx_err = crate::transactions::TransactionResultError::ParsingError { + let tx_err = TransactionResultError::ParsingError { message: err_str.clone(), }; let logic = client.expose_logic_error(&tx_err, is_clear_state_program); diff --git a/crates/algokit_utils/src/applications/app_deployer.rs b/crates/algokit_utils/src/applications/app_deployer.rs index aa5b0b06c..1a208fe6d 100644 --- a/crates/algokit_utils/src/applications/app_deployer.rs +++ b/crates/algokit_utils/src/applications/app_deployer.rs @@ -434,7 +434,8 @@ impl AppDeployer { // Check for changes let is_update = self.is_program_different(&approval_bytes, &clear_bytes, &existing_app)?; - let is_schema_break = self.is_schema_break(&create_params, &existing_app)?; + let is_schema_break = + self.is_schema_break(&create_params, &existing_app, &approval_bytes, &clear_bytes)?; if is_schema_break { self.handle_schema_break( @@ -496,7 +497,7 @@ impl AppDeployer { ), })?; - // Query indexer for apps created by this address + // Query indexer for apps created by this address; localnet-only retry to allow catch-up let created_apps_response = indexer .lookup_account_created_applications(&creator_address_str, None, Some(true), None, None) .await @@ -645,17 +646,18 @@ impl AppDeployer { ) -> Result<(Vec, Vec), AppDeployError> { let approval_bytes = match approval_program { AppProgram::Teal(code) => { - let deployment_metadata_for_compilation = DeploymentMetadata { + let metadata = DeploymentMetadata { updatable: deployment_metadata.updatable, deletable: deployment_metadata.deletable, }; + let metadata_opt = if metadata.updatable.is_some() || metadata.deletable.is_some() { + Some(&metadata) + } else { + None + }; let compiled = self .app_manager - .compile_teal_template( - code, - deploy_time_params, - Some(&deployment_metadata_for_compilation), - ) + .compile_teal_template(code, deploy_time_params, metadata_opt) .await .map_err(|e| AppDeployError::AppManagerError { source: e })?; compiled.compiled_base64_to_bytes @@ -705,20 +707,22 @@ impl AppDeployer { &self, create_params: &CreateParams, existing_app: &AppInformation, + approval_program: &[u8], + clear_state_program: &[u8], ) -> Result { - let (new_global_schema, new_local_schema, new_extra_pages) = match create_params { + let (new_global_schema, new_local_schema) = match create_params { CreateParams::AppCreateCall(params) => ( params.global_state_schema.as_ref(), params.local_state_schema.as_ref(), - params.extra_program_pages.unwrap_or(0), ), CreateParams::AppCreateMethodCall(params) => ( params.global_state_schema.as_ref(), params.local_state_schema.as_ref(), - params.extra_program_pages.unwrap_or(0), ), }; + let new_extra_pages = + Self::calculate_extra_program_pages(approval_program, clear_state_program); let global_ints_break = new_global_schema.is_some_and(|schema| schema.num_uints > existing_app.global_ints); let global_bytes_break = new_global_schema @@ -883,6 +887,8 @@ impl AppDeployer { ) -> Result { let result = match create_params { CreateParams::AppCreateCall(params) => { + let computed_extra_pages = + Self::calculate_extra_program_pages(approval_program, clear_state_program); let app_create_params = AppCreateParams { sender: params.sender.clone(), signer: params.signer.clone(), @@ -900,7 +906,7 @@ impl AppDeployer { clear_state_program: clear_state_program.to_vec(), global_state_schema: params.global_state_schema.clone(), local_state_schema: params.local_state_schema.clone(), - extra_program_pages: params.extra_program_pages, + extra_program_pages: params.extra_program_pages.or(Some(computed_extra_pages)), args: params.args.clone(), account_references: params.account_references.clone(), app_references: params.app_references.clone(), @@ -913,6 +919,8 @@ impl AppDeployer { .map_err(|e| AppDeployError::TransactionSenderError { source: e })? } CreateParams::AppCreateMethodCall(params) => { + let computed_extra_pages = + Self::calculate_extra_program_pages(approval_program, clear_state_program); let app_create_method_params = AppCreateMethodCallParams { sender: params.sender.clone(), signer: params.signer.clone(), @@ -930,7 +938,7 @@ impl AppDeployer { clear_state_program: clear_state_program.to_vec(), global_state_schema: params.global_state_schema.clone(), local_state_schema: params.local_state_schema.clone(), - extra_program_pages: params.extra_program_pages, + extra_program_pages: params.extra_program_pages.or(Some(computed_extra_pages)), method: params.method.clone(), args: params.args.clone(), account_references: params.account_references.clone(), @@ -1133,6 +1141,8 @@ impl AppDeployer { // Add create transaction match create_params { CreateParams::AppCreateCall(params) => { + let computed_extra_pages = + Self::calculate_extra_program_pages(approval_program, clear_state_program); let app_create_params = AppCreateParams { sender: params.sender.clone(), signer: params.signer.clone(), @@ -1150,7 +1160,7 @@ impl AppDeployer { clear_state_program: clear_state_program.to_vec(), global_state_schema: params.global_state_schema.clone(), local_state_schema: params.local_state_schema.clone(), - extra_program_pages: params.extra_program_pages, + extra_program_pages: params.extra_program_pages.or(Some(computed_extra_pages)), args: params.args.clone(), account_references: params.account_references.clone(), app_references: params.app_references.clone(), @@ -1162,6 +1172,8 @@ impl AppDeployer { .map_err(|e| AppDeployError::ComposerError { source: e })?; } CreateParams::AppCreateMethodCall(params) => { + let computed_extra_pages = + Self::calculate_extra_program_pages(approval_program, clear_state_program); let app_create_method_params = AppCreateMethodCallParams { sender: params.sender.clone(), signer: params.signer.clone(), @@ -1179,7 +1191,7 @@ impl AppDeployer { clear_state_program: clear_state_program.to_vec(), global_state_schema: params.global_state_schema.clone(), local_state_schema: params.local_state_schema.clone(), - extra_program_pages: params.extra_program_pages, + extra_program_pages: params.extra_program_pages.or(Some(computed_extra_pages)), method: params.method.clone(), args: params.args.clone(), account_references: params.account_references.clone(), @@ -1287,4 +1299,15 @@ impl AppDeployer { result, }) } + + /// Calculate minimum number of extra program pages required to fit the programs. + fn calculate_extra_program_pages(approval: &[u8], clear: &[u8]) -> u32 { + let total = approval.len().saturating_add(clear.len()); + if total == 0 { + return 0; + } + let page_size = algokit_transact::PROGRAM_PAGE_SIZE; + let pages = ((total - 1) / page_size) as u32; + std::cmp::min(pages, algokit_transact::MAX_EXTRA_PROGRAM_PAGES) + } } diff --git a/crates/algokit_utils/src/applications/app_factory/compilation.rs b/crates/algokit_utils/src/applications/app_factory/compilation.rs new file mode 100644 index 000000000..d85401f14 --- /dev/null +++ b/crates/algokit_utils/src/applications/app_factory/compilation.rs @@ -0,0 +1,90 @@ +use super::{AppFactory, AppFactoryError}; +use crate::applications::app_client::CompilationParams; +use crate::clients::app_manager::CompiledPrograms; + +impl AppFactory { + pub(crate) fn resolve_compilation_params( + &self, + override_cp: Option, + ) -> CompilationParams { + let mut resolved = override_cp.unwrap_or_default(); + if resolved.deploy_time_params.is_none() { + resolved.deploy_time_params = self.deploy_time_params.clone(); + } + if resolved.updatable.is_none() { + resolved.updatable = self.updatable.or_else(|| { + self.detect_deploy_time_control_flag( + crate::clients::app_manager::UPDATABLE_TEMPLATE_NAME, + algokit_abi::arc56_contract::CallOnApplicationComplete::UpdateApplication, + ) + }); + } + if resolved.deletable.is_none() { + resolved.deletable = self.deletable.or_else(|| { + self.detect_deploy_time_control_flag( + crate::clients::app_manager::DELETABLE_TEMPLATE_NAME, + algokit_abi::arc56_contract::CallOnApplicationComplete::DeleteApplication, + ) + }); + } + resolved + } + + pub(crate) async fn compile_programs_with( + &self, + override_cp: Option, + ) -> Result { + let cp = self.resolve_compilation_params(override_cp); + let source = + self.app_spec() + .source + .as_ref() + .ok_or_else(|| AppFactoryError::CompilationError { + message: "Missing source in app spec".to_string(), + })?; + + let approval_teal = + source + .get_decoded_approval() + .map_err(|e| AppFactoryError::CompilationError { + message: e.to_string(), + })?; + let clear_teal = + source + .get_decoded_clear() + .map_err(|e| AppFactoryError::CompilationError { + message: e.to_string(), + })?; + + let metadata = crate::clients::app_manager::DeploymentMetadata { + updatable: cp.updatable, + deletable: cp.deletable, + }; + let metadata_opt = if metadata.updatable.is_some() || metadata.deletable.is_some() { + Some(&metadata) + } else { + None + }; + let approval = self + .algorand() + .app() + .compile_teal_template(&approval_teal, cp.deploy_time_params.as_ref(), metadata_opt) + .await + .map_err(|e| AppFactoryError::CompilationError { + message: e.to_string(), + })?; + + let clear = self + .algorand() + .app() + .compile_teal_template(&clear_teal, cp.deploy_time_params.as_ref(), None) + .await + .map_err(|e| AppFactoryError::CompilationError { + message: e.to_string(), + })?; + + self.update_source_maps(approval.source_map.clone(), clear.source_map.clone()); + + Ok(CompiledPrograms { approval, clear }) + } +} diff --git a/crates/algokit_utils/src/applications/app_factory/error.rs b/crates/algokit_utils/src/applications/app_factory/error.rs new file mode 100644 index 000000000..d00c1f79f --- /dev/null +++ b/crates/algokit_utils/src/applications/app_factory/error.rs @@ -0,0 +1,20 @@ +use crate::AppClientError; +use crate::applications::app_deployer::AppDeployError; +use crate::transactions::TransactionSenderError; +use snafu::Snafu; + +#[derive(Debug, Snafu)] +pub enum AppFactoryError { + #[snafu(display("Method not found: {message}"))] + MethodNotFound { message: String }, + #[snafu(display("Compilation error: {message}"))] + CompilationError { message: String }, + #[snafu(display("Validation error: {message}"))] + ValidationError { message: String }, + #[snafu(display("App client error: {source}"))] + AppClientError { source: AppClientError }, + #[snafu(display("Transaction sender error: {source}"))] + TransactionSenderError { source: TransactionSenderError }, + #[snafu(display("App deployer error: {source}"))] + AppDeployerError { source: AppDeployError }, +} diff --git a/crates/algokit_utils/src/applications/app_factory/mod.rs b/crates/algokit_utils/src/applications/app_factory/mod.rs new file mode 100644 index 000000000..da8fa0be3 --- /dev/null +++ b/crates/algokit_utils/src/applications/app_factory/mod.rs @@ -0,0 +1,620 @@ +use std::collections::HashMap; +use std::str::FromStr; +use std::sync::{Arc, Mutex}; + +use algokit_abi::arc56_contract::CallOnApplicationComplete; +use algokit_abi::{ABIReturn, ABIValue, Arc56Contract}; + +use crate::applications::app_client::{AppClientMethodCallParams, CompilationParams}; +use crate::applications::app_deployer::{AppLookup, OnSchemaBreak, OnUpdate}; +use crate::applications::app_factory; +use crate::clients::app_manager::{ + DELETABLE_TEMPLATE_NAME, TealTemplateValue, UPDATABLE_TEMPLATE_NAME, +}; +use crate::transactions::{ + TransactionComposerConfig, TransactionResultError, TransactionSigner, + composer::{SendParams as ComposerSendParams, SendTransactionComposerResults}, + sender_results::{ + SendAppCallResult, SendAppCreateResult, SendAppUpdateResult, SendTransactionResult, + }, +}; +use crate::{AlgorandClient, AppClient, AppClientParams, AppSourceMaps}; +use app_factory::types as aftypes; + +mod compilation; +mod error; +mod params_builder; +mod sender; +mod transaction_builder; +mod types; +mod utils; + +pub use error::AppFactoryError; +pub use params_builder::ParamsBuilder; +pub use sender::TransactionSender; +pub use transaction_builder::TransactionBuilder; +pub use types::*; + +/// Factory for creating and deploying Algorand applications from an ARC-56 spec. +pub struct AppFactory { + app_spec: Arc56Contract, + algorand: Arc, + app_name: String, + version: String, + default_sender: Option, + default_signer: Option>, + approval_source_map: Mutex>, + clear_source_map: Mutex>, + pub(crate) deploy_time_params: Option>, + pub(crate) updatable: Option, + pub(crate) deletable: Option, + pub(crate) transaction_composer_config: Option, +} + +#[derive(Default)] +pub struct DeployArgs { + pub on_update: Option, + pub on_schema_break: Option, + pub create_params: Option, + pub update_params: Option, + pub delete_params: Option, + pub existing_deployments: Option, + pub ignore_cache: Option, + pub app_name: Option, + pub send_params: Option, +} + +impl AppFactory { + async fn deploy_create_result( + &self, + composer_result: &SendTransactionComposerResults, + ) -> Result { + let compiled = self.compile_programs_with(None).await?; + let base = self.to_send_transaction_result(composer_result)?; + let last_abi_return = base.abi_returns.as_ref().and_then(|v| v.last()).cloned(); + let created = SendAppCreateResult::new( + base, + last_abi_return.clone(), + Some(compiled.approval.compiled_base64_to_bytes.clone()), + Some(compiled.clear.compiled_base64_to_bytes.clone()), + compiled.approval.source_map.clone(), + compiled.clear.source_map.clone(), + ) + .map_err(|e| AppFactoryError::ValidationError { + message: e.to_string(), + })?; + let arc56_return = self.parse_method_return_value(&last_abi_return)?; + Ok(AppFactoryMethodCallResult::new(created, arc56_return)) + } + + async fn deploy_update_result( + &self, + composer_result: &SendTransactionComposerResults, + ) -> Result { + let compiled = self.compile_programs_with(None).await?; + let base = self.to_send_transaction_result(composer_result)?; + let last_abi_return = base.abi_returns.as_ref().and_then(|v| v.last()).cloned(); + let updated = SendAppUpdateResult::new( + base, + last_abi_return.clone(), + Some(compiled.approval.compiled_base64_to_bytes.clone()), + Some(compiled.clear.compiled_base64_to_bytes.clone()), + compiled.approval.source_map.clone(), + compiled.clear.source_map.clone(), + ); + let arc56_return = self.parse_method_return_value(&last_abi_return)?; + Ok(AppFactoryMethodCallResult::new(updated, arc56_return)) + } + + async fn deploy_replace_results( + &self, + composer_result: &SendTransactionComposerResults, + ) -> Result< + ( + Option, + Option, + ), + AppFactoryError, + > { + if composer_result.confirmations.is_empty() + || composer_result.confirmations.len() != composer_result.transaction_ids.len() + { + return Ok((None, None)); + } + let compiled = self.compile_programs_with(None).await?; + // Create index 0 + let create_tx = composer_result.confirmations[0].txn.transaction.clone(); + let create_base = SendTransactionResult::new( + composer_result.group.map(hex::encode).unwrap_or_default(), + vec![composer_result.transaction_ids[0].clone()], + vec![create_tx], + vec![composer_result.confirmations[0].clone()], + if !composer_result.abi_returns.is_empty() { + Some(vec![composer_result.abi_returns[0].clone()]) + } else { + None + }, + ) + .map_err(|e| AppFactoryError::ValidationError { + message: e.to_string(), + })?; + let create_abi = create_base + .abi_returns + .as_ref() + .and_then(|v| v.last()) + .cloned(); + let created = SendAppCreateResult::new( + create_base, + create_abi.clone(), + Some(compiled.approval.compiled_base64_to_bytes.clone()), + Some(compiled.clear.compiled_base64_to_bytes.clone()), + compiled.approval.source_map.clone(), + compiled.clear.source_map.clone(), + ) + .map_err(|e| AppFactoryError::ValidationError { + message: e.to_string(), + })?; + let create_arc56 = self.parse_method_return_value(&create_abi)?; + let create_result = Some(AppFactoryMethodCallResult::new(created, create_arc56)); + // Optional delete index 1 + let delete_result = if composer_result.confirmations.len() > 1 { + let delete_tx = composer_result.confirmations[1].txn.transaction.clone(); + let delete_base = SendTransactionResult::new( + composer_result.group.map(hex::encode).unwrap_or_default(), + vec![composer_result.transaction_ids[1].clone()], + vec![delete_tx], + vec![composer_result.confirmations[1].clone()], + if composer_result.abi_returns.len() > 1 { + Some(vec![composer_result.abi_returns[1].clone()]) + } else { + None + }, + ) + .map_err(|e| AppFactoryError::ValidationError { + message: e.to_string(), + })?; + let delete_abi = delete_base + .abi_returns + .as_ref() + .and_then(|v| v.last()) + .cloned(); + let deleted = SendAppCallResult::new(delete_base, delete_abi.clone()); + let delete_arc56 = self.parse_method_return_value(&delete_abi)?; + Some(AppFactoryMethodCallResult::new(deleted, delete_arc56)) + } else { + None + }; + Ok((create_result, delete_result)) + } + /// Convert SendTransactionComposerResults into a rich SendTransactionResult by + /// reconstructing transactions from confirmations. + fn to_send_transaction_result( + &self, + composer_results: &SendTransactionComposerResults, + ) -> Result { + let group_id = composer_results.group.map(hex::encode).unwrap_or_default(); + + // Reconstruct transactions from confirmations (txn.signed.transaction) + let transactions: Vec = composer_results + .confirmations + .iter() + .map(|c| c.txn.transaction.clone()) + .collect(); + + SendTransactionResult::new( + group_id, + composer_results.transaction_ids.clone(), + transactions, + composer_results.confirmations.clone(), + if composer_results.abi_returns.is_empty() { + None + } else { + Some(composer_results.abi_returns.clone()) + }, + ) + .map_err(|e| AppFactoryError::ValidationError { + message: e.to_string(), + }) + } + pub fn new(params: AppFactoryParams) -> Self { + let AppFactoryParams { + algorand, + app_spec, + app_name, + default_sender, + default_signer, + version, + deploy_time_params, + updatable, + deletable, + source_maps, + transaction_composer_config, + } = params; + + let (initial_approval_source_map, initial_clear_source_map) = match source_maps { + Some(maps) => (maps.approval_source_map, maps.clear_source_map), + None => (None, None), + }; + + Self { + app_spec, + algorand, + app_name: app_name.unwrap_or_else(|| "".to_string()), + version: version.unwrap_or_else(|| "1.0".to_string()), + default_sender, + default_signer, + approval_source_map: Mutex::new(initial_approval_source_map), + clear_source_map: Mutex::new(initial_clear_source_map), + deploy_time_params, + updatable, + deletable, + transaction_composer_config, + } + } + + pub fn app_name(&self) -> &str { + &self.app_name + } + pub fn app_spec(&self) -> &Arc56Contract { + &self.app_spec + } + + pub fn algorand(&self) -> Arc { + self.algorand.clone() + } + + pub fn version(&self) -> &str { + &self.version + } + + pub fn params(&self) -> ParamsBuilder<'_> { + ParamsBuilder { factory: self } + } + pub fn create_transaction(&self) -> TransactionBuilder<'_> { + TransactionBuilder { factory: self } + } + pub fn send(&self) -> TransactionSender<'_> { + TransactionSender { factory: self } + } + + pub fn import_source_maps(&self, source_maps: AppSourceMaps) { + *self.approval_source_map.lock().unwrap() = source_maps.approval_source_map; + *self.clear_source_map.lock().unwrap() = source_maps.clear_source_map; + } + + pub fn export_source_maps(&self) -> Result { + let approval = self + .approval_source_map + .lock() + .unwrap() + .clone() + .ok_or_else(|| AppFactoryError::ValidationError { + message: "Approval source map not loaded".to_string(), + })?; + let clear = self + .clear_source_map + .lock() + .unwrap() + .clone() + .ok_or_else(|| AppFactoryError::ValidationError { + message: "Clear source map not loaded".to_string(), + })?; + Ok(AppSourceMaps { + approval_source_map: Some(approval), + clear_source_map: Some(clear), + }) + } + + pub async fn compile( + &self, + compilation_params: Option, + ) -> Result { + let compiled = self.compile_programs_with(compilation_params).await?; + Ok(AppFactoryCompilationResult { + approval_program: compiled.approval.compiled_base64_to_bytes.clone(), + clear_state_program: compiled.clear.compiled_base64_to_bytes.clone(), + compiled_approval: compiled.approval, + compiled_clear: compiled.clear, + }) + } + + pub fn get_app_client_by_id( + &self, + app_id: u64, + app_name: Option, + default_sender: Option, + default_signer: Option>, + source_maps: Option, + ) -> AppClient { + let resolved_source_maps = source_maps.or_else(|| self.current_source_maps()); + AppClient::new(AppClientParams { + app_id, + app_spec: self.app_spec.clone(), + algorand: self.algorand.clone(), + app_name: Some(app_name.unwrap_or_else(|| self.app_name.clone())), + default_sender: Some( + default_sender.unwrap_or_else(|| self.default_sender.clone().unwrap_or_default()), + ), + default_signer: default_signer.or_else(|| self.default_signer.clone()), + source_maps: resolved_source_maps, + transaction_composer_config: self.transaction_composer_config.clone(), + }) + } + + pub async fn get_app_client_by_creator_and_name( + &self, + creator_address: &str, + app_name: Option, + default_sender: Option, + default_signer: Option>, + ignore_cache: Option, + ) -> Result { + let resolved_app_name = app_name.unwrap_or_else(|| self.app_name.clone()); + let resolved_sender = default_sender.or_else(|| self.default_sender.clone()); + let resolved_signer = default_signer.or_else(|| self.default_signer.clone()); + + let client = AppClient::from_creator_and_name( + creator_address, + &resolved_app_name, + self.app_spec.clone(), + self.algorand.clone(), + resolved_sender, + resolved_signer, + self.current_source_maps(), + ignore_cache, + self.transaction_composer_config.clone(), + ) + .await + .map_err(|e| AppFactoryError::AppClientError { source: e })?; + + Ok(client) + } +} + +impl AppFactory { + pub(crate) fn get_sender_address( + &self, + sender: &Option, + ) -> Result { + let sender_str = sender + .as_ref() + .or(self.default_sender.as_ref()) + .ok_or_else(|| { + format!( + "No sender provided and no default sender configured for app {}", + self.app_name + ) + })?; + algokit_transact::Address::from_str(sender_str) + .map_err(|e| format!("Invalid sender address: {}", e)) + } + + pub(crate) fn update_source_maps( + &self, + approval: Option, + clear: Option, + ) { + *self.approval_source_map.lock().unwrap() = approval; + *self.clear_source_map.lock().unwrap() = clear; + } + + pub(crate) fn current_source_maps(&self) -> Option { + let approval = self.approval_source_map.lock().unwrap().clone(); + let clear = self.clear_source_map.lock().unwrap().clone(); + + if approval.is_none() && clear.is_none() { + None + } else { + Some(AppSourceMaps { + approval_source_map: approval, + clear_source_map: clear, + }) + } + } + + pub(crate) fn parse_method_return_value( + &self, + abi_return: &Option, + ) -> Result, AppFactoryError> { + match abi_return { + None => Ok(None), + Some(ret) => { + if let Some(err) = &ret.decode_error { + return Err(AppFactoryError::ValidationError { + message: err.to_string(), + }); + } + Ok(ret.return_value.clone()) + } + } + } + + pub(crate) fn detect_deploy_time_control_flag( + &self, + template_name: &str, + on_complete: CallOnApplicationComplete, + ) -> Option { + let source = self.app_spec().source.as_ref()?; + let approval = source.get_decoded_approval().ok()?; + if !approval.contains(template_name) { + return None; + } + + let bare_allows = self + .app_spec() + .bare_actions + .call + .iter() + .any(|action| *action == on_complete); + let method_allows = self.app_spec().methods.iter().any(|method| { + method + .actions + .call + .iter() + .any(|action| *action == on_complete) + }); + + Some(bare_allows || method_allows) + } + + pub(crate) fn logic_error_for( + &self, + error_str: &str, + is_clear_state_program: bool, + ) -> Option { + if !(error_str.contains("logic eval error") || error_str.contains("logic error")) { + return None; + } + + let tx_err = TransactionResultError::ParsingError { + message: error_str.to_string(), + }; + + let client = AppClient::new(AppClientParams { + app_id: 0, + app_spec: self.app_spec.clone(), + algorand: self.algorand.clone(), + app_name: Some(self.app_name.clone()), + default_sender: self.default_sender.clone(), + default_signer: self.default_signer.clone(), + source_maps: self.current_source_maps(), + transaction_composer_config: self.transaction_composer_config.clone(), + }); + + Some( + client + .expose_logic_error(&tx_err, is_clear_state_program) + .message, + ) + } + + /// Idempotently deploy (create/update/delete) an application using AppDeployer + #[allow(clippy::too_many_arguments)] + pub async fn deploy( + &self, + args: DeployArgs, + ) -> Result<(AppClient, aftypes::AppFactoryDeployResult), AppFactoryError> { + // Prepare create/update/delete deploy params + // Auto-detect deploy-time controls if not explicitly provided + let mut resolved_updatable = self.updatable; + let mut resolved_deletable = self.deletable; + if resolved_updatable.is_none() { + resolved_updatable = self.detect_deploy_time_control_flag( + UPDATABLE_TEMPLATE_NAME, + CallOnApplicationComplete::UpdateApplication, + ); + } + + if resolved_deletable.is_none() { + resolved_deletable = self.detect_deploy_time_control_flag( + DELETABLE_TEMPLATE_NAME, + CallOnApplicationComplete::DeleteApplication, + ); + } + let resolved_deploy_time_params = self.deploy_time_params.clone(); + + let create_deploy_params = match args.create_params { + Some(cp) => crate::applications::app_deployer::CreateParams::AppCreateMethodCall( + self.params().create(cp)?, + ), + None => crate::applications::app_deployer::CreateParams::AppCreateCall( + self.params().bare().create(None)?, + ), + }; + + let update_deploy_params = match args.update_params { + Some(up) => crate::applications::app_deployer::UpdateParams::AppUpdateMethodCall( + self.params().deploy_update(up)?, + ), + None => crate::applications::app_deployer::UpdateParams::AppUpdateCall( + self.params().bare().deploy_update(None)?, + ), + }; + + let delete_deploy_params = match args.delete_params { + Some(dp) => crate::applications::app_deployer::DeleteParams::AppDeleteMethodCall( + self.params().deploy_delete(dp)?, + ), + None => crate::applications::app_deployer::DeleteParams::AppDeleteCall( + self.params().bare().deploy_delete(None)?, + ), + }; + + let metadata = crate::applications::app_deployer::AppDeployMetadata { + name: args.app_name.unwrap_or_else(|| self.app_name.clone()), + version: self.version.clone(), + updatable: resolved_updatable, + deletable: resolved_deletable, + }; + + let deploy_params = crate::applications::app_deployer::AppDeployParams { + metadata, + deploy_time_params: resolved_deploy_time_params, + on_schema_break: args.on_schema_break, + on_update: args.on_update, + create_params: create_deploy_params, + update_params: update_deploy_params, + delete_params: delete_deploy_params, + existing_deployments: args.existing_deployments, + ignore_cache: args.ignore_cache, + send_params: args.send_params.unwrap_or_default(), + }; + + let mut app_deployer = self.algorand.as_ref().app_deployer(); + + let deploy_result = app_deployer + .deploy(deploy_params) + .await + .map_err(|e| AppFactoryError::AppDeployerError { source: e })?; + + // Build AppClient for the resulting app + let app_metadata = match &deploy_result { + crate::applications::app_deployer::AppDeployResult::Create { app, .. } + | crate::applications::app_deployer::AppDeployResult::Update { app, .. } + | crate::applications::app_deployer::AppDeployResult::Replace { app, .. } + | crate::applications::app_deployer::AppDeployResult::Nothing { app } => app, + }; + + // Create AppClient with shared signers from the factory's AlgorandClient + let app_client = AppClient::new(AppClientParams { + app_id: app_metadata.app_id, + app_spec: self.app_spec.clone(), + algorand: self.algorand.clone(), + app_name: Some(self.app_name.clone()), + default_sender: self.default_sender.clone(), + default_signer: self.default_signer.clone(), + source_maps: self.current_source_maps(), + transaction_composer_config: self.transaction_composer_config.clone(), + }); + + // Convert deploy result into factory result with enriched typed results + let mut create_result: Option = None; + let mut update_result: Option = None; + let mut delete_result: Option = None; + + match &deploy_result { + crate::applications::app_deployer::AppDeployResult::Create { result, .. } => { + create_result = Some(self.deploy_create_result(result).await?); + } + crate::applications::app_deployer::AppDeployResult::Update { result, .. } => { + update_result = Some(self.deploy_update_result(result).await?); + } + crate::applications::app_deployer::AppDeployResult::Replace { result, .. } => { + let (c, d) = self.deploy_replace_results(result).await?; + create_result = c; + delete_result = d; + } + crate::applications::app_deployer::AppDeployResult::Nothing { .. } => {} + } + + let factory_result = aftypes::AppFactoryDeployResult { + app: app_metadata.clone(), + operation_performed: deploy_result, + create_result, + update_result, + delete_result, + }; + + Ok((app_client, factory_result)) + } +} diff --git a/crates/algokit_utils/src/applications/app_factory/params_builder.rs b/crates/algokit_utils/src/applications/app_factory/params_builder.rs new file mode 100644 index 000000000..e3ce38879 --- /dev/null +++ b/crates/algokit_utils/src/applications/app_factory/params_builder.rs @@ -0,0 +1,316 @@ +use super::{AppFactory, AppFactoryError}; +use crate::applications::app_deployer::{ + AppProgram, DeployAppCreateMethodCallParams, DeployAppCreateParams, + DeployAppDeleteMethodCallParams, DeployAppDeleteParams, DeployAppUpdateMethodCallParams, + DeployAppUpdateParams, +}; +use crate::applications::app_factory::utils::merge_args_with_defaults; +use crate::applications::app_factory::{AppFactoryCreateMethodCallParams, AppFactoryCreateParams}; +use algokit_abi::ABIMethod; +use algokit_transact::OnApplicationComplete; +use algokit_transact::StateSchema as TxStateSchema; +use std::str::FromStr; + +use super::utils::resolve_signer; +pub struct ParamsBuilder<'a> { + pub(crate) factory: &'a AppFactory, +} + +pub struct BareParamsBuilder<'a> { + pub(crate) factory: &'a AppFactory, +} + +impl<'a> ParamsBuilder<'a> { + pub fn bare(&self) -> BareParamsBuilder<'a> { + BareParamsBuilder { + factory: self.factory, + } + } + + /// Create DeployAppCreateMethodCallParams from factory inputs + pub fn create( + &self, + params: AppFactoryCreateMethodCallParams, + ) -> Result { + let (approval_teal, clear_teal) = decode_teal_from_spec(self.factory)?; + let method = to_abi_method(self.factory.app_spec(), ¶ms.method)?; + let sender = self + .factory + .get_sender_address(¶ms.sender) + .map_err(|message| AppFactoryError::ValidationError { message })?; + + // Merge user args with ARC-56 literal defaults for create-time ABI + let merged_args = merge_args_with_defaults(self.factory, ¶ms.method, ¶ms.args)?; + + Ok(DeployAppCreateMethodCallParams { + sender, + signer: resolve_signer(self.factory, ¶ms.sender, params.signer), + rekey_to: params.rekey_to, + note: params.note, + lease: params.lease, + static_fee: params.static_fee, + extra_fee: params.extra_fee, + max_fee: params.max_fee, + validity_window: params.validity_window, + first_valid_round: params.first_valid_round, + last_valid_round: params.last_valid_round, + on_complete: params.on_complete.unwrap_or(OnApplicationComplete::NoOp), + approval_program: AppProgram::Teal(approval_teal), + clear_state_program: AppProgram::Teal(clear_teal), + method, + args: merged_args, + account_references: None, + app_references: params.app_references, + asset_references: params.asset_references, + box_references: params.box_references, + global_state_schema: params + .global_state_schema + .or_else(|| Some(default_global_schema(self.factory))), + local_state_schema: params + .local_state_schema + .or_else(|| Some(default_local_schema(self.factory))), + extra_program_pages: params.extra_program_pages, + }) + } + + /// Create DeployAppUpdateMethodCallParams + pub fn deploy_update( + &self, + params: crate::applications::app_client::AppClientMethodCallParams, + ) -> Result { + let method = to_abi_method(self.factory.app_spec(), ¶ms.method)?; + let sender = self + .factory + .get_sender_address(¶ms.sender) + .map_err(|message| AppFactoryError::ValidationError { message })?; + + let merged_args = + merge_args_with_defaults(self.factory, ¶ms.method, &Some(params.args.clone()))?; + + Ok(DeployAppUpdateMethodCallParams { + sender, + signer: resolve_signer(self.factory, ¶ms.sender, params.signer), + rekey_to: params + .rekey_to + .as_ref() + .and_then(|s| algokit_transact::Address::from_str(s).ok()), + note: params.note, + lease: params.lease, + static_fee: params.static_fee, + extra_fee: params.extra_fee, + max_fee: params.max_fee, + validity_window: params.validity_window, + first_valid_round: params.first_valid_round, + last_valid_round: params.last_valid_round, + method, + args: merged_args, + account_references: None, + app_references: params.app_references, + asset_references: params.asset_references, + box_references: params.box_references, + }) + } + + /// Create DeployAppDeleteMethodCallParams + pub fn deploy_delete( + &self, + params: crate::applications::app_client::AppClientMethodCallParams, + ) -> Result { + let method = to_abi_method(self.factory.app_spec(), ¶ms.method)?; + let sender = self + .factory + .get_sender_address(¶ms.sender) + .map_err(|message| AppFactoryError::ValidationError { message })?; + + let merged_args = + merge_args_with_defaults(self.factory, ¶ms.method, &Some(params.args.clone()))?; + + Ok(DeployAppDeleteMethodCallParams { + sender, + signer: resolve_signer(self.factory, ¶ms.sender, params.signer), + rekey_to: params + .rekey_to + .as_ref() + .and_then(|s| algokit_transact::Address::from_str(s).ok()), + note: params.note, + lease: params.lease, + static_fee: params.static_fee, + extra_fee: params.extra_fee, + max_fee: params.max_fee, + validity_window: params.validity_window, + first_valid_round: params.first_valid_round, + last_valid_round: params.last_valid_round, + method, + args: merged_args, + account_references: None, + app_references: params.app_references, + asset_references: params.asset_references, + box_references: params.box_references, + }) + } +} + +impl BareParamsBuilder<'_> { + /// Create DeployAppCreateParams from factory inputs + pub fn create( + &self, + params: Option, + ) -> Result { + let params = params.unwrap_or_default(); + let (approval_teal, clear_teal) = decode_teal_from_spec(self.factory)?; + let sender = self + .factory + .get_sender_address(¶ms.sender) + .map_err(|message| AppFactoryError::ValidationError { message })?; + + Ok(DeployAppCreateParams { + sender, + signer: resolve_signer(self.factory, ¶ms.sender, params.signer), + rekey_to: params.rekey_to, + note: params.note, + lease: params.lease, + static_fee: params.static_fee, + extra_fee: params.extra_fee, + max_fee: params.max_fee, + validity_window: params.validity_window, + first_valid_round: params.first_valid_round, + last_valid_round: params.last_valid_round, + on_complete: params.on_complete.unwrap_or(OnApplicationComplete::NoOp), + approval_program: AppProgram::Teal(approval_teal), + clear_state_program: AppProgram::Teal(clear_teal), + args: params.args, + account_references: None, + app_references: params.app_references, + asset_references: params.asset_references, + box_references: params.box_references, + global_state_schema: params + .global_state_schema + .or_else(|| Some(default_global_schema(self.factory))), + local_state_schema: params + .local_state_schema + .or_else(|| Some(default_local_schema(self.factory))), + extra_program_pages: params.extra_program_pages, + }) + } + + /// Create DeployAppUpdateParams + pub fn deploy_update( + &self, + params: Option, + ) -> Result { + let params = params.unwrap_or_default(); + let sender = self + .factory + .get_sender_address(¶ms.sender) + .map_err(|message| AppFactoryError::ValidationError { message })?; + + Ok(DeployAppUpdateParams { + sender, + signer: resolve_signer(self.factory, ¶ms.sender, params.signer), + rekey_to: params + .rekey_to + .as_ref() + .and_then(|s| algokit_transact::Address::from_str(s).ok()), + note: params.note, + lease: params.lease, + static_fee: params.static_fee, + extra_fee: params.extra_fee, + max_fee: params.max_fee, + validity_window: params.validity_window, + first_valid_round: params.first_valid_round, + last_valid_round: params.last_valid_round, + args: params.args, + account_references: None, + app_references: params.app_references, + asset_references: params.asset_references, + box_references: params.box_references, + }) + } + + /// Create DeployAppDeleteParams + pub fn deploy_delete( + &self, + params: Option, + ) -> Result { + let params = params.unwrap_or_default(); + let sender = self + .factory + .get_sender_address(¶ms.sender) + .map_err(|message| AppFactoryError::ValidationError { message })?; + + Ok(DeployAppDeleteParams { + sender, + signer: resolve_signer(self.factory, ¶ms.sender, params.signer), + rekey_to: params + .rekey_to + .as_ref() + .and_then(|s| algokit_transact::Address::from_str(s).ok()), + note: params.note, + lease: params.lease, + static_fee: params.static_fee, + extra_fee: params.extra_fee, + max_fee: params.max_fee, + validity_window: params.validity_window, + first_valid_round: params.first_valid_round, + last_valid_round: params.last_valid_round, + args: params.args, + account_references: None, + app_references: params.app_references, + asset_references: params.asset_references, + box_references: params.box_references, + }) + } +} + +fn decode_teal_from_spec(factory: &AppFactory) -> Result<(String, String), AppFactoryError> { + let source = + factory + .app_spec() + .source + .as_ref() + .ok_or_else(|| AppFactoryError::CompilationError { + message: "Missing source in app spec".to_string(), + })?; + let approval = + source + .get_decoded_approval() + .map_err(|e| AppFactoryError::CompilationError { + message: e.to_string(), + })?; + let clear = source + .get_decoded_clear() + .map_err(|e| AppFactoryError::CompilationError { + message: e.to_string(), + })?; + Ok((approval, clear)) +} + +fn default_global_schema(factory: &AppFactory) -> TxStateSchema { + let s = &factory.app_spec().state.schema.global_state; + TxStateSchema { + num_uints: s.ints, + num_byte_slices: s.bytes, + } +} + +fn default_local_schema(factory: &AppFactory) -> TxStateSchema { + let s = &factory.app_spec().state.schema.local_state; + TxStateSchema { + num_uints: s.ints, + num_byte_slices: s.bytes, + } +} + +pub(crate) fn to_abi_method( + contract: &algokit_abi::Arc56Contract, + method: &str, +) -> Result { + contract + .find_abi_method(method) + .map_err(|e| AppFactoryError::MethodNotFound { + message: e.to_string(), + }) +} + +// Note: Deploy param structs accept Address already parsed where relevant; factory-level +// params use String types mirroring Python/TS. For now we pass through as-is. diff --git a/crates/algokit_utils/src/applications/app_factory/sender.rs b/crates/algokit_utils/src/applications/app_factory/sender.rs new file mode 100644 index 000000000..893d614aa --- /dev/null +++ b/crates/algokit_utils/src/applications/app_factory/sender.rs @@ -0,0 +1,313 @@ +use super::AppFactory; +use super::utils::{ + build_bare_create_params, build_bare_delete_params, build_bare_update_params, + build_create_method_call_params, build_delete_method_call_params, + build_update_method_call_params, merge_args_with_defaults, prepare_compiled_method, + transform_transaction_error_for_factory, +}; +use crate::SendTransactionResult; +use crate::applications::app_client::CompilationParams; +use crate::applications::app_client::{AppClient, AppClientParams}; +use crate::applications::app_factory::params_builder::to_abi_method; +use crate::applications::app_factory::{ + AppFactoryCreateMethodCallParams, AppFactoryCreateMethodCallResult, AppFactoryCreateParams, + AppFactoryDeleteMethodCallParams, AppFactoryDeleteParams, AppFactoryMethodCallResult, + AppFactoryUpdateMethodCallParams, AppFactoryUpdateParams, +}; +use crate::transactions::{ + SendAppCallResult, SendAppCreateResult, SendAppUpdateResult, SendParams, TransactionSenderError, +}; + +pub struct TransactionSender<'app_factory> { + pub(crate) factory: &'app_factory AppFactory, +} + +pub struct BareTransactionSender<'app_factory> { + pub(crate) factory: &'app_factory AppFactory, +} + +impl<'app_factory> TransactionSender<'app_factory> { + pub fn bare(&self) -> BareTransactionSender<'app_factory> { + BareTransactionSender { + factory: self.factory, + } + } + + /// Send an app creation via method call and return (AppClient, SendAppCreateResult) + pub async fn create( + &self, + params: AppFactoryCreateMethodCallParams, + send_params: Option, + compilation_params: Option, + ) -> Result<(AppClient, AppFactoryCreateMethodCallResult), TransactionSenderError> { + // Merge user args with ARC-56 literal defaults + let merged_args = merge_args_with_defaults(self.factory, ¶ms.method, ¶ms.args) + .map_err(|e| TransactionSenderError::ValidationError { + message: e.to_string(), + })?; + + // Prepare compiled programs, method and sender in one step + let (compiled, method, sender) = prepare_compiled_method( + self.factory, + ¶ms.method, + compilation_params, + ¶ms.sender, + ) + .await + .map_err(|e| TransactionSenderError::ValidationError { + message: e.to_string(), + })?; + + // Resolve schema defaults via helper only when needed by builder + + // Avoid moving compiled bytes we still need later + let approval_bytes = compiled.approval.compiled_base64_to_bytes.clone(); + let clear_bytes = compiled.clear.compiled_base64_to_bytes.clone(); + + let create_params = build_create_method_call_params( + self.factory, + sender, + ¶ms, + method, + merged_args, + approval_bytes.clone(), + clear_bytes.clone(), + ); + + let mut result = self + .factory + .algorand() + .send() + .app_create_method_call(create_params, send_params) + .await + .map_err(|e| transform_transaction_error_for_factory(self.factory, e, false))?; + + result.compiled_approval = Some(approval_bytes); + result.compiled_clear = Some(clear_bytes); + result.approval_source_map = compiled.approval.source_map.clone(); + result.clear_source_map = compiled.clear.source_map.clone(); + + let app_client = AppClient::new(AppClientParams { + app_id: result.app_id, + app_spec: self.factory.app_spec().clone(), + algorand: self.factory.algorand().clone(), + app_name: Some(self.factory.app_name().to_string()), + default_sender: self.factory.default_sender.clone(), + default_signer: self.factory.default_signer.clone(), + source_maps: self.factory.current_source_maps(), + transaction_composer_config: self.factory.transaction_composer_config.clone(), + }); + + // Extract ABI return value as ABIValue (if present and decodable) + let arc56_return = self + .factory + .parse_method_return_value(&result.abi_return) + .map_err(|e| TransactionSenderError::ValidationError { + message: e.to_string(), + })?; + + Ok(( + app_client, + AppFactoryMethodCallResult::new(result, arc56_return), + )) + } + + /// Send an app update via method call + pub async fn update( + &self, + params: AppFactoryUpdateMethodCallParams, + send_params: Option, + compilation_params: Option, + ) -> Result { + let (compiled, method, sender) = prepare_compiled_method( + self.factory, + ¶ms.method, + compilation_params, + ¶ms.sender, + ) + .await + .map_err(|e| TransactionSenderError::ValidationError { + message: e.to_string(), + })?; + + let approval_bytes = compiled.approval.compiled_base64_to_bytes.clone(); + let clear_bytes = compiled.clear.compiled_base64_to_bytes.clone(); + + let update_params = build_update_method_call_params( + self.factory, + sender, + ¶ms, + method, + params.args.clone().unwrap_or_default(), + approval_bytes.clone(), + clear_bytes.clone(), + ); + + let mut result = self + .factory + .algorand() + .send() + .app_update_method_call(update_params, send_params) + .await + .map_err(|e| transform_transaction_error_for_factory(self.factory, e, false))?; + + result.compiled_approval = Some(approval_bytes); + result.compiled_clear = Some(clear_bytes); + result.approval_source_map = compiled.approval.source_map.clone(); + result.clear_source_map = compiled.clear.source_map.clone(); + + Ok(result) + } + + /// Send an app delete via method call + pub async fn delete( + &self, + params: AppFactoryDeleteMethodCallParams, + send_params: Option, + ) -> Result { + let method = to_abi_method(self.factory.app_spec(), ¶ms.method).map_err(|e| { + TransactionSenderError::ValidationError { + message: e.to_string(), + } + })?; + + let sender = self + .factory + .get_sender_address(¶ms.sender) + .map_err(|e| TransactionSenderError::ValidationError { message: e })?; + + let delete_params = build_delete_method_call_params( + self.factory, + sender, + ¶ms, + method, + params.args.clone().unwrap_or_default(), + ); + + self.factory + .algorand() + .send() + .app_delete_method_call(delete_params, send_params) + .await + .map_err(|e| transform_transaction_error_for_factory(self.factory, e, true)) + } +} + +impl BareTransactionSender<'_> { + /// Send a bare app creation and return (AppClient, SendAppCreateResult) + pub async fn create( + &self, + params: Option, + send_params: Option, + compilation_params: Option, + ) -> Result<(AppClient, SendAppCreateResult), TransactionSenderError> { + let params = params.unwrap_or_default(); + + // Compile using centralized helper (with override params) + let compiled = self + .factory + .compile_programs_with(compilation_params) + .await + .map_err(|e| TransactionSenderError::ValidationError { + message: e.to_string(), + })?; + + let sender = self + .factory + .get_sender_address(¶ms.sender) + .map_err(|e| TransactionSenderError::ValidationError { message: e })?; + + // Schema defaults handled in builder + + let create_params = build_bare_create_params( + self.factory, + sender, + ¶ms, + compiled.approval.compiled_base64_to_bytes.clone(), + compiled.clear.compiled_base64_to_bytes.clone(), + ); + + let mut result = self + .factory + .algorand() + .send() + .app_create(create_params, send_params) + .await + .map_err(|e| transform_transaction_error_for_factory(self.factory, e, false))?; + + result.compiled_approval = Some(compiled.approval.compiled_base64_to_bytes.clone()); + result.compiled_clear = Some(compiled.clear.compiled_base64_to_bytes.clone()); + result.approval_source_map = compiled.approval.source_map.clone(); + result.clear_source_map = compiled.clear.source_map.clone(); + + let app_client = AppClient::new(AppClientParams { + app_id: result.app_id, + app_spec: self.factory.app_spec().clone(), + algorand: self.factory.algorand().clone(), + app_name: Some(self.factory.app_name().to_string()), + default_sender: self.factory.default_sender.clone(), + default_signer: self.factory.default_signer.clone(), + source_maps: self.factory.current_source_maps(), + transaction_composer_config: self.factory.transaction_composer_config.clone(), + }); + + Ok((app_client, result)) + } + + /// Send an app update (bare) + pub async fn update( + &self, + params: AppFactoryUpdateParams, + send_params: Option, + compilation_params: Option, + ) -> Result { + let compiled = self + .factory + .compile_programs_with(compilation_params) + .await + .map_err(|e| TransactionSenderError::ValidationError { + message: e.to_string(), + })?; + + let sender = self + .factory + .get_sender_address(¶ms.sender) + .map_err(|e| TransactionSenderError::ValidationError { message: e })?; + + let update_params = build_bare_update_params( + self.factory, + sender, + ¶ms, + compiled.approval.compiled_base64_to_bytes, + compiled.clear.compiled_base64_to_bytes, + ); + + self.factory + .algorand() + .send() + .app_update(update_params, send_params) + .await + .map_err(|e| transform_transaction_error_for_factory(self.factory, e, false)) + } + + /// Send an app delete (bare) + pub async fn delete( + &self, + params: AppFactoryDeleteParams, + send_params: Option, + ) -> Result { + let sender = self + .factory + .get_sender_address(¶ms.sender) + .map_err(|e| TransactionSenderError::ValidationError { message: e })?; + + let delete_params = build_bare_delete_params(self.factory, sender, ¶ms); + + self.factory + .algorand() + .send() + .app_delete(delete_params, send_params) + .await + .map_err(|e| transform_transaction_error_for_factory(self.factory, e, true)) + } +} diff --git a/crates/algokit_utils/src/applications/app_factory/transaction_builder.rs b/crates/algokit_utils/src/applications/app_factory/transaction_builder.rs new file mode 100644 index 000000000..e4143e510 --- /dev/null +++ b/crates/algokit_utils/src/applications/app_factory/transaction_builder.rs @@ -0,0 +1,252 @@ +use super::AppFactory; +use super::utils::{ + build_create_method_call_params, build_update_method_call_params, prepare_compiled_method, +}; +use crate::applications::app_client::CompilationParams; +use crate::applications::app_factory::utils::resolve_signer; +use crate::applications::app_factory::{ + AppFactoryCreateMethodCallParams, AppFactoryCreateParams, AppFactoryDeleteParams, + AppFactoryUpdateMethodCallParams, AppFactoryUpdateParams, +}; +use crate::transactions::{ + AppCreateParams, AppDeleteParams, AppUpdateParams, composer::ComposerError, +}; +use algokit_transact::Transaction; + +pub struct TransactionBuilder<'app_factory> { + pub(crate) factory: &'app_factory AppFactory, +} + +pub struct BareTransactionBuilder<'app_factory> { + pub(crate) factory: &'app_factory AppFactory, +} + +impl<'app_factory> TransactionBuilder<'app_factory> { + pub fn bare(&self) -> BareTransactionBuilder<'app_factory> { + BareTransactionBuilder { + factory: self.factory, + } + } + + pub async fn create( + &self, + params: AppFactoryCreateMethodCallParams, + compilation_params: Option, + ) -> Result, ComposerError> { + // Prepare compiled programs, method and sender in one step + let (compiled, method, sender) = prepare_compiled_method( + self.factory, + ¶ms.method, + compilation_params, + ¶ms.sender, + ) + .await + .map_err(|e| ComposerError::TransactionError { + message: e.to_string(), + })?; + + let create_params = build_create_method_call_params( + self.factory, + sender, + ¶ms, + method, + params.args.clone().unwrap_or_default(), + compiled.approval.compiled_base64_to_bytes, + compiled.clear.compiled_base64_to_bytes, + ); + + self.factory + .algorand() + .create() + .app_create_method_call(create_params) + .await + } + + pub async fn update( + &self, + params: AppFactoryUpdateMethodCallParams, + compilation_params: Option, + ) -> Result, ComposerError> { + let (compiled, method, sender) = prepare_compiled_method( + self.factory, + ¶ms.method, + compilation_params, + ¶ms.sender, + ) + .await + .map_err(|e| ComposerError::TransactionError { + message: e.to_string(), + })?; + + let update_params = build_update_method_call_params( + self.factory, + sender, + ¶ms, + method, + params.args.clone().unwrap_or_default(), + compiled.approval.compiled_base64_to_bytes, + compiled.clear.compiled_base64_to_bytes, + ); + + self.factory + .algorand() + .create() + .app_update_method_call(update_params) + .await + } +} + +impl BareTransactionBuilder<'_> { + pub async fn create( + &self, + params: Option, + compilation_params: Option, + ) -> Result { + let params = params.unwrap_or_default(); + + // Compile using centralized helper + let compiled = self + .factory + .compile_programs_with(compilation_params) + .await + .map_err(|e| ComposerError::TransactionError { + message: e.to_string(), + })?; + + let sender = self + .factory + .get_sender_address(¶ms.sender) + .map_err(|e| ComposerError::TransactionError { message: e })?; + + let global_schema = params.global_state_schema.or_else(|| { + let s = &self.factory.app_spec().state.schema.global_state; + Some(algokit_transact::StateSchema { + num_uints: s.ints, + num_byte_slices: s.bytes, + }) + }); + let local_schema = params.local_state_schema.or_else(|| { + let s = &self.factory.app_spec().state.schema.local_state; + Some(algokit_transact::StateSchema { + num_uints: s.ints, + num_byte_slices: s.bytes, + }) + }); + + let create_params = AppCreateParams { + sender, + signer: resolve_signer(self.factory, ¶ms.sender, params.signer), + rekey_to: params.rekey_to, + note: params.note, + lease: params.lease, + static_fee: params.static_fee, + extra_fee: params.extra_fee, + max_fee: params.max_fee, + validity_window: params.validity_window, + first_valid_round: params.first_valid_round, + last_valid_round: params.last_valid_round, + on_complete: params + .on_complete + .unwrap_or(algokit_transact::OnApplicationComplete::NoOp), + approval_program: compiled.approval.compiled_base64_to_bytes, + clear_state_program: compiled.clear.compiled_base64_to_bytes, + args: params.args, + account_references: params.account_references, + app_references: params.app_references, + asset_references: params.asset_references, + box_references: params.box_references, + global_state_schema: global_schema, + local_state_schema: local_schema, + extra_program_pages: params.extra_program_pages, + }; + + self.factory + .algorand() + .create() + .app_create(create_params) + .await + } + + pub async fn update( + &self, + params: AppFactoryUpdateParams, + compilation_params: Option, + ) -> Result { + let compiled = self + .factory + .compile_programs_with(compilation_params) + .await + .map_err(|e| ComposerError::TransactionError { + message: e.to_string(), + })?; + + let sender = self + .factory + .get_sender_address(¶ms.sender) + .map_err(|e| ComposerError::TransactionError { message: e })?; + + let update_params = AppUpdateParams { + sender, + signer: resolve_signer(self.factory, ¶ms.sender, params.signer), + rekey_to: params.rekey_to, + note: params.note, + lease: params.lease, + static_fee: params.static_fee, + extra_fee: params.extra_fee, + max_fee: params.max_fee, + validity_window: params.validity_window, + first_valid_round: params.first_valid_round, + last_valid_round: params.last_valid_round, + app_id: params.app_id, + approval_program: compiled.approval.compiled_base64_to_bytes, + clear_state_program: compiled.clear.compiled_base64_to_bytes, + args: params.args, + account_references: params.account_references, + app_references: params.app_references, + asset_references: params.asset_references, + box_references: params.box_references, + }; + + self.factory + .algorand() + .create() + .app_update(update_params) + .await + } + + pub async fn delete( + &self, + params: AppFactoryDeleteParams, + ) -> Result { + let sender = self + .factory + .get_sender_address(¶ms.sender) + .map_err(|e| ComposerError::TransactionError { message: e })?; + + let delete_params = AppDeleteParams { + sender, + signer: resolve_signer(self.factory, ¶ms.sender, params.signer), + rekey_to: params.rekey_to, + note: params.note, + lease: params.lease, + static_fee: params.static_fee, + extra_fee: params.extra_fee, + max_fee: params.max_fee, + validity_window: params.validity_window, + first_valid_round: params.first_valid_round, + last_valid_round: params.last_valid_round, + app_id: params.app_id, + args: params.args, + account_references: params.account_references, + app_references: params.app_references, + asset_references: params.asset_references, + box_references: params.box_references, + }; + + self.factory + .algorand() + .create() + .app_delete(delete_params) + .await + } +} diff --git a/crates/algokit_utils/src/applications/app_factory/types.rs b/crates/algokit_utils/src/applications/app_factory/types.rs new file mode 100644 index 000000000..d6c893e60 --- /dev/null +++ b/crates/algokit_utils/src/applications/app_factory/types.rs @@ -0,0 +1,220 @@ +use std::collections::HashMap; +use std::ops::{Deref, DerefMut}; +use std::sync::Arc; + +use algokit_abi::{ABIValue, Arc56Contract}; + +use crate::AlgorandClient; +use crate::AppSourceMaps; +use crate::clients::app_manager::TealTemplateValue; +use crate::transactions::{ + AppMethodCallArg, TransactionComposerConfig, TransactionSigner, + sender_results::{SendAppCallResult, SendAppCreateResult, SendAppUpdateResult}, +}; + +#[derive(Clone, Debug)] +pub struct AppFactoryCompilationResult { + pub approval_program: Vec, + pub clear_state_program: Vec, + pub compiled_approval: crate::clients::app_manager::CompiledTeal, + pub compiled_clear: crate::clients::app_manager::CompiledTeal, +} + +#[derive(Clone, Debug)] +pub struct AppFactoryMethodCallResult { + inner: T, + arc56_return: Option, +} + +impl AppFactoryMethodCallResult { + pub fn new(inner: T, arc56_return: Option) -> Self { + Self { + inner, + arc56_return, + } + } + + pub fn arc56_return(&self) -> Option<&ABIValue> { + self.arc56_return.as_ref() + } + + pub fn into_parts(self) -> (T, Option) { + (self.inner, self.arc56_return) + } +} + +impl Deref for AppFactoryMethodCallResult { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl DerefMut for AppFactoryMethodCallResult { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } +} + +pub struct AppFactoryParams { + pub algorand: Arc, + pub app_spec: Arc56Contract, + pub app_name: Option, + pub default_sender: Option, + pub default_signer: Option>, + pub version: Option, + pub deploy_time_params: Option>, + pub updatable: Option, + pub deletable: Option, + pub source_maps: Option, + pub transaction_composer_config: Option, +} + +#[derive(Clone, Default)] +pub struct AppFactoryCreateParams { + pub on_complete: Option, + pub args: Option>>, + pub account_references: Option>, + pub app_references: Option>, + pub asset_references: Option>, + pub box_references: Option>, + pub global_state_schema: Option, + pub local_state_schema: Option, + pub extra_program_pages: Option, + pub sender: Option, + pub signer: Option>, + pub rekey_to: Option, + pub note: Option>, + pub lease: Option<[u8; 32]>, + pub static_fee: Option, + pub extra_fee: Option, + pub max_fee: Option, + pub validity_window: Option, + pub first_valid_round: Option, + pub last_valid_round: Option, +} + +#[derive(Clone, Default)] +pub struct AppFactoryCreateMethodCallParams { + pub method: String, + pub args: Option>, + pub on_complete: Option, + pub account_references: Option>, + pub app_references: Option>, + pub asset_references: Option>, + pub box_references: Option>, + pub global_state_schema: Option, + pub local_state_schema: Option, + pub extra_program_pages: Option, + pub sender: Option, + pub signer: Option>, + pub rekey_to: Option, + pub note: Option>, + pub lease: Option<[u8; 32]>, + pub static_fee: Option, + pub extra_fee: Option, + pub max_fee: Option, + pub validity_window: Option, + pub first_valid_round: Option, + pub last_valid_round: Option, +} + +pub type AppFactoryCreateMethodCallResult = AppFactoryMethodCallResult; +pub type AppFactoryUpdateMethodCallResult = AppFactoryMethodCallResult; +pub type AppFactoryDeleteMethodCallResult = AppFactoryMethodCallResult; + +#[derive(Clone, Default)] +pub struct AppFactoryUpdateMethodCallParams { + pub app_id: u64, + pub method: String, + pub args: Option>, // raw args accepted; processing later + pub sender: Option, + pub account_references: Option>, + pub app_references: Option>, + pub asset_references: Option>, + pub box_references: Option>, + pub signer: Option>, + pub rekey_to: Option, + pub note: Option>, + pub lease: Option<[u8; 32]>, + pub static_fee: Option, + pub extra_fee: Option, + pub max_fee: Option, + pub validity_window: Option, + pub first_valid_round: Option, + pub last_valid_round: Option, +} + +#[derive(Clone, Default)] +pub struct AppFactoryUpdateParams { + pub app_id: u64, + pub args: Option>>, + pub sender: Option, + pub account_references: Option>, + pub app_references: Option>, + pub asset_references: Option>, + pub box_references: Option>, + pub signer: Option>, + pub rekey_to: Option, + pub note: Option>, + pub lease: Option<[u8; 32]>, + pub static_fee: Option, + pub extra_fee: Option, + pub max_fee: Option, + pub validity_window: Option, + pub first_valid_round: Option, + pub last_valid_round: Option, +} + +#[derive(Clone, Default)] +pub struct AppFactoryDeleteMethodCallParams { + pub app_id: u64, + pub method: String, + pub args: Option>, + pub sender: Option, + pub account_references: Option>, + pub app_references: Option>, + pub asset_references: Option>, + pub box_references: Option>, + pub signer: Option>, + pub rekey_to: Option, + pub note: Option>, + pub lease: Option<[u8; 32]>, + pub static_fee: Option, + pub extra_fee: Option, + pub max_fee: Option, + pub validity_window: Option, + pub first_valid_round: Option, + pub last_valid_round: Option, +} + +#[derive(Clone, Default)] +pub struct AppFactoryDeleteParams { + pub app_id: u64, + pub args: Option>>, + pub sender: Option, + pub account_references: Option>, + pub app_references: Option>, + pub asset_references: Option>, + pub box_references: Option>, + pub signer: Option>, + pub rekey_to: Option, + pub note: Option>, + pub lease: Option<[u8; 32]>, + pub static_fee: Option, + pub extra_fee: Option, + pub max_fee: Option, + pub validity_window: Option, + pub first_valid_round: Option, + pub last_valid_round: Option, +} + +#[derive(Debug)] +pub struct AppFactoryDeployResult { + pub app: crate::applications::app_deployer::AppMetadata, + pub operation_performed: crate::applications::app_deployer::AppDeployResult, + pub create_result: Option, + pub update_result: Option, + pub delete_result: Option, +} diff --git a/crates/algokit_utils/src/applications/app_factory/utils.rs b/crates/algokit_utils/src/applications/app_factory/utils.rs new file mode 100644 index 000000000..b384e68bd --- /dev/null +++ b/crates/algokit_utils/src/applications/app_factory/utils.rs @@ -0,0 +1,361 @@ +use super::{AppFactory, AppFactoryError}; +use crate::applications::app_client::CompilationParams; +use crate::applications::app_factory::params_builder::to_abi_method; +use crate::applications::app_factory::{ + AppFactoryCreateMethodCallParams, AppFactoryCreateParams, AppFactoryDeleteMethodCallParams, + AppFactoryDeleteParams, AppFactoryUpdateMethodCallParams, AppFactoryUpdateParams, +}; +use crate::clients::app_manager::CompiledPrograms; +use crate::transactions::{ + AppCreateMethodCallParams, AppCreateParams, AppDeleteMethodCallParams, AppDeleteParams, + AppMethodCallArg, AppUpdateMethodCallParams, AppUpdateParams, TransactionSenderError, + TransactionSigner, +}; +use algokit_abi::ABIMethod; +use algokit_abi::abi_type::ABIType; +use algokit_abi::arc56_contract::DefaultValueSource; +use algokit_transact::{Address, OnApplicationComplete, StateSchema}; +use base64::Engine; +use base64::engine::general_purpose::STANDARD as Base64; +use std::str::FromStr; +use std::sync::Arc; + +/// Merge user-provided ABI method arguments with ARC-56 literal defaults. +/// Only 'literal' default values are supported; others will be ignored and treated as missing. +pub(crate) fn merge_args_with_defaults( + factory: &AppFactory, + method_name_or_signature: &str, + user_args: &Option>, +) -> Result, AppFactoryError> { + let contract = factory.app_spec(); + let method = contract.get_method(method_name_or_signature).map_err(|e| { + AppFactoryError::ValidationError { + message: e.to_string(), + } + })?; + + let mut result: Vec = Vec::with_capacity(method.args.len()); + let provided = user_args.as_ref().map(|v| v.as_slice()).unwrap_or(&[]); + + for (i, arg_def) in method.args.iter().enumerate() { + if i < provided.len() { + // Use provided argument as-is + result.push(provided[i].clone()); + continue; + } + + // Otherwise try literal default + if let Some(default) = &arg_def.default_value { + if matches!(default.source, DefaultValueSource::Literal) { + // Determine ABI type to decode to: prefer the argument type + let abi_type = ABIType::from_str(&arg_def.arg_type).map_err(|e| { + AppFactoryError::ValidationError { + message: e.to_string(), + } + })?; + + let bytes = + Base64 + .decode(&default.data) + .map_err(|e| AppFactoryError::ValidationError { + message: format!("Failed to base64-decode default literal: {}", e), + })?; + + let abi_value = + abi_type + .decode(&bytes) + .map_err(|e| AppFactoryError::ValidationError { + message: e.to_string(), + })?; + + result.push(AppMethodCallArg::ABIValue(abi_value)); + continue; + } + } + + // No provided arg and no supported default -> error like Python implementation + let name = arg_def + .name + .as_ref() + .cloned() + .unwrap_or_else(|| format!("arg{}", i + 1)); + let method_name = &method.name; + return Err(AppFactoryError::ValidationError { + message: format!( + "No value provided for required argument {} in call to method {}", + name, method_name + ), + }); + } + + Ok(result) +} + +/// Transform a transaction error using AppClient logic error exposure for factory flows. +pub(crate) fn transform_transaction_error_for_factory( + factory: &AppFactory, + err: TransactionSenderError, + is_clear: bool, +) -> TransactionSenderError { + let err_str = err.to_string(); + if let Some(logic_message) = factory.logic_error_for(&err_str, is_clear) { + TransactionSenderError::ValidationError { + message: logic_message, + } + } else { + err + } +} + +/// Resolve signer: prefer explicit signer; otherwise use factory default signer when +/// sender is unspecified or equals the factory default sender. +pub(crate) fn resolve_signer( + factory: &AppFactory, + sender: &Option, + signer: Option>, +) -> Option> { + signer.or_else( + || match (sender.as_deref(), factory.default_sender.as_deref()) { + (None, _) => factory.default_signer.clone(), + (Some(s), Some(d)) if s == d => factory.default_signer.clone(), + _ => None, + }, + ) +} + +/// Compile programs, resolve ABI method and sender in one step. +pub(crate) async fn prepare_compiled_method( + factory: &AppFactory, + method_sig: &str, + compilation_params: Option, + sender_opt: &Option, +) -> Result<(CompiledPrograms, ABIMethod, algokit_transact::Address), AppFactoryError> { + let compiled = factory.compile_programs_with(compilation_params).await?; + let method = to_abi_method(factory.app_spec(), method_sig)?; + let sender = factory + .get_sender_address(sender_opt) + .map_err(|message| AppFactoryError::ValidationError { message })?; + Ok((compiled, method, sender)) +} + +/// Returns the provided schemas or falls back to those declared in the contract spec. +pub(crate) fn default_schemas( + factory: &AppFactory, + global: Option, + local: Option, +) -> (Option, Option) { + let g = global.or_else(|| { + let s = &factory.app_spec().state.schema.global_state; + Some(StateSchema { + num_uints: s.ints, + num_byte_slices: s.bytes, + }) + }); + let l = local.or_else(|| { + let s = &factory.app_spec().state.schema.local_state; + Some(StateSchema { + num_uints: s.ints, + num_byte_slices: s.bytes, + }) + }); + (g, l) +} + +pub(crate) fn build_create_method_call_params( + factory: &AppFactory, + sender: Address, + base: &AppFactoryCreateMethodCallParams, + method: ABIMethod, + args: Vec, + approval_program: Vec, + clear_state_program: Vec, +) -> AppCreateMethodCallParams { + let (global_state_schema, local_state_schema) = default_schemas( + factory, + base.global_state_schema.clone(), + base.local_state_schema.clone(), + ); + + AppCreateMethodCallParams { + sender, + signer: resolve_signer(factory, &base.sender, base.signer.clone()), + rekey_to: base.rekey_to.clone(), + note: base.note.clone(), + lease: base.lease, + static_fee: base.static_fee, + extra_fee: base.extra_fee, + max_fee: base.max_fee, + validity_window: base.validity_window, + first_valid_round: base.first_valid_round, + last_valid_round: base.last_valid_round, + on_complete: base.on_complete.unwrap_or(OnApplicationComplete::NoOp), + approval_program, + clear_state_program, + method, + args, + account_references: base.account_references.clone(), + app_references: base.app_references.clone(), + asset_references: base.asset_references.clone(), + box_references: base.box_references.clone(), + global_state_schema, + local_state_schema, + extra_program_pages: base.extra_program_pages, + } +} + +pub(crate) fn build_update_method_call_params( + factory: &AppFactory, + sender: Address, + base: &AppFactoryUpdateMethodCallParams, + method: ABIMethod, + args: Vec, + approval_program: Vec, + clear_state_program: Vec, +) -> AppUpdateMethodCallParams { + AppUpdateMethodCallParams { + sender, + signer: resolve_signer(factory, &base.sender, base.signer.clone()), + rekey_to: base.rekey_to.clone(), + note: base.note.clone(), + lease: base.lease, + static_fee: base.static_fee, + extra_fee: base.extra_fee, + max_fee: base.max_fee, + validity_window: base.validity_window, + first_valid_round: base.first_valid_round, + last_valid_round: base.last_valid_round, + app_id: base.app_id, + approval_program, + clear_state_program, + method, + args, + account_references: base.account_references.clone(), + app_references: base.app_references.clone(), + asset_references: base.asset_references.clone(), + box_references: base.box_references.clone(), + } +} + +pub(crate) fn build_delete_method_call_params( + factory: &AppFactory, + sender: Address, + base: &AppFactoryDeleteMethodCallParams, + method: ABIMethod, + args: Vec, +) -> AppDeleteMethodCallParams { + AppDeleteMethodCallParams { + sender, + signer: resolve_signer(factory, &base.sender, base.signer.clone()), + rekey_to: base.rekey_to.clone(), + note: base.note.clone(), + lease: base.lease, + static_fee: base.static_fee, + extra_fee: base.extra_fee, + max_fee: base.max_fee, + validity_window: base.validity_window, + first_valid_round: base.first_valid_round, + last_valid_round: base.last_valid_round, + app_id: base.app_id, + method, + args, + account_references: base.account_references.clone(), + app_references: base.app_references.clone(), + asset_references: base.asset_references.clone(), + box_references: base.box_references.clone(), + } +} + +pub(crate) fn build_bare_create_params( + factory: &AppFactory, + sender: Address, + base: &AppFactoryCreateParams, + approval_program: Vec, + clear_state_program: Vec, +) -> AppCreateParams { + let (global_state_schema, local_state_schema) = default_schemas( + factory, + base.global_state_schema.clone(), + base.local_state_schema.clone(), + ); + + AppCreateParams { + sender, + signer: resolve_signer(factory, &base.sender, base.signer.clone()), + rekey_to: base.rekey_to.clone(), + note: base.note.clone(), + lease: base.lease, + static_fee: base.static_fee, + extra_fee: base.extra_fee, + max_fee: base.max_fee, + validity_window: base.validity_window, + first_valid_round: base.first_valid_round, + last_valid_round: base.last_valid_round, + on_complete: base.on_complete.unwrap_or(OnApplicationComplete::NoOp), + approval_program, + clear_state_program, + args: base.args.clone(), + account_references: base.account_references.clone(), + app_references: base.app_references.clone(), + asset_references: base.asset_references.clone(), + box_references: base.box_references.clone(), + global_state_schema, + local_state_schema, + extra_program_pages: base.extra_program_pages, + } +} + +pub(crate) fn build_bare_update_params( + factory: &AppFactory, + sender: Address, + base: &AppFactoryUpdateParams, + approval_program: Vec, + clear_state_program: Vec, +) -> AppUpdateParams { + AppUpdateParams { + sender, + signer: resolve_signer(factory, &base.sender, base.signer.clone()), + rekey_to: base.rekey_to.clone(), + note: base.note.clone(), + lease: base.lease, + static_fee: base.static_fee, + extra_fee: base.extra_fee, + max_fee: base.max_fee, + validity_window: base.validity_window, + first_valid_round: base.first_valid_round, + last_valid_round: base.last_valid_round, + app_id: base.app_id, + approval_program, + clear_state_program, + args: base.args.clone(), + account_references: base.account_references.clone(), + app_references: base.app_references.clone(), + asset_references: base.asset_references.clone(), + box_references: base.box_references.clone(), + } +} + +pub(crate) fn build_bare_delete_params( + factory: &AppFactory, + sender: Address, + base: &AppFactoryDeleteParams, +) -> AppDeleteParams { + AppDeleteParams { + sender, + signer: resolve_signer(factory, &base.sender, base.signer.clone()), + rekey_to: base.rekey_to.clone(), + note: base.note.clone(), + lease: base.lease, + static_fee: base.static_fee, + extra_fee: base.extra_fee, + max_fee: base.max_fee, + validity_window: base.validity_window, + first_valid_round: base.first_valid_round, + last_valid_round: base.last_valid_round, + app_id: base.app_id, + args: base.args.clone(), + account_references: base.account_references.clone(), + app_references: base.app_references.clone(), + asset_references: base.asset_references.clone(), + box_references: base.box_references.clone(), + } +} diff --git a/crates/algokit_utils/src/applications/mod.rs b/crates/algokit_utils/src/applications/mod.rs index 928796a90..fd8cd5d0d 100644 --- a/crates/algokit_utils/src/applications/mod.rs +++ b/crates/algokit_utils/src/applications/mod.rs @@ -1,5 +1,6 @@ pub mod app_client; pub mod app_deployer; +pub mod app_factory; // Re-export commonly used client types pub use app_deployer::{ diff --git a/crates/algokit_utils/src/clients/algorand_client.rs b/crates/algokit_utils/src/clients/algorand_client.rs index 47f66a6c9..167590b67 100644 --- a/crates/algokit_utils/src/clients/algorand_client.rs +++ b/crates/algokit_utils/src/clients/algorand_client.rs @@ -1,3 +1,4 @@ +use crate::applications::AppDeployer; use crate::clients::app_manager::AppManager; use crate::clients::asset_manager::AssetManager; use crate::clients::client_manager::ClientManager; @@ -14,6 +15,7 @@ pub struct AlgorandClient { client_manager: ClientManager, asset_manager: AssetManager, app_manager: AppManager, + app_deployer: AppDeployer, transaction_sender: TransactionSender, transaction_creator: TransactionCreator, account_manager: Arc>, @@ -56,15 +58,21 @@ impl AlgorandClient { asset_manager.clone(), app_manager.clone(), ); - // Create closure for TransactionCreator let transaction_creator = TransactionCreator::new(new_group.clone()); + let app_deployer = AppDeployer::new( + app_manager.clone(), + transaction_sender.clone(), + Some(client_manager.indexer().unwrap()), + ); + Self { client_manager, account_manager: account_manager.clone(), asset_manager, app_manager, + app_deployer, transaction_sender, transaction_creator, default_composer_config: params.composer_config.clone(), @@ -166,4 +174,9 @@ impl AlgorandClient { .unwrap() .set_signer(sender, signer); } + + /// Get a clone of the persistent AppDeployer (shares cache across clones) + pub fn app_deployer(&self) -> AppDeployer { + self.app_deployer.clone() + } } diff --git a/crates/algokit_utils/src/clients/app_manager.rs b/crates/algokit_utils/src/clients/app_manager.rs index 41a23d176..f1651ccbd 100644 --- a/crates/algokit_utils/src/clients/app_manager.rs +++ b/crates/algokit_utils/src/clients/app_manager.rs @@ -1,3 +1,4 @@ +use crate::clients::network_client::genesis_id_is_localnet; use algod_client::{ apis::{AlgodClient, Error as AlgodError}, models::TealKeyValue, @@ -34,6 +35,12 @@ pub struct CompiledTeal { pub source_map: Option, // TODO: review this, relying on serde doesn't seem right } +#[derive(Debug, Clone)] +pub struct CompiledPrograms { + pub approval: CompiledTeal, + pub clear: CompiledTeal, +} + #[derive(Debug, Clone)] pub enum AppState { Uint(UintAppState), @@ -387,6 +394,16 @@ impl AppManager { Ok(values) } + /// Determine if the connected network is a localnet by inspecting genesis ID + pub async fn is_localnet(&self) -> Result { + let params = self + .algod_client + .transaction_params() + .await + .map_err(|e| AppManagerError::AlgodClientError { source: e })?; + Ok(genesis_id_is_localnet(¶ms.genesis_id)) + } + /// Get ABI return value from transaction confirmation. pub fn get_abi_return(confirmation_data: &[u8], method: &ABIMethod) -> Option { if let Some(return_type) = &method.returns { diff --git a/crates/algokit_utils/src/clients/client_manager.rs b/crates/algokit_utils/src/clients/client_manager.rs index 9cd3f7532..2bf0ae2a4 100644 --- a/crates/algokit_utils/src/clients/client_manager.rs +++ b/crates/algokit_utils/src/clients/client_manager.rs @@ -1,8 +1,12 @@ +use crate::AlgorandClient; +use crate::applications::app_client::{AppClient, AppClientError, AppClientParams, AppSourceMaps}; use crate::clients::network_client::{ AlgoClientConfig, AlgoConfig, AlgorandService, NetworkDetails, TokenHeader, genesis_id_is_localnet, }; +use crate::transactions::{TransactionComposerConfig, TransactionSigner}; use algod_client::{AlgodClient, apis::Error as AlgodError}; +use algokit_abi::Arc56Contract; use algokit_http_client::DefaultHttpClient; use base64::{Engine, engine::general_purpose}; use indexer_client::IndexerClient; @@ -37,6 +41,7 @@ pub struct ClientManager { cached_network_details: RwLock>>, } +// TODO: method to get the app client and app factory impl ClientManager { pub fn new(config: &AlgoConfig) -> Result { Ok(Self { @@ -288,6 +293,83 @@ impl ClientManager { let config = Self::get_indexer_config_from_environment()?; Self::get_indexer_client(&config) } + + /// Returns an AppClient resolved by creator address and name using indexer lookup. + #[allow(clippy::too_many_arguments)] + pub async fn get_app_client_by_creator_and_name( + &self, + algorand: Arc, + creator_address: &str, + app_name: &str, + app_spec: Arc56Contract, + default_sender: Option, + default_signer: Option>, + source_maps: Option, + ignore_cache: Option, + transaction_composer_config: Option, + ) -> Result { + AppClient::from_creator_and_name( + creator_address, + app_name, + app_spec, + algorand, + default_sender, + default_signer, + source_maps, + ignore_cache, + transaction_composer_config, + ) + .await + } + + /// Returns an AppClient for an existing application by ID. + #[allow(clippy::too_many_arguments)] + pub fn get_app_client_by_id( + &self, + algorand: Arc, + app_spec: Arc56Contract, + app_id: u64, + app_name: Option, + default_sender: Option, + default_signer: Option>, + source_maps: Option, + transaction_composer_config: Option, + ) -> AppClient { + AppClient::new(AppClientParams { + app_id, + app_spec, + algorand, + app_name, + default_sender, + default_signer, + source_maps, + transaction_composer_config, + }) + } + + /// Returns an AppClient resolved by network using app spec networks mapping. + #[allow(clippy::too_many_arguments)] + pub async fn get_app_client_by_network( + &self, + algorand: Arc, + app_spec: Arc56Contract, + app_name: Option, + default_sender: Option, + default_signer: Option>, + source_maps: Option, + transaction_composer_config: Option, + ) -> Result { + AppClient::from_network( + app_spec, + algorand, + app_name, + default_sender, + default_signer, + source_maps, + transaction_composer_config, + ) + .await + } } #[cfg(test)] diff --git a/crates/algokit_utils/src/transactions/app_call.rs b/crates/algokit_utils/src/transactions/app_call.rs index 68986f81b..38b4efc99 100644 --- a/crates/algokit_utils/src/transactions/app_call.rs +++ b/crates/algokit_utils/src/transactions/app_call.rs @@ -15,7 +15,7 @@ use algokit_transact::{ }; use derive_more::Debug; use num_bigint::BigUint; -use std::str::FromStr; +use std::{str::FromStr, sync::Arc}; #[derive(Debug, Clone)] pub enum AppMethodCallArg { @@ -217,7 +217,7 @@ where /// A signer used to sign transaction(s); if not specified then /// an attempt will be made to find a registered signer for the /// given `sender` or use a default signer (if configured). - pub signer: Option>, + pub signer: Option>, /// The address of the account sending the transaction. pub sender: algokit_transact::Address, /// Change the signing key of the sender to the given address. @@ -308,7 +308,7 @@ where /// A signer used to sign transaction(s); if not specified then /// an attempt will be made to find a registered signer for the /// given `sender` or use a default signer (if configured). - pub signer: Option>, + pub signer: Option>, /// The address of the account sending the transaction. pub sender: algokit_transact::Address, /// Change the signing key of the sender to the given address. @@ -393,7 +393,7 @@ where /// A signer used to sign transaction(s); if not specified then /// an attempt will be made to find a registered signer for the /// given `sender` or use a default signer (if configured). - pub signer: Option>, + pub signer: Option>, /// The address of the account sending the transaction. pub sender: algokit_transact::Address, /// Change the signing key of the sender to the given address. @@ -462,7 +462,7 @@ where /// A signer used to sign transaction(s); if not specified then /// an attempt will be made to find a registered signer for the /// given `sender` or use a default signer (if configured). - pub signer: Option>, + pub signer: Option>, /// The address of the account sending the transaction. pub sender: algokit_transact::Address, /// Change the signing key of the sender to the given address. @@ -1163,6 +1163,14 @@ pub fn build_app_update_method_call( header.clone(), params, |header, account_refs, app_refs, asset_refs, encoded_args| { + // Calculate extra program pages if not explicitly provided + let total_len = params.approval_program.len() + params.clear_state_program.len(); + let extra_pages = if total_len > 2048 { + // ceil(total_len / 2048) - 1 + Some(((total_len as u32 + 2047) / 2048) - 1) + } else { + Some(0) + }; Transaction::AppCall(algokit_transact::AppCallTransactionFields { header, app_id: params.app_id, @@ -1171,7 +1179,7 @@ pub fn build_app_update_method_call( clear_state_program: Some(params.clear_state_program.clone()), global_state_schema: None, local_state_schema: None, - extra_program_pages: None, + extra_program_pages: extra_pages, args: Some(encoded_args), account_references: Some(account_refs), app_references: Some(app_refs), diff --git a/crates/algokit_utils/src/transactions/composer.rs b/crates/algokit_utils/src/transactions/composer.rs index 03adbb85d..7e1d7dc96 100644 --- a/crates/algokit_utils/src/transactions/composer.rs +++ b/crates/algokit_utils/src/transactions/composer.rs @@ -1,4 +1,4 @@ -use crate::config::Config; +use crate::config::{Config, EventData, EventType, TxnGroupSimulatedEventData}; use crate::{ genesis_id_is_localnet, transactions::{ @@ -102,6 +102,75 @@ impl Default for ResourcePopulation { } } +trait HasTxnSigner { + fn signer_mut(&mut self) -> &mut Option>; +} + +fn set_default_signer_if_missing( + params: &mut impl HasTxnSigner, + method_signer: &Option>, +) { + if params.signer_mut().is_none() { + *params.signer_mut() = method_signer.clone(); + } +} + +impl HasTxnSigner for PaymentParams { + fn signer_mut(&mut self) -> &mut Option> { + &mut self.signer + } +} +impl HasTxnSigner for AccountCloseParams { + fn signer_mut(&mut self) -> &mut Option> { + &mut self.signer + } +} +impl HasTxnSigner for AssetTransferParams { + fn signer_mut(&mut self) -> &mut Option> { + &mut self.signer + } +} +impl HasTxnSigner for AssetOptInParams { + fn signer_mut(&mut self) -> &mut Option> { + &mut self.signer + } +} +impl HasTxnSigner for AssetOptOutParams { + fn signer_mut(&mut self) -> &mut Option> { + &mut self.signer + } +} +impl HasTxnSigner for AssetClawbackParams { + fn signer_mut(&mut self) -> &mut Option> { + &mut self.signer + } +} +impl HasTxnSigner for AssetCreateParams { + fn signer_mut(&mut self) -> &mut Option> { + &mut self.signer + } +} +impl HasTxnSigner for AssetConfigParams { + fn signer_mut(&mut self) -> &mut Option> { + &mut self.signer + } +} +impl HasTxnSigner for AssetDestroyParams { + fn signer_mut(&mut self) -> &mut Option> { + &mut self.signer + } +} +impl HasTxnSigner for AssetFreezeParams { + fn signer_mut(&mut self) -> &mut Option> { + &mut self.signer + } +} +impl HasTxnSigner for AssetUnfreezeParams { + fn signer_mut(&mut self) -> &mut Option> { + &mut self.signer + } +} + /// Types of resources that can be populated at the group level #[derive(Debug, Clone)] enum GroupResourceToPopulate { @@ -350,8 +419,8 @@ impl ComposerTransaction { ); get_composer_transaction_field!( signer, - Option>, - |x: &Option>| x.clone(), + Option>, + |x: &Option>| x.clone(), None ); get_composer_transaction_field!( @@ -531,14 +600,24 @@ impl Composer { fn extract_composer_transactions_from_app_method_call_params( method_call_args: &[AppMethodCallArg], + method_signer: Option>, ) -> Vec { let mut composer_transactions: Vec = vec![]; for arg in method_call_args.iter() { match arg { AppMethodCallArg::Transaction(transaction) => { - composer_transactions - .push(ComposerTransaction::Transaction(transaction.clone())); + if let Some(ref signer) = method_signer { + composer_transactions.push(ComposerTransaction::TransactionWithSigner( + TransactionWithSigner { + transaction: transaction.clone(), + signer: signer.clone(), + }, + )); + } else { + composer_transactions + .push(ComposerTransaction::Transaction(transaction.clone())); + } } AppMethodCallArg::TransactionWithSigner(transaction) => { composer_transactions.push(ComposerTransaction::TransactionWithSigner( @@ -546,37 +625,59 @@ impl Composer { )); } AppMethodCallArg::Payment(params) => { - composer_transactions.push(ComposerTransaction::Payment(params.clone())); + let mut p = params.clone(); + set_default_signer_if_missing(&mut p, &method_signer); + composer_transactions.push(ComposerTransaction::Payment(p)); } AppMethodCallArg::AccountClose(params) => { - composer_transactions.push(ComposerTransaction::AccountClose(params.clone())); + let mut p = params.clone(); + set_default_signer_if_missing(&mut p, &method_signer); + composer_transactions.push(ComposerTransaction::AccountClose(p)); } AppMethodCallArg::AssetTransfer(params) => { - composer_transactions.push(ComposerTransaction::AssetTransfer(params.clone())); + let mut p = params.clone(); + set_default_signer_if_missing(&mut p, &method_signer); + composer_transactions.push(ComposerTransaction::AssetTransfer(p)); } AppMethodCallArg::AssetOptIn(params) => { - composer_transactions.push(ComposerTransaction::AssetOptIn(params.clone())); + let mut p = params.clone(); + set_default_signer_if_missing(&mut p, &method_signer); + composer_transactions.push(ComposerTransaction::AssetOptIn(p)); } AppMethodCallArg::AssetOptOut(params) => { - composer_transactions.push(ComposerTransaction::AssetOptOut(params.clone())); + let mut p = params.clone(); + set_default_signer_if_missing(&mut p, &method_signer); + composer_transactions.push(ComposerTransaction::AssetOptOut(p)); } AppMethodCallArg::AssetClawback(params) => { - composer_transactions.push(ComposerTransaction::AssetClawback(params.clone())); + let mut p = params.clone(); + set_default_signer_if_missing(&mut p, &method_signer); + composer_transactions.push(ComposerTransaction::AssetClawback(p)); } AppMethodCallArg::AssetCreate(params) => { - composer_transactions.push(ComposerTransaction::AssetCreate(params.clone())); + let mut p = params.clone(); + set_default_signer_if_missing(&mut p, &method_signer); + composer_transactions.push(ComposerTransaction::AssetCreate(p)); } AppMethodCallArg::AssetConfig(params) => { - composer_transactions.push(ComposerTransaction::AssetConfig(params.clone())); + let mut p = params.clone(); + set_default_signer_if_missing(&mut p, &method_signer); + composer_transactions.push(ComposerTransaction::AssetConfig(p)); } AppMethodCallArg::AssetDestroy(params) => { - composer_transactions.push(ComposerTransaction::AssetDestroy(params.clone())); + let mut p = params.clone(); + set_default_signer_if_missing(&mut p, &method_signer); + composer_transactions.push(ComposerTransaction::AssetDestroy(p)); } AppMethodCallArg::AssetFreeze(params) => { - composer_transactions.push(ComposerTransaction::AssetFreeze(params.clone())); + let mut p = params.clone(); + set_default_signer_if_missing(&mut p, &method_signer); + composer_transactions.push(ComposerTransaction::AssetFreeze(p)); } AppMethodCallArg::AssetUnfreeze(params) => { - composer_transactions.push(ComposerTransaction::AssetUnfreeze(params.clone())); + let mut p = params.clone(); + set_default_signer_if_missing(&mut p, &method_signer); + composer_transactions.push(ComposerTransaction::AssetUnfreeze(p)); } AppMethodCallArg::AppCall(params) => { composer_transactions.push(ComposerTransaction::AppCall(params.clone())); @@ -594,6 +695,7 @@ impl Composer { let nested_composer_transactions = Self::extract_composer_transactions_from_app_method_call_params( ¶ms.args, + params.signer.clone(), ); composer_transactions.extend(nested_composer_transactions); @@ -604,6 +706,7 @@ impl Composer { let nested_composer_transactions = Self::extract_composer_transactions_from_app_method_call_params( ¶ms.args, + params.signer.clone(), ); composer_transactions.extend(nested_composer_transactions); @@ -614,6 +717,7 @@ impl Composer { let nested_composer_transactions = Self::extract_composer_transactions_from_app_method_call_params( ¶ms.args, + params.signer.clone(), ); composer_transactions.extend(nested_composer_transactions); @@ -624,6 +728,7 @@ impl Composer { let nested_composer_transactions = Self::extract_composer_transactions_from_app_method_call_params( ¶ms.args, + params.signer.clone(), ); composer_transactions.extend(nested_composer_transactions); @@ -641,10 +746,11 @@ impl Composer { &mut self, args: &[AppMethodCallArg], transaction: ComposerTransaction, + method_signer: Option>, ) -> Result<(), ComposerError> { let starting_index = self.transactions.len(); let mut composer_transactions = - Self::extract_composer_transactions_from_app_method_call_params(args); + Self::extract_composer_transactions_from_app_method_call_params(args, method_signer); composer_transactions.push(transaction); if self.transactions.len() + composer_transactions.len() > MAX_TX_GROUP_SIZE { @@ -723,6 +829,7 @@ impl Composer { self.add_app_method_call_internal( ¶ms.args, ComposerTransaction::AppCallMethodCall((¶ms).into()), + params.signer.clone(), ) } @@ -733,6 +840,7 @@ impl Composer { self.add_app_method_call_internal( ¶ms.args, ComposerTransaction::AppCreateMethodCall((¶ms).into()), + params.signer.clone(), ) } @@ -743,6 +851,7 @@ impl Composer { self.add_app_method_call_internal( ¶ms.args, ComposerTransaction::AppUpdateMethodCall((¶ms).into()), + params.signer.clone(), ) } @@ -753,6 +862,7 @@ impl Composer { self.add_app_method_call_internal( ¶ms.args, ComposerTransaction::AppDeleteMethodCall((¶ms).into()), + params.signer.clone(), ) } @@ -2113,7 +2223,7 @@ impl Composer { ) -> Result { self.gather_signatures().await?; - let (group, signed_transactions) = { + let (group, encoded_bytes, transaction_ids, last_valid_max) = { let stxns = self .signed_group .as_ref() @@ -2121,7 +2231,35 @@ impl Composer { .ok_or(ComposerError::StateError { message: "No transactions available".to_string(), })?; - (stxns[0].transaction.header().group, stxns) + + let group = stxns[0].transaction.header().group; + + // Encode each signed transaction and concatenate them + let mut encoded_bytes = Vec::new(); + for signed_txn in stxns { + let encoded_txn = + signed_txn + .encode() + .map_err(|e| ComposerError::TransactionError { + message: format!("Failed to encode signed transaction: {}", e), + })?; + encoded_bytes.extend_from_slice(&encoded_txn); + } + + let transaction_ids: Vec = stxns + .iter() + .map(|txn| txn.id()) + .collect::, _>>()?; + + let last_valid_max = stxns + .iter() + .map(|signed_transaction| signed_transaction.transaction.header().last_valid) + .max() + .ok_or(ComposerError::StateError { + message: "Failed to calculate last valid round".to_string(), + })?; + + (group, encoded_bytes, transaction_ids, last_valid_max) }; let wait_rounds = if let Some(max_rounds_to_wait_for_confirmation) = @@ -2131,30 +2269,42 @@ impl Composer { } else { let suggested_params = self.get_suggested_params().await?; let first_round: u64 = suggested_params.last_round; // The last round seen, so is the first round valid - let last_round: u64 = signed_transactions - .iter() - .map(|signed_transaction| signed_transaction.transaction.header().last_valid) - .max() - .ok_or(ComposerError::StateError { - message: "Failed to calculate last valid round".to_string(), - })?; - ((last_round - first_round) + 1).try_into().map_err(|e| { - ComposerError::TransactionError { + ((last_valid_max - first_round) + 1) + .try_into() + .map_err(|e| ComposerError::TransactionError { message: format!("Failed to calculate rounds to wait: {}", e), - } - })? + })? }; - // Encode each signed transaction and concatenate them - let mut encoded_bytes = Vec::new(); + // If debugging with full tracing enabled, emit a simulate event before submission for AVM debugging + if Config::debug() && Config::trace_all() { + let simulate_params = SimulateParams { + allow_more_logging: Some(true), + allow_empty_signatures: Some(true), + allow_unnamed_resources: Some(true), + extra_opcode_budget: None, + exec_trace_config: Some(algod_client::models::SimulateTraceConfig { + enable: Some(true), + stack_change: Some(true), + scratch_change: Some(true), + state_change: Some(true), + }), + simulation_round: None, + skip_signatures: true, + }; - for signed_txn in signed_transactions { - let encoded_txn = signed_txn - .encode() - .map_err(|e| ComposerError::TransactionError { - message: format!("Failed to encode signed transaction: {}", e), - })?; - encoded_bytes.extend_from_slice(&encoded_txn); + if let Ok(simulated) = self.simulate(Some(simulate_params)).await { + let payload = serde_json::to_value(&simulated.simulate_response) + .unwrap_or_else(|_| serde_json::json!({})); + Config::events() + .emit( + EventType::TxnGroupSimulated, + EventData::TxnGroupSimulated(TxnGroupSimulatedEventData { + simulate_response: payload, + }), + ) + .await; + } } let _ = self @@ -2165,11 +2315,6 @@ impl Composer { message: format!("Failed to submit transaction(s): {:?}", e), })?; - let transaction_ids: Vec = signed_transactions - .iter() - .map(|txn| txn.id()) - .collect::, _>>()?; - let mut confirmations = Vec::new(); for id in &transaction_ids { let confirmation = self.wait_for_confirmation(id, wait_rounds).await?; @@ -2262,6 +2407,18 @@ impl Composer { .join(", ") }) .unwrap_or_else(|| "unknown".to_string()); + if Config::debug() { + let payload = serde_json::to_value(&simulate_response) + .unwrap_or_else(|_| serde_json::json!({})); + Config::events() + .emit( + EventType::TxnGroupSimulated, + EventData::TxnGroupSimulated(TxnGroupSimulatedEventData { + simulate_response: payload, + }), + ) + .await; + } return Err(ComposerError::TransactionError { message: format!( "Transaction failed at transaction(s) {} in the group. {}", @@ -2279,6 +2436,19 @@ impl Composer { let abi_returns = self.parse_abi_return_values(&confirmations); + if Config::debug() && Config::trace_all() { + let payload = + serde_json::to_value(&simulate_response).unwrap_or_else(|_| serde_json::json!({})); + Config::events() + .emit( + EventType::TxnGroupSimulated, + EventData::TxnGroupSimulated(TxnGroupSimulatedEventData { + simulate_response: payload, + }), + ) + .await; + } + Ok(SimulateComposerResults { group, transaction_ids, diff --git a/crates/algokit_utils/src/transactions/sender.rs b/crates/algokit_utils/src/transactions/sender.rs index 8751648f4..d070144f0 100644 --- a/crates/algokit_utils/src/transactions/sender.rs +++ b/crates/algokit_utils/src/transactions/sender.rs @@ -489,7 +489,7 @@ impl TransactionSender { let approval_bytes = compiled_approval.map(|ct| ct.compiled_base64_to_bytes); let clear_bytes = compiled_clear.map(|ct| ct.compiled_base64_to_bytes); - SendAppCreateResult::new(base_result, None, approval_bytes, clear_bytes) + SendAppCreateResult::new(base_result, None, approval_bytes, clear_bytes, None, None) .map_err(|e| TransactionSenderError::TransactionResultError { source: e }) }, ) @@ -518,6 +518,8 @@ impl TransactionSender { None, approval_bytes, clear_bytes, + None, + None, )) }, ) @@ -578,8 +580,15 @@ impl TransactionSender { let approval_bytes = compiled_approval.map(|ct| ct.compiled_base64_to_bytes); let clear_bytes = compiled_clear.map(|ct| ct.compiled_base64_to_bytes); - SendAppCreateResult::new(base_result, abi_return, approval_bytes, clear_bytes) - .map_err(|e| TransactionSenderError::TransactionResultError { source: e }) + SendAppCreateResult::new( + base_result, + abi_return, + approval_bytes, + clear_bytes, + None, + None, + ) + .map_err(|e| TransactionSenderError::TransactionResultError { source: e }) }, ) .await @@ -612,6 +621,8 @@ impl TransactionSender { abi_return, approval_bytes, clear_bytes, + None, + None, )) }, ) diff --git a/crates/algokit_utils/src/transactions/sender_results.rs b/crates/algokit_utils/src/transactions/sender_results.rs index a888e75af..4f88f63fb 100644 --- a/crates/algokit_utils/src/transactions/sender_results.rs +++ b/crates/algokit_utils/src/transactions/sender_results.rs @@ -63,6 +63,10 @@ pub struct SendAppCreateResult { pub compiled_approval: Option>, /// The compiled clear state program (if provided) pub compiled_clear: Option>, + /// The approval program source map (if available) + pub approval_source_map: Option, + /// The clear program source map (if available) + pub clear_source_map: Option, } /// Result of sending an app update transaction. @@ -78,6 +82,10 @@ pub struct SendAppUpdateResult { pub compiled_approval: Option>, /// The compiled clear state program (if provided) pub compiled_clear: Option>, + /// The approval program source map (if available) + pub approval_source_map: Option, + /// The clear program source map (if available) + pub clear_source_map: Option, } /// Result of sending an app call transaction. @@ -280,6 +288,8 @@ impl SendAppCreateResult { abi_return: Option, compiled_approval: Option>, compiled_clear: Option>, + approval_source_map: Option, + clear_source_map: Option, ) -> Result { // Extract app ID from the confirmation let app_id = common_params.confirmation.app_id.ok_or_else(|| { @@ -298,6 +308,8 @@ impl SendAppCreateResult { abi_return, compiled_approval, compiled_clear, + approval_source_map, + clear_source_map, }) } @@ -317,12 +329,16 @@ impl SendAppUpdateResult { abi_return: Option, compiled_approval: Option>, compiled_clear: Option>, + approval_source_map: Option, + clear_source_map: Option, ) -> Self { SendAppUpdateResult { common_params, abi_return, compiled_approval, compiled_clear, + approval_source_map, + clear_source_map, } } } diff --git a/crates/algokit_utils/tests/applications/app_client/client_management.rs b/crates/algokit_utils/tests/applications/app_client/client_management.rs index eaba54898..39277c79c 100644 --- a/crates/algokit_utils/tests/applications/app_client/client_management.rs +++ b/crates/algokit_utils/tests/applications/app_client/client_management.rs @@ -35,7 +35,7 @@ async fn from_network_resolves_id(#[future] algorand_fixture: AlgorandFixtureRes let client = AppClient::from_network( spec_with_networks, - RootAlgorandClient::default_localnet(None), + RootAlgorandClient::default_localnet(None).into(), None, None, None, @@ -110,7 +110,7 @@ async fn from_creator_and_name_resolves_and_can_call( &sender.to_string(), &app_name, spec.clone(), - algorand, + algorand.into(), Some(sender.to_string()), Some(Arc::new(fixture.test_account.clone())), None, diff --git a/crates/algokit_utils/tests/applications/app_client/create_transaction.rs b/crates/algokit_utils/tests/applications/app_client/create_transaction.rs new file mode 100644 index 000000000..f4a7e368e --- /dev/null +++ b/crates/algokit_utils/tests/applications/app_client/create_transaction.rs @@ -0,0 +1,80 @@ +use crate::common::{AlgorandFixtureResult, TestResult, algorand_fixture, deploy_arc56_contract}; +use algokit_abi::{ABIValue, Arc56Contract}; +use algokit_transact::BoxReference; +use algokit_utils::applications::app_client::{ + AppClient, AppClientMethodCallParams, AppClientParams, +}; +use algokit_utils::clients::app_manager::TealTemplateValue; +use algokit_utils::{AlgorandClient as RootAlgorandClient, AppMethodCallArg}; +use rstest::*; +use std::sync::Arc; + +fn get_testing_app_spec() -> Arc56Contract { + let json = algokit_test_artifacts::testing_app::APPLICATION_ARC56; + Arc56Contract::from_json(json).expect("valid arc56") +} + +#[rstest] +#[tokio::test] +async fn create_txn_with_box_references( + #[future] algorand_fixture: AlgorandFixtureResult, +) -> TestResult { + let fixture = algorand_fixture.await?; + let sender = fixture.test_account.account().address(); + + let app_id = deploy_arc56_contract( + &fixture, + &sender, + &get_testing_app_spec(), + Some( + [("VALUE", 1), ("UPDATABLE", 0), ("DELETABLE", 0)] + .into_iter() + .map(|(k, v)| (k.to_string(), TealTemplateValue::Int(v))) + .collect(), + ), + None, + None, + ) + .await?; + + let mut algorand = RootAlgorandClient::default_localnet(None); + algorand.set_signer(sender.clone(), Arc::new(fixture.test_account.clone())); + let client = AppClient::new(AppClientParams { + app_id, + app_spec: get_testing_app_spec(), + algorand, + app_name: None, + default_sender: Some(sender.to_string()), + default_signer: None, + source_maps: None, + transaction_composer_config: None, + }); + + let tx = client + .create_transaction() + .call( + AppClientMethodCallParams { + method: "call_abi".to_string(), + args: vec![AppMethodCallArg::ABIValue(ABIValue::from("test"))], + sender: Some(sender.to_string()), + box_references: Some(vec![BoxReference { + app_id: 0, + name: b"1".to_vec(), + }]), + ..Default::default() + }, + None, + ) + .await?; + + if let algokit_transact::Transaction::AppCall(fields) = tx { + let boxes = fields.box_references.expect("boxes"); + assert_eq!(boxes.len(), 1); + assert_eq!(boxes[0].app_id, 0); + assert_eq!(boxes[0].name, b"1".to_vec()); + } else { + panic!("expected app call txn") + } + + Ok(()) +} diff --git a/crates/algokit_utils/tests/applications/app_client/structs.rs b/crates/algokit_utils/tests/applications/app_client/structs.rs index 4e4213746..539cea4ea 100644 --- a/crates/algokit_utils/tests/applications/app_client/structs.rs +++ b/crates/algokit_utils/tests/applications/app_client/structs.rs @@ -41,7 +41,7 @@ async fn test_nested_structs_described_by_structure( algokit_utils::applications::app_client::AppClientParams { app_id, app_spec: spec, - algorand, + algorand: algorand.into(), app_name: None, default_sender: Some(sender.to_string()), default_signer: None, @@ -151,7 +151,7 @@ async fn test_nested_structs_referenced_by_name( algokit_utils::applications::app_client::AppClientParams { app_id, app_spec: spec, - algorand, + algorand: algorand.into(), app_name: None, default_sender: Some(sender.to_string()), default_signer: None, diff --git a/crates/algokit_utils/tests/applications/app_factory.rs b/crates/algokit_utils/tests/applications/app_factory.rs new file mode 100644 index 000000000..b70ec83d8 --- /dev/null +++ b/crates/algokit_utils/tests/applications/app_factory.rs @@ -0,0 +1,1163 @@ +use crate::common::TestAccount; +use crate::common::{ + AlgorandFixture, AlgorandFixtureResult, TestResult, algorand_fixture, testing_app_spec, +}; +use algokit_abi::Arc56Contract; +use algokit_transact::Address; +use algokit_transact::OnApplicationComplete; +use algokit_utils::applications::app_client::{AppClientMethodCallParams, CompilationParams}; +use algokit_utils::applications::app_factory::{ + AppFactory, AppFactoryCreateMethodCallParams, AppFactoryParams, +}; +use algokit_utils::applications::app_factory::{AppFactoryCreateParams, DeployArgs}; +use algokit_utils::applications::{AppDeployResult, OnSchemaBreak, OnUpdate}; +use algokit_utils::clients::app_manager::{TealTemplateParams, TealTemplateValue}; +use algokit_utils::transactions::TransactionComposerConfig; +use algokit_utils::{AlgorandClient, AppMethodCallArg}; +use rstest::*; +use std::collections::HashMap; +use std::sync::Arc; + +#[derive(Default)] +pub struct AppFactoryOptions { + pub app_name: Option, + pub updatable: Option, + pub deletable: Option, + pub deploy_time_params: Option>, + pub transaction_composer_config: Option, +} + +fn abi_str_arg(s: &str) -> AppMethodCallArg { + AppMethodCallArg::ABIValue(algokit_abi::ABIValue::from(s)) +} + +fn into_factory_inputs(fixture: AlgorandFixture) -> (Arc, TestAccount) { + let AlgorandFixture { + algorand_client, + test_account, + .. + } = fixture; + (Arc::new(algorand_client), test_account) +} + +/// Construct an `AppFactory` for a provided ARC-56 spec with common defaults. +pub async fn build_app_factory_with_spec( + algorand_client: Arc, + test_account: TestAccount, + app_spec: Arc56Contract, + opts: AppFactoryOptions, +) -> AppFactory { + let sender: Address = test_account.account().address(); + + AppFactory::new(AppFactoryParams { + algorand: algorand_client, + app_spec, + app_name: opts.app_name, + default_sender: Some(sender.to_string()), + default_signer: Some(Arc::new(test_account.clone())), + version: None, + deploy_time_params: opts.deploy_time_params, + updatable: opts.updatable, + deletable: opts.deletable, + source_maps: None, + transaction_composer_config: opts.transaction_composer_config, + }) +} + +async fn build_testing_app_factory( + algorand_client: Arc, + test_account: TestAccount, + opts: AppFactoryOptions, +) -> AppFactory { + return build_app_factory_with_spec(algorand_client, test_account, testing_app_spec(), opts) + .await; +} + +fn compilation_params(value: u64, updatable: bool, deletable: bool) -> CompilationParams { + let mut t = TealTemplateParams::default(); + t.insert("VALUE".to_string(), TealTemplateValue::Int(value)); + CompilationParams { + deploy_time_params: Some(t), + updatable: Some(updatable), + deletable: Some(deletable), + ..Default::default() + } +} + +#[rstest] +#[tokio::test] +async fn bare_create_with_deploy_time_params( + #[future] algorand_fixture: AlgorandFixtureResult, +) -> TestResult { + let fixture = algorand_fixture.await?; + let (algorand_client, test_account) = into_factory_inputs(fixture); + + let factory = build_testing_app_factory( + algorand_client, + test_account, + AppFactoryOptions { + deploy_time_params: Some(HashMap::from([( + "VALUE".to_string(), + TealTemplateValue::Int(1), + )])), + updatable: Some(false), + deletable: Some(false), + ..Default::default() + }, + ) + .await; + + let compilation_params = compilation_params(1, false, false); + + let (client, res) = factory + .send() + .bare() + .create( + Some(AppFactoryCreateParams::default()), + None, + Some(compilation_params), + ) + .await?; + + assert!(client.app_id() > 0); + assert_eq!( + client.app_address(), + algokit_transact::Address::from_app_id(&client.app_id()) + ); + assert!(res.app_id > 0); + assert!(res.compiled_approval.is_some()); + assert!(res.compiled_clear.is_some()); + assert!(res.approval_source_map.is_some()); + assert!(res.clear_source_map.is_some()); + assert!(res.common_params.confirmation.confirmed_round.is_some()); + Ok(()) +} + +#[rstest] +#[tokio::test] +async fn constructor_compilation_params_precedence( + #[future] algorand_fixture: AlgorandFixtureResult, +) -> TestResult { + let fixture = algorand_fixture.await?; + let (algorand_client, test_account) = into_factory_inputs(fixture); + + let factory = build_testing_app_factory( + algorand_client, + test_account, + AppFactoryOptions { + deploy_time_params: Some(HashMap::from([( + "VALUE".to_string(), + TealTemplateValue::Int(1), + )])), + updatable: Some(false), + deletable: Some(false), + ..Default::default() + }, + ) + .await; + + let (client, result) = factory.send().bare().create(None, None, None).await?; + + assert!(result.app_id > 0); + assert_eq!(client.app_id(), result.app_id); + Ok(()) +} + +#[rstest] +#[tokio::test] +async fn oncomplete_override_on_create( + #[future] algorand_fixture: AlgorandFixtureResult, +) -> TestResult { + let fixture = algorand_fixture.await?; + let (algorand_client, test_account) = into_factory_inputs(fixture); + + let factory = build_testing_app_factory( + algorand_client, + test_account, + AppFactoryOptions { + updatable: Some(true), + deletable: Some(true), + ..Default::default() + }, + ) + .await; + + let params = AppFactoryCreateParams { + on_complete: Some(OnApplicationComplete::OptIn), + ..Default::default() + }; + let compilation_params = compilation_params(1, true, true); + let (client, result) = factory + .send() + .bare() + .create(Some(params), None, Some(compilation_params)) + .await?; + + match &result.common_params.transaction { + algokit_transact::Transaction::AppCall(fields) => { + assert_eq!( + fields.on_complete, + algokit_transact::OnApplicationComplete::OptIn + ); + } + _ => return Err("expected app call".into()), + } + assert!(client.app_id() > 0); + assert_eq!( + client.app_address(), + algokit_transact::Address::from_app_id(&client.app_id()) + ); + assert!(result.common_params.confirmations.first().is_some()); + assert!(result.compiled_approval.is_some()); + assert!(result.compiled_clear.is_some()); + assert!(result.approval_source_map.is_some()); + assert!(result.clear_source_map.is_some()); + Ok(()) +} + +#[rstest] +#[tokio::test] +async fn abi_based_create_returns_value( + #[future] algorand_fixture: AlgorandFixtureResult, +) -> TestResult { + let fixture = algorand_fixture.await?; + let (algorand_client, test_account) = into_factory_inputs(fixture); + + let factory = build_testing_app_factory( + algorand_client, + test_account, + AppFactoryOptions { + deploy_time_params: Some(HashMap::from([( + "VALUE".to_string(), + TealTemplateValue::Int(1), + )])), + updatable: Some(true), + deletable: Some(true), + ..Default::default() + }, + ) + .await; + + let cp = compilation_params(1, true, false); + + let (_client, call_return) = factory + .send() + .create( + AppFactoryCreateMethodCallParams { + method: "create_abi(string)string".to_string(), + args: Some(vec![abi_str_arg("string_io")]), + ..Default::default() + }, + None, + Some(cp), + ) + .await?; + + let abi_ret = call_return + .arc56_return() + .expect("abi return expected") + .clone(); + match abi_ret { + algokit_abi::ABIValue::String(s) => assert_eq!(s, "string_io"), + other => return Err(format!("expected string return, got {other:?}").into()), + } + assert!(call_return.compiled_approval.is_some()); + assert!(call_return.compiled_clear.is_some()); + assert!(call_return.approval_source_map.is_some()); + assert!(call_return.clear_source_map.is_some()); + Ok(()) +} + +#[rstest] +#[tokio::test] +async fn create_then_call_via_app_client( + #[future] algorand_fixture: AlgorandFixtureResult, +) -> TestResult { + let fixture = algorand_fixture.await?; + let sender = fixture.test_account.account().address(); + let (algorand_client, test_account) = into_factory_inputs(fixture); + + let factory = build_testing_app_factory( + algorand_client, + test_account, + AppFactoryOptions { + updatable: Some(true), + ..Default::default() + }, + ) + .await; + + let cp = compilation_params(1, true, true); + + let (client, _res) = factory.send().bare().create(None, None, Some(cp)).await?; + + let send_res = client + .send() + .call( + AppClientMethodCallParams { + method: "call_abi(string)string".to_string(), + args: vec![abi_str_arg("test")], + sender: Some(sender.to_string()), + ..Default::default() + }, + None, + None, + ) + .await?; + + let abi_ret = send_res.abi_return.clone().expect("abi return expected"); + if let Some(algokit_abi::ABIValue::String(s)) = abi_ret.return_value { + assert_eq!(s, "Hello, test"); + } else { + return Err("expected string".into()); + } + Ok(()) +} + +#[rstest] +#[tokio::test] +async fn call_app_with_too_many_args( + #[future] algorand_fixture: AlgorandFixtureResult, +) -> TestResult { + let fixture = algorand_fixture.await?; + let sender = fixture.test_account.account().address(); + let (algorand_client, test_account) = into_factory_inputs(fixture); + + let factory = build_testing_app_factory( + algorand_client, + test_account, + AppFactoryOptions { + deploy_time_params: Some(HashMap::from([( + "VALUE".to_string(), + TealTemplateValue::Int(1), + )])), + updatable: Some(false), + deletable: Some(false), + ..Default::default() + }, + ) + .await; + + let (client, _res) = factory + .send() + .bare() + .create(None, None, Some(compilation_params(1, false, false))) + .await?; + + let err = client + .send() + .call( + AppClientMethodCallParams { + method: "call_abi(string)string".to_string(), + args: vec![abi_str_arg("test"), abi_str_arg("extra")], + sender: Some(sender.to_string()), + ..Default::default() + }, + None, + None, + ) + .await + .expect_err("expected error for too many args"); + // The error is wrapped into a ValidationError; extract message via Display + let msg = err.to_string(); + // Accept the actual error message format from Rust implementation + assert!( + msg.contains("The number of provided arguments is 2 while the method expects 1 arguments"), + "Expected error message about too many arguments, got: {msg}" + ); + Ok(()) +} + +#[rstest] +#[tokio::test] +async fn call_app_with_rekey(#[future] algorand_fixture: AlgorandFixtureResult) -> TestResult { + let mut fixture = algorand_fixture.await?; + let sender = fixture.test_account.account().address(); + // Generate a new account to rekey to before consuming the fixture + let rekey_to = fixture.generate_account(None).await?; + let rekey_to_addr = rekey_to.account().address(); + let (algorand_client, test_account) = into_factory_inputs(fixture); + + let factory = build_testing_app_factory( + Arc::clone(&algorand_client), + test_account, + AppFactoryOptions { + deploy_time_params: Some(HashMap::from([( + "VALUE".to_string(), + TealTemplateValue::Int(1), + )])), + updatable: Some(true), + deletable: Some(true), + ..Default::default() + }, + ) + .await; + + let (client, _res) = factory.send().bare().create(None, None, None).await?; + + // Opt-in with rekey_to + client + .send() + .opt_in( + AppClientMethodCallParams { + method: "opt_in()void".to_string(), + args: vec![], + sender: Some(sender.to_string()), + rekey_to: Some(rekey_to_addr.to_string()), + ..Default::default() + }, + None, + ) + .await?; + + // If rekey succeeded, a zero payment using the rekeyed signer should succeed + let pay = algokit_utils::PaymentParams { + sender: sender.clone(), + // signer will be picked up from account manager: set_signer already configured for original sender, + // but after rekey the auth address must be rekey_to's signer. Use explicit signer. + signer: Some(Arc::new(rekey_to.clone())), + receiver: sender.clone(), + amount: 0, + ..Default::default() + }; + let _ = algorand_client.send().payment(pay, None).await?; + Ok(()) +} + +#[rstest] +#[tokio::test] +async fn delete_app_with_abi_direct( + #[future] algorand_fixture: AlgorandFixtureResult, +) -> TestResult { + let fixture = algorand_fixture.await?; + let sender = fixture.test_account.account().address(); + let (algorand_client, test_account) = into_factory_inputs(fixture); + + let factory = build_testing_app_factory( + algorand_client, + test_account, + AppFactoryOptions { + deploy_time_params: Some(HashMap::from([( + "VALUE".to_string(), + TealTemplateValue::Int(1), + )])), + updatable: Some(false), + deletable: Some(true), + ..Default::default() + }, + ) + .await; + + let (client, _res) = factory + .send() + .bare() + .create(None, None, Some(compilation_params(1, false, true))) + .await?; + + let delete_res = client + .send() + .delete( + AppClientMethodCallParams { + method: "delete_abi(string)string".to_string(), + args: vec![abi_str_arg("string_io")], + sender: Some(sender.to_string()), + ..Default::default() + }, + None, + ) + .await?; + + let abi_ret = delete_res.abi_return.clone().expect("abi return expected"); + if let Some(algokit_abi::ABIValue::String(s)) = abi_ret.return_value { + assert_eq!(s, "string_io"); + } else { + return Err("expected string return".into()); + } + Ok(()) +} + +#[rstest] +#[tokio::test] +async fn update_app_with_abi_direct( + #[future] algorand_fixture: AlgorandFixtureResult, +) -> TestResult { + let fixture = algorand_fixture.await?; + let sender = fixture.test_account.account().address(); + let (algorand_client, test_account) = into_factory_inputs(fixture); + + let factory = build_testing_app_factory( + algorand_client, + test_account, + AppFactoryOptions { + deploy_time_params: Some(HashMap::from([( + "VALUE".to_string(), + TealTemplateValue::Int(1), + )])), + updatable: Some(true), + deletable: Some(false), + ..Default::default() + }, + ) + .await; + + // Initial create + let (client, _create_res) = factory + .send() + .bare() + .create(None, None, Some(compilation_params(1, true, false))) + .await?; + + // Update via ABI (extra pages are auto-calculated internally) + let update_res = client + .send() + .update( + AppClientMethodCallParams { + method: "update_abi(string)string".to_string(), + args: vec![abi_str_arg("string_io")], + sender: Some(sender.to_string()), + ..Default::default() + }, + Some(compilation_params(1, true, false)), + None, + ) + .await?; + + let abi_ret = update_res.abi_return.clone().expect("abi return expected"); + if let Some(algokit_abi::ABIValue::String(s)) = abi_ret.return_value { + assert_eq!(s, "string_io"); + } else { + return Err("expected string return".into()); + } + assert!(update_res.compiled_approval.is_some()); + assert!(update_res.compiled_clear.is_some()); + assert!(update_res.approval_source_map.is_some()); + assert!(update_res.clear_source_map.is_some()); + Ok(()) +} + +#[rstest] +#[tokio::test] +async fn deploy_when_immutable_and_permanent( + #[future] algorand_fixture: AlgorandFixtureResult, +) -> TestResult { + let fixture = algorand_fixture.await?; + let (algorand_client, test_account) = into_factory_inputs(fixture); + + let factory = build_testing_app_factory( + algorand_client, + test_account, + AppFactoryOptions { + deploy_time_params: Some(HashMap::from([( + "VALUE".to_string(), + TealTemplateValue::Int(1), + )])), + updatable: Some(false), + deletable: Some(false), + ..Default::default() + }, + ) + .await; + + factory + .deploy(DeployArgs { + on_update: Some(OnUpdate::Fail), + on_schema_break: Some(OnSchemaBreak::Fail), + ..Default::default() + }) + .await?; + Ok(()) +} + +#[rstest] +#[tokio::test] +async fn deploy_app_create(#[future] algorand_fixture: AlgorandFixtureResult) -> TestResult { + let fixture = algorand_fixture.await?; + let (algorand_client, test_account) = into_factory_inputs(fixture); + + let factory = build_testing_app_factory( + algorand_client, + test_account, + AppFactoryOptions { + deploy_time_params: Some(HashMap::from([( + "VALUE".to_string(), + TealTemplateValue::Int(1), + )])), + updatable: Some(true), + ..Default::default() + }, + ) + .await; + + let (client, deploy_result) = factory.deploy(Default::default()).await?; + + match &deploy_result.operation_performed { + AppDeployResult::Create { .. } => {} + _ => return Err("expected Create".into()), + } + let create_result = deploy_result + .create_result + .as_ref() + .expect("create result expected"); + assert!(client.app_id() > 0); + assert_eq!(client.app_id(), deploy_result.app.app_id); + assert!(create_result.compiled_approval.is_some()); + assert!(create_result.compiled_clear.is_some()); + assert!(create_result.approval_source_map.is_some()); + assert!(create_result.clear_source_map.is_some()); + assert_eq!( + create_result + .common_params + .confirmation + .app_id + .unwrap_or_default(), + deploy_result.app.app_id + ); + Ok(()) +} + +#[rstest] +#[tokio::test] +async fn deploy_app_create_abi(#[future] algorand_fixture: AlgorandFixtureResult) -> TestResult { + let fixture = algorand_fixture.await?; + let (algorand_client, test_account) = into_factory_inputs(fixture); + + let factory = build_testing_app_factory( + algorand_client, + test_account, + AppFactoryOptions { + deploy_time_params: Some(HashMap::from([( + "VALUE".to_string(), + TealTemplateValue::Int(1), + )])), + updatable: Some(true), + deletable: Some(true), + ..Default::default() + }, + ) + .await; + + let create_params = AppFactoryCreateMethodCallParams { + method: "create_abi(string)string".to_string(), + args: Some(vec![algokit_utils::AppMethodCallArg::ABIValue( + algokit_abi::ABIValue::from("arg_io"), + )]), + ..Default::default() + }; + + let (client, deploy_result) = factory + .deploy(DeployArgs { + create_params: Some(create_params), + ..Default::default() + }) + .await?; + + match &deploy_result.operation_performed { + AppDeployResult::Create { .. } => {} + _ => return Err("expected Create".into()), + } + let create_result = deploy_result + .create_result + .as_ref() + .expect("create result expected"); + assert!(client.app_id() > 0); + assert_eq!(client.app_id(), deploy_result.app.app_id); + let abi_value = create_result + .arc56_return() + .cloned() + .expect("abi return expected"); + let abi_value = match abi_value { + algokit_abi::ABIValue::String(s) => s, + other => return Err(format!("expected string abi return, got {other:?}").into()), + }; + assert_eq!(abi_value, "arg_io"); + assert!(create_result.compiled_approval.is_some()); + assert!(create_result.compiled_clear.is_some()); + assert!(create_result.approval_source_map.is_some()); + assert!(create_result.clear_source_map.is_some()); + Ok(()) +} + +#[rstest] +#[tokio::test] +async fn deploy_app_update(#[future] algorand_fixture: AlgorandFixtureResult) -> TestResult { + let fixture = algorand_fixture.await?; + let (algorand_client, test_account) = into_factory_inputs(fixture); + + let factory = build_testing_app_factory( + Arc::clone(&algorand_client), + test_account.clone(), + AppFactoryOptions { + app_name: Some("APP_NAME".to_string()), + deploy_time_params: Some(HashMap::from([( + "VALUE".to_string(), + TealTemplateValue::Int(1), + )])), + updatable: Some(true), + deletable: Some(true), + ..Default::default() + }, + ) + .await; + + // Initial create (updatable) + let (_client1, create_res) = factory.deploy(Default::default()).await?; + match &create_res.operation_performed { + AppDeployResult::Create { .. } => {} + _ => return Err("expected Create".into()), + } + let initial_create = create_res + .create_result + .as_ref() + .expect("create result expected"); + + // Update + let factory2 = build_testing_app_factory( + Arc::clone(&algorand_client), + test_account, + AppFactoryOptions { + app_name: Some("APP_NAME".to_string()), + deploy_time_params: Some(HashMap::from([( + "VALUE".to_string(), + TealTemplateValue::Int(2), + )])), + updatable: Some(true), + deletable: Some(true), + ..Default::default() + }, + ) + .await; + + let (_client2, update_res) = factory2 + .deploy(DeployArgs { + on_update: Some(OnUpdate::Update), + ..Default::default() + }) + .await?; + + match &update_res.operation_performed { + AppDeployResult::Update { .. } => {} + _ => return Err("expected Update".into()), + } + let updated = update_res + .update_result + .as_ref() + .expect("update result expected"); + assert_eq!(create_res.app.app_id, update_res.app.app_id); + assert_eq!(create_res.app.app_address, update_res.app.app_address); + assert!(update_res.app.updated_round >= create_res.app.created_round); + assert!(initial_create.compiled_approval.is_some()); + assert!(initial_create.compiled_clear.is_some()); + assert!(updated.compiled_approval.is_some()); + assert!(updated.compiled_clear.is_some()); + assert!(updated.approval_source_map.is_some()); + assert!(updated.clear_source_map.is_some()); + assert_eq!( + update_res + .update_result + .as_ref() + .and_then(|r| r.common_params.confirmation.confirmed_round), + Some(update_res.app.updated_round) + ); + Ok(()) +} + +#[rstest] +#[tokio::test] +async fn deploy_app_update_detects_extra_pages_as_breaking_change( + #[future] algorand_fixture: AlgorandFixtureResult, +) -> TestResult { + let fixture = algorand_fixture.await?; + // Factory with small program spec + let small_spec = algokit_abi::Arc56Contract::from_json( + algokit_test_artifacts::extra_pages_test::SMALL_ARC56, + ) + .expect("valid arc56"); + let (algorand_client, test_account) = into_factory_inputs(fixture); + let factory = build_app_factory_with_spec( + Arc::clone(&algorand_client), + test_account.clone(), + small_spec, + AppFactoryOptions { + deploy_time_params: Some(HashMap::from([( + "VALUE".to_string(), + TealTemplateValue::Int(1), + )])), + updatable: Some(true), + ..Default::default() + }, + ) + .await; + + // Create using small + let (_small_client, create_res) = factory.deploy(Default::default()).await?; + match &create_res.operation_performed { + AppDeployResult::Create { .. } => {} + _ => return Err("expected Create for small".into()), + } + + // Switch to large spec and attempt update with Append schema break + let large_spec = algokit_abi::Arc56Contract::from_json( + algokit_test_artifacts::extra_pages_test::LARGE_ARC56, + ) + .expect("valid arc56"); + let factory_large = build_app_factory_with_spec( + algorand_client, + test_account, + large_spec, + AppFactoryOptions { + deploy_time_params: Some(HashMap::from([( + "VALUE".to_string(), + TealTemplateValue::Int(2), + )])), + updatable: Some(true), + ..Default::default() + }, + ) + .await; + + let (large_client, update_res) = factory_large + .deploy(DeployArgs { + on_update: Some(OnUpdate::Update), + on_schema_break: Some(OnSchemaBreak::Append), + ..Default::default() + }) + .await?; + + match &update_res.operation_performed { + AppDeployResult::Create { .. } => {} + _ => return Err("expected Create on schema break append".into()), + } + + // App id should differ between small and large + assert_ne!(create_res.app.app_id, large_client.app_id()); + Ok(()) +} + +#[rstest] +#[tokio::test] +async fn deploy_app_update_detects_extra_pages_as_breaking_change_fail_case( + #[future] algorand_fixture: AlgorandFixtureResult, +) -> TestResult { + let fixture = algorand_fixture.await?; + // Start with small + let small_spec = algokit_abi::Arc56Contract::from_json( + algokit_test_artifacts::extra_pages_test::SMALL_ARC56, + ) + .expect("valid arc56"); + let (algorand_client, test_account) = into_factory_inputs(fixture); + let factory_small = build_app_factory_with_spec( + Arc::clone(&algorand_client), + test_account.clone(), + small_spec, + AppFactoryOptions { + updatable: Some(true), + ..Default::default() + }, + ) + .await; + + // Create using small + let (_small_client, _create_res) = factory_small.deploy(Default::default()).await?; + + // Switch to large and attempt update with Fail schema break + let large_spec = algokit_abi::Arc56Contract::from_json( + algokit_test_artifacts::extra_pages_test::LARGE_ARC56, + ) + .expect("valid arc56"); + let factory_fail = build_app_factory_with_spec( + algorand_client, + test_account, + large_spec, + AppFactoryOptions { + updatable: Some(true), + ..Default::default() + }, + ) + .await; + + let msg = match factory_fail + .deploy(DeployArgs { + on_update: Some(OnUpdate::Update), + on_schema_break: Some(OnSchemaBreak::Fail), + ..Default::default() + }) + .await + { + Ok(_) => return Err("expected schema break fail error".into()), + Err(e) => e.to_string(), + }; + assert!(msg.contains("Executing the fail on schema break strategy, stopping deployment.")); + Ok(()) +} + +#[rstest] +#[tokio::test] +async fn deploy_app_update_abi(#[future] algorand_fixture: AlgorandFixtureResult) -> TestResult { + let fixture = algorand_fixture.await?; + let (algorand_client, test_account) = into_factory_inputs(fixture); + + let factory = build_testing_app_factory( + Arc::clone(&algorand_client), + test_account.clone(), + AppFactoryOptions { + app_name: Some("APP_NAME".to_string()), + deploy_time_params: Some(HashMap::from([( + "VALUE".to_string(), + TealTemplateValue::Int(1), + )])), + updatable: Some(true), + deletable: Some(true), + ..Default::default() + }, + ) + .await; + + // Create updatable + let _ = factory.deploy(Default::default()).await?; + + // Update via ABI with VALUE=2 but same updatable/deletable + let update_params = AppClientMethodCallParams { + method: "update_abi(string)string".to_string(), + args: vec![algokit_utils::AppMethodCallArg::ABIValue( + algokit_abi::ABIValue::from("args_io"), + )], + ..Default::default() + }; + let factory2 = build_testing_app_factory( + Arc::clone(&algorand_client), + test_account, + AppFactoryOptions { + app_name: Some("APP_NAME".to_string()), + deploy_time_params: Some(HashMap::from([( + "VALUE".to_string(), + TealTemplateValue::Int(2), + )])), + updatable: Some(true), + deletable: Some(true), + ..Default::default() + }, + ) + .await; + let (_client2, update_res) = factory2 + .deploy(DeployArgs { + on_update: Some(OnUpdate::Update), + update_params: Some(update_params), + ..Default::default() + }) + .await?; + match &update_res.operation_performed { + AppDeployResult::Update { .. } => {} + _ => return Err("expected Update".into()), + } + let update_result = update_res + .update_result + .as_ref() + .expect("update result expected"); + let abi_value = update_result.arc56_return().cloned().expect("abi return"); + let abi_return = match abi_value { + algokit_abi::ABIValue::String(s) => s, + other => return Err(format!("expected string return, got {other:?}").into()), + }; + assert_eq!(abi_return, "args_io"); + assert!(update_result.compiled_approval.is_some()); + assert!(update_result.compiled_clear.is_some()); + assert!(update_result.approval_source_map.is_some()); + assert!(update_result.clear_source_map.is_some()); + // Ensure update onComplete is UpdateApplication + match &update_result.common_params.transaction { + algokit_transact::Transaction::AppCall(fields) => { + assert_eq!( + fields.on_complete, + algokit_transact::OnApplicationComplete::UpdateApplication + ); + } + _ => return Err("expected app call".into()), + } + Ok(()) +} + +#[rstest] +#[tokio::test] +async fn deploy_app_replace(#[future] algorand_fixture: AlgorandFixtureResult) -> TestResult { + let fixture = algorand_fixture.await?; + let (algorand_client, test_account) = into_factory_inputs(fixture); + + let factory = build_testing_app_factory( + Arc::clone(&algorand_client), + test_account.clone(), + AppFactoryOptions { + app_name: Some("APP_NAME".to_string()), + deploy_time_params: Some(HashMap::from([( + "VALUE".to_string(), + TealTemplateValue::Int(1), + )])), + updatable: Some(true), + deletable: Some(true), + ..Default::default() + }, + ) + .await; + + let (_client1, create_res) = factory.deploy(Default::default()).await?; + let old_app_id = create_res.app.app_id; + + // Replace + let factory2 = build_testing_app_factory( + Arc::clone(&algorand_client), + test_account, + AppFactoryOptions { + app_name: Some("APP_NAME".to_string()), + deploy_time_params: Some(HashMap::from([( + "VALUE".to_string(), + TealTemplateValue::Int(2), + )])), + updatable: Some(true), + deletable: Some(true), + ..Default::default() + }, + ) + .await; + let (_client2, replace_res) = factory2 + .deploy(DeployArgs { + on_update: Some(OnUpdate::Replace), + ..Default::default() + }) + .await?; + match &replace_res.operation_performed { + AppDeployResult::Replace { .. } => {} + _ => return Err("expected Replace".into()), + } + assert!(replace_res.app.app_id > old_app_id); + let replace_create = replace_res + .create_result + .as_ref() + .expect("replace create result expected"); + let replace_delete = replace_res + .delete_result + .as_ref() + .expect("replace delete result expected"); + assert!(replace_create.compiled_approval.is_some()); + assert!(replace_create.compiled_clear.is_some()); + assert!(replace_create.compiled_approval.is_some()); + assert!(replace_create.compiled_clear.is_some()); + assert!( + replace_delete + .common_params + .confirmation + .confirmed_round + .is_some() + ); + // Ensure delete app call references old app id and correct onComplete + match &replace_delete.common_params.transaction { + algokit_transact::Transaction::AppCall(fields) => { + assert_eq!( + fields.on_complete, + algokit_transact::OnApplicationComplete::DeleteApplication + ); + assert_eq!(fields.app_id, old_app_id); + } + _ => return Err("expected app call".into()), + } + assert_eq!( + replace_res.app.app_address, + algokit_transact::Address::from_app_id(&replace_res.app.app_id) + ); + Ok(()) +} + +#[rstest] +#[tokio::test] +async fn deploy_app_replace_abi(#[future] algorand_fixture: AlgorandFixtureResult) -> TestResult { + let fixture = algorand_fixture.await?; + let (algorand_client, test_account) = into_factory_inputs(fixture); + + let factory = build_testing_app_factory( + Arc::clone(&algorand_client), + test_account.clone(), + AppFactoryOptions { + app_name: Some("APP_NAME".to_string()), + deploy_time_params: Some(HashMap::from([( + "VALUE".to_string(), + TealTemplateValue::Int(1), + )])), + updatable: Some(true), + deletable: Some(true), + ..Default::default() + }, + ) + .await; + + // Initial create + let (_client1, create_res) = factory + .deploy(DeployArgs { + app_name: Some("APP_NAME".to_string()), + ..Default::default() + }) + .await?; + + let old_app_id = create_res.app.app_id; + + // Replace via ABI create/delete + let create_params = AppFactoryCreateMethodCallParams { + method: "create_abi(string)string".to_string(), + args: Some(vec![abi_str_arg("arg_io")]), + ..Default::default() + }; + let delete_params = AppClientMethodCallParams { + method: "delete_abi(string)string".to_string(), + args: vec![abi_str_arg("arg2_io")], + ..Default::default() + }; + let factory2 = build_testing_app_factory( + Arc::clone(&algorand_client), + test_account, + AppFactoryOptions { + app_name: Some("APP_NAME".to_string()), + deploy_time_params: Some(HashMap::from([( + "VALUE".to_string(), + TealTemplateValue::Int(2), + )])), + updatable: Some(true), + deletable: Some(true), + ..Default::default() + }, + ) + .await; + let (_client2, replace_res) = factory2 + .deploy(DeployArgs { + on_update: Some(OnUpdate::Replace), + create_params: Some(create_params), + delete_params: Some(delete_params), + ..Default::default() + }) + .await?; + match &replace_res.operation_performed { + AppDeployResult::Replace { .. } => {} + _ => return Err("expected Replace".into()), + } + assert!(replace_res.app.app_id > old_app_id); + // Validate ABI return values for create/delete + let create_res = replace_res + .create_result + .as_ref() + .expect("create result expected"); + + let create_value = create_res + .arc56_return() + .cloned() + .expect("create abi return"); + let create_ret = match create_value { + algokit_abi::ABIValue::String(s) => s, + _ => return Err("create abi return".into()), + }; + assert_eq!(create_ret, "arg_io"); + + if let Some(delete_res) = replace_res.delete_result.as_ref() { + if let Some(abi_ret) = delete_res.abi_return.clone().and_then(|r| r.return_value) { + if let algokit_abi::ABIValue::String(s) = abi_ret { + assert_eq!(s, "arg2_io"); + } + } + } + Ok(()) +} diff --git a/crates/algokit_utils/tests/applications/mod.rs b/crates/algokit_utils/tests/applications/mod.rs index 4d5af633a..1a0af34f2 100644 --- a/crates/algokit_utils/tests/applications/mod.rs +++ b/crates/algokit_utils/tests/applications/mod.rs @@ -1,2 +1,3 @@ pub mod app_client; pub mod app_deployer; +pub mod app_factory; diff --git a/crates/algokit_utils/tests/common/app_fixture.rs b/crates/algokit_utils/tests/common/app_fixture.rs index 594e0f695..f332a1025 100644 --- a/crates/algokit_utils/tests/common/app_fixture.rs +++ b/crates/algokit_utils/tests/common/app_fixture.rs @@ -55,7 +55,7 @@ pub async fn build_app_fixture( let client = AppClient::new(AppClientParams { app_id, app_spec: spec.clone(), - algorand, + algorand: algorand.into(), app_name: opts.app_name.clone(), default_sender: Some( opts.default_sender_override diff --git a/crates/algokit_utils/tests/common/mod.rs b/crates/algokit_utils/tests/common/mod.rs index 8896fd7c2..92abd66b9 100644 --- a/crates/algokit_utils/tests/common/mod.rs +++ b/crates/algokit_utils/tests/common/mod.rs @@ -10,6 +10,7 @@ pub mod test_account; use algokit_abi::Arc56Contract; use algokit_utils::AppCreateParams; +use algokit_utils::applications::app_factory; use algokit_utils::clients::app_manager::{ AppManager, DeploymentMetadata, TealTemplateParams, TealTemplateValue, };