From 89cfe151c16bd9fb53ea5e09da84db0f4ac1f2a6 Mon Sep 17 00:00:00 2001 From: Daniel McGregor Date: Wed, 15 Oct 2025 15:34:01 +0800 Subject: [PATCH 1/7] test: add test for resource population of a box and app --- .../testing_app_puya/app_spec.arc32.json | 41 ++++++++++++++++--- tests/artifacts/testing_app_puya/contract.py | 24 ++++++++++- tests/transactions/test_resource_packing.py | 25 +++++++++++ 3 files changed, 84 insertions(+), 6 deletions(-) diff --git a/tests/artifacts/testing_app_puya/app_spec.arc32.json b/tests/artifacts/testing_app_puya/app_spec.arc32.json index d8518906..9a318013 100644 --- a/tests/artifacts/testing_app_puya/app_spec.arc32.json +++ b/tests/artifacts/testing_app_puya/app_spec.arc32.json @@ -44,16 +44,26 @@ ] } } + }, + "bootstrap_external_app()uint64": { + "call_config": { + "no_op": "CALL" + } + }, + "set_external_box()void": { + "call_config": { + "no_op": "CALL" + } } }, "source": { - "approval": "I3ByYWdtYSB2ZXJzaW9uIDEwCgpzbWFydF9jb250cmFjdHMuaGVsbG9fd29ybGQzLmNvbnRyYWN0LlRlc3RQdXlhQm94ZXMuYXBwcm92YWxfcHJvZ3JhbToKICAgIGludGNibG9jayAxIDAKICAgIGNhbGxzdWIgX19wdXlhX2FyYzRfcm91dGVyX18KICAgIHJldHVybgoKCi8vIHNtYXJ0X2NvbnRyYWN0cy5oZWxsb193b3JsZDMuY29udHJhY3QuVGVzdFB1eWFCb3hlcy5fX3B1eWFfYXJjNF9yb3V0ZXJfXygpIC0+IHVpbnQ2NDoKX19wdXlhX2FyYzRfcm91dGVyX186CiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQzL2NvbnRyYWN0LnB5OjExCiAgICAvLyBjbGFzcyBUZXN0UHV5YUJveGVzKEFSQzRDb250cmFjdCk6CiAgICBwcm90byAwIDEKICAgIHR4biBOdW1BcHBBcmdzCiAgICBieiBfX3B1eWFfYXJjNF9yb3V0ZXJfX19iYXJlX3JvdXRpbmdAMTAKICAgIHB1c2hieXRlc3MgMHgyMDJmYTczYSAweGRmN2VlYTRhIDB4MzY4OGVkMmMgMHg1ZDE3MjBkZCAweGY4MDY2NjVjIDB4ODFkMjYwZTIgLy8gbWV0aG9kICJzZXRfYm94X2J5dGVzKHN0cmluZyxieXRlW10pdm9pZCIsIG1ldGhvZCAic2V0X2JveF9zdHIoc3RyaW5nLHN0cmluZyl2b2lkIiwgbWV0aG9kICJzZXRfYm94X2ludChzdHJpbmcsdWludDMyKXZvaWQiLCBtZXRob2QgInNldF9ib3hfaW50NTEyKHN0cmluZyx1aW50NTEyKXZvaWQiLCBtZXRob2QgInNldF9ib3hfc3RhdGljKHN0cmluZyxieXRlWzRdKXZvaWQiLCBtZXRob2QgInNldF9zdHJ1Y3Qoc3RyaW5nLChzdHJpbmcsdWludDY0KSl2b2lkIgogICAgdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMAogICAgbWF0Y2ggX19wdXlhX2FyYzRfcm91dGVyX19fc2V0X2JveF9ieXRlc19yb3V0ZUAyIF9fcHV5YV9hcmM0X3JvdXRlcl9fX3NldF9ib3hfc3RyX3JvdXRlQDMgX19wdXlhX2FyYzRfcm91dGVyX19fc2V0X2JveF9pbnRfcm91dGVANCBfX3B1eWFfYXJjNF9yb3V0ZXJfX19zZXRfYm94X2ludDUxMl9yb3V0ZUA1IF9fcHV5YV9hcmM0X3JvdXRlcl9fX3NldF9ib3hfc3RhdGljX3JvdXRlQDYgX19wdXlhX2FyYzRfcm91dGVyX19fc2V0X3N0cnVjdF9yb3V0ZUA3CiAgICBpbnRjXzEgLy8gMAogICAgcmV0c3ViCgpfX3B1eWFfYXJjNF9yb3V0ZXJfX19zZXRfYm94X2J5dGVzX3JvdXRlQDI6CiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQzL2NvbnRyYWN0LnB5OjIwCiAgICAvLyBAYXJjNC5hYmltZXRob2QKICAgIHR4biBPbkNvbXBsZXRpb24KICAgICEKICAgIGFzc2VydCAvLyBPbkNvbXBsZXRpb24gaXMgbm90IE5vT3AKICAgIHR4biBBcHBsaWNhdGlvbklECiAgICBhc3NlcnQgLy8gY2FuIG9ubHkgY2FsbCB3aGVuIG5vdCBjcmVhdGluZwogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkMy9jb250cmFjdC5weToxMQogICAgLy8gY2xhc3MgVGVzdFB1eWFCb3hlcyhBUkM0Q29udHJhY3QpOgogICAgdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMQogICAgdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMgogICAgZXh0cmFjdCAyIDAKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZDMvY29udHJhY3QucHk6MjAKICAgIC8vIEBhcmM0LmFiaW1ldGhvZAogICAgY2FsbHN1YiBzZXRfYm94X2J5dGVzCiAgICBpbnRjXzAgLy8gMQogICAgcmV0c3ViCgpfX3B1eWFfYXJjNF9yb3V0ZXJfX19zZXRfYm94X3N0cl9yb3V0ZUAzOgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkMy9jb250cmFjdC5weToyNAogICAgLy8gQGFyYzQuYWJpbWV0aG9kCiAgICB0eG4gT25Db21wbGV0aW9uCiAgICAhCiAgICBhc3NlcnQgLy8gT25Db21wbGV0aW9uIGlzIG5vdCBOb09wCiAgICB0eG4gQXBwbGljYXRpb25JRAogICAgYXNzZXJ0IC8vIGNhbiBvbmx5IGNhbGwgd2hlbiBub3QgY3JlYXRpbmcKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZDMvY29udHJhY3QucHk6MTEKICAgIC8vIGNsYXNzIFRlc3RQdXlhQm94ZXMoQVJDNENvbnRyYWN0KToKICAgIHR4bmEgQXBwbGljYXRpb25BcmdzIDEKICAgIHR4bmEgQXBwbGljYXRpb25BcmdzIDIKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZDMvY29udHJhY3QucHk6MjQKICAgIC8vIEBhcmM0LmFiaW1ldGhvZAogICAgY2FsbHN1YiBzZXRfYm94X3N0cgogICAgaW50Y18wIC8vIDEKICAgIHJldHN1YgoKX19wdXlhX2FyYzRfcm91dGVyX19fc2V0X2JveF9pbnRfcm91dGVANDoKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZDMvY29udHJhY3QucHk6MjgKICAgIC8vIEBhcmM0LmFiaW1ldGhvZAogICAgdHhuIE9uQ29tcGxldGlvbgogICAgIQogICAgYXNzZXJ0IC8vIE9uQ29tcGxldGlvbiBpcyBub3QgTm9PcAogICAgdHhuIEFwcGxpY2F0aW9uSUQKICAgIGFzc2VydCAvLyBjYW4gb25seSBjYWxsIHdoZW4gbm90IGNyZWF0aW5nCiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQzL2NvbnRyYWN0LnB5OjExCiAgICAvLyBjbGFzcyBUZXN0UHV5YUJveGVzKEFSQzRDb250cmFjdCk6CiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAxCiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAyCiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQzL2NvbnRyYWN0LnB5OjI4CiAgICAvLyBAYXJjNC5hYmltZXRob2QKICAgIGNhbGxzdWIgc2V0X2JveF9pbnQKICAgIGludGNfMCAvLyAxCiAgICByZXRzdWIKCl9fcHV5YV9hcmM0X3JvdXRlcl9fX3NldF9ib3hfaW50NTEyX3JvdXRlQDU6CiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQzL2NvbnRyYWN0LnB5OjMyCiAgICAvLyBAYXJjNC5hYmltZXRob2QKICAgIHR4biBPbkNvbXBsZXRpb24KICAgICEKICAgIGFzc2VydCAvLyBPbkNvbXBsZXRpb24gaXMgbm90IE5vT3AKICAgIHR4biBBcHBsaWNhdGlvbklECiAgICBhc3NlcnQgLy8gY2FuIG9ubHkgY2FsbCB3aGVuIG5vdCBjcmVhdGluZwogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkMy9jb250cmFjdC5weToxMQogICAgLy8gY2xhc3MgVGVzdFB1eWFCb3hlcyhBUkM0Q29udHJhY3QpOgogICAgdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMQogICAgdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkMy9jb250cmFjdC5weTozMgogICAgLy8gQGFyYzQuYWJpbWV0aG9kCiAgICBjYWxsc3ViIHNldF9ib3hfaW50NTEyCiAgICBpbnRjXzAgLy8gMQogICAgcmV0c3ViCgpfX3B1eWFfYXJjNF9yb3V0ZXJfX19zZXRfYm94X3N0YXRpY19yb3V0ZUA2OgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkMy9jb250cmFjdC5weTozNgogICAgLy8gQGFyYzQuYWJpbWV0aG9kCiAgICB0eG4gT25Db21wbGV0aW9uCiAgICAhCiAgICBhc3NlcnQgLy8gT25Db21wbGV0aW9uIGlzIG5vdCBOb09wCiAgICB0eG4gQXBwbGljYXRpb25JRAogICAgYXNzZXJ0IC8vIGNhbiBvbmx5IGNhbGwgd2hlbiBub3QgY3JlYXRpbmcKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZDMvY29udHJhY3QucHk6MTEKICAgIC8vIGNsYXNzIFRlc3RQdXlhQm94ZXMoQVJDNENvbnRyYWN0KToKICAgIHR4bmEgQXBwbGljYXRpb25BcmdzIDEKICAgIHR4bmEgQXBwbGljYXRpb25BcmdzIDIKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZDMvY29udHJhY3QucHk6MzYKICAgIC8vIEBhcmM0LmFiaW1ldGhvZAogICAgY2FsbHN1YiBzZXRfYm94X3N0YXRpYwogICAgaW50Y18wIC8vIDEKICAgIHJldHN1YgoKX19wdXlhX2FyYzRfcm91dGVyX19fc2V0X3N0cnVjdF9yb3V0ZUA3OgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkMy9jb250cmFjdC5weTo0MgogICAgLy8gQGFyYzQuYWJpbWV0aG9kKCkKICAgIHR4biBPbkNvbXBsZXRpb24KICAgICEKICAgIGFzc2VydCAvLyBPbkNvbXBsZXRpb24gaXMgbm90IE5vT3AKICAgIHR4biBBcHBsaWNhdGlvbklECiAgICBhc3NlcnQgLy8gY2FuIG9ubHkgY2FsbCB3aGVuIG5vdCBjcmVhdGluZwogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkMy9jb250cmFjdC5weToxMQogICAgLy8gY2xhc3MgVGVzdFB1eWFCb3hlcyhBUkM0Q29udHJhY3QpOgogICAgdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMQogICAgdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkMy9jb250cmFjdC5weTo0MgogICAgLy8gQGFyYzQuYWJpbWV0aG9kKCkKICAgIGNhbGxzdWIgc2V0X3N0cnVjdAogICAgaW50Y18wIC8vIDEKICAgIHJldHN1YgoKX19wdXlhX2FyYzRfcm91dGVyX19fYmFyZV9yb3V0aW5nQDEwOgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkMy9jb250cmFjdC5weToxMQogICAgLy8gY2xhc3MgVGVzdFB1eWFCb3hlcyhBUkM0Q29udHJhY3QpOgogICAgdHhuIE9uQ29tcGxldGlvbgogICAgYm56IF9fcHV5YV9hcmM0X3JvdXRlcl9fX2FmdGVyX2lmX2Vsc2VAMTQKICAgIHR4biBBcHBsaWNhdGlvbklECiAgICAhCiAgICBhc3NlcnQgLy8gY2FuIG9ubHkgY2FsbCB3aGVuIGNyZWF0aW5nCiAgICBpbnRjXzAgLy8gMQogICAgcmV0c3ViCgpfX3B1eWFfYXJjNF9yb3V0ZXJfX19hZnRlcl9pZl9lbHNlQDE0OgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkMy9jb250cmFjdC5weToxMQogICAgLy8gY2xhc3MgVGVzdFB1eWFCb3hlcyhBUkM0Q29udHJhY3QpOgogICAgaW50Y18xIC8vIDAKICAgIHJldHN1YgoKCi8vIHNtYXJ0X2NvbnRyYWN0cy5oZWxsb193b3JsZDMuY29udHJhY3QuVGVzdFB1eWFCb3hlcy5zZXRfYm94X2J5dGVzKG5hbWU6IGJ5dGVzLCB2YWx1ZTogYnl0ZXMpIC0+IHZvaWQ6CnNldF9ib3hfYnl0ZXM6CiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQzL2NvbnRyYWN0LnB5OjIwLTIxCiAgICAvLyBAYXJjNC5hYmltZXRob2QKICAgIC8vIGRlZiBzZXRfYm94X2J5dGVzKHNlbGYsIG5hbWU6IGFyYzQuU3RyaW5nLCB2YWx1ZTogQnl0ZXMpIC0+IE5vbmU6CiAgICBwcm90byAyIDAKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZDMvY29udHJhY3QucHk6MjIKICAgIC8vIHNlbGYuYm94X2J5dGVzW25hbWVdID0gdmFsdWUKICAgIHB1c2hieXRlcyAiYm94X2J5dGVzIgogICAgZnJhbWVfZGlnIC0yCiAgICBjb25jYXQKICAgIGR1cAogICAgYm94X2RlbAogICAgcG9wCiAgICBmcmFtZV9kaWcgLTEKICAgIGJveF9wdXQKICAgIHJldHN1YgoKCi8vIHNtYXJ0X2NvbnRyYWN0cy5oZWxsb193b3JsZDMuY29udHJhY3QuVGVzdFB1eWFCb3hlcy5zZXRfYm94X3N0cihuYW1lOiBieXRlcywgdmFsdWU6IGJ5dGVzKSAtPiB2b2lkOgpzZXRfYm94X3N0cjoKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZDMvY29udHJhY3QucHk6MjQtMjUKICAgIC8vIEBhcmM0LmFiaW1ldGhvZAogICAgLy8gZGVmIHNldF9ib3hfc3RyKHNlbGYsIG5hbWU6IGFyYzQuU3RyaW5nLCB2YWx1ZTogYXJjNC5TdHJpbmcpIC0+IE5vbmU6CiAgICBwcm90byAyIDAKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZDMvY29udHJhY3QucHk6MjYKICAgIC8vIHNlbGYuYm94X3N0cltuYW1lXSA9IHZhbHVlCiAgICBwdXNoYnl0ZXMgImJveF9zdHIiCiAgICBmcmFtZV9kaWcgLTIKICAgIGNvbmNhdAogICAgZHVwCiAgICBib3hfZGVsCiAgICBwb3AKICAgIGZyYW1lX2RpZyAtMQogICAgYm94X3B1dAogICAgcmV0c3ViCgoKLy8gc21hcnRfY29udHJhY3RzLmhlbGxvX3dvcmxkMy5jb250cmFjdC5UZXN0UHV5YUJveGVzLnNldF9ib3hfaW50KG5hbWU6IGJ5dGVzLCB2YWx1ZTogYnl0ZXMpIC0+IHZvaWQ6CnNldF9ib3hfaW50OgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkMy9jb250cmFjdC5weToyOC0yOQogICAgLy8gQGFyYzQuYWJpbWV0aG9kCiAgICAvLyBkZWYgc2V0X2JveF9pbnQoc2VsZiwgbmFtZTogYXJjNC5TdHJpbmcsIHZhbHVlOiBhcmM0LlVJbnQzMikgLT4gTm9uZToKICAgIHByb3RvIDIgMAogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkMy9jb250cmFjdC5weTozMAogICAgLy8gc2VsZi5ib3hfaW50W25hbWVdID0gdmFsdWUKICAgIHB1c2hieXRlcyAiYm94X2ludCIKICAgIGZyYW1lX2RpZyAtMgogICAgY29uY2F0CiAgICBmcmFtZV9kaWcgLTEKICAgIGJveF9wdXQKICAgIHJldHN1YgoKCi8vIHNtYXJ0X2NvbnRyYWN0cy5oZWxsb193b3JsZDMuY29udHJhY3QuVGVzdFB1eWFCb3hlcy5zZXRfYm94X2ludDUxMihuYW1lOiBieXRlcywgdmFsdWU6IGJ5dGVzKSAtPiB2b2lkOgpzZXRfYm94X2ludDUxMjoKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZDMvY29udHJhY3QucHk6MzItMzMKICAgIC8vIEBhcmM0LmFiaW1ldGhvZAogICAgLy8gZGVmIHNldF9ib3hfaW50NTEyKHNlbGYsIG5hbWU6IGFyYzQuU3RyaW5nLCB2YWx1ZTogYXJjNC5VSW50NTEyKSAtPiBOb25lOgogICAgcHJvdG8gMiAwCiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQzL2NvbnRyYWN0LnB5OjM0CiAgICAvLyBzZWxmLmJveF9pbnQ1MTJbbmFtZV0gPSB2YWx1ZQogICAgcHVzaGJ5dGVzICJib3hfaW50NTEyIgogICAgZnJhbWVfZGlnIC0yCiAgICBjb25jYXQKICAgIGZyYW1lX2RpZyAtMQogICAgYm94X3B1dAogICAgcmV0c3ViCgoKLy8gc21hcnRfY29udHJhY3RzLmhlbGxvX3dvcmxkMy5jb250cmFjdC5UZXN0UHV5YUJveGVzLnNldF9ib3hfc3RhdGljKG5hbWU6IGJ5dGVzLCB2YWx1ZTogYnl0ZXMpIC0+IHZvaWQ6CnNldF9ib3hfc3RhdGljOgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkMy9jb250cmFjdC5weTozNi0zOQogICAgLy8gQGFyYzQuYWJpbWV0aG9kCiAgICAvLyBkZWYgc2V0X2JveF9zdGF0aWMoCiAgICAvLyAgICAgc2VsZiwgbmFtZTogYXJjNC5TdHJpbmcsIHZhbHVlOiBhcmM0LlN0YXRpY0FycmF5W2FyYzQuQnl0ZSwgTGl0ZXJhbFs0XV0KICAgIC8vICkgLT4gTm9uZToKICAgIHByb3RvIDIgMAogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkMy9jb250cmFjdC5weTo0MAogICAgLy8gc2VsZi5ib3hfc3RhdGljW25hbWVdID0gdmFsdWUuY29weSgpCiAgICBwdXNoYnl0ZXMgImJveF9zdGF0aWMiCiAgICBmcmFtZV9kaWcgLTIKICAgIGNvbmNhdAogICAgZnJhbWVfZGlnIC0xCiAgICBib3hfcHV0CiAgICByZXRzdWIKCgovLyBzbWFydF9jb250cmFjdHMuaGVsbG9fd29ybGQzLmNvbnRyYWN0LlRlc3RQdXlhQm94ZXMuc2V0X3N0cnVjdChuYW1lOiBieXRlcywgdmFsdWU6IGJ5dGVzKSAtPiB2b2lkOgpzZXRfc3RydWN0OgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkMy9jb250cmFjdC5weTo0Mi00MwogICAgLy8gQGFyYzQuYWJpbWV0aG9kKCkKICAgIC8vIGRlZiBzZXRfc3RydWN0KHNlbGYsIG5hbWU6IGFyYzQuU3RyaW5nLCB2YWx1ZTogRHVtbXlTdHJ1Y3QpIC0+IE5vbmU6CiAgICBwcm90byAyIDAKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZDMvY29udHJhY3QucHk6NDQKICAgIC8vIGFzc2VydCBuYW1lLmJ5dGVzID09IHZhbHVlLm5hbWUuYnl0ZXMsICJOYW1lIG11c3QgbWF0Y2ggaWQgb2Ygc3RydWN0IgogICAgZnJhbWVfZGlnIC0xCiAgICBpbnRjXzEgLy8gMAogICAgZXh0cmFjdF91aW50MTYKICAgIGZyYW1lX2RpZyAtMQogICAgbGVuCiAgICBmcmFtZV9kaWcgLTEKICAgIGNvdmVyIDIKICAgIHN1YnN0cmluZzMKICAgIGZyYW1lX2RpZyAtMgogICAgPT0KICAgIGFzc2VydCAvLyBOYW1lIG11c3QgbWF0Y2ggaWQgb2Ygc3RydWN0CiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQzL2NvbnRyYWN0LnB5OjQ1CiAgICAvLyBvcC5Cb3gucHV0KG5hbWUuYnl0ZXMsIHZhbHVlLmJ5dGVzKQogICAgZnJhbWVfZGlnIC0yCiAgICBmcmFtZV9kaWcgLTEKICAgIGJveF9wdXQKICAgIHJldHN1Ygo=", - "clear": "I3ByYWdtYSB2ZXJzaW9uIDEwCgpzbWFydF9jb250cmFjdHMuaGVsbG9fd29ybGQzLmNvbnRyYWN0LlRlc3RQdXlhQm94ZXMuY2xlYXJfc3RhdGVfcHJvZ3JhbToKICAgIHB1c2hpbnQgMSAvLyAxCiAgICByZXR1cm4K" + "approval": "I3ByYWdtYSB2ZXJzaW9uIDExCiNwcmFnbWEgdHlwZXRyYWNrIGZhbHNlCgovLyBhbGdvcHkuYXJjNC5BUkM0Q29udHJhY3QuYXBwcm92YWxfcHJvZ3JhbSgpIC0+IHVpbnQ2NDoKbWFpbjoKICAgIGludGNibG9jayAwIDIgMSA0CiAgICBieXRlY2Jsb2NrICJleHRlcm5hbCIKICAgIHR4biBBcHBsaWNhdGlvbklECiAgICBibnogbWFpbl9hZnRlcl9pZl9lbHNlQDIKICAgIC8vIHRlc3RzL2FydGlmYWN0cy90ZXN0aW5nX2FwcF9wdXlhL2NvbnRyYWN0LnB5OjI3CiAgICAvLyBzZWxmLmV4dGVybmFsID0gQXBwbGljYXRpb24oMCkKICAgIGJ5dGVjXzAgLy8gImV4dGVybmFsIgogICAgaW50Y18wIC8vIDAKICAgIGFwcF9nbG9iYWxfcHV0CgptYWluX2FmdGVyX2lmX2Vsc2VAMjoKICAgIC8vIHRlc3RzL2FydGlmYWN0cy90ZXN0aW5nX2FwcF9wdXlhL2NvbnRyYWN0LnB5OjE5CiAgICAvLyBjbGFzcyBUZXN0UHV5YUJveGVzKEFSQzRDb250cmFjdCk6CiAgICB0eG4gTnVtQXBwQXJncwogICAgYnogbWFpbl9fX2FsZ29weV9kZWZhdWx0X2NyZWF0ZUAxNwogICAgdHhuIE9uQ29tcGxldGlvbgogICAgIQogICAgYXNzZXJ0IC8vIE9uQ29tcGxldGlvbiBtdXN0IGJlIE5vT3AKICAgIHR4biBBcHBsaWNhdGlvbklECiAgICBhc3NlcnQKICAgIHB1c2hieXRlc3MgMHgyMDJmYTczYSAweGRmN2VlYTRhIDB4MzY4OGVkMmMgMHg1ZDE3MjBkZCAweGY4MDY2NjVjIDB4ODFkMjYwZTIgMHgyYzI3OGQ0ZiAweGE1NGUyMDJjIC8vIG1ldGhvZCAic2V0X2JveF9ieXRlcyhzdHJpbmcsYnl0ZVtdKXZvaWQiLCBtZXRob2QgInNldF9ib3hfc3RyKHN0cmluZyxzdHJpbmcpdm9pZCIsIG1ldGhvZCAic2V0X2JveF9pbnQoc3RyaW5nLHVpbnQzMil2b2lkIiwgbWV0aG9kICJzZXRfYm94X2ludDUxMihzdHJpbmcsdWludDUxMil2b2lkIiwgbWV0aG9kICJzZXRfYm94X3N0YXRpYyhzdHJpbmcsYnl0ZVs0XSl2b2lkIiwgbWV0aG9kICJzZXRfc3RydWN0KHN0cmluZywoc3RyaW5nLHVpbnQ2NCkpdm9pZCIsIG1ldGhvZCAiYm9vdHN0cmFwX2V4dGVybmFsX2FwcCgpdWludDY0IiwgbWV0aG9kICJzZXRfZXh0ZXJuYWxfYm94KCl2b2lkIgogICAgdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMAogICAgbWF0Y2ggc2V0X2JveF9ieXRlcyBzZXRfYm94X3N0ciBzZXRfYm94X2ludCBzZXRfYm94X2ludDUxMiBzZXRfYm94X3N0YXRpYyBzZXRfc3RydWN0IGJvb3RzdHJhcF9leHRlcm5hbF9hcHAgc2V0X2V4dGVybmFsX2JveAogICAgZXJyCgptYWluX19fYWxnb3B5X2RlZmF1bHRfY3JlYXRlQDE3OgogICAgdHhuIE9uQ29tcGxldGlvbgogICAgIQogICAgdHhuIEFwcGxpY2F0aW9uSUQKICAgICEKICAgICYmCiAgICByZXR1cm4gLy8gb24gZXJyb3I6IE9uQ29tcGxldGlvbiBtdXN0IGJlIE5vT3AgJiYgY2FuIG9ubHkgY2FsbCB3aGVuIGNyZWF0aW5nCgoKLy8gdGVzdHMuYXJ0aWZhY3RzLnRlc3RpbmdfYXBwX3B1eWEuY29udHJhY3QuVGVzdFB1eWFCb3hlcy5zZXRfYm94X2J5dGVzW3JvdXRpbmddKCkgLT4gdm9pZDoKc2V0X2JveF9ieXRlczoKICAgIC8vIHRlc3RzL2FydGlmYWN0cy90ZXN0aW5nX2FwcF9wdXlhL2NvbnRyYWN0LnB5OjI5CiAgICAvLyBAYXJjNC5hYmltZXRob2QKICAgIHR4bmEgQXBwbGljYXRpb25BcmdzIDEKICAgIGR1cAogICAgaW50Y18wIC8vIDAKICAgIGV4dHJhY3RfdWludDE2CiAgICBpbnRjXzEgLy8gMgogICAgKwogICAgZGlnIDEKICAgIGxlbgogICAgPT0KICAgIGFzc2VydCAvLyBpbnZhbGlkIG51bWJlciBvZiBieXRlcyBmb3IgKGxlbit1dGY4W10pCiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAyCiAgICBkdXAKICAgIGludGNfMCAvLyAwCiAgICBleHRyYWN0X3VpbnQxNgogICAgaW50Y18xIC8vIDIKICAgICsKICAgIGRpZyAxCiAgICBsZW4KICAgID09CiAgICBhc3NlcnQgLy8gaW52YWxpZCBudW1iZXIgb2YgYnl0ZXMgZm9yIChsZW4rdWludDhbXSkKICAgIGV4dHJhY3QgMiAwCiAgICAvLyB0ZXN0cy9hcnRpZmFjdHMvdGVzdGluZ19hcHBfcHV5YS9jb250cmFjdC5weTozMQogICAgLy8gc2VsZi5ib3hfYnl0ZXNbbmFtZV0gPSB2YWx1ZQogICAgcHVzaGJ5dGVzICJib3hfYnl0ZXMiCiAgICB1bmNvdmVyIDIKICAgIGNvbmNhdAogICAgZHVwCiAgICBib3hfZGVsCiAgICBwb3AKICAgIHN3YXAKICAgIGJveF9wdXQKICAgIC8vIHRlc3RzL2FydGlmYWN0cy90ZXN0aW5nX2FwcF9wdXlhL2NvbnRyYWN0LnB5OjI5CiAgICAvLyBAYXJjNC5hYmltZXRob2QKICAgIGludGNfMiAvLyAxCiAgICByZXR1cm4KCgovLyB0ZXN0cy5hcnRpZmFjdHMudGVzdGluZ19hcHBfcHV5YS5jb250cmFjdC5UZXN0UHV5YUJveGVzLnNldF9ib3hfc3RyW3JvdXRpbmddKCkgLT4gdm9pZDoKc2V0X2JveF9zdHI6CiAgICAvLyB0ZXN0cy9hcnRpZmFjdHMvdGVzdGluZ19hcHBfcHV5YS9jb250cmFjdC5weTozMwogICAgLy8gQGFyYzQuYWJpbWV0aG9kCiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAxCiAgICBkdXAKICAgIGludGNfMCAvLyAwCiAgICBleHRyYWN0X3VpbnQxNgogICAgaW50Y18xIC8vIDIKICAgICsKICAgIGRpZyAxCiAgICBsZW4KICAgID09CiAgICBhc3NlcnQgLy8gaW52YWxpZCBudW1iZXIgb2YgYnl0ZXMgZm9yIChsZW4rdXRmOFtdKQogICAgdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMgogICAgZHVwCiAgICBpbnRjXzAgLy8gMAogICAgZXh0cmFjdF91aW50MTYKICAgIGludGNfMSAvLyAyCiAgICArCiAgICBkaWcgMQogICAgbGVuCiAgICA9PQogICAgYXNzZXJ0IC8vIGludmFsaWQgbnVtYmVyIG9mIGJ5dGVzIGZvciAobGVuK3V0ZjhbXSkKICAgIC8vIHRlc3RzL2FydGlmYWN0cy90ZXN0aW5nX2FwcF9wdXlhL2NvbnRyYWN0LnB5OjM1CiAgICAvLyBzZWxmLmJveF9zdHJbbmFtZV0gPSB2YWx1ZQogICAgcHVzaGJ5dGVzICJib3hfc3RyIgogICAgdW5jb3ZlciAyCiAgICBjb25jYXQKICAgIGR1cAogICAgYm94X2RlbAogICAgcG9wCiAgICBzd2FwCiAgICBib3hfcHV0CiAgICAvLyB0ZXN0cy9hcnRpZmFjdHMvdGVzdGluZ19hcHBfcHV5YS9jb250cmFjdC5weTozMwogICAgLy8gQGFyYzQuYWJpbWV0aG9kCiAgICBpbnRjXzIgLy8gMQogICAgcmV0dXJuCgoKLy8gdGVzdHMuYXJ0aWZhY3RzLnRlc3RpbmdfYXBwX3B1eWEuY29udHJhY3QuVGVzdFB1eWFCb3hlcy5zZXRfYm94X2ludFtyb3V0aW5nXSgpIC0+IHZvaWQ6CnNldF9ib3hfaW50OgogICAgLy8gdGVzdHMvYXJ0aWZhY3RzL3Rlc3RpbmdfYXBwX3B1eWEvY29udHJhY3QucHk6MzcKICAgIC8vIEBhcmM0LmFiaW1ldGhvZAogICAgdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMQogICAgZHVwCiAgICBpbnRjXzAgLy8gMAogICAgZXh0cmFjdF91aW50MTYKICAgIGludGNfMSAvLyAyCiAgICArCiAgICBkaWcgMQogICAgbGVuCiAgICA9PQogICAgYXNzZXJ0IC8vIGludmFsaWQgbnVtYmVyIG9mIGJ5dGVzIGZvciAobGVuK3V0ZjhbXSkKICAgIHR4bmEgQXBwbGljYXRpb25BcmdzIDIKICAgIGR1cAogICAgbGVuCiAgICBpbnRjXzMgLy8gNAogICAgPT0KICAgIGFzc2VydCAvLyBpbnZhbGlkIG51bWJlciBvZiBieXRlcyBmb3IgdWludDMyCiAgICAvLyB0ZXN0cy9hcnRpZmFjdHMvdGVzdGluZ19hcHBfcHV5YS9jb250cmFjdC5weTozOQogICAgLy8gc2VsZi5ib3hfaW50W25hbWVdID0gdmFsdWUKICAgIHB1c2hieXRlcyAiYm94X2ludCIKICAgIHVuY292ZXIgMgogICAgY29uY2F0CiAgICBzd2FwCiAgICBib3hfcHV0CiAgICAvLyB0ZXN0cy9hcnRpZmFjdHMvdGVzdGluZ19hcHBfcHV5YS9jb250cmFjdC5weTozNwogICAgLy8gQGFyYzQuYWJpbWV0aG9kCiAgICBpbnRjXzIgLy8gMQogICAgcmV0dXJuCgoKLy8gdGVzdHMuYXJ0aWZhY3RzLnRlc3RpbmdfYXBwX3B1eWEuY29udHJhY3QuVGVzdFB1eWFCb3hlcy5zZXRfYm94X2ludDUxMltyb3V0aW5nXSgpIC0+IHZvaWQ6CnNldF9ib3hfaW50NTEyOgogICAgLy8gdGVzdHMvYXJ0aWZhY3RzL3Rlc3RpbmdfYXBwX3B1eWEvY29udHJhY3QucHk6NDEKICAgIC8vIEBhcmM0LmFiaW1ldGhvZAogICAgdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMQogICAgZHVwCiAgICBpbnRjXzAgLy8gMAogICAgZXh0cmFjdF91aW50MTYKICAgIGludGNfMSAvLyAyCiAgICArCiAgICBkaWcgMQogICAgbGVuCiAgICA9PQogICAgYXNzZXJ0IC8vIGludmFsaWQgbnVtYmVyIG9mIGJ5dGVzIGZvciAobGVuK3V0ZjhbXSkKICAgIHR4bmEgQXBwbGljYXRpb25BcmdzIDIKICAgIGR1cAogICAgbGVuCiAgICBwdXNoaW50IDY0IC8vIDY0CiAgICA9PQogICAgYXNzZXJ0IC8vIGludmFsaWQgbnVtYmVyIG9mIGJ5dGVzIGZvciB1aW50NTEyCiAgICAvLyB0ZXN0cy9hcnRpZmFjdHMvdGVzdGluZ19hcHBfcHV5YS9jb250cmFjdC5weTo0MwogICAgLy8gc2VsZi5ib3hfaW50NTEyW25hbWVdID0gdmFsdWUKICAgIHB1c2hieXRlcyAiYm94X2ludDUxMiIKICAgIHVuY292ZXIgMgogICAgY29uY2F0CiAgICBzd2FwCiAgICBib3hfcHV0CiAgICAvLyB0ZXN0cy9hcnRpZmFjdHMvdGVzdGluZ19hcHBfcHV5YS9jb250cmFjdC5weTo0MQogICAgLy8gQGFyYzQuYWJpbWV0aG9kCiAgICBpbnRjXzIgLy8gMQogICAgcmV0dXJuCgoKLy8gdGVzdHMuYXJ0aWZhY3RzLnRlc3RpbmdfYXBwX3B1eWEuY29udHJhY3QuVGVzdFB1eWFCb3hlcy5zZXRfYm94X3N0YXRpY1tyb3V0aW5nXSgpIC0+IHZvaWQ6CnNldF9ib3hfc3RhdGljOgogICAgLy8gdGVzdHMvYXJ0aWZhY3RzL3Rlc3RpbmdfYXBwX3B1eWEvY29udHJhY3QucHk6NDUKICAgIC8vIEBhcmM0LmFiaW1ldGhvZAogICAgdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMQogICAgZHVwCiAgICBpbnRjXzAgLy8gMAogICAgZXh0cmFjdF91aW50MTYKICAgIGludGNfMSAvLyAyCiAgICArCiAgICBkaWcgMQogICAgbGVuCiAgICA9PQogICAgYXNzZXJ0IC8vIGludmFsaWQgbnVtYmVyIG9mIGJ5dGVzIGZvciAobGVuK3V0ZjhbXSkKICAgIHR4bmEgQXBwbGljYXRpb25BcmdzIDIKICAgIGR1cAogICAgbGVuCiAgICBpbnRjXzMgLy8gNAogICAgPT0KICAgIGFzc2VydCAvLyBpbnZhbGlkIG51bWJlciBvZiBieXRlcyBmb3IgdWludDhbNF0KICAgIC8vIHRlc3RzL2FydGlmYWN0cy90ZXN0aW5nX2FwcF9wdXlhL2NvbnRyYWN0LnB5OjQ3CiAgICAvLyBzZWxmLmJveF9zdGF0aWNbbmFtZV0gPSB2YWx1ZS5jb3B5KCkKICAgIHB1c2hieXRlcyAiYm94X3N0YXRpYyIKICAgIHVuY292ZXIgMgogICAgY29uY2F0CiAgICBzd2FwCiAgICBib3hfcHV0CiAgICAvLyB0ZXN0cy9hcnRpZmFjdHMvdGVzdGluZ19hcHBfcHV5YS9jb250cmFjdC5weTo0NQogICAgLy8gQGFyYzQuYWJpbWV0aG9kCiAgICBpbnRjXzIgLy8gMQogICAgcmV0dXJuCgoKLy8gdGVzdHMuYXJ0aWZhY3RzLnRlc3RpbmdfYXBwX3B1eWEuY29udHJhY3QuVGVzdFB1eWFCb3hlcy5zZXRfc3RydWN0W3JvdXRpbmddKCkgLT4gdm9pZDoKc2V0X3N0cnVjdDoKICAgIC8vIHRlc3RzL2FydGlmYWN0cy90ZXN0aW5nX2FwcF9wdXlhL2NvbnRyYWN0LnB5OjQ5CiAgICAvLyBAYXJjNC5hYmltZXRob2QoKQogICAgdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMQogICAgZHVwCiAgICBpbnRjXzAgLy8gMAogICAgZXh0cmFjdF91aW50MTYKICAgIGludGNfMSAvLyAyCiAgICArCiAgICBkaWcgMQogICAgbGVuCiAgICA9PQogICAgYXNzZXJ0IC8vIGludmFsaWQgbnVtYmVyIG9mIGJ5dGVzIGZvciAobGVuK3V0ZjhbXSkKICAgIHR4bmEgQXBwbGljYXRpb25BcmdzIDIKICAgIC8vIHRlc3RzL2FydGlmYWN0cy90ZXN0aW5nX2FwcF9wdXlhL2NvbnRyYWN0LnB5OjUxCiAgICAvLyBhc3NlcnQgbmFtZS5ieXRlcyA9PSB2YWx1ZS5uYW1lLmJ5dGVzLCAiTmFtZSBtdXN0IG1hdGNoIGlkIG9mIHN0cnVjdCIKICAgIGR1cAogICAgaW50Y18wIC8vIDAKICAgIGV4dHJhY3RfdWludDE2CiAgICBkaWcgMQogICAgbGVuCiAgICBkaWcgMgogICAgY292ZXIgMgogICAgc3Vic3RyaW5nMwogICAgZGlnIDIKICAgID09CiAgICBhc3NlcnQgLy8gTmFtZSBtdXN0IG1hdGNoIGlkIG9mIHN0cnVjdAogICAgLy8gdGVzdHMvYXJ0aWZhY3RzL3Rlc3RpbmdfYXBwX3B1eWEvY29udHJhY3QucHk6NTIKICAgIC8vIG9wLkJveC5wdXQobmFtZS5ieXRlcywgdmFsdWUuYnl0ZXMpCiAgICBib3hfcHV0CiAgICAvLyB0ZXN0cy9hcnRpZmFjdHMvdGVzdGluZ19hcHBfcHV5YS9jb250cmFjdC5weTo0OQogICAgLy8gQGFyYzQuYWJpbWV0aG9kKCkKICAgIGludGNfMiAvLyAxCiAgICByZXR1cm4KCgovLyB0ZXN0cy5hcnRpZmFjdHMudGVzdGluZ19hcHBfcHV5YS5jb250cmFjdC5UZXN0UHV5YUJveGVzLmJvb3RzdHJhcF9leHRlcm5hbF9hcHBbcm91dGluZ10oKSAtPiB2b2lkOgpib290c3RyYXBfZXh0ZXJuYWxfYXBwOgogICAgLy8gdGVzdHMvYXJ0aWZhY3RzL3Rlc3RpbmdfYXBwX3B1eWEvY29udHJhY3QucHk6NTYKICAgIC8vIHNlbGYuZXh0ZXJuYWwgPSBhcmM0LmFyYzRfY3JlYXRlKEV4dGVybmFsKS5jcmVhdGVkX2FwcAogICAgaXR4bl9iZWdpbgogICAgcHVzaGJ5dGVzIGJhc2U2NChDNEVCUXc9PSkKICAgIGl0eG5fZmllbGQgQ2xlYXJTdGF0ZVByb2dyYW1QYWdlcwogICAgcHVzaGJ5dGVzIGJhc2U2NChDeVlCQTJKdmVERWJRUUFZZ0FSTlBrUHNOaG9BamdFQUFRQXhHUlF4R0JCRVFnQUlNUmtVTVJnVUVFTW92RWdvZ0FObWIyKy9nUUZEKQogICAgaXR4bl9maWVsZCBBcHByb3ZhbFByb2dyYW1QYWdlcwogICAgcHVzaGludCA2IC8vIGFwcGwKICAgIGl0eG5fZmllbGQgVHlwZUVudW0KICAgIGludGNfMCAvLyAwCiAgICBpdHhuX2ZpZWxkIEZlZQogICAgaXR4bl9zdWJtaXQKICAgIGl0eG4gQ3JlYXRlZEFwcGxpY2F0aW9uSUQKICAgIGJ5dGVjXzAgLy8gImV4dGVybmFsIgogICAgZGlnIDEKICAgIGFwcF9nbG9iYWxfcHV0CiAgICAvLyB0ZXN0cy9hcnRpZmFjdHMvdGVzdGluZ19hcHBfcHV5YS9jb250cmFjdC5weTo1NAogICAgLy8gQGFyYzQuYWJpbWV0aG9kCiAgICBpdG9iCiAgICBwdXNoYnl0ZXMgMHgxNTFmN2M3NQogICAgc3dhcAogICAgY29uY2F0CiAgICBsb2cKICAgIGludGNfMiAvLyAxCiAgICByZXR1cm4KCgovLyB0ZXN0cy5hcnRpZmFjdHMudGVzdGluZ19hcHBfcHV5YS5jb250cmFjdC5UZXN0UHV5YUJveGVzLnNldF9leHRlcm5hbF9ib3hbcm91dGluZ10oKSAtPiB2b2lkOgpzZXRfZXh0ZXJuYWxfYm94OgogICAgLy8gdGVzdHMvYXJ0aWZhY3RzL3Rlc3RpbmdfYXBwX3B1eWEvY29udHJhY3QucHk6NjEtNjQKICAgIC8vIGFyYzQuYWJpX2NhbGwoCiAgICAvLyAgICAgRXh0ZXJuYWwuc2V0X2JveCwKICAgIC8vICAgICBhcHBfaWQ9c2VsZi5leHRlcm5hbCwKICAgIC8vICkKICAgIGl0eG5fYmVnaW4KICAgIC8vIHRlc3RzL2FydGlmYWN0cy90ZXN0aW5nX2FwcF9wdXlhL2NvbnRyYWN0LnB5OjYzCiAgICAvLyBhcHBfaWQ9c2VsZi5leHRlcm5hbCwKICAgIGludGNfMCAvLyAwCiAgICBieXRlY18wIC8vICJleHRlcm5hbCIKICAgIGFwcF9nbG9iYWxfZ2V0X2V4CiAgICBhc3NlcnQgLy8gY2hlY2sgc2VsZi5leHRlcm5hbCBleGlzdHMKICAgIGl0eG5fZmllbGQgQXBwbGljYXRpb25JRAogICAgLy8gdGVzdHMvYXJ0aWZhY3RzL3Rlc3RpbmdfYXBwX3B1eWEvY29udHJhY3QucHk6NjEtNjQKICAgIC8vIGFyYzQuYWJpX2NhbGwoCiAgICAvLyAgICAgRXh0ZXJuYWwuc2V0X2JveCwKICAgIC8vICAgICBhcHBfaWQ9c2VsZi5leHRlcm5hbCwKICAgIC8vICkKICAgIHB1c2hieXRlcyAweDRkM2U0M2VjIC8vIG1ldGhvZCAic2V0X2JveCgpdm9pZCIKICAgIGl0eG5fZmllbGQgQXBwbGljYXRpb25BcmdzCiAgICBwdXNoaW50IDYgLy8gYXBwbAogICAgaXR4bl9maWVsZCBUeXBlRW51bQogICAgaW50Y18wIC8vIDAKICAgIGl0eG5fZmllbGQgRmVlCiAgICBpdHhuX3N1Ym1pdAogICAgLy8gdGVzdHMvYXJ0aWZhY3RzL3Rlc3RpbmdfYXBwX3B1eWEvY29udHJhY3QucHk6NTkKICAgIC8vIEBhcmM0LmFiaW1ldGhvZAogICAgaW50Y18yIC8vIDEKICAgIHJldHVybgo=", + "clear": "I3ByYWdtYSB2ZXJzaW9uIDExCiNwcmFnbWEgdHlwZXRyYWNrIGZhbHNlCgovLyBhbGdvcHkuYXJjNC5BUkM0Q29udHJhY3QuY2xlYXJfc3RhdGVfcHJvZ3JhbSgpIC0+IHVpbnQ2NDoKbWFpbjoKICAgIHB1c2hpbnQgMSAvLyAxCiAgICByZXR1cm4K" }, "state": { "global": { "num_byte_slices": 0, - "num_uints": 0 + "num_uints": 1 }, "local": { "num_byte_slices": 0, @@ -62,7 +72,12 @@ }, "schema": { "global": { - "declared": {}, + "declared": { + "external": { + "type": "uint64", + "key": "external" + } + }, "reserved": {} }, "local": { @@ -174,6 +189,22 @@ "returns": { "type": "void" } + }, + { + "name": "bootstrap_external_app", + "args": [], + "readonly": false, + "returns": { + "type": "uint64" + } + }, + { + "name": "set_external_box", + "args": [], + "readonly": false, + "returns": { + "type": "void" + } } ], "networks": {} @@ -181,4 +212,4 @@ "bare_call_config": { "no_op": "CREATE" } -} +} \ No newline at end of file diff --git a/tests/artifacts/testing_app_puya/contract.py b/tests/artifacts/testing_app_puya/contract.py index 7074dd6b..d2c22499 100644 --- a/tests/artifacts/testing_app_puya/contract.py +++ b/tests/artifacts/testing_app_puya/contract.py @@ -1,6 +1,6 @@ from typing import Literal -from algopy import ARC4Contract, BoxMap, Bytes, arc4, op +from algopy import Application, ARC4Contract, Box, BoxMap, Bytes, arc4, op class DummyStruct(arc4.Struct): @@ -8,6 +8,15 @@ class DummyStruct(arc4.Struct): id: arc4.UInt64 +class External(ARC4Contract): + def __init__(self) -> None: + self.box = Box(Bytes) + + @arc4.abimethod + def set_box(self) -> None: + self.box.value = Bytes(b"foo") + + class TestPuyaBoxes(ARC4Contract): def __init__(self) -> None: self.box_bytes = BoxMap(arc4.String, Bytes) @@ -16,6 +25,7 @@ def __init__(self) -> None: self.box_int = BoxMap(arc4.String, arc4.UInt32) self.box_int512 = BoxMap(arc4.String, arc4.UInt512) self.box_static = BoxMap(arc4.String, arc4.StaticArray[arc4.Byte, Literal[4]]) + self.external = Application(0) @arc4.abimethod def set_box_bytes(self, name: arc4.String, value: Bytes) -> None: @@ -41,3 +51,15 @@ def set_box_static(self, name: arc4.String, value: arc4.StaticArray[arc4.Byte, L def set_struct(self, name: arc4.String, value: DummyStruct) -> None: assert name.bytes == value.name.bytes, "Name must match id of struct" op.Box.put(name.bytes, value.bytes) + + @arc4.abimethod + def bootstrap_external_app(self) -> Application: + self.external = arc4.arc4_create(External).created_app + return self.external + + @arc4.abimethod + def set_external_box(self) -> None: + arc4.abi_call( + External.set_box, + app_id=self.external, + ) diff --git a/tests/transactions/test_resource_packing.py b/tests/transactions/test_resource_packing.py index e0f563cb..c3e3e516 100644 --- a/tests/transactions/test_resource_packing.py +++ b/tests/transactions/test_resource_packing.py @@ -428,3 +428,28 @@ def test_create_box_in_new_app(self, algorand: AlgorandClient, funded_account: S box_ref = result.transaction.application_call.boxes[0] if result.transaction.application_call.boxes else None assert box_ref is not None assert box_ref.app_index == 0 # type: ignore # noqa: PGH003 + + +def test_inner_txn_with_box(algorand: AlgorandClient, funded_account: SigningAccount) -> None: + spec = (Path(__file__).parent.parent / "artifacts" / "testing_app_puya" / "app_spec.arc32.json").read_text() + factory = algorand.client.get_app_factory( + app_spec=spec, + default_sender=funded_account.address, + ) + app_client, _ = factory.send.bare.create() + app_client.fund_app_account(FundAppAccountParams(amount=AlgoAmount.from_algo(1))) + result = app_client.send.call( + AppClientMethodCallParams(method="bootstrap_external_app", static_fee=AlgoAmount.from_micro_algo(3_000)) + ) + external_app_id = result.abi_return + assert isinstance(external_app_id, int), "expected app id" + external_app = algorand.app.get_by_id(external_app_id) + algorand.account.ensure_funded( + external_app.app_address, funded_account, min_spending_balance=AlgoAmount.from_algo(1) + ) + + app_client.send.call( + AppClientMethodCallParams(method="set_external_box", static_fee=AlgoAmount.from_micro_algo(2_000)) + ) + + assert algorand.app.get_box_value(external_app_id, "box") == b"foo" From 87a141ea50366266ca7e537ec96146359b1aeece Mon Sep 17 00:00:00 2001 From: Daniel McGregor Date: Wed, 15 Oct 2025 13:54:21 +0800 Subject: [PATCH 2/7] fix: add app id first when populating a box ref --- src/algokit_utils/transactions/transaction_composer.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/algokit_utils/transactions/transaction_composer.py b/src/algokit_utils/transactions/transaction_composer.py index 0f75f033..0b05e999 100644 --- a/src/algokit_utils/transactions/transaction_composer.py +++ b/src/algokit_utils/transactions/transaction_composer.py @@ -1041,13 +1041,15 @@ def is_appl_below_limit(t: TransactionWithSigner) -> bool: foreign_apps.append(app_id) app_txn.foreign_apps = foreign_apps elif ref_type == "box": + # ensure app_id is added before calling translate_box_reference + app_id = box_ref[0] + if app_id != 0: + foreign_apps = list(getattr(app_txn, "foreign_apps", []) or []) + foreign_apps.append(app_id) + app_txn.foreign_apps = foreign_apps boxes = list(getattr(app_txn, "boxes", []) or []) boxes.append(BoxReference.translate_box_reference(box_ref, app_txn.foreign_apps or [], app_txn.index)) # type: ignore[arg-type] app_txn.boxes = boxes - if box_ref[0] != 0: - foreign_apps = list(getattr(app_txn, "foreign_apps", []) or []) - foreign_apps.append(box_ref[0]) - app_txn.foreign_apps = foreign_apps elif ref_type == "asset": asset_id = int(cast(str | int, reference)) foreign_assets = list(getattr(app_txn, "foreign_assets", []) or []) From c934ed364247fd6968cb4b8b0f13b0c1f5489a00 Mon Sep 17 00:00:00 2001 From: Daniel McGregor Date: Wed, 15 Oct 2025 16:52:29 +0800 Subject: [PATCH 3/7] chore: ignore pip audit --- .github/workflows/check-python.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/check-python.yaml b/.github/workflows/check-python.yaml index 1aab5125..7a3042ef 100644 --- a/.github/workflows/check-python.yaml +++ b/.github/workflows/check-python.yaml @@ -23,14 +23,16 @@ jobs: - name: Audit with pip-audit run: | + # GHSA-4xh5-x5gv-qwph is safe to ignore since we are using a python version that is not affected + # can remove once pip has fix # audit non dev dependencies, no exclusions - poetry export --without=dev > requirements.txt && poetry run pip-audit -r requirements.txt + poetry export --without=dev > requirements.txt && poetry run pip-audit -r requirements.txt --ignore-vuln GHSA-4xh5-x5gv-qwph # audit all dependencies, with exclusions. # If a vulnerability is found in a dev dependency without an available fix, # it can be temporarily ignored by adding --ignore-vuln e.g. # --ignore-vuln "GHSA-hcpj-qp55-gfph" # GitPython vulnerability, dev only dependency - poetry run pip-audit + poetry run pip-audit --ignore-vuln GHSA-4xh5-x5gv-qwph - name: Check formatting with Ruff run: | From 0239ca21155d939a88d080eca09cd6dd897be68a Mon Sep 17 00:00:00 2001 From: Daniel McGregor Date: Wed, 15 Oct 2025 17:25:55 +0800 Subject: [PATCH 4/7] test: add test for an error due to a rejection --- tests/applications/test_app_client.py | 65 +++++++++---------- .../testing_app_puya/app_spec.arc32.json | 28 +++++++- tests/artifacts/testing_app_puya/contract.py | 11 +++- 3 files changed, 67 insertions(+), 37 deletions(-) diff --git a/tests/applications/test_app_client.py b/tests/applications/test_app_client.py index 24637506..f88cde38 100644 --- a/tests/applications/test_app_client.py +++ b/tests/applications/test_app_client.py @@ -8,6 +8,7 @@ import pytest from algosdk.atomic_transaction_composer import TransactionSigner, TransactionWithSigner +from algokit_utils import SendParams from algokit_utils._legacy_v2.application_specification import ApplicationSpecification from algokit_utils.algorand import AlgorandClient from algokit_utils.applications.abi import ABIType @@ -166,45 +167,16 @@ def testing_app_puya_arc32_app_spec() -> ApplicationSpecification: return ApplicationSpecification.from_json(raw_json_spec.read_text()) -@pytest.fixture -def testing_app_puya_arc32_app_id( - algorand: AlgorandClient, funded_account: SigningAccount, testing_app_puya_arc32_app_spec: ApplicationSpecification -) -> int: - global_schema = testing_app_puya_arc32_app_spec.global_state_schema - local_schema = testing_app_puya_arc32_app_spec.local_state_schema - - response = algorand.send.app_create( - AppCreateParams( - sender=funded_account.address, - approval_program=testing_app_puya_arc32_app_spec.approval_program, - clear_state_program=testing_app_puya_arc32_app_spec.clear_program, - schema={ - "global_byte_slices": int(global_schema.num_byte_slices) if global_schema.num_byte_slices else 0, - "global_ints": int(global_schema.num_uints) if global_schema.num_uints else 0, - "local_byte_slices": int(local_schema.num_byte_slices) if local_schema.num_byte_slices else 0, - "local_ints": int(local_schema.num_uints) if local_schema.num_uints else 0, - }, - ) - ) - return response.app_id - - @pytest.fixture def test_app_client_puya( - algorand: AlgorandClient, - funded_account: SigningAccount, - testing_app_puya_arc32_app_spec: ApplicationSpecification, - testing_app_puya_arc32_app_id: int, + algorand: AlgorandClient, funded_account: SigningAccount, testing_app_puya_arc32_app_spec: ApplicationSpecification ) -> AppClient: - return AppClient( - AppClientParams( - default_sender=funded_account.address, - default_signer=funded_account.signer, - app_id=testing_app_puya_arc32_app_id, - algorand=algorand, - app_spec=testing_app_puya_arc32_app_spec, - ) + factory = algorand.client.get_app_factory( + app_spec=testing_app_puya_arc32_app_spec, + default_sender=funded_account.address, ) + app_client, _ = factory.send.bare.create() + return app_client def test_clone_overriding_default_sender_and_inheriting_app_name( @@ -709,6 +681,29 @@ def test_box_methods_with_arc4_returns_parametrized( assert abi_decoded_boxes[0].value == arg_value +@pytest.mark.parametrize( + "populate", + [ + True, + # False, # enable this test once rejected transactions contain pc information + ], +) +def test_txn_with_reject(test_app_client_puya: AppClient, *, populate: bool) -> None: + with pytest.raises(LogicError, match="expect this txn to be rejected"): + test_app_client_puya.send.call( + AppClientMethodCallParams(method="rejected"), send_params=SendParams(populate_app_call_resources=populate) + ) + + +@pytest.mark.parametrize("populate", [True, False]) +def test_txn_with_logic_err(test_app_client_puya: AppClient, *, populate: bool) -> None: + with pytest.raises(LogicError, match="expect this to be a logic err") as exc: + test_app_client_puya.send.call( + AppClientMethodCallParams(method="logic_err"), send_params=SendParams(populate_app_call_resources=populate) + ) + assert exc + + def test_abi_with_default_arg_method( algorand: AlgorandClient, funded_account: SigningAccount, diff --git a/tests/artifacts/testing_app_puya/app_spec.arc32.json b/tests/artifacts/testing_app_puya/app_spec.arc32.json index 9a318013..bb42312b 100644 --- a/tests/artifacts/testing_app_puya/app_spec.arc32.json +++ b/tests/artifacts/testing_app_puya/app_spec.arc32.json @@ -54,10 +54,20 @@ "call_config": { "no_op": "CALL" } + }, + "rejected()void": { + "call_config": { + "no_op": "CALL" + } + }, + "logic_err()bool": { + "call_config": { + "no_op": "CALL" + } } }, "source": { - "approval": "I3ByYWdtYSB2ZXJzaW9uIDExCiNwcmFnbWEgdHlwZXRyYWNrIGZhbHNlCgovLyBhbGdvcHkuYXJjNC5BUkM0Q29udHJhY3QuYXBwcm92YWxfcHJvZ3JhbSgpIC0+IHVpbnQ2NDoKbWFpbjoKICAgIGludGNibG9jayAwIDIgMSA0CiAgICBieXRlY2Jsb2NrICJleHRlcm5hbCIKICAgIHR4biBBcHBsaWNhdGlvbklECiAgICBibnogbWFpbl9hZnRlcl9pZl9lbHNlQDIKICAgIC8vIHRlc3RzL2FydGlmYWN0cy90ZXN0aW5nX2FwcF9wdXlhL2NvbnRyYWN0LnB5OjI3CiAgICAvLyBzZWxmLmV4dGVybmFsID0gQXBwbGljYXRpb24oMCkKICAgIGJ5dGVjXzAgLy8gImV4dGVybmFsIgogICAgaW50Y18wIC8vIDAKICAgIGFwcF9nbG9iYWxfcHV0CgptYWluX2FmdGVyX2lmX2Vsc2VAMjoKICAgIC8vIHRlc3RzL2FydGlmYWN0cy90ZXN0aW5nX2FwcF9wdXlhL2NvbnRyYWN0LnB5OjE5CiAgICAvLyBjbGFzcyBUZXN0UHV5YUJveGVzKEFSQzRDb250cmFjdCk6CiAgICB0eG4gTnVtQXBwQXJncwogICAgYnogbWFpbl9fX2FsZ29weV9kZWZhdWx0X2NyZWF0ZUAxNwogICAgdHhuIE9uQ29tcGxldGlvbgogICAgIQogICAgYXNzZXJ0IC8vIE9uQ29tcGxldGlvbiBtdXN0IGJlIE5vT3AKICAgIHR4biBBcHBsaWNhdGlvbklECiAgICBhc3NlcnQKICAgIHB1c2hieXRlc3MgMHgyMDJmYTczYSAweGRmN2VlYTRhIDB4MzY4OGVkMmMgMHg1ZDE3MjBkZCAweGY4MDY2NjVjIDB4ODFkMjYwZTIgMHgyYzI3OGQ0ZiAweGE1NGUyMDJjIC8vIG1ldGhvZCAic2V0X2JveF9ieXRlcyhzdHJpbmcsYnl0ZVtdKXZvaWQiLCBtZXRob2QgInNldF9ib3hfc3RyKHN0cmluZyxzdHJpbmcpdm9pZCIsIG1ldGhvZCAic2V0X2JveF9pbnQoc3RyaW5nLHVpbnQzMil2b2lkIiwgbWV0aG9kICJzZXRfYm94X2ludDUxMihzdHJpbmcsdWludDUxMil2b2lkIiwgbWV0aG9kICJzZXRfYm94X3N0YXRpYyhzdHJpbmcsYnl0ZVs0XSl2b2lkIiwgbWV0aG9kICJzZXRfc3RydWN0KHN0cmluZywoc3RyaW5nLHVpbnQ2NCkpdm9pZCIsIG1ldGhvZCAiYm9vdHN0cmFwX2V4dGVybmFsX2FwcCgpdWludDY0IiwgbWV0aG9kICJzZXRfZXh0ZXJuYWxfYm94KCl2b2lkIgogICAgdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMAogICAgbWF0Y2ggc2V0X2JveF9ieXRlcyBzZXRfYm94X3N0ciBzZXRfYm94X2ludCBzZXRfYm94X2ludDUxMiBzZXRfYm94X3N0YXRpYyBzZXRfc3RydWN0IGJvb3RzdHJhcF9leHRlcm5hbF9hcHAgc2V0X2V4dGVybmFsX2JveAogICAgZXJyCgptYWluX19fYWxnb3B5X2RlZmF1bHRfY3JlYXRlQDE3OgogICAgdHhuIE9uQ29tcGxldGlvbgogICAgIQogICAgdHhuIEFwcGxpY2F0aW9uSUQKICAgICEKICAgICYmCiAgICByZXR1cm4gLy8gb24gZXJyb3I6IE9uQ29tcGxldGlvbiBtdXN0IGJlIE5vT3AgJiYgY2FuIG9ubHkgY2FsbCB3aGVuIGNyZWF0aW5nCgoKLy8gdGVzdHMuYXJ0aWZhY3RzLnRlc3RpbmdfYXBwX3B1eWEuY29udHJhY3QuVGVzdFB1eWFCb3hlcy5zZXRfYm94X2J5dGVzW3JvdXRpbmddKCkgLT4gdm9pZDoKc2V0X2JveF9ieXRlczoKICAgIC8vIHRlc3RzL2FydGlmYWN0cy90ZXN0aW5nX2FwcF9wdXlhL2NvbnRyYWN0LnB5OjI5CiAgICAvLyBAYXJjNC5hYmltZXRob2QKICAgIHR4bmEgQXBwbGljYXRpb25BcmdzIDEKICAgIGR1cAogICAgaW50Y18wIC8vIDAKICAgIGV4dHJhY3RfdWludDE2CiAgICBpbnRjXzEgLy8gMgogICAgKwogICAgZGlnIDEKICAgIGxlbgogICAgPT0KICAgIGFzc2VydCAvLyBpbnZhbGlkIG51bWJlciBvZiBieXRlcyBmb3IgKGxlbit1dGY4W10pCiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAyCiAgICBkdXAKICAgIGludGNfMCAvLyAwCiAgICBleHRyYWN0X3VpbnQxNgogICAgaW50Y18xIC8vIDIKICAgICsKICAgIGRpZyAxCiAgICBsZW4KICAgID09CiAgICBhc3NlcnQgLy8gaW52YWxpZCBudW1iZXIgb2YgYnl0ZXMgZm9yIChsZW4rdWludDhbXSkKICAgIGV4dHJhY3QgMiAwCiAgICAvLyB0ZXN0cy9hcnRpZmFjdHMvdGVzdGluZ19hcHBfcHV5YS9jb250cmFjdC5weTozMQogICAgLy8gc2VsZi5ib3hfYnl0ZXNbbmFtZV0gPSB2YWx1ZQogICAgcHVzaGJ5dGVzICJib3hfYnl0ZXMiCiAgICB1bmNvdmVyIDIKICAgIGNvbmNhdAogICAgZHVwCiAgICBib3hfZGVsCiAgICBwb3AKICAgIHN3YXAKICAgIGJveF9wdXQKICAgIC8vIHRlc3RzL2FydGlmYWN0cy90ZXN0aW5nX2FwcF9wdXlhL2NvbnRyYWN0LnB5OjI5CiAgICAvLyBAYXJjNC5hYmltZXRob2QKICAgIGludGNfMiAvLyAxCiAgICByZXR1cm4KCgovLyB0ZXN0cy5hcnRpZmFjdHMudGVzdGluZ19hcHBfcHV5YS5jb250cmFjdC5UZXN0UHV5YUJveGVzLnNldF9ib3hfc3RyW3JvdXRpbmddKCkgLT4gdm9pZDoKc2V0X2JveF9zdHI6CiAgICAvLyB0ZXN0cy9hcnRpZmFjdHMvdGVzdGluZ19hcHBfcHV5YS9jb250cmFjdC5weTozMwogICAgLy8gQGFyYzQuYWJpbWV0aG9kCiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAxCiAgICBkdXAKICAgIGludGNfMCAvLyAwCiAgICBleHRyYWN0X3VpbnQxNgogICAgaW50Y18xIC8vIDIKICAgICsKICAgIGRpZyAxCiAgICBsZW4KICAgID09CiAgICBhc3NlcnQgLy8gaW52YWxpZCBudW1iZXIgb2YgYnl0ZXMgZm9yIChsZW4rdXRmOFtdKQogICAgdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMgogICAgZHVwCiAgICBpbnRjXzAgLy8gMAogICAgZXh0cmFjdF91aW50MTYKICAgIGludGNfMSAvLyAyCiAgICArCiAgICBkaWcgMQogICAgbGVuCiAgICA9PQogICAgYXNzZXJ0IC8vIGludmFsaWQgbnVtYmVyIG9mIGJ5dGVzIGZvciAobGVuK3V0ZjhbXSkKICAgIC8vIHRlc3RzL2FydGlmYWN0cy90ZXN0aW5nX2FwcF9wdXlhL2NvbnRyYWN0LnB5OjM1CiAgICAvLyBzZWxmLmJveF9zdHJbbmFtZV0gPSB2YWx1ZQogICAgcHVzaGJ5dGVzICJib3hfc3RyIgogICAgdW5jb3ZlciAyCiAgICBjb25jYXQKICAgIGR1cAogICAgYm94X2RlbAogICAgcG9wCiAgICBzd2FwCiAgICBib3hfcHV0CiAgICAvLyB0ZXN0cy9hcnRpZmFjdHMvdGVzdGluZ19hcHBfcHV5YS9jb250cmFjdC5weTozMwogICAgLy8gQGFyYzQuYWJpbWV0aG9kCiAgICBpbnRjXzIgLy8gMQogICAgcmV0dXJuCgoKLy8gdGVzdHMuYXJ0aWZhY3RzLnRlc3RpbmdfYXBwX3B1eWEuY29udHJhY3QuVGVzdFB1eWFCb3hlcy5zZXRfYm94X2ludFtyb3V0aW5nXSgpIC0+IHZvaWQ6CnNldF9ib3hfaW50OgogICAgLy8gdGVzdHMvYXJ0aWZhY3RzL3Rlc3RpbmdfYXBwX3B1eWEvY29udHJhY3QucHk6MzcKICAgIC8vIEBhcmM0LmFiaW1ldGhvZAogICAgdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMQogICAgZHVwCiAgICBpbnRjXzAgLy8gMAogICAgZXh0cmFjdF91aW50MTYKICAgIGludGNfMSAvLyAyCiAgICArCiAgICBkaWcgMQogICAgbGVuCiAgICA9PQogICAgYXNzZXJ0IC8vIGludmFsaWQgbnVtYmVyIG9mIGJ5dGVzIGZvciAobGVuK3V0ZjhbXSkKICAgIHR4bmEgQXBwbGljYXRpb25BcmdzIDIKICAgIGR1cAogICAgbGVuCiAgICBpbnRjXzMgLy8gNAogICAgPT0KICAgIGFzc2VydCAvLyBpbnZhbGlkIG51bWJlciBvZiBieXRlcyBmb3IgdWludDMyCiAgICAvLyB0ZXN0cy9hcnRpZmFjdHMvdGVzdGluZ19hcHBfcHV5YS9jb250cmFjdC5weTozOQogICAgLy8gc2VsZi5ib3hfaW50W25hbWVdID0gdmFsdWUKICAgIHB1c2hieXRlcyAiYm94X2ludCIKICAgIHVuY292ZXIgMgogICAgY29uY2F0CiAgICBzd2FwCiAgICBib3hfcHV0CiAgICAvLyB0ZXN0cy9hcnRpZmFjdHMvdGVzdGluZ19hcHBfcHV5YS9jb250cmFjdC5weTozNwogICAgLy8gQGFyYzQuYWJpbWV0aG9kCiAgICBpbnRjXzIgLy8gMQogICAgcmV0dXJuCgoKLy8gdGVzdHMuYXJ0aWZhY3RzLnRlc3RpbmdfYXBwX3B1eWEuY29udHJhY3QuVGVzdFB1eWFCb3hlcy5zZXRfYm94X2ludDUxMltyb3V0aW5nXSgpIC0+IHZvaWQ6CnNldF9ib3hfaW50NTEyOgogICAgLy8gdGVzdHMvYXJ0aWZhY3RzL3Rlc3RpbmdfYXBwX3B1eWEvY29udHJhY3QucHk6NDEKICAgIC8vIEBhcmM0LmFiaW1ldGhvZAogICAgdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMQogICAgZHVwCiAgICBpbnRjXzAgLy8gMAogICAgZXh0cmFjdF91aW50MTYKICAgIGludGNfMSAvLyAyCiAgICArCiAgICBkaWcgMQogICAgbGVuCiAgICA9PQogICAgYXNzZXJ0IC8vIGludmFsaWQgbnVtYmVyIG9mIGJ5dGVzIGZvciAobGVuK3V0ZjhbXSkKICAgIHR4bmEgQXBwbGljYXRpb25BcmdzIDIKICAgIGR1cAogICAgbGVuCiAgICBwdXNoaW50IDY0IC8vIDY0CiAgICA9PQogICAgYXNzZXJ0IC8vIGludmFsaWQgbnVtYmVyIG9mIGJ5dGVzIGZvciB1aW50NTEyCiAgICAvLyB0ZXN0cy9hcnRpZmFjdHMvdGVzdGluZ19hcHBfcHV5YS9jb250cmFjdC5weTo0MwogICAgLy8gc2VsZi5ib3hfaW50NTEyW25hbWVdID0gdmFsdWUKICAgIHB1c2hieXRlcyAiYm94X2ludDUxMiIKICAgIHVuY292ZXIgMgogICAgY29uY2F0CiAgICBzd2FwCiAgICBib3hfcHV0CiAgICAvLyB0ZXN0cy9hcnRpZmFjdHMvdGVzdGluZ19hcHBfcHV5YS9jb250cmFjdC5weTo0MQogICAgLy8gQGFyYzQuYWJpbWV0aG9kCiAgICBpbnRjXzIgLy8gMQogICAgcmV0dXJuCgoKLy8gdGVzdHMuYXJ0aWZhY3RzLnRlc3RpbmdfYXBwX3B1eWEuY29udHJhY3QuVGVzdFB1eWFCb3hlcy5zZXRfYm94X3N0YXRpY1tyb3V0aW5nXSgpIC0+IHZvaWQ6CnNldF9ib3hfc3RhdGljOgogICAgLy8gdGVzdHMvYXJ0aWZhY3RzL3Rlc3RpbmdfYXBwX3B1eWEvY29udHJhY3QucHk6NDUKICAgIC8vIEBhcmM0LmFiaW1ldGhvZAogICAgdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMQogICAgZHVwCiAgICBpbnRjXzAgLy8gMAogICAgZXh0cmFjdF91aW50MTYKICAgIGludGNfMSAvLyAyCiAgICArCiAgICBkaWcgMQogICAgbGVuCiAgICA9PQogICAgYXNzZXJ0IC8vIGludmFsaWQgbnVtYmVyIG9mIGJ5dGVzIGZvciAobGVuK3V0ZjhbXSkKICAgIHR4bmEgQXBwbGljYXRpb25BcmdzIDIKICAgIGR1cAogICAgbGVuCiAgICBpbnRjXzMgLy8gNAogICAgPT0KICAgIGFzc2VydCAvLyBpbnZhbGlkIG51bWJlciBvZiBieXRlcyBmb3IgdWludDhbNF0KICAgIC8vIHRlc3RzL2FydGlmYWN0cy90ZXN0aW5nX2FwcF9wdXlhL2NvbnRyYWN0LnB5OjQ3CiAgICAvLyBzZWxmLmJveF9zdGF0aWNbbmFtZV0gPSB2YWx1ZS5jb3B5KCkKICAgIHB1c2hieXRlcyAiYm94X3N0YXRpYyIKICAgIHVuY292ZXIgMgogICAgY29uY2F0CiAgICBzd2FwCiAgICBib3hfcHV0CiAgICAvLyB0ZXN0cy9hcnRpZmFjdHMvdGVzdGluZ19hcHBfcHV5YS9jb250cmFjdC5weTo0NQogICAgLy8gQGFyYzQuYWJpbWV0aG9kCiAgICBpbnRjXzIgLy8gMQogICAgcmV0dXJuCgoKLy8gdGVzdHMuYXJ0aWZhY3RzLnRlc3RpbmdfYXBwX3B1eWEuY29udHJhY3QuVGVzdFB1eWFCb3hlcy5zZXRfc3RydWN0W3JvdXRpbmddKCkgLT4gdm9pZDoKc2V0X3N0cnVjdDoKICAgIC8vIHRlc3RzL2FydGlmYWN0cy90ZXN0aW5nX2FwcF9wdXlhL2NvbnRyYWN0LnB5OjQ5CiAgICAvLyBAYXJjNC5hYmltZXRob2QoKQogICAgdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMQogICAgZHVwCiAgICBpbnRjXzAgLy8gMAogICAgZXh0cmFjdF91aW50MTYKICAgIGludGNfMSAvLyAyCiAgICArCiAgICBkaWcgMQogICAgbGVuCiAgICA9PQogICAgYXNzZXJ0IC8vIGludmFsaWQgbnVtYmVyIG9mIGJ5dGVzIGZvciAobGVuK3V0ZjhbXSkKICAgIHR4bmEgQXBwbGljYXRpb25BcmdzIDIKICAgIC8vIHRlc3RzL2FydGlmYWN0cy90ZXN0aW5nX2FwcF9wdXlhL2NvbnRyYWN0LnB5OjUxCiAgICAvLyBhc3NlcnQgbmFtZS5ieXRlcyA9PSB2YWx1ZS5uYW1lLmJ5dGVzLCAiTmFtZSBtdXN0IG1hdGNoIGlkIG9mIHN0cnVjdCIKICAgIGR1cAogICAgaW50Y18wIC8vIDAKICAgIGV4dHJhY3RfdWludDE2CiAgICBkaWcgMQogICAgbGVuCiAgICBkaWcgMgogICAgY292ZXIgMgogICAgc3Vic3RyaW5nMwogICAgZGlnIDIKICAgID09CiAgICBhc3NlcnQgLy8gTmFtZSBtdXN0IG1hdGNoIGlkIG9mIHN0cnVjdAogICAgLy8gdGVzdHMvYXJ0aWZhY3RzL3Rlc3RpbmdfYXBwX3B1eWEvY29udHJhY3QucHk6NTIKICAgIC8vIG9wLkJveC5wdXQobmFtZS5ieXRlcywgdmFsdWUuYnl0ZXMpCiAgICBib3hfcHV0CiAgICAvLyB0ZXN0cy9hcnRpZmFjdHMvdGVzdGluZ19hcHBfcHV5YS9jb250cmFjdC5weTo0OQogICAgLy8gQGFyYzQuYWJpbWV0aG9kKCkKICAgIGludGNfMiAvLyAxCiAgICByZXR1cm4KCgovLyB0ZXN0cy5hcnRpZmFjdHMudGVzdGluZ19hcHBfcHV5YS5jb250cmFjdC5UZXN0UHV5YUJveGVzLmJvb3RzdHJhcF9leHRlcm5hbF9hcHBbcm91dGluZ10oKSAtPiB2b2lkOgpib290c3RyYXBfZXh0ZXJuYWxfYXBwOgogICAgLy8gdGVzdHMvYXJ0aWZhY3RzL3Rlc3RpbmdfYXBwX3B1eWEvY29udHJhY3QucHk6NTYKICAgIC8vIHNlbGYuZXh0ZXJuYWwgPSBhcmM0LmFyYzRfY3JlYXRlKEV4dGVybmFsKS5jcmVhdGVkX2FwcAogICAgaXR4bl9iZWdpbgogICAgcHVzaGJ5dGVzIGJhc2U2NChDNEVCUXc9PSkKICAgIGl0eG5fZmllbGQgQ2xlYXJTdGF0ZVByb2dyYW1QYWdlcwogICAgcHVzaGJ5dGVzIGJhc2U2NChDeVlCQTJKdmVERWJRUUFZZ0FSTlBrUHNOaG9BamdFQUFRQXhHUlF4R0JCRVFnQUlNUmtVTVJnVUVFTW92RWdvZ0FObWIyKy9nUUZEKQogICAgaXR4bl9maWVsZCBBcHByb3ZhbFByb2dyYW1QYWdlcwogICAgcHVzaGludCA2IC8vIGFwcGwKICAgIGl0eG5fZmllbGQgVHlwZUVudW0KICAgIGludGNfMCAvLyAwCiAgICBpdHhuX2ZpZWxkIEZlZQogICAgaXR4bl9zdWJtaXQKICAgIGl0eG4gQ3JlYXRlZEFwcGxpY2F0aW9uSUQKICAgIGJ5dGVjXzAgLy8gImV4dGVybmFsIgogICAgZGlnIDEKICAgIGFwcF9nbG9iYWxfcHV0CiAgICAvLyB0ZXN0cy9hcnRpZmFjdHMvdGVzdGluZ19hcHBfcHV5YS9jb250cmFjdC5weTo1NAogICAgLy8gQGFyYzQuYWJpbWV0aG9kCiAgICBpdG9iCiAgICBwdXNoYnl0ZXMgMHgxNTFmN2M3NQogICAgc3dhcAogICAgY29uY2F0CiAgICBsb2cKICAgIGludGNfMiAvLyAxCiAgICByZXR1cm4KCgovLyB0ZXN0cy5hcnRpZmFjdHMudGVzdGluZ19hcHBfcHV5YS5jb250cmFjdC5UZXN0UHV5YUJveGVzLnNldF9leHRlcm5hbF9ib3hbcm91dGluZ10oKSAtPiB2b2lkOgpzZXRfZXh0ZXJuYWxfYm94OgogICAgLy8gdGVzdHMvYXJ0aWZhY3RzL3Rlc3RpbmdfYXBwX3B1eWEvY29udHJhY3QucHk6NjEtNjQKICAgIC8vIGFyYzQuYWJpX2NhbGwoCiAgICAvLyAgICAgRXh0ZXJuYWwuc2V0X2JveCwKICAgIC8vICAgICBhcHBfaWQ9c2VsZi5leHRlcm5hbCwKICAgIC8vICkKICAgIGl0eG5fYmVnaW4KICAgIC8vIHRlc3RzL2FydGlmYWN0cy90ZXN0aW5nX2FwcF9wdXlhL2NvbnRyYWN0LnB5OjYzCiAgICAvLyBhcHBfaWQ9c2VsZi5leHRlcm5hbCwKICAgIGludGNfMCAvLyAwCiAgICBieXRlY18wIC8vICJleHRlcm5hbCIKICAgIGFwcF9nbG9iYWxfZ2V0X2V4CiAgICBhc3NlcnQgLy8gY2hlY2sgc2VsZi5leHRlcm5hbCBleGlzdHMKICAgIGl0eG5fZmllbGQgQXBwbGljYXRpb25JRAogICAgLy8gdGVzdHMvYXJ0aWZhY3RzL3Rlc3RpbmdfYXBwX3B1eWEvY29udHJhY3QucHk6NjEtNjQKICAgIC8vIGFyYzQuYWJpX2NhbGwoCiAgICAvLyAgICAgRXh0ZXJuYWwuc2V0X2JveCwKICAgIC8vICAgICBhcHBfaWQ9c2VsZi5leHRlcm5hbCwKICAgIC8vICkKICAgIHB1c2hieXRlcyAweDRkM2U0M2VjIC8vIG1ldGhvZCAic2V0X2JveCgpdm9pZCIKICAgIGl0eG5fZmllbGQgQXBwbGljYXRpb25BcmdzCiAgICBwdXNoaW50IDYgLy8gYXBwbAogICAgaXR4bl9maWVsZCBUeXBlRW51bQogICAgaW50Y18wIC8vIDAKICAgIGl0eG5fZmllbGQgRmVlCiAgICBpdHhuX3N1Ym1pdAogICAgLy8gdGVzdHMvYXJ0aWZhY3RzL3Rlc3RpbmdfYXBwX3B1eWEvY29udHJhY3QucHk6NTkKICAgIC8vIEBhcmM0LmFiaW1ldGhvZAogICAgaW50Y18yIC8vIDEKICAgIHJldHVybgo=", + "approval": "I3ByYWdtYSB2ZXJzaW9uIDExCiNwcmFnbWEgdHlwZXRyYWNrIGZhbHNlCgovLyBhbGdvcHkuYXJjNC5BUkM0Q29udHJhY3QuYXBwcm92YWxfcHJvZ3JhbSgpIC0+IHVpbnQ2NDoKbWFpbjoKICAgIGludGNibG9jayAwIDEgMiA0CiAgICBieXRlY2Jsb2NrICJleHRlcm5hbCIKICAgIHR4biBBcHBsaWNhdGlvbklECiAgICBibnogbWFpbl9hZnRlcl9pZl9lbHNlQDIKICAgIC8vIHRlc3RzL2FydGlmYWN0cy90ZXN0aW5nX2FwcF9wdXlhL2NvbnRyYWN0LnB5OjI4CiAgICAvLyBzZWxmLmV4dGVybmFsID0gQXBwbGljYXRpb24oMCkKICAgIGJ5dGVjXzAgLy8gImV4dGVybmFsIgogICAgaW50Y18wIC8vIDAKICAgIGFwcF9nbG9iYWxfcHV0CgptYWluX2FmdGVyX2lmX2Vsc2VAMjoKICAgIC8vIHRlc3RzL2FydGlmYWN0cy90ZXN0aW5nX2FwcF9wdXlhL2NvbnRyYWN0LnB5OjIwCiAgICAvLyBjbGFzcyBUZXN0UHV5YUJveGVzKEFSQzRDb250cmFjdCk6CiAgICB0eG4gTnVtQXBwQXJncwogICAgYnogbWFpbl9fX2FsZ29weV9kZWZhdWx0X2NyZWF0ZUAxOQogICAgdHhuIE9uQ29tcGxldGlvbgogICAgIQogICAgYXNzZXJ0IC8vIE9uQ29tcGxldGlvbiBtdXN0IGJlIE5vT3AKICAgIHR4biBBcHBsaWNhdGlvbklECiAgICBhc3NlcnQKICAgIHB1c2hieXRlc3MgMHgyMDJmYTczYSAweGRmN2VlYTRhIDB4MzY4OGVkMmMgMHg1ZDE3MjBkZCAweGY4MDY2NjVjIDB4ODFkMjYwZTIgMHgyYzI3OGQ0ZiAweGE1NGUyMDJjIDB4ZTRkZWI3NDMgMHhiMTgwZmJiNSAvLyBtZXRob2QgInNldF9ib3hfYnl0ZXMoc3RyaW5nLGJ5dGVbXSl2b2lkIiwgbWV0aG9kICJzZXRfYm94X3N0cihzdHJpbmcsc3RyaW5nKXZvaWQiLCBtZXRob2QgInNldF9ib3hfaW50KHN0cmluZyx1aW50MzIpdm9pZCIsIG1ldGhvZCAic2V0X2JveF9pbnQ1MTIoc3RyaW5nLHVpbnQ1MTIpdm9pZCIsIG1ldGhvZCAic2V0X2JveF9zdGF0aWMoc3RyaW5nLGJ5dGVbNF0pdm9pZCIsIG1ldGhvZCAic2V0X3N0cnVjdChzdHJpbmcsKHN0cmluZyx1aW50NjQpKXZvaWQiLCBtZXRob2QgImJvb3RzdHJhcF9leHRlcm5hbF9hcHAoKXVpbnQ2NCIsIG1ldGhvZCAic2V0X2V4dGVybmFsX2JveCgpdm9pZCIsIG1ldGhvZCAicmVqZWN0ZWQoKXZvaWQiLCBtZXRob2QgImxvZ2ljX2VycigpYm9vbCIKICAgIHR4bmEgQXBwbGljYXRpb25BcmdzIDAKICAgIG1hdGNoIHNldF9ib3hfYnl0ZXMgc2V0X2JveF9zdHIgc2V0X2JveF9pbnQgc2V0X2JveF9pbnQ1MTIgc2V0X2JveF9zdGF0aWMgc2V0X3N0cnVjdCBib290c3RyYXBfZXh0ZXJuYWxfYXBwIHNldF9leHRlcm5hbF9ib3ggcmVqZWN0ZWQgbG9naWNfZXJyCiAgICBlcnIKCm1haW5fX19hbGdvcHlfZGVmYXVsdF9jcmVhdGVAMTk6CiAgICB0eG4gT25Db21wbGV0aW9uCiAgICAhCiAgICB0eG4gQXBwbGljYXRpb25JRAogICAgIQogICAgJiYKICAgIHJldHVybiAvLyBvbiBlcnJvcjogT25Db21wbGV0aW9uIG11c3QgYmUgTm9PcCAmJiBjYW4gb25seSBjYWxsIHdoZW4gY3JlYXRpbmcKCgovLyB0ZXN0cy5hcnRpZmFjdHMudGVzdGluZ19hcHBfcHV5YS5jb250cmFjdC5UZXN0UHV5YUJveGVzLnNldF9ib3hfYnl0ZXNbcm91dGluZ10oKSAtPiB2b2lkOgpzZXRfYm94X2J5dGVzOgogICAgLy8gdGVzdHMvYXJ0aWZhY3RzL3Rlc3RpbmdfYXBwX3B1eWEvY29udHJhY3QucHk6MzAKICAgIC8vIEBhcmM0LmFiaW1ldGhvZAogICAgdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMQogICAgZHVwCiAgICBpbnRjXzAgLy8gMAogICAgZXh0cmFjdF91aW50MTYKICAgIGludGNfMiAvLyAyCiAgICArCiAgICBkaWcgMQogICAgbGVuCiAgICA9PQogICAgYXNzZXJ0IC8vIGludmFsaWQgbnVtYmVyIG9mIGJ5dGVzIGZvciAobGVuK3V0ZjhbXSkKICAgIHR4bmEgQXBwbGljYXRpb25BcmdzIDIKICAgIGR1cAogICAgaW50Y18wIC8vIDAKICAgIGV4dHJhY3RfdWludDE2CiAgICBpbnRjXzIgLy8gMgogICAgKwogICAgZGlnIDEKICAgIGxlbgogICAgPT0KICAgIGFzc2VydCAvLyBpbnZhbGlkIG51bWJlciBvZiBieXRlcyBmb3IgKGxlbit1aW50OFtdKQogICAgZXh0cmFjdCAyIDAKICAgIC8vIHRlc3RzL2FydGlmYWN0cy90ZXN0aW5nX2FwcF9wdXlhL2NvbnRyYWN0LnB5OjMyCiAgICAvLyBzZWxmLmJveF9ieXRlc1tuYW1lXSA9IHZhbHVlCiAgICBwdXNoYnl0ZXMgImJveF9ieXRlcyIKICAgIHVuY292ZXIgMgogICAgY29uY2F0CiAgICBkdXAKICAgIGJveF9kZWwKICAgIHBvcAogICAgc3dhcAogICAgYm94X3B1dAogICAgLy8gdGVzdHMvYXJ0aWZhY3RzL3Rlc3RpbmdfYXBwX3B1eWEvY29udHJhY3QucHk6MzAKICAgIC8vIEBhcmM0LmFiaW1ldGhvZAogICAgaW50Y18xIC8vIDEKICAgIHJldHVybgoKCi8vIHRlc3RzLmFydGlmYWN0cy50ZXN0aW5nX2FwcF9wdXlhLmNvbnRyYWN0LlRlc3RQdXlhQm94ZXMuc2V0X2JveF9zdHJbcm91dGluZ10oKSAtPiB2b2lkOgpzZXRfYm94X3N0cjoKICAgIC8vIHRlc3RzL2FydGlmYWN0cy90ZXN0aW5nX2FwcF9wdXlhL2NvbnRyYWN0LnB5OjM0CiAgICAvLyBAYXJjNC5hYmltZXRob2QKICAgIHR4bmEgQXBwbGljYXRpb25BcmdzIDEKICAgIGR1cAogICAgaW50Y18wIC8vIDAKICAgIGV4dHJhY3RfdWludDE2CiAgICBpbnRjXzIgLy8gMgogICAgKwogICAgZGlnIDEKICAgIGxlbgogICAgPT0KICAgIGFzc2VydCAvLyBpbnZhbGlkIG51bWJlciBvZiBieXRlcyBmb3IgKGxlbit1dGY4W10pCiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAyCiAgICBkdXAKICAgIGludGNfMCAvLyAwCiAgICBleHRyYWN0X3VpbnQxNgogICAgaW50Y18yIC8vIDIKICAgICsKICAgIGRpZyAxCiAgICBsZW4KICAgID09CiAgICBhc3NlcnQgLy8gaW52YWxpZCBudW1iZXIgb2YgYnl0ZXMgZm9yIChsZW4rdXRmOFtdKQogICAgLy8gdGVzdHMvYXJ0aWZhY3RzL3Rlc3RpbmdfYXBwX3B1eWEvY29udHJhY3QucHk6MzYKICAgIC8vIHNlbGYuYm94X3N0cltuYW1lXSA9IHZhbHVlCiAgICBwdXNoYnl0ZXMgImJveF9zdHIiCiAgICB1bmNvdmVyIDIKICAgIGNvbmNhdAogICAgZHVwCiAgICBib3hfZGVsCiAgICBwb3AKICAgIHN3YXAKICAgIGJveF9wdXQKICAgIC8vIHRlc3RzL2FydGlmYWN0cy90ZXN0aW5nX2FwcF9wdXlhL2NvbnRyYWN0LnB5OjM0CiAgICAvLyBAYXJjNC5hYmltZXRob2QKICAgIGludGNfMSAvLyAxCiAgICByZXR1cm4KCgovLyB0ZXN0cy5hcnRpZmFjdHMudGVzdGluZ19hcHBfcHV5YS5jb250cmFjdC5UZXN0UHV5YUJveGVzLnNldF9ib3hfaW50W3JvdXRpbmddKCkgLT4gdm9pZDoKc2V0X2JveF9pbnQ6CiAgICAvLyB0ZXN0cy9hcnRpZmFjdHMvdGVzdGluZ19hcHBfcHV5YS9jb250cmFjdC5weTozOAogICAgLy8gQGFyYzQuYWJpbWV0aG9kCiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAxCiAgICBkdXAKICAgIGludGNfMCAvLyAwCiAgICBleHRyYWN0X3VpbnQxNgogICAgaW50Y18yIC8vIDIKICAgICsKICAgIGRpZyAxCiAgICBsZW4KICAgID09CiAgICBhc3NlcnQgLy8gaW52YWxpZCBudW1iZXIgb2YgYnl0ZXMgZm9yIChsZW4rdXRmOFtdKQogICAgdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMgogICAgZHVwCiAgICBsZW4KICAgIGludGNfMyAvLyA0CiAgICA9PQogICAgYXNzZXJ0IC8vIGludmFsaWQgbnVtYmVyIG9mIGJ5dGVzIGZvciB1aW50MzIKICAgIC8vIHRlc3RzL2FydGlmYWN0cy90ZXN0aW5nX2FwcF9wdXlhL2NvbnRyYWN0LnB5OjQwCiAgICAvLyBzZWxmLmJveF9pbnRbbmFtZV0gPSB2YWx1ZQogICAgcHVzaGJ5dGVzICJib3hfaW50IgogICAgdW5jb3ZlciAyCiAgICBjb25jYXQKICAgIHN3YXAKICAgIGJveF9wdXQKICAgIC8vIHRlc3RzL2FydGlmYWN0cy90ZXN0aW5nX2FwcF9wdXlhL2NvbnRyYWN0LnB5OjM4CiAgICAvLyBAYXJjNC5hYmltZXRob2QKICAgIGludGNfMSAvLyAxCiAgICByZXR1cm4KCgovLyB0ZXN0cy5hcnRpZmFjdHMudGVzdGluZ19hcHBfcHV5YS5jb250cmFjdC5UZXN0UHV5YUJveGVzLnNldF9ib3hfaW50NTEyW3JvdXRpbmddKCkgLT4gdm9pZDoKc2V0X2JveF9pbnQ1MTI6CiAgICAvLyB0ZXN0cy9hcnRpZmFjdHMvdGVzdGluZ19hcHBfcHV5YS9jb250cmFjdC5weTo0MgogICAgLy8gQGFyYzQuYWJpbWV0aG9kCiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAxCiAgICBkdXAKICAgIGludGNfMCAvLyAwCiAgICBleHRyYWN0X3VpbnQxNgogICAgaW50Y18yIC8vIDIKICAgICsKICAgIGRpZyAxCiAgICBsZW4KICAgID09CiAgICBhc3NlcnQgLy8gaW52YWxpZCBudW1iZXIgb2YgYnl0ZXMgZm9yIChsZW4rdXRmOFtdKQogICAgdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMgogICAgZHVwCiAgICBsZW4KICAgIHB1c2hpbnQgNjQgLy8gNjQKICAgID09CiAgICBhc3NlcnQgLy8gaW52YWxpZCBudW1iZXIgb2YgYnl0ZXMgZm9yIHVpbnQ1MTIKICAgIC8vIHRlc3RzL2FydGlmYWN0cy90ZXN0aW5nX2FwcF9wdXlhL2NvbnRyYWN0LnB5OjQ0CiAgICAvLyBzZWxmLmJveF9pbnQ1MTJbbmFtZV0gPSB2YWx1ZQogICAgcHVzaGJ5dGVzICJib3hfaW50NTEyIgogICAgdW5jb3ZlciAyCiAgICBjb25jYXQKICAgIHN3YXAKICAgIGJveF9wdXQKICAgIC8vIHRlc3RzL2FydGlmYWN0cy90ZXN0aW5nX2FwcF9wdXlhL2NvbnRyYWN0LnB5OjQyCiAgICAvLyBAYXJjNC5hYmltZXRob2QKICAgIGludGNfMSAvLyAxCiAgICByZXR1cm4KCgovLyB0ZXN0cy5hcnRpZmFjdHMudGVzdGluZ19hcHBfcHV5YS5jb250cmFjdC5UZXN0UHV5YUJveGVzLnNldF9ib3hfc3RhdGljW3JvdXRpbmddKCkgLT4gdm9pZDoKc2V0X2JveF9zdGF0aWM6CiAgICAvLyB0ZXN0cy9hcnRpZmFjdHMvdGVzdGluZ19hcHBfcHV5YS9jb250cmFjdC5weTo0NgogICAgLy8gQGFyYzQuYWJpbWV0aG9kCiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAxCiAgICBkdXAKICAgIGludGNfMCAvLyAwCiAgICBleHRyYWN0X3VpbnQxNgogICAgaW50Y18yIC8vIDIKICAgICsKICAgIGRpZyAxCiAgICBsZW4KICAgID09CiAgICBhc3NlcnQgLy8gaW52YWxpZCBudW1iZXIgb2YgYnl0ZXMgZm9yIChsZW4rdXRmOFtdKQogICAgdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMgogICAgZHVwCiAgICBsZW4KICAgIGludGNfMyAvLyA0CiAgICA9PQogICAgYXNzZXJ0IC8vIGludmFsaWQgbnVtYmVyIG9mIGJ5dGVzIGZvciB1aW50OFs0XQogICAgLy8gdGVzdHMvYXJ0aWZhY3RzL3Rlc3RpbmdfYXBwX3B1eWEvY29udHJhY3QucHk6NDgKICAgIC8vIHNlbGYuYm94X3N0YXRpY1tuYW1lXSA9IHZhbHVlLmNvcHkoKQogICAgcHVzaGJ5dGVzICJib3hfc3RhdGljIgogICAgdW5jb3ZlciAyCiAgICBjb25jYXQKICAgIHN3YXAKICAgIGJveF9wdXQKICAgIC8vIHRlc3RzL2FydGlmYWN0cy90ZXN0aW5nX2FwcF9wdXlhL2NvbnRyYWN0LnB5OjQ2CiAgICAvLyBAYXJjNC5hYmltZXRob2QKICAgIGludGNfMSAvLyAxCiAgICByZXR1cm4KCgovLyB0ZXN0cy5hcnRpZmFjdHMudGVzdGluZ19hcHBfcHV5YS5jb250cmFjdC5UZXN0UHV5YUJveGVzLnNldF9zdHJ1Y3Rbcm91dGluZ10oKSAtPiB2b2lkOgpzZXRfc3RydWN0OgogICAgLy8gdGVzdHMvYXJ0aWZhY3RzL3Rlc3RpbmdfYXBwX3B1eWEvY29udHJhY3QucHk6NTAKICAgIC8vIEBhcmM0LmFiaW1ldGhvZCgpCiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAxCiAgICBkdXAKICAgIGludGNfMCAvLyAwCiAgICBleHRyYWN0X3VpbnQxNgogICAgaW50Y18yIC8vIDIKICAgICsKICAgIGRpZyAxCiAgICBsZW4KICAgID09CiAgICBhc3NlcnQgLy8gaW52YWxpZCBudW1iZXIgb2YgYnl0ZXMgZm9yIChsZW4rdXRmOFtdKQogICAgdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMgogICAgLy8gdGVzdHMvYXJ0aWZhY3RzL3Rlc3RpbmdfYXBwX3B1eWEvY29udHJhY3QucHk6NTIKICAgIC8vIGFzc2VydCBuYW1lLmJ5dGVzID09IHZhbHVlLm5hbWUuYnl0ZXMsICJOYW1lIG11c3QgbWF0Y2ggaWQgb2Ygc3RydWN0IgogICAgZHVwCiAgICBpbnRjXzAgLy8gMAogICAgZXh0cmFjdF91aW50MTYKICAgIGRpZyAxCiAgICBsZW4KICAgIGRpZyAyCiAgICBjb3ZlciAyCiAgICBzdWJzdHJpbmczCiAgICBkaWcgMgogICAgPT0KICAgIGFzc2VydCAvLyBOYW1lIG11c3QgbWF0Y2ggaWQgb2Ygc3RydWN0CiAgICAvLyB0ZXN0cy9hcnRpZmFjdHMvdGVzdGluZ19hcHBfcHV5YS9jb250cmFjdC5weTo1MwogICAgLy8gb3AuQm94LnB1dChuYW1lLmJ5dGVzLCB2YWx1ZS5ieXRlcykKICAgIGJveF9wdXQKICAgIC8vIHRlc3RzL2FydGlmYWN0cy90ZXN0aW5nX2FwcF9wdXlhL2NvbnRyYWN0LnB5OjUwCiAgICAvLyBAYXJjNC5hYmltZXRob2QoKQogICAgaW50Y18xIC8vIDEKICAgIHJldHVybgoKCi8vIHRlc3RzLmFydGlmYWN0cy50ZXN0aW5nX2FwcF9wdXlhLmNvbnRyYWN0LlRlc3RQdXlhQm94ZXMuYm9vdHN0cmFwX2V4dGVybmFsX2FwcFtyb3V0aW5nXSgpIC0+IHZvaWQ6CmJvb3RzdHJhcF9leHRlcm5hbF9hcHA6CiAgICAvLyB0ZXN0cy9hcnRpZmFjdHMvdGVzdGluZ19hcHBfcHV5YS9jb250cmFjdC5weTo1NwogICAgLy8gc2VsZi5leHRlcm5hbCA9IGFyYzQuYXJjNF9jcmVhdGUoRXh0ZXJuYWwpLmNyZWF0ZWRfYXBwCiAgICBpdHhuX2JlZ2luCiAgICBwdXNoYnl0ZXMgYmFzZTY0KEM0RUJRdz09KQogICAgaXR4bl9maWVsZCBDbGVhclN0YXRlUHJvZ3JhbVBhZ2VzCiAgICBwdXNoYnl0ZXMgYmFzZTY0KEN5WUJBMkp2ZURFYlFRQVlnQVJOUGtQc05ob0FqZ0VBQVFBeEdSUXhHQkJFUWdBSU1Sa1VNUmdVRUVNb3ZFZ29nQU5tYjIrL2dRRkQpCiAgICBpdHhuX2ZpZWxkIEFwcHJvdmFsUHJvZ3JhbVBhZ2VzCiAgICBwdXNoaW50IDYgLy8gYXBwbAogICAgaXR4bl9maWVsZCBUeXBlRW51bQogICAgaW50Y18wIC8vIDAKICAgIGl0eG5fZmllbGQgRmVlCiAgICBpdHhuX3N1Ym1pdAogICAgaXR4biBDcmVhdGVkQXBwbGljYXRpb25JRAogICAgYnl0ZWNfMCAvLyAiZXh0ZXJuYWwiCiAgICBkaWcgMQogICAgYXBwX2dsb2JhbF9wdXQKICAgIC8vIHRlc3RzL2FydGlmYWN0cy90ZXN0aW5nX2FwcF9wdXlhL2NvbnRyYWN0LnB5OjU1CiAgICAvLyBAYXJjNC5hYmltZXRob2QKICAgIGl0b2IKICAgIHB1c2hieXRlcyAweDE1MWY3Yzc1CiAgICBzd2FwCiAgICBjb25jYXQKICAgIGxvZwogICAgaW50Y18xIC8vIDEKICAgIHJldHVybgoKCi8vIHRlc3RzLmFydGlmYWN0cy50ZXN0aW5nX2FwcF9wdXlhLmNvbnRyYWN0LlRlc3RQdXlhQm94ZXMuc2V0X2V4dGVybmFsX2JveFtyb3V0aW5nXSgpIC0+IHZvaWQ6CnNldF9leHRlcm5hbF9ib3g6CiAgICAvLyB0ZXN0cy9hcnRpZmFjdHMvdGVzdGluZ19hcHBfcHV5YS9jb250cmFjdC5weTo2Mi02NQogICAgLy8gYXJjNC5hYmlfY2FsbCgKICAgIC8vICAgICBFeHRlcm5hbC5zZXRfYm94LAogICAgLy8gICAgIGFwcF9pZD1zZWxmLmV4dGVybmFsLAogICAgLy8gKQogICAgaXR4bl9iZWdpbgogICAgLy8gdGVzdHMvYXJ0aWZhY3RzL3Rlc3RpbmdfYXBwX3B1eWEvY29udHJhY3QucHk6NjQKICAgIC8vIGFwcF9pZD1zZWxmLmV4dGVybmFsLAogICAgaW50Y18wIC8vIDAKICAgIGJ5dGVjXzAgLy8gImV4dGVybmFsIgogICAgYXBwX2dsb2JhbF9nZXRfZXgKICAgIGFzc2VydCAvLyBjaGVjayBzZWxmLmV4dGVybmFsIGV4aXN0cwogICAgaXR4bl9maWVsZCBBcHBsaWNhdGlvbklECiAgICAvLyB0ZXN0cy9hcnRpZmFjdHMvdGVzdGluZ19hcHBfcHV5YS9jb250cmFjdC5weTo2Mi02NQogICAgLy8gYXJjNC5hYmlfY2FsbCgKICAgIC8vICAgICBFeHRlcm5hbC5zZXRfYm94LAogICAgLy8gICAgIGFwcF9pZD1zZWxmLmV4dGVybmFsLAogICAgLy8gKQogICAgcHVzaGJ5dGVzIDB4NGQzZTQzZWMgLy8gbWV0aG9kICJzZXRfYm94KCl2b2lkIgogICAgaXR4bl9maWVsZCBBcHBsaWNhdGlvbkFyZ3MKICAgIHB1c2hpbnQgNiAvLyBhcHBsCiAgICBpdHhuX2ZpZWxkIFR5cGVFbnVtCiAgICBpbnRjXzAgLy8gMAogICAgaXR4bl9maWVsZCBGZWUKICAgIGl0eG5fc3VibWl0CiAgICAvLyB0ZXN0cy9hcnRpZmFjdHMvdGVzdGluZ19hcHBfcHV5YS9jb250cmFjdC5weTo2MAogICAgLy8gQGFyYzQuYWJpbWV0aG9kCiAgICBpbnRjXzEgLy8gMQogICAgcmV0dXJuCgoKLy8gdGVzdHMuYXJ0aWZhY3RzLnRlc3RpbmdfYXBwX3B1eWEuY29udHJhY3QuVGVzdFB1eWFCb3hlcy5yZWplY3RlZFtyb3V0aW5nXSgpIC0+IHZvaWQ6CnJlamVjdGVkOgogICAgLy8gdGVzdHMvYXJ0aWZhY3RzL3Rlc3RpbmdfYXBwX3B1eWEvY29udHJhY3QucHk6NjkKICAgIC8vIGFzc2VydCBUeG4ubnVtX2FwcF9hcmdzID09IDAsICJleHBlY3QgdGhpcyB0eG4gdG8gYmUgcmVqZWN0ZWQiCiAgICB0eG4gTnVtQXBwQXJncwogICAgIQogICAgLy8gdGVzdHMvYXJ0aWZhY3RzL3Rlc3RpbmdfYXBwX3B1eWEvY29udHJhY3QucHk6NjcKICAgIC8vIEBhcmM0LmFiaW1ldGhvZAogICAgcmV0dXJuIC8vIG9uIGVycm9yOiBleHBlY3QgdGhpcyB0eG4gdG8gYmUgcmVqZWN0ZWQKCgovLyB0ZXN0cy5hcnRpZmFjdHMudGVzdGluZ19hcHBfcHV5YS5jb250cmFjdC5UZXN0UHV5YUJveGVzLmxvZ2ljX2Vycltyb3V0aW5nXSgpIC0+IHZvaWQ6CmxvZ2ljX2VycjoKICAgIC8vIHRlc3RzL2FydGlmYWN0cy90ZXN0aW5nX2FwcF9wdXlhL2NvbnRyYWN0LnB5OjczCiAgICAvLyBhc3NlcnQgVHhuLm51bV9hcHBfYXJncyA9PSAwLCAiZXhwZWN0IHRoaXMgdG8gYmUgYSBsb2dpYyBlcnIiCiAgICB0eG4gTnVtQXBwQXJncwogICAgIQogICAgYXNzZXJ0IC8vIGV4cGVjdCB0aGlzIHRvIGJlIGEgbG9naWMgZXJyCiAgICAvLyB0ZXN0cy9hcnRpZmFjdHMvdGVzdGluZ19hcHBfcHV5YS9jb250cmFjdC5weTo3MQogICAgLy8gQGFyYzQuYWJpbWV0aG9kCiAgICBwdXNoYnl0ZXMgMHgxNTFmN2M3NTgwCiAgICBsb2cKICAgIGludGNfMSAvLyAxCiAgICByZXR1cm4K", "clear": "I3ByYWdtYSB2ZXJzaW9uIDExCiNwcmFnbWEgdHlwZXRyYWNrIGZhbHNlCgovLyBhbGdvcHkuYXJjNC5BUkM0Q29udHJhY3QuY2xlYXJfc3RhdGVfcHJvZ3JhbSgpIC0+IHVpbnQ2NDoKbWFpbjoKICAgIHB1c2hpbnQgMSAvLyAxCiAgICByZXR1cm4K" }, "state": { @@ -205,6 +215,22 @@ "returns": { "type": "void" } + }, + { + "name": "rejected", + "args": [], + "readonly": false, + "returns": { + "type": "void" + } + }, + { + "name": "logic_err", + "args": [], + "readonly": false, + "returns": { + "type": "bool" + } } ], "networks": {} diff --git a/tests/artifacts/testing_app_puya/contract.py b/tests/artifacts/testing_app_puya/contract.py index d2c22499..631e4329 100644 --- a/tests/artifacts/testing_app_puya/contract.py +++ b/tests/artifacts/testing_app_puya/contract.py @@ -1,6 +1,6 @@ from typing import Literal -from algopy import Application, ARC4Contract, Box, BoxMap, Bytes, arc4, op +from algopy import Application, ARC4Contract, Box, BoxMap, Bytes, Txn, arc4, op class DummyStruct(arc4.Struct): @@ -63,3 +63,12 @@ def set_external_box(self) -> None: External.set_box, app_id=self.external, ) + + @arc4.abimethod + def rejected(self) -> None: + assert Txn.num_app_args == 0, "expect this txn to be rejected" + + @arc4.abimethod + def logic_err(self) -> bool: + assert Txn.num_app_args == 0, "expect this to be a logic err" + return True From 2aa9768b68601b2a3d830d0cd4b05f5b435f47fb Mon Sep 17 00:00:00 2001 From: Daniel McGregor Date: Wed, 15 Oct 2025 18:18:26 +0800 Subject: [PATCH 5/7] fix: extract pc from simulation trace for rejected transactions during simulation --- src/algokit_utils/errors/logic_error.py | 2 +- .../transactions/transaction_composer.py | 47 ++++++++++++++----- 2 files changed, 36 insertions(+), 13 deletions(-) diff --git a/src/algokit_utils/errors/logic_error.py b/src/algokit_utils/errors/logic_error.py index 755b89e4..42b19c20 100644 --- a/src/algokit_utils/errors/logic_error.py +++ b/src/algokit_utils/errors/logic_error.py @@ -20,7 +20,7 @@ LOGIC_ERROR = ( - ".*transaction (?P[A-Z0-9]+): logic eval error: (?P.*). Details: .*pc=(?P[0-9]+).*" + ".*transaction (?P[A-Z0-9]+): (logic eval error: )?(?P.*). Details: .*pc=(?P[0-9]+).*" ) diff --git a/src/algokit_utils/transactions/transaction_composer.py b/src/algokit_utils/transactions/transaction_composer.py index 0b05e999..57ab24a3 100644 --- a/src/algokit_utils/transactions/transaction_composer.py +++ b/src/algokit_utils/transactions/transaction_composer.py @@ -21,7 +21,7 @@ from algosdk.transaction import OnComplete, SuggestedParams from algosdk.v2client.algod import AlgodClient from algosdk.v2client.models.simulate_request import SimulateRequest -from typing_extensions import deprecated +from typing_extensions import Never, deprecated from algokit_utils.applications.abi import ABIReturn, ABIValue from algokit_utils.applications.app_manager import AppManager @@ -667,7 +667,7 @@ def _encode_lease(lease: str | bytes | None) -> bytes | None: raise TypeError(f"Unknown lease type received of {type(lease)}") -def _get_group_execution_info( # noqa: C901, PLR0912 +def _get_group_execution_info( # noqa: C901 atc: AtomicTransactionComposer, algod: AlgodClient, populate_app_call_resources: bool | None = None, @@ -682,6 +682,7 @@ def _get_group_execution_info( # noqa: C901, PLR0912 txn_groups=[], allow_unnamed_resources=True, allow_empty_signatures=True, + exec_trace_config=algosdk.v2client.models.SimulateTraceConfig(enable=True), ) # Clone ATC with null signers @@ -720,16 +721,8 @@ def _get_group_execution_info( # noqa: C901, PLR0912 group_response = result.simulate_response["txn-groups"][0] if group_response.get("failure-message"): - msg = group_response["failure-message"] - if cover_app_call_inner_transaction_fees and "fee too small" in msg: - raise ValueError( - "Fees were too small to resolve execution info via simulate. " - "You may need to increase an app call transaction maxFee." - ) - failed_at = group_response.get("failed-at", [0])[0] - raise ValueError( - f"Error resolving execution info via simulate in transaction {failed_at}: " - f"{group_response['failure-message']}" + _handle_simulation_error( + group_response, cover_app_call_inner_transaction_fees=cover_app_call_inner_transaction_fees ) # Build execution info @@ -782,6 +775,36 @@ def calculate_inner_fee_delta(inner_txns: list[dict], acc: int = 0) -> int: ) +def _handle_simulation_error( + group_response: dict[str, Any], *, cover_app_call_inner_transaction_fees: bool | None +) -> Never: + msg = group_response["failure-message"] + if cover_app_call_inner_transaction_fees and "fee too small" in msg: + raise ValueError( + "Fees were too small to resolve execution info via simulate. " + "You may need to increase an app call transaction maxFee." + ) + failed_at = group_response.get("failed-at", [0])[0] + details = "" + if "logic eval error" not in msg: + # extract last pc from trace so we can format an error that can be parsed into a LogicError + try: + trace = group_response["txn-results"][failed_at]["exec-trace"] + except (KeyError, IndexError): + pass + else: + try: + program_trace = trace["approval-program-trace"] + except KeyError: + program_trace = trace["clear-program-trace"] + pc = program_trace[-1]["pc"] + details = f". Details: pc={pc}" + raise ValueError( + f"Error resolving execution info via simulate in transaction {failed_at}: " + f"{group_response['failure-message']}{details}" + ) + + def _find_available_transaction_index( txns: list[TransactionWithSigner], reference_type: str, reference: str | dict[str, Any] | int ) -> int: From f23475b01a39ff429f2ef2561f219d0ca8a697b1 Mon Sep 17 00:00:00 2001 From: Daniel McGregor Date: Wed, 15 Oct 2025 19:12:14 +0800 Subject: [PATCH 6/7] refactor: extract method --- .../transactions/transaction_composer.py | 53 +++++++++++-------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/src/algokit_utils/transactions/transaction_composer.py b/src/algokit_utils/transactions/transaction_composer.py index 57ab24a3..2eacb9df 100644 --- a/src/algokit_utils/transactions/transaction_composer.py +++ b/src/algokit_utils/transactions/transaction_composer.py @@ -667,7 +667,7 @@ def _encode_lease(lease: str | bytes | None) -> bytes | None: raise TypeError(f"Unknown lease type received of {type(lease)}") -def _get_group_execution_info( # noqa: C901 +def _get_group_execution_info( atc: AtomicTransactionComposer, algod: AlgodClient, populate_app_call_resources: bool | None = None, @@ -736,27 +736,12 @@ def _get_group_execution_info( # noqa: C901 required_fee_delta = 0 if cover_app_call_inner_transaction_fees: - # Calculate parent transaction fee - parent_per_byte_fee = per_byte_txn_fee * (original_txn.estimate_size() + 75) - parent_min_fee = max(parent_per_byte_fee, min_txn_fee) - parent_fee_delta = parent_min_fee - original_txn.fee - - if isinstance(original_txn, algosdk.transaction.ApplicationCallTxn): - # Calculate inner transaction fees recursively - def calculate_inner_fee_delta(inner_txns: list[dict], acc: int = 0) -> int: - for inner_txn in reversed(inner_txns): - current_fee_delta = ( - calculate_inner_fee_delta(inner_txn["inner-txns"], acc) - if inner_txn.get("inner-txns") - else acc - ) + (min_txn_fee - inner_txn["txn"]["txn"].get("fee", 0)) - acc = max(0, current_fee_delta) - return acc - - inner_fee_delta = calculate_inner_fee_delta(txn_result.get("inner-txns", [])) - required_fee_delta = inner_fee_delta + parent_fee_delta - else: - required_fee_delta = parent_fee_delta + required_fee_delta = _calculate_required_fee_delta( + original_txn, + txn_result, + per_byte_txn_fee=per_byte_txn_fee, + min_txn_fee=min_txn_fee, + ) txn_results.append( ExecutionInfoTxn( @@ -805,6 +790,30 @@ def _handle_simulation_error( ) +def _calculate_required_fee_delta( + original_txn: transaction.Transaction, txn_result: dict[str, Any], *, per_byte_txn_fee: int, min_txn_fee: int +) -> int: + # Calculate parent transaction fee + parent_per_byte_fee = per_byte_txn_fee * (original_txn.estimate_size() + 75) + parent_min_fee = max(parent_per_byte_fee, min_txn_fee) + parent_fee_delta = parent_min_fee - original_txn.fee + + if isinstance(original_txn, algosdk.transaction.ApplicationCallTxn): + # Calculate inner transaction fees recursively + def calculate_inner_fee_delta(inner_txns: list[dict], acc: int = 0) -> int: + for inner_txn in reversed(inner_txns): + current_fee_delta = ( + calculate_inner_fee_delta(inner_txn["inner-txns"], acc) if inner_txn.get("inner-txns") else acc + ) + (min_txn_fee - inner_txn["txn"]["txn"].get("fee", 0)) + acc = max(0, current_fee_delta) + return acc + + inner_fee_delta = calculate_inner_fee_delta(txn_result.get("inner-txns", [])) + return inner_fee_delta + parent_fee_delta + else: + return parent_fee_delta + + def _find_available_transaction_index( txns: list[TransactionWithSigner], reference_type: str, reference: str | dict[str, Any] | int ) -> int: From 935b5f917a2f65618428be10df6bf4dbf8b0d5d0 Mon Sep 17 00:00:00 2001 From: Daniel McGregor Date: Wed, 15 Oct 2025 19:16:32 +0800 Subject: [PATCH 7/7] chore: remove type warnings and minor refactor --- .../transactions/transaction_composer.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/algokit_utils/transactions/transaction_composer.py b/src/algokit_utils/transactions/transaction_composer.py index 2eacb9df..b2e78332 100644 --- a/src/algokit_utils/transactions/transaction_composer.py +++ b/src/algokit_utils/transactions/transaction_composer.py @@ -711,10 +711,6 @@ def _get_group_execution_info( f"Required for transactions: {', '.join(str(i) for i in app_call_indexes_without_max_fees)}" ) - # Get fee parameters - per_byte_txn_fee = suggested_params.fee if suggested_params else 0 - min_txn_fee = int(suggested_params.min_fee) if suggested_params else 1000 # type: ignore[unused-ignore] - # Simulate transactions result = empty_signer_atc.simulate(algod, simulate_request) @@ -739,8 +735,8 @@ def _get_group_execution_info( required_fee_delta = _calculate_required_fee_delta( original_txn, txn_result, - per_byte_txn_fee=per_byte_txn_fee, - min_txn_fee=min_txn_fee, + per_byte_txn_fee=suggested_params.fee if suggested_params else 0, + min_txn_fee=int(suggested_params.min_fee) if suggested_params else 1000, ) txn_results.append( @@ -791,14 +787,18 @@ def _handle_simulation_error( def _calculate_required_fee_delta( - original_txn: transaction.Transaction, txn_result: dict[str, Any], *, per_byte_txn_fee: int, min_txn_fee: int + txn: transaction.Transaction, txn_result: dict[str, Any], *, per_byte_txn_fee: int, min_txn_fee: int ) -> int: # Calculate parent transaction fee - parent_per_byte_fee = per_byte_txn_fee * (original_txn.estimate_size() + 75) + original_txn_size = txn.estimate_size() + assert isinstance(original_txn_size, int), "expected txn size to be an int" + parent_per_byte_fee = per_byte_txn_fee * (original_txn_size + 75) parent_min_fee = max(parent_per_byte_fee, min_txn_fee) - parent_fee_delta = parent_min_fee - original_txn.fee + original_txn_fee = txn.fee + assert isinstance(original_txn_fee, int), "expected original txn fee to be an int" + parent_fee_delta = parent_min_fee - original_txn_fee - if isinstance(original_txn, algosdk.transaction.ApplicationCallTxn): + if isinstance(txn, algosdk.transaction.ApplicationCallTxn): # Calculate inner transaction fees recursively def calculate_inner_fee_delta(inner_txns: list[dict], acc: int = 0) -> int: for inner_txn in reversed(inner_txns):