From 87148893d6f5e58ddb295a3a9d9c17acf054bd96 Mon Sep 17 00:00:00 2001 From: Al Date: Mon, 14 Oct 2024 19:27:15 +0200 Subject: [PATCH] feat: puya debugging support (#2) * chore: adding option to treat null under sourcemap-locations as valid 'ignore' cases * chore: npm audit * chore: adding npm prepare script for installation via github * feat: add puya debug support (WIP) * chore: support for passing file contents for sourcemaps; puya/teal edge cases (#1) * chore: hiding locals on non puya sourcemaps; adding puya and teal example * chore: no tmpl vars offset edge case fix * chore: rel path for example on puya + teal * chore: workaround on skipping syntentic refs that does not require async file exists * feat: support direct file or encoded content for sourcemaps * refactor: make programSourcesDescription an object instead of JSON * chore: adding more edge cases * chore(src/common): Add program source entry file and type Added 'ProgramSourceEntryFile' and 'ProgramSourceEntry' to the common module. These types are used for parsing program source files. * chore: add support for 'null' sourcemap paths when user prefers ignores * chore: add mocharc file for global timeouts Also fixing a few tests * fix: move program forward call after inner transaction processing * chore: further refine puya example with third parties * feat: integrating Locals with existing expand avm methods * chore: adding puya unit test; moving process unit into program replay --------- Co-authored-by: Daniel McGregor * fix: step backwards on puya sourcemaps * refactor: use different interface for CallStackFrame vs TraceReplayFrame capture lastIndex for when needing to walk backward in a program remove _ expandedAvmValueCache and instead handle the 'named' and 'indexed' filters correctly * feat: support evaluation of Puya variables * chore: reduce diff * chore: bumping to algosdkv3; adding puya-ts example; extra unit test; * fix: backwards compatibility with SimulateResponse encoded via algosdkv2 * fix: update unit tests to match latest examples * fix: hiding locals on non puya frontend sourcemap paths * chore: transferring advanced examples to extension repo * chore: apply suggestions from code review Co-authored-by: Neil Campbell --------- Co-authored-by: Daniel McGregor Co-authored-by: Neil Campbell --- .mocharc.js | 4 + .vscode/launch.json | 3 +- extension/package.json | 6 + extension/src/configuration.ts | 17 +- package-lock.json | 51 +- package.json | 6 +- sampleWorkspace/.vscode/launch.json | 40 +- sampleWorkspace/README.md | 12 + .../.algokit/sources/opup/approval.teal.map | 8 + .../puya/.algokit/sources/opup/opup.teal | 2 + .../puya/ProofOfAttendance.approval.puya.map | 5945 +++++++++++++++++ sampleWorkspace/puya/contract.py | 214 + sampleWorkspace/puya/simulate-response.json | 3186 +++++++++ sampleWorkspace/puya/sources.json | 12 + src/common/debugSession.ts | 328 +- src/common/index.ts | 1 + src/common/programReplay.ts | 389 ++ src/common/runtime.ts | 66 +- src/common/traceReplayEngine.ts | 494 +- src/common/utils.ts | 220 +- tests/adapter.test.ts | 106 + tsconfig.json | 4 +- 22 files changed, 10603 insertions(+), 511 deletions(-) create mode 100644 .mocharc.js create mode 100644 sampleWorkspace/README.md create mode 100644 sampleWorkspace/puya/.algokit/sources/opup/approval.teal.map create mode 100644 sampleWorkspace/puya/.algokit/sources/opup/opup.teal create mode 100644 sampleWorkspace/puya/ProofOfAttendance.approval.puya.map create mode 100644 sampleWorkspace/puya/contract.py create mode 100644 sampleWorkspace/puya/simulate-response.json create mode 100644 sampleWorkspace/puya/sources.json create mode 100644 src/common/programReplay.ts diff --git a/.mocharc.js b/.mocharc.js new file mode 100644 index 0000000..143fb88 --- /dev/null +++ b/.mocharc.js @@ -0,0 +1,4 @@ +// .mocharc.js +module.exports = { + timeout: 30000, // 30 seconds +}; diff --git a/.vscode/launch.json b/.vscode/launch.json index 7b9898e..5898c60 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -6,11 +6,12 @@ "type": "extensionHost", "request": "launch", "args": [ + "--disable-extensions", "--extensionDevelopmentPath=${workspaceFolder}/extension", "${workspaceFolder}/sampleWorkspace" ], "outFiles": ["${workspaceFolder}/extension/dist/**/*.js"], - "preLaunchTask": "npm: extension watch" + "preLaunchTask": "npm: extension:build" }, { "name": "Server", diff --git a/extension/package.json b/extension/package.json index 18d31cf..ad88ee1 100644 --- a/extension/package.json +++ b/extension/package.json @@ -56,6 +56,12 @@ "breakpoints": [ { "language": "teal" + }, + { + "language": "python" + }, + { + "language": "typescript" } ], "debuggers": [ diff --git a/extension/src/configuration.ts b/extension/src/configuration.ts index 89319f1..4f948ce 100644 --- a/extension/src/configuration.ts +++ b/extension/src/configuration.ts @@ -26,9 +26,22 @@ export class AvmDebugConfigProvider return null; } - if (!config.programSourcesDescriptionFile) { + if ( + config.programSourcesDescription && + !config.programSourcesDescriptionFolder + ) { vscode.window.showErrorMessage( - 'Missing property "programSourcesDescriptionFile" in debug config', + 'Missing property "programSourcesDescriptionFolder" in debug config', + ); + return null; + } + + if ( + !config.programSourcesDescriptionFile && + !config.programSourcesDescription + ) { + vscode.window.showErrorMessage( + 'Either "programSourcesDescriptionFile" or "programSourcesDescription" + "programSourcesDescriptionFolder" must be provided in debug config', ); return null; } diff --git a/package-lock.json b/package-lock.json index be8e17d..b696dac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "MIT", "dependencies": { "@vscode/debugadapter": "^1.64.0", - "algosdk": "^3.0.0-beta.1", + "algosdk": "^3.0.0", "await-notify": "^1.0.1" }, "bin": { @@ -1321,9 +1321,10 @@ } }, "node_modules/algosdk": { - "version": "3.0.0-beta.1", - "resolved": "https://registry.npmjs.org/algosdk/-/algosdk-3.0.0-beta.1.tgz", - "integrity": "sha512-45+F7zH+a3u2eRx7IaSaRl7BfyBej1IO9Fk/Rz7f1c+FhSCj/OxZoToGYAkr6OsJuL3KmJAnX4l3BQYOY/ddSA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/algosdk/-/algosdk-3.0.0.tgz", + "integrity": "sha512-PIKZ/YvbBpCudduug4KSH1CY/pTotI7/ccbUIbXKtcI9Onevl+57E+K5X4ow4gsCdysZ8zVvSLdxuCcXvsmPOw==", + "license": "MIT", "dependencies": { "algorand-msgpack": "^1.1.0", "hi-base32": "^0.5.1", @@ -1554,12 +1555,13 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, + "license": "MIT", "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -2493,10 +2495,11 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, + "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -2611,6 +2614,21 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -3036,6 +3054,7 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.12.0" } @@ -3474,12 +3493,13 @@ } }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, + "license": "MIT", "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -4916,6 +4936,7 @@ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, + "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, diff --git a/package.json b/package.json index e47710d..380c99b 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "src" ], "scripts": { + "prepare": "npm run compile", "compile": "shx rm -rf out && tsc -p ./", "lint": "eslint src --ext ts", "typecheck": "tsc -p tsconfig.json --noEmit", @@ -63,11 +64,12 @@ "extension:publish": "vsce publish", "extension:publish-pre-release": "vsce publish --pre-release", "test": "ts-mocha -p tsconfig.json 'tests/**/*test.ts' --timeout 30s --diff false", - "test:coverage": "nyc npm run test" + "test:coverage": "nyc npm run test", + "pre-commit": "npm run lint && npm run typecheck && npm run format" }, "dependencies": { "@vscode/debugadapter": "^1.64.0", - "algosdk": "^3.0.0-beta.1", + "algosdk": "^3.0.0", "await-notify": "^1.0.1" }, "devDependencies": { diff --git a/sampleWorkspace/.vscode/launch.json b/sampleWorkspace/.vscode/launch.json index 9667da6..5bb186a 100644 --- a/sampleWorkspace/.vscode/launch.json +++ b/sampleWorkspace/.vscode/launch.json @@ -8,160 +8,136 @@ "type": "avm", "request": "launch", "name": "Debug App Box State Changes", - "simulateTraceFile": "${workspaceFolder}/app-state-changes/box-simulate-response.json", "programSourcesDescriptionFile": "${workspaceFolder}/app-state-changes/sources.json", - "stopOnEntry": true }, { "type": "avm", "request": "launch", "name": "Debug App Global State Changes", - "simulateTraceFile": "${workspaceFolder}/app-state-changes/global-simulate-response.json", "programSourcesDescriptionFile": "${workspaceFolder}/app-state-changes/sources.json", - "stopOnEntry": true }, { "type": "avm", "request": "launch", "name": "Debug App Local State Changes", - "simulateTraceFile": "${workspaceFolder}/app-state-changes/local-simulate-response.json", "programSourcesDescriptionFile": "${workspaceFolder}/app-state-changes/sources.json", - "stopOnEntry": true }, { "type": "avm", "request": "launch", "name": "Debug Recursive App", - "simulateTraceFile": "${workspaceFolder}/recursive-app/simulate-response.json", "programSourcesDescriptionFile": "${workspaceFolder}/recursive-app/sources.json", - "stopOnEntry": true }, { "type": "avm", "request": "launch", "name": "Debug Slot Machine", - "simulateTraceFile": "${workspaceFolder}/slot-machine/simulate-response.json", "programSourcesDescriptionFile": "${workspaceFolder}/slot-machine/sources.json", - "stopOnEntry": true }, { "type": "avm", "request": "launch", "name": "Debug Sourcemap Test", - "simulateTraceFile": "${workspaceFolder}/sourcemap-test/simulate-response.json", "programSourcesDescriptionFile": "${workspaceFolder}/sourcemap-test/sources.json", - "stopOnEntry": true }, { "type": "avm", "request": "launch", "name": "Debug Stack Scratch", - "simulateTraceFile": "${workspaceFolder}/stack-scratch/simulate-response.json", "programSourcesDescriptionFile": "${workspaceFolder}/stack-scratch/sources.json", - "stopOnEntry": true }, { "type": "avm", "request": "launch", "name": "Debug Stepping Test", - "simulateTraceFile": "${workspaceFolder}/stepping-test/simulate-response.json", "programSourcesDescriptionFile": "${workspaceFolder}/stepping-test/sources.json", - "stopOnEntry": true }, { "type": "avm", "request": "launch", "name": "Debug App Error", - "simulateTraceFile": "${workspaceFolder}/errors/app/simulate-response.json", "programSourcesDescriptionFile": "${workspaceFolder}/errors/app/sources.json", - "stopOnEntry": true }, { "type": "avm", "request": "launch", "name": "Debug App from LogicSig Error", - "simulateTraceFile": "${workspaceFolder}/errors/app-from-logicsig/simulate-response.json", "programSourcesDescriptionFile": "${workspaceFolder}/errors/app-from-logicsig/sources.json", - "stopOnEntry": true }, { "type": "avm", "request": "launch", "name": "Debug Inner App Error (App Reject)", - "simulateTraceFile": "${workspaceFolder}/errors/inner-app/app-reject-simulate-response.json", "programSourcesDescriptionFile": "${workspaceFolder}/errors/inner-app/sources.json", - "stopOnEntry": true }, { "type": "avm", "request": "launch", "name": "Debug Inner App Error (Overspend)", - "simulateTraceFile": "${workspaceFolder}/errors/inner-app/overspend-simulate-response.json", "programSourcesDescriptionFile": "${workspaceFolder}/errors/inner-app/sources.json", - "stopOnEntry": true }, { "type": "avm", "request": "launch", "name": "Debug LogicSig Error", - "simulateTraceFile": "${workspaceFolder}/errors/logicsig/simulate-response.json", "programSourcesDescriptionFile": "${workspaceFolder}/errors/logicsig/sources.json", - "stopOnEntry": true }, { "type": "avm", "request": "launch", "name": "Debug Error Before LogicSig", - "simulateTraceFile": "${workspaceFolder}/errors/logicsig-after-error/simulate-response.json", "programSourcesDescriptionFile": "${workspaceFolder}/errors/logicsig-after-error/sources.json", - "stopOnEntry": true }, { "type": "avm", "request": "launch", "name": "Clear State Rejection", - "simulateTraceFile": "${workspaceFolder}/errors/clear-state/rejection-response.json", "programSourcesDescriptionFile": "${workspaceFolder}/errors/clear-state/sources.json", - "stopOnEntry": true }, { "type": "avm", "request": "launch", "name": "Clear State Error", - "simulateTraceFile": "${workspaceFolder}/errors/clear-state/error-response.json", "programSourcesDescriptionFile": "${workspaceFolder}/errors/clear-state/sources.json", - + "stopOnEntry": true + }, + { + "type": "avm", + "request": "launch", + "name": "Debug Puya", + "simulateTraceFile": "${workspaceFolder}/puya/simulate-response.json", + "programSourcesDescriptionFile": "${workspaceFolder}/puya/sources.json", "stopOnEntry": true } ] diff --git a/sampleWorkspace/README.md b/sampleWorkspace/README.md new file mode 100644 index 0000000..c581916 --- /dev/null +++ b/sampleWorkspace/README.md @@ -0,0 +1,12 @@ +## How to navigate examples? + +This folder contains sample workspaces representing various debugger use cases: + +- Simulate traces, TEAL sources and sourcemaps (e.g. `stack-scratch`, `stepping-test`, `app-state-changes`, `errors`). Various examples of using the debugger with TEAL sourcemaps and sources. +- Simulate traces, [Puya](https://github.com/algorandfoundation/puya) sources and sourcemaps (e.g. `puya`). Showcasing various ways to simulate traces with puya sourcemaps and sources. + +> **Note:** The frontend language for Puya compiler scenarios is [Algorand Python](https://pypi.org/project/algorand-python/). + +### Launching sample workspaces + +Refer to the [launch.json](.vscode/launch.json) file to launch sample workspaces from VSCode. Keep the marketplace-based AlgoKit AVM Debugger extension disabled (if installed). diff --git a/sampleWorkspace/puya/.algokit/sources/opup/approval.teal.map b/sampleWorkspace/puya/.algokit/sources/opup/approval.teal.map new file mode 100644 index 0000000..35a17c6 --- /dev/null +++ b/sampleWorkspace/puya/.algokit/sources/opup/approval.teal.map @@ -0,0 +1,8 @@ +{ + "version": 3, + "sources": [ + "opup.teal" + ], + "names": [], + "mappings": ";AACA" + } diff --git a/sampleWorkspace/puya/.algokit/sources/opup/opup.teal b/sampleWorkspace/puya/.algokit/sources/opup/opup.teal new file mode 100644 index 0000000..e3ae8f2 --- /dev/null +++ b/sampleWorkspace/puya/.algokit/sources/opup/opup.teal @@ -0,0 +1,2 @@ +#pragma version 6 +int 1 diff --git a/sampleWorkspace/puya/ProofOfAttendance.approval.puya.map b/sampleWorkspace/puya/ProofOfAttendance.approval.puya.map new file mode 100644 index 0000000..5e62b26 --- /dev/null +++ b/sampleWorkspace/puya/ProofOfAttendance.approval.puya.map @@ -0,0 +1,5945 @@ +{ + "version": 3, + "sources": [ + "contract.py" + ], + "mappings": ";;;;;;;;;;;;AAKA;;;AAAA;;AAAA;;;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;;;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AASK;;AAAA;AAAA;AAAA;;AAAA;AAAA;AATL;;;AAAA;AASK;;;AAAA;AAAA;AAOA;;AAAA;AAAA;AAAA;;AAAA;AAAA;;;AAAA;AAAA;AAYA;;AAAA;AAAA;AAAA;;AAAA;AAAA;;;AAAA;AAAA;AAaA;;AAAA;AAAA;AAAA;;AAAA;AAAA;;;AAAA;AAAA;AAaA;;AAAA;AAAA;AAAA;;AAAA;AAAA;;;AAAA;AAAA;AAYA;;AAAA;AAAA;AAAA;;AAAA;AAAA;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAMA;;AAAA;AAAA;AAAA;;AAAA;AAAA;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAOA;;AAAA;AAAA;AAAA;;AAAA;AAAA;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAOA;;AAAA;AAAA;AAAA;;AAAA;AAAA;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAMA;;AAAA;AAAA;AAAA;;AAAA;AA5FL;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AA4FK;;;AAAA;AAAA;AAqBA;;AAAA;AAAA;AAAA;;AAAA;AAjHL;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAiHK;;;AAAA;AAAA;AAwBA;;AAAA;AAAA;AAAA;;AAAA;AAzIL;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAyIK;;;AAAA;AAAA;AAwBA;;AAAA;AAAA;AAAA;;AAAA;AAjKL;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAiKK;;;AAAA;AAAA;AAjKL;AAAA;AASA;;;AAGY;;AAAqB;;AAArB;AADJ;AAGA;AAAA;;AAAA;;AAER;;;AAEe;AAAA;AAAA;AAAA;AAAuB;AAAA;AAAA;AAAA;AAAvB;AAAP;AAE8B;;AAAf;;;AACf;AAAA;AAAA;AAAA;AAAwB;AAAxB;AAAA;AAAA;AAAA;AAEqC;;AAAlB;AAAA;;AACZ;AAAP;AAEkB;;AAAyB;AAAA;AAA3C;;AA8JR;;;AAE6B;;;AAAsB;AAA3C;;;AAC+C;AAAA;AAAA;AAAA;AAAf;AAAnB;;;;;;;;;;;;;;;AAAb;AAAa;AAET;AAKQ;AAAA;;AAAA;AAAA;;;;;;;AADK;;;AADH;;;AADI;;;;;;;;;AAFd;;;;AAAA;;;AAAA;AAAA;;AADJ;AAhKR;;;AAEe;AAAA;AAAA;AAAA;AAAuB;AAAA;AAAA;AAAA;AAAvB;AAAP;AAE8B;;AAAf;;;AACf;AAAA;AAAA;AAAA;AAAwB;AAAxB;AAAA;AAAA;AAAA;AAEoC;;AACtB;AAAA;AAAA;;AACP;AAAP;AAEA;AAAA;AAAA;;AAER;;;AAEe;AAAA;AAAA;AAAA;AAAuB;AAAA;AAAA;AAAA;AAAvB;AAAP;AAE8B;;AAAf;;;AACf;AAAA;AAAA;AAAA;AAAwB;AAAxB;AAAA;AAAA;AAAA;AAE4B;;AACd;AAAA;AAAA;;AACP;AAAP;AAEY;AAAA;AAAZ;;AAER;;;AAEe;AAAA;AAAA;AAAA;AAAuB;AAAA;AAAA;AAAA;AAAvB;AAAP;AAE8B;;AAAf;;;AACf;AAAA;AAAA;AAAA;AAAwB;AAAxB;AAAA;AAAA;AAAA;AAEyC;AAA3B;;AAAA;AAAA;AAAA;;AACP;AAAP;AAEA;AAAa;;AAAb;AAAA;AAAA;AAAA;;AAER;;;AAE2C;;AAAlB;AACjB;AACO;AAAP;AAER;;;AAE4C;;AACnB;AAAA;AAAA;AACjB;AAAA;AACA;AAER;;;AAEoC;;AACX;AACjB;AACO;AAAP;AAER;;;AAEyB;AAAmB;;AAAnB;AAAA;AAAA;AAAA;AACjB;AAAA;AACA;AAER;;;;AAE2C;;AAAlB;AACjB;AACO;;AAAA;;AAA4B;AAAA;AAAA;AAAA;;AAA5B;AAAP;AACO;;AAAA;;AAAA;AAAP;AACO;;AAAA;;AAAA;AAAP;AAEI;;AAAA;;AAAqB;;AAAA;;AAAA;AAAA;;AAArB;AAAA;;;AAAqB;;AAA6B;;AAA7B;AAArB;;;;;;;;AADJ;AAII;;AAAA;;AACG;;AAAA;;AAAA;AAAA;;AADH;AAAA;;;AACG;;AACA;;AADA;AADH;;;;;;;;AADJ;AAOI;;AADJ;;AAAA;;;;AA6FR;;;AAEQ;AAEW;;AAEM;;;;;;;;;;;;;AAJjB;;;AAAA;;;AAAA;;AA1FR;;;;AAI4C;;AACnB;AAAA;AAAA;AAAA;AAAA;;AACjB;AACO;;AAAA;;AAAA;AAAP;AACO;;AAAA;;AAAA;AAAP;AACO;;AAAA;;AAAA;AAAP;AAEI;;AAAA;;AAAqB;;AAAA;;AAAA;AAAA;;AAArB;AAAA;;;AAAqB;;AAA6B;;AAA7B;AAArB;;;;;;;;AADJ;AAII;;AAAA;;AACG;;AAAA;;AAAA;AAAA;;AADH;AAAA;;;AACG;;AACA;;AADA;AADH;;;;;;;;AADJ;AAOI;;AADJ;;AAAA;;;;AAKR;;;;AAIoC;;AACX;AACjB;AACO;;AAAA;;AAA4B;AAAA;AAAA;AAAA;;AAA5B;AAAP;AACO;;AAAA;;AAAA;AAAP;AACO;;AAAA;;AAAA;AAAP;AAEI;;AAAA;;AAAqB;;AAAA;;AAAA;AAAA;;AAArB;AAAA;;;AAAqB;;AAA6B;;AAA7B;AAArB;;;;;;;;AADJ;AAII;;AAAA;;AACG;;AAAA;;AAAA;AAAA;;AADH;AAAA;;;AACG;;AACA;;AADA;AADH;;;;;;;;AADJ;AAOI;;AADJ;;AAAA;;;;AAKR;;;;AAIyB;AAAmB;;AAAnB;AAAA;AAAA;AAAA;AAAA;AAAA;;AACjB;AACO;;AAAA;;AAAA;AAAP;AACO;;AAAA;;AAAA;AAAP;AACO;;AAAA;;AAAA;AAAP;AAEI;;AAAA;;AAAqB;;AAAA;;AAAA;AAAA;;AAArB;AAAA;;;AAAqB;;AAA6B;;AAA7B;AAArB;;;;;;;;AADJ;AAII;;AAAA;;AACG;;AAAA;;AAAA;AAAA;;AADH;AAAA;;;AACG;;AACA;;AADA;AADH;;;;;;;;AADJ;AAOI;;AADJ;;AAAA;;;;AAlLR;;;AACQ;AAAqB;;AAArB;AACA;;AAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAjB;AAGA;AAAuB;AAAvB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;", + "op_pc_offset": 2, + "pc_events": { + "0": { + "op": "txn ApplicationID", + "defined_out": [ + "app_id%0#0" + ], + "stack_out": [ + "app_id%0#0" + ] + }, + "2": { + "op": "bnz main_entrypoint@2", + "stack_out": [] + }, + "5": { + "callsub": "smart_contracts.poa.contract.ProofOfAttendance.__init__", + "op": "callsub __init__" + }, + "8": { + "block": "main_entrypoint@2", + "stack_in": [], + "callsub": "smart_contracts.poa.contract.ProofOfAttendance.__puya_arc4_router__", + "op": "callsub __puya_arc4_router__", + "defined_out": [ + "tmp%0#0" + ], + "stack_out": [ + "tmp%0#0" + ] + }, + "11": { + "op": "return", + "stack_out": [] + }, + "12": { + "subroutine": "smart_contracts.poa.contract.ProofOfAttendance.__puya_arc4_router__", + "params": {}, + "block": "__puya_arc4_router__", + "stack_in": [], + "op": "proto 0 1" + }, + "15": { + "op": "txn NumAppArgs", + "defined_out": [ + "tmp%0#0" + ], + "stack_out": [ + "tmp%0#0" + ] + }, + "17": { + "op": "bz __puya_arc4_router___after_if_else@18", + "stack_out": [] + }, + "20": { + "op": "pushbytess 0x1bcde52d 0xb8962962 0xfe9b38ed 0x9f5df126 0xbb36b1d7 0x77a24669 0xd2709766 0x7f323833 0x03d246c7 0xe946d06b 0x90a65932 0x9b7186d3 0xeb2af226 // method \"init(uint64)void\", method \"confirm_attendance()void\", method \"confirm_attendance_with_box()void\", method \"confirm_attendance_with_box_ref()void\", method \"confirm_attendance_with_box_map()void\", method \"get_poa_id()uint64\", method \"get_poa_id_with_box()uint64\", method \"get_poa_id_with_box_ref()uint64\", method \"get_poa_id_with_box_map()uint64\", method \"claim_poa(axfer)void\", method \"claim_poa_with_box(axfer)void\", method \"claim_poa_with_box_ref(axfer)void\", method \"claim_poa_with_box_map(axfer)void\"" + }, + "87": { + "op": "txna ApplicationArgs 0", + "defined_out": [ + "Method(claim_poa(axfer)void)", + "Method(claim_poa_with_box(axfer)void)", + "Method(claim_poa_with_box_map(axfer)void)", + "Method(claim_poa_with_box_ref(axfer)void)", + "Method(confirm_attendance()void)", + "Method(confirm_attendance_with_box()void)", + "Method(confirm_attendance_with_box_map()void)", + "Method(confirm_attendance_with_box_ref()void)", + "Method(get_poa_id()uint64)", + "Method(get_poa_id_with_box()uint64)", + "Method(get_poa_id_with_box_map()uint64)", + "Method(get_poa_id_with_box_ref()uint64)", + "Method(init(uint64)void)", + "tmp%2#0" + ], + "stack_out": [ + "Method(init(uint64)void)", + "Method(confirm_attendance()void)", + "Method(confirm_attendance_with_box()void)", + "Method(confirm_attendance_with_box_ref()void)", + "Method(confirm_attendance_with_box_map()void)", + "Method(get_poa_id()uint64)", + "Method(get_poa_id_with_box()uint64)", + "Method(get_poa_id_with_box_ref()uint64)", + "Method(get_poa_id_with_box_map()uint64)", + "Method(claim_poa(axfer)void)", + "Method(claim_poa_with_box(axfer)void)", + "Method(claim_poa_with_box_ref(axfer)void)", + "Method(claim_poa_with_box_map(axfer)void)", + "tmp%2#0" + ] + }, + "90": { + "op": "match __puya_arc4_router___init_route@2 __puya_arc4_router___confirm_attendance_route@3 __puya_arc4_router___confirm_attendance_with_box_route@4 __puya_arc4_router___confirm_attendance_with_box_ref_route@5 __puya_arc4_router___confirm_attendance_with_box_map_route@6 __puya_arc4_router___get_poa_id_route@7 __puya_arc4_router___get_poa_id_with_box_route@8 __puya_arc4_router___get_poa_id_with_box_ref_route@9 __puya_arc4_router___get_poa_id_with_box_map_route@10 __puya_arc4_router___claim_poa_route@11 __puya_arc4_router___claim_poa_with_box_route@12 __puya_arc4_router___claim_poa_with_box_ref_route@13 __puya_arc4_router___claim_poa_with_box_map_route@14", + "stack_out": [] + }, + "118": { + "op": "intc_0 // 0", + "defined_out": [ + "0" + ], + "stack_out": [ + "0" + ] + }, + "119": { + "retsub": true, + "op": "retsub" + }, + "120": { + "block": "__puya_arc4_router___init_route@2", + "stack_in": [], + "op": "txn OnCompletion", + "defined_out": [ + "tmp%3#0" + ], + "stack_out": [ + "tmp%3#0" + ] + }, + "122": { + "op": "!", + "defined_out": [ + "tmp%4#0" + ], + "stack_out": [ + "tmp%4#0" + ] + }, + "123": { + "op": "assert // OnCompletion is NoOp", + "stack_out": [] + }, + "124": { + "op": "txn ApplicationID", + "defined_out": [ + "tmp%5#0" + ], + "stack_out": [ + "tmp%5#0" + ] + }, + "126": { + "op": "!", + "defined_out": [ + "tmp%6#0" + ], + "stack_out": [ + "tmp%6#0" + ] + }, + "127": { + "op": "assert // is creating", + "stack_out": [] + }, + "128": { + "op": "txna ApplicationArgs 1", + "defined_out": [ + "tmp%7#0" + ], + "stack_out": [ + "tmp%7#0" + ] + }, + "131": { + "op": "btoi", + "defined_out": [ + "tmp%8#0" + ], + "stack_out": [ + "tmp%8#0" + ] + }, + "132": { + "callsub": "smart_contracts.poa.contract.ProofOfAttendance.init", + "op": "callsub init", + "stack_out": [] + }, + "135": { + "op": "intc_1 // 1", + "defined_out": [ + "1" + ], + "stack_out": [ + "1" + ] + }, + "136": { + "retsub": true, + "op": "retsub" + }, + "137": { + "block": "__puya_arc4_router___confirm_attendance_route@3", + "stack_in": [], + "op": "txn OnCompletion", + "defined_out": [ + "tmp%9#0" + ], + "stack_out": [ + "tmp%9#0" + ] + }, + "139": { + "op": "!", + "defined_out": [ + "tmp%10#0" + ], + "stack_out": [ + "tmp%10#0" + ] + }, + "140": { + "op": "assert // OnCompletion is NoOp", + "stack_out": [] + }, + "141": { + "op": "txn ApplicationID", + "defined_out": [ + "tmp%11#0" + ], + "stack_out": [ + "tmp%11#0" + ] + }, + "143": { + "op": "assert // is not creating", + "stack_out": [] + }, + "144": { + "callsub": "smart_contracts.poa.contract.ProofOfAttendance.confirm_attendance", + "op": "callsub confirm_attendance" + }, + "147": { + "op": "intc_1 // 1", + "defined_out": [ + "1" + ], + "stack_out": [ + "1" + ] + }, + "148": { + "retsub": true, + "op": "retsub" + }, + "149": { + "block": "__puya_arc4_router___confirm_attendance_with_box_route@4", + "stack_in": [], + "op": "txn OnCompletion", + "defined_out": [ + "tmp%13#0" + ], + "stack_out": [ + "tmp%13#0" + ] + }, + "151": { + "op": "!", + "defined_out": [ + "tmp%14#0" + ], + "stack_out": [ + "tmp%14#0" + ] + }, + "152": { + "op": "assert // OnCompletion is NoOp", + "stack_out": [] + }, + "153": { + "op": "txn ApplicationID", + "defined_out": [ + "tmp%15#0" + ], + "stack_out": [ + "tmp%15#0" + ] + }, + "155": { + "op": "assert // is not creating", + "stack_out": [] + }, + "156": { + "callsub": "smart_contracts.poa.contract.ProofOfAttendance.confirm_attendance_with_box", + "op": "callsub confirm_attendance_with_box" + }, + "159": { + "op": "intc_1 // 1", + "defined_out": [ + "1" + ], + "stack_out": [ + "1" + ] + }, + "160": { + "retsub": true, + "op": "retsub" + }, + "161": { + "block": "__puya_arc4_router___confirm_attendance_with_box_ref_route@5", + "stack_in": [], + "op": "txn OnCompletion", + "defined_out": [ + "tmp%17#0" + ], + "stack_out": [ + "tmp%17#0" + ] + }, + "163": { + "op": "!", + "defined_out": [ + "tmp%18#0" + ], + "stack_out": [ + "tmp%18#0" + ] + }, + "164": { + "op": "assert // OnCompletion is NoOp", + "stack_out": [] + }, + "165": { + "op": "txn ApplicationID", + "defined_out": [ + "tmp%19#0" + ], + "stack_out": [ + "tmp%19#0" + ] + }, + "167": { + "op": "assert // is not creating", + "stack_out": [] + }, + "168": { + "callsub": "smart_contracts.poa.contract.ProofOfAttendance.confirm_attendance_with_box_ref", + "op": "callsub confirm_attendance_with_box_ref" + }, + "171": { + "op": "intc_1 // 1", + "defined_out": [ + "1" + ], + "stack_out": [ + "1" + ] + }, + "172": { + "retsub": true, + "op": "retsub" + }, + "173": { + "block": "__puya_arc4_router___confirm_attendance_with_box_map_route@6", + "stack_in": [], + "op": "txn OnCompletion", + "defined_out": [ + "tmp%21#0" + ], + "stack_out": [ + "tmp%21#0" + ] + }, + "175": { + "op": "!", + "defined_out": [ + "tmp%22#0" + ], + "stack_out": [ + "tmp%22#0" + ] + }, + "176": { + "op": "assert // OnCompletion is NoOp", + "stack_out": [] + }, + "177": { + "op": "txn ApplicationID", + "defined_out": [ + "tmp%23#0" + ], + "stack_out": [ + "tmp%23#0" + ] + }, + "179": { + "op": "assert // is not creating", + "stack_out": [] + }, + "180": { + "callsub": "smart_contracts.poa.contract.ProofOfAttendance.confirm_attendance_with_box_map", + "op": "callsub confirm_attendance_with_box_map" + }, + "183": { + "op": "intc_1 // 1", + "defined_out": [ + "1" + ], + "stack_out": [ + "1" + ] + }, + "184": { + "retsub": true, + "op": "retsub" + }, + "185": { + "block": "__puya_arc4_router___get_poa_id_route@7", + "stack_in": [], + "op": "txn OnCompletion", + "defined_out": [ + "tmp%25#0" + ], + "stack_out": [ + "tmp%25#0" + ] + }, + "187": { + "op": "!", + "defined_out": [ + "tmp%26#0" + ], + "stack_out": [ + "tmp%26#0" + ] + }, + "188": { + "op": "assert // OnCompletion is NoOp", + "stack_out": [] + }, + "189": { + "op": "txn ApplicationID", + "defined_out": [ + "tmp%27#0" + ], + "stack_out": [ + "tmp%27#0" + ] + }, + "191": { + "op": "assert // is not creating", + "stack_out": [] + }, + "192": { + "callsub": "smart_contracts.poa.contract.ProofOfAttendance.get_poa_id", + "op": "callsub get_poa_id", + "defined_out": [ + "tmp%29#0" + ], + "stack_out": [ + "tmp%29#0" + ] + }, + "195": { + "op": "itob", + "defined_out": [ + "val_as_bytes%0#0" + ], + "stack_out": [ + "val_as_bytes%0#0" + ] + }, + "196": { + "op": "bytec_2 // 0x151f7c75", + "defined_out": [ + "0x151f7c75", + "val_as_bytes%0#0" + ], + "stack_out": [ + "val_as_bytes%0#0", + "0x151f7c75" + ] + }, + "197": { + "op": "swap", + "stack_out": [ + "0x151f7c75", + "val_as_bytes%0#0" + ] + }, + "198": { + "op": "concat", + "defined_out": [ + "tmp%30#0" + ], + "stack_out": [ + "tmp%30#0" + ] + }, + "199": { + "op": "log", + "stack_out": [] + }, + "200": { + "op": "intc_1 // 1", + "defined_out": [ + "1" + ], + "stack_out": [ + "1" + ] + }, + "201": { + "retsub": true, + "op": "retsub" + }, + "202": { + "block": "__puya_arc4_router___get_poa_id_with_box_route@8", + "stack_in": [], + "op": "txn OnCompletion", + "defined_out": [ + "tmp%31#0" + ], + "stack_out": [ + "tmp%31#0" + ] + }, + "204": { + "op": "!", + "defined_out": [ + "tmp%32#0" + ], + "stack_out": [ + "tmp%32#0" + ] + }, + "205": { + "op": "assert // OnCompletion is NoOp", + "stack_out": [] + }, + "206": { + "op": "txn ApplicationID", + "defined_out": [ + "tmp%33#0" + ], + "stack_out": [ + "tmp%33#0" + ] + }, + "208": { + "op": "assert // is not creating", + "stack_out": [] + }, + "209": { + "callsub": "smart_contracts.poa.contract.ProofOfAttendance.get_poa_id_with_box", + "op": "callsub get_poa_id_with_box", + "defined_out": [ + "tmp%35#0" + ], + "stack_out": [ + "tmp%35#0" + ] + }, + "212": { + "op": "itob", + "defined_out": [ + "val_as_bytes%1#0" + ], + "stack_out": [ + "val_as_bytes%1#0" + ] + }, + "213": { + "op": "bytec_2 // 0x151f7c75", + "defined_out": [ + "0x151f7c75", + "val_as_bytes%1#0" + ], + "stack_out": [ + "val_as_bytes%1#0", + "0x151f7c75" + ] + }, + "214": { + "op": "swap", + "stack_out": [ + "0x151f7c75", + "val_as_bytes%1#0" + ] + }, + "215": { + "op": "concat", + "defined_out": [ + "tmp%36#0" + ], + "stack_out": [ + "tmp%36#0" + ] + }, + "216": { + "op": "log", + "stack_out": [] + }, + "217": { + "op": "intc_1 // 1", + "defined_out": [ + "1" + ], + "stack_out": [ + "1" + ] + }, + "218": { + "retsub": true, + "op": "retsub" + }, + "219": { + "block": "__puya_arc4_router___get_poa_id_with_box_ref_route@9", + "stack_in": [], + "op": "txn OnCompletion", + "defined_out": [ + "tmp%37#0" + ], + "stack_out": [ + "tmp%37#0" + ] + }, + "221": { + "op": "!", + "defined_out": [ + "tmp%38#0" + ], + "stack_out": [ + "tmp%38#0" + ] + }, + "222": { + "op": "assert // OnCompletion is NoOp", + "stack_out": [] + }, + "223": { + "op": "txn ApplicationID", + "defined_out": [ + "tmp%39#0" + ], + "stack_out": [ + "tmp%39#0" + ] + }, + "225": { + "op": "assert // is not creating", + "stack_out": [] + }, + "226": { + "callsub": "smart_contracts.poa.contract.ProofOfAttendance.get_poa_id_with_box_ref", + "op": "callsub get_poa_id_with_box_ref", + "defined_out": [ + "tmp%41#0" + ], + "stack_out": [ + "tmp%41#0" + ] + }, + "229": { + "op": "itob", + "defined_out": [ + "val_as_bytes%2#0" + ], + "stack_out": [ + "val_as_bytes%2#0" + ] + }, + "230": { + "op": "bytec_2 // 0x151f7c75", + "defined_out": [ + "0x151f7c75", + "val_as_bytes%2#0" + ], + "stack_out": [ + "val_as_bytes%2#0", + "0x151f7c75" + ] + }, + "231": { + "op": "swap", + "stack_out": [ + "0x151f7c75", + "val_as_bytes%2#0" + ] + }, + "232": { + "op": "concat", + "defined_out": [ + "tmp%42#0" + ], + "stack_out": [ + "tmp%42#0" + ] + }, + "233": { + "op": "log", + "stack_out": [] + }, + "234": { + "op": "intc_1 // 1", + "defined_out": [ + "1" + ], + "stack_out": [ + "1" + ] + }, + "235": { + "retsub": true, + "op": "retsub" + }, + "236": { + "block": "__puya_arc4_router___get_poa_id_with_box_map_route@10", + "stack_in": [], + "op": "txn OnCompletion", + "defined_out": [ + "tmp%43#0" + ], + "stack_out": [ + "tmp%43#0" + ] + }, + "238": { + "op": "!", + "defined_out": [ + "tmp%44#0" + ], + "stack_out": [ + "tmp%44#0" + ] + }, + "239": { + "op": "assert // OnCompletion is NoOp", + "stack_out": [] + }, + "240": { + "op": "txn ApplicationID", + "defined_out": [ + "tmp%45#0" + ], + "stack_out": [ + "tmp%45#0" + ] + }, + "242": { + "op": "assert // is not creating", + "stack_out": [] + }, + "243": { + "callsub": "smart_contracts.poa.contract.ProofOfAttendance.get_poa_id_with_box_map", + "op": "callsub get_poa_id_with_box_map", + "defined_out": [ + "tmp%47#0" + ], + "stack_out": [ + "tmp%47#0" + ] + }, + "246": { + "op": "itob", + "defined_out": [ + "val_as_bytes%3#0" + ], + "stack_out": [ + "val_as_bytes%3#0" + ] + }, + "247": { + "op": "bytec_2 // 0x151f7c75", + "defined_out": [ + "0x151f7c75", + "val_as_bytes%3#0" + ], + "stack_out": [ + "val_as_bytes%3#0", + "0x151f7c75" + ] + }, + "248": { + "op": "swap", + "stack_out": [ + "0x151f7c75", + "val_as_bytes%3#0" + ] + }, + "249": { + "op": "concat", + "defined_out": [ + "tmp%48#0" + ], + "stack_out": [ + "tmp%48#0" + ] + }, + "250": { + "op": "log", + "stack_out": [] + }, + "251": { + "op": "intc_1 // 1", + "defined_out": [ + "1" + ], + "stack_out": [ + "1" + ] + }, + "252": { + "retsub": true, + "op": "retsub" + }, + "253": { + "block": "__puya_arc4_router___claim_poa_route@11", + "stack_in": [], + "op": "txn OnCompletion", + "defined_out": [ + "tmp%49#0" + ], + "stack_out": [ + "tmp%49#0" + ] + }, + "255": { + "op": "!", + "defined_out": [ + "tmp%50#0" + ], + "stack_out": [ + "tmp%50#0" + ] + }, + "256": { + "op": "assert // OnCompletion is NoOp", + "stack_out": [] + }, + "257": { + "op": "txn ApplicationID", + "defined_out": [ + "tmp%51#0" + ], + "stack_out": [ + "tmp%51#0" + ] + }, + "259": { + "op": "assert // is not creating", + "stack_out": [] + }, + "260": { + "op": "txn GroupIndex", + "defined_out": [ + "tmp%53#0" + ], + "stack_out": [ + "tmp%53#0" + ] + }, + "262": { + "op": "intc_1 // 1", + "defined_out": [ + "1", + "tmp%53#0" + ], + "stack_out": [ + "tmp%53#0", + "1" + ] + }, + "263": { + "op": "-", + "defined_out": [ + "gtxn_idx%0#0" + ], + "stack_out": [ + "gtxn_idx%0#0" + ] + }, + "264": { + "op": "dup", + "defined_out": [ + "gtxn_idx%0#0", + "gtxn_idx%0#0 (copy)" + ], + "stack_out": [ + "gtxn_idx%0#0", + "gtxn_idx%0#0 (copy)" + ] + }, + "265": { + "op": "gtxns TypeEnum", + "defined_out": [ + "gtxn_idx%0#0", + "gtxn_type%0#0" + ], + "stack_out": [ + "gtxn_idx%0#0", + "gtxn_type%0#0" + ] + }, + "267": { + "op": "intc_2 // axfer", + "defined_out": [ + "axfer", + "gtxn_idx%0#0", + "gtxn_type%0#0" + ], + "stack_out": [ + "gtxn_idx%0#0", + "gtxn_type%0#0", + "axfer" + ] + }, + "268": { + "op": "==", + "defined_out": [ + "gtxn_idx%0#0", + "gtxn_type_matches%0#0" + ], + "stack_out": [ + "gtxn_idx%0#0", + "gtxn_type_matches%0#0" + ] + }, + "269": { + "op": "assert // transaction type is axfer", + "stack_out": [ + "gtxn_idx%0#0" + ] + }, + "270": { + "callsub": "smart_contracts.poa.contract.ProofOfAttendance.claim_poa", + "op": "callsub claim_poa", + "stack_out": [] + }, + "273": { + "op": "intc_1 // 1", + "stack_out": [ + "1" + ] + }, + "274": { + "retsub": true, + "op": "retsub" + }, + "275": { + "block": "__puya_arc4_router___claim_poa_with_box_route@12", + "stack_in": [], + "op": "txn OnCompletion", + "defined_out": [ + "tmp%54#0" + ], + "stack_out": [ + "tmp%54#0" + ] + }, + "277": { + "op": "!", + "defined_out": [ + "tmp%55#0" + ], + "stack_out": [ + "tmp%55#0" + ] + }, + "278": { + "op": "assert // OnCompletion is NoOp", + "stack_out": [] + }, + "279": { + "op": "txn ApplicationID", + "defined_out": [ + "tmp%56#0" + ], + "stack_out": [ + "tmp%56#0" + ] + }, + "281": { + "op": "assert // is not creating", + "stack_out": [] + }, + "282": { + "op": "txn GroupIndex", + "defined_out": [ + "tmp%58#0" + ], + "stack_out": [ + "tmp%58#0" + ] + }, + "284": { + "op": "intc_1 // 1", + "defined_out": [ + "1", + "tmp%58#0" + ], + "stack_out": [ + "tmp%58#0", + "1" + ] + }, + "285": { + "op": "-", + "defined_out": [ + "gtxn_idx%1#0" + ], + "stack_out": [ + "gtxn_idx%1#0" + ] + }, + "286": { + "op": "dup", + "defined_out": [ + "gtxn_idx%1#0", + "gtxn_idx%1#0 (copy)" + ], + "stack_out": [ + "gtxn_idx%1#0", + "gtxn_idx%1#0 (copy)" + ] + }, + "287": { + "op": "gtxns TypeEnum", + "defined_out": [ + "gtxn_idx%1#0", + "gtxn_type%1#0" + ], + "stack_out": [ + "gtxn_idx%1#0", + "gtxn_type%1#0" + ] + }, + "289": { + "op": "intc_2 // axfer", + "defined_out": [ + "axfer", + "gtxn_idx%1#0", + "gtxn_type%1#0" + ], + "stack_out": [ + "gtxn_idx%1#0", + "gtxn_type%1#0", + "axfer" + ] + }, + "290": { + "op": "==", + "defined_out": [ + "gtxn_idx%1#0", + "gtxn_type_matches%1#0" + ], + "stack_out": [ + "gtxn_idx%1#0", + "gtxn_type_matches%1#0" + ] + }, + "291": { + "op": "assert // transaction type is axfer", + "stack_out": [ + "gtxn_idx%1#0" + ] + }, + "292": { + "callsub": "smart_contracts.poa.contract.ProofOfAttendance.claim_poa_with_box", + "op": "callsub claim_poa_with_box", + "stack_out": [] + }, + "295": { + "op": "intc_1 // 1", + "stack_out": [ + "1" + ] + }, + "296": { + "retsub": true, + "op": "retsub" + }, + "297": { + "block": "__puya_arc4_router___claim_poa_with_box_ref_route@13", + "stack_in": [], + "op": "txn OnCompletion", + "defined_out": [ + "tmp%59#0" + ], + "stack_out": [ + "tmp%59#0" + ] + }, + "299": { + "op": "!", + "defined_out": [ + "tmp%60#0" + ], + "stack_out": [ + "tmp%60#0" + ] + }, + "300": { + "op": "assert // OnCompletion is NoOp", + "stack_out": [] + }, + "301": { + "op": "txn ApplicationID", + "defined_out": [ + "tmp%61#0" + ], + "stack_out": [ + "tmp%61#0" + ] + }, + "303": { + "op": "assert // is not creating", + "stack_out": [] + }, + "304": { + "op": "txn GroupIndex", + "defined_out": [ + "tmp%63#0" + ], + "stack_out": [ + "tmp%63#0" + ] + }, + "306": { + "op": "intc_1 // 1", + "defined_out": [ + "1", + "tmp%63#0" + ], + "stack_out": [ + "tmp%63#0", + "1" + ] + }, + "307": { + "op": "-", + "defined_out": [ + "gtxn_idx%2#0" + ], + "stack_out": [ + "gtxn_idx%2#0" + ] + }, + "308": { + "op": "dup", + "defined_out": [ + "gtxn_idx%2#0", + "gtxn_idx%2#0 (copy)" + ], + "stack_out": [ + "gtxn_idx%2#0", + "gtxn_idx%2#0 (copy)" + ] + }, + "309": { + "op": "gtxns TypeEnum", + "defined_out": [ + "gtxn_idx%2#0", + "gtxn_type%2#0" + ], + "stack_out": [ + "gtxn_idx%2#0", + "gtxn_type%2#0" + ] + }, + "311": { + "op": "intc_2 // axfer", + "defined_out": [ + "axfer", + "gtxn_idx%2#0", + "gtxn_type%2#0" + ], + "stack_out": [ + "gtxn_idx%2#0", + "gtxn_type%2#0", + "axfer" + ] + }, + "312": { + "op": "==", + "defined_out": [ + "gtxn_idx%2#0", + "gtxn_type_matches%2#0" + ], + "stack_out": [ + "gtxn_idx%2#0", + "gtxn_type_matches%2#0" + ] + }, + "313": { + "op": "assert // transaction type is axfer", + "stack_out": [ + "gtxn_idx%2#0" + ] + }, + "314": { + "callsub": "smart_contracts.poa.contract.ProofOfAttendance.claim_poa_with_box_ref", + "op": "callsub claim_poa_with_box_ref", + "stack_out": [] + }, + "317": { + "op": "intc_1 // 1", + "stack_out": [ + "1" + ] + }, + "318": { + "retsub": true, + "op": "retsub" + }, + "319": { + "block": "__puya_arc4_router___claim_poa_with_box_map_route@14", + "stack_in": [], + "op": "txn OnCompletion", + "defined_out": [ + "tmp%64#0" + ], + "stack_out": [ + "tmp%64#0" + ] + }, + "321": { + "op": "!", + "defined_out": [ + "tmp%65#0" + ], + "stack_out": [ + "tmp%65#0" + ] + }, + "322": { + "op": "assert // OnCompletion is NoOp", + "stack_out": [] + }, + "323": { + "op": "txn ApplicationID", + "defined_out": [ + "tmp%66#0" + ], + "stack_out": [ + "tmp%66#0" + ] + }, + "325": { + "op": "assert // is not creating", + "stack_out": [] + }, + "326": { + "op": "txn GroupIndex", + "defined_out": [ + "tmp%68#0" + ], + "stack_out": [ + "tmp%68#0" + ] + }, + "328": { + "op": "intc_1 // 1", + "defined_out": [ + "1", + "tmp%68#0" + ], + "stack_out": [ + "tmp%68#0", + "1" + ] + }, + "329": { + "op": "-", + "defined_out": [ + "gtxn_idx%3#0" + ], + "stack_out": [ + "gtxn_idx%3#0" + ] + }, + "330": { + "op": "dup", + "defined_out": [ + "gtxn_idx%3#0", + "gtxn_idx%3#0 (copy)" + ], + "stack_out": [ + "gtxn_idx%3#0", + "gtxn_idx%3#0 (copy)" + ] + }, + "331": { + "op": "gtxns TypeEnum", + "defined_out": [ + "gtxn_idx%3#0", + "gtxn_type%3#0" + ], + "stack_out": [ + "gtxn_idx%3#0", + "gtxn_type%3#0" + ] + }, + "333": { + "op": "intc_2 // axfer", + "defined_out": [ + "axfer", + "gtxn_idx%3#0", + "gtxn_type%3#0" + ], + "stack_out": [ + "gtxn_idx%3#0", + "gtxn_type%3#0", + "axfer" + ] + }, + "334": { + "op": "==", + "defined_out": [ + "gtxn_idx%3#0", + "gtxn_type_matches%3#0" + ], + "stack_out": [ + "gtxn_idx%3#0", + "gtxn_type_matches%3#0" + ] + }, + "335": { + "op": "assert // transaction type is axfer", + "stack_out": [ + "gtxn_idx%3#0" + ] + }, + "336": { + "callsub": "smart_contracts.poa.contract.ProofOfAttendance.claim_poa_with_box_map", + "op": "callsub claim_poa_with_box_map", + "stack_out": [] + }, + "339": { + "op": "intc_1 // 1", + "stack_out": [ + "1" + ] + }, + "340": { + "retsub": true, + "op": "retsub" + }, + "341": { + "block": "__puya_arc4_router___after_if_else@18", + "stack_in": [], + "op": "intc_0 // 0", + "defined_out": [ + "0" + ], + "stack_out": [ + "0" + ] + }, + "342": { + "retsub": true, + "op": "retsub" + }, + "343": { + "subroutine": "smart_contracts.poa.contract.ProofOfAttendance.init", + "params": { + "max_attendees#0": "uint64" + }, + "block": "init", + "stack_in": [], + "op": "proto 1 0" + }, + "346": { + "op": "txn Sender" + }, + "348": { + "op": "global CreatorAddress", + "defined_out": [ + "tmp%0#0", + "tmp%1#0" + ], + "stack_out": [ + "tmp%0#0", + "tmp%1#0" + ] + }, + "350": { + "op": "==", + "defined_out": [ + "tmp%2#0" + ], + "stack_out": [ + "tmp%2#0" + ] + }, + "351": { + "op": "assert // Only creator can initialize", + "stack_out": [] + }, + "352": { + "op": "bytec_1 // \"max_attendees\"", + "defined_out": [ + "\"max_attendees\"" + ], + "stack_out": [ + "\"max_attendees\"" + ] + }, + "353": { + "op": "frame_dig -1", + "defined_out": [ + "\"max_attendees\"", + "max_attendees#0 (copy)" + ], + "stack_out": [ + "\"max_attendees\"", + "max_attendees#0 (copy)" + ] + }, + "355": { + "op": "app_global_put", + "stack_out": [] + }, + "356": { + "retsub": true, + "op": "retsub" + }, + "357": { + "subroutine": "smart_contracts.poa.contract.ProofOfAttendance.confirm_attendance", + "params": {}, + "block": "confirm_attendance", + "stack_in": [], + "op": "proto 0 0" + }, + "360": { + "op": "intc_0 // 0", + "defined_out": [ + "0" + ], + "stack_out": [ + "0" + ] + }, + "361": { + "op": "bytec_0 // \"total_attendees\"", + "defined_out": [ + "\"total_attendees\"", + "0" + ], + "stack_out": [ + "0", + "\"total_attendees\"" + ] + }, + "362": { + "op": "app_global_get_ex", + "defined_out": [ + "maybe_exists%0#0", + "maybe_value%0#0" + ], + "stack_out": [ + "maybe_value%0#0", + "maybe_exists%0#0" + ] + }, + "363": { + "op": "assert // check self.total_attendees exists", + "stack_out": [ + "maybe_value%0#0" + ] + }, + "364": { + "op": "intc_0 // 0", + "stack_out": [ + "maybe_value%0#0", + "0" + ] + }, + "365": { + "op": "bytec_1 // \"max_attendees\"", + "defined_out": [ + "\"max_attendees\"", + "0", + "maybe_value%0#0" + ], + "stack_out": [ + "maybe_value%0#0", + "0", + "\"max_attendees\"" + ] + }, + "366": { + "op": "app_global_get_ex", + "defined_out": [ + "maybe_exists%1#0", + "maybe_value%0#0", + "maybe_value%1#0" + ], + "stack_out": [ + "maybe_value%0#0", + "maybe_value%1#0", + "maybe_exists%1#0" + ] + }, + "367": { + "op": "assert // check self.max_attendees exists", + "stack_out": [ + "maybe_value%0#0", + "maybe_value%1#0" + ] + }, + "368": { + "op": "<", + "defined_out": [ + "tmp%0#0" + ], + "stack_out": [ + "tmp%0#0" + ] + }, + "369": { + "op": "assert // Max attendees reached", + "stack_out": [] + }, + "370": { + "op": "txn Sender", + "defined_out": [ + "tmp%1#0" + ], + "stack_out": [ + "tmp%1#0" + ] + }, + "372": { + "callsub": "smart_contracts.poa.contract.ProofOfAttendance._mint_poa", + "op": "callsub _mint_poa", + "defined_out": [ + "minted_asset#0" + ], + "stack_out": [ + "minted_asset#0" + ] + }, + "375": { + "op": "intc_0 // 0", + "stack_out": [ + "minted_asset#0", + "0" + ] + }, + "376": { + "op": "bytec_0 // \"total_attendees\"", + "stack_out": [ + "minted_asset#0", + "0", + "\"total_attendees\"" + ] + }, + "377": { + "op": "app_global_get_ex", + "defined_out": [ + "maybe_exists%2#0", + "maybe_value%2#0", + "minted_asset#0" + ], + "stack_out": [ + "minted_asset#0", + "maybe_value%2#0", + "maybe_exists%2#0" + ] + }, + "378": { + "op": "assert // check self.total_attendees exists", + "stack_out": [ + "minted_asset#0", + "maybe_value%2#0" + ] + }, + "379": { + "op": "intc_3 // TMPL_INCREMENT", + "defined_out": [ + "TMPL_INCREMENT", + "maybe_value%2#0", + "minted_asset#0" + ], + "stack_out": [ + "minted_asset#0", + "maybe_value%2#0", + "TMPL_INCREMENT" + ] + }, + "380": { + "op": "+", + "defined_out": [ + "minted_asset#0", + "new_state_value%0#0" + ], + "stack_out": [ + "minted_asset#0", + "new_state_value%0#0" + ] + }, + "381": { + "op": "bytec_0 // \"total_attendees\"", + "stack_out": [ + "minted_asset#0", + "new_state_value%0#0", + "\"total_attendees\"" + ] + }, + "382": { + "op": "swap", + "stack_out": [ + "minted_asset#0", + "\"total_attendees\"", + "new_state_value%0#0" + ] + }, + "383": { + "op": "app_global_put", + "stack_out": [ + "minted_asset#0" + ] + }, + "384": { + "op": "txn Sender", + "defined_out": [ + "minted_asset#0", + "tmp%2#0" + ], + "stack_out": [ + "minted_asset#0", + "tmp%2#0" + ] + }, + "386": { + "op": "box_get", + "defined_out": [ + "_id#0", + "has_claimed#0", + "minted_asset#0" + ], + "stack_out": [ + "minted_asset#0", + "_id#0", + "has_claimed#0" + ] + }, + "387": { + "op": "bury 1", + "stack_out": [ + "minted_asset#0", + "has_claimed#0" + ] + }, + "389": { + "op": "!", + "defined_out": [ + "minted_asset#0", + "tmp%3#0" + ], + "stack_out": [ + "minted_asset#0", + "tmp%3#0" + ] + }, + "390": { + "op": "assert // Already claimed POA", + "stack_out": [ + "minted_asset#0" + ] + }, + "391": { + "op": "txn Sender", + "defined_out": [ + "minted_asset#0", + "tmp%4#0" + ], + "stack_out": [ + "minted_asset#0", + "tmp%4#0" + ] + }, + "393": { + "op": "swap", + "stack_out": [ + "tmp%4#0", + "minted_asset#0" + ] + }, + "394": { + "op": "itob", + "defined_out": [ + "tmp%4#0", + "tmp%5#0" + ], + "stack_out": [ + "tmp%4#0", + "tmp%5#0" + ] + }, + "395": { + "op": "box_put", + "stack_out": [] + }, + "396": { + "retsub": true, + "op": "retsub" + }, + "397": { + "subroutine": "smart_contracts.poa.contract.ProofOfAttendance._mint_poa", + "params": { + "claimer#0": "bytes" + }, + "block": "_mint_poa", + "stack_in": [], + "op": "proto 1 1" + }, + "400": { + "op": "pushint 10000 // 10000", + "defined_out": [ + "10000" + ], + "stack_out": [ + "10000" + ] + }, + "403": { + "op": "intc_1 // 1", + "defined_out": [ + "1", + "10000" + ], + "stack_out": [ + "10000", + "1" + ] + }, + "404": { + "callsub": "_puya_lib.util.ensure_budget", + "op": "callsub ensure_budget", + "stack_out": [] + }, + "407": { + "op": "intc_0 // 0", + "defined_out": [ + "0" + ], + "stack_out": [ + "0" + ] + }, + "408": { + "op": "bytec_0 // \"total_attendees\"", + "defined_out": [ + "\"total_attendees\"", + "0" + ], + "stack_out": [ + "0", + "\"total_attendees\"" + ] + }, + "409": { + "op": "app_global_get_ex", + "defined_out": [ + "maybe_exists%0#0", + "maybe_value%0#0" + ], + "stack_out": [ + "maybe_value%0#0", + "maybe_exists%0#0" + ] + }, + "410": { + "op": "assert // check self.total_attendees exists", + "stack_out": [ + "maybe_value%0#0" + ] + }, + "411": { + "op": "itob", + "defined_out": [ + "tmp%0#0" + ], + "stack_out": [ + "tmp%0#0" + ] + }, + "412": { + "op": "pushbytes 0x416c676f4b697420504f412023", + "defined_out": [ + "0x416c676f4b697420504f412023", + "tmp%0#0" + ], + "stack_out": [ + "tmp%0#0", + "0x416c676f4b697420504f412023" + ] + }, + "427": { + "op": "swap", + "stack_out": [ + "0x416c676f4b697420504f412023", + "tmp%0#0" + ] + }, + "428": { + "op": "concat", + "defined_out": [ + "asset_name#0" + ], + "stack_out": [ + "asset_name#0" + ] + }, + "429": { + "op": "itxn_begin" + }, + "430": { + "op": "intc_0 // 0", + "stack_out": [ + "asset_name#0", + "0" + ] + }, + "431": { + "op": "bytec 4 // \"asset_url\"", + "defined_out": [ + "\"asset_url\"", + "0", + "asset_name#0" + ], + "stack_out": [ + "asset_name#0", + "0", + "\"asset_url\"" + ] + }, + "433": { + "op": "app_global_get_ex", + "defined_out": [ + "asset_name#0", + "maybe_exists%1#0", + "maybe_value%1#0" + ], + "stack_out": [ + "asset_name#0", + "maybe_value%1#0", + "maybe_exists%1#0" + ] + }, + "434": { + "op": "assert // check self.asset_url exists", + "stack_out": [ + "asset_name#0", + "maybe_value%1#0" + ] + }, + "435": { + "op": "frame_dig -1", + "defined_out": [ + "asset_name#0", + "claimer#0 (copy)", + "maybe_value%1#0" + ], + "stack_out": [ + "asset_name#0", + "maybe_value%1#0", + "claimer#0 (copy)" + ] + }, + "437": { + "op": "itxn_field ConfigAssetManager", + "stack_out": [ + "asset_name#0", + "maybe_value%1#0" + ] + }, + "439": { + "op": "itxn_field ConfigAssetURL", + "stack_out": [ + "asset_name#0" + ] + }, + "441": { + "op": "intc_0 // 0", + "stack_out": [ + "asset_name#0", + "0" + ] + }, + "442": { + "op": "itxn_field ConfigAssetDecimals", + "stack_out": [ + "asset_name#0" + ] + }, + "444": { + "op": "intc_1 // 1", + "stack_out": [ + "asset_name#0", + "1" + ] + }, + "445": { + "op": "itxn_field ConfigAssetTotal", + "stack_out": [ + "asset_name#0" + ] + }, + "447": { + "op": "pushbytes \"POA\"", + "defined_out": [ + "\"POA\"", + "asset_name#0" + ], + "stack_out": [ + "asset_name#0", + "\"POA\"" + ] + }, + "452": { + "op": "itxn_field ConfigAssetUnitName", + "stack_out": [ + "asset_name#0" + ] + }, + "454": { + "op": "itxn_field ConfigAssetName", + "stack_out": [] + }, + "456": { + "op": "pushint 3 // acfg", + "defined_out": [ + "acfg" + ], + "stack_out": [ + "acfg" + ] + }, + "458": { + "op": "itxn_field TypeEnum", + "stack_out": [] + }, + "460": { + "op": "intc_0 // 0", + "stack_out": [ + "0" + ] + }, + "461": { + "op": "itxn_field Fee", + "stack_out": [] + }, + "463": { + "op": "itxn_submit" + }, + "464": { + "op": "itxn CreatedAssetID", + "defined_out": [ + "tmp%1#0" + ], + "stack_out": [ + "tmp%1#0" + ] + }, + "466": { + "retsub": true, + "op": "retsub" + }, + "467": { + "subroutine": "smart_contracts.poa.contract.ProofOfAttendance.confirm_attendance_with_box", + "params": {}, + "block": "confirm_attendance_with_box", + "stack_in": [], + "op": "proto 0 0" + }, + "470": { + "op": "intc_0 // 0", + "defined_out": [ + "0" + ], + "stack_out": [ + "0" + ] + }, + "471": { + "op": "bytec_0 // \"total_attendees\"", + "defined_out": [ + "\"total_attendees\"", + "0" + ], + "stack_out": [ + "0", + "\"total_attendees\"" + ] + }, + "472": { + "op": "app_global_get_ex", + "defined_out": [ + "maybe_exists%0#0", + "maybe_value%0#0" + ], + "stack_out": [ + "maybe_value%0#0", + "maybe_exists%0#0" + ] + }, + "473": { + "op": "assert // check self.total_attendees exists", + "stack_out": [ + "maybe_value%0#0" + ] + }, + "474": { + "op": "intc_0 // 0", + "stack_out": [ + "maybe_value%0#0", + "0" + ] + }, + "475": { + "op": "bytec_1 // \"max_attendees\"", + "defined_out": [ + "\"max_attendees\"", + "0", + "maybe_value%0#0" + ], + "stack_out": [ + "maybe_value%0#0", + "0", + "\"max_attendees\"" + ] + }, + "476": { + "op": "app_global_get_ex", + "defined_out": [ + "maybe_exists%1#0", + "maybe_value%0#0", + "maybe_value%1#0" + ], + "stack_out": [ + "maybe_value%0#0", + "maybe_value%1#0", + "maybe_exists%1#0" + ] + }, + "477": { + "op": "assert // check self.max_attendees exists", + "stack_out": [ + "maybe_value%0#0", + "maybe_value%1#0" + ] + }, + "478": { + "op": "<", + "defined_out": [ + "tmp%0#0" + ], + "stack_out": [ + "tmp%0#0" + ] + }, + "479": { + "op": "assert // Max attendees reached", + "stack_out": [] + }, + "480": { + "op": "txn Sender", + "defined_out": [ + "tmp%1#0" + ], + "stack_out": [ + "tmp%1#0" + ] + }, + "482": { + "callsub": "smart_contracts.poa.contract.ProofOfAttendance._mint_poa", + "op": "callsub _mint_poa", + "defined_out": [ + "minted_asset#0" + ], + "stack_out": [ + "minted_asset#0" + ] + }, + "485": { + "op": "intc_0 // 0", + "stack_out": [ + "minted_asset#0", + "0" + ] + }, + "486": { + "op": "bytec_0 // \"total_attendees\"", + "stack_out": [ + "minted_asset#0", + "0", + "\"total_attendees\"" + ] + }, + "487": { + "op": "app_global_get_ex", + "defined_out": [ + "maybe_exists%2#0", + "maybe_value%2#0", + "minted_asset#0" + ], + "stack_out": [ + "minted_asset#0", + "maybe_value%2#0", + "maybe_exists%2#0" + ] + }, + "488": { + "op": "assert // check self.total_attendees exists", + "stack_out": [ + "minted_asset#0", + "maybe_value%2#0" + ] + }, + "489": { + "op": "intc_1 // 1", + "defined_out": [ + "1", + "maybe_value%2#0", + "minted_asset#0" + ], + "stack_out": [ + "minted_asset#0", + "maybe_value%2#0", + "1" + ] + }, + "490": { + "op": "+", + "defined_out": [ + "minted_asset#0", + "new_state_value%0#0" + ], + "stack_out": [ + "minted_asset#0", + "new_state_value%0#0" + ] + }, + "491": { + "op": "bytec_0 // \"total_attendees\"", + "stack_out": [ + "minted_asset#0", + "new_state_value%0#0", + "\"total_attendees\"" + ] + }, + "492": { + "op": "swap", + "stack_out": [ + "minted_asset#0", + "\"total_attendees\"", + "new_state_value%0#0" + ] + }, + "493": { + "op": "app_global_put", + "stack_out": [ + "minted_asset#0" + ] + }, + "494": { + "op": "txn Sender" + }, + "496": { + "op": "dup", + "defined_out": [ + "box#0", + "box#0 (copy)", + "minted_asset#0" + ], + "stack_out": [ + "minted_asset#0", + "box#0", + "box#0 (copy)" + ] + }, + "497": { + "op": "box_len", + "defined_out": [ + "box#0", + "has_claimed#0", + "maybe_value%3#0", + "minted_asset#0" + ], + "stack_out": [ + "minted_asset#0", + "box#0", + "maybe_value%3#0", + "has_claimed#0" + ] + }, + "498": { + "op": "bury 1", + "stack_out": [ + "minted_asset#0", + "box#0", + "has_claimed#0" + ] + }, + "500": { + "op": "!", + "defined_out": [ + "box#0", + "minted_asset#0", + "tmp%2#0" + ], + "stack_out": [ + "minted_asset#0", + "box#0", + "tmp%2#0" + ] + }, + "501": { + "op": "assert // Already claimed POA", + "stack_out": [ + "minted_asset#0", + "box#0" + ] + }, + "502": { + "op": "swap", + "stack_out": [ + "box#0", + "minted_asset#0" + ] + }, + "503": { + "op": "itob", + "defined_out": [ + "box#0", + "new_box_value%0#0" + ], + "stack_out": [ + "box#0", + "new_box_value%0#0" + ] + }, + "504": { + "op": "box_put", + "stack_out": [] + }, + "505": { + "retsub": true, + "op": "retsub" + }, + "506": { + "subroutine": "smart_contracts.poa.contract.ProofOfAttendance.confirm_attendance_with_box_ref", + "params": {}, + "block": "confirm_attendance_with_box_ref", + "stack_in": [], + "op": "proto 0 0" + }, + "509": { + "op": "intc_0 // 0", + "defined_out": [ + "0" + ], + "stack_out": [ + "0" + ] + }, + "510": { + "op": "bytec_0 // \"total_attendees\"", + "defined_out": [ + "\"total_attendees\"", + "0" + ], + "stack_out": [ + "0", + "\"total_attendees\"" + ] + }, + "511": { + "op": "app_global_get_ex", + "defined_out": [ + "maybe_exists%0#0", + "maybe_value%0#0" + ], + "stack_out": [ + "maybe_value%0#0", + "maybe_exists%0#0" + ] + }, + "512": { + "op": "assert // check self.total_attendees exists", + "stack_out": [ + "maybe_value%0#0" + ] + }, + "513": { + "op": "intc_0 // 0", + "stack_out": [ + "maybe_value%0#0", + "0" + ] + }, + "514": { + "op": "bytec_1 // \"max_attendees\"", + "defined_out": [ + "\"max_attendees\"", + "0", + "maybe_value%0#0" + ], + "stack_out": [ + "maybe_value%0#0", + "0", + "\"max_attendees\"" + ] + }, + "515": { + "op": "app_global_get_ex", + "defined_out": [ + "maybe_exists%1#0", + "maybe_value%0#0", + "maybe_value%1#0" + ], + "stack_out": [ + "maybe_value%0#0", + "maybe_value%1#0", + "maybe_exists%1#0" + ] + }, + "516": { + "op": "assert // check self.max_attendees exists", + "stack_out": [ + "maybe_value%0#0", + "maybe_value%1#0" + ] + }, + "517": { + "op": "<", + "defined_out": [ + "tmp%0#0" + ], + "stack_out": [ + "tmp%0#0" + ] + }, + "518": { + "op": "assert // Max attendees reached", + "stack_out": [] + }, + "519": { + "op": "txn Sender", + "defined_out": [ + "tmp%1#0" + ], + "stack_out": [ + "tmp%1#0" + ] + }, + "521": { + "callsub": "smart_contracts.poa.contract.ProofOfAttendance._mint_poa", + "op": "callsub _mint_poa", + "defined_out": [ + "minted_asset#0" + ], + "stack_out": [ + "minted_asset#0" + ] + }, + "524": { + "op": "intc_0 // 0", + "stack_out": [ + "minted_asset#0", + "0" + ] + }, + "525": { + "op": "bytec_0 // \"total_attendees\"", + "stack_out": [ + "minted_asset#0", + "0", + "\"total_attendees\"" + ] + }, + "526": { + "op": "app_global_get_ex", + "defined_out": [ + "maybe_exists%2#0", + "maybe_value%2#0", + "minted_asset#0" + ], + "stack_out": [ + "minted_asset#0", + "maybe_value%2#0", + "maybe_exists%2#0" + ] + }, + "527": { + "op": "assert // check self.total_attendees exists", + "stack_out": [ + "minted_asset#0", + "maybe_value%2#0" + ] + }, + "528": { + "op": "intc_1 // 1", + "defined_out": [ + "1", + "maybe_value%2#0", + "minted_asset#0" + ], + "stack_out": [ + "minted_asset#0", + "maybe_value%2#0", + "1" + ] + }, + "529": { + "op": "+", + "defined_out": [ + "minted_asset#0", + "new_state_value%0#0" + ], + "stack_out": [ + "minted_asset#0", + "new_state_value%0#0" + ] + }, + "530": { + "op": "bytec_0 // \"total_attendees\"", + "stack_out": [ + "minted_asset#0", + "new_state_value%0#0", + "\"total_attendees\"" + ] + }, + "531": { + "op": "swap", + "stack_out": [ + "minted_asset#0", + "\"total_attendees\"", + "new_state_value%0#0" + ] + }, + "532": { + "op": "app_global_put", + "stack_out": [ + "minted_asset#0" + ] + }, + "533": { + "op": "txn Sender" + }, + "535": { + "op": "dup", + "defined_out": [ + "box_ref#0", + "box_ref#0 (copy)", + "minted_asset#0" + ], + "stack_out": [ + "minted_asset#0", + "box_ref#0", + "box_ref#0 (copy)" + ] + }, + "536": { + "op": "box_len", + "defined_out": [ + "box_ref#0", + "has_claimed#0", + "maybe_value%3#0", + "minted_asset#0" + ], + "stack_out": [ + "minted_asset#0", + "box_ref#0", + "maybe_value%3#0", + "has_claimed#0" + ] + }, + "537": { + "op": "bury 1", + "stack_out": [ + "minted_asset#0", + "box_ref#0", + "has_claimed#0" + ] + }, + "539": { + "op": "!", + "defined_out": [ + "box_ref#0", + "minted_asset#0", + "tmp%2#0" + ], + "stack_out": [ + "minted_asset#0", + "box_ref#0", + "tmp%2#0" + ] + }, + "540": { + "op": "assert // Already claimed POA", + "stack_out": [ + "minted_asset#0", + "box_ref#0" + ] + }, + "541": { + "op": "swap", + "stack_out": [ + "box_ref#0", + "minted_asset#0" + ] + }, + "542": { + "op": "itob", + "defined_out": [ + "box_ref#0", + "tmp%3#0" + ], + "stack_out": [ + "box_ref#0", + "tmp%3#0" + ] + }, + "543": { + "op": "box_put", + "stack_out": [] + }, + "544": { + "retsub": true, + "op": "retsub" + }, + "545": { + "subroutine": "smart_contracts.poa.contract.ProofOfAttendance.confirm_attendance_with_box_map", + "params": {}, + "block": "confirm_attendance_with_box_map", + "stack_in": [], + "op": "proto 0 0" + }, + "548": { + "op": "intc_0 // 0", + "defined_out": [ + "0" + ], + "stack_out": [ + "0" + ] + }, + "549": { + "op": "bytec_0 // \"total_attendees\"", + "defined_out": [ + "\"total_attendees\"", + "0" + ], + "stack_out": [ + "0", + "\"total_attendees\"" + ] + }, + "550": { + "op": "app_global_get_ex", + "defined_out": [ + "maybe_exists%0#0", + "maybe_value%0#0" + ], + "stack_out": [ + "maybe_value%0#0", + "maybe_exists%0#0" + ] + }, + "551": { + "op": "assert // check self.total_attendees exists", + "stack_out": [ + "maybe_value%0#0" + ] + }, + "552": { + "op": "intc_0 // 0", + "stack_out": [ + "maybe_value%0#0", + "0" + ] + }, + "553": { + "op": "bytec_1 // \"max_attendees\"", + "defined_out": [ + "\"max_attendees\"", + "0", + "maybe_value%0#0" + ], + "stack_out": [ + "maybe_value%0#0", + "0", + "\"max_attendees\"" + ] + }, + "554": { + "op": "app_global_get_ex", + "defined_out": [ + "maybe_exists%1#0", + "maybe_value%0#0", + "maybe_value%1#0" + ], + "stack_out": [ + "maybe_value%0#0", + "maybe_value%1#0", + "maybe_exists%1#0" + ] + }, + "555": { + "op": "assert // check self.max_attendees exists", + "stack_out": [ + "maybe_value%0#0", + "maybe_value%1#0" + ] + }, + "556": { + "op": "<", + "defined_out": [ + "tmp%0#0" + ], + "stack_out": [ + "tmp%0#0" + ] + }, + "557": { + "op": "assert // Max attendees reached", + "stack_out": [] + }, + "558": { + "op": "txn Sender", + "defined_out": [ + "tmp%1#0" + ], + "stack_out": [ + "tmp%1#0" + ] + }, + "560": { + "callsub": "smart_contracts.poa.contract.ProofOfAttendance._mint_poa", + "op": "callsub _mint_poa", + "defined_out": [ + "minted_asset#0" + ], + "stack_out": [ + "minted_asset#0" + ] + }, + "563": { + "op": "intc_0 // 0", + "stack_out": [ + "minted_asset#0", + "0" + ] + }, + "564": { + "op": "bytec_0 // \"total_attendees\"", + "stack_out": [ + "minted_asset#0", + "0", + "\"total_attendees\"" + ] + }, + "565": { + "op": "app_global_get_ex", + "defined_out": [ + "maybe_exists%2#0", + "maybe_value%2#0", + "minted_asset#0" + ], + "stack_out": [ + "minted_asset#0", + "maybe_value%2#0", + "maybe_exists%2#0" + ] + }, + "566": { + "op": "assert // check self.total_attendees exists", + "stack_out": [ + "minted_asset#0", + "maybe_value%2#0" + ] + }, + "567": { + "op": "intc_1 // 1", + "defined_out": [ + "1", + "maybe_value%2#0", + "minted_asset#0" + ], + "stack_out": [ + "minted_asset#0", + "maybe_value%2#0", + "1" + ] + }, + "568": { + "op": "+", + "defined_out": [ + "minted_asset#0", + "new_state_value%0#0" + ], + "stack_out": [ + "minted_asset#0", + "new_state_value%0#0" + ] + }, + "569": { + "op": "bytec_0 // \"total_attendees\"", + "stack_out": [ + "minted_asset#0", + "new_state_value%0#0", + "\"total_attendees\"" + ] + }, + "570": { + "op": "swap", + "stack_out": [ + "minted_asset#0", + "\"total_attendees\"", + "new_state_value%0#0" + ] + }, + "571": { + "op": "app_global_put", + "stack_out": [ + "minted_asset#0" + ] + }, + "572": { + "op": "bytec_3 // \"box_map\"" + }, + "573": { + "op": "txn Sender", + "defined_out": [ + "\"box_map\"", + "minted_asset#0", + "tmp%2#0" + ], + "stack_out": [ + "minted_asset#0", + "\"box_map\"", + "tmp%2#0" + ] + }, + "575": { + "op": "concat", + "defined_out": [ + "minted_asset#0", + "tmp%3#0" + ], + "stack_out": [ + "minted_asset#0", + "tmp%3#0" + ] + }, + "576": { + "op": "box_len", + "defined_out": [ + "has_claimed#0", + "maybe_value%3#0", + "minted_asset#0" + ], + "stack_out": [ + "minted_asset#0", + "maybe_value%3#0", + "has_claimed#0" + ] + }, + "577": { + "op": "bury 1", + "stack_out": [ + "minted_asset#0", + "has_claimed#0" + ] + }, + "579": { + "op": "!", + "defined_out": [ + "minted_asset#0", + "tmp%4#0" + ], + "stack_out": [ + "minted_asset#0", + "tmp%4#0" + ] + }, + "580": { + "op": "assert // Already claimed POA", + "stack_out": [ + "minted_asset#0" + ] + }, + "581": { + "op": "bytec_3 // \"box_map\"" + }, + "582": { + "op": "txn Sender", + "defined_out": [ + "\"box_map\"", + "minted_asset#0", + "tmp%5#0" + ], + "stack_out": [ + "minted_asset#0", + "\"box_map\"", + "tmp%5#0" + ] + }, + "584": { + "op": "concat", + "defined_out": [ + "minted_asset#0", + "tmp%6#0" + ], + "stack_out": [ + "minted_asset#0", + "tmp%6#0" + ] + }, + "585": { + "op": "swap", + "stack_out": [ + "tmp%6#0", + "minted_asset#0" + ] + }, + "586": { + "op": "itob", + "defined_out": [ + "new_box_value%0#0", + "tmp%6#0" + ], + "stack_out": [ + "tmp%6#0", + "new_box_value%0#0" + ] + }, + "587": { + "op": "box_put", + "stack_out": [] + }, + "588": { + "retsub": true, + "op": "retsub" + }, + "589": { + "subroutine": "smart_contracts.poa.contract.ProofOfAttendance.get_poa_id", + "params": {}, + "block": "get_poa_id", + "stack_in": [], + "op": "proto 0 1" + }, + "592": { + "op": "txn Sender", + "defined_out": [ + "tmp%0#0" + ], + "stack_out": [ + "tmp%0#0" + ] + }, + "594": { + "op": "box_get", + "defined_out": [ + "exists#0", + "poa_id#0" + ], + "stack_out": [ + "poa_id#0", + "exists#0" + ] + }, + "595": { + "op": "assert // POA not found", + "stack_out": [ + "poa_id#0" + ] + }, + "596": { + "op": "btoi", + "defined_out": [ + "tmp%1#0" + ], + "stack_out": [ + "tmp%1#0" + ] + }, + "597": { + "retsub": true, + "op": "retsub" + }, + "598": { + "subroutine": "smart_contracts.poa.contract.ProofOfAttendance.get_poa_id_with_box", + "params": {}, + "block": "get_poa_id_with_box", + "stack_in": [], + "op": "proto 0 1" + }, + "601": { + "op": "txn Sender", + "defined_out": [ + "box#0" + ], + "stack_out": [ + "box#0" + ] + }, + "603": { + "op": "box_get", + "defined_out": [ + "exists#0", + "maybe_value%0#0" + ], + "stack_out": [ + "maybe_value%0#0", + "exists#0" + ] + }, + "604": { + "op": "swap", + "stack_out": [ + "exists#0", + "maybe_value%0#0" + ] + }, + "605": { + "op": "btoi", + "defined_out": [ + "exists#0", + "poa_id#0" + ], + "stack_out": [ + "exists#0", + "poa_id#0" + ] + }, + "606": { + "op": "swap", + "stack_out": [ + "poa_id#0", + "exists#0" + ] + }, + "607": { + "op": "assert // POA not found", + "stack_out": [ + "poa_id#0" + ] + }, + "608": { + "retsub": true, + "op": "retsub" + }, + "609": { + "subroutine": "smart_contracts.poa.contract.ProofOfAttendance.get_poa_id_with_box_ref", + "params": {}, + "block": "get_poa_id_with_box_ref", + "stack_in": [], + "op": "proto 0 1" + }, + "612": { + "op": "txn Sender", + "defined_out": [ + "box_ref#0" + ], + "stack_out": [ + "box_ref#0" + ] + }, + "614": { + "op": "box_get", + "defined_out": [ + "exists#0", + "poa_id#0" + ], + "stack_out": [ + "poa_id#0", + "exists#0" + ] + }, + "615": { + "op": "assert // POA not found", + "stack_out": [ + "poa_id#0" + ] + }, + "616": { + "op": "btoi", + "defined_out": [ + "tmp%0#0" + ], + "stack_out": [ + "tmp%0#0" + ] + }, + "617": { + "retsub": true, + "op": "retsub" + }, + "618": { + "subroutine": "smart_contracts.poa.contract.ProofOfAttendance.get_poa_id_with_box_map", + "params": {}, + "block": "get_poa_id_with_box_map", + "stack_in": [], + "op": "proto 0 1" + }, + "621": { + "op": "bytec_3 // \"box_map\"" + }, + "622": { + "op": "txn Sender", + "defined_out": [ + "\"box_map\"", + "tmp%0#0" + ], + "stack_out": [ + "\"box_map\"", + "tmp%0#0" + ] + }, + "624": { + "op": "concat", + "defined_out": [ + "tmp%1#0" + ], + "stack_out": [ + "tmp%1#0" + ] + }, + "625": { + "op": "box_get", + "defined_out": [ + "exists#0", + "maybe_value%0#0" + ], + "stack_out": [ + "maybe_value%0#0", + "exists#0" + ] + }, + "626": { + "op": "swap", + "stack_out": [ + "exists#0", + "maybe_value%0#0" + ] + }, + "627": { + "op": "btoi", + "defined_out": [ + "exists#0", + "poa_id#0" + ], + "stack_out": [ + "exists#0", + "poa_id#0" + ] + }, + "628": { + "op": "swap", + "stack_out": [ + "poa_id#0", + "exists#0" + ] + }, + "629": { + "op": "assert // POA not found", + "stack_out": [ + "poa_id#0" + ] + }, + "630": { + "retsub": true, + "op": "retsub" + }, + "631": { + "subroutine": "smart_contracts.poa.contract.ProofOfAttendance.claim_poa", + "params": { + "opt_in_txn#0": "uint64" + }, + "block": "claim_poa", + "stack_in": [], + "op": "proto 1 0" + }, + "634": { + "op": "intc_0 // 0", + "stack_out": [ + "awst_tmp%1#0" + ] + }, + "635": { + "op": "txn Sender", + "defined_out": [ + "tmp%0#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%0#0" + ] + }, + "637": { + "op": "box_get", + "defined_out": [ + "exists#0", + "poa_id#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "exists#0" + ] + }, + "638": { + "op": "assert // POA not found, attendance validation failed!", + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0" + ] + }, + "639": { + "op": "frame_dig -1", + "defined_out": [ + "opt_in_txn#0 (copy)", + "poa_id#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "opt_in_txn#0 (copy)" + ] + }, + "641": { + "op": "gtxns XferAsset", + "defined_out": [ + "poa_id#0", + "tmp%1#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "tmp%1#0" + ] + }, + "643": { + "op": "swap", + "stack_out": [ + "awst_tmp%1#0", + "tmp%1#0", + "poa_id#0" + ] + }, + "644": { + "op": "btoi", + "defined_out": [ + "tmp%1#0", + "tmp%2#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%1#0", + "tmp%2#0" + ] + }, + "645": { + "op": "dup", + "stack_out": [ + "awst_tmp%1#0", + "tmp%1#0", + "tmp%2#0", + "tmp%2#0" + ] + }, + "646": { + "op": "cover 2", + "defined_out": [ + "tmp%1#0", + "tmp%2#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%2#0", + "tmp%1#0", + "tmp%2#0" + ] + }, + "648": { + "op": "==", + "defined_out": [ + "tmp%2#0", + "tmp%3#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%2#0", + "tmp%3#0" + ] + }, + "649": { + "op": "assert // POA ID mismatch", + "stack_out": [ + "awst_tmp%1#0", + "tmp%2#0" + ] + }, + "650": { + "op": "frame_dig -1", + "stack_out": [ + "awst_tmp%1#0", + "tmp%2#0", + "opt_in_txn#0 (copy)" + ] + }, + "652": { + "op": "gtxns Fee", + "defined_out": [ + "tmp%2#0", + "tmp%4#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%2#0", + "tmp%4#0" + ] + }, + "654": { + "op": "!", + "defined_out": [ + "tmp%2#0", + "tmp%5#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%2#0", + "tmp%5#0" + ] + }, + "655": { + "op": "assert // We got you covered for free!", + "stack_out": [ + "awst_tmp%1#0", + "tmp%2#0" + ] + }, + "656": { + "op": "frame_dig -1", + "stack_out": [ + "awst_tmp%1#0", + "tmp%2#0", + "opt_in_txn#0 (copy)" + ] + }, + "658": { + "op": "gtxns AssetAmount", + "defined_out": [ + "tmp%2#0", + "tmp%6#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%2#0", + "tmp%6#0" + ] + }, + "660": { + "op": "!", + "defined_out": [ + "tmp%2#0", + "tmp%7#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%2#0", + "tmp%7#0" + ] + }, + "661": { + "op": "assert", + "stack_out": [ + "awst_tmp%1#0", + "tmp%2#0" + ] + }, + "662": { + "op": "frame_dig -1", + "stack_out": [ + "awst_tmp%1#0", + "tmp%2#0", + "opt_in_txn#0 (copy)" + ] + }, + "664": { + "op": "gtxns Sender", + "defined_out": [ + "tmp%2#0", + "tmp%8#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%2#0", + "tmp%8#0" + ] + }, + "666": { + "op": "frame_dig -1", + "stack_out": [ + "awst_tmp%1#0", + "tmp%2#0", + "tmp%8#0", + "opt_in_txn#0 (copy)" + ] + }, + "668": { + "op": "gtxns AssetReceiver", + "defined_out": [ + "awst_tmp%0#0", + "tmp%2#0", + "tmp%8#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%2#0", + "tmp%8#0", + "awst_tmp%0#0" + ] + }, + "670": { + "op": "dup", + "stack_out": [ + "awst_tmp%1#0", + "tmp%2#0", + "tmp%8#0", + "awst_tmp%0#0", + "awst_tmp%0#0" + ] + }, + "671": { + "op": "cover 2", + "defined_out": [ + "awst_tmp%0#0", + "tmp%2#0", + "tmp%8#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%2#0", + "awst_tmp%0#0", + "tmp%8#0", + "awst_tmp%0#0" + ] + }, + "673": { + "op": "==", + "defined_out": [ + "awst_tmp%0#0", + "tmp%2#0", + "tmp%9#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%2#0", + "awst_tmp%0#0", + "tmp%9#0" + ] + }, + "674": { + "op": "bz claim_poa_bool_false@3", + "stack_out": [ + "awst_tmp%1#0", + "tmp%2#0", + "awst_tmp%0#0" + ] + }, + "677": { + "op": "frame_dig 2" + }, + "679": { + "op": "txn Sender", + "defined_out": [ + "awst_tmp%0#0", + "tmp%10#0", + "tmp%2#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%2#0", + "awst_tmp%0#0", + "awst_tmp%0#0", + "tmp%10#0" + ] + }, + "681": { + "op": "==", + "defined_out": [ + "awst_tmp%0#0", + "tmp%11#0", + "tmp%2#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%2#0", + "awst_tmp%0#0", + "tmp%11#0" + ] + }, + "682": { + "op": "bz claim_poa_bool_false@3", + "stack_out": [ + "awst_tmp%1#0", + "tmp%2#0", + "awst_tmp%0#0" + ] + }, + "685": { + "op": "intc_1 // 1", + "defined_out": [ + "and_result%0#0", + "awst_tmp%0#0", + "tmp%2#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%2#0", + "awst_tmp%0#0", + "and_result%0#0" + ] + }, + "686": { + "op": "b claim_poa_bool_merge@4" + }, + "689": { + "block": "claim_poa_bool_false@3", + "stack_in": [ + "awst_tmp%1#0", + "tmp%2#0", + "awst_tmp%0#0" + ], + "op": "intc_0 // 0", + "defined_out": [ + "and_result%0#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%2#0", + "awst_tmp%0#0", + "and_result%0#0" + ] + }, + "690": { + "block": "claim_poa_bool_merge@4", + "stack_in": [ + "awst_tmp%1#0", + "tmp%2#0", + "awst_tmp%0#0", + "and_result%0#0" + ], + "op": "assert // Opt-in transaction sender and receiver must be the same", + "defined_out": [], + "stack_out": [ + "awst_tmp%1#0", + "tmp%2#0", + "awst_tmp%0#0" + ] + }, + "691": { + "op": "frame_dig -1", + "defined_out": [ + "opt_in_txn#0 (copy)" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%2#0", + "awst_tmp%0#0", + "opt_in_txn#0 (copy)" + ] + }, + "693": { + "op": "gtxns AssetCloseTo", + "defined_out": [ + "tmp%12#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%2#0", + "awst_tmp%0#0", + "tmp%12#0" + ] + }, + "695": { + "op": "frame_dig -1", + "stack_out": [ + "awst_tmp%1#0", + "tmp%2#0", + "awst_tmp%0#0", + "tmp%12#0", + "opt_in_txn#0 (copy)" + ] + }, + "697": { + "op": "gtxns RekeyTo", + "defined_out": [ + "awst_tmp%1#0", + "tmp%12#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%2#0", + "awst_tmp%0#0", + "tmp%12#0", + "awst_tmp%1#0" + ] + }, + "699": { + "op": "dup", + "stack_out": [ + "awst_tmp%1#0", + "tmp%2#0", + "awst_tmp%0#0", + "tmp%12#0", + "awst_tmp%1#0", + "awst_tmp%1#0" + ] + }, + "700": { + "op": "frame_bury 0", + "defined_out": [ + "awst_tmp%1#0", + "tmp%12#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%2#0", + "awst_tmp%0#0", + "tmp%12#0", + "awst_tmp%1#0" + ] + }, + "702": { + "op": "==", + "defined_out": [ + "awst_tmp%1#0", + "tmp%13#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%2#0", + "awst_tmp%0#0", + "tmp%13#0" + ] + }, + "703": { + "op": "bz claim_poa_bool_false@7", + "stack_out": [ + "awst_tmp%1#0", + "tmp%2#0", + "awst_tmp%0#0" + ] + }, + "706": { + "op": "frame_dig 0" + }, + "708": { + "op": "global ZeroAddress", + "defined_out": [ + "awst_tmp%1#0", + "tmp%14#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%2#0", + "awst_tmp%0#0", + "awst_tmp%1#0", + "tmp%14#0" + ] + }, + "710": { + "op": "==", + "defined_out": [ + "awst_tmp%1#0", + "tmp%15#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%2#0", + "awst_tmp%0#0", + "tmp%15#0" + ] + }, + "711": { + "op": "bz claim_poa_bool_false@7", + "stack_out": [ + "awst_tmp%1#0", + "tmp%2#0", + "awst_tmp%0#0" + ] + }, + "714": { + "op": "intc_1 // 1", + "defined_out": [ + "and_result%1#0", + "awst_tmp%1#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%2#0", + "awst_tmp%0#0", + "and_result%1#0" + ] + }, + "715": { + "op": "b claim_poa_bool_merge@8" + }, + "718": { + "block": "claim_poa_bool_false@7", + "stack_in": [ + "awst_tmp%1#0", + "tmp%2#0", + "awst_tmp%0#0" + ], + "op": "intc_0 // 0", + "defined_out": [ + "and_result%1#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%2#0", + "awst_tmp%0#0", + "and_result%1#0" + ] + }, + "719": { + "block": "claim_poa_bool_merge@8", + "stack_in": [ + "awst_tmp%1#0", + "tmp%2#0", + "awst_tmp%0#0", + "and_result%1#0" + ], + "op": "assert // Opt-in transaction close to must be zero address", + "defined_out": [], + "stack_out": [ + "awst_tmp%1#0", + "tmp%2#0", + "awst_tmp%0#0" + ] + }, + "720": { + "op": "txn Sender", + "defined_out": [ + "tmp%16#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%2#0", + "awst_tmp%0#0", + "tmp%16#0" + ] + }, + "722": { + "op": "frame_dig 1", + "defined_out": [ + "tmp%16#0", + "tmp%2#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%2#0", + "awst_tmp%0#0", + "tmp%16#0", + "tmp%2#0" + ] + }, + "724": { + "callsub": "smart_contracts.poa.contract.ProofOfAttendance._send_poa", + "op": "callsub _send_poa", + "stack_out": [ + "awst_tmp%1#0", + "tmp%2#0", + "awst_tmp%0#0" + ] + }, + "727": { + "retsub": true, + "op": "retsub" + }, + "728": { + "subroutine": "smart_contracts.poa.contract.ProofOfAttendance._send_poa", + "params": { + "receiver#0": "bytes", + "asset_id#0": "uint64" + }, + "block": "_send_poa", + "stack_in": [], + "op": "proto 2 0" + }, + "731": { + "op": "itxn_begin" + }, + "732": { + "op": "global CurrentApplicationAddress", + "defined_out": [ + "inner_txn_params%0%%param_Sender_idx_0#0" + ], + "stack_out": [ + "inner_txn_params%0%%param_Sender_idx_0#0" + ] + }, + "734": { + "op": "intc_1 // 1", + "defined_out": [ + "1", + "inner_txn_params%0%%param_Sender_idx_0#0" + ], + "stack_out": [ + "inner_txn_params%0%%param_Sender_idx_0#0", + "1" + ] + }, + "735": { + "op": "itxn_field AssetAmount", + "stack_out": [ + "inner_txn_params%0%%param_Sender_idx_0#0" + ] + }, + "737": { + "op": "frame_dig -2", + "defined_out": [ + "inner_txn_params%0%%param_Sender_idx_0#0", + "receiver#0 (copy)" + ], + "stack_out": [ + "inner_txn_params%0%%param_Sender_idx_0#0", + "receiver#0 (copy)" + ] + }, + "739": { + "op": "itxn_field AssetReceiver", + "stack_out": [ + "inner_txn_params%0%%param_Sender_idx_0#0" + ] + }, + "741": { + "op": "itxn_field Sender", + "stack_out": [] + }, + "743": { + "op": "frame_dig -1", + "defined_out": [ + "asset_id#0 (copy)" + ], + "stack_out": [ + "asset_id#0 (copy)" + ] + }, + "745": { + "op": "itxn_field XferAsset", + "stack_out": [] + }, + "747": { + "op": "intc_2 // axfer", + "defined_out": [ + "axfer" + ], + "stack_out": [ + "axfer" + ] + }, + "748": { + "op": "itxn_field TypeEnum", + "stack_out": [] + }, + "750": { + "op": "intc_0 // 0", + "defined_out": [ + "0" + ], + "stack_out": [ + "0" + ] + }, + "751": { + "op": "itxn_field Fee", + "stack_out": [] + }, + "753": { + "op": "itxn_submit" + }, + "754": { + "retsub": true, + "op": "retsub" + }, + "755": { + "subroutine": "smart_contracts.poa.contract.ProofOfAttendance.claim_poa_with_box", + "params": { + "opt_in_txn#0": "uint64" + }, + "block": "claim_poa_with_box", + "stack_in": [], + "op": "proto 1 0" + }, + "758": { + "op": "intc_0 // 0", + "stack_out": [ + "awst_tmp%1#0" + ] + }, + "759": { + "op": "txn Sender", + "defined_out": [ + "box#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "box#0" + ] + }, + "761": { + "op": "box_get", + "defined_out": [ + "exists#0", + "maybe_value%0#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "maybe_value%0#0", + "exists#0" + ] + }, + "762": { + "op": "swap", + "stack_out": [ + "awst_tmp%1#0", + "exists#0", + "maybe_value%0#0" + ] + }, + "763": { + "op": "btoi", + "defined_out": [ + "exists#0", + "poa_id#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "exists#0", + "poa_id#0" + ] + }, + "764": { + "op": "dup", + "stack_out": [ + "awst_tmp%1#0", + "exists#0", + "poa_id#0", + "poa_id#0 (copy)" + ] + }, + "765": { + "op": "uncover 2", + "defined_out": [ + "exists#0", + "poa_id#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "poa_id#0", + "exists#0" + ] + }, + "767": { + "op": "assert // POA not found, attendance validation failed!", + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "poa_id#0" + ] + }, + "768": { + "op": "frame_dig -1", + "defined_out": [ + "opt_in_txn#0 (copy)", + "poa_id#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "poa_id#0", + "opt_in_txn#0 (copy)" + ] + }, + "770": { + "op": "gtxns XferAsset", + "defined_out": [ + "poa_id#0", + "tmp%0#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "poa_id#0", + "tmp%0#0" + ] + }, + "772": { + "op": "==", + "defined_out": [ + "poa_id#0", + "tmp%1#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "tmp%1#0" + ] + }, + "773": { + "op": "assert // POA ID mismatch", + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0" + ] + }, + "774": { + "op": "frame_dig -1", + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "opt_in_txn#0 (copy)" + ] + }, + "776": { + "op": "gtxns Fee", + "defined_out": [ + "poa_id#0", + "tmp%2#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "tmp%2#0" + ] + }, + "778": { + "op": "!", + "defined_out": [ + "poa_id#0", + "tmp%3#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "tmp%3#0" + ] + }, + "779": { + "op": "assert // We got you covered for free!", + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0" + ] + }, + "780": { + "op": "frame_dig -1", + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "opt_in_txn#0 (copy)" + ] + }, + "782": { + "op": "gtxns AssetAmount", + "defined_out": [ + "poa_id#0", + "tmp%4#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "tmp%4#0" + ] + }, + "784": { + "op": "!", + "defined_out": [ + "poa_id#0", + "tmp%5#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "tmp%5#0" + ] + }, + "785": { + "op": "assert", + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0" + ] + }, + "786": { + "op": "frame_dig -1", + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "opt_in_txn#0 (copy)" + ] + }, + "788": { + "op": "gtxns Sender", + "defined_out": [ + "poa_id#0", + "tmp%6#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "tmp%6#0" + ] + }, + "790": { + "op": "frame_dig -1", + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "tmp%6#0", + "opt_in_txn#0 (copy)" + ] + }, + "792": { + "op": "gtxns AssetReceiver", + "defined_out": [ + "awst_tmp%0#0", + "poa_id#0", + "tmp%6#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "tmp%6#0", + "awst_tmp%0#0" + ] + }, + "794": { + "op": "dup", + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "tmp%6#0", + "awst_tmp%0#0", + "awst_tmp%0#0" + ] + }, + "795": { + "op": "cover 2", + "defined_out": [ + "awst_tmp%0#0", + "poa_id#0", + "tmp%6#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0", + "tmp%6#0", + "awst_tmp%0#0" + ] + }, + "797": { + "op": "==", + "defined_out": [ + "awst_tmp%0#0", + "poa_id#0", + "tmp%7#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0", + "tmp%7#0" + ] + }, + "798": { + "op": "bz claim_poa_with_box_bool_false@3", + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0" + ] + }, + "801": { + "op": "frame_dig 2" + }, + "803": { + "op": "txn Sender", + "defined_out": [ + "awst_tmp%0#0", + "poa_id#0", + "tmp%8#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0", + "awst_tmp%0#0", + "tmp%8#0" + ] + }, + "805": { + "op": "==", + "defined_out": [ + "awst_tmp%0#0", + "poa_id#0", + "tmp%9#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0", + "tmp%9#0" + ] + }, + "806": { + "op": "bz claim_poa_with_box_bool_false@3", + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0" + ] + }, + "809": { + "op": "intc_1 // 1", + "defined_out": [ + "and_result%0#0", + "awst_tmp%0#0", + "poa_id#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0", + "and_result%0#0" + ] + }, + "810": { + "op": "b claim_poa_with_box_bool_merge@4" + }, + "813": { + "block": "claim_poa_with_box_bool_false@3", + "stack_in": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0" + ], + "op": "intc_0 // 0", + "defined_out": [ + "and_result%0#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0", + "and_result%0#0" + ] + }, + "814": { + "block": "claim_poa_with_box_bool_merge@4", + "stack_in": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0", + "and_result%0#0" + ], + "op": "assert // Opt-in transaction sender and receiver must be the same", + "defined_out": [], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0" + ] + }, + "815": { + "op": "frame_dig -1", + "defined_out": [ + "opt_in_txn#0 (copy)" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0", + "opt_in_txn#0 (copy)" + ] + }, + "817": { + "op": "gtxns AssetCloseTo", + "defined_out": [ + "tmp%10#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0", + "tmp%10#0" + ] + }, + "819": { + "op": "frame_dig -1", + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0", + "tmp%10#0", + "opt_in_txn#0 (copy)" + ] + }, + "821": { + "op": "gtxns RekeyTo", + "defined_out": [ + "awst_tmp%1#0", + "tmp%10#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0", + "tmp%10#0", + "awst_tmp%1#0" + ] + }, + "823": { + "op": "dup", + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0", + "tmp%10#0", + "awst_tmp%1#0", + "awst_tmp%1#0" + ] + }, + "824": { + "op": "frame_bury 0", + "defined_out": [ + "awst_tmp%1#0", + "tmp%10#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0", + "tmp%10#0", + "awst_tmp%1#0" + ] + }, + "826": { + "op": "==", + "defined_out": [ + "awst_tmp%1#0", + "tmp%11#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0", + "tmp%11#0" + ] + }, + "827": { + "op": "bz claim_poa_with_box_bool_false@7", + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0" + ] + }, + "830": { + "op": "frame_dig 0" + }, + "832": { + "op": "global ZeroAddress", + "defined_out": [ + "awst_tmp%1#0", + "tmp%12#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0", + "awst_tmp%1#0", + "tmp%12#0" + ] + }, + "834": { + "op": "==", + "defined_out": [ + "awst_tmp%1#0", + "tmp%13#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0", + "tmp%13#0" + ] + }, + "835": { + "op": "bz claim_poa_with_box_bool_false@7", + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0" + ] + }, + "838": { + "op": "intc_1 // 1", + "defined_out": [ + "and_result%1#0", + "awst_tmp%1#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0", + "and_result%1#0" + ] + }, + "839": { + "op": "b claim_poa_with_box_bool_merge@8" + }, + "842": { + "block": "claim_poa_with_box_bool_false@7", + "stack_in": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0" + ], + "op": "intc_0 // 0", + "defined_out": [ + "and_result%1#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0", + "and_result%1#0" + ] + }, + "843": { + "block": "claim_poa_with_box_bool_merge@8", + "stack_in": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0", + "and_result%1#0" + ], + "op": "assert // Opt-in transaction close to must be zero address", + "defined_out": [], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0" + ] + }, + "844": { + "op": "txn Sender", + "defined_out": [ + "tmp%14#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0", + "tmp%14#0" + ] + }, + "846": { + "op": "frame_dig 1", + "defined_out": [ + "poa_id#0", + "tmp%14#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0", + "tmp%14#0", + "poa_id#0" + ] + }, + "848": { + "callsub": "smart_contracts.poa.contract.ProofOfAttendance._send_poa", + "op": "callsub _send_poa", + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0" + ] + }, + "851": { + "retsub": true, + "op": "retsub" + }, + "852": { + "subroutine": "smart_contracts.poa.contract.ProofOfAttendance.claim_poa_with_box_ref", + "params": { + "opt_in_txn#0": "uint64" + }, + "block": "claim_poa_with_box_ref", + "stack_in": [], + "op": "proto 1 0" + }, + "855": { + "op": "intc_0 // 0", + "stack_out": [ + "awst_tmp%1#0" + ] + }, + "856": { + "op": "txn Sender", + "defined_out": [ + "box_ref#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "box_ref#0" + ] + }, + "858": { + "op": "box_get", + "defined_out": [ + "exists#0", + "poa_id#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "exists#0" + ] + }, + "859": { + "op": "assert // POA not found, attendance validation failed!", + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0" + ] + }, + "860": { + "op": "frame_dig -1", + "defined_out": [ + "opt_in_txn#0 (copy)", + "poa_id#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "opt_in_txn#0 (copy)" + ] + }, + "862": { + "op": "gtxns XferAsset", + "defined_out": [ + "poa_id#0", + "tmp%0#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "tmp%0#0" + ] + }, + "864": { + "op": "swap", + "stack_out": [ + "awst_tmp%1#0", + "tmp%0#0", + "poa_id#0" + ] + }, + "865": { + "op": "btoi", + "defined_out": [ + "tmp%0#0", + "tmp%1#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%0#0", + "tmp%1#0" + ] + }, + "866": { + "op": "dup", + "stack_out": [ + "awst_tmp%1#0", + "tmp%0#0", + "tmp%1#0", + "tmp%1#0" + ] + }, + "867": { + "op": "cover 2", + "defined_out": [ + "tmp%0#0", + "tmp%1#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%1#0", + "tmp%0#0", + "tmp%1#0" + ] + }, + "869": { + "op": "==", + "defined_out": [ + "tmp%1#0", + "tmp%2#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%1#0", + "tmp%2#0" + ] + }, + "870": { + "op": "assert // POA ID mismatch", + "stack_out": [ + "awst_tmp%1#0", + "tmp%1#0" + ] + }, + "871": { + "op": "frame_dig -1", + "stack_out": [ + "awst_tmp%1#0", + "tmp%1#0", + "opt_in_txn#0 (copy)" + ] + }, + "873": { + "op": "gtxns Fee", + "defined_out": [ + "tmp%1#0", + "tmp%3#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%1#0", + "tmp%3#0" + ] + }, + "875": { + "op": "!", + "defined_out": [ + "tmp%1#0", + "tmp%4#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%1#0", + "tmp%4#0" + ] + }, + "876": { + "op": "assert // We got you covered for free!", + "stack_out": [ + "awst_tmp%1#0", + "tmp%1#0" + ] + }, + "877": { + "op": "frame_dig -1", + "stack_out": [ + "awst_tmp%1#0", + "tmp%1#0", + "opt_in_txn#0 (copy)" + ] + }, + "879": { + "op": "gtxns AssetAmount", + "defined_out": [ + "tmp%1#0", + "tmp%5#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%1#0", + "tmp%5#0" + ] + }, + "881": { + "op": "!", + "defined_out": [ + "tmp%1#0", + "tmp%6#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%1#0", + "tmp%6#0" + ] + }, + "882": { + "op": "assert", + "stack_out": [ + "awst_tmp%1#0", + "tmp%1#0" + ] + }, + "883": { + "op": "frame_dig -1", + "stack_out": [ + "awst_tmp%1#0", + "tmp%1#0", + "opt_in_txn#0 (copy)" + ] + }, + "885": { + "op": "gtxns Sender", + "defined_out": [ + "tmp%1#0", + "tmp%7#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%1#0", + "tmp%7#0" + ] + }, + "887": { + "op": "frame_dig -1", + "stack_out": [ + "awst_tmp%1#0", + "tmp%1#0", + "tmp%7#0", + "opt_in_txn#0 (copy)" + ] + }, + "889": { + "op": "gtxns AssetReceiver", + "defined_out": [ + "awst_tmp%0#0", + "tmp%1#0", + "tmp%7#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%1#0", + "tmp%7#0", + "awst_tmp%0#0" + ] + }, + "891": { + "op": "dup", + "stack_out": [ + "awst_tmp%1#0", + "tmp%1#0", + "tmp%7#0", + "awst_tmp%0#0", + "awst_tmp%0#0" + ] + }, + "892": { + "op": "cover 2", + "defined_out": [ + "awst_tmp%0#0", + "tmp%1#0", + "tmp%7#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%1#0", + "awst_tmp%0#0", + "tmp%7#0", + "awst_tmp%0#0" + ] + }, + "894": { + "op": "==", + "defined_out": [ + "awst_tmp%0#0", + "tmp%1#0", + "tmp%8#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%1#0", + "awst_tmp%0#0", + "tmp%8#0" + ] + }, + "895": { + "op": "bz claim_poa_with_box_ref_bool_false@3", + "stack_out": [ + "awst_tmp%1#0", + "tmp%1#0", + "awst_tmp%0#0" + ] + }, + "898": { + "op": "frame_dig 2" + }, + "900": { + "op": "txn Sender", + "defined_out": [ + "awst_tmp%0#0", + "tmp%1#0", + "tmp%9#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%1#0", + "awst_tmp%0#0", + "awst_tmp%0#0", + "tmp%9#0" + ] + }, + "902": { + "op": "==", + "defined_out": [ + "awst_tmp%0#0", + "tmp%1#0", + "tmp%10#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%1#0", + "awst_tmp%0#0", + "tmp%10#0" + ] + }, + "903": { + "op": "bz claim_poa_with_box_ref_bool_false@3", + "stack_out": [ + "awst_tmp%1#0", + "tmp%1#0", + "awst_tmp%0#0" + ] + }, + "906": { + "op": "intc_1 // 1", + "defined_out": [ + "and_result%0#0", + "awst_tmp%0#0", + "tmp%1#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%1#0", + "awst_tmp%0#0", + "and_result%0#0" + ] + }, + "907": { + "op": "b claim_poa_with_box_ref_bool_merge@4" + }, + "910": { + "block": "claim_poa_with_box_ref_bool_false@3", + "stack_in": [ + "awst_tmp%1#0", + "tmp%1#0", + "awst_tmp%0#0" + ], + "op": "intc_0 // 0", + "defined_out": [ + "and_result%0#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%1#0", + "awst_tmp%0#0", + "and_result%0#0" + ] + }, + "911": { + "block": "claim_poa_with_box_ref_bool_merge@4", + "stack_in": [ + "awst_tmp%1#0", + "tmp%1#0", + "awst_tmp%0#0", + "and_result%0#0" + ], + "op": "assert // Opt-in transaction sender and receiver must be the same", + "defined_out": [], + "stack_out": [ + "awst_tmp%1#0", + "tmp%1#0", + "awst_tmp%0#0" + ] + }, + "912": { + "op": "frame_dig -1", + "defined_out": [ + "opt_in_txn#0 (copy)" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%1#0", + "awst_tmp%0#0", + "opt_in_txn#0 (copy)" + ] + }, + "914": { + "op": "gtxns AssetCloseTo", + "defined_out": [ + "tmp%11#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%1#0", + "awst_tmp%0#0", + "tmp%11#0" + ] + }, + "916": { + "op": "frame_dig -1", + "stack_out": [ + "awst_tmp%1#0", + "tmp%1#0", + "awst_tmp%0#0", + "tmp%11#0", + "opt_in_txn#0 (copy)" + ] + }, + "918": { + "op": "gtxns RekeyTo", + "defined_out": [ + "awst_tmp%1#0", + "tmp%11#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%1#0", + "awst_tmp%0#0", + "tmp%11#0", + "awst_tmp%1#0" + ] + }, + "920": { + "op": "dup", + "stack_out": [ + "awst_tmp%1#0", + "tmp%1#0", + "awst_tmp%0#0", + "tmp%11#0", + "awst_tmp%1#0", + "awst_tmp%1#0" + ] + }, + "921": { + "op": "frame_bury 0", + "defined_out": [ + "awst_tmp%1#0", + "tmp%11#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%1#0", + "awst_tmp%0#0", + "tmp%11#0", + "awst_tmp%1#0" + ] + }, + "923": { + "op": "==", + "defined_out": [ + "awst_tmp%1#0", + "tmp%12#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%1#0", + "awst_tmp%0#0", + "tmp%12#0" + ] + }, + "924": { + "op": "bz claim_poa_with_box_ref_bool_false@7", + "stack_out": [ + "awst_tmp%1#0", + "tmp%1#0", + "awst_tmp%0#0" + ] + }, + "927": { + "op": "frame_dig 0" + }, + "929": { + "op": "global ZeroAddress", + "defined_out": [ + "awst_tmp%1#0", + "tmp%13#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%1#0", + "awst_tmp%0#0", + "awst_tmp%1#0", + "tmp%13#0" + ] + }, + "931": { + "op": "==", + "defined_out": [ + "awst_tmp%1#0", + "tmp%14#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%1#0", + "awst_tmp%0#0", + "tmp%14#0" + ] + }, + "932": { + "op": "bz claim_poa_with_box_ref_bool_false@7", + "stack_out": [ + "awst_tmp%1#0", + "tmp%1#0", + "awst_tmp%0#0" + ] + }, + "935": { + "op": "intc_1 // 1", + "defined_out": [ + "and_result%1#0", + "awst_tmp%1#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%1#0", + "awst_tmp%0#0", + "and_result%1#0" + ] + }, + "936": { + "op": "b claim_poa_with_box_ref_bool_merge@8" + }, + "939": { + "block": "claim_poa_with_box_ref_bool_false@7", + "stack_in": [ + "awst_tmp%1#0", + "tmp%1#0", + "awst_tmp%0#0" + ], + "op": "intc_0 // 0", + "defined_out": [ + "and_result%1#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%1#0", + "awst_tmp%0#0", + "and_result%1#0" + ] + }, + "940": { + "block": "claim_poa_with_box_ref_bool_merge@8", + "stack_in": [ + "awst_tmp%1#0", + "tmp%1#0", + "awst_tmp%0#0", + "and_result%1#0" + ], + "op": "assert // Opt-in transaction close to must be zero address", + "defined_out": [], + "stack_out": [ + "awst_tmp%1#0", + "tmp%1#0", + "awst_tmp%0#0" + ] + }, + "941": { + "op": "txn Sender", + "defined_out": [ + "tmp%15#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%1#0", + "awst_tmp%0#0", + "tmp%15#0" + ] + }, + "943": { + "op": "frame_dig 1", + "defined_out": [ + "tmp%1#0", + "tmp%15#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%1#0", + "awst_tmp%0#0", + "tmp%15#0", + "tmp%1#0" + ] + }, + "945": { + "callsub": "smart_contracts.poa.contract.ProofOfAttendance._send_poa", + "op": "callsub _send_poa", + "stack_out": [ + "awst_tmp%1#0", + "tmp%1#0", + "awst_tmp%0#0" + ] + }, + "948": { + "retsub": true, + "op": "retsub" + }, + "949": { + "subroutine": "smart_contracts.poa.contract.ProofOfAttendance.claim_poa_with_box_map", + "params": { + "opt_in_txn#0": "uint64" + }, + "block": "claim_poa_with_box_map", + "stack_in": [], + "op": "proto 1 0" + }, + "952": { + "op": "intc_0 // 0" + }, + "953": { + "op": "bytec_3 // \"box_map\"" + }, + "954": { + "op": "txn Sender", + "defined_out": [ + "\"box_map\"", + "tmp%0#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "\"box_map\"", + "tmp%0#0" + ] + }, + "956": { + "op": "concat", + "defined_out": [ + "tmp%1#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "tmp%1#0" + ] + }, + "957": { + "op": "box_get", + "defined_out": [ + "exists#0", + "maybe_value%0#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "maybe_value%0#0", + "exists#0" + ] + }, + "958": { + "op": "swap", + "stack_out": [ + "awst_tmp%1#0", + "exists#0", + "maybe_value%0#0" + ] + }, + "959": { + "op": "btoi", + "defined_out": [ + "exists#0", + "poa_id#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "exists#0", + "poa_id#0" + ] + }, + "960": { + "op": "dup", + "stack_out": [ + "awst_tmp%1#0", + "exists#0", + "poa_id#0", + "poa_id#0 (copy)" + ] + }, + "961": { + "op": "uncover 2", + "defined_out": [ + "exists#0", + "poa_id#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "poa_id#0", + "exists#0" + ] + }, + "963": { + "op": "assert // POA not found, attendance validation failed!", + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "poa_id#0" + ] + }, + "964": { + "op": "frame_dig -1", + "defined_out": [ + "opt_in_txn#0 (copy)", + "poa_id#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "poa_id#0", + "opt_in_txn#0 (copy)" + ] + }, + "966": { + "op": "gtxns XferAsset", + "defined_out": [ + "poa_id#0", + "tmp%2#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "poa_id#0", + "tmp%2#0" + ] + }, + "968": { + "op": "==", + "defined_out": [ + "poa_id#0", + "tmp%3#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "tmp%3#0" + ] + }, + "969": { + "op": "assert // POA ID mismatch", + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0" + ] + }, + "970": { + "op": "frame_dig -1", + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "opt_in_txn#0 (copy)" + ] + }, + "972": { + "op": "gtxns Fee", + "defined_out": [ + "poa_id#0", + "tmp%4#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "tmp%4#0" + ] + }, + "974": { + "op": "!", + "defined_out": [ + "poa_id#0", + "tmp%5#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "tmp%5#0" + ] + }, + "975": { + "op": "assert // We got you covered for free!", + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0" + ] + }, + "976": { + "op": "frame_dig -1", + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "opt_in_txn#0 (copy)" + ] + }, + "978": { + "op": "gtxns AssetAmount", + "defined_out": [ + "poa_id#0", + "tmp%6#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "tmp%6#0" + ] + }, + "980": { + "op": "!", + "defined_out": [ + "poa_id#0", + "tmp%7#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "tmp%7#0" + ] + }, + "981": { + "op": "assert", + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0" + ] + }, + "982": { + "op": "frame_dig -1", + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "opt_in_txn#0 (copy)" + ] + }, + "984": { + "op": "gtxns Sender", + "defined_out": [ + "poa_id#0", + "tmp%8#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "tmp%8#0" + ] + }, + "986": { + "op": "frame_dig -1", + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "tmp%8#0", + "opt_in_txn#0 (copy)" + ] + }, + "988": { + "op": "gtxns AssetReceiver", + "defined_out": [ + "awst_tmp%0#0", + "poa_id#0", + "tmp%8#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "tmp%8#0", + "awst_tmp%0#0" + ] + }, + "990": { + "op": "dup", + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "tmp%8#0", + "awst_tmp%0#0", + "awst_tmp%0#0" + ] + }, + "991": { + "op": "cover 2", + "defined_out": [ + "awst_tmp%0#0", + "poa_id#0", + "tmp%8#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0", + "tmp%8#0", + "awst_tmp%0#0" + ] + }, + "993": { + "op": "==", + "defined_out": [ + "awst_tmp%0#0", + "poa_id#0", + "tmp%9#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0", + "tmp%9#0" + ] + }, + "994": { + "op": "bz claim_poa_with_box_map_bool_false@3", + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0" + ] + }, + "997": { + "op": "frame_dig 2" + }, + "999": { + "op": "txn Sender", + "defined_out": [ + "awst_tmp%0#0", + "poa_id#0", + "tmp%10#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0", + "awst_tmp%0#0", + "tmp%10#0" + ] + }, + "1001": { + "op": "==", + "defined_out": [ + "awst_tmp%0#0", + "poa_id#0", + "tmp%11#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0", + "tmp%11#0" + ] + }, + "1002": { + "op": "bz claim_poa_with_box_map_bool_false@3", + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0" + ] + }, + "1005": { + "op": "intc_1 // 1", + "defined_out": [ + "and_result%0#0", + "awst_tmp%0#0", + "poa_id#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0", + "and_result%0#0" + ] + }, + "1006": { + "op": "b claim_poa_with_box_map_bool_merge@4" + }, + "1009": { + "block": "claim_poa_with_box_map_bool_false@3", + "stack_in": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0" + ], + "op": "intc_0 // 0", + "defined_out": [ + "and_result%0#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0", + "and_result%0#0" + ] + }, + "1010": { + "block": "claim_poa_with_box_map_bool_merge@4", + "stack_in": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0", + "and_result%0#0" + ], + "op": "assert // Opt-in transaction sender and receiver must be the same", + "defined_out": [], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0" + ] + }, + "1011": { + "op": "frame_dig -1", + "defined_out": [ + "opt_in_txn#0 (copy)" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0", + "opt_in_txn#0 (copy)" + ] + }, + "1013": { + "op": "gtxns AssetCloseTo", + "defined_out": [ + "tmp%12#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0", + "tmp%12#0" + ] + }, + "1015": { + "op": "frame_dig -1", + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0", + "tmp%12#0", + "opt_in_txn#0 (copy)" + ] + }, + "1017": { + "op": "gtxns RekeyTo", + "defined_out": [ + "awst_tmp%1#0", + "tmp%12#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0", + "tmp%12#0", + "awst_tmp%1#0" + ] + }, + "1019": { + "op": "dup", + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0", + "tmp%12#0", + "awst_tmp%1#0", + "awst_tmp%1#0" + ] + }, + "1020": { + "op": "frame_bury 0", + "defined_out": [ + "awst_tmp%1#0", + "tmp%12#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0", + "tmp%12#0", + "awst_tmp%1#0" + ] + }, + "1022": { + "op": "==", + "defined_out": [ + "awst_tmp%1#0", + "tmp%13#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0", + "tmp%13#0" + ] + }, + "1023": { + "op": "bz claim_poa_with_box_map_bool_false@7", + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0" + ] + }, + "1026": { + "op": "frame_dig 0" + }, + "1028": { + "op": "global ZeroAddress", + "defined_out": [ + "awst_tmp%1#0", + "tmp%14#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0", + "awst_tmp%1#0", + "tmp%14#0" + ] + }, + "1030": { + "op": "==", + "defined_out": [ + "awst_tmp%1#0", + "tmp%15#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0", + "tmp%15#0" + ] + }, + "1031": { + "op": "bz claim_poa_with_box_map_bool_false@7", + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0" + ] + }, + "1034": { + "op": "intc_1 // 1", + "defined_out": [ + "and_result%1#0", + "awst_tmp%1#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0", + "and_result%1#0" + ] + }, + "1035": { + "op": "b claim_poa_with_box_map_bool_merge@8" + }, + "1038": { + "block": "claim_poa_with_box_map_bool_false@7", + "stack_in": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0" + ], + "op": "intc_0 // 0", + "defined_out": [ + "and_result%1#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0", + "and_result%1#0" + ] + }, + "1039": { + "block": "claim_poa_with_box_map_bool_merge@8", + "stack_in": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0", + "and_result%1#0" + ], + "op": "assert // Opt-in transaction close to must be zero address", + "defined_out": [], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0" + ] + }, + "1040": { + "op": "txn Sender", + "defined_out": [ + "tmp%16#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0", + "tmp%16#0" + ] + }, + "1042": { + "op": "frame_dig 1", + "defined_out": [ + "poa_id#0", + "tmp%16#0" + ], + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0", + "tmp%16#0", + "poa_id#0" + ] + }, + "1044": { + "callsub": "smart_contracts.poa.contract.ProofOfAttendance._send_poa", + "op": "callsub _send_poa", + "stack_out": [ + "awst_tmp%1#0", + "poa_id#0", + "awst_tmp%0#0" + ] + }, + "1047": { + "retsub": true, + "op": "retsub" + }, + "1048": { + "subroutine": "smart_contracts.poa.contract.ProofOfAttendance.__init__", + "params": {}, + "block": "__init__", + "stack_in": [], + "op": "proto 0 0" + }, + "1051": { + "op": "bytec_1 // \"max_attendees\"", + "defined_out": [ + "\"max_attendees\"" + ], + "stack_out": [ + "\"max_attendees\"" + ] + }, + "1052": { + "op": "pushint 30 // 30", + "defined_out": [ + "\"max_attendees\"", + "30" + ], + "stack_out": [ + "\"max_attendees\"", + "30" + ] + }, + "1054": { + "op": "app_global_put", + "stack_out": [] + }, + "1055": { + "op": "bytec 4 // \"asset_url\"", + "defined_out": [ + "\"asset_url\"" + ], + "stack_out": [ + "\"asset_url\"" + ] + }, + "1057": { + "op": "pushbytes \"ipfs://QmW5vERkgeJJtSY1YQdcWU6gsHCZCyLFtM1oT9uyy2WGm8\"", + "defined_out": [ + "\"asset_url\"", + "\"ipfs://QmW5vERkgeJJtSY1YQdcWU6gsHCZCyLFtM1oT9uyy2WGm8\"" + ], + "stack_out": [ + "\"asset_url\"", + "\"ipfs://QmW5vERkgeJJtSY1YQdcWU6gsHCZCyLFtM1oT9uyy2WGm8\"" + ] + }, + "1112": { + "op": "app_global_put", + "stack_out": [] + }, + "1113": { + "op": "bytec_0 // \"total_attendees\"", + "defined_out": [ + "\"total_attendees\"" + ], + "stack_out": [ + "\"total_attendees\"" + ] + }, + "1114": { + "op": "intc_0 // 0", + "defined_out": [ + "\"total_attendees\"", + "0" + ], + "stack_out": [ + "\"total_attendees\"", + "0" + ] + }, + "1115": { + "op": "app_global_put", + "stack_out": [] + }, + "1116": { + "retsub": true, + "op": "retsub" + }, + "1117": { + "subroutine": "_puya_lib.util.ensure_budget", + "params": { + "required_budget#0": "uint64", + "fee_source#0": "uint64" + }, + "block": "ensure_budget", + "stack_in": [], + "op": "proto 2 0" + }, + "1120": { + "op": "frame_dig -2", + "defined_out": [ + "required_budget#0 (copy)" + ], + "stack_out": [ + "required_budget#0 (copy)" + ] + }, + "1122": { + "op": "pushint 10 // 10", + "defined_out": [ + "10", + "required_budget#0 (copy)" + ], + "stack_out": [ + "required_budget#0 (copy)", + "10" + ] + }, + "1124": { + "op": "+", + "defined_out": [ + "required_budget_with_buffer#0" + ], + "stack_out": [ + "required_budget_with_buffer#0" + ] + }, + "1125": { + "block": "ensure_budget_while_top@1", + "stack_in": [ + "required_budget_with_buffer#0" + ], + "op": "frame_dig 0" + }, + "1127": { + "op": "global OpcodeBudget", + "defined_out": [ + "required_budget_with_buffer#0", + "tmp%0#0" + ], + "stack_out": [ + "required_budget_with_buffer#0", + "required_budget_with_buffer#0", + "tmp%0#0" + ] + }, + "1129": { + "op": ">", + "defined_out": [ + "required_budget_with_buffer#0", + "tmp%1#0" + ], + "stack_out": [ + "required_budget_with_buffer#0", + "tmp%1#0" + ] + }, + "1130": { + "op": "bz ensure_budget_after_while@7", + "stack_out": [ + "required_budget_with_buffer#0" + ] + }, + "1133": { + "op": "itxn_begin" + }, + "1134": { + "op": "pushint 6 // appl", + "defined_out": [ + "appl", + "required_budget_with_buffer#0" + ], + "stack_out": [ + "required_budget_with_buffer#0", + "appl" + ] + }, + "1136": { + "op": "itxn_field TypeEnum", + "stack_out": [ + "required_budget_with_buffer#0" + ] + }, + "1138": { + "op": "pushint 5 // DeleteApplication", + "defined_out": [ + "DeleteApplication", + "required_budget_with_buffer#0" + ], + "stack_out": [ + "required_budget_with_buffer#0", + "DeleteApplication" + ] + }, + "1140": { + "op": "itxn_field OnCompletion", + "stack_out": [ + "required_budget_with_buffer#0" + ] + }, + "1142": { + "op": "bytec 5 // 0x068101", + "defined_out": [ + "0x068101", + "required_budget_with_buffer#0" + ], + "stack_out": [ + "required_budget_with_buffer#0", + "0x068101" + ] + }, + "1144": { + "op": "itxn_field ApprovalProgram", + "stack_out": [ + "required_budget_with_buffer#0" + ] + }, + "1146": { + "op": "bytec 5 // 0x068101", + "stack_out": [ + "required_budget_with_buffer#0", + "0x068101" + ] + }, + "1148": { + "op": "itxn_field ClearStateProgram", + "stack_out": [ + "required_budget_with_buffer#0" + ] + }, + "1150": { + "op": "frame_dig -1", + "defined_out": [ + "fee_source#0 (copy)", + "required_budget_with_buffer#0" + ], + "stack_out": [ + "required_budget_with_buffer#0", + "fee_source#0 (copy)" + ] + }, + "1152": { + "op": "switch ensure_budget_switch_case_0@3 ensure_budget_switch_case_1@4", + "stack_out": [ + "required_budget_with_buffer#0" + ] + }, + "1158": { + "op": "b ensure_budget_switch_case_next@6" + }, + "1161": { + "block": "ensure_budget_switch_case_0@3", + "stack_in": [ + "required_budget_with_buffer#0" + ], + "op": "intc_0 // 0", + "defined_out": [ + "0" + ], + "stack_out": [ + "required_budget_with_buffer#0", + "0" + ] + }, + "1162": { + "op": "itxn_field Fee", + "stack_out": [ + "required_budget_with_buffer#0" + ] + }, + "1164": { + "op": "b ensure_budget_switch_case_next@6" + }, + "1167": { + "block": "ensure_budget_switch_case_1@4", + "stack_in": [ + "required_budget_with_buffer#0" + ], + "op": "global MinTxnFee", + "defined_out": [ + "tmp%2#0" + ], + "stack_out": [ + "required_budget_with_buffer#0", + "tmp%2#0" + ] + }, + "1169": { + "op": "itxn_field Fee", + "stack_out": [ + "required_budget_with_buffer#0" + ] + }, + "1171": { + "block": "ensure_budget_switch_case_next@6", + "stack_in": [ + "required_budget_with_buffer#0" + ], + "op": "itxn_submit" + }, + "1172": { + "op": "b ensure_budget_while_top@1" + }, + "1175": { + "block": "ensure_budget_after_while@7", + "stack_in": [ + "required_budget_with_buffer#0" + ], + "retsub": true, + "op": "retsub" + } + } +} diff --git a/sampleWorkspace/puya/contract.py b/sampleWorkspace/puya/contract.py new file mode 100644 index 0000000..aac5c2a --- /dev/null +++ b/sampleWorkspace/puya/contract.py @@ -0,0 +1,214 @@ +import algopy +from algopy import TemplateVar +from algopy import UInt64 + + +class ProofOfAttendance(algopy.ARC4Contract): + def __init__(self) -> None: + self.max_attendees = algopy.UInt64(30) + self.asset_url = algopy.String( + "ipfs://QmW5vERkgeJJtSY1YQdcWU6gsHCZCyLFtM1oT9uyy2WGm8" + ) + self.total_attendees = algopy.UInt64(0) + self.box_map = algopy.BoxMap(algopy.Bytes, algopy.UInt64) + + @algopy.arc4.abimethod(create="require") + def init(self, max_attendees: algopy.UInt64) -> None: + assert ( + algopy.Txn.sender == algopy.Global.creator_address + ), "Only creator can initialize" + self.max_attendees = max_attendees + + @algopy.arc4.abimethod() + def confirm_attendance(self) -> None: + assert self.total_attendees < self.max_attendees, "Max attendees reached" + + minted_asset = self._mint_poa(algopy.Txn.sender) + self.total_attendees += TemplateVar[UInt64]("INCREMENT") + + _id, has_claimed = algopy.op.Box.get(algopy.Txn.sender.bytes) + assert not has_claimed, "Already claimed POA" + + algopy.op.Box.put(algopy.Txn.sender.bytes, algopy.op.itob(minted_asset.id)) + + @algopy.arc4.abimethod() + def confirm_attendance_with_box(self) -> None: + assert self.total_attendees < self.max_attendees, "Max attendees reached" + + minted_asset = self._mint_poa(algopy.Txn.sender) + self.total_attendees += 1 + + box = algopy.Box(algopy.UInt64, key=algopy.Txn.sender.bytes) + has_claimed = bool(box) + assert not has_claimed, "Already claimed POA" + + box.value = minted_asset.id + + @algopy.arc4.abimethod() + def confirm_attendance_with_box_ref(self) -> None: + assert self.total_attendees < self.max_attendees, "Max attendees reached" + + minted_asset = self._mint_poa(algopy.Txn.sender) + self.total_attendees += 1 + + box_ref = algopy.BoxRef(key=algopy.Txn.sender.bytes) + has_claimed = bool(box_ref) + assert not has_claimed, "Already claimed POA" + + box_ref.put(algopy.op.itob(minted_asset.id)) + + @algopy.arc4.abimethod() + def confirm_attendance_with_box_map(self) -> None: + assert self.total_attendees < self.max_attendees, "Max attendees reached" + + minted_asset = self._mint_poa(algopy.Txn.sender) + self.total_attendees += 1 + + has_claimed = algopy.Txn.sender.bytes in self.box_map + assert not has_claimed, "Already claimed POA" + + self.box_map[algopy.Txn.sender.bytes] = minted_asset.id + + @algopy.arc4.abimethod(readonly=True) + def get_poa_id(self) -> algopy.UInt64: + poa_id, exists = algopy.op.Box.get(algopy.Txn.sender.bytes) + assert exists, "POA not found" + return algopy.op.btoi(poa_id) + + @algopy.arc4.abimethod(readonly=True) + def get_poa_id_with_box(self) -> algopy.UInt64: + box = algopy.Box(algopy.UInt64, key=algopy.Txn.sender.bytes) + poa_id, exists = box.maybe() + assert exists, "POA not found" + return poa_id + + @algopy.arc4.abimethod(readonly=True) + def get_poa_id_with_box_ref(self) -> algopy.UInt64: + box_ref = algopy.BoxRef(key=algopy.Txn.sender.bytes) + poa_id, exists = box_ref.maybe() + assert exists, "POA not found" + return algopy.op.btoi(poa_id) + + @algopy.arc4.abimethod(readonly=True) + def get_poa_id_with_box_map(self) -> algopy.UInt64: + poa_id, exists = self.box_map.maybe(algopy.Txn.sender.bytes) + assert exists, "POA not found" + return poa_id + + @algopy.arc4.abimethod() + def claim_poa(self, opt_in_txn: algopy.gtxn.AssetTransferTransaction) -> None: + poa_id, exists = algopy.op.Box.get(algopy.Txn.sender.bytes) + assert exists, "POA not found, attendance validation failed!" + assert opt_in_txn.xfer_asset.id == algopy.op.btoi(poa_id), "POA ID mismatch" + assert opt_in_txn.fee == algopy.UInt64(0), "We got you covered for free!" + assert opt_in_txn.asset_amount == algopy.UInt64(0) + assert ( + opt_in_txn.sender == opt_in_txn.asset_receiver == algopy.Txn.sender + ), "Opt-in transaction sender and receiver must be the same" + assert ( + opt_in_txn.asset_close_to + == opt_in_txn.rekey_to + == algopy.Global.zero_address + ), "Opt-in transaction close to must be zero address" + + self._send_poa( + algopy.Txn.sender, + algopy.op.btoi(poa_id), + ) + + @algopy.arc4.abimethod() + def claim_poa_with_box( + self, opt_in_txn: algopy.gtxn.AssetTransferTransaction + ) -> None: + box = algopy.Box(algopy.UInt64, key=algopy.Txn.sender.bytes) + poa_id, exists = box.maybe() + assert exists, "POA not found, attendance validation failed!" + assert opt_in_txn.xfer_asset.id == poa_id, "POA ID mismatch" + assert opt_in_txn.fee == algopy.UInt64(0), "We got you covered for free!" + assert opt_in_txn.asset_amount == algopy.UInt64(0) + assert ( + opt_in_txn.sender == opt_in_txn.asset_receiver == algopy.Txn.sender + ), "Opt-in transaction sender and receiver must be the same" + assert ( + opt_in_txn.asset_close_to + == opt_in_txn.rekey_to + == algopy.Global.zero_address + ), "Opt-in transaction close to must be zero address" + + self._send_poa( + algopy.Txn.sender, + poa_id, + ) + + @algopy.arc4.abimethod() + def claim_poa_with_box_ref( + self, opt_in_txn: algopy.gtxn.AssetTransferTransaction + ) -> None: + box_ref = algopy.BoxRef(key=algopy.Txn.sender.bytes) + poa_id, exists = box_ref.maybe() + assert exists, "POA not found, attendance validation failed!" + assert opt_in_txn.xfer_asset.id == algopy.op.btoi(poa_id), "POA ID mismatch" + assert opt_in_txn.fee == algopy.UInt64(0), "We got you covered for free!" + assert opt_in_txn.asset_amount == algopy.UInt64(0) + assert ( + opt_in_txn.sender == opt_in_txn.asset_receiver == algopy.Txn.sender + ), "Opt-in transaction sender and receiver must be the same" + assert ( + opt_in_txn.asset_close_to + == opt_in_txn.rekey_to + == algopy.Global.zero_address + ), "Opt-in transaction close to must be zero address" + + self._send_poa( + algopy.Txn.sender, + algopy.op.btoi(poa_id), + ) + + @algopy.arc4.abimethod() + def claim_poa_with_box_map( + self, opt_in_txn: algopy.gtxn.AssetTransferTransaction + ) -> None: + poa_id, exists = self.box_map.maybe(algopy.Txn.sender.bytes) + assert exists, "POA not found, attendance validation failed!" + assert opt_in_txn.xfer_asset.id == poa_id, "POA ID mismatch" + assert opt_in_txn.fee == algopy.UInt64(0), "We got you covered for free!" + assert opt_in_txn.asset_amount == algopy.UInt64(0) + assert ( + opt_in_txn.sender == opt_in_txn.asset_receiver == algopy.Txn.sender + ), "Opt-in transaction sender and receiver must be the same" + assert ( + opt_in_txn.asset_close_to + == opt_in_txn.rekey_to + == algopy.Global.zero_address + ), "Opt-in transaction close to must be zero address" + + self._send_poa( + algopy.Txn.sender, + poa_id, + ) + + @algopy.subroutine + def _mint_poa(self, claimer: algopy.Account) -> algopy.Asset: + algopy.ensure_budget(algopy.UInt64(10000), algopy.OpUpFeeSource.AppAccount) + asset_name = b"AlgoKit POA #" + algopy.op.itob(self.total_attendees) + return ( + algopy.itxn.AssetConfig( + asset_name=asset_name, + unit_name=algopy.String("POA"), + total=algopy.UInt64(1), + decimals=0, + url=self.asset_url, + manager=claimer, + ) + .submit() + .created_asset + ) + + @algopy.subroutine + def _send_poa(self, receiver: algopy.Account, asset_id: algopy.UInt64) -> None: + algopy.itxn.AssetTransfer( + xfer_asset=asset_id, + sender=algopy.Global.current_application_address, + asset_receiver=receiver, + asset_amount=1, + ).submit() diff --git a/sampleWorkspace/puya/simulate-response.json b/sampleWorkspace/puya/simulate-response.json new file mode 100644 index 0000000..09cb2bb --- /dev/null +++ b/sampleWorkspace/puya/simulate-response.json @@ -0,0 +1,3186 @@ +{ + "eval-overrides": { + "allow-empty-signatures": true, + "max-log-calls": 2048, + "max-log-size": 65536 + }, + "exec-trace-config": { + "enable": true, + "scratch-change": true, + "stack-change": true + }, + "last-round": 3, + "txn-groups": [ + { + "app-budget-added": 11200, + "app-budget-consumed": 375, + "txn-results": [ + { + "app-budget-consumed": 375, + "exec-trace": { + "approval-program-hash": "9OQMiPyi2J20NA7qdxurd6RlJi8dTAlzNxPzdR7pjPc=", + "approval-program-trace": [ + { + "pc": 1 + }, + { + "pc": 7 + }, + { + "pc": 66, + "stack-additions": [ + { + "type": 2, + "uint": 1002 + } + ] + }, + { + "pc": 68, + "stack-pop-count": 1 + }, + { + "pc": 74 + }, + { + "pc": 78 + }, + { + "pc": 81, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ] + }, + { + "pc": 83, + "stack-pop-count": 1 + }, + { + "pc": 86, + "stack-additions": [ + { + "bytes": "G83lLQ==", + "type": 1 + }, + { + "bytes": "uJYpYg==", + "type": 1 + }, + { + "bytes": "/ps47Q==", + "type": 1 + }, + { + "bytes": "n13xJg==", + "type": 1 + }, + { + "bytes": "uzax1w==", + "type": 1 + }, + { + "bytes": "d6JGaQ==", + "type": 1 + }, + { + "bytes": "0nCXZg==", + "type": 1 + }, + { + "bytes": "fzI4Mw==", + "type": 1 + }, + { + "bytes": "A9JGxw==", + "type": 1 + }, + { + "bytes": "6UbQaw==", + "type": 1 + }, + { + "bytes": "kKZZMg==", + "type": 1 + }, + { + "bytes": "m3GG0w==", + "type": 1 + }, + { + "bytes": "6yryJg==", + "type": 1 + } + ] + }, + { + "pc": 153, + "stack-additions": [ + { + "bytes": "uJYpYg==", + "type": 1 + } + ] + }, + { + "pc": 156, + "stack-pop-count": 14 + }, + { + "pc": 203, + "stack-additions": [ + { + "type": 2 + } + ] + }, + { + "pc": 205, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ], + "stack-pop-count": 1 + }, + { + "pc": 206, + "stack-pop-count": 1 + }, + { + "pc": 207, + "stack-additions": [ + { + "type": 2, + "uint": 1002 + } + ] + }, + { + "pc": 209, + "stack-pop-count": 1 + }, + { + "pc": 210 + }, + { + "pc": 423 + }, + { + "pc": 426, + "stack-additions": [ + { + "type": 2 + } + ] + }, + { + "pc": 427, + "stack-additions": [ + { + "bytes": "dG90YWxfYXR0ZW5kZWVz", + "type": 1 + } + ] + }, + { + "pc": 428, + "stack-additions": [ + { + "type": 2 + }, + { + "type": 2, + "uint": 1 + } + ], + "stack-pop-count": 2 + }, + { + "pc": 429, + "stack-pop-count": 1 + }, + { + "pc": 430, + "stack-additions": [ + { + "type": 2 + } + ] + }, + { + "pc": 431, + "stack-additions": [ + { + "bytes": "bWF4X2F0dGVuZGVlcw==", + "type": 1 + } + ] + }, + { + "pc": 432, + "stack-additions": [ + { + "type": 2, + "uint": 20 + }, + { + "type": 2, + "uint": 1 + } + ], + "stack-pop-count": 2 + }, + { + "pc": 433, + "stack-pop-count": 1 + }, + { + "pc": 434, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ], + "stack-pop-count": 2 + }, + { + "pc": 435, + "stack-pop-count": 1 + }, + { + "pc": 436, + "stack-additions": [ + { + "bytes": "1V0+UuAVNk4S8f4JpJKzTJ0rtSS3bTIhH6jBNQN25LM=", + "type": 1 + } + ] + }, + { + "pc": 438 + }, + { + "pc": 463 + }, + { + "pc": 466, + "stack-additions": [ + { + "type": 2, + "uint": 10000 + } + ] + }, + { + "pc": 469, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ] + }, + { + "pc": 470 + }, + { + "pc": 1183 + }, + { + "pc": 1186, + "stack-additions": [ + { + "type": 2, + "uint": 10000 + } + ] + }, + { + "pc": 1188, + "stack-additions": [ + { + "type": 2, + "uint": 10 + } + ] + }, + { + "pc": 1190, + "stack-additions": [ + { + "type": 2, + "uint": 10010 + } + ], + "stack-pop-count": 2 + }, + { + "pc": 1191, + "stack-additions": [ + { + "type": 2, + "uint": 10010 + } + ] + }, + { + "pc": 1193, + "stack-additions": [ + { + "type": 2, + "uint": 660 + } + ] + }, + { + "pc": 1195, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ], + "stack-pop-count": 2 + }, + { + "pc": 1196, + "stack-pop-count": 1 + }, + { + "pc": 1199 + }, + { + "pc": 1200, + "stack-additions": [ + { + "type": 2, + "uint": 6 + } + ] + }, + { + "pc": 1202, + "stack-pop-count": 1 + }, + { + "pc": 1204, + "stack-additions": [ + { + "type": 2, + "uint": 5 + } + ] + }, + { + "pc": 1206, + "stack-pop-count": 1 + }, + { + "pc": 1208, + "stack-additions": [ + { + "bytes": "BoEB", + "type": 1 + } + ] + }, + { + "pc": 1210, + "stack-pop-count": 1 + }, + { + "pc": 1212, + "stack-additions": [ + { + "bytes": "BoEB", + "type": 1 + } + ] + }, + { + "pc": 1214, + "stack-pop-count": 1 + }, + { + "pc": 1216, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ] + }, + { + "pc": 1218, + "stack-pop-count": 1 + }, + { + "pc": 1233, + "stack-additions": [ + { + "type": 2, + "uint": 1000 + } + ] + }, + { + "pc": 1235, + "stack-pop-count": 1 + }, + { + "pc": 1237, + "spawned-inners": [0], + "stack-additions": [ + { + "bytes": "1V0+UuAVNk4S8f4JpJKzTJ0rtSS3bTIhH6jBNQN25LM=", + "type": 1 + }, + { + "type": 2, + "uint": 10000 + }, + { + "type": 2, + "uint": 1 + }, + { + "type": 2, + "uint": 10010 + } + ] + }, + { + "pc": 1238 + }, + { + "pc": 1191, + "stack-additions": [ + { + "type": 2, + "uint": 10010 + } + ] + }, + { + "pc": 1193, + "stack-additions": [ + { + "type": 2, + "uint": 1340 + } + ] + }, + { + "pc": 1195, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ], + "stack-pop-count": 2 + }, + { + "pc": 1196, + "stack-pop-count": 1 + }, + { + "pc": 1199 + }, + { + "pc": 1200, + "stack-additions": [ + { + "type": 2, + "uint": 6 + } + ] + }, + { + "pc": 1202, + "stack-pop-count": 1 + }, + { + "pc": 1204, + "stack-additions": [ + { + "type": 2, + "uint": 5 + } + ] + }, + { + "pc": 1206, + "stack-pop-count": 1 + }, + { + "pc": 1208, + "stack-additions": [ + { + "bytes": "BoEB", + "type": 1 + } + ] + }, + { + "pc": 1210, + "stack-pop-count": 1 + }, + { + "pc": 1212, + "stack-additions": [ + { + "bytes": "BoEB", + "type": 1 + } + ] + }, + { + "pc": 1214, + "stack-pop-count": 1 + }, + { + "pc": 1216, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ] + }, + { + "pc": 1218, + "stack-pop-count": 1 + }, + { + "pc": 1233, + "stack-additions": [ + { + "type": 2, + "uint": 1000 + } + ] + }, + { + "pc": 1235, + "stack-pop-count": 1 + }, + { + "pc": 1237, + "spawned-inners": [1], + "stack-additions": [ + { + "bytes": "1V0+UuAVNk4S8f4JpJKzTJ0rtSS3bTIhH6jBNQN25LM=", + "type": 1 + }, + { + "type": 2, + "uint": 10000 + }, + { + "type": 2, + "uint": 1 + }, + { + "type": 2, + "uint": 10010 + } + ] + }, + { + "pc": 1238 + }, + { + "pc": 1191, + "stack-additions": [ + { + "type": 2, + "uint": 10010 + } + ] + }, + { + "pc": 1193, + "stack-additions": [ + { + "type": 2, + "uint": 2020 + } + ] + }, + { + "pc": 1195, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ], + "stack-pop-count": 2 + }, + { + "pc": 1196, + "stack-pop-count": 1 + }, + { + "pc": 1199 + }, + { + "pc": 1200, + "stack-additions": [ + { + "type": 2, + "uint": 6 + } + ] + }, + { + "pc": 1202, + "stack-pop-count": 1 + }, + { + "pc": 1204, + "stack-additions": [ + { + "type": 2, + "uint": 5 + } + ] + }, + { + "pc": 1206, + "stack-pop-count": 1 + }, + { + "pc": 1208, + "stack-additions": [ + { + "bytes": "BoEB", + "type": 1 + } + ] + }, + { + "pc": 1210, + "stack-pop-count": 1 + }, + { + "pc": 1212, + "stack-additions": [ + { + "bytes": "BoEB", + "type": 1 + } + ] + }, + { + "pc": 1214, + "stack-pop-count": 1 + }, + { + "pc": 1216, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ] + }, + { + "pc": 1218, + "stack-pop-count": 1 + }, + { + "pc": 1233, + "stack-additions": [ + { + "type": 2, + "uint": 1000 + } + ] + }, + { + "pc": 1235, + "stack-pop-count": 1 + }, + { + "pc": 1237, + "spawned-inners": [2], + "stack-additions": [ + { + "bytes": "1V0+UuAVNk4S8f4JpJKzTJ0rtSS3bTIhH6jBNQN25LM=", + "type": 1 + }, + { + "type": 2, + "uint": 10000 + }, + { + "type": 2, + "uint": 1 + }, + { + "type": 2, + "uint": 10010 + } + ] + }, + { + "pc": 1238 + }, + { + "pc": 1191, + "stack-additions": [ + { + "type": 2, + "uint": 10010 + } + ] + }, + { + "pc": 1193, + "stack-additions": [ + { + "type": 2, + "uint": 2700 + } + ] + }, + { + "pc": 1195, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ], + "stack-pop-count": 2 + }, + { + "pc": 1196, + "stack-pop-count": 1 + }, + { + "pc": 1199 + }, + { + "pc": 1200, + "stack-additions": [ + { + "type": 2, + "uint": 6 + } + ] + }, + { + "pc": 1202, + "stack-pop-count": 1 + }, + { + "pc": 1204, + "stack-additions": [ + { + "type": 2, + "uint": 5 + } + ] + }, + { + "pc": 1206, + "stack-pop-count": 1 + }, + { + "pc": 1208, + "stack-additions": [ + { + "bytes": "BoEB", + "type": 1 + } + ] + }, + { + "pc": 1210, + "stack-pop-count": 1 + }, + { + "pc": 1212, + "stack-additions": [ + { + "bytes": "BoEB", + "type": 1 + } + ] + }, + { + "pc": 1214, + "stack-pop-count": 1 + }, + { + "pc": 1216, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ] + }, + { + "pc": 1218, + "stack-pop-count": 1 + }, + { + "pc": 1233, + "stack-additions": [ + { + "type": 2, + "uint": 1000 + } + ] + }, + { + "pc": 1235, + "stack-pop-count": 1 + }, + { + "pc": 1237, + "spawned-inners": [3], + "stack-additions": [ + { + "bytes": "1V0+UuAVNk4S8f4JpJKzTJ0rtSS3bTIhH6jBNQN25LM=", + "type": 1 + }, + { + "type": 2, + "uint": 10000 + }, + { + "type": 2, + "uint": 1 + }, + { + "type": 2, + "uint": 10010 + } + ] + }, + { + "pc": 1238 + }, + { + "pc": 1191, + "stack-additions": [ + { + "type": 2, + "uint": 10010 + } + ] + }, + { + "pc": 1193, + "stack-additions": [ + { + "type": 2, + "uint": 3380 + } + ] + }, + { + "pc": 1195, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ], + "stack-pop-count": 2 + }, + { + "pc": 1196, + "stack-pop-count": 1 + }, + { + "pc": 1199 + }, + { + "pc": 1200, + "stack-additions": [ + { + "type": 2, + "uint": 6 + } + ] + }, + { + "pc": 1202, + "stack-pop-count": 1 + }, + { + "pc": 1204, + "stack-additions": [ + { + "type": 2, + "uint": 5 + } + ] + }, + { + "pc": 1206, + "stack-pop-count": 1 + }, + { + "pc": 1208, + "stack-additions": [ + { + "bytes": "BoEB", + "type": 1 + } + ] + }, + { + "pc": 1210, + "stack-pop-count": 1 + }, + { + "pc": 1212, + "stack-additions": [ + { + "bytes": "BoEB", + "type": 1 + } + ] + }, + { + "pc": 1214, + "stack-pop-count": 1 + }, + { + "pc": 1216, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ] + }, + { + "pc": 1218, + "stack-pop-count": 1 + }, + { + "pc": 1233, + "stack-additions": [ + { + "type": 2, + "uint": 1000 + } + ] + }, + { + "pc": 1235, + "stack-pop-count": 1 + }, + { + "pc": 1237, + "spawned-inners": [4], + "stack-additions": [ + { + "bytes": "1V0+UuAVNk4S8f4JpJKzTJ0rtSS3bTIhH6jBNQN25LM=", + "type": 1 + }, + { + "type": 2, + "uint": 10000 + }, + { + "type": 2, + "uint": 1 + }, + { + "type": 2, + "uint": 10010 + } + ] + }, + { + "pc": 1238 + }, + { + "pc": 1191, + "stack-additions": [ + { + "type": 2, + "uint": 10010 + } + ] + }, + { + "pc": 1193, + "stack-additions": [ + { + "type": 2, + "uint": 4060 + } + ] + }, + { + "pc": 1195, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ], + "stack-pop-count": 2 + }, + { + "pc": 1196, + "stack-pop-count": 1 + }, + { + "pc": 1199 + }, + { + "pc": 1200, + "stack-additions": [ + { + "type": 2, + "uint": 6 + } + ] + }, + { + "pc": 1202, + "stack-pop-count": 1 + }, + { + "pc": 1204, + "stack-additions": [ + { + "type": 2, + "uint": 5 + } + ] + }, + { + "pc": 1206, + "stack-pop-count": 1 + }, + { + "pc": 1208, + "stack-additions": [ + { + "bytes": "BoEB", + "type": 1 + } + ] + }, + { + "pc": 1210, + "stack-pop-count": 1 + }, + { + "pc": 1212, + "stack-additions": [ + { + "bytes": "BoEB", + "type": 1 + } + ] + }, + { + "pc": 1214, + "stack-pop-count": 1 + }, + { + "pc": 1216, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ] + }, + { + "pc": 1218, + "stack-pop-count": 1 + }, + { + "pc": 1233, + "stack-additions": [ + { + "type": 2, + "uint": 1000 + } + ] + }, + { + "pc": 1235, + "stack-pop-count": 1 + }, + { + "pc": 1237, + "spawned-inners": [5], + "stack-additions": [ + { + "bytes": "1V0+UuAVNk4S8f4JpJKzTJ0rtSS3bTIhH6jBNQN25LM=", + "type": 1 + }, + { + "type": 2, + "uint": 10000 + }, + { + "type": 2, + "uint": 1 + }, + { + "type": 2, + "uint": 10010 + } + ] + }, + { + "pc": 1238 + }, + { + "pc": 1191, + "stack-additions": [ + { + "type": 2, + "uint": 10010 + } + ] + }, + { + "pc": 1193, + "stack-additions": [ + { + "type": 2, + "uint": 4740 + } + ] + }, + { + "pc": 1195, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ], + "stack-pop-count": 2 + }, + { + "pc": 1196, + "stack-pop-count": 1 + }, + { + "pc": 1199 + }, + { + "pc": 1200, + "stack-additions": [ + { + "type": 2, + "uint": 6 + } + ] + }, + { + "pc": 1202, + "stack-pop-count": 1 + }, + { + "pc": 1204, + "stack-additions": [ + { + "type": 2, + "uint": 5 + } + ] + }, + { + "pc": 1206, + "stack-pop-count": 1 + }, + { + "pc": 1208, + "stack-additions": [ + { + "bytes": "BoEB", + "type": 1 + } + ] + }, + { + "pc": 1210, + "stack-pop-count": 1 + }, + { + "pc": 1212, + "stack-additions": [ + { + "bytes": "BoEB", + "type": 1 + } + ] + }, + { + "pc": 1214, + "stack-pop-count": 1 + }, + { + "pc": 1216, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ] + }, + { + "pc": 1218, + "stack-pop-count": 1 + }, + { + "pc": 1233, + "stack-additions": [ + { + "type": 2, + "uint": 1000 + } + ] + }, + { + "pc": 1235, + "stack-pop-count": 1 + }, + { + "pc": 1237, + "spawned-inners": [6], + "stack-additions": [ + { + "bytes": "1V0+UuAVNk4S8f4JpJKzTJ0rtSS3bTIhH6jBNQN25LM=", + "type": 1 + }, + { + "type": 2, + "uint": 10000 + }, + { + "type": 2, + "uint": 1 + }, + { + "type": 2, + "uint": 10010 + } + ] + }, + { + "pc": 1238 + }, + { + "pc": 1191, + "stack-additions": [ + { + "type": 2, + "uint": 10010 + } + ] + }, + { + "pc": 1193, + "stack-additions": [ + { + "type": 2, + "uint": 5420 + } + ] + }, + { + "pc": 1195, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ], + "stack-pop-count": 2 + }, + { + "pc": 1196, + "stack-pop-count": 1 + }, + { + "pc": 1199 + }, + { + "pc": 1200, + "stack-additions": [ + { + "type": 2, + "uint": 6 + } + ] + }, + { + "pc": 1202, + "stack-pop-count": 1 + }, + { + "pc": 1204, + "stack-additions": [ + { + "type": 2, + "uint": 5 + } + ] + }, + { + "pc": 1206, + "stack-pop-count": 1 + }, + { + "pc": 1208, + "stack-additions": [ + { + "bytes": "BoEB", + "type": 1 + } + ] + }, + { + "pc": 1210, + "stack-pop-count": 1 + }, + { + "pc": 1212, + "stack-additions": [ + { + "bytes": "BoEB", + "type": 1 + } + ] + }, + { + "pc": 1214, + "stack-pop-count": 1 + }, + { + "pc": 1216, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ] + }, + { + "pc": 1218, + "stack-pop-count": 1 + }, + { + "pc": 1233, + "stack-additions": [ + { + "type": 2, + "uint": 1000 + } + ] + }, + { + "pc": 1235, + "stack-pop-count": 1 + }, + { + "pc": 1237, + "spawned-inners": [7], + "stack-additions": [ + { + "bytes": "1V0+UuAVNk4S8f4JpJKzTJ0rtSS3bTIhH6jBNQN25LM=", + "type": 1 + }, + { + "type": 2, + "uint": 10000 + }, + { + "type": 2, + "uint": 1 + }, + { + "type": 2, + "uint": 10010 + } + ] + }, + { + "pc": 1238 + }, + { + "pc": 1191, + "stack-additions": [ + { + "type": 2, + "uint": 10010 + } + ] + }, + { + "pc": 1193, + "stack-additions": [ + { + "type": 2, + "uint": 6100 + } + ] + }, + { + "pc": 1195, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ], + "stack-pop-count": 2 + }, + { + "pc": 1196, + "stack-pop-count": 1 + }, + { + "pc": 1199 + }, + { + "pc": 1200, + "stack-additions": [ + { + "type": 2, + "uint": 6 + } + ] + }, + { + "pc": 1202, + "stack-pop-count": 1 + }, + { + "pc": 1204, + "stack-additions": [ + { + "type": 2, + "uint": 5 + } + ] + }, + { + "pc": 1206, + "stack-pop-count": 1 + }, + { + "pc": 1208, + "stack-additions": [ + { + "bytes": "BoEB", + "type": 1 + } + ] + }, + { + "pc": 1210, + "stack-pop-count": 1 + }, + { + "pc": 1212, + "stack-additions": [ + { + "bytes": "BoEB", + "type": 1 + } + ] + }, + { + "pc": 1214, + "stack-pop-count": 1 + }, + { + "pc": 1216, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ] + }, + { + "pc": 1218, + "stack-pop-count": 1 + }, + { + "pc": 1233, + "stack-additions": [ + { + "type": 2, + "uint": 1000 + } + ] + }, + { + "pc": 1235, + "stack-pop-count": 1 + }, + { + "pc": 1237, + "spawned-inners": [8], + "stack-additions": [ + { + "bytes": "1V0+UuAVNk4S8f4JpJKzTJ0rtSS3bTIhH6jBNQN25LM=", + "type": 1 + }, + { + "type": 2, + "uint": 10000 + }, + { + "type": 2, + "uint": 1 + }, + { + "type": 2, + "uint": 10010 + } + ] + }, + { + "pc": 1238 + }, + { + "pc": 1191, + "stack-additions": [ + { + "type": 2, + "uint": 10010 + } + ] + }, + { + "pc": 1193, + "stack-additions": [ + { + "type": 2, + "uint": 6780 + } + ] + }, + { + "pc": 1195, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ], + "stack-pop-count": 2 + }, + { + "pc": 1196, + "stack-pop-count": 1 + }, + { + "pc": 1199 + }, + { + "pc": 1200, + "stack-additions": [ + { + "type": 2, + "uint": 6 + } + ] + }, + { + "pc": 1202, + "stack-pop-count": 1 + }, + { + "pc": 1204, + "stack-additions": [ + { + "type": 2, + "uint": 5 + } + ] + }, + { + "pc": 1206, + "stack-pop-count": 1 + }, + { + "pc": 1208, + "stack-additions": [ + { + "bytes": "BoEB", + "type": 1 + } + ] + }, + { + "pc": 1210, + "stack-pop-count": 1 + }, + { + "pc": 1212, + "stack-additions": [ + { + "bytes": "BoEB", + "type": 1 + } + ] + }, + { + "pc": 1214, + "stack-pop-count": 1 + }, + { + "pc": 1216, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ] + }, + { + "pc": 1218, + "stack-pop-count": 1 + }, + { + "pc": 1233, + "stack-additions": [ + { + "type": 2, + "uint": 1000 + } + ] + }, + { + "pc": 1235, + "stack-pop-count": 1 + }, + { + "pc": 1237, + "spawned-inners": [9], + "stack-additions": [ + { + "bytes": "1V0+UuAVNk4S8f4JpJKzTJ0rtSS3bTIhH6jBNQN25LM=", + "type": 1 + }, + { + "type": 2, + "uint": 10000 + }, + { + "type": 2, + "uint": 1 + }, + { + "type": 2, + "uint": 10010 + } + ] + }, + { + "pc": 1238 + }, + { + "pc": 1191, + "stack-additions": [ + { + "type": 2, + "uint": 10010 + } + ] + }, + { + "pc": 1193, + "stack-additions": [ + { + "type": 2, + "uint": 7460 + } + ] + }, + { + "pc": 1195, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ], + "stack-pop-count": 2 + }, + { + "pc": 1196, + "stack-pop-count": 1 + }, + { + "pc": 1199 + }, + { + "pc": 1200, + "stack-additions": [ + { + "type": 2, + "uint": 6 + } + ] + }, + { + "pc": 1202, + "stack-pop-count": 1 + }, + { + "pc": 1204, + "stack-additions": [ + { + "type": 2, + "uint": 5 + } + ] + }, + { + "pc": 1206, + "stack-pop-count": 1 + }, + { + "pc": 1208, + "stack-additions": [ + { + "bytes": "BoEB", + "type": 1 + } + ] + }, + { + "pc": 1210, + "stack-pop-count": 1 + }, + { + "pc": 1212, + "stack-additions": [ + { + "bytes": "BoEB", + "type": 1 + } + ] + }, + { + "pc": 1214, + "stack-pop-count": 1 + }, + { + "pc": 1216, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ] + }, + { + "pc": 1218, + "stack-pop-count": 1 + }, + { + "pc": 1233, + "stack-additions": [ + { + "type": 2, + "uint": 1000 + } + ] + }, + { + "pc": 1235, + "stack-pop-count": 1 + }, + { + "pc": 1237, + "spawned-inners": [10], + "stack-additions": [ + { + "bytes": "1V0+UuAVNk4S8f4JpJKzTJ0rtSS3bTIhH6jBNQN25LM=", + "type": 1 + }, + { + "type": 2, + "uint": 10000 + }, + { + "type": 2, + "uint": 1 + }, + { + "type": 2, + "uint": 10010 + } + ] + }, + { + "pc": 1238 + }, + { + "pc": 1191, + "stack-additions": [ + { + "type": 2, + "uint": 10010 + } + ] + }, + { + "pc": 1193, + "stack-additions": [ + { + "type": 2, + "uint": 8140 + } + ] + }, + { + "pc": 1195, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ], + "stack-pop-count": 2 + }, + { + "pc": 1196, + "stack-pop-count": 1 + }, + { + "pc": 1199 + }, + { + "pc": 1200, + "stack-additions": [ + { + "type": 2, + "uint": 6 + } + ] + }, + { + "pc": 1202, + "stack-pop-count": 1 + }, + { + "pc": 1204, + "stack-additions": [ + { + "type": 2, + "uint": 5 + } + ] + }, + { + "pc": 1206, + "stack-pop-count": 1 + }, + { + "pc": 1208, + "stack-additions": [ + { + "bytes": "BoEB", + "type": 1 + } + ] + }, + { + "pc": 1210, + "stack-pop-count": 1 + }, + { + "pc": 1212, + "stack-additions": [ + { + "bytes": "BoEB", + "type": 1 + } + ] + }, + { + "pc": 1214, + "stack-pop-count": 1 + }, + { + "pc": 1216, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ] + }, + { + "pc": 1218, + "stack-pop-count": 1 + }, + { + "pc": 1233, + "stack-additions": [ + { + "type": 2, + "uint": 1000 + } + ] + }, + { + "pc": 1235, + "stack-pop-count": 1 + }, + { + "pc": 1237, + "spawned-inners": [11], + "stack-additions": [ + { + "bytes": "1V0+UuAVNk4S8f4JpJKzTJ0rtSS3bTIhH6jBNQN25LM=", + "type": 1 + }, + { + "type": 2, + "uint": 10000 + }, + { + "type": 2, + "uint": 1 + }, + { + "type": 2, + "uint": 10010 + } + ] + }, + { + "pc": 1238 + }, + { + "pc": 1191, + "stack-additions": [ + { + "type": 2, + "uint": 10010 + } + ] + }, + { + "pc": 1193, + "stack-additions": [ + { + "type": 2, + "uint": 8820 + } + ] + }, + { + "pc": 1195, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ], + "stack-pop-count": 2 + }, + { + "pc": 1196, + "stack-pop-count": 1 + }, + { + "pc": 1199 + }, + { + "pc": 1200, + "stack-additions": [ + { + "type": 2, + "uint": 6 + } + ] + }, + { + "pc": 1202, + "stack-pop-count": 1 + }, + { + "pc": 1204, + "stack-additions": [ + { + "type": 2, + "uint": 5 + } + ] + }, + { + "pc": 1206, + "stack-pop-count": 1 + }, + { + "pc": 1208, + "stack-additions": [ + { + "bytes": "BoEB", + "type": 1 + } + ] + }, + { + "pc": 1210, + "stack-pop-count": 1 + }, + { + "pc": 1212, + "stack-additions": [ + { + "bytes": "BoEB", + "type": 1 + } + ] + }, + { + "pc": 1214, + "stack-pop-count": 1 + }, + { + "pc": 1216, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ] + }, + { + "pc": 1218, + "stack-pop-count": 1 + }, + { + "pc": 1233, + "stack-additions": [ + { + "type": 2, + "uint": 1000 + } + ] + }, + { + "pc": 1235, + "stack-pop-count": 1 + }, + { + "pc": 1237, + "spawned-inners": [12], + "stack-additions": [ + { + "bytes": "1V0+UuAVNk4S8f4JpJKzTJ0rtSS3bTIhH6jBNQN25LM=", + "type": 1 + }, + { + "type": 2, + "uint": 10000 + }, + { + "type": 2, + "uint": 1 + }, + { + "type": 2, + "uint": 10010 + } + ] + }, + { + "pc": 1238 + }, + { + "pc": 1191, + "stack-additions": [ + { + "type": 2, + "uint": 10010 + } + ] + }, + { + "pc": 1193, + "stack-additions": [ + { + "type": 2, + "uint": 9500 + } + ] + }, + { + "pc": 1195, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ], + "stack-pop-count": 2 + }, + { + "pc": 1196, + "stack-pop-count": 1 + }, + { + "pc": 1199 + }, + { + "pc": 1200, + "stack-additions": [ + { + "type": 2, + "uint": 6 + } + ] + }, + { + "pc": 1202, + "stack-pop-count": 1 + }, + { + "pc": 1204, + "stack-additions": [ + { + "type": 2, + "uint": 5 + } + ] + }, + { + "pc": 1206, + "stack-pop-count": 1 + }, + { + "pc": 1208, + "stack-additions": [ + { + "bytes": "BoEB", + "type": 1 + } + ] + }, + { + "pc": 1210, + "stack-pop-count": 1 + }, + { + "pc": 1212, + "stack-additions": [ + { + "bytes": "BoEB", + "type": 1 + } + ] + }, + { + "pc": 1214, + "stack-pop-count": 1 + }, + { + "pc": 1216, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ] + }, + { + "pc": 1218, + "stack-pop-count": 1 + }, + { + "pc": 1233, + "stack-additions": [ + { + "type": 2, + "uint": 1000 + } + ] + }, + { + "pc": 1235, + "stack-pop-count": 1 + }, + { + "pc": 1237, + "spawned-inners": [13], + "stack-additions": [ + { + "bytes": "1V0+UuAVNk4S8f4JpJKzTJ0rtSS3bTIhH6jBNQN25LM=", + "type": 1 + }, + { + "type": 2, + "uint": 10000 + }, + { + "type": 2, + "uint": 1 + }, + { + "type": 2, + "uint": 10010 + } + ] + }, + { + "pc": 1238 + }, + { + "pc": 1191, + "stack-additions": [ + { + "type": 2, + "uint": 10010 + } + ] + }, + { + "pc": 1193, + "stack-additions": [ + { + "type": 2, + "uint": 10180 + } + ] + }, + { + "pc": 1195, + "stack-additions": [ + { + "type": 2 + } + ], + "stack-pop-count": 2 + }, + { + "pc": 1196, + "stack-pop-count": 1 + }, + { + "pc": 1241, + "stack-pop-count": 3 + }, + { + "pc": 473, + "stack-additions": [ + { + "type": 2 + } + ] + }, + { + "pc": 474, + "stack-additions": [ + { + "bytes": "dG90YWxfYXR0ZW5kZWVz", + "type": 1 + } + ] + }, + { + "pc": 475, + "stack-additions": [ + { + "type": 2 + }, + { + "type": 2, + "uint": 1 + } + ], + "stack-pop-count": 2 + }, + { + "pc": 476, + "stack-pop-count": 1 + }, + { + "pc": 477, + "stack-additions": [ + { + "bytes": "AAAAAAAAAAA=", + "type": 1 + } + ], + "stack-pop-count": 1 + }, + { + "pc": 478, + "stack-additions": [ + { + "bytes": "QWxnb0tpdCBQT0EgIw==", + "type": 1 + } + ] + }, + { + "pc": 493, + "stack-additions": [ + { + "bytes": "QWxnb0tpdCBQT0EgIw==", + "type": 1 + }, + { + "bytes": "AAAAAAAAAAA=", + "type": 1 + } + ], + "stack-pop-count": 2 + }, + { + "pc": 494, + "stack-additions": [ + { + "bytes": "QWxnb0tpdCBQT0EgIwAAAAAAAAAA", + "type": 1 + } + ], + "stack-pop-count": 2 + }, + { + "pc": 495 + }, + { + "pc": 496, + "stack-additions": [ + { + "type": 2 + } + ] + }, + { + "pc": 497, + "stack-additions": [ + { + "bytes": "YXNzZXRfdXJs", + "type": 1 + } + ] + }, + { + "pc": 499, + "stack-additions": [ + { + "bytes": "aXBmczovL1FtVzV2RVJrZ2VKSnRTWTFZUWRjV1U2Z3NIQ1pDeUxGdE0xb1Q5dXl5MldHbTg=", + "type": 1 + }, + { + "type": 2, + "uint": 1 + } + ], + "stack-pop-count": 2 + }, + { + "pc": 500, + "stack-pop-count": 1 + }, + { + "pc": 501, + "stack-additions": [ + { + "bytes": "1V0+UuAVNk4S8f4JpJKzTJ0rtSS3bTIhH6jBNQN25LM=", + "type": 1 + } + ] + }, + { + "pc": 503, + "stack-pop-count": 1 + }, + { + "pc": 505, + "stack-pop-count": 1 + }, + { + "pc": 507, + "stack-additions": [ + { + "type": 2 + } + ] + }, + { + "pc": 508, + "stack-pop-count": 1 + }, + { + "pc": 510, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ] + }, + { + "pc": 511, + "stack-pop-count": 1 + }, + { + "pc": 513, + "stack-additions": [ + { + "bytes": "UE9B", + "type": 1 + } + ] + }, + { + "pc": 518, + "stack-pop-count": 1 + }, + { + "pc": 520, + "stack-pop-count": 1 + }, + { + "pc": 522, + "stack-additions": [ + { + "type": 2, + "uint": 3 + } + ] + }, + { + "pc": 524, + "stack-pop-count": 1 + }, + { + "pc": 526, + "stack-additions": [ + { + "type": 2 + } + ] + }, + { + "pc": 527, + "stack-pop-count": 1 + }, + { + "pc": 529, + "spawned-inners": [14] + }, + { + "pc": 530, + "stack-additions": [ + { + "type": 2, + "uint": 1019 + } + ] + }, + { + "pc": 532, + "stack-additions": [ + { + "type": 2, + "uint": 1019 + } + ], + "stack-pop-count": 2 + }, + { + "pc": 441, + "stack-additions": [ + { + "type": 2 + } + ] + }, + { + "pc": 442, + "stack-additions": [ + { + "bytes": "dG90YWxfYXR0ZW5kZWVz", + "type": 1 + } + ] + }, + { + "pc": 443, + "stack-additions": [ + { + "type": 2 + }, + { + "type": 2, + "uint": 1 + } + ], + "stack-pop-count": 2 + }, + { + "pc": 444, + "stack-pop-count": 1 + }, + { + "pc": 445, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ] + }, + { + "pc": 446, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ], + "stack-pop-count": 2 + }, + { + "pc": 447, + "stack-additions": [ + { + "bytes": "dG90YWxfYXR0ZW5kZWVz", + "type": 1 + } + ] + }, + { + "pc": 448, + "stack-additions": [ + { + "bytes": "dG90YWxfYXR0ZW5kZWVz", + "type": 1 + }, + { + "type": 2, + "uint": 1 + } + ], + "stack-pop-count": 2 + }, + { + "pc": 449, + "stack-pop-count": 2 + }, + { + "pc": 450, + "stack-additions": [ + { + "bytes": "1V0+UuAVNk4S8f4JpJKzTJ0rtSS3bTIhH6jBNQN25LM=", + "type": 1 + } + ] + }, + { + "pc": 452, + "stack-additions": [ + { + "type": 1 + }, + { + "type": 2 + } + ], + "stack-pop-count": 1 + }, + { + "pc": 453, + "stack-additions": [ + { + "type": 2 + } + ], + "stack-pop-count": 2 + }, + { + "pc": 455, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ], + "stack-pop-count": 1 + }, + { + "pc": 456, + "stack-pop-count": 1 + }, + { + "pc": 457, + "stack-additions": [ + { + "bytes": "1V0+UuAVNk4S8f4JpJKzTJ0rtSS3bTIhH6jBNQN25LM=", + "type": 1 + } + ] + }, + { + "pc": 459, + "stack-additions": [ + { + "bytes": "1V0+UuAVNk4S8f4JpJKzTJ0rtSS3bTIhH6jBNQN25LM=", + "type": 1 + }, + { + "type": 2, + "uint": 1019 + } + ], + "stack-pop-count": 2 + }, + { + "pc": 460, + "stack-additions": [ + { + "bytes": "AAAAAAAAA/s=", + "type": 1 + } + ], + "stack-pop-count": 1 + }, + { + "pc": 461, + "stack-pop-count": 2 + }, + { + "pc": 462 + }, + { + "pc": 213, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ] + }, + { + "pc": 214, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ], + "stack-pop-count": 1 + }, + { + "pc": 77, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ], + "stack-pop-count": 1 + } + ], + "inner-trace": [ + { + "approval-program-hash": "p0zHRDBy7V/S2TDcAaXYEd2OF8KSxV/1y0ceYEL6qxU=", + "approval-program-trace": [ + { + "pc": 1, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ] + } + ] + }, + { + "approval-program-hash": "p0zHRDBy7V/S2TDcAaXYEd2OF8KSxV/1y0ceYEL6qxU=", + "approval-program-trace": [ + { + "pc": 1, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ] + } + ] + }, + { + "approval-program-hash": "p0zHRDBy7V/S2TDcAaXYEd2OF8KSxV/1y0ceYEL6qxU=", + "approval-program-trace": [ + { + "pc": 1, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ] + } + ] + }, + { + "approval-program-hash": "p0zHRDBy7V/S2TDcAaXYEd2OF8KSxV/1y0ceYEL6qxU=", + "approval-program-trace": [ + { + "pc": 1, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ] + } + ] + }, + { + "approval-program-hash": "p0zHRDBy7V/S2TDcAaXYEd2OF8KSxV/1y0ceYEL6qxU=", + "approval-program-trace": [ + { + "pc": 1, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ] + } + ] + }, + { + "approval-program-hash": "p0zHRDBy7V/S2TDcAaXYEd2OF8KSxV/1y0ceYEL6qxU=", + "approval-program-trace": [ + { + "pc": 1, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ] + } + ] + }, + { + "approval-program-hash": "p0zHRDBy7V/S2TDcAaXYEd2OF8KSxV/1y0ceYEL6qxU=", + "approval-program-trace": [ + { + "pc": 1, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ] + } + ] + }, + { + "approval-program-hash": "p0zHRDBy7V/S2TDcAaXYEd2OF8KSxV/1y0ceYEL6qxU=", + "approval-program-trace": [ + { + "pc": 1, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ] + } + ] + }, + { + "approval-program-hash": "p0zHRDBy7V/S2TDcAaXYEd2OF8KSxV/1y0ceYEL6qxU=", + "approval-program-trace": [ + { + "pc": 1, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ] + } + ] + }, + { + "approval-program-hash": "p0zHRDBy7V/S2TDcAaXYEd2OF8KSxV/1y0ceYEL6qxU=", + "approval-program-trace": [ + { + "pc": 1, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ] + } + ] + }, + { + "approval-program-hash": "p0zHRDBy7V/S2TDcAaXYEd2OF8KSxV/1y0ceYEL6qxU=", + "approval-program-trace": [ + { + "pc": 1, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ] + } + ] + }, + { + "approval-program-hash": "p0zHRDBy7V/S2TDcAaXYEd2OF8KSxV/1y0ceYEL6qxU=", + "approval-program-trace": [ + { + "pc": 1, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ] + } + ] + }, + { + "approval-program-hash": "p0zHRDBy7V/S2TDcAaXYEd2OF8KSxV/1y0ceYEL6qxU=", + "approval-program-trace": [ + { + "pc": 1, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ] + } + ] + }, + { + "approval-program-hash": "p0zHRDBy7V/S2TDcAaXYEd2OF8KSxV/1y0ceYEL6qxU=", + "approval-program-trace": [ + { + "pc": 1, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ] + } + ] + }, + {} + ] + }, + "txn-result": { + "global-state-delta": [ + { + "key": "dG90YWxfYXR0ZW5kZWVz", + "value": { + "action": 2, + "uint": 1 + } + } + ], + "inner-txns": [ + { + "application-index": 1005, + "pool-error": "", + "txn": { + "txn": { + "apan": 5, + "apap": "BoEB", + "apsu": "BoEB", + "fee": 1000, + "fv": 3, + "lv": 1003, + "snd": "O3VYQKJ45XILV2GVDO44LM2IGPUD2QYRXNFX5K4ZDC2B4BD4ZZXU5AQG24", + "type": "appl" + } + } + }, + { + "application-index": 1006, + "pool-error": "", + "txn": { + "txn": { + "apan": 5, + "apap": "BoEB", + "apsu": "BoEB", + "fee": 1000, + "fv": 3, + "lv": 1003, + "snd": "O3VYQKJ45XILV2GVDO44LM2IGPUD2QYRXNFX5K4ZDC2B4BD4ZZXU5AQG24", + "type": "appl" + } + } + }, + { + "application-index": 1007, + "pool-error": "", + "txn": { + "txn": { + "apan": 5, + "apap": "BoEB", + "apsu": "BoEB", + "fee": 1000, + "fv": 3, + "lv": 1003, + "snd": "O3VYQKJ45XILV2GVDO44LM2IGPUD2QYRXNFX5K4ZDC2B4BD4ZZXU5AQG24", + "type": "appl" + } + } + }, + { + "application-index": 1008, + "pool-error": "", + "txn": { + "txn": { + "apan": 5, + "apap": "BoEB", + "apsu": "BoEB", + "fee": 1000, + "fv": 3, + "lv": 1003, + "snd": "O3VYQKJ45XILV2GVDO44LM2IGPUD2QYRXNFX5K4ZDC2B4BD4ZZXU5AQG24", + "type": "appl" + } + } + }, + { + "application-index": 1009, + "pool-error": "", + "txn": { + "txn": { + "apan": 5, + "apap": "BoEB", + "apsu": "BoEB", + "fee": 1000, + "fv": 3, + "lv": 1003, + "snd": "O3VYQKJ45XILV2GVDO44LM2IGPUD2QYRXNFX5K4ZDC2B4BD4ZZXU5AQG24", + "type": "appl" + } + } + }, + { + "application-index": 1010, + "pool-error": "", + "txn": { + "txn": { + "apan": 5, + "apap": "BoEB", + "apsu": "BoEB", + "fee": 1000, + "fv": 3, + "lv": 1003, + "snd": "O3VYQKJ45XILV2GVDO44LM2IGPUD2QYRXNFX5K4ZDC2B4BD4ZZXU5AQG24", + "type": "appl" + } + } + }, + { + "application-index": 1011, + "pool-error": "", + "txn": { + "txn": { + "apan": 5, + "apap": "BoEB", + "apsu": "BoEB", + "fee": 1000, + "fv": 3, + "lv": 1003, + "snd": "O3VYQKJ45XILV2GVDO44LM2IGPUD2QYRXNFX5K4ZDC2B4BD4ZZXU5AQG24", + "type": "appl" + } + } + }, + { + "application-index": 1012, + "pool-error": "", + "txn": { + "txn": { + "apan": 5, + "apap": "BoEB", + "apsu": "BoEB", + "fee": 1000, + "fv": 3, + "lv": 1003, + "snd": "O3VYQKJ45XILV2GVDO44LM2IGPUD2QYRXNFX5K4ZDC2B4BD4ZZXU5AQG24", + "type": "appl" + } + } + }, + { + "application-index": 1013, + "pool-error": "", + "txn": { + "txn": { + "apan": 5, + "apap": "BoEB", + "apsu": "BoEB", + "fee": 1000, + "fv": 3, + "lv": 1003, + "snd": "O3VYQKJ45XILV2GVDO44LM2IGPUD2QYRXNFX5K4ZDC2B4BD4ZZXU5AQG24", + "type": "appl" + } + } + }, + { + "application-index": 1014, + "pool-error": "", + "txn": { + "txn": { + "apan": 5, + "apap": "BoEB", + "apsu": "BoEB", + "fee": 1000, + "fv": 3, + "lv": 1003, + "snd": "O3VYQKJ45XILV2GVDO44LM2IGPUD2QYRXNFX5K4ZDC2B4BD4ZZXU5AQG24", + "type": "appl" + } + } + }, + { + "application-index": 1015, + "pool-error": "", + "txn": { + "txn": { + "apan": 5, + "apap": "BoEB", + "apsu": "BoEB", + "fee": 1000, + "fv": 3, + "lv": 1003, + "snd": "O3VYQKJ45XILV2GVDO44LM2IGPUD2QYRXNFX5K4ZDC2B4BD4ZZXU5AQG24", + "type": "appl" + } + } + }, + { + "application-index": 1016, + "pool-error": "", + "txn": { + "txn": { + "apan": 5, + "apap": "BoEB", + "apsu": "BoEB", + "fee": 1000, + "fv": 3, + "lv": 1003, + "snd": "O3VYQKJ45XILV2GVDO44LM2IGPUD2QYRXNFX5K4ZDC2B4BD4ZZXU5AQG24", + "type": "appl" + } + } + }, + { + "application-index": 1017, + "pool-error": "", + "txn": { + "txn": { + "apan": 5, + "apap": "BoEB", + "apsu": "BoEB", + "fee": 1000, + "fv": 3, + "lv": 1003, + "snd": "O3VYQKJ45XILV2GVDO44LM2IGPUD2QYRXNFX5K4ZDC2B4BD4ZZXU5AQG24", + "type": "appl" + } + } + }, + { + "application-index": 1018, + "pool-error": "", + "txn": { + "txn": { + "apan": 5, + "apap": "BoEB", + "apsu": "BoEB", + "fee": 1000, + "fv": 3, + "lv": 1003, + "snd": "O3VYQKJ45XILV2GVDO44LM2IGPUD2QYRXNFX5K4ZDC2B4BD4ZZXU5AQG24", + "type": "appl" + } + } + }, + { + "asset-index": 1019, + "pool-error": "", + "txn": { + "txn": { + "apar": { + "an": "AlgoKit POA #\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000", + "au": "ipfs://QmW5vERkgeJJtSY1YQdcWU6gsHCZCyLFtM1oT9uyy2WGm8", + "m": "2VOT4UXACU3E4EXR7YE2JEVTJSOSXNJEW5WTEII7VDATKA3W4SZV67DCXA", + "t": 1, + "un": "POA" + }, + "fv": 3, + "lv": 1003, + "snd": "O3VYQKJ45XILV2GVDO44LM2IGPUD2QYRXNFX5K4ZDC2B4BD4ZZXU5AQG24", + "type": "acfg" + } + } + } + ], + "pool-error": "", + "txn": { + "sig": "57rrIdDntYaBzS0+e+Bm/zprOwIf02Bp5/ZK7T8BbdndFau1EVRZncW0lDkBqZwToPDN0zIuVn9f041rj9PGAA==", + "txn": { + "apaa": ["uJYpYg=="], + "apbx": [ + { + "n": "1V0+UuAVNk4S8f4JpJKzTJ0rtSS3bTIhH6jBNQN25LM=" + } + ], + "apid": 1002, + "fee": 258000, + "fv": 3, + "gen": "dockernet-v1", + "gh": "gsXq02PGgDJOG98oa+AZDRQ6JBed9RthhXIaOSwKO/8=", + "lv": 1003, + "snd": "2VOT4UXACU3E4EXR7YE2JEVTJSOSXNJEW5WTEII7VDATKA3W4SZV67DCXA", + "type": "appl" + } + } + } + } + ] + } + ], + "version": 2 +} diff --git a/sampleWorkspace/puya/sources.json b/sampleWorkspace/puya/sources.json new file mode 100644 index 0000000..545d975 --- /dev/null +++ b/sampleWorkspace/puya/sources.json @@ -0,0 +1,12 @@ +{ + "txn-group-sources": [ + { + "sourcemap-location": "ProofOfAttendance.approval.puya.map", + "hash": "9OQMiPyi2J20NA7qdxurd6RlJi8dTAlzNxPzdR7pjPc=" + }, + { + "sourcemap-location": ".algokit/sources/opup/approval.teal.map", + "hash": "p0zHRDBy7V/S2TDcAaXYEd2OF8KSxV/1y0ceYEL6qxU=" + } + ] +} diff --git a/src/common/debugSession.ts b/src/common/debugSession.ts index 71e7093..a3b5bc0 100644 --- a/src/common/debugSession.ts +++ b/src/common/debugSession.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ import { DebugSession, InitializedEvent, @@ -14,11 +15,17 @@ import { } from '@vscode/debugadapter'; import { DebugProtocol } from '@vscode/debugprotocol'; import { AvmRuntime, IRuntimeBreakpoint } from './runtime'; -import { ProgramStackFrame } from './traceReplayEngine'; import { Subject } from 'await-notify'; import * as algosdk from 'algosdk'; import { FileAccessor } from './fileAccessor'; -import { AvmDebuggingAssets, utf8Decode, limitArray } from './utils'; +import { + AvmDebuggingAssets, + utf8Decode, + limitArray, + ProgramSourceEntryFile, + prefixPotentialError, + isPuyaFrontendSourceExtension, +} from './utils'; const GENERIC_ERROR_ID = 9999; @@ -43,7 +50,11 @@ export interface ILaunchRequestArguments /** An absolute path to the simulate response to debug. */ simulateTraceFile: string; /** An absolute path to the file which maps programs to source maps. */ - programSourcesDescriptionFile: string; + programSourcesDescriptionFile?: string; + /** JSON encoded content of the program sources description file. */ + programSourcesDescription?: ProgramSourceEntryFile; + /** The folder containing the program sources description file (when using programSourcesDescription). */ + programSourcesDescriptionFolder?: string; /** Automatically stop target after launch. If not specified, target does not stop. */ stopOnEntry?: boolean; } @@ -64,6 +75,7 @@ export class AvmDebugSession extends DebugSession { | AppStateScope | AppSpecificStateScope | AvmValueReference + | PuyaScope >(); private _sourceHandles = new Handles<{ @@ -190,10 +202,33 @@ export class AvmDebugSession extends DebugSession { args: ILaunchRequestArguments, ) { try { + let programSourcesDescription: ProgramSourceEntryFile; + let folder: string; + if (args.programSourcesDescription !== undefined) { + programSourcesDescription = args.programSourcesDescription; + folder = args.programSourcesDescriptionFolder || ''; + } else if (args.programSourcesDescriptionFile !== undefined) { + // earlier versions of avm-debugger passed program source information via a file/ + folder = args.programSourcesDescriptionFile; + const sourcesDescriptionBytes = await prefixPotentialError( + this.fileAccessor.readFile(args.programSourcesDescriptionFile), + 'Could not read program sources description file', + ); + const sourcesDescriptionText = new TextDecoder().decode( + sourcesDescriptionBytes, + ); + programSourcesDescription = JSON.parse( + sourcesDescriptionText, + ) as ProgramSourceEntryFile; + } else { + throw Error('missing programSources'); + } + const debugAssets = await AvmDebuggingAssets.loadFromFiles( this.fileAccessor, args.simulateTraceFile, - args.programSourcesDescriptionFile, + programSourcesDescription, + folder, ); await this._runtime.onLaunch(debugAssets); @@ -327,57 +362,51 @@ export class AvmDebugSession extends DebugSession { args: DebugProtocol.StackTraceArguments, ): void { try { - const startFrame = - typeof args.startFrame === 'number' ? args.startFrame : 0; - const maxLevels = typeof args.levels === 'number' ? args.levels : 1000; - - // The runtime has a stack where the latest call is the last element. We need to return the - // reverse of that. - const adjustedEndFrame = this._runtime.stackLength() - startFrame; - const adjustedStartFrame = Math.max(0, adjustedEndFrame - maxLevels); + const startFrame = args.startFrame || 0; + const maxLevels = args.levels || 1000; - const stk = this._runtime.stack(adjustedStartFrame, adjustedEndFrame); + const stk = this._runtime.stack(startFrame, startFrame + maxLevels); const stackFramesForResponse = stk.frames.map((frame, index) => { - const id = adjustedStartFrame + index; - - const sourceFile = frame.sourceFile(); - let source: Source | undefined = undefined; - if (typeof sourceFile.path !== 'undefined') { - source = this.createSource(sourceFile.path); - } else if (typeof sourceFile.content !== 'undefined') { - source = this.createSourceWithContent( - sourceFile.name, - sourceFile.content, - sourceFile.contentMimeType, + const id = startFrame + index; + + const protocolFrame = new StackFrame(id, frame.name); + const sourceFile = frame.source; + if (sourceFile !== undefined) { + let source: Source | undefined = undefined; + + if (typeof sourceFile.path !== 'undefined') { + source = this.createSource(sourceFile.path); + } else if (typeof sourceFile.content !== 'undefined') { + source = this.createSourceWithContent( + sourceFile.name, + sourceFile.content, + sourceFile.contentMimeType, + ); + } + protocolFrame.source = source; + protocolFrame.line = this.convertDebuggerLineToClient( + sourceFile.line, ); - } - const sourceLocation = frame.sourceLocation(); - const line = this.convertDebuggerLineToClient(sourceLocation.line); - const column = - typeof sourceLocation.column !== 'undefined' - ? this.convertDebuggerColumnToClient(sourceLocation.column) - : undefined; - - const protocolFrame = new StackFrame( - id, - frame.name(), - source, - line, - column, - ); - protocolFrame.endLine = - typeof sourceLocation.endLine !== 'undefined' - ? this.convertDebuggerLineToClient(sourceLocation.endLine) - : undefined; - protocolFrame.endColumn = - typeof sourceLocation.endColumn !== 'undefined' - ? this.convertDebuggerColumnToClient(sourceLocation.endColumn) - : undefined; + if (typeof sourceFile.column !== 'undefined') { + protocolFrame.column = this.convertDebuggerColumnToClient( + sourceFile.column, + ); + } + if (typeof sourceFile.endLine !== 'undefined') { + protocolFrame.endLine = this.convertDebuggerLineToClient( + sourceFile.endLine, + ); + } + if (typeof sourceFile.endColumn !== 'undefined') { + protocolFrame.endColumn = this.convertDebuggerColumnToClient( + sourceFile.endColumn, + ); + } + } return protocolFrame; }); - stackFramesForResponse.reverse(); response.body = { totalFrames: stk.count, @@ -402,11 +431,25 @@ export class AvmDebugSession extends DebugSession { const frame = this._runtime.getStackFrame(args.frameId); const scopes: DebugProtocol.Scope[] = []; - if (typeof frame !== 'undefined') { - if (frame instanceof ProgramStackFrame) { - const programScope = new ProgramStateScope(args.frameId); + if (frame !== undefined) { + if ( + frame.programState?.variables !== undefined && + isPuyaFrontendSourceExtension(frame.source?.path) + ) { + scopes.push( + new Scope( + 'Locals', + this._variableHandles.create(new PuyaScope(args.frameId)), + false, + ), + ); + } + + const programScope = new ProgramStateScope(args.frameId); + const state = frame.programState; + if (state !== undefined) { let scopeName = 'Program State'; - const appID = frame.currentAppID(); + const appID = state.appId; if (typeof appID !== 'undefined') { scopeName += `: App ${appID}`; } @@ -445,13 +488,8 @@ export class AvmDebugSession extends DebugSession { const v = this._variableHandles.get(args.variablesReference); if (v instanceof ProgramStateScope) { - const frame = this._runtime.getStackFrame(v.frameIndex); - if (!frame || !(frame instanceof ProgramStackFrame)) { - throw new Error(`Unexpected frame: ${typeof frame}`); - } - const programState = frame.state; - - if (typeof v.specificState === 'undefined') { + const programState = this.getProgramState(v.frameIndex); + if (v.specificState === 'program') { variables = [ { name: 'pc', @@ -460,6 +498,17 @@ export class AvmDebugSession extends DebugSession { variablesReference: 0, evaluateName: 'pc', }, + ...(programState.op !== undefined + ? [ + { + name: 'op', + value: programState.op, + type: 'string', + variablesReference: 0, + evaluateName: 'op', + }, + ] + : []), { name: 'stack', value: programState.stack.length === 0 ? '[]' : '[...]', @@ -505,6 +554,13 @@ export class AvmDebugSession extends DebugSession { ); } } + } else if (v instanceof PuyaScope) { + const state = this.getProgramState(v.frameIndex); + variables = state.variables.map((variable) => { + const name = variable[0]; + const avmValue = variable[1]; + return this.convertAvmValue(v, avmValue, name); + }); } else if (v === 'chain') { const appIDs = this._runtime.getAppStateReferences(); variables = [ @@ -591,22 +647,31 @@ export class AvmDebugSession extends DebugSession { .map((kv) => this.convertAvmKeyValue(v, kv)); } } else if (v instanceof AvmValueReference) { - if (v.scope instanceof ProgramStateScope) { - const frame = this._runtime.getStackFrame(v.scope.frameIndex); - if (!frame || !(frame instanceof ProgramStackFrame)) { - throw new Error(`Unexpected frame: ${typeof frame}`); + if ( + v.scope instanceof ProgramStateScope || + v.scope instanceof PuyaScope + ) { + const state = this.getProgramState(v.scope.frameIndex); + let toExpand: algosdk.modelsv2.AvmValue | undefined; + + if (v.scope instanceof ProgramStateScope) { + if (v.scope.specificState === 'stack') { + toExpand = state.stack[v.key as number]; + } else if (v.scope.specificState === 'scratch') { + toExpand = + state.scratch.get(v.key as number) || + new algosdk.modelsv2.AvmValue({ type: 2 }); + } + } else if (v.scope instanceof PuyaScope) { + const variable = state.variables.find(([name]) => name === v.key); + if (variable) { + toExpand = variable[1]; + } } - let toExpand: algosdk.modelsv2.AvmValue; - if (v.scope.specificState === 'stack') { - toExpand = frame.state.stack[v.key as number]; - } else if (v.scope.specificState === 'scratch') { - toExpand = - frame.state.scratch.get(v.key as number) || - new algosdk.modelsv2.AvmValue({ type: 2 }); - } else { - throw new Error(`Unexpected AvmValueReference scope: ${v.scope}`); + + if (toExpand) { + variables = this.expandAvmValue(toExpand, args.filter); } - variables = this.expandAvmValue(toExpand, args.filter); } else if ( v.scope instanceof AppSpecificStateScope && typeof v.key === 'string' && @@ -676,16 +741,52 @@ export class AvmDebugSession extends DebugSession { } } + private getProgramState(frameIndex: number) { + const frame = this._runtime.getStackFrame(frameIndex); + const state = frame?.programState; + if (state === undefined) { + throw new Error(`Unexpected frame: ${typeof frame}`); + } + return state; + } + protected async evaluateRequest( response: DebugProtocol.EvaluateResponse, args: DebugProtocol.EvaluateArguments, ): Promise { try { - let reply: string | undefined; - let rv: DebugProtocol.Variable | undefined = undefined; - // Note, can use args.context to perform different actions based on where the expression is evaluated + // check if expression matches a Puya local variable + const frame = + args.frameId !== undefined + ? this._runtime.getStackFrame(args.frameId) + : undefined; + const frameVariables = frame?.programState?.variables; + if (frameVariables) { + const variable = frameVariables.find( + ([name]) => name == args.expression, + ); + if (variable) { + const avmValue = variable[1]; + const debugVariable = this.convertAvmValue( + new PuyaScope(args.frameId!), + avmValue, + args.expression, + ); + response.body = { + result: debugVariable.value, + type: debugVariable.type, + variablesReference: debugVariable.variablesReference, + presentationHint: debugVariable.presentationHint, + }; + this.sendResponse(response); + return; + } + } + + let reply: string | undefined; + let rv: DebugProtocol.Variable | undefined = undefined; let result: [AvmValueScope, number | string] | undefined = undefined; try { result = evaluateNameToScope(args.expression); @@ -704,20 +805,29 @@ export class AvmDebugSession extends DebugSession { scope.specificState, ); const frame = this._runtime.getStackFrame(args.frameId); - if (!frame || !(frame instanceof ProgramStackFrame)) { + const state = frame?.programState; + if (state === undefined) { reply = `Unexpected frame: ${typeof frame}`; } else { if (scope.specificState === 'pc') { rv = { name: 'pc', - value: frame.state.pc.toString(), + value: state.pc.toString(), type: 'uint64', variablesReference: 0, evaluateName: 'pc', }; + } else if (scope.specificState === 'op') { + rv = { + name: 'op', + value: state.op || 'unknown', + type: 'string', + variablesReference: 0, + evaluateName: 'op', + }; } else if (scope.specificState === 'stack') { let index = key as number; - const stackValues = frame.state.stack; + const stackValues = state.stack; if (index < 0) { const adjustedIndex = index + stackValues.length; if (adjustedIndex < 0) { @@ -746,7 +856,7 @@ export class AvmDebugSession extends DebugSession { if (0 <= index && index < 256) { rv = this.convertAvmValue( scopeWithFrame, - frame.state.scratch.get(index) || + state.scratch.get(index) || new algosdk.modelsv2.AvmValue({ type: 2 }), index, ); @@ -953,7 +1063,7 @@ export class AvmDebugSession extends DebugSession { //---- helpers private convertAvmValue( - scope: AvmValueScope, + scope: AvmValueScope | PuyaScope, avmValue: algosdk.modelsv2.AvmValue, key: number | string, overrideVariableReference?: boolean, @@ -962,7 +1072,8 @@ export class AvmDebugSession extends DebugSession { let indexedVariables: number | undefined = undefined; let presentationHint: DebugProtocol.VariablePresentationHint | undefined = undefined; - let makeVariableReference = false; + let variablesReference = 0; + if (avmValue.type === 1) { // byte array const bytes = avmValue.bytes || new Uint8Array(); @@ -975,22 +1086,34 @@ export class AvmDebugSession extends DebugSession { kind: 'data', attributes: ['rawString'], }; - makeVariableReference = true; + variablesReference = this._variableHandles.create( + new AvmValueReference(scope, key), + ); } - if (typeof overrideVariableReference !== 'undefined') { - makeVariableReference = overrideVariableReference; + // For uint64 (type 2), variablesReference remains 0 + + if ( + avmValue.type !== 2 && + typeof overrideVariableReference !== 'undefined' && + overrideVariableReference + ) { + variablesReference = this._variableHandles.create( + new AvmValueReference(scope, key), + ); } + return { name: key.toString(), value: this.avmValueToString(avmValue), type: avmValue.type === 1 ? 'byte[]' : 'uint64', - variablesReference: makeVariableReference - ? this._variableHandles.create(new AvmValueReference(scope, key)) - : 0, + variablesReference, namedVariables, indexedVariables, presentationHint, - evaluateName: evaluateNameForScope(scope, key), + evaluateName: + scope instanceof PuyaScope + ? key.toString() + : evaluateNameForScope(scope, key), }; } @@ -998,12 +1121,12 @@ export class AvmDebugSession extends DebugSession { avmValue: algosdk.modelsv2.AvmValue, filter?: DebugProtocol.VariablesArguments['filter'], ): DebugProtocol.Variable[] { + // uint64 has no expanded variables if (avmValue.type !== 1) { return []; } const bytes = avmValue.bytes || new Uint8Array(); - const values: DebugProtocol.Variable[] = []; if (filter !== 'indexed') { @@ -1184,19 +1307,19 @@ export class AvmDebugSession extends DebugSession { return new Source(fileName, undefined, id); } } - +class PuyaScope { + constructor(public readonly frameIndex: number) {} +} class ProgramStateScope { constructor( public readonly frameIndex: number, - public readonly specificState?: 'pc' | 'stack' | 'scratch', + public readonly specificState: + | 'pc' + | 'op' + | 'stack' + | 'scratch' + | 'program' = 'program', ) {} - - public scopeString(): string { - if (typeof this.specificState === 'undefined') { - return `program`; - } - return this.specificState; - } } type OnChainStateScope = 'chain' | 'app'; @@ -1233,7 +1356,7 @@ type AvmValueScope = ProgramStateScope | AppSpecificStateScope; class AvmValueReference { constructor( - public readonly scope: AvmValueScope, + public readonly scope: AvmValueScope | PuyaScope, public readonly key: number | string, ) {} } @@ -1243,7 +1366,7 @@ function evaluateNameForScope( key: number | string, ): string { if (scope instanceof ProgramStateScope) { - return `${scope.scopeString()}[${key}]`; + return `${scope.specificState}[${key}]`; } if (scope.scope === 'local') { if (typeof scope.account === 'undefined') { @@ -1260,6 +1383,9 @@ function evaluateNameToScope(name: string): [AvmValueScope, number | string] { if (name === 'pc') { return [new ProgramStateScope(-1, 'pc'), 0]; } + if (name === 'op') { + return [new ProgramStateScope(-1, 'op'), '']; + } const stackMatches = /^stack\[(-?\d+)\]$/.exec(name); if (stackMatches) { return [new ProgramStateScope(-1, 'stack'), parseInt(stackMatches[1], 10)]; diff --git a/src/common/index.ts b/src/common/index.ts index 06200ed..be9b335 100644 --- a/src/common/index.ts +++ b/src/common/index.ts @@ -1,2 +1,3 @@ export { AvmDebugSession, ILaunchRequestArguments } from './debugSession'; export { FileAccessor } from './fileAccessor'; +export { ProgramSourceEntryFile, ProgramSourceEntry } from './utils'; diff --git a/src/common/programReplay.ts b/src/common/programReplay.ts new file mode 100644 index 0000000..ded0780 --- /dev/null +++ b/src/common/programReplay.ts @@ -0,0 +1,389 @@ +import { + AvmValue, + SimulationOpcodeTraceUnit, +} from 'algosdk/dist/types/client/v2/algod/models/types'; +import { FrameSource, CallStackFrame } from './traceReplayEngine'; +import { + ByteArrayMap, + isPuyaSourceMap, + PCEvent, + ProgramSourceDescriptor, +} from './utils'; +import algosdk from 'algosdk'; +import { AppState } from './appState'; + +const HIDE_VERSION = true; +const DEFINED_ONLY = true; +const INCLUDE_STACK_SCOPE = false; +const HIDE_TEMP = true; + +class MutableCallStack implements CallStackFrame { + public readonly name: string; + private definedVariables: Record; + private _paramVariables: string[]; + private stack: string[]; + + constructor( + public readonly callEvent: PCEvent, + public source: FrameSource | undefined, + public stackOffset: number, + private readonly parent: ProgramReplay, + ) { + if (callEvent.callsub === undefined) { + throw Error('Invalid enter to begin a frame'); + } + this.name = callEvent.callsub; + this.definedVariables = {}; + this._paramVariables = []; + this.stack = []; + } + + public get paramVariables() { + return this._paramVariables; + } + + public set paramVariables(value: string[]) { + this._paramVariables = value; + // params are always defined + this.setDefinedVariables(value); + } + + public get programState() { + const knownStack = this.parent.stack.slice( + this.stackOffset, + this.stackOffset + this.stackVariables.length, + ); + const variables: Record = {}; + knownStack.forEach((value, index) => { + const variableScope = this.stackVariables[index]; + let variable = variableScope[0]; + const scope = variableScope[1]; + if (HIDE_TEMP && variable.indexOf('%') >= 0) { + return; + } + if (!DEFINED_ONLY || this.definedVariables[variable]) { + if (HIDE_VERSION) { + variable = variable.split('#', 2)[0]; + } + if (INCLUDE_STACK_SCOPE) { + variable = variable + ` (${scope})`; + } + variables[variable] = value; + } + }); + + return { + stack: this.parent.stack, + scratch: this.parent.scratch, + pc: this.parent.nextPc, + op: this.parent.nextPcEvent?.op, + appId: this.parent.appId, + variables: Object.entries(variables).sort((a, b) => + a[0].localeCompare(b[0]), + ), + }; + } + get stackVariables(): string[][] { + return [ + ...this.paramVariables.map((v) => [v, 'p']), + ...this.stack.map((v) => [v, 's']), + ]; + } + + private setDefinedVariables(variables: string[]) { + for (const variable of variables) { + this.definedVariables[variable] = true; + } + } + + public applyIn(event: PCEvent) { + if (event.params !== undefined) { + const params = Object.keys(event.params); + this.stackOffset = this.parent.stack.length - params.length; + this.paramVariables = params; + } + if (event.stack_in !== undefined) { + this.stack = event.stack_in; + } + } + + public applyOut(event: PCEvent) { + if (event.stack_out !== undefined) { + this.stack = event.stack_out; + } + if (event.defined_out !== undefined) { + this.setDefinedVariables(event.defined_out); + } + } +} + +export class ProgramReplay { + public stack: AvmValue[] = []; + public scratch: Map = new Map(); + private traceIndex: number = 0; + private _callStack: MutableCallStack[] = []; + private readonly sourceInfo: ProgramSourceDescriptor | undefined; + private currentAppState: Map; + + constructor( + private readonly programName: string, + private readonly programTrace: SimulationOpcodeTraceUnit[], + sourceInfo: ProgramSourceDescriptor | undefined, + public readonly appId: bigint | undefined, + currentAppState: Map, + ) { + if (isPuyaSourceMap(sourceInfo?.json)) { + this.sourceInfo = checkTraceMatchesSourceInfo(programTrace, sourceInfo); + } else { + // If value is undefined, we still set it given that this can signify that user + // wants to skip debugging for this particular program + this.sourceInfo = sourceInfo; + } + + this.currentAppState = currentAppState; + this.reset(); + } + + private pushCallStack(event: PCEvent) { + this._callStack.push( + new MutableCallStack(event, this.nextPcSource, this.stack.length, this), + ); + } + + get nextOpTrace() { + if (this.traceIndex >= this.programTrace.length) { + return this.programTrace[this.programTrace.length - 1]; + } + + return this.programTrace[this.traceIndex]; + } + + get nextPc() { + return this.nextOpTrace?.pc; + } + + get nextPcEvent(): PCEvent | undefined { + if ( + this.nextPc === undefined && + (this.sourceInfo === undefined || + this.sourceInfo.json.pc_events === undefined) + ) { + return undefined; + } + return this.sourceInfo?.json.pc_events?.[this.nextPc.toString()]; + } + + get nextPcSource(): FrameSource | undefined { + if (this.sourceInfo === undefined) { + return undefined; + } + const location = this.sourceInfo.sourcemap.getLocationForPc(this.nextPc); + if (location == undefined) { + return undefined; + } + const line = location.line; + const column = location.column; + const sourceIndex = location.sourceIndex; + const source = this.sourceInfo.getFullSourcePath(sourceIndex); + + return { + name: source, + path: source, + line: line, + column: column, + }; + } + + get callStack(): CallStackFrame[] { + return this._callStack; + } + + get topCallStack(): MutableCallStack { + return this._callStack[this._callStack.length - 1]; + } + + get ended() { + return this.nextOpTrace === undefined; + } + + public forward(): void { + if (this.traceIndex === this.programTrace.length) { + return; + } + this.processOpExit(); + this.processUnit(this.nextOpTrace); + this.traceIndex++; + if (this.traceIndex < this.programTrace.length) { + this.processOpEnter(); + this.updateSource(); + } + } + + public reset() { + this.stack = []; + this.scratch = new Map(); + this.traceIndex = 0; + this._callStack = []; + this.pushCallStack({ callsub: this.programName }); + this.processOpEnter(); + } + + private updateSource() { + this._callStack[this._callStack.length - 1].source = this.nextPcSource; + } + + private processUnit(unit: algosdk.modelsv2.SimulationOpcodeTraceUnit) { + if (unit.stateChanges && unit.stateChanges.length !== 0) { + const appID = this.appId; + if (typeof appID === 'undefined') { + throw new Error('No appID'); + } + + const state = this.currentAppState.get(appID); + if (!state) { + throw new Error(`No state for appID ${appID}`); + } + + for (const stateChange of unit.stateChanges) { + switch (stateChange.appStateType) { + case 'g': + if (stateChange.operation === 'w') { + state.globalState.set(stateChange.key, stateChange.newValue!); + } else if (stateChange.operation === 'd') { + state.globalState.delete(stateChange.key); + } + break; + case 'l': + if (stateChange.operation === 'w') { + const accountAddress = stateChange.account!.toString(); + let accountState = state.localState.get(accountAddress); + if (!accountState) { + accountState = new ByteArrayMap(); + state.localState.set(accountAddress, accountState); + } + accountState.set(stateChange.key, stateChange.newValue!); + } else if (stateChange.operation === 'd') { + const accountState = state.localState.get( + stateChange.account!.toString(), + ); + if (accountState) { + accountState.delete(stateChange.key); + } + } + break; + case 'b': + if (stateChange.operation === 'w') { + state.boxState.set(stateChange.key, stateChange.newValue!); + } else if (stateChange.operation === 'd') { + state.boxState.delete(stateChange.key); + } + } + } + } + } + + private processOpEnter() { + const event = this.nextPcEvent; + if (event !== undefined) { + this.topCallStack.applyIn(event); + } + } + + private processOpExit() { + const next = this.nextOpTrace; + + const stackPopCount = next.stackPopCount ? Number(next.stackPopCount) : 0; + if (stackPopCount > this.stack.length) { + throw new Error( + `Stack underflow at pc ${this.nextPc}: ${stackPopCount} > ${this.stack.length}`, + ); + } + this.stack = this.stack.slice(0, this.stack.length - stackPopCount); + if (next.stackAdditions) { + this.stack.push(...next.stackAdditions); + } + + for (const scratchWrite of next.scratchChanges || []) { + const slot = Number(scratchWrite.slot); + if (slot < 0 || slot >= 256) { + throw new Error(`Invalid scratch slot ${slot}`); + } + const newValue = scratchWrite.newValue; + if (newValue.type === 2 && !newValue.uint) { + // When setting to 0, delete the entry, since 0 is the default. + this.scratch.delete(slot); + } else { + this.scratch.set(slot, newValue); + } + } + + const event = this.nextPcEvent; + if (event !== undefined) { + if (event.params !== undefined) { + const params = Object.keys(event.params); + this.topCallStack.stackOffset = this.stack.length - params.length; + this.topCallStack.paramVariables = params; + } + if (event.callsub !== undefined) { + this.pushCallStack(event); + } else if (event.retsub !== undefined) { + const last = this._callStack.pop(); + if (last === undefined) { + throw Error('empty call stack'); + } + this.topCallStack.applyOut(last.callEvent); + } else { + this.topCallStack.applyOut(event); + } + } + } +} + +function checkTraceMatchesSourceInfo( + traces: SimulationOpcodeTraceUnit[], + sourceInfo: ProgramSourceDescriptor | undefined, +): ProgramSourceDescriptor { + if (sourceInfo === undefined) { + throw Error('missing program source information'); + } + const offset = sourceInfo.json.op_pc_offset || 0; + const pcOffset = offset === 0 ? offset : traces[offset].pc; + let events = sourceInfo.json.pc_events; + if (events === undefined) { + throw Error('not a puya source map'); + } + + events = Object.fromEntries( + Object.entries(events).map((entry) => [ + (+entry[0] + pcOffset).toString(), + entry[1], + ]), + ); + for (const trace of traces) { + if (events[trace.pc] === undefined && trace.pc >= pcOffset) { + throw Error('source map is not valid for program trace'); + } + } + + const json = { + ...sourceInfo.json, + pc_events: events, + }; + const sourcemap = { + ...sourceInfo.sourcemap, + getLocationForPc: (pc) => { + if (pc < pcOffset) { + return undefined; + } + return sourceInfo?.sourcemap.getLocationForPc(pc - pcOffset); + }, + } as unknown as algosdk.ProgramSourceMap; + + return new ProgramSourceDescriptor( + sourceInfo.fileAccessor, + sourceInfo.sourcemapFileLocation, + json, + sourcemap, + sourceInfo.hash, + ); +} diff --git a/src/common/runtime.ts b/src/common/runtime.ts index 42a227d..b7ea210 100644 --- a/src/common/runtime.ts +++ b/src/common/runtime.ts @@ -1,11 +1,12 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ import { EventEmitter } from 'events'; import { RuntimeEvents } from './debugSession'; import { AppState } from './appState'; import { - FrameSourceLocation, + FrameSource, + CallStackFrame, SteppingResultType, TraceReplayEngine, - TraceReplayStackFrame, } from './traceReplayEngine'; import { FileAccessor } from './fileAccessor'; import { @@ -27,7 +28,7 @@ export interface IRuntimeBreakpointLocation { interface IRuntimeStack { count: number; - frames: TraceReplayStackFrame[]; + frames: CallStackFrame[]; } export class AvmRuntime extends EventEmitter { @@ -160,12 +161,12 @@ export class AvmRuntime extends EventEmitter { * "Step out" */ public stepOut() { - const targetStackDepth = this.engine.stack.length - 1; + const targetStackDepth = this.stackLength - 1; if (targetStackDepth <= 0) { this.continue(false); } this.nextTickWithErrorReporting(() => { - while (targetStackDepth < this.engine.stack.length) { + while (targetStackDepth < this.stackLength) { if (!this.updateCurrentLine(false)) { return; } @@ -181,7 +182,7 @@ export class AvmRuntime extends EventEmitter { * "Step over" */ public step(reverse: boolean) { - const targetStackDepth = this.engine.stack.length; + const targetStackDepth = this.stackLength; this.nextTickWithErrorReporting(() => { do { if (!this.updateCurrentLine(reverse)) { @@ -190,37 +191,43 @@ export class AvmRuntime extends EventEmitter { if (this.hitBreakpoint()) { return; } - } while (targetStackDepth < this.engine.stack.length); + } while (targetStackDepth < this.stackLength); this.sendEvent(RuntimeEvents.stopOnStep); }); } - public stackLength(): number { - return this.engine.stack.length; + public get stackLength(): number { + return this.getStack().length; } /** * Returns a 'stacktrace' where every frame is a TraceReplayStackFrame. */ public stack(startFrame: number, endFrame: number): IRuntimeStack { - if (this.engine.stack.length < endFrame) { - endFrame = this.engine.stack.length; - } - const frames: TraceReplayStackFrame[] = []; - for (let i = startFrame; i < endFrame; i++) { - frames.push(this.engine.stack[i]); + const stack = this.getStack(); + if (stack.length < endFrame) { + endFrame = stack.length; } return { - frames: frames, - count: this.engine.stack.length, + frames: stack.slice(startFrame, endFrame), + count: stack.length, }; } - public getStackFrame(index: number): TraceReplayStackFrame | undefined { - if (index < 0 || index >= this.engine.stack.length) { - return undefined; + private getStack(): CallStackFrame[] { + const result: CallStackFrame[] = []; + for (const frame of this.engine.stack) { + for (const f of frame.callStack) { + result.push(f); + } } - return this.engine.stack[index]; + result.reverse(); + return result; + } + + public getStackFrame(index: number): CallStackFrame | undefined { + const stack = this.getStack(); + return stack[index]; } /* @@ -295,13 +302,13 @@ export class AvmRuntime extends EventEmitter { } private hitBreakpoint(): boolean { - const frame = this.engine.currentFrame(); - const sourceInfo = frame.sourceFile(); - const sourceLocation = frame.sourceLocation(); - if (sourceInfo.path) { - const breakpoints = this.breakPoints.get(sourceInfo.path) || []; + const stack = this.getStack(); + const frame = stack[0]; + const source = frame.source; + if (source && source.path) { + const breakpoints = this.breakPoints.get(source.path) || []; const bps = breakpoints.filter((bp) => - this.isFrameLocationOnBreakpoint(sourceLocation, bp.location), + this.isFrameLocationOnBreakpoint(source, bp.location), ); if (bps.length !== 0) { // send 'stopped' event @@ -340,7 +347,7 @@ export class AvmRuntime extends EventEmitter { location.line, ); if (typeof location.column === 'undefined' && pcs.length !== 0) { - const sortedPcs = pcs.slice().sort((a, b) => a.column - b.column); + const sortedPcs = pcs.slice().sort((a, b) => a.pc - b.pc); location.column = sortedPcs[0].column; } @@ -357,6 +364,7 @@ export class AvmRuntime extends EventEmitter { } } + // eslint-disable-next-line @typescript-eslint/no-explicit-any private sendEvent(event: string, ...args: any[]): void { setTimeout(() => { this.emit(event, ...args); @@ -364,7 +372,7 @@ export class AvmRuntime extends EventEmitter { } private isFrameLocationOnBreakpoint( - location: FrameSourceLocation, + location: FrameSource, bp: IRuntimeBreakpointLocation, ): boolean { if (location.line !== bp.line) { diff --git a/src/common/traceReplayEngine.ts b/src/common/traceReplayEngine.ts index ed53886..a17d734 100644 --- a/src/common/traceReplayEngine.ts +++ b/src/common/traceReplayEngine.ts @@ -5,7 +5,10 @@ import { AvmDebuggingAssets, ProgramSourceDescriptor, ProgramSourceDescriptorRegistry, + isPuyaSourceMap, } from './utils'; +import { ProgramReplay } from './programReplay'; +import { AvmValue } from 'algosdk/dist/types/client/v2/algod/models/types'; export enum SteppingResultType { /* eslint-disable @typescript-eslint/naming-convention */ @@ -48,7 +51,7 @@ export class TraceReplayEngine { public initialAppState = new Map(); public currentAppState = new Map(); - public stack: TraceReplayStackFrame[] = []; + public stack: TraceReplayFrame[] = []; public reset() { this.simulateResponse = undefined; @@ -182,7 +185,7 @@ export class TraceReplayEngine { this.programHashToSource.set(programHash, sourceDescriptor); } - public currentFrame(): TraceReplayStackFrame { + public currentFrame(): TraceReplayFrame { return this.stack[this.stack.length - 1]; } @@ -260,44 +263,63 @@ export interface FrameSource { path?: string; content?: string; contentMimeType?: string; -} - -export interface FrameSourceLocation { line: number; endLine?: number; column?: number; endColumn?: number; } -export abstract class TraceReplayStackFrame { - constructor(protected readonly engine: TraceReplayEngine) {} +export interface ProgramState { + readonly stack: AvmValue[]; + readonly scratch: Map; + readonly pc: number; + readonly op: string | undefined; + readonly appId: bigint | undefined; + readonly variables: [string, AvmValue][]; +} - public abstract name(): string; - public abstract sourceFile(): FrameSource; - public abstract sourceLocation(): FrameSourceLocation; +export interface CallStackFrame { + /* Represents a function call within a program's call stack */ + readonly name: string; + readonly source: FrameSource | undefined; + readonly programState: ProgramState | undefined; +} - public abstract forward(stack: TraceReplayStackFrame[]): ExceptionInfo | void; - public abstract backward( - stack: TraceReplayStackFrame[], - ): ExceptionInfo | void; +export interface TraceReplayFrame { + /* + TraceReplayFrame represents a frame within the trace + a program frame may have multiple call frames within in it + one for each function in the call stack + */ + + get callStack(): CallStackFrame[]; + forward(stack: TraceReplayFrame[]): ExceptionInfo | void; + backward(stack: TraceReplayFrame[]): ExceptionInfo | void; } -export class TopLevelTransactionGroupsFrame extends TraceReplayStackFrame { +export class TopLevelTransactionGroupsFrame + implements TraceReplayFrame, CallStackFrame +{ private index: number = 0; private txnGroupDone: boolean = false; - constructor( - engine: TraceReplayEngine, + private readonly engine: TraceReplayEngine, private readonly response: algosdk.modelsv2.SimulateResponse, - ) { - super(engine); + ) {} + + public get callStack(): CallStackFrame[] { + return [this]; } - public name(): string { + public get programState() { + return undefined; + } + + public get name(): string { return `group ${this.index}`; } - public sourceFile(): FrameSource { + public get source(): FrameSource { const individualGroups = this.response.txnGroups.map((group) => group.txnResults.map((txnResult) => algosdk.parseJSON(algosdk.encodeJSON(txnResult.txnResult.txn), { @@ -305,14 +327,6 @@ export class TopLevelTransactionGroupsFrame extends TraceReplayStackFrame { }), ), ); - return { - name: `transaction-groups.json`, - content: algosdk.stringifyJSON(individualGroups, undefined, 2), - contentMimeType: 'application/json', - }; - } - - public sourceLocation(): FrameSourceLocation { let lineOffset = 1; // For opening bracket for (let i = 0; i < this.index; i++) { for (const txnResult of this.response.txnGroups[i].txnResults) { @@ -331,12 +345,15 @@ export class TopLevelTransactionGroupsFrame extends TraceReplayStackFrame { .split('\n').length; } return { + name: `transaction-groups.json`, + content: algosdk.stringifyJSON(individualGroups, undefined, 2), + contentMimeType: 'application/json', line: lineOffset, endLine: lineOffset + lineCount, }; } - public forward(stack: TraceReplayStackFrame[]): ExceptionInfo | void { + public forward(stack: TraceReplayFrame[]): ExceptionInfo | void { if (!this.txnGroupDone) { stack.push(this.frameForIndex(this.index)); this.txnGroupDone = true; @@ -377,7 +394,7 @@ export class TopLevelTransactionGroupsFrame extends TraceReplayStackFrame { return txnGroupFrame; } - public backward(stack: TraceReplayStackFrame[]): ExceptionInfo | void { + public backward(stack: TraceReplayFrame[]): ExceptionInfo | void { if (this.txnGroupDone) { this.txnGroupDone = false; return; @@ -417,7 +434,7 @@ interface TransactionFailureInfo { path: number[]; } -export class TransactionGroupStackFrame extends TraceReplayStackFrame { +export class TransactionGroupStackFrame implements TraceReplayFrame { private txnIndex: number = 0; private logicSigStatus: ProgramStatus = ProgramStatus.DONE; private appStatus: ProgramStatus = ProgramStatus.DONE; @@ -427,7 +444,7 @@ export class TransactionGroupStackFrame extends TraceReplayStackFrame { private sourceLocations: TransactionSourceLocation[] = []; constructor( - engine: TraceReplayEngine, + private engine: TraceReplayEngine, private txnPath: number[], private readonly txnInfos: algosdk.modelsv2.PendingTransactionResponse[], private readonly txnTraces: Array< @@ -435,8 +452,6 @@ export class TransactionGroupStackFrame extends TraceReplayStackFrame { >, private readonly failureInfo: TransactionFailureInfo | undefined, ) { - super(engine); - const firstTrace = txnTraces[0]; if (firstTrace) { if (firstTrace.logicSigTrace) { @@ -514,47 +529,47 @@ export class TransactionGroupStackFrame extends TraceReplayStackFrame { } } - public name(): string { + public get callStack(): CallStackFrame[] { + return [this]; + } + + public get programState() { + return undefined; + } + + public get name(): string { return `${this.txnPath.length > 2 ? 'inner ' : ''}transaction ${ this.txnIndex }`; } - public sourceFile(): FrameSource { - return { - name: `${ - this.txnPath.length > 2 ? 'inner-' : '' - }transaction-group-${this.txnPath.slice(0, -1).join('-')}.json`, - content: this.sourceContent, - contentMimeType: 'application/json', - }; - } - - public sourceLocation(): FrameSourceLocation { + public get source(): FrameSource { const sourceLocation = this.sourceLocations[this.txnIndex]; - let frameSourceLocation: FrameSourceLocation = { - line: sourceLocation.line, - endLine: sourceLocation.lineEnd, - }; + let line = sourceLocation.line; + let endLine = sourceLocation.lineEnd; if (this.logicSigStatus === ProgramStatus.STARTING) { if (sourceLocation.lsigLocation) { - frameSourceLocation = { - line: sourceLocation.lsigLocation.line, - endLine: sourceLocation.lsigLocation.lineEnd, - }; + line = sourceLocation.lsigLocation.line; + endLine = sourceLocation.lsigLocation.lineEnd; } } else if (this.appStatus === ProgramStatus.STARTING) { if (sourceLocation.appLocation) { - frameSourceLocation = { - line: sourceLocation.appLocation.line, - endLine: sourceLocation.appLocation.lineEnd, - }; + line = sourceLocation.appLocation.line; + endLine = sourceLocation.appLocation.lineEnd; } } - return frameSourceLocation; + return { + name: `${ + this.txnPath.length > 2 ? 'inner-' : '' + }transaction-group-${this.txnPath.slice(0, -1).join('-')}.json`, + content: this.sourceContent, + contentMimeType: 'application/json', + line: line, + endLine: endLine, + }; } - public forward(stack: TraceReplayStackFrame[]): ExceptionInfo | void { + public forward(stack: TraceReplayFrame[]): ExceptionInfo | void { const currentTxnTrace = this.txnTraces[this.txnIndex]; const currentTxnInfo = this.txnInfos[this.txnIndex]; @@ -659,7 +674,7 @@ export class TransactionGroupStackFrame extends TraceReplayStackFrame { stack.pop(); } - public backward(stack: TraceReplayStackFrame[]): ExceptionInfo | void { + public backward(stack: TraceReplayFrame[]): ExceptionInfo | void { if (this.onException) { this.onException = false; return; @@ -712,34 +727,26 @@ export class TransactionGroupStackFrame extends TraceReplayStackFrame { } } -export interface ProgramState { - pc: number; - stack: algosdk.modelsv2.AvmValue[]; - scratch: Map; -} - -export class ProgramStackFrame extends TraceReplayStackFrame { +export class ProgramStackFrame implements TraceReplayFrame { + private lastIndex: number | undefined; private index: number = 0; private handledInnerTxns: boolean = false; private initialAppState: AppState | undefined; private logicSigAddress: string | undefined; private blockingException: ExceptionInfo | undefined; - - public state: ProgramState = { pc: 0, stack: [], scratch: new Map() }; + private programReplay: ProgramReplay; + public readonly isPuyaFrame: boolean; constructor( - engine: TraceReplayEngine, + private readonly engine: TraceReplayEngine, private readonly txnPath: number[], private readonly programType: 'logic sig' | 'approval' | 'clear state', - private readonly programHash: Uint8Array, + programHash: Uint8Array, private readonly programTrace: algosdk.modelsv2.SimulationOpcodeTraceUnit[], private readonly trace: algosdk.modelsv2.SimulationTransactionExecTrace, private readonly txnInfo: algosdk.modelsv2.PendingTransactionResponse, private readonly failureInfo: TransactionFailureInfo | undefined, ) { - super(engine); - this.state.pc = Number(programTrace[0].pc); - const appID = this.currentAppID(); if (typeof appID !== 'undefined') { this.initialAppState = engine.currentAppState.get(appID)!.clone(); @@ -753,6 +760,17 @@ export class ProgramStackFrame extends TraceReplayStackFrame { const lsigAccount = new algosdk.LogicSigAccount(lsigBytes); this.logicSigAddress = lsigAccount.address().toString(); } + + const sourceMapPath = this.engine.programHashToSource.get(programHash); + this.isPuyaFrame = isPuyaSourceMap(sourceMapPath?.json); + + this.programReplay = new ProgramReplay( + this.name, + programTrace, + sourceMapPath, + this.currentAppID(), + engine.currentAppState, + ); } public currentAppID(): bigint | undefined { @@ -770,7 +788,11 @@ export class ProgramStackFrame extends TraceReplayStackFrame { return undefined; } - public name(): string { + public get callStack(): CallStackFrame[] { + return this.programReplay.callStack; + } + + public get name(): string { const appID = this.currentAppID(); if (typeof appID !== 'undefined') { return `app ${appID} ${this.programType} program`; @@ -781,213 +803,113 @@ export class ProgramStackFrame extends TraceReplayStackFrame { return `${this.programType} program`; } - public sourceFile(): FrameSource { - const sourceInfo = this.engine.programHashToSource.get(this.programHash); - if (!sourceInfo) { - let name: string; - const appID = this.currentAppID(); - if (typeof appID !== 'undefined') { - name = `app ${appID} ${this.programType}.teal`; - } else if (typeof this.logicSigAddress !== 'undefined') { - name = `logic sig ${this.logicSigAddress}.teal`; - } else { - name = `program ${algosdk.bytesToHex(this.programHash)}.teal`; - } - return { - name, - content: '// source not available', - }; - } - const location = sourceInfo.sourcemap.getLocationForPc(this.state.pc); - // If we can't find a location for this PC, just return the first source. - const sourceIndex = location ? location.sourceIndex : 0; - const source = sourceInfo.getFullSourcePath(sourceIndex); - return { - name: source, - path: source, - }; - } - - public sourceLocation(): FrameSourceLocation { - const sourceInfo = this.engine.programHashToSource.get(this.programHash); - if (!sourceInfo) { - return { line: 0 }; - } - const location = sourceInfo.sourcemap.getLocationForPc(this.state.pc); - if (!location) { - return { line: 0 }; - } - return { - line: location.line, - column: location.column, - }; + get pendingInnerTxn() { + const currentUnit = this.programTrace[this.index]; + const spawnedInners = currentUnit?.spawnedInners; + return ( + !this.handledInnerTxns && spawnedInners && spawnedInners.length !== 0 + ); } - public forward(stack: TraceReplayStackFrame[]): ExceptionInfo | void { - if (this.blockingException) { - return this.blockingException; - } - - if (this.index === this.programTrace.length) { - stack.pop(); - return; - } - - const currentUnit = this.programTrace[this.index]; - this.processUnit(currentUnit); - - const spawnedInners = currentUnit.spawnedInners; - if (!this.handledInnerTxns && spawnedInners && spawnedInners.length !== 0) { - const spawnedInnerIndexes = spawnedInners.map((i) => Number(i)); - const innerGroupInfo: algosdk.modelsv2.PendingTransactionResponse[] = []; - const innerTraces: algosdk.modelsv2.SimulationTransactionExecTrace[] = []; - for (const innerIndex of spawnedInnerIndexes) { - const innerTxnInfo = this.txnInfo.innerTxns![innerIndex]; - const innerTrace = this.trace.innerTrace![innerIndex]; - innerGroupInfo.push(innerTxnInfo); - innerTraces.push(innerTrace); - } - const expandedPath = this.txnPath.slice(); - expandedPath.push(spawnedInnerIndexes[0]); - let innerFailureInfo: TransactionFailureInfo | undefined = undefined; - if ( - this.failureInfo && - this.failureInfo.path.length > this.txnPath.length - 1 && - pathStartWith(this.failureInfo.path, this.txnPath.slice(1)) - ) { - innerFailureInfo = this.failureInfo; - } - const innerGroupFrame = new TransactionGroupStackFrame( - this.engine, - expandedPath, - innerGroupInfo, - innerTraces, - innerFailureInfo, - ); - stack.push(innerGroupFrame); - this.handledInnerTxns = true; - return; + public forward(stack: TraceReplayFrame[]): ExceptionInfo | void { + if (!this.pendingInnerTxn && this.index < this.programTrace.length) { + this.lastIndex = this.index; } - - this.index++; - - if (this.index < this.programTrace.length) { - this.state.pc = Number(this.programTrace[this.index].pc); - this.handledInnerTxns = false; - } else { - if (this.programType === 'clear state' && this.trace.clearStateRollback) { - // If there's a rollback, reset the app state to the initial state - this.engine.currentAppState.set( - this.currentAppID()!, - this.initialAppState!.clone(), - ); - // Don't return the clear state error here, failureInfo takes precedence + const lastLocation = this.programReplay.nextPcSource; + let again = true; + while (again) { + if (this.blockingException) { + return this.blockingException; } - if ( - this.failureInfo && - pathsEqual(this.txnPath.slice(1), this.failureInfo.path) - ) { - // If there's an error, show it at the end of execution - this.blockingException = new ExceptionInfo(this.failureInfo.message); - return this.blockingException; + if (this.index === this.programTrace.length) { + stack.pop(); + return; } - if (this.programType === 'clear state' && this.trace.clearStateRollback) { - // Show error message for clear state rollback. This is NOT a blocking error. - if (typeof this.trace.clearStateRollbackError !== 'undefined') { - return new ExceptionInfo(this.trace.clearStateRollbackError); + if (this.pendingInnerTxn) { + const currentUnit = this.programTrace[this.index]; + const spawnedInners = currentUnit.spawnedInners!; + const spawnedInnerIndexes = spawnedInners.map((i) => Number(i)); + const innerGroupInfo: algosdk.modelsv2.PendingTransactionResponse[] = + []; + const innerTraces: algosdk.modelsv2.SimulationTransactionExecTrace[] = + []; + for (const innerIndex of spawnedInnerIndexes) { + const innerTxnInfo = this.txnInfo.innerTxns![innerIndex]; + const innerTrace = this.trace.innerTrace![innerIndex]; + innerGroupInfo.push(innerTxnInfo); + innerTraces.push(innerTrace); } - // If no specific error message, show a generic one (this is what happens during rejection) - return new ExceptionInfo('Clear state program did not succeed'); + const expandedPath = this.txnPath.slice(); + expandedPath.push(spawnedInnerIndexes[0]); + let innerFailureInfo: TransactionFailureInfo | undefined = undefined; + if ( + this.failureInfo && + this.failureInfo.path.length > this.txnPath.length - 1 && + pathStartWith(this.failureInfo.path, this.txnPath.slice(1)) + ) { + innerFailureInfo = this.failureInfo; + } + const innerGroupFrame = new TransactionGroupStackFrame( + this.engine, + expandedPath, + innerGroupInfo, + innerTraces, + innerFailureInfo, + ); + stack.push(innerGroupFrame); + this.handledInnerTxns = true; + return; } - } - } - - private processUnit(unit: algosdk.modelsv2.SimulationOpcodeTraceUnit) { - this.state.pc = Number(unit.pc); + this.programReplay.forward(); + // loop until location has advanced + again = + this.isPuyaFrame && + !locationHasAdvanced(lastLocation, this.programReplay.nextPcSource); - const stackPopCount = unit.stackPopCount ? Number(unit.stackPopCount) : 0; - if (stackPopCount > this.state.stack.length) { - throw new Error( - `Stack underflow at pc ${unit.pc}: ${stackPopCount} > ${this.state.stack.length}`, - ); - } - this.state.stack = this.state.stack.slice( - 0, - this.state.stack.length - stackPopCount, - ); - if (unit.stackAdditions) { - this.state.stack.push(...unit.stackAdditions); - } + this.index++; - for (const scratchWrite of unit.scratchChanges || []) { - const slot = Number(scratchWrite.slot); - if (slot < 0 || slot >= 256) { - throw new Error(`Invalid scratch slot ${slot}`); - } - const newValue = scratchWrite.newValue; - if (newValue.type === 2 && !newValue.uint) { - // When setting to 0, delete the entry, since 0 is the default. - this.state.scratch.delete(slot); + if (this.index < this.programTrace.length) { + this.handledInnerTxns = false; } else { - this.state.scratch.set(slot, newValue); - } - } - - if (unit.stateChanges && unit.stateChanges.length !== 0) { - const appID = this.currentAppID(); - if (typeof appID === 'undefined') { - throw new Error('No appID'); - } + if ( + this.programType === 'clear state' && + this.trace.clearStateRollback + ) { + // If there's a rollback, reset the app state to the initial state + this.engine.currentAppState.set( + this.currentAppID()!, + this.initialAppState!.clone(), + ); + // Don't return the clear state error here, failureInfo takes precedence + } - const state = this.engine.currentAppState.get(appID); - if (!state) { - throw new Error(`No state for appID ${appID}`); - } + if ( + this.failureInfo && + pathsEqual(this.txnPath.slice(1), this.failureInfo.path) + ) { + // If there's an error, show it at the end of execution + this.blockingException = new ExceptionInfo(this.failureInfo.message); + return this.blockingException; + } - for (const stateChange of unit.stateChanges) { - switch (stateChange.appStateType) { - case 'g': - if (stateChange.operation === 'w') { - state.globalState.set(stateChange.key, stateChange.newValue!); - } else if (stateChange.operation === 'd') { - state.globalState.delete(stateChange.key); - } - break; - case 'l': - if (stateChange.operation === 'w') { - const accountState = state.localState.get( - stateChange.account!.toString(), - ); - if (!accountState) { - const newState = new ByteArrayMap(); - newState.set(stateChange.key, stateChange.newValue!); - state.localState.set(stateChange.account!.toString(), newState); - } else { - accountState.set(stateChange.key, stateChange.newValue!); - } - } else if (stateChange.operation === 'd') { - const accountState = state.localState.get( - stateChange.account!.toString(), - ); - if (accountState) { - accountState.delete(stateChange.key); - } - } - break; - case 'b': - if (stateChange.operation === 'w') { - state.boxState.set(stateChange.key, stateChange.newValue!); - } else if (stateChange.operation === 'd') { - state.boxState.delete(stateChange.key); - } + if ( + this.programType === 'clear state' && + this.trace.clearStateRollback + ) { + // Show error message for clear state rollback. This is NOT a blocking error. + if (typeof this.trace.clearStateRollbackError !== 'undefined') { + return new ExceptionInfo(this.trace.clearStateRollbackError); + } + // If no specific error message, show a generic one (this is what happens during rejection) + return new ExceptionInfo('Clear state program did not succeed'); } } } } - public backward(stack: TraceReplayStackFrame[]): ExceptionInfo | void { + public backward(stack: TraceReplayFrame[]): ExceptionInfo | void { if (this.blockingException) { this.blockingException = undefined; } @@ -1000,19 +922,21 @@ export class ProgramStackFrame extends TraceReplayStackFrame { stack.pop(); return; } - const targetIndex = this.index - 1; + // reset and then advance until the current index is the lastIndex + const stopAt = this.lastIndex; this.reset(); - while (this.index < targetIndex) { - this.engine.forward(); + if (stopAt !== undefined) { + while (this.index < stopAt) { + this.engine.forward(); + } } } private reset() { + this.lastIndex = undefined; this.index = 0; this.handledInnerTxns = false; - this.state.pc = Number(this.programTrace[0].pc); - this.state.stack = []; - this.state.scratch.clear(); + this.programReplay.reset(); if (typeof this.initialAppState !== 'undefined') { this.engine.currentAppState.set( this.currentAppID()!, @@ -1048,3 +972,31 @@ function pathStartWith(path: number[], prefix: number[]): boolean { } return true; } + +function locationHasAdvanced( + from: FrameSource | undefined, + to: FrameSource | undefined, +): boolean { + // two unknown locations have not advanced + if (from === undefined && to === undefined) { + return false; + } + // unknown -> known has advanced + if (from === undefined && to !== undefined) { + return true; + } + // known -> unknown has not advanced + if (from !== undefined && to === undefined) { + return false; + } + if (from?.path !== to?.path) { + return true; + } + if (from?.line !== to?.line) { + return true; + } + if (from?.column !== to?.column) { + return true; + } + return false; +} diff --git a/src/common/utils.ts b/src/common/utils.ts index 99197db..1b61c17 100644 --- a/src/common/utils.ts +++ b/src/common/utils.ts @@ -14,6 +14,91 @@ export function utf8Decode(data: Uint8Array): string | undefined { } } +// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unused-vars +function parseAlgosdkV2SimulateResponse(obj: any): any { + if (obj === null || typeof obj !== 'object') return obj; + + const addressFields = new Set([ + 'snd', + 'close', + 'aclose', + 'rekey', + 'rcv', + 'arcv', + 'fadd', + 'asnd', + ]); + const toUintFields = new Set(['gh', 'apaa']); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const processValue = (key: string, value: any): any => { + if (typeof value === 'string') { + if (addressFields.has(key)) { + try { + return algosdk.encodeAddress(algosdk.base64ToBytes(value)); + } catch { + return value; + } + } + if (toUintFields.has(key)) { + return algosdk.base64ToBytes(value); + } + } else if (Array.isArray(value)) { + if (toUintFields.has(key)) { + return value.map((item) => + typeof item === 'string' ? algosdk.base64ToBytes(item) : item, + ); + } + return value.map((item) => parseAlgosdkV2SimulateResponse(item)); + } else if (typeof value === 'object' && value !== null) { + return parseAlgosdkV2SimulateResponse(value); + } + return value; + }; + + return Object.fromEntries( + Object.entries(obj).map(([key, value]) => [key, processValue(key, value)]), + ); +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function objectToMapRecursive(obj: any): any { + if (obj === null || typeof obj !== 'object' || obj instanceof Uint8Array) { + return obj; + } + + if (Array.isArray(obj)) { + return obj.map(objectToMapRecursive); + } + + return new Map( + Object.entries(obj).map(([key, value]) => [ + key, + objectToMapRecursive(value), + ]), + ); +} + +function tryParseAlgosdkV2SimulateResponse( + rawSimulateTrace: Uint8Array, +): algosdk.modelsv2.SimulateResponse { + const algosdkV2Response = parseAlgosdkV2SimulateResponse( + algosdk.parseJSON(algosdk.bytesToString(rawSimulateTrace), { + intDecoding: algosdk.IntDecoding.MIXED, + }), + ); + + if (algosdkV2Response.version !== 2) { + throw new Error( + `Unsupported simulate response version: ${algosdkV2Response.version}`, + ); + } + + return algosdk.modelsv2.SimulateResponse.fromEncodingData( + objectToMapRecursive(algosdkV2Response), + ); +} + /** * Normalize the given file path. * @@ -125,37 +210,44 @@ export class ByteArrayMap { } } -interface ProgramSourceEntryFile { +export interface ProgramSourceEntryFile { 'txn-group-sources': ProgramSourceEntry[]; } -interface ProgramSourceEntry { +export interface ProgramSourceEntry { hash: string; - 'sourcemap-location': string; + 'sourcemap-location': string | null; } -export class ProgramSourceDescriptor { - public readonly fileAccessor: FileAccessor; - public readonly sourcemapFileLocation: string; - public readonly sourcemap: algosdk.ProgramSourceMap; - public readonly hash: Uint8Array; +export interface PCEvent { + subroutine?: string; + block?: string; + op?: string; + callsub?: string; + retsub?: boolean; + params?: Record; + stack_in?: string[]; + stack_out?: string[]; + defined_out?: string[]; +} - constructor({ - fileAccessor, - sourcemapFileLocation, - sourcemap, - hash, - }: { - fileAccessor: FileAccessor; - sourcemapFileLocation: string; - sourcemap: algosdk.ProgramSourceMap; - hash: Uint8Array; - }) { - this.fileAccessor = fileAccessor; - this.sourcemapFileLocation = sourcemapFileLocation; - this.sourcemap = sourcemap; - this.hash = hash; - } +interface ISourceMap { + version: number; + sources: string[]; + names: string[]; + mappings: string; + op_pc_offset?: number; + pc_events?: Record; +} + +export class ProgramSourceDescriptor { + constructor( + public readonly fileAccessor: FileAccessor, + public readonly sourcemapFileLocation: string, + public readonly json: ISourceMap, + public readonly sourcemap: algosdk.ProgramSourceMap, + public readonly hash: Uint8Array, + ) {} public sourcePaths(): string[] { return this.sourcemap.sources.map((_, index) => @@ -175,27 +267,27 @@ export class ProgramSourceDescriptor { static async fromJSONObj( fileAccessor: FileAccessor, - originFile: string, + originPath: string, data: ProgramSourceEntry, ): Promise { const sourcemapFileLocation = normalizePathAndCasing( fileAccessor, - fileAccessor.filePathRelativeTo(originFile, data['sourcemap-location']), + fileAccessor.filePathRelativeTo(originPath, data['sourcemap-location']!), ); const rawSourcemap = await prefixPotentialError( fileAccessor.readFile(sourcemapFileLocation), 'Could not read source map file', ); - const sourcemap = new algosdk.ProgramSourceMap( - JSON.parse(new TextDecoder().decode(rawSourcemap)), - ); + const json = JSON.parse(new TextDecoder().decode(rawSourcemap)); + const sourcemap = new algosdk.ProgramSourceMap(json); - return new ProgramSourceDescriptor({ + return new ProgramSourceDescriptor( fileAccessor, sourcemapFileLocation, + json, sourcemap, - hash: algosdk.base64ToBytes(data.hash), - }); + algosdk.base64ToBytes(data.hash), + ); } } @@ -216,25 +308,19 @@ export class ProgramSourceDescriptorRegistry { return this.registry.get(hash); } - static async loadFromFile( + static async loadFromContent( fileAccessor: FileAccessor, - programSourcesDescriptionFilePath: string, + jsonSourcesDescription: ProgramSourceEntryFile, + originPath?: string, ): Promise { - const rawSourcesDescription = await prefixPotentialError( - fileAccessor.readFile(programSourcesDescriptionFilePath), - 'Could not read program sources description file', - ); - let jsonSourcesDescription: ProgramSourceEntryFile; try { - jsonSourcesDescription = JSON.parse( - new TextDecoder().decode(rawSourcesDescription), - ) as ProgramSourceEntryFile; if ( !Array.isArray(jsonSourcesDescription['txn-group-sources']) || !jsonSourcesDescription['txn-group-sources'].every( (entry) => typeof entry.hash === 'string' && - typeof entry['sourcemap-location'] === 'string', + (typeof entry['sourcemap-location'] === 'string' || + entry['sourcemap-location'] === null), ) ) { throw new Error('Invalid program sources description file'); @@ -242,17 +328,22 @@ export class ProgramSourceDescriptorRegistry { } catch (e) { const err = e as Error; throw new Error( - `Could not parse program sources description file from '${programSourcesDescriptionFilePath}': ${err.message}`, + `Could not parse program sources description ${ + originPath ? `file from '${originPath}'` : 'content' + }: ${err.message}`, ); } - const programSources = jsonSourcesDescription['txn-group-sources'].map( - (source) => + + const programSources = jsonSourcesDescription['txn-group-sources'] + .filter((source) => source['sourcemap-location'] !== null) + .map((source) => ProgramSourceDescriptor.fromJSONObj( fileAccessor, - programSourcesDescriptionFilePath, + originPath || '', source, ), - ); + ); + return new ProgramSourceDescriptorRegistry({ txnGroupSources: await Promise.all(programSources), }); @@ -268,7 +359,8 @@ export class AvmDebuggingAssets { static async loadFromFiles( fileAccessor: FileAccessor, simulateTraceFilePath: string, - programSourcesDescriptionFilePath: string, + programSourcesDescription: ProgramSourceEntryFile, + programSourcesDescriptionFolder: string, ): Promise { const rawSimulateTrace = await prefixPotentialError( fileAccessor.readFile(simulateTraceFilePath), @@ -276,10 +368,14 @@ export class AvmDebuggingAssets { ); let simulateResponse: algosdk.modelsv2.SimulateResponse; try { - simulateResponse = algosdk.decodeJSON( - algosdk.bytesToString(rawSimulateTrace), - algosdk.modelsv2.SimulateResponse, - ); + try { + simulateResponse = algosdk.decodeJSON( + algosdk.bytesToString(rawSimulateTrace), + algosdk.modelsv2.SimulateResponse, + ); + } catch (e) { + simulateResponse = tryParseAlgosdkV2SimulateResponse(rawSimulateTrace); + } if (simulateResponse.version !== 2) { throw new Error( `Unsupported simulate response version: ${simulateResponse.version}`, @@ -302,16 +398,28 @@ export class AvmDebuggingAssets { } const txnGroupDescriptorList = - await ProgramSourceDescriptorRegistry.loadFromFile( + await ProgramSourceDescriptorRegistry.loadFromContent( fileAccessor, - programSourcesDescriptionFilePath, + programSourcesDescription, + programSourcesDescriptionFolder, ); return new AvmDebuggingAssets(simulateResponse, txnGroupDescriptorList); } } -function prefixPotentialError(task: Promise, prefix: string): Promise { +export function isPuyaSourceMap(sourcemap: ISourceMap | undefined): boolean { + return sourcemap?.pc_events !== undefined; +} + +export function isPuyaFrontendSourceExtension(filePath?: string): boolean { + return !filePath?.endsWith('.teal') || false; +} + +export function prefixPotentialError( + task: Promise, + prefix: string, +): Promise { return task.catch((error) => { throw new Error(`${prefix}: ${error.message}`); }); diff --git a/tests/adapter.test.ts b/tests/adapter.test.ts index 7912b4f..d93def1 100644 --- a/tests/adapter.test.ts +++ b/tests/adapter.test.ts @@ -2563,3 +2563,109 @@ describe('Debug Adapter Tests', () => { await fixture.client.waitForEvent('terminated'); }); }); + +describe('Puya Debugging', () => { + const fixture = new TestFixture(); + + before(async () => await fixture.init()); + + afterEach(async () => { + await fixture.reset(); + }); + + after(async () => { + await fixture.stop(); + }); + + it('should correctly step through and inspect variables in a Puya program', async () => { + const simulateTraceFile = path.join( + DATA_ROOT, + 'puya/simulate-response.json', + ); + const programSourcesDescriptionFile = path.join( + DATA_ROOT, + 'puya/sources.json', + ); + const { client } = fixture; + + const program = normalizePathAndCasing( + nodeFileAccessor, + path.join(DATA_ROOT, 'puya/contract.py'), + ); + + await Promise.all([ + client.configurationSequence(), + client.launch({ + simulateTraceFile, + programSourcesDescriptionFile, + stopOnEntry: true, + }), + client.assertStoppedLocation('entry', {}), + ]); + + // Set breakpoint at the beginning of the confirm attendance method + await client.setBreakpointsRequest({ + source: { path: program }, + breakpoints: [{ line: 24 }], + }); + + await client.continueRequest({ threadId: 1 }); + await client.assertStoppedLocation('breakpoint', { + path: program, + line: 24, + }); + + // Check variables at the start of the confirm attendance method + await assertVariables(client, { + pc: 426, + stack: [], + }); + + // Set breakpoint at the beginning of the confirm attendance method + await client.setBreakpointsRequest({ + source: { path: program }, + breakpoints: [{ line: 27 }], + }); + + await client.continueRequest({ threadId: 1 }); + await client.assertStoppedLocation('breakpoint', { + path: program, + line: 27, + }); + + // Verify Puya-specific variables + const scopesResponse = await client.scopesRequest({ frameId: 0 }); + const localsScope = scopesResponse.body.scopes.find( + (s) => s.name === 'Locals', + ); + assert.ok(localsScope, 'Locals scope not found'); + + const variablesResponse = await client.variablesRequest({ + variablesReference: localsScope!.variablesReference, + }); + assert.deepStrictEqual( + variablesResponse.body.variables.find((v) => v.name === 'minted_asset'), + { + evaluateName: 'minted_asset', + indexedVariables: 32, + name: 'minted_asset', + namedVariables: 2, + presentationHint: { + attributes: ['rawString'], + kind: 'data', + }, + type: 'byte[]', + value: + '0xd55d3e52e015364e12f1fe09a492b34c9d2bb524b76d32211fa8c1350376e4b3', + variablesReference: 1003, + }, + ); + await client.continueRequest({ threadId: 1 }); + + // Continue to the end + await Promise.all([ + client.continueRequest({ threadId: 1 }), + client.waitForEvent('terminated'), + ]); + }); +}); diff --git a/tsconfig.json b/tsconfig.json index 5749b2b..652f0bd 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,7 +12,6 @@ // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ // "noUnusedParameters": true, /* Report errors on unused parameters. */ - "noImplicitAny": false, "removeComments": false, "noUnusedLocals": true, @@ -22,5 +21,6 @@ "strictNullChecks": true, "noUnusedParameters": false }, - "include": ["src/**/*", "extension/**/*", "tests/**/*"] + "include": ["src/**/*", "extension/**/*", "tests/**/*"], + "exclude": ["sampleWorkspace/**/*"] }