Repository: algorand/js-algorand-sdk
Pull request #520 (2022-02-15 20:55:39)

FOR REVIEW ONLY - js-algorand-sdk 1.14.0-beta.1
# v1.14.0-beta.1

## Added

- Add stateproof keyreg field (#463)

## Changed

- Implement C2C tests (#498)
- Moving from travis to circleci (#507)
- Add installation instructions for vite users (#512)
- Update langspec for TEAL 6 (#518)



Patch:
diff --git a/.circleci/config.yml b/.circleci/config.yml
new file mode 100644
index 0000000..99d0ddd
--- /dev/null
+++ b/.circleci/config.yml
@@ -0,0 +1,134 @@
+version: 2.1
+
+orbs:
+  node: circleci/node@5.0.0
+  slack: circleci/slack@4.4.2
+  browser-tools: circleci/browser-tools@1.2.4
+  gh-pages: sugarshin/gh-pages@1.0.0
+
+parameters:
+  ubuntu_machine:
+    type: string
+    default: 'ubuntu-2004:202111-02'
+  ubuntu_docker:
+    type: string
+    default: 'ubuntu:focal'
+
+workflows:
+  circleci_build_and_test:
+    jobs:
+      - ESLint
+      - prettier
+      - compile_test
+      - build_and_test:
+          name: build_and_test_<< matrix.browser >>
+          context: slack-secrets
+          matrix:
+            parameters:
+              browser: ['node', 'chrome', 'firefox']
+      - generate_docs:
+          requires:
+            - ESLint
+            - prettier
+            - compile_test
+            - build_and_test
+          filters:
+            branches:
+              only:
+                - master
+
+jobs:
+  ESLint:
+    docker:
+      - image: << pipeline.parameters.ubuntu_docker >>
+    steps:
+      - checkout
+      - install_dependencies
+      - run: npm run lint
+
+  prettier:
+    docker:
+      - image: << pipeline.parameters.ubuntu_docker >>
+    steps:
+      - checkout
+      - install_dependencies
+      - run: npx prettier --check .
+
+  compile_test:
+    docker:
+      - image: << pipeline.parameters.ubuntu_docker >>
+    steps:
+      - checkout
+      - install_dependencies
+      - run:
+          name: Compile test
+          command: |
+            npm run build
+            npx tsc -p tests/compile
+
+  build_and_test:
+    parameters:
+      browser:
+        type: string
+    machine:
+      image: << pipeline.parameters.ubuntu_machine >>
+    resource_class: medium
+    steps:
+      - checkout
+      - install_dependencies:
+          sudo: 'sudo'
+      - run:
+          name: Install docker
+          command: |
+            export SUDO=sudo
+            $SUDO apt update
+            $SUDO apt -y install ca-certificates curl gnupg lsb-release
+            $SUDO curl -fsSL https://download.docker.com/linux/ubuntu/gpg | $SUDO gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
+            $SUDO echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
+              $(lsb_release -cs) stable" | $SUDO tee /etc/apt/sources.list.d/docker.list > /dev/null
+            $SUDO apt update
+            $SUDO apt -y install docker-ce docker-ce-cli containerd.io
+      - browser-tools/install-browser-tools
+      - run:
+          name: << parameters.browser >> test
+          command: |
+            if [ "<< parameters.browser >>" == "node" ]; then unset TEST_BROWSER; else export TEST_BROWSER=<< parameters.browser >> ; fi
+            npm test
+            make docker-test
+
+  generate_docs:
+    docker:
+      - image: << pipeline.parameters.ubuntu_docker >>
+    steps:
+      - checkout
+      - install_dependencies
+      - run:
+          name: Generate docs for GitHub Pages
+          command: |
+            rm -rf docs
+            npm run docs
+            touch docs/.nojekyll
+      - gh-pages/deploy:
+          ssh-fingerprints: '75:f5:ad:46:65:a5:22:81:f7:20:ca:74:fb:c6:57:d1'
+          build-dir: docs
+          commit-message: 'Automated docs update'
+
+commands:
+  install_dependencies:
+    parameters:
+      sudo:
+        type: string
+        default: ''
+    steps:
+      - run:
+          name: Install Dependencies
+          command: |
+            << parameters.sudo >> apt -y update && << parameters.sudo >> apt -y upgrade
+            << parameters.sudo >> apt -y install curl make git build-essential jq unzip
+      - node/install:
+          node-version: '12'
+      - run:
+          name: npm ci
+          command: |
+            set -e
+            npm ci
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 818b9d8..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,44 +0,0 @@
-dist: bionic
-language: node_js
-
-node_js:
-  - '12'
-
-script:
-  - set -e
-  - if [ -n "$DOCKER_PASSWORD" ]; then echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin ; fi
-  - npm test
-  - make docker-test
-
-jobs:
-  include:
-    - stage: Code Style
-      name: ESLint
-      script: npm run lint
-    - name: Prettier
-      script: prettier --check .
-    - stage: Testing
-      name: Compile test
-      script:
-        - npm run build
-        - npx tsc -p tests/compile
-    - name: Node test
-    - name: Chrome test
-      env: TEST_BROWSER=chrome
-      addons:
-        chrome: stable
-    - name: Firefox test
-      env: TEST_BROWSER=firefox
-      addons:
-        firefox: latest
-    - stage: Docs
-      name: GitHub Pages
-      if: branch = master
-      script: npm run docs
-      deploy:
-        provider: pages
-        skip_cleanup: true
-        keep_history: true
-        local_dir: docs
-        github_token: $GITHUB_TOKEN # Set in travis-ci.org dashboard
-      before_deploy: touch docs/.nojekyll
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 65bbef1..dd2feb0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,16 @@
+# v1.14.0-beta.1
+
+## Added
+
+- Add stateproof keyreg field (#463)
+
+## Changed
+
+- Implement C2C tests (#498)
+- Moving from travis to circleci (#507)
+- Add installation instructions for vite users (#512)
+- Update langspec for TEAL 6 (#518)
+
 # 1.13.1
 
 ## Added:
diff --git a/FAQ.md b/FAQ.md
index 1d44239..dafc8f2 100644
--- a/FAQ.md
+++ b/FAQ.md
@@ -10,6 +10,66 @@ In prior versions, the browser bundles were made available in the `dist` directo
 
 Yes! If you would instead prefer to host the package yourself, a minified browser bundle can be found in the `dist/browser/` folder of the published npm package starting with version 1.9.0.
 
+## It says `Error: Can't resolve....` in the sdk
+
+This kind of errors is usually seen in Webpack 5 or Vite projects. You will need to install additional polyfill packages.
+
+#### Webpack 5 projects
+
+Typically, with Webpack 5 you would see:
+
+```
+Module not found: Error: Can't resolve 'crypto'
+ERROR in ./node_modules/algosdk/dist/browser/algosdk.min.js 7471:55-72
+```
+
+Webpack 5 no longer auto-polyfills some of the node modules used by this sdk. You will have to polyfill them to fix any errors regarding missing modules.
+
+#### Vite projects
+
+With Vite, you would see:
+
+```
+Uncaught ReferenceError: Buffer is not defined
+```
+
+You will have to install `buffer` and `path-browserify` as dependencies.
+
+In `vite.config.js`, specify:
+
+```js
+resolve: {
+  alias: {
+    path: 'path-browserify';
+  }
+}
+```
+
+In `index.html`, add the following:
+
+```html
+<script type="module">
+  import { Buffer } from 'buffer';
+  window.Buffer = Buffer;
+</script>
+```
+
+To utilize the Buffer polyfill in production builds, in `vite.config.js`, add:
+
+```js
+import inject from '@rollup/plugin-inject';
+
+export default defineConfig({
+  ...,
+  build: {
+    rollupOptions: {
+      plugins: [inject({ Buffer: ['buffer', 'Buffer'] })],
+    },
+  },
+  ...
+});
+```
+
 ## How do I generate the SRI hash of `algosdk.min.js`?
 
 The SRI hash of `algosdk.min.js` is the base64 string after `sha384-` in the `integrity` attribute of the `<script>` tag used to import `algosdk.min.js`.
diff --git a/Makefile b/Makefile
index df7fe68..6ab1df4 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 unit:
-	node_modules/.bin/cucumber-js --tags "@unit.offline or @unit.algod or @unit.indexer or @unit.rekey or @unit.tealsign or @unit.dryrun or @unit.applications or @unit.responses or @unit.transactions or @unit.transactions.payment or @unit.responses.231 or @unit.feetest or @unit.indexer.logs or @unit.abijson or @unit.atomic_transaction_composer" tests/cucumber/features --require-module ts-node/register --require tests/cucumber/steps/index.js
+	node_modules/.bin/cucumber-js --tags "@unit.offline or @unit.algod or @unit.indexer or @unit.rekey or @unit.tealsign or @unit.dryrun or @unit.applications or @unit.responses or @unit.transactions or @unit.transactions.keyreg or @unit.transactions.payment or @unit.responses.231 or @unit.feetest or @unit.indexer.logs or @unit.abijson or @unit.atomic_transaction_composer" tests/cucumber/features --require-module ts-node/register --require tests/cucumber/steps/index.js
 integration:
-	node_modules/.bin/cucumber-js --tags "@algod or @assets or @auction or @kmd or @send or @template or @indexer or @rekey or @dryrun or @compile or @applications or @indexer.applications or @applications.verified or @indexer.231 or @abi" tests/cucumber/features --require-module ts-node/register --require tests/cucumber/steps/index.js
+	node_modules/.bin/cucumber-js --tags "@algod or @assets or @auction or @kmd or @send or @indexer or @rekey or @send.keyregtxn or @dryrun or @compile or @applications or @indexer.applications or @applications.verified or @indexer.231 or @abi or @c2c" tests/cucumber/features --require-module ts-node/register --require tests/cucumber/steps/index.js
 
 docker-test:
 	./tests/cucumber/docker/run_docker.sh
diff --git a/README.md b/README.md
index 4d0585f..aa97b9b 100644
--- a/README.md
+++ b/README.md
@@ -14,14 +14,16 @@ $ npm install algosdk
 
 > This package provides TypeScript types, but you will need [TypeScript](https://www.typescriptlang.org/) version 4.2 or higher to use them properly.
 
+For errors in Webpack 5 or Vite projects, you will need to [install extra dependencies](FAQ.md#it-says-error-cant-resolve-in-the-sdk).
+
 ### Browser
 
 Include a minified browser bundle directly in your HTML like so:
 
 ```html
 <script
-  src="https://unpkg.com/algosdk@1.13.1/dist/browser/algosdk.min.js"
-  integrity="sha384-0BSEzBpLxqFWYBI+sOGhv3W91/wPf+jFwCiuXNrC52XZav2qb3Rz+pfq3AFI0CrL"
+  src="https://unpkg.com/algosdk@1.14.0-beta.1/dist/browser/algosdk.min.js"
+  integrity="sha384-GDxzwLVcHrypyA8CcFGLiOne9xqCWuLz9Bfay2fNICEkCuYBUvkC5g0bnY5aeoZC"
   crossorigin="anonymous"
 ></script>
 ```
@@ -30,8 +32,8 @@ or
 
 ```html
 <script
-  src="https://cdn.jsdelivr.net/npm/algosdk@1.13.1/dist/browser/algosdk.min.js"
-  integrity="sha384-0BSEzBpLxqFWYBI+sOGhv3W91/wPf+jFwCiuXNrC52XZav2qb3Rz+pfq3AFI0CrL"
+  src="https://cdn.jsdelivr.net/npm/algosdk@1.14.0-beta.1/dist/browser/algosdk.min.js"
+  integrity="sha384-GDxzwLVcHrypyA8CcFGLiOne9xqCWuLz9Bfay2fNICEkCuYBUvkC5g0bnY5aeoZC"
   crossorigin="anonymous"
 ></script>
 ```
diff --git a/package-lock.json b/package-lock.json
index 99f61d8..24c287c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
 {
   "name": "algosdk",
-  "version": "1.13.1",
+  "version": "1.14.0-beta.1",
   "lockfileVersion": 2,
   "requires": true,
   "packages": {
     "": {
       "name": "algosdk",
-      "version": "1.13.1",
+      "version": "1.14.0-beta.1",
       "license": "MIT",
       "dependencies": {
         "algo-msgpack-with-bigint": "^2.1.1",
diff --git a/package.json b/package.json
index 7d303ad..88f12ab 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "algosdk",
-  "version": "1.13.1",
+  "version": "1.14.0-beta.1",
   "description": "The official JavaScript SDK for Algorand",
   "main": "dist/cjs/index.js",
   "module": "dist/esm/index.js",
diff --git a/src/client/v2/algod/models/types.ts b/src/client/v2/algod/models/types.ts
index 7228d2e..018c4bb 100644
--- a/src/client/v2/algod/models/types.ts
+++ b/src/client/v2/algod/models/types.ts
@@ -28,6 +28,12 @@ export class Account extends BaseModel {
    */
   public amountWithoutPendingRewards: number | bigint;
 
+  /**
+   * MicroAlgo balance required by the account.
+   * The requirement grows based on asset and application usage.
+   */
+  public minBalance: number | bigint;
+
   /**
    * amount of MicroAlgos of pending rewards in this account.
    */
@@ -123,6 +129,8 @@ export class Account extends BaseModel {
    * @param address - the account public key
    * @param amount - (algo) total number of MicroAlgos in the account
    * @param amountWithoutPendingRewards - specifies the amount of MicroAlgos in the account, without the pending rewards.
+   * @param minBalance - MicroAlgo balance required by the account.
+   * The requirement grows based on asset and application usage.
    * @param pendingRewards - amount of MicroAlgos of pending rewards in this account.
    * @param rewards - (ern) total rewards of MicroAlgos the account has received, including pending
    * rewards.
@@ -162,6 +170,7 @@ export class Account extends BaseModel {
     address,
     amount,
     amountWithoutPendingRewards,
+    minBalance,
     pendingRewards,
     rewards,
     round,
@@ -180,6 +189,7 @@ export class Account extends BaseModel {
     address: string;
     amount: number | bigint;
     amountWithoutPendingRewards: number | bigint;
+    minBalance: number | bigint;
     pendingRewards: number | bigint;
     rewards: number | bigint;
     round: number | bigint;
@@ -199,6 +209,7 @@ export class Account extends BaseModel {
     this.address = address;
     this.amount = amount;
     this.amountWithoutPendingRewards = amountWithoutPendingRewards;
+    this.minBalance = minBalance;
     this.pendingRewards = pendingRewards;
     this.rewards = rewards;
     this.round = round;
@@ -218,6 +229,7 @@ export class Account extends BaseModel {
       address: 'address',
       amount: 'amount',
       amountWithoutPendingRewards: 'amount-without-pending-rewards',
+      minBalance: 'min-balance',
       pendingRewards: 'pending-rewards',
       rewards: 'rewards',
       round: 'round',
@@ -267,6 +279,11 @@ export class AccountParticipation extends BaseModel {
    */
   public voteParticipationKey: Uint8Array;
 
+  /**
+   * (stprf) Root of the state proof key (if any)
+   */
+  public stateProofKey?: Uint8Array;
+
   /**
    * Creates a new `AccountParticipation` object.
    * @param selectionParticipationKey - (sel) Selection public key (if any) currently registered for this round.
@@ -275,6 +292,7 @@ export class AccountParticipation extends BaseModel {
    * @param voteLastValid - (voteLst) Last round for which this participation is valid.
    * @param voteParticipationKey - (vote) root participation public key (if any) currently registered for this
    * round.
+   * @param stateProofKey - (stprf) Root of the state proof key (if any)
    */
   constructor({
     selectionParticipationKey,
@@ -282,12 +300,14 @@ export class AccountParticipation extends BaseModel {
     voteKeyDilution,
     voteLastValid,
     voteParticipationKey,
+    stateProofKey,
   }: {
     selectionParticipationKey: string | Uint8Array;
     voteFirstValid: number | bigint;
     voteKeyDilution: number | bigint;
     voteLastValid: number | bigint;
     voteParticipationKey: string | Uint8Array;
+    stateProofKey?: string | Uint8Array;
   }) {
     super();
     this.selectionParticipationKey =
@@ -301,6 +321,10 @@ export class AccountParticipation extends BaseModel {
       typeof voteParticipationKey === 'string'
         ? new Uint8Array(Buffer.from(voteParticipationKey, 'base64'))
         : voteParticipationKey;
+    this.stateProofKey =
+      typeof stateProofKey === 'string'
+        ? new Uint8Array(Buffer.from(stateProofKey, 'base64'))
+        : stateProofKey;
 
     this.attribute_map = {
       selectionParticipationKey: 'selection-participation-key',
@@ -308,6 +332,7 @@ export class AccountParticipation extends BaseModel {
       voteKeyDilution: 'vote-key-dilution',
       voteLastValid: 'vote-last-valid',
       voteParticipationKey: 'vote-participation-key',
+      stateProofKey: 'state-proof-key',
     };
   }
 }
@@ -1255,6 +1280,11 @@ export class DryrunTxnResult extends BaseModel {
 
   public localDeltas?: AccountStateDelta[];
 
+  /**
+   * Disassembled lsig program line by line.
+   */
+  public logicSigDisassembly?: string[];
+
   public logicSigMessages?: string[];
 
   public logicSigTrace?: DryrunState[];
@@ -1269,6 +1299,7 @@ export class DryrunTxnResult extends BaseModel {
    * @param cost - Execution cost of app call transaction
    * @param globalDelta - Application state delta.
    * @param localDeltas -
+   * @param logicSigDisassembly - Disassembled lsig program line by line.
    * @param logicSigMessages -
    * @param logicSigTrace -
    * @param logs -
@@ -1280,6 +1311,7 @@ export class DryrunTxnResult extends BaseModel {
     cost,
     globalDelta,
     localDeltas,
+    logicSigDisassembly,
     logicSigMessages,
     logicSigTrace,
     logs,
@@ -1290,6 +1322,7 @@ export class DryrunTxnResult extends BaseModel {
     cost?: number | bigint;
     globalDelta?: EvalDeltaKeyValue[];
     localDeltas?: AccountStateDelta[];
+    logicSigDisassembly?: string[];
     logicSigMessages?: string[];
     logicSigTrace?: DryrunState[];
     logs?: Uint8Array[];
@@ -1301,6 +1334,7 @@ export class DryrunTxnResult extends BaseModel {
     this.cost = cost;
     this.globalDelta = globalDelta;
     this.localDeltas = localDeltas;
+    this.logicSigDisassembly = logicSigDisassembly;
     this.logicSigMessages = logicSigMessages;
     this.logicSigTrace = logicSigTrace;
     this.logs = logs;
@@ -1312,6 +1346,7 @@ export class DryrunTxnResult extends BaseModel {
       cost: 'cost',
       globalDelta: 'global-delta',
       localDeltas: 'local-deltas',
+      logicSigDisassembly: 'logic-sig-disassembly',
       logicSigMessages: 'logic-sig-messages',
       logicSigTrace: 'logic-sig-trace',
       logs: 'logs',
@@ -1836,17 +1871,43 @@ export class ProofResponse extends BaseModel {
    */
   public stibhash: Uint8Array;
 
+  /**
+   * Represents the depth of the tree that is being proven, i.e. the number of edges
+   * from a leaf to the root.
+   */
+  public treedepth: number | bigint;
+
+  /**
+   * The type of hash function used to create the proof, must be one of:
+   * * sumhash
+   * * sha512_256
+   */
+  public hashtype?: string;
+
   /**
    * Creates a new `ProofResponse` object.
    * @param idx - Index of the transaction in the block's payset.
    * @param proof - Merkle proof of transaction membership.
    * @param stibhash - Hash of SignedTxnInBlock for verifying proof.
+   * @param treedepth - Represents the depth of the tree that is being proven, i.e. the number of edges
+   * from a leaf to the root.
+   * @param hashtype - The type of hash function used to create the proof, must be one of:
+   * * sumhash
+   * * sha512_256
    */
-  constructor(
-    idx: number | bigint,
-    proof: string | Uint8Array,
-    stibhash: string | Uint8Array
-  ) {
+  constructor({
+    idx,
+    proof,
+    stibhash,
+    treedepth,
+    hashtype,
+  }: {
+    idx: number | bigint;
+    proof: string | Uint8Array;
+    stibhash: string | Uint8Array;
+    treedepth: number | bigint;
+    hashtype?: string;
+  }) {
     super();
     this.idx = idx;
     this.proof =
@@ -1857,11 +1918,15 @@ export class ProofResponse extends BaseModel {
       typeof stibhash === 'string'
         ? new Uint8Array(Buffer.from(stibhash, 'base64'))
         : stibhash;
+    this.treedepth = treedepth;
+    this.hashtype = hashtype;
 
     this.attribute_map = {
       idx: 'idx',
       proof: 'proof',
       stibhash: 'stibhash',
+      treedepth: 'treedepth',
+      hashtype: 'hashtype',
     };
   }
 }
diff --git a/src/composer.ts b/src/composer.ts
index c9b5a2c..0117a99 100644
--- a/src/composer.ts
+++ b/src/composer.ts
@@ -49,6 +49,8 @@ export interface ABIResult {
   returnValue?: ABIValue;
   /** If the SDK was unable to decode a return value, the error will be here. */
   decodeError?: Error;
+  /** The pending transaction information from the method transaction */
+  txInfo?: Record<string, any>;
 }
 
 export enum AtomicTransactionComposerStatus {
@@ -633,13 +635,13 @@ export class AtomicTransactionComposer {
       };
 
       try {
+        const pendingInfo =
+          txnIndex === firstMethodCallIndex
+            ? confirmedTxnInfo
+            : // eslint-disable-next-line no-await-in-loop
+              await client.pendingTransactionInformation(txID).do();
+        methodResult.txInfo = pendingInfo;
         if (method.returns.type !== 'void') {
-          const pendingInfo =
-            txnIndex === firstMethodCallIndex
-              ? confirmedTxnInfo
-              : // eslint-disable-next-line no-await-in-loop
-                await client.pendingTransactionInformation(txID).do();
-
           const logs: string[] = pendingInfo.logs || [];
           if (logs.length === 0) {
             throw new Error('App call transaction did not log a return value');
diff --git a/src/logic/langspec.json b/src/logic/langspec.json
index 4ccd4f4..5fa35c4 100644
--- a/src/logic/langspec.json
+++ b/src/logic/langspec.json
@@ -1,5 +1,5 @@
 {
-  "EvalMaxVersion": 5,
+  "EvalMaxVersion": 6,
   "LogicSigVersion": 5,
   "Ops": [
     {
@@ -7,7 +7,7 @@
       "Name": "err",
       "Cost": 1,
       "Size": 1,
-      "Doc": "Error. Fail immediately. This is primarily a fencepost against accidental zero bytes getting compiled into programs.",
+      "Doc": "Fail immediately.",
       "Groups": ["Flow Control"]
     },
     {
@@ -17,7 +17,7 @@
       "Returns": "B",
       "Cost": 35,
       "Size": 1,
-      "Doc": "SHA256 hash of value X, yields [32]byte",
+      "Doc": "SHA256 hash of value A, yields [32]byte",
       "Groups": ["Arithmetic"]
     },
     {
@@ -27,7 +27,7 @@
       "Returns": "B",
       "Cost": 130,
       "Size": 1,
-      "Doc": "Keccak256 hash of value X, yields [32]byte",
+      "Doc": "Keccak256 hash of value A, yields [32]byte",
       "Groups": ["Arithmetic"]
     },
     {
@@ -37,7 +37,7 @@
       "Returns": "B",
       "Cost": 45,
       "Size": 1,
-      "Doc": "SHA512_256 hash of value X, yields [32]byte",
+      "Doc": "SHA512_256 hash of value A, yields [32]byte",
       "Groups": ["Arithmetic"]
     },
     {
@@ -70,7 +70,7 @@
       "Returns": "BB",
       "Cost": 650,
       "Size": 2,
-      "Doc": "decompress pubkey A into components X, Y =\u003e [*... stack*, X, Y]",
+      "Doc": "decompress pubkey A into components X, Y",
       "DocExtra": "The 33 byte public key in a compressed form to be decompressed into X and Y (top) components. All values are big-endian encoded.",
       "ImmediateNote": "{uint8 curve index}",
       "Groups": ["Arithmetic"]
@@ -82,7 +82,7 @@
       "Returns": "BB",
       "Cost": 2000,
       "Size": 2,
-      "Doc": "for (data A, recovery id B, signature C, D) recover a public key =\u003e [*... stack*, X, Y]",
+      "Doc": "for (data A, recovery id B, signature C, D) recover a public key",
       "DocExtra": "S (top) and R elements of a signature, recovery id and data (bottom) are expected on the stack and used to deriver a public key. All values are big-endian encoded. The signed data must be 32 bytes long.",
       "ImmediateNote": "{uint8 curve index}",
       "Groups": ["Arithmetic"]
@@ -217,7 +217,7 @@
       "Returns": "U",
       "Cost": 1,
       "Size": 1,
-      "Doc": "X == 0 yields 1; else 0",
+      "Doc": "A == 0 yields 1; else 0",
       "Groups": ["Arithmetic"]
     },
     {
@@ -227,7 +227,7 @@
       "Returns": "U",
       "Cost": 1,
       "Size": 1,
-      "Doc": "yields length of byte value X",
+      "Doc": "yields length of byte value A",
       "Groups": ["Arithmetic"]
     },
     {
@@ -237,7 +237,7 @@
       "Returns": "B",
       "Cost": 1,
       "Size": 1,
-      "Doc": "converts uint64 X to big endian bytes",
+      "Doc": "converts uint64 A to big endian bytes",
       "Groups": ["Arithmetic"]
     },
     {
@@ -247,7 +247,7 @@
       "Returns": "U",
       "Cost": 1,
       "Size": 1,
-      "Doc": "converts bytes X as big endian to uint64",
+      "Doc": "converts bytes A as big endian to uint64",
       "DocExtra": "`btoi` fails if the input is longer than 8 bytes.",
       "Groups": ["Arithmetic"]
     },
@@ -298,7 +298,7 @@
       "Returns": "U",
       "Cost": 1,
       "Size": 1,
-      "Doc": "bitwise invert value X",
+      "Doc": "bitwise invert value A",
       "Groups": ["Arithmetic"]
     },
     {
@@ -308,7 +308,7 @@
       "Returns": "UU",
       "Cost": 1,
       "Size": 1,
-      "Doc": "A times B out to 128-bit long result as low (top) and high uint64 values on the stack",
+      "Doc": "A times B as a 128-bit result in two uint64s. X is the high 64 bits, Y is the low",
       "Groups": ["Arithmetic"]
     },
     {
@@ -318,7 +318,7 @@
       "Returns": "UU",
       "Cost": 1,
       "Size": 1,
-      "Doc": "A plus B out to 128-bit long result as sum (top) and carry-bit uint64 values on the stack",
+      "Doc": "A plus B as a 128-bit result. X is the carry-bit, Y is the low-order 64 bits.",
       "Groups": ["Arithmetic"]
     },
     {
@@ -328,7 +328,8 @@
       "Returns": "UUUU",
       "Cost": 20,
       "Size": 1,
-      "Doc": "Pop four uint64 values.  The deepest two are interpreted as a uint128 dividend (deepest value is high word), the top two are interpreted as a uint128 divisor.  Four uint64 values are pushed to the stack. The deepest two are the quotient (deeper value is the high uint64). The top two are the remainder, low bits on top.",
+      "Doc": "W,X = (A,B / C,D); Y,Z = (A,B modulo C,D)",
+      "DocExtra": "The notation J,K indicates that two uint64 values J and K are interpreted as a uint128 value, with J as the high uint64 and K the low.",
       "Groups": ["Arithmetic"]
     },
     {
@@ -347,7 +348,7 @@
       "Returns": "U",
       "Cost": 1,
       "Size": 2,
-      "Doc": "push Ith constant from intcblock to stack",
+      "Doc": "Ith constant from intcblock",
       "ImmediateNote": "{uint8 int constant index}",
       "Groups": ["Loading Values"]
     },
@@ -357,7 +358,7 @@
       "Returns": "U",
       "Cost": 1,
       "Size": 1,
-      "Doc": "push constant 0 from intcblock to stack",
+      "Doc": "constant 0 from intcblock",
       "Groups": ["Loading Values"]
     },
     {
@@ -366,7 +367,7 @@
       "Returns": "U",
       "Cost": 1,
       "Size": 1,
-      "Doc": "push constant 1 from intcblock to stack",
+      "Doc": "constant 1 from intcblock",
       "Groups": ["Loading Values"]
     },
     {
@@ -375,7 +376,7 @@
       "Returns": "U",
       "Cost": 1,
       "Size": 1,
-      "Doc": "push constant 2 from intcblock to stack",
+      "Doc": "constant 2 from intcblock",
       "Groups": ["Loading Values"]
     },
     {
@@ -384,7 +385,7 @@
       "Returns": "U",
       "Cost": 1,
       "Size": 1,
-      "Doc": "push constant 3 from intcblock to stack",
+      "Doc": "constant 3 from intcblock",
       "Groups": ["Loading Values"]
     },
     {
@@ -403,7 +404,7 @@
       "Returns": "B",
       "Cost": 1,
       "Size": 2,
-      "Doc": "push Ith constant from bytecblock to stack",
+      "Doc": "Ith constant from bytecblock",
       "ImmediateNote": "{uint8 byte constant index}",
       "Groups": ["Loading Values"]
     },
@@ -413,7 +414,7 @@
       "Returns": "B",
       "Cost": 1,
       "Size": 1,
-      "Doc": "push constant 0 from bytecblock to stack",
+      "Doc": "constant 0 from bytecblock",
       "Groups": ["Loading Values"]
     },
     {
@@ -422,7 +423,7 @@
       "Returns": "B",
       "Cost": 1,
       "Size": 1,
-      "Doc": "push constant 1 from bytecblock to stack",
+      "Doc": "constant 1 from bytecblock",
       "Groups": ["Loading Values"]
     },
     {
@@ -431,7 +432,7 @@
       "Returns": "B",
       "Cost": 1,
       "Size": 1,
-      "Doc": "push constant 2 from bytecblock to stack",
+      "Doc": "constant 2 from bytecblock",
       "Groups": ["Loading Values"]
     },
     {
@@ -440,7 +441,7 @@
       "Returns": "B",
       "Cost": 1,
       "Size": 1,
-      "Doc": "push constant 3 from bytecblock to stack",
+      "Doc": "constant 3 from bytecblock",
       "Groups": ["Loading Values"]
     },
     {
@@ -449,7 +450,7 @@
       "Returns": "B",
       "Cost": 1,
       "Size": 2,
-      "Doc": "push Nth LogicSig argument to stack",
+      "Doc": "Nth LogicSig argument",
       "ImmediateNote": "{uint8 arg index N}",
       "Groups": ["Loading Values"]
     },
@@ -459,7 +460,7 @@
       "Returns": "B",
       "Cost": 1,
       "Size": 1,
-      "Doc": "push LogicSig argument 0 to stack",
+      "Doc": "LogicSig argument 0",
       "Groups": ["Loading Values"]
     },
     {
@@ -468,7 +469,7 @@
       "Returns": "B",
       "Cost": 1,
       "Size": 1,
-      "Doc": "push LogicSig argument 1 to stack",
+      "Doc": "LogicSig argument 1",
       "Groups": ["Loading Values"]
     },
     {
@@ -477,7 +478,7 @@
       "Returns": "B",
       "Cost": 1,
       "Size": 1,
-      "Doc": "push LogicSig argument 2 to stack",
+      "Doc": "LogicSig argument 2",
       "Groups": ["Loading Values"]
     },
     {
@@ -486,7 +487,7 @@
       "Returns": "B",
       "Cost": 1,
       "Size": 1,
-      "Doc": "push LogicSig argument 3 to stack",
+      "Doc": "LogicSig argument 3",
       "Groups": ["Loading Values"]
     },
     {
@@ -557,10 +558,12 @@
         "Logs",
         "NumLogs",
         "CreatedAssetID",
-        "CreatedApplicationID"
+        "CreatedApplicationID",
+        "LastLog",
+        "StateProofPK"
       ],
-      "ArgEnumTypes": "BUUUUBBBUBBBUUUBUUUBBBUBUUBUBUBBBUUUUBBBBBBBBUBUUUUUUUUUUUBUUU",
-      "Doc": "push field F of current transaction to stack",
+      "ArgEnumTypes": "BUUUUBBBUBBBUUUBUUUBBBUBUUBUBUBBBUUUUBBBBBBBBUBUUUUUUUUUUUBUUUBB",
+      "Doc": "field F of current transaction",
       "DocExtra": "FirstValidTime causes the program to fail. The field is reserved for future use.",
       "ImmediateNote": "{uint8 transaction field index}",
       "Groups": ["Loading Values"]
@@ -571,22 +574,7 @@
       "Returns": ".",
       "Cost": 1,
       "Size": 2,
-      "ArgEnum": [
-        "MinTxnFee",
-        "MinBalance",
-        "MaxTxnLife",
-        "ZeroAddress",
-        "GroupSize",
-        "LogicSigVersion",
-        "Round",
-        "LatestTimestamp",
-        "CurrentApplicationID",
-        "CreatorAddress",
-        "CurrentApplicationAddress",
-        "GroupID"
-      ],
-      "ArgEnumTypes": "UUUBUUUUUBBB",
-      "Doc": "push value from globals to stack",
+      "Doc": "global field F",
       "ImmediateNote": "{uint8 global field index}",
       "Groups": ["Loading Values"]
     },
@@ -658,10 +646,12 @@
         "Logs",
         "NumLogs",
         "CreatedAssetID",
-        "CreatedApplicationID"
+        "CreatedApplicationID",
+        "LastLog",
+        "StateProofPK"
       ],
-      "ArgEnumTypes": "BUUUUBBBUBBBUUUBUUUBBBUBUUBUBUBBBUUUUBBBBBBBBUBUUUUUUUUUUUBUUU",
-      "Doc": "push field F of the Tth transaction in the current group",
+      "ArgEnumTypes": "BUUUUBBBUBBBUUUBUUUBBBUBUUBUBUBBBUUUUBBBBBBBBUBUUUUUUUUUUUBUUUBB",
+      "Doc": "field F of the Tth transaction in the current group",
       "DocExtra": "for notes on transaction fields available, see `txn`. If this transaction is _i_ in the group, `gtxn i field` is equivalent to `txn field`.",
       "ImmediateNote": "{uint8 transaction group index} {uint8 transaction field index}",
       "Groups": ["Loading Values"]
@@ -672,7 +662,7 @@
       "Returns": ".",
       "Cost": 1,
       "Size": 2,
-      "Doc": "copy a value from scratch space to the stack. All scratch spaces are 0 at program start.",
+      "Doc": "Ith scratch space value. All scratch spaces are 0 at program start.",
       "ImmediateNote": "{uint8 position in scratch space to load from}",
       "Groups": ["Loading Values"]
     },
@@ -682,7 +672,7 @@
       "Args": ".",
       "Cost": 1,
       "Size": 2,
-      "Doc": "pop value X. store X to the Ith scratch space",
+      "Doc": "store A to the Ith scratch space",
       "ImmediateNote": "{uint8 position in scratch space to store to}",
       "Groups": ["Loading Values"]
     },
@@ -700,7 +690,7 @@
         "Logs"
       ],
       "ArgEnumTypes": "BBUUB",
-      "Doc": "push Ith value of the array field F of the current transaction",
+      "Doc": "Ith value of the array field F of the current transaction",
       "ImmediateNote": "{uint8 transaction field index} {uint8 transaction field array index}",
       "Groups": ["Loading Values"]
     },
@@ -718,7 +708,7 @@
         "Logs"
       ],
       "ArgEnumTypes": "BBUUB",
-      "Doc": "push Ith value of the array field F from the Tth transaction in the current group",
+      "Doc": "Ith value of the array field F from the Tth transaction in the current group",
       "ImmediateNote": "{uint8 transaction group index} {uint8 transaction field index} {uint8 transaction field array index}",
       "Groups": ["Loading Values"]
     },
@@ -791,10 +781,12 @@
         "Logs",
         "NumLogs",
         "CreatedAssetID",
-        "CreatedApplicationID"
+        "CreatedApplicationID",
+        "LastLog",
+        "StateProofPK"
       ],
-      "ArgEnumTypes": "BUUUUBBBUBBBUUUBUUUBBBUBUUBUBUBBBUUUUBBBBBBBBUBUUUUUUUUUUUBUUU",
-      "Doc": "push field F of the Xth transaction in the current group",
+      "ArgEnumTypes": "BUUUUBBBUBBBUUUBUUUBBBUBUUBUBUBBBUUUUBBBBBBBBUBUUUUUUUUUUUBUUUBB",
+      "Doc": "field F of the Ath transaction in the current group",
       "DocExtra": "for notes on transaction fields available, see `txn`. If top of stack is _i_, `gtxns field` is equivalent to `gtxn _i_ field`. gtxns exists so that _i_ can be calculated, often based on the index of the current transaction.",
       "ImmediateNote": "{uint8 transaction field index}",
       "Groups": ["Loading Values"]
@@ -814,7 +806,7 @@
         "Logs"
       ],
       "ArgEnumTypes": "BBUUB",
-      "Doc": "push Ith value of the array field F from the Xth transaction in the current group",
+      "Doc": "Ith value of the array field F from the Ath transaction in the current group",
       "ImmediateNote": "{uint8 transaction field index} {uint8 transaction field array index}",
       "Groups": ["Loading Values"]
     },
@@ -824,7 +816,7 @@
       "Returns": ".",
       "Cost": 1,
       "Size": 3,
-      "Doc": "push Ith scratch space index of the Tth transaction in the current group",
+      "Doc": "Ith scratch space value of the Tth transaction in the current group",
       "DocExtra": "`gload` fails unless the requested transaction is an ApplicationCall and T \u003c GroupIndex.",
       "ImmediateNote": "{uint8 transaction group index} {uint8 position in scratch space to load from}",
       "Groups": ["Loading Values"]
@@ -836,8 +828,8 @@
       "Returns": ".",
       "Cost": 1,
       "Size": 2,
-      "Doc": "push Ith scratch space index of the Xth transaction in the current group",
-      "DocExtra": "`gloads` fails unless the requested transaction is an ApplicationCall and X \u003c GroupIndex.",
+      "Doc": "Ith scratch space value of the Ath transaction in the current group",
+      "DocExtra": "`gloads` fails unless the requested transaction is an ApplicationCall and A \u003c GroupIndex.",
       "ImmediateNote": "{uint8 position in scratch space to load from}",
       "Groups": ["Loading Values"]
     },
@@ -847,7 +839,7 @@
       "Returns": "U",
       "Cost": 1,
       "Size": 2,
-      "Doc": "push the ID of the asset or application created in the Tth transaction of the current group",
+      "Doc": "ID of the asset or application created in the Tth transaction of the current group",
       "DocExtra": "`gaid` fails unless the requested transaction created an asset or application and T \u003c GroupIndex.",
       "ImmediateNote": "{uint8 transaction group index}",
       "Groups": ["Loading Values"]
@@ -859,8 +851,8 @@
       "Returns": "U",
       "Cost": 1,
       "Size": 1,
-      "Doc": "push the ID of the asset or application created in the Xth transaction of the current group",
-      "DocExtra": "`gaids` fails unless the requested transaction created an asset or application and X \u003c GroupIndex.",
+      "Doc": "ID of the asset or application created in the Ath transaction of the current group",
+      "DocExtra": "`gaids` fails unless the requested transaction created an asset or application and A \u003c GroupIndex.",
       "Groups": ["Loading Values"]
     },
     {
@@ -870,7 +862,7 @@
       "Returns": ".",
       "Cost": 1,
       "Size": 1,
-      "Doc": "copy a value from the Xth scratch space to the stack.  All scratch spaces are 0 at program start.",
+      "Doc": "Ath scratch space value.  All scratch spaces are 0 at program start.",
       "Groups": ["Loading Values"]
     },
     {
@@ -879,7 +871,7 @@
       "Args": "U.",
       "Cost": 1,
       "Size": 1,
-      "Doc": "pop indexes A and B. store B to the Ath scratch space",
+      "Doc": "store B to the Ath scratch space",
       "Groups": ["Loading Values"]
     },
     {
@@ -888,7 +880,7 @@
       "Args": "U",
       "Cost": 1,
       "Size": 3,
-      "Doc": "branch to TARGET if value X is not zero",
+      "Doc": "branch to TARGET if value A is not zero",
       "DocExtra": "The `bnz` instruction opcode 0x40 is followed by two immediate data bytes which are a high byte first and low byte second which together form a 16 bit offset which the instruction may branch to. For a bnz instruction at `pc`, if the last element of the stack is not zero then branch to instruction at `pc + 3 + N`, else proceed to next instruction at `pc + 3`. Branch targets must be aligned instructions. (e.g. Branching to the second byte of a 2 byte op will be rejected.) Starting at v4, the offset is treated as a signed 16 bit integer allowing for backward branches and looping. In prior version (v1 to v3), branch offsets are limited to forward branches only, 0-0x7fff.\n\nAt v2 it became allowed to branch to the end of the program exactly after the last instruction: bnz to byte N (with 0-indexing) was illegal for a TEAL program with N bytes before v2, and is legal after it. This change eliminates the need for a last instruction of no-op as a branch target at the end. (Branching beyond the end--in other words, to a byte larger than N--is still illegal and will cause the program to fail.)",
       "ImmediateNote": "{int16 branch offset, big endian}",
       "Groups": ["Flow Control"]
@@ -899,7 +891,7 @@
       "Args": "U",
       "Cost": 1,
       "Size": 3,
-      "Doc": "branch to TARGET if value X is zero",
+      "Doc": "branch to TARGET if value A is zero",
       "DocExtra": "See `bnz` for details on how branches work. `bz` inverts the behavior of `bnz`.",
       "ImmediateNote": "{int16 branch offset, big endian}",
       "Groups": ["Flow Control"]
@@ -920,7 +912,7 @@
       "Args": "U",
       "Cost": 1,
       "Size": 1,
-      "Doc": "use last value on stack as success value; end",
+      "Doc": "use A as success value; end",
       "Groups": ["Flow Control"]
     },
     {
@@ -929,7 +921,7 @@
       "Args": "U",
       "Cost": 1,
       "Size": 1,
-      "Doc": "immediately fail unless value X is a non-zero number",
+      "Doc": "immediately fail unless A is a non-zero number",
       "Groups": ["Flow Control"]
     },
     {
@@ -938,7 +930,7 @@
       "Args": ".",
       "Cost": 1,
       "Size": 1,
-      "Doc": "discard value X from stack",
+      "Doc": "discard A",
       "Groups": ["Flow Control"]
     },
     {
@@ -948,7 +940,7 @@
       "Returns": "..",
       "Cost": 1,
       "Size": 1,
-      "Doc": "duplicate last value on stack",
+      "Doc": "duplicate A",
       "Groups": ["Flow Control"]
     },
     {
@@ -958,7 +950,7 @@
       "Returns": "....",
       "Cost": 1,
       "Size": 1,
-      "Doc": "duplicate two last values on stack: A, B -\u003e A, B, A, B",
+      "Doc": "duplicate A and B",
       "Groups": ["Flow Control"]
     },
     {
@@ -968,7 +960,7 @@
       "Returns": "..",
       "Cost": 1,
       "Size": 2,
-      "Doc": "push the Nth value from the top of the stack. dig 0 is equivalent to dup",
+      "Doc": "Nth value from the top of the stack. dig 0 is equivalent to dup",
       "ImmediateNote": "{uint8 depth}",
       "Groups": ["Flow Control"]
     },
@@ -979,7 +971,7 @@
       "Returns": "..",
       "Cost": 1,
       "Size": 1,
-      "Doc": "swaps two last values on stack: A, B -\u003e B, A",
+      "Doc": "swaps A and B on stack",
       "Groups": ["Flow Control"]
     },
     {
@@ -989,7 +981,7 @@
       "Returns": ".",
       "Cost": 1,
       "Size": 1,
-      "Doc": "selects one of two values based on top-of-stack: A, B, C -\u003e (if C != 0 then B else A)",
+      "Doc": "selects one of two values based on top-of-stack: B if C != 0, else A",
       "Groups": ["Flow Control"]
     },
     {
@@ -1021,7 +1013,7 @@
       "Returns": "B",
       "Cost": 1,
       "Size": 1,
-      "Doc": "pop two byte-arrays A and B and join them, push the result",
+      "Doc": "join A and B",
       "DocExtra": "`concat` fails if the result would be greater than 4096 bytes.",
       "Groups": ["Arithmetic"]
     },
@@ -1032,9 +1024,9 @@
       "Returns": "B",
       "Cost": 1,
       "Size": 3,
-      "Doc": "pop a byte-array A. For immediate values in 0..255 S and E: extract a range of bytes from A starting at S up to but not including E, push the substring result. If E \u003c S, or either is larger than the array length, the program fails",
+      "Doc": "A range of bytes from A starting at S up to but not including E. If E \u003c S, or either is larger than the array length, the program fails",
       "ImmediateNote": "{uint8 start position} {uint8 end position}",
-      "Groups": ["Byte Array Slicing"]
+      "Groups": ["Byte Array Manipulation"]
     },
     {
       "Opcode": 82,
@@ -1043,8 +1035,8 @@
       "Returns": "B",
       "Cost": 1,
       "Size": 1,
-      "Doc": "pop a byte-array A and two integers B and C. Extract a range of bytes from A starting at B up to but not including C, push the substring result. If C \u003c B, or either is larger than the array length, the program fails",
-      "Groups": ["Byte Array Slicing"]
+      "Doc": "A range of bytes from A starting at B up to but not including C. If C \u003c B, or either is larger than the array length, the program fails",
+      "Groups": ["Byte Array Manipulation"]
     },
     {
       "Opcode": 83,
@@ -1053,7 +1045,7 @@
       "Returns": "U",
       "Cost": 1,
       "Size": 1,
-      "Doc": "pop a target A (integer or byte-array), and index B. Push the Bth bit of A.",
+      "Doc": "Bth bit of (byte-array or integer) A.",
       "DocExtra": "see explanation of bit ordering in setbit",
       "Groups": ["Arithmetic"]
     },
@@ -1064,7 +1056,7 @@
       "Returns": ".",
       "Cost": 1,
       "Size": 1,
-      "Doc": "pop a target A, index B, and bit C. Set the Bth bit of A to C, and push the result",
+      "Doc": "Copy of (byte-array or integer) A, with the Bth bit set to (0 or 1) C",
       "DocExtra": "When A is a uint64, index 0 is the least significant bit. Setting bit 3 to 1 on the integer 0 yields 8, or 2^3. When A is a byte array, index 0 is the leftmost bit of the leftmost byte. Setting bits 0 through 11 to 1 in a 4-byte-array of 0s yields the byte array 0xfff00000. Setting bit 3 to 1 on the 1-byte-array 0x00 yields the byte array 0x10.",
       "Groups": ["Arithmetic"]
     },
@@ -1075,7 +1067,7 @@
       "Returns": "U",
       "Cost": 1,
       "Size": 1,
-      "Doc": "pop a byte-array A and integer B. Extract the Bth byte of A and push it as an integer",
+      "Doc": "Bth byte of A, as an integer",
       "Groups": ["Arithmetic"]
     },
     {
@@ -1085,7 +1077,7 @@
       "Returns": "B",
       "Cost": 1,
       "Size": 1,
-      "Doc": "pop a byte-array A, integer B, and small integer C (between 0..255). Set the Bth byte of A to C, and push the result",
+      "Doc": "Copy of A with the Bth byte set to small integer (between 0..255) C",
       "Groups": ["Arithmetic"]
     },
     {
@@ -1095,9 +1087,9 @@
       "Returns": "B",
       "Cost": 1,
       "Size": 3,
-      "Doc": "pop a byte-array A. For immediate values in 0..255 S and L: extract a range of bytes from A starting at S up to but not including S+L, push the substring result. If L is 0, then extract to the end of the string. If S or S+L is larger than the array length, the program fails",
+      "Doc": "A range of bytes from A starting at S up to but not including S+L. If L is 0, then extract to the end of the string. If S or S+L is larger than the array length, the program fails",
       "ImmediateNote": "{uint8 start position} {uint8 length}",
-      "Groups": ["Byte Array Slicing"]
+      "Groups": ["Byte Array Manipulation"]
     },
     {
       "Opcode": 88,
@@ -1106,8 +1098,8 @@
       "Returns": "B",
       "Cost": 1,
       "Size": 1,
-      "Doc": "pop a byte-array A and two integers B and C. Extract a range of bytes from A starting at B up to but not including B+C, push the substring result. If B+C is larger than the array length, the program fails",
-      "Groups": ["Byte Array Slicing"]
+      "Doc": "A range of bytes from A starting at B up to but not including B+C. If B+C is larger than the array length, the program fails",
+      "Groups": ["Byte Array Manipulation"]
     },
     {
       "Opcode": 89,
@@ -1116,8 +1108,8 @@
       "Returns": "U",
       "Cost": 1,
       "Size": 1,
-      "Doc": "pop a byte-array A and integer B. Extract a range of bytes from A starting at B up to but not including B+2, convert bytes as big endian and push the uint64 result. If B+2 is larger than the array length, the program fails",
-      "Groups": ["Byte Array Slicing"]
+      "Doc": "A uint16 formed from a range of big-endian bytes from A starting at B up to but not including B+2. If B+2 is larger than the array length, the program fails",
+      "Groups": ["Byte Array Manipulation"]
     },
     {
       "Opcode": 90,
@@ -1126,8 +1118,8 @@
       "Returns": "U",
       "Cost": 1,
       "Size": 1,
-      "Doc": "pop a byte-array A and integer B. Extract a range of bytes from A starting at B up to but not including B+4, convert bytes as big endian and push the uint64 result. If B+4 is larger than the array length, the program fails",
-      "Groups": ["Byte Array Slicing"]
+      "Doc": "A uint32 formed from a range of big-endian bytes from A starting at B up to but not including B+4. If B+4 is larger than the array length, the program fails",
+      "Groups": ["Byte Array Manipulation"]
     },
     {
       "Opcode": 91,
@@ -1136,8 +1128,8 @@
       "Returns": "U",
       "Cost": 1,
       "Size": 1,
-      "Doc": "pop a byte-array A and integer B. Extract a range of bytes from A starting at B up to but not including B+8, convert bytes as big endian and push the uint64 result. If B+8 is larger than the array length, the program fails",
-      "Groups": ["Byte Array Slicing"]
+      "Doc": "A uint64 formed from a range of big-endian bytes from A starting at B up to but not including B+8. If B+8 is larger than the array length, the program fails",
+      "Groups": ["Byte Array Manipulation"]
     },
     {
       "Opcode": 96,
@@ -1147,7 +1139,7 @@
       "Cost": 1,
       "Size": 1,
       "Doc": "get balance for account A, in microalgos. The balance is observed after the effects of previous transactions in the group, and after the fee for the current transaction is deducted.",
-      "DocExtra": "params: Before v4, Txn.Accounts offset. Since v4, Txn.Accounts offset or an account address that appears in Txn.Accounts or is Txn.Sender). Return: value.",
+      "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: value.",
       "Groups": ["State Access"]
     },
     {
@@ -1157,8 +1149,8 @@
       "Returns": "U",
       "Cost": 1,
       "Size": 1,
-      "Doc": "check if account A opted in for the application B =\u003e {0 or 1}",
-      "DocExtra": "params: Txn.Accounts offset (or, since v4, an account address that appears in Txn.Accounts or is Txn.Sender), application id (or, since v4, a Txn.ForeignApps offset). Return: 1 if opted in and 0 otherwise.",
+      "Doc": "1 if account A is opted in to application B, else 0",
+      "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: 1 if opted in and 0 otherwise.",
       "Groups": ["State Access"]
     },
     {
@@ -1168,8 +1160,8 @@
       "Returns": ".",
       "Cost": 1,
       "Size": 1,
-      "Doc": "read from account A from local state of the current application key B =\u003e value",
-      "DocExtra": "params: Txn.Accounts offset (or, since v4, an account address that appears in Txn.Accounts or is Txn.Sender), state key. Return: value. The value is zero (of type uint64) if the key does not exist.",
+      "Doc": "local state of the key B in the current application in account A",
+      "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), state key. Return: value. The value is zero (of type uint64) if the key does not exist.",
       "Groups": ["State Access"]
     },
     {
@@ -1179,8 +1171,8 @@
       "Returns": ".U",
       "Cost": 1,
       "Size": 1,
-      "Doc": "read from account A from local state of the application B key C =\u003e [*... stack*, value, 0 or 1]",
-      "DocExtra": "params: Txn.Accounts offset (or, since v4, an account address that appears in Txn.Accounts or is Txn.Sender), application id (or, since v4, a Txn.ForeignApps offset), state key. Return: did_exist flag (top of the stack, 1 if the application existed and 0 otherwise), value. The value is zero (of type uint64) if the key does not exist.",
+      "Doc": "X is the local state of application B, key C in account A. Y is 1 if key existed, else 0",
+      "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset), state key. Return: did_exist flag (top of the stack, 1 if the application and key existed and 0 otherwise), value. The value is zero (of type uint64) if the key does not exist.",
       "Groups": ["State Access"]
     },
     {
@@ -1190,7 +1182,7 @@
       "Returns": ".",
       "Cost": 1,
       "Size": 1,
-      "Doc": "read key A from global state of a current application =\u003e value",
+      "Doc": "global state of the key A in the current application",
       "DocExtra": "params: state key. Return: value. The value is zero (of type uint64) if the key does not exist.",
       "Groups": ["State Access"]
     },
@@ -1201,8 +1193,8 @@
       "Returns": ".U",
       "Cost": 1,
       "Size": 1,
-      "Doc": "read from application A global state key B =\u003e [*... stack*, value, 0 or 1]",
-      "DocExtra": "params: Txn.ForeignApps offset (or, since v4, an application id that appears in Txn.ForeignApps or is the CurrentApplicationID), state key. Return: did_exist flag (top of the stack, 1 if the application existed and 0 otherwise), value. The value is zero (of type uint64) if the key does not exist.",
+      "Doc": "X is the global state of application A, key B. Y is 1 if key existed, else 0",
+      "DocExtra": "params: Txn.ForeignApps offset (or, since v4, an _available_ application id), state key. Return: did_exist flag (top of the stack, 1 if the application and key existed and 0 otherwise), value. The value is zero (of type uint64) if the key does not exist.",
       "Groups": ["State Access"]
     },
     {
@@ -1211,8 +1203,8 @@
       "Args": ".B.",
       "Cost": 1,
       "Size": 1,
-      "Doc": "write to account specified by A to local state of a current application key B with value C",
-      "DocExtra": "params: Txn.Accounts offset (or, since v4, an account address that appears in Txn.Accounts or is Txn.Sender), state key, value.",
+      "Doc": "write C to key B in account A's local state of the current application",
+      "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), state key, value.",
       "Groups": ["State Access"]
     },
     {
@@ -1221,7 +1213,7 @@
       "Args": "B.",
       "Cost": 1,
       "Size": 1,
-      "Doc": "write key A and value B to global state of the current application",
+      "Doc": "write B to key A in the global state of the current application",
       "Groups": ["State Access"]
     },
     {
@@ -1230,8 +1222,8 @@
       "Args": ".B",
       "Cost": 1,
       "Size": 1,
-      "Doc": "delete from account A local state key B of the current application",
-      "DocExtra": "params: Txn.Accounts offset (or, since v4, an account address that appears in Txn.Accounts or is Txn.Sender), state key.\n\nDeleting a key which is already absent has no effect on the application local state. (In particular, it does _not_ cause the program to fail.)",
+      "Doc": "delete key B from account A's local state of the current application",
+      "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), state key.\n\nDeleting a key which is already absent has no effect on the application local state. (In particular, it does _not_ cause the program to fail.)",
       "Groups": ["State Access"]
     },
     {
@@ -1240,7 +1232,7 @@
       "Args": "B",
       "Cost": 1,
       "Size": 1,
-      "Doc": "delete key A from a global state of the current application",
+      "Doc": "delete key A from the global state of the current application",
       "DocExtra": "params: state key.\n\nDeleting a key which is already absent has no effect on the application global state. (In particular, it does _not_ cause the program to fail.)",
       "Groups": ["State Access"]
     },
@@ -1253,8 +1245,8 @@
       "Size": 2,
       "ArgEnum": ["AssetBalance", "AssetFrozen"],
       "ArgEnumTypes": "UU",
-      "Doc": "read from account A and asset B holding field X (imm arg) =\u003e {0 or 1 (top), value}",
-      "DocExtra": "params: Txn.Accounts offset (or, since v4, an account address that appears in Txn.Accounts or is Txn.Sender), asset id (or, since v4, a Txn.ForeignAssets offset). Return: did_exist flag (1 if the asset existed and 0 otherwise), value.",
+      "Doc": "X is field F from account A's holding of asset B. Y is 1 if A is opted into B, else 0",
+      "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ address), asset id (or, since v4, a Txn.ForeignAssets offset). Return: did_exist flag (1 if the asset existed and 0 otherwise), value.",
       "ImmediateNote": "{uint8 asset holding field index}",
       "Groups": ["State Access"]
     },
@@ -1280,8 +1272,8 @@
         "AssetCreator"
       ],
       "ArgEnumTypes": "UUUBBBBBBBBB",
-      "Doc": "read from asset A params field X (imm arg) =\u003e {0 or 1 (top), value}",
-      "DocExtra": "params: Before v4, Txn.ForeignAssets offset. Since v4, Txn.ForeignAssets offset or an asset id that appears in Txn.ForeignAssets. Return: did_exist flag (1 if the asset existed and 0 otherwise), value.",
+      "Doc": "X is field F from asset A. Y is 1 if A exists, else 0",
+      "DocExtra": "params: Txn.ForeignAssets offset (or, since v4, an _available_ asset id. Return: did_exist flag (1 if the asset existed and 0 otherwise), value.",
       "ImmediateNote": "{uint8 asset params field index}",
       "Groups": ["State Access"]
     },
@@ -1304,11 +1296,22 @@
         "AppAddress"
       ],
       "ArgEnumTypes": "BBUUUUUBB",
-      "Doc": "read from app A params field X (imm arg) =\u003e {0 or 1 (top), value}",
-      "DocExtra": "params: Txn.ForeignApps offset or an app id that appears in Txn.ForeignApps. Return: did_exist flag (1 if the application existed and 0 otherwise), value.",
+      "Doc": "X is field F from app A. Y is 1 if A exists, else 0",
+      "DocExtra": "params: Txn.ForeignApps offset or an _available_ app id. Return: did_exist flag (1 if the application existed and 0 otherwise), value.",
       "ImmediateNote": "{uint8 app params field index}",
       "Groups": ["State Access"]
     },
+    {
+      "Opcode": 115,
+      "Name": "acct_params_get",
+      "Args": ".",
+      "Returns": ".U",
+      "Cost": 1,
+      "Size": 2,
+      "Doc": "X is field F from account A. Y is 1 if A owns positive algos, else 0",
+      "ImmediateNote": "{uint8 account params field index}",
+      "Groups": ["State Access"]
+    },
     {
       "Opcode": 120,
       "Name": "min_balance",
@@ -1317,7 +1320,7 @@
       "Cost": 1,
       "Size": 1,
       "Doc": "get minimum required balance for account A, in microalgos. Required balance is affected by [ASA](https://developer.algorand.org/docs/features/asa/#assets-overview) and [App](https://developer.algorand.org/docs/features/asc1/stateful/#minimum-balance-requirement-for-a-smart-contract) usage. When creating or opting into an app, the minimum balance grows before the app code runs, therefore the increase is visible there. When deleting or closing out, the minimum balance decreases after the app executes.",
-      "DocExtra": "params: Before v4, Txn.Accounts offset. Since v4, Txn.Accounts offset or an account address that appears in Txn.Accounts or is Txn.Sender). Return: value.",
+      "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: value.",
       "Groups": ["State Access"]
     },
     {
@@ -1326,7 +1329,7 @@
       "Returns": "B",
       "Cost": 1,
       "Size": 0,
-      "Doc": "push the following program bytes to the stack",
+      "Doc": "immediate BYTES",
       "DocExtra": "pushbytes args are not added to the bytecblock during assembly processes",
       "ImmediateNote": "{varuint length} {bytes}",
       "Groups": ["Loading Values"]
@@ -1337,7 +1340,7 @@
       "Returns": "U",
       "Cost": 1,
       "Size": 0,
-      "Doc": "push immediate UINT to the stack as an integer",
+      "Doc": "immediate UINT",
       "DocExtra": "pushint args are not added to the intcblock during assembly processes",
       "ImmediateNote": "{varuint int}",
       "Groups": ["Loading Values"]
@@ -1388,7 +1391,7 @@
       "Returns": "U",
       "Cost": 4,
       "Size": 1,
-      "Doc": "The largest integer B such that B^2 \u003c= X",
+      "Doc": "The largest integer I such that I^2 \u003c= A",
       "Groups": ["Arithmetic"]
     },
     {
@@ -1398,7 +1401,7 @@
       "Returns": "U",
       "Cost": 1,
       "Size": 1,
-      "Doc": "The highest set bit in X. If X is a byte-array, it is interpreted as a big-endian unsigned integer. bitlen of 0 is 0, bitlen of 8 is 4",
+      "Doc": "The highest set bit in A. If A is a byte-array, it is interpreted as a big-endian unsigned integer. bitlen of 0 is 0, bitlen of 8 is 4",
       "DocExtra": "bitlen interprets arrays as big-endian integers, unlike setbit/getbit",
       "Groups": ["Arithmetic"]
     },
@@ -1419,7 +1422,28 @@
       "Returns": "UU",
       "Cost": 10,
       "Size": 1,
-      "Doc": "A raised to the Bth power as a 128-bit long result as low (top) and high uint64 values on the stack. Fail if A == B == 0 or if the results exceeds 2^128-1",
+      "Doc": "A raised to the Bth power as a 128-bit result in two uint64s. X is the high 64 bits, Y is the low. Fail if A == B == 0 or if the results exceeds 2^128-1",
+      "Groups": ["Arithmetic"]
+    },
+    {
+      "Opcode": 150,
+      "Name": "bsqrt",
+      "Args": "B",
+      "Returns": "B",
+      "Cost": 40,
+      "Size": 1,
+      "Doc": "The largest integer I such that I^2 \u003c= A. A and I are interpreted as big-endian unsigned integers",
+      "Groups": ["Byte Array Arithmetic"]
+    },
+    {
+      "Opcode": 151,
+      "Name": "divw",
+      "Args": "UUU",
+      "Returns": "U",
+      "Cost": 1,
+      "Size": 1,
+      "Doc": "A,B / C. Fail if C == 0 or if result overflows.",
+      "DocExtra": "The notation A,B indicates that A and B are interpreted as a uint128 value, with A as the high uint64 and B the low.",
       "Groups": ["Arithmetic"]
     },
     {
@@ -1429,7 +1453,7 @@
       "Returns": "B",
       "Cost": 10,
       "Size": 1,
-      "Doc": "A plus B, where A and B are byte-arrays interpreted as big-endian unsigned integers",
+      "Doc": "A plus B. A and B are interpreted as big-endian unsigned integers",
       "Groups": ["Byte Array Arithmetic"]
     },
     {
@@ -1439,7 +1463,7 @@
       "Returns": "B",
       "Cost": 10,
       "Size": 1,
-      "Doc": "A minus B, where A and B are byte-arrays interpreted as big-endian unsigned integers. Fail on underflow.",
+      "Doc": "A minus B. A and B are interpreted as big-endian unsigned integers. Fail on underflow.",
       "Groups": ["Byte Array Arithmetic"]
     },
     {
@@ -1449,7 +1473,7 @@
       "Returns": "B",
       "Cost": 20,
       "Size": 1,
-      "Doc": "A divided by B (truncated division), where A and B are byte-arrays interpreted as big-endian unsigned integers. Fail if B is zero.",
+      "Doc": "A divided by B (truncated division). A and B are interpreted as big-endian unsigned integers. Fail if B is zero.",
       "Groups": ["Byte Array Arithmetic"]
     },
     {
@@ -1459,7 +1483,7 @@
       "Returns": "B",
       "Cost": 20,
       "Size": 1,
-      "Doc": "A times B, where A and B are byte-arrays interpreted as big-endian unsigned integers.",
+      "Doc": "A times B. A and B are interpreted as big-endian unsigned integers.",
       "Groups": ["Byte Array Arithmetic"]
     },
     {
@@ -1469,7 +1493,7 @@
       "Returns": "U",
       "Cost": 1,
       "Size": 1,
-      "Doc": "A is less than B, where A and B are byte-arrays interpreted as big-endian unsigned integers =\u003e { 0 or 1}",
+      "Doc": "1 if A is less than B, else 0. A and B are interpreted as big-endian unsigned integers",
       "Groups": ["Byte Array Arithmetic"]
     },
     {
@@ -1479,7 +1503,7 @@
       "Returns": "U",
       "Cost": 1,
       "Size": 1,
-      "Doc": "A is greater than B, where A and B are byte-arrays interpreted as big-endian unsigned integers =\u003e { 0 or 1}",
+      "Doc": "1 if A is greater than B, else 0. A and B are interpreted as big-endian unsigned integers",
       "Groups": ["Byte Array Arithmetic"]
     },
     {
@@ -1489,7 +1513,7 @@
       "Returns": "U",
       "Cost": 1,
       "Size": 1,
-      "Doc": "A is less than or equal to B, where A and B are byte-arrays interpreted as big-endian unsigned integers =\u003e { 0 or 1}",
+      "Doc": "1 if A is less than or equal to B, else 0. A and B are interpreted as big-endian unsigned integers",
       "Groups": ["Byte Array Arithmetic"]
     },
     {
@@ -1499,7 +1523,7 @@
       "Returns": "U",
       "Cost": 1,
       "Size": 1,
-      "Doc": "A is greater than or equal to B, where A and B are byte-arrays interpreted as big-endian unsigned integers =\u003e { 0 or 1}",
+      "Doc": "1 if A is greater than or equal to B, else 0. A and B are interpreted as big-endian unsigned integers",
       "Groups": ["Byte Array Arithmetic"]
     },
     {
@@ -1509,7 +1533,7 @@
       "Returns": "U",
       "Cost": 1,
       "Size": 1,
-      "Doc": "A is equals to B, where A and B are byte-arrays interpreted as big-endian unsigned integers =\u003e { 0 or 1}",
+      "Doc": "1 if A is equal to B, else 0. A and B are interpreted as big-endian unsigned integers",
       "Groups": ["Byte Array Arithmetic"]
     },
     {
@@ -1519,7 +1543,7 @@
       "Returns": "U",
       "Cost": 1,
       "Size": 1,
-      "Doc": "A is not equal to B, where A and B are byte-arrays interpreted as big-endian unsigned integers =\u003e { 0 or 1}",
+      "Doc": "0 if A is equal to B, else 1. A and B are interpreted as big-endian unsigned integers",
       "Groups": ["Byte Array Arithmetic"]
     },
     {
@@ -1529,7 +1553,7 @@
       "Returns": "B",
       "Cost": 20,
       "Size": 1,
-      "Doc": "A modulo B, where A and B are byte-arrays interpreted as big-endian unsigned integers. Fail if B is zero.",
+      "Doc": "A modulo B. A and B are interpreted as big-endian unsigned integers. Fail if B is zero.",
       "Groups": ["Byte Array Arithmetic"]
     },
     {
@@ -1539,7 +1563,7 @@
       "Returns": "B",
       "Cost": 6,
       "Size": 1,
-      "Doc": "A bitwise-or B, where A and B are byte-arrays, zero-left extended to the greater of their lengths",
+      "Doc": "A bitwise-or B. A and B are zero-left extended to the greater of their lengths",
       "Groups": ["Byte Array Logic"]
     },
     {
@@ -1549,7 +1573,7 @@
       "Returns": "B",
       "Cost": 6,
       "Size": 1,
-      "Doc": "A bitwise-and B, where A and B are byte-arrays, zero-left extended to the greater of their lengths",
+      "Doc": "A bitwise-and B. A and B are zero-left extended to the greater of their lengths",
       "Groups": ["Byte Array Logic"]
     },
     {
@@ -1559,7 +1583,7 @@
       "Returns": "B",
       "Cost": 6,
       "Size": 1,
-      "Doc": "A bitwise-xor B, where A and B are byte-arrays, zero-left extended to the greater of their lengths",
+      "Doc": "A bitwise-xor B. A and B are zero-left extended to the greater of their lengths",
       "Groups": ["Byte Array Logic"]
     },
     {
@@ -1569,7 +1593,7 @@
       "Returns": "B",
       "Cost": 4,
       "Size": 1,
-      "Doc": "X with all bits inverted",
+      "Doc": "A with all bits inverted",
       "Groups": ["Byte Array Logic"]
     },
     {
@@ -1579,7 +1603,7 @@
       "Returns": "B",
       "Cost": 1,
       "Size": 1,
-      "Doc": "push a byte-array of length X, containing all zero bytes",
+      "Doc": "zero filled byte-array of length A",
       "Groups": ["Loading Values"]
     },
     {
@@ -1588,7 +1612,7 @@
       "Args": "B",
       "Cost": 1,
       "Size": 1,
-      "Doc": "write bytes to log state of the current application",
+      "Doc": "write A to log state of the current application",
       "DocExtra": "`log` fails if called more than MaxLogCalls times in a program, or if the sum of logged bytes exceeds 1024 bytes.",
       "Groups": ["State Access"]
     },
@@ -1597,8 +1621,8 @@
       "Name": "itxn_begin",
       "Cost": 1,
       "Size": 1,
-      "Doc": "Begin preparation of a new inner transaction",
-      "DocExtra": "`itxn_begin` initializes Sender to the application address; Fee to the minimum allowable, taking into account MinTxnFee and credit from overpaying in earlier transactions; FirstValid/LastValid to the values in the top-level transaction, and all other fields to zero values.",
+      "Doc": "begin preparation of a new inner transaction in a new transaction group",
+      "DocExtra": "`itxn_begin` initializes Sender to the application address; Fee to the minimum allowable, taking into account MinTxnFee and credit from overpaying in earlier transactions; FirstValid/LastValid to the values in the invoking transaction, and all other fields to zero or empty values.",
       "Groups": ["Inner Transactions"]
     },
     {
@@ -1607,8 +1631,75 @@
       "Args": ".",
       "Cost": 1,
       "Size": 2,
-      "Doc": "Set field F of the current inner transaction to X",
-      "DocExtra": "`itxn_field` fails if X is of the wrong type for F, including a byte array of the wrong size for use as an address when F is an address field. `itxn_field` also fails if X is an account or asset that does not appear in `txn.Accounts` or `txn.ForeignAssets` of the top-level transaction. (Setting addresses in asset creation are exempted from this requirement.)",
+      "ArgEnum": [
+        "Sender",
+        "Fee",
+        "FirstValid",
+        "FirstValidTime",
+        "LastValid",
+        "Note",
+        "Lease",
+        "Receiver",
+        "Amount",
+        "CloseRemainderTo",
+        "VotePK",
+        "SelectionPK",
+        "VoteFirst",
+        "VoteLast",
+        "VoteKeyDilution",
+        "Type",
+        "TypeEnum",
+        "XferAsset",
+        "AssetAmount",
+        "AssetSender",
+        "AssetReceiver",
+        "AssetCloseTo",
+        "GroupIndex",
+        "TxID",
+        "ApplicationID",
+        "OnCompletion",
+        "ApplicationArgs",
+        "NumAppArgs",
+        "Accounts",
+        "NumAccounts",
+        "ApprovalProgram",
+        "ClearStateProgram",
+        "RekeyTo",
+        "ConfigAsset",
+        "ConfigAssetTotal",
+        "ConfigAssetDecimals",
+        "ConfigAssetDefaultFrozen",
+        "ConfigAssetUnitName",
+        "ConfigAssetName",
+        "ConfigAssetURL",
+        "ConfigAssetMetadataHash",
+        "ConfigAssetManager",
+        "ConfigAssetReserve",
+        "ConfigAssetFreeze",
+        "ConfigAssetClawback",
+        "FreezeAsset",
+        "FreezeAssetAccount",
+        "FreezeAssetFrozen",
+        "Assets",
+        "NumAssets",
+        "Applications",
+        "NumApplications",
+        "GlobalNumUint",
+        "GlobalNumByteSlice",
+        "LocalNumUint",
+        "LocalNumByteSlice",
+        "ExtraProgramPages",
+        "Nonparticipation",
+        "Logs",
+        "NumLogs",
+        "CreatedAssetID",
+        "CreatedApplicationID",
+        "LastLog",
+        "StateProofPK"
+      ],
+      "ArgEnumTypes": "BUUUUBBBUBBBUUUBUUUBBBUBUUBUBUBBBUUUUBBBBBBBBUBUUUUUUUUUUUBUUUBB",
+      "Doc": "set field F of the current inner transaction to A",
+      "DocExtra": "`itxn_field` fails if A is of the wrong type for F, including a byte array of the wrong size for use as an address when F is an address field. `itxn_field` also fails if A is an account, asset, or app that is not _available_, or an attempt is made extend an array field beyond the limit imposed by consensus parameters. (Addresses set into asset params of acfg transactions need not be _available_.)",
       "ImmediateNote": "{uint8 transaction field index}",
       "Groups": ["Inner Transactions"]
     },
@@ -1617,7 +1708,8 @@
       "Name": "itxn_submit",
       "Cost": 1,
       "Size": 1,
-      "Doc": "Execute the current inner transaction. Fail if 16 inner transactions have already been executed, or if the transaction itself fails.",
+      "Doc": "execute the current inner transaction group. Fail if executing this group would exceed the inner transaction limit, or if any transaction in the group fails.",
+      "DocExtra": "`itxn_submit` resets the current transaction so that it can not be resubmitted. A new `itxn_begin` is required to prepare another inner transaction.",
       "Groups": ["Inner Transactions"]
     },
     {
@@ -1626,7 +1718,74 @@
       "Returns": ".",
       "Cost": 1,
       "Size": 2,
-      "Doc": "push field F of the last inner transaction to stack",
+      "ArgEnum": [
+        "Sender",
+        "Fee",
+        "FirstValid",
+        "FirstValidTime",
+        "LastValid",
+        "Note",
+        "Lease",
+        "Receiver",
+        "Amount",
+        "CloseRemainderTo",
+        "VotePK",
+        "SelectionPK",
+        "VoteFirst",
+        "VoteLast",
+        "VoteKeyDilution",
+        "Type",
+        "TypeEnum",
+        "XferAsset",
+        "AssetAmount",
+        "AssetSender",
+        "AssetReceiver",
+        "AssetCloseTo",
+        "GroupIndex",
+        "TxID",
+        "ApplicationID",
+        "OnCompletion",
+        "ApplicationArgs",
+        "NumAppArgs",
+        "Accounts",
+        "NumAccounts",
+        "ApprovalProgram",
+        "ClearStateProgram",
+        "RekeyTo",
+        "ConfigAsset",
+        "ConfigAssetTotal",
+        "ConfigAssetDecimals",
+        "ConfigAssetDefaultFrozen",
+        "ConfigAssetUnitName",
+        "ConfigAssetName",
+        "ConfigAssetURL",
+        "ConfigAssetMetadataHash",
+        "ConfigAssetManager",
+        "ConfigAssetReserve",
+        "ConfigAssetFreeze",
+        "ConfigAssetClawback",
+        "FreezeAsset",
+        "FreezeAssetAccount",
+        "FreezeAssetFrozen",
+        "Assets",
+        "NumAssets",
+        "Applications",
+        "NumApplications",
+        "GlobalNumUint",
+        "GlobalNumByteSlice",
+        "LocalNumUint",
+        "LocalNumByteSlice",
+        "ExtraProgramPages",
+        "Nonparticipation",
+        "Logs",
+        "NumLogs",
+        "CreatedAssetID",
+        "CreatedApplicationID",
+        "LastLog",
+        "StateProofPK"
+      ],
+      "ArgEnumTypes": "BUUUUBBBUBBBUUUBUUUBBBUBUUBUBUBBBUUUUBBBBBBBBUBUUUUUUUUUUUBUUUBB",
+      "Doc": "field F of the last inner transaction",
       "ImmediateNote": "{uint8 transaction field index}",
       "Groups": ["Inner Transactions"]
     },
@@ -1636,10 +1795,122 @@
       "Returns": ".",
       "Cost": 1,
       "Size": 3,
-      "Doc": "push Ith value of the array field F of the last inner transaction to stack",
+      "ArgEnum": [
+        "ApplicationArgs",
+        "Accounts",
+        "Assets",
+        "Applications",
+        "Logs"
+      ],
+      "ArgEnumTypes": "BBUUB",
+      "Doc": "Ith value of the array field F of the last inner transaction",
       "ImmediateNote": "{uint8 transaction field index} {uint8 transaction field array index}",
       "Groups": ["Inner Transactions"]
     },
+    {
+      "Opcode": 182,
+      "Name": "itxn_next",
+      "Cost": 1,
+      "Size": 1,
+      "Doc": "begin preparation of a new inner transaction in the same transaction group",
+      "DocExtra": "`itxn_next` initializes the transaction exactly as `itxn_begin` does",
+      "Groups": ["Inner Transactions"]
+    },
+    {
+      "Opcode": 183,
+      "Name": "gitxn",
+      "Returns": ".",
+      "Cost": 1,
+      "Size": 3,
+      "ArgEnum": [
+        "Sender",
+        "Fee",
+        "FirstValid",
+        "FirstValidTime",
+        "LastValid",
+        "Note",
+        "Lease",
+        "Receiver",
+        "Amount",
+        "CloseRemainderTo",
+        "VotePK",
+        "SelectionPK",
+        "VoteFirst",
+        "VoteLast",
+        "VoteKeyDilution",
+        "Type",
+        "TypeEnum",
+        "XferAsset",
+        "AssetAmount",
+        "AssetSender",
+        "AssetReceiver",
+        "AssetCloseTo",
+        "GroupIndex",
+        "TxID",
+        "ApplicationID",
+        "OnCompletion",
+        "ApplicationArgs",
+        "NumAppArgs",
+        "Accounts",
+        "NumAccounts",
+        "ApprovalProgram",
+        "ClearStateProgram",
+        "RekeyTo",
+        "ConfigAsset",
+        "ConfigAssetTotal",
+        "ConfigAssetDecimals",
+        "ConfigAssetDefaultFrozen",
+        "ConfigAssetUnitName",
+        "ConfigAssetName",
+        "ConfigAssetURL",
+        "ConfigAssetMetadataHash",
+        "ConfigAssetManager",
+        "ConfigAssetReserve",
+        "ConfigAssetFreeze",
+        "ConfigAssetClawback",
+        "FreezeAsset",
+        "FreezeAssetAccount",
+        "FreezeAssetFrozen",
+        "Assets",
+        "NumAssets",
+        "Applications",
+        "NumApplications",
+        "GlobalNumUint",
+        "GlobalNumByteSlice",
+        "LocalNumUint",
+        "LocalNumByteSlice",
+        "ExtraProgramPages",
+        "Nonparticipation",
+        "Logs",
+        "NumLogs",
+        "CreatedAssetID",
+        "CreatedApplicationID",
+        "LastLog",
+        "StateProofPK"
+      ],
+      "ArgEnumTypes": "BUUUUBBBUBBBUUUBUUUBBBUBUUBUBUBBBUUUUBBBBBBBBUBUUUUUUUUUUUBUUUBB",
+      "Doc": "field F of the Tth transaction in the last inner group submitted",
+      "ImmediateNote": "{uint8 transaction group index} {uint8 transaction field index}",
+      "Groups": ["Inner Transactions"]
+    },
+    {
+      "Opcode": 184,
+      "Name": "gitxna",
+      "Returns": ".",
+      "Cost": 1,
+      "Size": 4,
+      "ArgEnum": [
+        "ApplicationArgs",
+        "Accounts",
+        "Assets",
+        "Applications",
+        "Logs"
+      ],
+      "ArgEnumTypes": "BBUUB",
+      "Doc": "Ith value of the array field F from the Tth transaction in the last inner group submitted",
+      "ImmediateNote": "{uint8 transaction group index} {uint8 transaction field index} {uint8 transaction field array index}",
+      "Groups": ["Inner Transactions"]
+    },
     {
       "Opcode": 192,
       "Name": "txnas",
@@ -1655,7 +1926,7 @@
         "Logs"
       ],
       "ArgEnumTypes": "BBUUB",
-      "Doc": "push Xth value of the array field F of the current transaction",
+      "Doc": "Ath value of the array field F of the current transaction",
       "ImmediateNote": "{uint8 transaction field index}",
       "Groups": ["Loading Values"]
     },
@@ -1674,7 +1945,7 @@
         "Logs"
       ],
       "ArgEnumTypes": "BBUUB",
-      "Doc": "push Xth value of the array field F from the Tth transaction in the current group",
+      "Doc": "Ath value of the array field F from the Tth transaction in the current group",
       "ImmediateNote": "{uint8 transaction group index} {uint8 transaction field index}",
       "Groups": ["Loading Values"]
     },
@@ -1693,7 +1964,7 @@
         "Logs"
       ],
       "ArgEnumTypes": "BBUUB",
-      "Doc": "pop an index A and an index B. push Bth value of the array field F from the Ath transaction in the current group",
+      "Doc": "Bth value of the array field F from the Ath transaction in the current group",
       "ImmediateNote": "{uint8 transaction field index}",
       "Groups": ["Loading Values"]
     },
@@ -1704,8 +1975,40 @@
       "Returns": "B",
       "Cost": 1,
       "Size": 1,
-      "Doc": "push Xth LogicSig argument to stack",
+      "Doc": "Ath LogicSig argument",
+      "Groups": ["Loading Values"]
+    },
+    {
+      "Opcode": 196,
+      "Name": "gloadss",
+      "Args": "UU",
+      "Returns": ".",
+      "Cost": 1,
+      "Size": 1,
+      "Doc": "Bth scratch space value of the Ath transaction in the current group",
       "Groups": ["Loading Values"]
+    },
+    {
+      "Opcode": 197,
+      "Name": "itxnas",
+      "Args": "U",
+      "Returns": ".",
+      "Cost": 1,
+      "Size": 2,
+      "Doc": "Ath value of the array field F of the last inner transaction",
+      "ImmediateNote": "{uint8 transaction field index}",
+      "Groups": ["Inner Transactions"]
+    },
+    {
+      "Opcode": 198,
+      "Name": "gitxnas",
+      "Args": "U",
+      "Returns": ".",
+      "Cost": 1,
+      "Size": 3,
+      "Doc": "Ath value of the array field F from the Tth transaction in the last inner group submitted",
+      "ImmediateNote": "{uint8 transaction group index} {uint8 transaction field index}",
+      "Groups": ["Inner Transactions"]
     }
   ]
 }
diff --git a/src/logicTemplates/dynamicfee.js b/src/logicTemplates/dynamicfee.js
index 7cce542..0f71e01 100644
--- a/src/logicTemplates/dynamicfee.js
+++ b/src/logicTemplates/dynamicfee.js
@@ -13,6 +13,8 @@ class DynamicFee {
    * specifying the fee. The fee will be determined at the moment of
    * transfer.
    *
+   * @deprecated This feature will be removed in v2.
+   *
    * Constructor Parameters:
    * @param {string} receiver: address to receive the assets
    * @param {int} amount: amount of assets to transfer
@@ -102,6 +104,9 @@ class DynamicFee {
 /**
  * signDynamicFee returns the main transaction and signed logic needed to complete the transfer.
  * These should be sent to the fee payer, who can use GetDynamicFeeTransactions
+ *
+ * @deprecated This feature will be removed in v2.
+ *
  * @param {Uint8Array} contract: the bytearray representing the contract
  * @param {Uint8Array} secretKey: the secret key for building the logic sig
  * @param {string} genesisHash: the genesisHash to use for the txn
@@ -142,6 +147,9 @@ function signDynamicFee(contract, secretKey, genesisHash) {
  * getDynamicFeeTransactions creates and signs the secondary dynamic fee transaction, updates
  * transaction fields, and signs as the fee payer; it returns both
  * transactions as bytes suitable for sendRaw.
+ *
+ * @deprecated This feature will be removed in v2.
+ *
  * Parameters:
  * @param {dict} txn - main transaction from payer's signDynamicFee output (a dict of constructor arguments, NOT a transaction.Transaction)
  * @param {LogicSig} lsig - the signed logic received from the payer's signDynamicFee output
diff --git a/src/logicTemplates/htlc.js b/src/logicTemplates/htlc.js
index 394c08c..bef0b60 100644
--- a/src/logicTemplates/htlc.js
+++ b/src/logicTemplates/htlc.js
@@ -17,6 +17,8 @@ class HTLC {
    * 2. To owner if txn.FirstValid > expiry_round
    * ...
    *
+   * @deprecated This feature will be removed in v2.
+   *
    *Parameters
    *----------
    * @param {string} owner: an address that can receive the asset after the expiry round
@@ -97,6 +99,9 @@ class HTLC {
 
 /**
  *  signTransactionWithHTLCUnlock accepts a transaction, such as a payment, and builds the HTLC-unlocking signature around that transaction
+ *
+ * @deprecated This feature will be removed in v2.
+ *
  * @param {Uint8Array} contract : byte representation of the HTLC
  * @param {Object} txn dictionary containing constructor arguments for a transaction
  * @param {string} preImageAsBase64 : preimage of the hash as base64 string
diff --git a/src/logicTemplates/limitorder.js b/src/logicTemplates/limitorder.js
index c331dbb..c724e84 100644
--- a/src/logicTemplates/limitorder.js
+++ b/src/logicTemplates/limitorder.js
@@ -28,6 +28,9 @@ class LimitOrder {
    *
    * close-out case:
    * txn alone, close out value after timeout
+   *
+   * @deprecated This class will be removed in v2.
+   *
    * Constructor Parameters:
    * @param {string} owner: the address to refund funds to on timeout
    * @param {int} assetid: the ID of the transferred asset
@@ -117,6 +120,9 @@ class LimitOrder {
 
 /**
  * returns a group transactions array which transfer funds according to the contract's ratio
+ *
+ * @deprecated This feature will be removed in v2.
+ *
  * @param {Uint8Array} contract: byteform of the contract from the payer
  * @param {int} assetAmount: the amount of assets to be sent
  * @param {int} microAlgoAmount: number of microAlgos to transfer
diff --git a/src/logicTemplates/periodicpayment.js b/src/logicTemplates/periodicpayment.js
index 1baeada..d001132 100644
--- a/src/logicTemplates/periodicpayment.js
+++ b/src/logicTemplates/periodicpayment.js
@@ -17,6 +17,8 @@ class PeriodicPayment {
    * After expiryRound, all remaining funds in the escrow
    * are available to receiver.
    *
+   * @deprecated This class will be removed in v2.
+   *
    * Constructor Parameters:
    * @param {string} receiver: address which is authorized to receive withdrawals
    * @param {int} amount: the amount to send each period
@@ -124,6 +126,9 @@ class PeriodicPayment {
 
 /**
  * getPeriodicPaymentWithdrawalTransaction returns a signed transaction extracting funds form the contract
+ *
+ * @deprecated This feature will be removed in v2.
+ *
  * @param {Uint8Array} contract: the bytearray defining the contract, received from the payer
  * @param {int} fee: the fee per byte for the transaction
  * @param {int} firstValid: the first round on which the txn will be valid
diff --git a/src/logicTemplates/split.js b/src/logicTemplates/split.js
index 0d4d1ae..2f0ddcd 100644
--- a/src/logicTemplates/split.js
+++ b/src/logicTemplates/split.js
@@ -21,6 +21,9 @@ class Split {
    * (CloseRemainderTo must be zero.)
    *
    * After expiryRound passes, all funds can be refunded to owner.
+   *
+   * @deprecated This feature will be removed in v2.
+   *
    * Constructor Parameters:
    * @param {string} owner: the address to refund funds to on timeout
    * @param {string} receiverOne: the first recipient in the split account
@@ -118,6 +121,9 @@ class Split {
 }
 /**
  * returns a group transactions array which transfer funds according to the contract's ratio
+ *
+ * @deprecated This feature will be removed in v2.
+ *
  * @param {Uint8Array} contract: bytes representing the contract in question
  * @param {int} amount: the amount to be transferred
  * @param {int} firstRound: the first round on which the transaction group will be valid
diff --git a/src/main.ts b/src/main.ts
index 29195b2..4d1c806 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -135,6 +135,7 @@ export {
   decodeAddress,
   getApplicationAddress,
 } from './encoding/address';
+export { bytesToBigInt, bigIntToBytes } from './encoding/bigint';
 export { encodeUint64, decodeUint64 } from './encoding/uint64';
 export { default as generateAccount } from './account';
 export * as modelsv2 from './client/v2/algod/models/types';
diff --git a/src/makeTxn.ts b/src/makeTxn.ts
index 150d916..d402db7 100644
--- a/src/makeTxn.ts
+++ b/src/makeTxn.ts
@@ -141,8 +141,8 @@ export function makePaymentTxnWithSuggestedParamsFromObject(
  *
  * @param from - string representation of Algorand address of sender
  * @param note - uint8array of arbitrary data for sender to store
- * @param voteKey - string representation of voting key. for key deregistration, leave undefined
- * @param selectionKey - string representation of selection key. for key deregistration, leave undefined
+ * @param voteKey - voting key. for key deregistration, leave undefined
+ * @param selectionKey - selection key. for key deregistration, leave undefined
  * @param voteFirst - first round on which voteKey is valid
  * @param voteLast - last round on which voteKey is valid
  * @param voteKeyDilution - integer
@@ -157,6 +157,7 @@ export function makePaymentTxnWithSuggestedParamsFromObject(
  * @param rekeyTo - rekeyTo address, optional
  * @param nonParticipation - configure whether the address wants to stop participating. If true,
  *   voteKey, selectionKey, voteFirst, voteLast, and voteKeyDilution must be undefined.
+ * @param stateProofKey - state proof key. for key deregistration, leave undefined
  */
 export function makeKeyRegistrationTxnWithSuggestedParams(
   from: KeyRegistrationTxn['from'],
@@ -168,7 +169,8 @@ export function makeKeyRegistrationTxnWithSuggestedParams(
   voteKeyDilution: KeyRegistrationTxn['voteKeyDilution'],
   suggestedParams: MustHaveSuggestedParams<KeyRegistrationTxn>['suggestedParams'],
   rekeyTo?: KeyRegistrationTxn['reKeyTo'],
-  nonParticipation?: false
+  nonParticipation?: false,
+  stateProofKey?: KeyRegistrationTxn['stateProofKey']
 ): txnBuilder.Transaction;
 export function makeKeyRegistrationTxnWithSuggestedParams(
   from: KeyRegistrationTxn['from'],
@@ -180,7 +182,8 @@ export function makeKeyRegistrationTxnWithSuggestedParams(
   voteKeyDilution: undefined,
   suggestedParams: MustHaveSuggestedParams<KeyRegistrationTxn>['suggestedParams'],
   rekeyTo?: KeyRegistrationTxn['reKeyTo'],
-  nonParticipation?: true
+  nonParticipation?: true,
+  stateProofKey?: undefined
 ): txnBuilder.Transaction;
 export function makeKeyRegistrationTxnWithSuggestedParams(
   from: any,
@@ -192,7 +195,8 @@ export function makeKeyRegistrationTxnWithSuggestedParams(
   voteKeyDilution: any,
   suggestedParams: any,
   rekeyTo?: any,
-  nonParticipation = false
+  nonParticipation = false,
+  stateProofKey: any = undefined
 ) {
   const o: KeyRegistrationTxn = {
     from,
@@ -206,6 +210,7 @@ export function makeKeyRegistrationTxnWithSuggestedParams(
     type: TransactionType.keyreg,
     reKeyTo: rekeyTo,
     nonParticipation,
+    stateProofKey,
   };
   return new txnBuilder.Transaction(o);
 }
@@ -222,14 +227,15 @@ export function makeKeyRegistrationTxnWithSuggestedParams(
  * @param note - uint8array of arbitrary data for sender to store
  * @param genesisHash - string specifies hash genesis block of network in use
  * @param genesisID - string specifies genesis ID of network in use
- * @param voteKey - string representation of voting key. for key deregistration, leave undefined
- * @param selectionKey - string representation of selection key. for key deregistration, leave undefined
+ * @param voteKey - voting key. for key deregistration, leave undefined
+ * @param selectionKey - selection key. for key deregistration, leave undefined
  * @param voteFirst - first round on which voteKey is valid
  * @param voteLast - last round on which voteKey is valid
  * @param voteKeyDilution - integer
  * @param rekeyTo - rekeyTo address, optional
  * @param nonParticipation - configure whether the address wants to stop participating. If true,
  *   voteKey, selectionKey, voteFirst, voteLast, and voteKeyDilution must be undefined.
+ * @param stateProofKey - state proof key. for key deregistration, leave undefined
  * @Deprecated in version 2.0 this will change to use the "WithSuggestedParams" signature.
  */
 export function makeKeyRegistrationTxn(
@@ -246,7 +252,8 @@ export function makeKeyRegistrationTxn(
   voteLast: KeyRegistrationTxn['voteLast'],
   voteKeyDilution: KeyRegistrationTxn['voteKeyDilution'],
   rekeyTo?: KeyRegistrationTxn['reKeyTo'],
-  nonParticipation?: false
+  nonParticipation?: false,
+  stateProofKey?: KeyRegistrationTxn['stateProofKey']
 ): txnBuilder.Transaction;
 export function makeKeyRegistrationTxn(
   from: KeyRegistrationTxn['from'],
@@ -262,7 +269,8 @@ export function makeKeyRegistrationTxn(
   voteLast: undefined,
   voteKeyDilution: undefined,
   rekeyTo?: KeyRegistrationTxn['reKeyTo'],
-  nonParticipation?: true
+  nonParticipation?: true,
+  stateProofKey?: undefined
 ): txnBuilder.Transaction;
 export function makeKeyRegistrationTxn(
   from: any,
@@ -278,7 +286,8 @@ export function makeKeyRegistrationTxn(
   voteLast: any,
   voteKeyDilution: any,
   rekeyTo?: any,
-  nonParticipation: any = false
+  nonParticipation: any = false,
+  stateProofKey: any = undefined
 ) {
   const suggestedParams: SuggestedParams = {
     genesisHash,
@@ -297,7 +306,8 @@ export function makeKeyRegistrationTxn(
     voteKeyDilution,
     suggestedParams,
     rekeyTo,
-    nonParticipation
+    nonParticipation,
+    stateProofKey
   );
 }
 
@@ -313,6 +323,7 @@ export function makeKeyRegistrationTxnWithSuggestedParamsFromObject(
     | 'note'
     | 'voteKey'
     | 'selectionKey'
+    | 'stateProofKey'
     | 'voteFirst'
     | 'voteLast'
     | 'voteKeyDilution'
@@ -345,7 +356,8 @@ export function makeKeyRegistrationTxnWithSuggestedParamsFromObject(o: any) {
     o.voteKeyDilution,
     o.suggestedParams,
     o.rekeyTo,
-    o.nonParticipation
+    o.nonParticipation,
+    o.stateProofKey
   );
 }
 
diff --git a/src/transaction.ts b/src/transaction.ts
index 37035a3..10825a4 100644
--- a/src/transaction.ts
+++ b/src/transaction.ts
@@ -7,6 +7,7 @@ import {
   OnApplicationComplete,
   TransactionParams,
   TransactionType,
+  isTransactionType,
 } from './types/transactions/base';
 import AnyTransaction, {
   MustHaveSuggestedParams,
@@ -27,6 +28,9 @@ const ALGORAND_TRANSACTION_LEASE_LABEL_LENGTH = 5;
 const ALGORAND_TRANSACTION_ADDRESS_LENGTH = 32;
 const ALGORAND_TRANSACTION_REKEY_LABEL_LENGTH = 5;
 const ASSET_METADATA_HASH_LENGTH = 32;
+const KEYREG_VOTE_KEY_LENGTH = 32;
+const KEYREG_SELECTION_KEY_LENGTH = 32;
+const KEYREG_STATE_PROOF_KEY_LENGTH = 64;
 
 type AnyTransactionWithParams = MustHaveSuggestedParams<AnyTransaction>;
 type AnyTransactionWithParamsInline = MustHaveSuggestedParamsInline<AnyTransaction>;
@@ -45,6 +49,7 @@ interface TransactionStorageStructure
     | 'closeRemainderTo'
     | 'voteKey'
     | 'selectionKey'
+    | 'stateProofKey'
     | 'assetManager'
     | 'assetReserve'
     | 'assetFreeze'
@@ -68,6 +73,7 @@ interface TransactionStorageStructure
   closeRemainderTo?: string | Address;
   voteKey: string | Buffer;
   selectionKey: string | Buffer;
+  stateProofKey: string | Buffer;
   voteFirst: number;
   voteLast: number;
   voteKeyDilution: number;
@@ -106,6 +112,34 @@ interface TransactionStorageStructure
   extraPages?: number;
 }
 
+function getKeyregKey(
+  input: undefined | string | Uint8Array | Buffer,
+  inputName: string,
+  length: number
+): Buffer | undefined {
+  if (input == null) {
+    return undefined;
+  }
+
+  let inputAsBuffer: Buffer | undefined;
+
+  if (typeof input === 'string') {
+    inputAsBuffer = Buffer.from(input, 'base64');
+  } else if (input.constructor === Uint8Array) {
+    inputAsBuffer = Buffer.from(input);
+  } else if (Buffer.isBuffer(input)) {
+    inputAsBuffer = input;
+  }
+
+  if (inputAsBuffer == null || inputAsBuffer.byteLength !== length) {
+    throw Error(
+      `${inputName} must be a ${length} byte Uint8Array or Buffer or base64 string.`
+    );
+  }
+
+  return inputAsBuffer;
+}
+
 /**
  * Transaction enables construction of Algorand transactions
  * */
@@ -127,6 +161,7 @@ export class Transaction implements TransactionStorageStructure {
   closeRemainderTo?: Address;
   voteKey: Buffer;
   selectionKey: Buffer;
+  stateProofKey: Buffer;
   voteFirst: number;
   voteLast: number;
   voteKeyDilution: number;
@@ -426,18 +461,24 @@ export class Transaction implements TransactionStorageStructure {
     } else {
       txn.lease = new Uint8Array(0);
     }
-    if (typeof txn.voteKey !== 'undefined') {
-      txn.voteKey = Buffer.from(txn.voteKey as string, 'base64');
-    }
-    if (txn.selectionKey !== undefined) {
-      txn.selectionKey = Buffer.from(txn.selectionKey as string, 'base64');
-    }
+    txn.voteKey = getKeyregKey(txn.voteKey, 'voteKey', KEYREG_VOTE_KEY_LENGTH);
+    txn.selectionKey = getKeyregKey(
+      txn.selectionKey,
+      'selectionKey',
+      KEYREG_SELECTION_KEY_LENGTH
+    );
+    txn.stateProofKey = getKeyregKey(
+      txn.stateProofKey,
+      'stateProofKey',
+      KEYREG_STATE_PROOF_KEY_LENGTH
+    );
     // Checking non-participation key registration
     if (
       txn.nonParticipation &&
       (txn.voteKey ||
         txn.selectionKey ||
         txn.voteFirst ||
+        txn.stateProofKey ||
         txn.voteLast ||
         txn.voteKeyDilution)
     ) {
@@ -450,6 +491,7 @@ export class Transaction implements TransactionStorageStructure {
       !txn.nonParticipation &&
       (txn.voteKey ||
         txn.selectionKey ||
+        txn.stateProofKey ||
         txn.voteFirst ||
         txn.voteLast ||
         txn.voteKeyDilution) &&
@@ -460,6 +502,7 @@ export class Transaction implements TransactionStorageStructure {
         txn.voteLast &&
         txn.voteKeyDilution
       )
+      // stateProofKey not included here for backwards compatibility
     ) {
       throw new Error(
         'online key registration missing at least one of the following fields: ' +
@@ -541,6 +584,7 @@ export class Transaction implements TransactionStorageStructure {
         grp: this.group,
         votekey: this.voteKey,
         selkey: this.selectionKey,
+        sprfkey: this.stateProofKey,
         votefst: this.voteFirst,
         votelst: this.voteLast,
         votekd: this.voteKeyDilution,
@@ -560,6 +604,7 @@ export class Transaction implements TransactionStorageStructure {
       }
       if (!txn.selkey) delete txn.selkey;
       if (!txn.votekey) delete txn.votekey;
+      if (!txn.sprfkey) delete txn.sprfkey;
       if (!txn.votefst) delete txn.votefst;
       if (!txn.votelst) delete txn.votelst;
       if (!txn.votekd) delete txn.votekd;
@@ -787,12 +832,15 @@ export class Transaction implements TransactionStorageStructure {
 
   // eslint-disable-next-line camelcase
   static from_obj_for_encoding(txnForEnc: EncodedTransaction): Transaction {
-    const txn = Object.create(this.prototype);
+    const txn = Object.create(this.prototype) as Transaction;
     txn.name = 'Transaction';
     txn.tag = Buffer.from('TX');
 
     txn.genesisID = txnForEnc.gen;
     txn.genesisHash = Buffer.from(txnForEnc.gh);
+    if (!isTransactionType(txnForEnc.type)) {
+      throw new Error(`Unrecognized transaction type: ${txnForEnc.type}`);
+    }
     txn.type = txnForEnc.type;
     txn.fee = txnForEnc.fee;
     txn.firstRound = txnForEnc.fv;
@@ -824,6 +872,9 @@ export class Transaction implements TransactionStorageStructure {
       if (txnForEnc.selkey !== undefined) {
         txn.selectionKey = Buffer.from(txnForEnc.selkey);
       }
+      if (txnForEnc.sprfkey !== undefined) {
+        txn.stateProofKey = Buffer.from(txnForEnc.sprfkey);
+      }
       if (txnForEnc.votekd !== undefined) {
         txn.voteKeyDilution = txnForEnc.votekd;
       }
diff --git a/src/types/transactions/base.ts b/src/types/transactions/base.ts
index 8f33146..739c88e 100644
--- a/src/types/transactions/base.ts
+++ b/src/types/transactions/base.ts
@@ -1,7 +1,7 @@
 /**
  * Enum for application transaction types.
  *
- * The full list is availabe at https://developer.algorand.org/docs/reference/transactions/
+ * The full list is available at https://developer.algorand.org/docs/reference/transactions/
  */
 export enum TransactionType {
   /**
@@ -35,6 +35,17 @@ export enum TransactionType {
   appl = 'appl',
 }
 
+export function isTransactionType(s: string): s is TransactionType {
+  return (
+    s === TransactionType.pay ||
+    s === TransactionType.keyreg ||
+    s === TransactionType.acfg ||
+    s === TransactionType.axfer ||
+    s === TransactionType.afrz ||
+    s === TransactionType.appl
+  );
+}
+
 /**
  * Enums for application transactions on-transaction-complete behavior
  */
@@ -177,14 +188,19 @@ export interface TransactionParams {
   closeRemainderTo?: string;
 
   /**
-   * String representation of voting key. For key deregistration, leave undefined
+   * Voting key bytes. For key deregistration, leave undefined
+   */
+  voteKey: Uint8Array | string;
+
+  /**
+   *Selection key bytes. For key deregistration, leave undefined
    */
-  voteKey: string;
+  selectionKey: Uint8Array | string;
 
   /**
-   * String representation of selection key. For key deregistration, leave undefined
+   * State proof key bytes. For key deregistration, leave undefined
    */
-  selectionKey: string;
+  stateProofKey: Uint8Array | string;
 
   /**
    * First round on which voteKey is valid
diff --git a/src/types/transactions/encoded.ts b/src/types/transactions/encoded.ts
index 80438ca..bc5b9e9 100644
--- a/src/types/transactions/encoded.ts
+++ b/src/types/transactions/encoded.ts
@@ -182,6 +182,11 @@ export interface EncodedTransaction {
    */
   selkey?: Buffer;
 
+  /**
+   * stateProofKey
+   */
+  sprfkey?: Buffer;
+
   /**
    * voteFirst
    */
diff --git a/src/types/transactions/keyreg.ts b/src/types/transactions/keyreg.ts
index a973143..8856849 100644
--- a/src/types/transactions/keyreg.ts
+++ b/src/types/transactions/keyreg.ts
@@ -5,6 +5,7 @@ type SpecificParameters = Pick<
   TransactionParams,
   | 'voteKey'
   | 'selectionKey'
+  | 'stateProofKey'
   | 'voteFirst'
   | 'voteLast'
   | 'voteKeyDilution'


Test patch:
diff --git a/tests/8.LogicSig.ts b/tests/8.LogicSig.ts
index 491d7b5..9947ac3 100644
--- a/tests/8.LogicSig.ts
+++ b/tests/8.LogicSig.ts
@@ -1100,415 +1100,30 @@ describe('Program validation', () => {
       // byte "a"; byte "b"; byte "c"; cover 2; uncover 2; concat; concat; log; int 1
       assert.ok(logic.checkProgram(program));
     });
-  });
-});
+    it('should support TEAL v6 opcodes', () => {
+      assert.ok(logic.langspecEvalMaxVersion >= 6);
 
-describe('Template logic validation', () => {
-  describe('Split', () => {
-    it('should match the goldens', () => {
-      // Inputs
-      const owner =
-        'WO3QIJ6T4DZHBX5PWJH26JLHFSRT7W7M2DJOULPXDTUS6TUX7ZRIO4KDFY';
-      const receivers = [
-        'W6UUUSEAOGLBHT7VFT4H2SDATKKSG6ZBUIJXTZMSLW36YS44FRP5NVAU7U',
-        'XCIBIN7RT4ZXGBMVAMU3QS6L5EKB7XGROC5EPCNHHYXUIBAA5Q6C5Y7NEU',
-      ];
-      const rat1 = 100;
-      const rat2 = 30;
-      const expiryRound = 123456;
-      const minPay = 10000;
-      const maxFee = 5000000;
-      const split = new algosdk.LogicTemplates.Split(
-        owner,
-        receivers[0],
-        receivers[1],
-        rat1,
-        rat2,
-        expiryRound,
-        minPay,
-        maxFee
-      );
-      // Outputs
-      const goldenProgram =
-        'ASAIAcCWsQICAMDEBx5kkE4mAyCztwQn0+DycN+vsk+vJWcsoz/b7NDS6i33HOkvTpf+YiC3qUpIgHGWE8/1LPh9SGCalSN7IaITeeWSXbfsS5wsXyC4kBQ38Z8zcwWVAym4S8vpFB/c0XC6R4mnPi9EBADsPDEQIhIxASMMEDIEJBJAABkxCSgSMQcyAxIQMQglEhAxAiEEDRAiQAAuMwAAMwEAEjEJMgMSEDMABykSEDMBByoSEDMACCEFCzMBCCEGCxIQMwAIIQcPEBA=';
-      const goldenBytes = Buffer.from(goldenProgram, 'base64');
-      const actualBytes = split.getProgram();
-      assert.deepStrictEqual(goldenBytes, actualBytes);
-      const goldenAddress =
-        'KPYGWKTV7CKMPMTLQRNGMEQRSYTYDHUOFNV4UDSBDLC44CLIJPQWRTCPBU';
-      assert.deepStrictEqual(goldenAddress, split.getAddress());
-    });
-  });
-  describe('HTLC', () => {
-    it('sha256 should match the goldens', () => {
-      // Inputs
-      const owner =
-        '726KBOYUJJNE5J5UHCSGQGWIBZWKCBN4WYD7YVSTEXEVNFPWUIJ7TAEOPM';
-      const receiver =
-        '42NJMHTPFVPXVSDGA6JGKUV6TARV5UZTMPFIREMLXHETRKIVW34QFSDFRE';
-      const hashFn = 'sha256';
-      const hashImg = 'EHZhE08h/HwCIj1Qq56zYAvD/8NxJCOh5Hux+anb9V8=';
-      const expiryRound = 600000;
-      const maxFee = 1000;
-      const htlc = new algosdk.LogicTemplates.HTLC(
-        owner,
-        receiver,
-        hashFn,
-        hashImg,
-        expiryRound,
-        maxFee
-      );
-      // Outputs
-      const goldenProgram =
-        'ASAE6AcBAMDPJCYDIOaalh5vLV96yGYHkmVSvpgjXtMzY8qIkYu5yTipFbb5IBB2YRNPIfx8AiI9UKues2ALw//DcSQjoeR7sfmp2/VfIP68oLsUSlpOp7Q4pGgayA5soQW8tgf8VlMlyVaV9qITMQEiDjEQIxIQMQcyAxIQMQgkEhAxCSgSLQEpEhAxCSoSMQIlDRAREA==';
-      const goldenBytes = Buffer.from(goldenProgram, 'base64');
-      const actualBytes = htlc.getProgram();
-      assert.deepStrictEqual(goldenBytes, actualBytes);
-      const goldenAddress =
-        'FBZIR3RWVT2BTGVOG25H3VAOLVD54RTCRNRLQCCJJO6SVSCT5IVDYKNCSU';
-      assert.deepStrictEqual(goldenAddress, htlc.getAddress());
-      const goldenLtxn =
-        'gqRsc2lngqNhcmeRxAhwcmVpbWFnZaFsxJcBIAToBwEAwM8kJgMg5pqWHm8tX3rIZgeSZVK+mCNe0zNjyoiRi7nJOKkVtvkgEHZhE08h/HwCIj1Qq56zYAvD/8NxJCOh5Hux+anb9V8g/ryguxRKWk6ntDikaBrIDmyhBby2B/xWUyXJVpX2ohMxASIOMRAjEhAxBzIDEhAxCCQSEDEJKBItASkSEDEJKhIxAiUNEBEQo3R4boelY2xvc2XEIOaalh5vLV96yGYHkmVSvpgjXtMzY8qIkYu5yTipFbb5o2ZlZc0D6KJmdgGiZ2jEIH+DsWV/8fxTuS3BgUih1l38LUsfo9Z3KErd0gASbZBpomx2ZKNzbmTEIChyiO42rPQZmq42un3UDl1H3kZii2K4CElLvSrIU+oqpHR5cGWjcGF5';
-      const o = {
-        from: goldenAddress,
-        to: receiver,
-        fee: 0,
-        amount: 0,
-        closeRemainderTo: receiver,
-        firstRound: 1,
-        lastRound: 100,
-        genesisHash: 'f4OxZX/x/FO5LcGBSKHWXfwtSx+j1ncoSt3SABJtkGk=',
-        type: 'pay',
-      };
-      const preImageAsBase64 = 'cHJlaW1hZ2U=';
-      const actualTxn = algosdk.LogicTemplates.signTransactionWithHTLCUnlock(
-        htlc.getProgram(),
-        o,
-        preImageAsBase64
-      );
-      assert.deepStrictEqual(
-        Buffer.from(goldenLtxn, 'base64'),
-        Buffer.from(actualTxn.blob)
-      );
-    });
-    it('keccak256 should match the goldens', () => {
-      // Inputs
-      const owner =
-        '726KBOYUJJNE5J5UHCSGQGWIBZWKCBN4WYD7YVSTEXEVNFPWUIJ7TAEOPM';
-      const receiver =
-        '42NJMHTPFVPXVSDGA6JGKUV6TARV5UZTMPFIREMLXHETRKIVW34QFSDFRE';
-      const hashFn = 'keccak256';
-      const hashImg = 'D7d4MrvBrOSyNSmUs0kzucuJ+/9DbLkA6OOOocywoAc=';
-      const expiryRound = 600000;
-      const maxFee = 1000;
-      const htlc = new algosdk.LogicTemplates.HTLC(
-        owner,
-        receiver,
-        hashFn,
-        hashImg,
-        expiryRound,
-        maxFee
-      );
-      // Outputs
-      const goldenProgram =
-        'ASAE6AcBAMDPJCYDIOaalh5vLV96yGYHkmVSvpgjXtMzY8qIkYu5yTipFbb5IA+3eDK7wazksjUplLNJM7nLifv/Q2y5AOjjjqHMsKAHIP68oLsUSlpOp7Q4pGgayA5soQW8tgf8VlMlyVaV9qITMQEiDjEQIxIQMQcyAxIQMQgkEhAxCSgSLQIpEhAxCSoSMQIlDRAREA==';
-      const goldenBytes = Buffer.from(goldenProgram, 'base64');
-      const actualBytes = htlc.getProgram();
-      assert.deepStrictEqual(goldenBytes, actualBytes);
-      const goldenAddress =
-        '3MJ6JY3P6AU4R6I2RASYSAOPNI3QMWPZ7HYXJRNRGBIAXCHAY7QZRBH5PQ';
-      assert.deepStrictEqual(goldenAddress, htlc.getAddress());
-      const goldenLtxn =
-        'gqRsc2lngqNhcmeRxAhwcmVpbWFnZaFsxJcBIAToBwEAwM8kJgMg5pqWHm8tX3rIZgeSZVK+mCNe0zNjyoiRi7nJOKkVtvkgD7d4MrvBrOSyNSmUs0kzucuJ+/9DbLkA6OOOocywoAcg/ryguxRKWk6ntDikaBrIDmyhBby2B/xWUyXJVpX2ohMxASIOMRAjEhAxBzIDEhAxCCQSEDEJKBItAikSEDEJKhIxAiUNEBEQo3R4boelY2xvc2XEIOaalh5vLV96yGYHkmVSvpgjXtMzY8qIkYu5yTipFbb5o2ZlZc0D6KJmdgGiZ2jEIH+DsWV/8fxTuS3BgUih1l38LUsfo9Z3KErd0gASbZBpomx2ZKNzbmTEINsT5ONv8CnI+RqIJYkBz2o3Bln5+fF0xbEwUAuI4MfhpHR5cGWjcGF5';
-      const o = {
-        from: goldenAddress,
-        to: receiver,
-        fee: 0,
-        amount: 0,
-        closeRemainderTo: receiver,
-        firstRound: 1,
-        lastRound: 100,
-        genesisHash: 'f4OxZX/x/FO5LcGBSKHWXfwtSx+j1ncoSt3SABJtkGk=',
-        type: 'pay',
-      };
-      const preImageAsBase64 = 'cHJlaW1hZ2U=';
-      const actualTxn = algosdk.LogicTemplates.signTransactionWithHTLCUnlock(
-        htlc.getProgram(),
-        o,
-        preImageAsBase64
-      );
-      assert.deepStrictEqual(
-        Buffer.from(goldenLtxn, 'base64'),
-        Buffer.from(actualTxn.blob)
-      );
-    });
-    it('other hash function should fail', () => {
-      // Inputs
-      const owner =
-        '726KBOYUJJNE5J5UHCSGQGWIBZWKCBN4WYD7YVSTEXEVNFPWUIJ7TAEOPM';
-      const receiver =
-        '42NJMHTPFVPXVSDGA6JGKUV6TARV5UZTMPFIREMLXHETRKIVW34QFSDFRE';
-      const hashFn = 'made-up-hash-fn';
-      const hashImg = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=';
-      const expiryRound = 600000;
-      const maxFee = 1000;
-      assert.throws(
-        () =>
-          new algosdk.LogicTemplates.HTLC(
-            owner,
-            receiver,
-            hashFn,
-            hashImg,
-            expiryRound,
-            maxFee
-          )
-      );
-    });
-  });
-  describe('Limit Order', () => {
-    it('should match the goldens', () => {
-      // Inputs
-      const owner =
-        '726KBOYUJJNE5J5UHCSGQGWIBZWKCBN4WYD7YVSTEXEVNFPWUIJ7TAEOPM';
-      const assetid = 12345;
-      const ratn = 30;
-      const ratd = 100;
-      const expiryRound = 123456;
-      const minTrade = 10000;
-      const maxFee = 5000000;
-      const limitOrder = new algosdk.LogicTemplates.LimitOrder(
-        owner,
-        assetid,
-        ratn,
-        ratd,
-        expiryRound,
-        minTrade,
-        maxFee
-      );
-      // Outputs
-      const goldenProgram =
-        'ASAKAAHAlrECApBOBLlgZB7AxAcmASD+vKC7FEpaTqe0OKRoGsgObKEFvLYH/FZTJclWlfaiEzEWIhIxECMSEDEBJA4QMgQjEkAAVTIEJRIxCCEEDRAxCTIDEhAzARAhBRIQMwERIQYSEDMBFCgSEDMBEzIDEhAzARIhBx01AjUBMQghCB01BDUDNAE0Aw1AACQ0ATQDEjQCNAQPEEAAFgAxCSgSMQIhCQ0QMQcyAxIQMQgiEhAQ';
-      const goldenBytes = Buffer.from(goldenProgram, 'base64');
-      const actualBytes = limitOrder.getProgram();
-      assert.deepStrictEqual(goldenBytes, actualBytes);
-      const goldenAddress =
-        'LXQWT2XLIVNFS54VTLR63UY5K6AMIEWI7YTVE6LB4RWZDBZKH22ZO3S36I';
-      assert.deepStrictEqual(goldenAddress, limitOrder.getAddress());
-      const secretKey = Buffer.from(
-        'DTKVj7KMON3GSWBwMX9McQHtaDDi8SDEBi0bt4rOxlHNRahLa0zVG+25BDIaHB1dSoIHIsUQ8FFcdnCdKoG+Bg==',
-        'base64'
-      );
-      const actualBlob = algosdk.LogicTemplates.getSwapAssetsTransaction(
-        actualBytes,
-        3000,
-        10000,
-        secretKey,
-        10,
-        1234,
-        2234,
-        'f4OxZX/x/FO5LcGBSKHWXfwtSx+j1ncoSt3SABJtkGk='
-      );
-      const expectedTxn1 = Buffer.from(
-        'gqRsc2lngaFsxLcBIAoAAcCWsQICkE4EuWBkHsDEByYBIP68oLsUSlpOp7Q4pGgayA5soQW8tgf8VlMlyVaV9qITMRYiEjEQIxIQMQEkDhAyBCMSQABVMgQlEjEIIQQNEDEJMgMSEDMBECEFEhAzAREhBhIQMwEUKBIQMwETMgMSEDMBEiEHHTUCNQExCCEIHTUENQM0ATQDDUAAJDQBNAMSNAI0BA8QQAAWADEJKBIxAiEJDRAxBzIDEhAxCCISEBCjdHhuiaNhbXTNJxCjZmVlzQisomZ2zQTSomdoxCB/g7Flf/H8U7ktwYFIodZd/C1LH6PWdyhK3dIAEm2QaaNncnDEIKz368WOGpdE/Ww0L8wUu5Ly2u2bpG3ZSMKCJvcvGApTomx2zQi6o3JjdsQgzUWoS2tM1RvtuQQyGhwdXUqCByLFEPBRXHZwnSqBvgajc25kxCBd4Wnq60VaWXeVmuPt0x1XgMQSyP4nUnlh5G2Rhyo+taR0eXBlo3BheQ==',
-        'base64'
-      );
-      const expectedTxn2 = Buffer.from(
-        'gqNzaWfEQKXv8Z6OUDNmiZ5phpoQJHmfKyBal4gBZLPYsByYnlXCAlXMBeVFG5CLP1k5L6BPyEG2/XIbjbyM0CGG55CxxAKjdHhuiqRhYW10zQu4pGFyY3bEIP68oLsUSlpOp7Q4pGgayA5soQW8tgf8VlMlyVaV9qITo2ZlZc0JJKJmds0E0qJnaMQgf4OxZX/x/FO5LcGBSKHWXfwtSx+j1ncoSt3SABJtkGmjZ3JwxCCs9+vFjhqXRP1sNC/MFLuS8trtm6Rt2UjCgib3LxgKU6Jsds0IuqNzbmTEIM1FqEtrTNUb7bkEMhocHV1KggcixRDwUVx2cJ0qgb4GpHR5cGWlYXhmZXKkeGFpZM0wOQ==',
-        'base64'
-      );
-      const expectedBlob = Buffer.concat([expectedTxn1, expectedTxn2]);
-      assert.deepStrictEqual(expectedBlob, Buffer.from(actualBlob));
-    });
-  });
-  describe('Periodic payment', () => {
-    it('should match the goldens', () => {
-      // Inputs
-      const receiver =
-        'SKXZDBHECM6AS73GVPGJHMIRDMJKEAN5TUGMUPSKJCQ44E6M6TC2H2UJ3I';
-      const leaseb64 = 'AQIDBAUGBwgBAgMEBQYHCAECAwQFBgcIAQIDBAUGBwg=';
-      const amount = 500000;
-      const withdrawalWindow = 95;
-      const period = 100;
-      const expiryRound = 2445756;
-      const maxFee = 1000;
-      const periodicPayment = new algosdk.LogicTemplates.PeriodicPayment(
-        receiver,
-        amount,
-        withdrawalWindow,
-        period,
-        expiryRound,
-        maxFee,
-        leaseb64
-      );
-      // Outputs
-      const goldenProgram =
-        'ASAHAegHZABfoMIevKOVASYCIAECAwQFBgcIAQIDBAUGBwgBAgMEBQYHCAECAwQFBgcIIJKvkYTkEzwJf2arzJOxERsSogG9nQzKPkpIoc4TzPTFMRAiEjEBIw4QMQIkGCUSEDEEIQQxAggSEDEGKBIQMQkyAxIxBykSEDEIIQUSEDEJKRIxBzIDEhAxAiEGDRAxCCUSEBEQ';
-      const goldenBytes = Buffer.from(goldenProgram, 'base64');
-      const actualBytes = periodicPayment.getProgram();
-      assert.deepStrictEqual(goldenBytes, actualBytes);
-      const goldenAddress =
-        'JMS3K4LSHPULANJIVQBTEDP5PZK6HHMDQS4OKHIMHUZZ6OILYO3FVQW7IY';
-      assert.deepStrictEqual(goldenAddress, periodicPayment.getAddress());
-      const goldenGenesisHash = 'f4OxZX/x/FO5LcGBSKHWXfwtSx+j1ncoSt3SABJtkGk=';
-      const goldenStx =
-        'gqRsc2lngaFsxJkBIAcB6AdkAF+gwh68o5UBJgIgAQIDBAUGBwgBAgMEBQYHCAECAwQFBgcIAQIDBAUGBwggkq+RhOQTPAl/ZqvMk7ERGxKiAb2dDMo+SkihzhPM9MUxECISMQEjDhAxAiQYJRIQMQQhBDECCBIQMQYoEhAxCTIDEjEHKRIQMQghBRIQMQkpEjEHMgMSEDECIQYNEDEIJRIQERCjdHhuiaNhbXTOAAehIKNmZWXNA+iiZnbNBLCiZ2jEIH+DsWV/8fxTuS3BgUih1l38LUsfo9Z3KErd0gASbZBpomx2zQUPomx4xCABAgMEBQYHCAECAwQFBgcIAQIDBAUGBwgBAgMEBQYHCKNyY3bEIJKvkYTkEzwJf2arzJOxERsSogG9nQzKPkpIoc4TzPTFo3NuZMQgSyW1cXI76LA1KKwDMg39flXjnYOEuOUdDD0znzkLw7akdHlwZaNwYXk=';
-      const goldenStxBlob = Buffer.from(goldenStx, 'base64');
-      const stx = algosdk.LogicTemplates.getPeriodicPaymentWithdrawalTransaction(
-        actualBytes,
-        0,
-        1200,
-        goldenGenesisHash
-      );
-      const expectedDict = algosdk.decodeObj(new Uint8Array(goldenStxBlob));
-      const actualDict = algosdk.decodeObj(stx.blob);
-      assert.deepStrictEqual(expectedDict, actualDict);
-    });
-  });
-  describe('Limit Order', () => {
-    it('should match the goldens', () => {
-      // Inputs
-      const owner =
-        '726KBOYUJJNE5J5UHCSGQGWIBZWKCBN4WYD7YVSTEXEVNFPWUIJ7TAEOPM';
-      const assetid = 12345;
-      const ratn = 30;
-      const ratd = 100;
-      const expiryRound = 123456;
-      const minTrade = 10000;
-      const maxFee = 5000000;
-      const limitOrder = new algosdk.LogicTemplates.LimitOrder(
-        owner,
-        assetid,
-        ratn,
-        ratd,
-        expiryRound,
-        minTrade,
-        maxFee
-      );
-      // Outputs
-      const goldenProgram =
-        'ASAKAAHAlrECApBOBLlgZB7AxAcmASD+vKC7FEpaTqe0OKRoGsgObKEFvLYH/FZTJclWlfaiEzEWIhIxECMSEDEBJA4QMgQjEkAAVTIEJRIxCCEEDRAxCTIDEhAzARAhBRIQMwERIQYSEDMBFCgSEDMBEzIDEhAzARIhBx01AjUBMQghCB01BDUDNAE0Aw1AACQ0ATQDEjQCNAQPEEAAFgAxCSgSMQIhCQ0QMQcyAxIQMQgiEhAQ';
-      const goldenBytes = Buffer.from(goldenProgram, 'base64');
-      const actualBytes = limitOrder.getProgram();
-      assert.deepStrictEqual(goldenBytes, actualBytes);
-      const goldenAddress =
-        'LXQWT2XLIVNFS54VTLR63UY5K6AMIEWI7YTVE6LB4RWZDBZKH22ZO3S36I';
-      assert.deepStrictEqual(goldenAddress, limitOrder.getAddress());
-    });
-  });
-  describe('Periodic payment', () => {
-    it('should match the goldens', () => {
-      // Inputs
-      const receiver =
-        'SKXZDBHECM6AS73GVPGJHMIRDMJKEAN5TUGMUPSKJCQ44E6M6TC2H2UJ3I';
-      const leaseb64 = 'AQIDBAUGBwgBAgMEBQYHCAECAwQFBgcIAQIDBAUGBwg=';
-      const amount = 500000;
-      const withdrawalWindow = 95;
-      const period = 100;
-      const expiryRound = 2445756;
-      const maxFee = 1000;
-      const periodicPayment = new algosdk.LogicTemplates.PeriodicPayment(
-        receiver,
-        amount,
-        withdrawalWindow,
-        period,
-        expiryRound,
-        maxFee,
-        leaseb64
-      );
-      // Outputs
-      const goldenProgram =
-        'ASAHAegHZABfoMIevKOVASYCIAECAwQFBgcIAQIDBAUGBwgBAgMEBQYHCAECAwQFBgcIIJKvkYTkEzwJf2arzJOxERsSogG9nQzKPkpIoc4TzPTFMRAiEjEBIw4QMQIkGCUSEDEEIQQxAggSEDEGKBIQMQkyAxIxBykSEDEIIQUSEDEJKRIxBzIDEhAxAiEGDRAxCCUSEBEQ';
-      const goldenBytes = Buffer.from(goldenProgram, 'base64');
-      const actualBytes = periodicPayment.getProgram();
-      assert.deepStrictEqual(goldenBytes, actualBytes);
-      const goldenAddress =
-        'JMS3K4LSHPULANJIVQBTEDP5PZK6HHMDQS4OKHIMHUZZ6OILYO3FVQW7IY';
-      assert.deepStrictEqual(goldenAddress, periodicPayment.getAddress());
-      const goldenGenesisHash = 'f4OxZX/x/FO5LcGBSKHWXfwtSx+j1ncoSt3SABJtkGk=';
-      const goldenStx =
-        'gqRsc2lngaFsxJkBIAcB6AdkAF+gwh68o5UBJgIgAQIDBAUGBwgBAgMEBQYHCAECAwQFBgcIAQIDBAUGBwggkq+RhOQTPAl/ZqvMk7ERGxKiAb2dDMo+SkihzhPM9MUxECISMQEjDhAxAiQYJRIQMQQhBDECCBIQMQYoEhAxCTIDEjEHKRIQMQghBRIQMQkpEjEHMgMSEDECIQYNEDEIJRIQERCjdHhuiaNhbXTOAAehIKNmZWXNA+iiZnbNBLCiZ2jEIH+DsWV/8fxTuS3BgUih1l38LUsfo9Z3KErd0gASbZBpomx2zQUPomx4xCABAgMEBQYHCAECAwQFBgcIAQIDBAUGBwgBAgMEBQYHCKNyY3bEIJKvkYTkEzwJf2arzJOxERsSogG9nQzKPkpIoc4TzPTFo3NuZMQgSyW1cXI76LA1KKwDMg39flXjnYOEuOUdDD0znzkLw7akdHlwZaNwYXk=';
-      const goldenStxBlob = Buffer.from(goldenStx, 'base64');
-      const stx = algosdk.LogicTemplates.getPeriodicPaymentWithdrawalTransaction(
-        actualBytes,
-        0,
-        1200,
-        goldenGenesisHash
-      );
-      const expectedDict = algosdk.decodeObj(new Uint8Array(goldenStxBlob));
-      const actualDict = algosdk.decodeObj(stx.blob);
-      assert.deepStrictEqual(expectedDict, actualDict);
-    });
-  });
-  describe('Dynamic Fee', () => {
-    it('should match the goldens', () => {
-      // Inputs
-      const receiver =
-        '726KBOYUJJNE5J5UHCSGQGWIBZWKCBN4WYD7YVSTEXEVNFPWUIJ7TAEOPM';
-      const amount = 5000;
-      const firstValid = 12345;
-      const lastValid = 12346;
-      const closeRemainder =
-        '42NJMHTPFVPXVSDGA6JGKUV6TARV5UZTMPFIREMLXHETRKIVW34QFSDFRE';
-      const artificialLease = 'f4OxZX/x/FO5LcGBSKHWXfwtSx+j1ncoSt3SABJtkGk=';
-      const leaseBytes = new Uint8Array(Buffer.from(artificialLease, 'base64'));
-      const dynamicFee = new algosdk.LogicTemplates.DynamicFee(
-        receiver,
-        amount,
-        firstValid,
-        lastValid,
-        closeRemainder,
-        leaseBytes
-      );
-      // Outputs
-      const goldenProgram =
-        'ASAFAgGIJ7lgumAmAyD+vKC7FEpaTqe0OKRoGsgObKEFvLYH/FZTJclWlfaiEyDmmpYeby1feshmB5JlUr6YI17TM2PKiJGLuck4qRW2+SB/g7Flf/H8U7ktwYFIodZd/C1LH6PWdyhK3dIAEm2QaTIEIhIzABAjEhAzAAcxABIQMwAIMQESEDEWIxIQMRAjEhAxBygSEDEJKRIQMQgkEhAxAiUSEDEEIQQSEDEGKhIQ';
-      const goldenBytes = Buffer.from(goldenProgram, 'base64');
-      const actualBytes = dynamicFee.getProgram();
-      assert.deepStrictEqual(goldenBytes, actualBytes);
-      const goldenAddress =
-        'GCI4WWDIWUFATVPOQ372OZYG52EULPUZKI7Y34MXK3ZJKIBZXHD2H5C5TI';
-      assert.deepStrictEqual(goldenAddress, dynamicFee.getAddress());
-      const privateKeyOneB64 =
-        'cv8E0Ln24FSkwDgGeuXKStOTGcze5u8yldpXxgrBxumFPYdMJymqcGoxdDeyuM8t6Kxixfq0PJCyJP71uhYT7w==';
-      const privateKeyOne = Buffer.from(privateKeyOneB64, 'base64');
-      const goldenGenesisHash = 'f4OxZX/x/FO5LcGBSKHWXfwtSx+j1ncoSt3SABJtkGk=';
-      const txnAndLsig = algosdk.LogicTemplates.signDynamicFee(
-        actualBytes,
-        privateKeyOne,
-        goldenGenesisHash
-      );
-      const txnDict = txnAndLsig.txn;
-      const txnObj = new algosdk.Transaction(txnDict);
-      const txnBytes = txnObj.toByte();
-      const goldenTxn =
-        'iqNhbXTNE4ilY2xvc2XEIOaalh5vLV96yGYHkmVSvpgjXtMzY8qIkYu5yTipFbb5o2ZlZc0D6KJmds0wOaJnaMQgf4OxZX/x/FO5LcGBSKHWXfwtSx+j1ncoSt3SABJtkGmibHbNMDqibHjEIH+DsWV/8fxTuS3BgUih1l38LUsfo9Z3KErd0gASbZBpo3JjdsQg/ryguxRKWk6ntDikaBrIDmyhBby2B/xWUyXJVpX2ohOjc25kxCCFPYdMJymqcGoxdDeyuM8t6Kxixfq0PJCyJP71uhYT76R0eXBlo3BheQ==';
-      assert.deepStrictEqual(
-        new Uint8Array(Buffer.from(goldenTxn, 'base64')),
-        txnBytes
-      );
-      const { lsig } = txnAndLsig;
-      const lsigBytes = lsig.toByte();
-      const goldenLsig =
-        'gqFsxLEBIAUCAYgnuWC6YCYDIP68oLsUSlpOp7Q4pGgayA5soQW8tgf8VlMlyVaV9qITIOaalh5vLV96yGYHkmVSvpgjXtMzY8qIkYu5yTipFbb5IH+DsWV/8fxTuS3BgUih1l38LUsfo9Z3KErd0gASbZBpMgQiEjMAECMSEDMABzEAEhAzAAgxARIQMRYjEhAxECMSEDEHKBIQMQkpEhAxCCQSEDECJRIQMQQhBBIQMQYqEhCjc2lnxEAhLNdfdDp9Wbi0YwsEQCpP7TVHbHG7y41F4MoESNW/vL1guS+5Wj4f5V9fmM63/VKTSMFidHOSwm5o+pbV5lYH';
-      assert.deepStrictEqual(
-        new Uint8Array(Buffer.from(goldenLsig, 'base64')),
-        lsigBytes
+      // bsqrt op
+      let program = new Uint8Array(Buffer.from('068001909680010ca8', 'hex'));
+      // byte 0x90; bsqrt; byte 0x0c; b==
+      assert.ok(logic.checkProgram(program));
+
+      // divw op
+      program = new Uint8Array(
+        Buffer.from(
+          '06810981ecffffffffffffffff01810a9781feffffffffffffffff0112',
+          'hex'
+        )
       );
-      const privateKeyTwoB64 =
-        '2qjz96Vj9M6YOqtNlfJUOKac13EHCXyDty94ozCjuwwriI+jzFgStFx9E6kEk1l4+lFsW4Te2PY1KV8kNcccRg==';
-      const privateKeyTwo = Buffer.from(privateKeyTwoB64, 'base64');
-      const stxns = algosdk.LogicTemplates.getDynamicFeeTransactions(
-        txnDict,
-        lsig,
-        privateKeyTwo,
-        1234,
-        firstValid,
-        lastValid
+      //  int 9; int 18446744073709551596; int 10; divw; int 18446744073709551614; ==
+      assert.ok(logic.checkProgram(program));
+
+      // txn fields
+      program = new Uint8Array(
+        Buffer.from('06313f1581401233003e15810a1210', 'hex')
       );
-      const goldenStxns =
-        'gqNzaWfEQJBNVry9qdpnco+uQzwFicUWHteYUIxwDkdHqY5Qw2Q8Fc2StrQUgN+2k8q4rC0LKrTMJQnE+mLWhZgMMJvq3QCjdHhuiqNhbXTOAAWq6qNmZWXOAATzvqJmds0wOaJnaMQgf4OxZX/x/FO5LcGBSKHWXfwtSx+j1ncoSt3SABJtkGmjZ3JwxCCCVfqhCinRBXKMIq9eSrJQIXZ+7iXUTig91oGd/mZEAqJsds0wOqJseMQgf4OxZX/x/FO5LcGBSKHWXfwtSx+j1ncoSt3SABJtkGmjcmN2xCCFPYdMJymqcGoxdDeyuM8t6Kxixfq0PJCyJP71uhYT76NzbmTEICuIj6PMWBK0XH0TqQSTWXj6UWxbhN7Y9jUpXyQ1xxxGpHR5cGWjcGF5gqRsc2lngqFsxLEBIAUCAYgnuWC6YCYDIP68oLsUSlpOp7Q4pGgayA5soQW8tgf8VlMlyVaV9qITIOaalh5vLV96yGYHkmVSvpgjXtMzY8qIkYu5yTipFbb5IH+DsWV/8fxTuS3BgUih1l38LUsfo9Z3KErd0gASbZBpMgQiEjMAECMSEDMABzEAEhAzAAgxARIQMRYjEhAxECMSEDEHKBIQMQkpEhAxCCQSEDECJRIQMQQhBBIQMQYqEhCjc2lnxEAhLNdfdDp9Wbi0YwsEQCpP7TVHbHG7y41F4MoESNW/vL1guS+5Wj4f5V9fmM63/VKTSMFidHOSwm5o+pbV5lYHo3R4boujYW10zROIpWNsb3NlxCDmmpYeby1feshmB5JlUr6YI17TM2PKiJGLuck4qRW2+aNmZWXOAAWq6qJmds0wOaJnaMQgf4OxZX/x/FO5LcGBSKHWXfwtSx+j1ncoSt3SABJtkGmjZ3JwxCCCVfqhCinRBXKMIq9eSrJQIXZ+7iXUTig91oGd/mZEAqJsds0wOqJseMQgf4OxZX/x/FO5LcGBSKHWXfwtSx+j1ncoSt3SABJtkGmjcmN2xCD+vKC7FEpaTqe0OKRoGsgObKEFvLYH/FZTJclWlfaiE6NzbmTEIIU9h0wnKapwajF0N7K4zy3orGLF+rQ8kLIk/vW6FhPvpHR5cGWjcGF5';
-      const goldenStxnBytes = Buffer.from(goldenStxns, 'base64');
-      assert.deepStrictEqual(new Uint8Array(goldenStxnBytes), stxns);
+      // txn StateProofPK; len; int 64; ==; gtxn 0 LastLog; len; int 10; ==; &&
+      assert.ok(logic.checkProgram(program));
     });
   });
 });
diff --git a/tests/cucumber/browser/test.js b/tests/cucumber/browser/test.js
index ec3fc13..7fd997d 100644
--- a/tests/cucumber/browser/test.js
+++ b/tests/cucumber/browser/test.js
@@ -1,9 +1,8 @@
 const assert = require('assert');
-const sha256 = require('js-sha256');
+const sha512 = require('js-sha512');
 const nacl = require('tweetnacl');
 
 window.assert = assert;
-window.sha256 = sha256;
 window.Buffer = Buffer;
 
 window.keyPairFromSecretKey = function keyPairFromSecretKey(sk) {
@@ -14,6 +13,10 @@ window.keyPairFromSeed = function keyPairFromSeed(seed) {
   return nacl.sign.keyPair.fromSeed(seed);
 };
 
+window.genericHash = function genericHash(toHash) {
+  return sha512.sha512_256.array(toHash);
+};
+
 window.loadResource = async function loadResource(resource) {
   const res = await fetch(`/features/resources/${resource}`);
   if (!res.ok) {
diff --git a/tests/cucumber/steps/steps.js b/tests/cucumber/steps/steps.js
index b9c76d0..673ddfd 100644
--- a/tests/cucumber/steps/steps.js
+++ b/tests/cucumber/steps/steps.js
@@ -1,10 +1,10 @@
 /* eslint-disable func-names,radix */
 const assert = require('assert');
-const sha256 = require('js-sha256');
 const fs = require('fs');
 const path = require('path');
-const nacl = require('../../../src/nacl/naclWrappers');
+
 const algosdk = require('../../../index');
+const nacl = require('../../../src/nacl/naclWrappers');
 
 const maindir = path.dirname(path.dirname(path.dirname(__dirname)));
 
@@ -16,6 +16,10 @@ function keyPairFromSeed(seed) {
   return nacl.keyPairFromSeed(seed);
 }
 
+function genericHash(toHash) {
+  return nacl.genericHash(toHash);
+}
+
 async function loadResource(res) {
   const p = path.join(
     maindir,
@@ -977,6 +981,63 @@ module.exports = function getSteps(options) {
     }
   });
 
+  Given(
+    'default V2 key registration transaction {string}',
+    async function (type) {
+      const voteKey = makeUint8Array(
+        Buffer.from('9mr13Ri8rFepxN3ghIUrZNui6LqqM5hEzB45Rri5lkU=', 'base64')
+      );
+      const selectionKey = makeUint8Array(
+        Buffer.from('dx717L3uOIIb/jr9OIyls1l5Ei00NFgRa380w7TnPr4=', 'base64')
+      );
+      const stateProofKey = makeUint8Array(
+        Buffer.from(
+          'mYR0GVEObMTSNdsKM6RwYywHYPqVDqg3E4JFzxZOreH9NU8B+tKzUanyY8AQ144hETgSMX7fXWwjBdHz6AWk9w==',
+          'base64'
+        )
+      );
+
+      const from = this.accounts[0];
+      this.pk = from;
+
+      const result = await this.acl.getTransactionParams();
+      const suggestedParams = {
+        fee: result.fee,
+        firstRound: result.lastRound + 1,
+        lastRound: result.lastRound + 1000,
+        genesisHash: result.genesishashb64,
+        genesisID: result.genesisID,
+      };
+      this.lastRound = result.lastRound;
+
+      if (type === 'online') {
+        this.txn = algosdk.makeKeyRegistrationTxnWithSuggestedParamsFromObject({
+          from,
+          voteKey,
+          selectionKey,
+          stateProofKey,
+          voteFirst: 1,
+          voteLast: 2000,
+          voteKeyDilution: 10,
+          suggestedParams,
+        });
+      } else if (type === 'offline') {
+        this.txn = algosdk.makeKeyRegistrationTxnWithSuggestedParamsFromObject({
+          from,
+          suggestedParams,
+        });
+      } else if (type === 'nonparticipation') {
+        this.txn = algosdk.makeKeyRegistrationTxnWithSuggestedParamsFromObject({
+          from,
+          nonParticipation: true,
+          suggestedParams,
+        });
+      } else {
+        throw new Error(`Unrecognized keyreg type: ${type}`);
+      }
+    }
+  );
+
   When(
     'I get recent transactions, limited by {int} transactions',
     function (int) {
@@ -1469,331 +1530,6 @@ module.exports = function getSteps(options) {
     }
   );
 
-  /// /////////////////////////////////
-  // begin teal contract template tests
-  /// /////////////////////////////////
-
-  Given('contract test fixture', function () {
-    this.contractTestFixture = {
-      split: undefined,
-      htlc: undefined,
-      periodicPay: undefined,
-      limitOrder: undefined,
-      dynamicFee: undefined,
-      activeAddress: '',
-      htlcPreimage: '',
-      limitOrderN: 0,
-      limitOrderD: 0,
-      limitOrderMin: 0,
-      splitRat1: 0,
-      splitRat2: 0,
-      splitMin: 0,
-      contractFundAmount: 0,
-      periodicPayPeriod: 0,
-    };
-  });
-
-  When('I fund the contract account', async function () {
-    const amount = this.contractTestFixture.contractFundAmount;
-    const from = this.accounts[0];
-    const to = this.contractTestFixture.activeAddress;
-    this.params = await this.acl.getTransactionParams();
-    this.fee = this.params.fee;
-    this.fv = this.params.lastRound;
-    if (this.fv === 0) {
-      this.fv = 1;
-    }
-    this.lv = this.fv + 1000;
-    this.lastRound = this.params.lastRound;
-    this.note = undefined;
-    this.gh = this.params.genesishashb64;
-    this.txn = {
-      from,
-      to,
-      fee: this.fee,
-      amount,
-      firstRound: this.fv,
-      lastRound: this.lv,
-      genesisHash: this.gh,
-      type: 'pay',
-    };
-    const stxKmd = await this.kcl.signTransaction(
-      this.handle,
-      this.wallet_pswd,
-      this.txn
-    );
-    this.txid = await this.acl.sendRawTransaction(stxKmd);
-    this.txid = this.txid.txId;
-    await this.acl.statusAfterBlock(this.lastRound + 2);
-    let info = await this.acl.transactionInformation(from, this.txid);
-    assert.deepStrictEqual(true, 'type' in info);
-    info = await this.acl.transactionById(this.txid);
-    assert.deepStrictEqual(true, 'type' in info);
-  });
-
-  Given(
-    'a split contract with ratio {int} to {int} and minimum payment {int}',
-    function (rat2, rat1, minPay) {
-      const owner = this.accounts[0];
-      const receivers = [this.accounts[0], this.accounts[1]];
-      const expiryRound = 100;
-      const maxFee = 5000000;
-      this.contractTestFixture.splitRat1 = parseInt(rat1);
-      this.contractTestFixture.splitRat2 = parseInt(rat2);
-      this.contractTestFixture.splitMin = parseInt(minPay);
-      this.contractTestFixture.split = new algosdk.LogicTemplates.Split(
-        owner,
-        receivers[0],
-        receivers[1],
-        this.contractTestFixture.splitRat1,
-        this.contractTestFixture.splitRat2,
-        expiryRound,
-        this.contractTestFixture.splitMin,
-        maxFee
-      );
-      this.contractTestFixture.activeAddress = this.contractTestFixture.split.getAddress();
-      this.contractTestFixture.contractFundAmount = minPay * (rat1 + rat2) * 10;
-    }
-  );
-
-  When('I send the split transactions', async function () {
-    const contractCode = this.contractTestFixture.split.getProgram();
-    this.params = await this.acl.getTransactionParams();
-    this.fee = this.params.fee;
-    this.fv = this.params.lastRound;
-    this.lv = this.fv + 1000;
-    this.lastRound = this.params.lastRound;
-    const amount =
-      this.contractTestFixture.splitMin *
-      (this.contractTestFixture.splitRat1 + this.contractTestFixture.splitRat2);
-    const txnBytes = algosdk.LogicTemplates.getSplitFundsTransaction(
-      contractCode,
-      amount,
-      this.fv,
-      this.lv,
-      this.fee,
-      this.params.genesishashb64
-    );
-    this.txid = await this.acl.sendRawTransaction(txnBytes);
-    this.txid = this.txid.txId;
-    this.pk = this.contractTestFixture.activeAddress;
-  });
-
-  Given(
-    'an HTLC contract with hash preimage {string}',
-    function (preimageStringB64) {
-      // Write code here that turns the phrase above into concrete actions
-      this.contractTestFixture.htlcPreimage = preimageStringB64;
-      const preimageBytes = Buffer.from(preimageStringB64, 'base64');
-      const hash = sha256.create();
-      hash.update(preimageBytes);
-      const hashB64String = Buffer.from(hash.hex(), 'hex').toString('base64');
-      const hashFn = 'sha256';
-      const owner = this.accounts[0];
-      const receiver = this.accounts[1];
-      const expiryRound = 100;
-      const maxFee = 1000000;
-      this.contractTestFixture.htlc = new algosdk.LogicTemplates.HTLC(
-        owner,
-        receiver,
-        hashFn,
-        hashB64String,
-        expiryRound,
-        maxFee
-      );
-      this.contractTestFixture.activeAddress = this.contractTestFixture.htlc.getAddress();
-      this.contractTestFixture.contractFundAmount = 100000000;
-    }
-  );
-
-  When('I claim the algos', async function () {
-    this.params = await this.acl.getTransactionParams();
-    this.fee = this.params.fee;
-    this.fv = this.params.lastRound;
-    this.lv = this.fv + 1000;
-    this.lastRound = this.params.lastRound;
-    const payTxn = {
-      from: this.contractTestFixture.htlc.getAddress(),
-      to: this.accounts[1],
-      closeRemainderTo: this.accounts[1],
-      fee: this.fee,
-      amount: 0,
-      firstRound: this.fv,
-      lastRound: this.lv,
-      genesisHash: this.params.genesishashb64,
-      type: 'pay',
-    };
-    const txnBytes = algosdk.LogicTemplates.signTransactionWithHTLCUnlock(
-      this.contractTestFixture.htlc.getProgram(),
-      payTxn,
-      this.contractTestFixture.htlcPreimage
-    );
-    this.txid = await this.acl.sendRawTransaction(txnBytes.blob);
-    this.txid = this.txid.txId;
-    this.pk = this.contractTestFixture.activeAddress;
-  });
-
-  Given(
-    'a periodic payment contract with withdrawing window {int} and period {int}',
-    function (withdrawWindow, period) {
-      const receiver = this.accounts[0];
-      const amount = 10000000;
-      const expiryRound = 100;
-      const maxFee = 1000000000000;
-      this.contractTestFixture.periodicPay = new algosdk.LogicTemplates.PeriodicPayment(
-        receiver,
-        amount,
-        parseInt(withdrawWindow),
-        parseInt(period),
-        expiryRound,
-        maxFee,
-        undefined
-      );
-      this.contractTestFixture.activeAddress = this.contractTestFixture.periodicPay.getAddress();
-      this.contractTestFixture.contractFundAmount = amount * 10;
-      this.contractTestFixture.periodicPayPeriod = parseInt(period);
-    }
-  );
-
-  When('I claim the periodic payment', async function () {
-    this.params = await this.acl.getTransactionParams();
-    this.fee = this.params.fee;
-    this.fv = this.params.lastRound;
-    const remainder = this.fv % this.contractTestFixture.periodicPayPeriod;
-    this.fv += remainder;
-    this.lv = this.fv + 1000;
-    this.lastRound = this.params.lastRound;
-    this.gh = this.params.genesishashb64;
-    const txnBytes = algosdk.LogicTemplates.getPeriodicPaymentWithdrawalTransaction(
-      this.contractTestFixture.periodicPay.getProgram(),
-      this.fee,
-      this.fv,
-      this.gh
-    );
-    this.txid = await this.acl.sendRawTransaction(txnBytes.blob);
-    this.txid = this.txid.txId;
-    this.pk = this.contractTestFixture.activeAddress;
-  });
-
-  Given(
-    'a limit order contract with parameters {int} {int} {int}',
-    function (ratn, ratd, minTrade) {
-      const maxFee = 100000;
-      const expiryRound = 100;
-      this.contractTestFixture.limitOrderN = parseInt(ratn);
-      this.contractTestFixture.limitOrderD = parseInt(ratd);
-      this.contractTestFixture.limitOrderMin = parseInt(minTrade);
-      this.contractTestFixture.contractFundAmount = 2 * parseInt(minTrade);
-      if (this.contractTestFixture.contractFundAmount < 1000000) {
-        this.contractTestFixture.contractFundAmount = 1000000;
-      }
-      const assetid = parseInt(this.assetTestFixture.index);
-      this.contractTestFixture.limitOrder = new algosdk.LogicTemplates.LimitOrder(
-        this.accounts[0],
-        assetid,
-        parseInt(ratn),
-        parseInt(ratd),
-        expiryRound,
-        parseInt(minTrade),
-        maxFee
-      );
-      this.contractTestFixture.activeAddress = this.contractTestFixture.limitOrder.getAddress();
-    }
-  );
-
-  When('I swap assets for algos', async function () {
-    const response = await this.kcl.exportKey(
-      this.handle,
-      this.wallet_pswd,
-      this.accounts[1]
-    );
-    const secretKey = response.private_key;
-    const microAlgoAmount = this.contractTestFixture.limitOrderMin + 1; // just over the minimum
-    const assetAmount =
-      Math.floor(
-        (microAlgoAmount * this.contractTestFixture.limitOrderN) /
-          this.contractTestFixture.limitOrderD
-      ) + 1;
-    this.params = await this.acl.getTransactionParams();
-    this.fee = this.params.fee;
-    this.fv = this.params.lastRound;
-    this.lv = this.fv + 1000;
-    this.lastRound = this.params.lastRound;
-    this.gh = this.params.genesishashb64;
-    const txnBytes = algosdk.LogicTemplates.getSwapAssetsTransaction(
-      this.contractTestFixture.limitOrder.getProgram(),
-      assetAmount,
-      microAlgoAmount,
-      secretKey,
-      this.fee,
-      this.fv,
-      this.lv,
-      this.gh
-    );
-    this.txid = await this.acl.sendRawTransaction(txnBytes);
-    this.txid = this.txid.txId;
-    this.pk = this.contractTestFixture.activeAddress;
-  });
-
-  Given('a dynamic fee contract with amount {int}', async function (amount) {
-    this.params = await this.acl.getTransactionParams();
-    this.fee = this.params.fee;
-    this.fv = this.params.lastRound;
-    if (this.fv === 0) {
-      this.fv = 1;
-    }
-    this.lv = this.fv + 1000;
-    this.lastRound = this.params.lastRound;
-    this.gh = this.params.genesishashb64;
-    this.contractTestFixture.contractFundAmount = parseInt(amount);
-    this.contractTestFixture.dynamicFee = new algosdk.LogicTemplates.DynamicFee(
-      this.accounts[1],
-      parseInt(amount),
-      this.fv,
-      this.lv
-    ); // intentionally leave optional args undefined
-    this.contractTestFixture.activeAddress = this.contractTestFixture.dynamicFee.getAddress();
-  });
-
-  Given('I send the dynamic fee transactions', async function () {
-    this.params = await this.acl.getTransactionParams();
-    this.fee = this.params.fee;
-    this.fv = this.params.lastRound;
-    if (this.fv === 0) {
-      this.fv = 1;
-    }
-    this.lv = this.fv + 1000;
-    this.lastRound = this.params.lastRound;
-    this.gh = this.params.genesishashb64;
-
-    const firstResponse = await this.kcl.exportKey(
-      this.handle,
-      this.wallet_pswd,
-      this.accounts[0]
-    );
-    const secretKeyOne = firstResponse.private_key;
-    const txnAndLsig = algosdk.LogicTemplates.signDynamicFee(
-      this.contractTestFixture.dynamicFee.getProgram(),
-      secretKeyOne,
-      this.gh
-    );
-    const secondResponse = await this.kcl.exportKey(
-      this.handle,
-      this.wallet_pswd,
-      this.accounts[1]
-    );
-    const secretKeyTwo = secondResponse.private_key;
-    const txnBytes = algosdk.LogicTemplates.getDynamicFeeTransactions(
-      txnAndLsig.txn,
-      txnAndLsig.lsig,
-      secretKeyTwo,
-      this.fee
-    );
-    this.txid = await this.acl.sendRawTransaction(txnBytes);
-    this.txid = this.txid.txId;
-    [this.pk] = this.accounts;
-  });
-
   /// /////////////////////////////////
   // begin indexer and algodv2 unit tests
   /// /////////////////////////////////
@@ -3950,6 +3686,17 @@ module.exports = function getSteps(options) {
     }
   );
 
+  Then(
+    'base64 decoding the response is the same as the binary {string}',
+    async (program) => {
+      const data = await loadResource(program);
+      const decodedResult = makeUint8Array(
+        Buffer.from(compileResponse.result, 'base64')
+      );
+      assert.deepStrictEqual(makeUint8Array(data), decodedResult);
+    }
+  );
+
   /// /////////////////////////////////
   // TealSign tests
   /// /////////////////////////////////
@@ -3999,22 +3746,6 @@ module.exports = function getSteps(options) {
     }
   );
 
-  Given(
-    'suggested transaction parameters fee {int}, flat-fee {string}, first-valid {int}, last-valid {int}, genesis-hash {string}, genesis-id {string}',
-    function (fee, flatFee, firstRound, lastRound, genesisHash, genesisID) {
-      assert.ok(['true', 'false'].includes(flatFee));
-
-      this.suggestedParams = {
-        flatFee: flatFee === 'true',
-        fee,
-        firstRound,
-        lastRound,
-        genesisID,
-        genesisHash,
-      };
-    }
-  );
-
   Given(
     'suggested transaction parameters from the algod v2 client',
     async function () {
@@ -4039,6 +3770,95 @@ module.exports = function getSteps(options) {
     }
   );
 
+  Then(
+    "I get the account address for the current application and see that it matches the app id's hash",
+    async function () {
+      const appID = this.currentApplicationIndex;
+      const toSign = Buffer.concat([
+        Buffer.from('appID'),
+        algosdk.encodeUint64(appID),
+      ]);
+      const expected = algosdk.encodeAddress(
+        makeUint8Array(genericHash(toSign))
+      );
+      const actual = algosdk.getApplicationAddress(appID);
+      assert.strictEqual(expected, actual);
+    }
+  );
+
+  Given(
+    "I fund the current application's address with {int} microalgos.",
+    async function (amount) {
+      const sp = await this.v2Client.getTransactionParams().do();
+      if (sp.firstRound === 0) sp.firstRound = 1;
+      const fundingTxnArgs = {
+        from: this.accounts[0],
+        to: algosdk.getApplicationAddress(this.currentApplicationIndex),
+        amount,
+        suggestedParams: sp,
+      };
+      const stxn = await this.kcl.signTransaction(
+        this.handle,
+        this.wallet_pswd,
+        fundingTxnArgs
+      );
+
+      const fundingResponse = await this.v2Client.sendRawTransaction(stxn).do();
+      const info = await algosdk.waitForConfirmation(
+        this.v2Client,
+        fundingResponse.txId,
+        2
+      );
+      assert.ok(info['confirmed-round'] > 0);
+    }
+  );
+
+  Given(
+    'suggested transaction parameters fee {int}, flat-fee {string}, first-valid {int}, last-valid {int}, genesis-hash {string}, genesis-id {string}',
+    function (fee, flatFee, firstRound, lastRound, genesisHash, genesisID) {
+      assert.ok(['true', 'false'].includes(flatFee));
+
+      this.suggestedParams = {
+        flatFee: flatFee === 'true',
+        fee,
+        firstRound,
+        lastRound,
+        genesisID,
+        genesisHash,
+      };
+    }
+  );
+
+  When(
+    'I build a keyreg transaction with sender {string}, nonparticipation {string}, vote first {int}, vote last {int}, key dilution {int}, vote public key {string}, selection public key {string}, and state proof public key {string}',
+    function (
+      sender,
+      nonpart,
+      voteFirst,
+      voteLast,
+      keyDilution,
+      votePk,
+      selectionPk,
+      stateProofPk
+    ) {
+      assert.ok(['true', 'false'].includes(nonpart));
+
+      this.txn = algosdk.makeKeyRegistrationTxnWithSuggestedParams(
+        sender,
+        undefined,
+        votePk.length ? votePk : undefined,
+        selectionPk.length ? selectionPk : undefined,
+        voteFirst,
+        voteLast,
+        keyDilution,
+        this.suggestedParams,
+        undefined,
+        nonpart === 'true',
+        stateProofPk.length ? stateProofPk : undefined
+      );
+    }
+  );
+
   function operationStringToEnum(inString) {
     switch (inString) {
       case 'noop':
@@ -4064,6 +3884,22 @@ module.exports = function getSteps(options) {
     }
   }
 
+  async function compileProgram(client, program) {
+    const data = await loadResource(program);
+    if (program.endsWith('.teal')) {
+      try {
+        const compiledResponse = await client.compile(data).do();
+        const compiledProgram = makeUint8Array(
+          Buffer.from(compiledResponse.result, 'base64')
+        );
+        return compiledProgram;
+      } catch (err) {
+        throw new Error(`could not compile teal program: ${err}`);
+      }
+    }
+    return makeUint8Array(data);
+  }
+
   function splitAndProcessAppArgs(inArgs) {
     const splitArgs = inArgs.split(',');
     const subArgs = [];
@@ -4082,6 +3918,9 @@ module.exports = function getSteps(options) {
         case 'addr':
           appArgs.push(algosdk.decodeAddress(subArg[1]).publicKey);
           break;
+        case 'b64':
+          appArgs.push(Buffer.from(subArg[1], 'base64'));
+          break;
         default:
           throw Error(`did not recognize app arg of type${subArg[0]}`);
       }
@@ -4116,14 +3955,18 @@ module.exports = function getSteps(options) {
       // open and load in approval program
       let approvalProgramBytes;
       if (approvalProgramFile !== '') {
-        const resource = await loadResource(approvalProgramFile);
-        approvalProgramBytes = makeUint8Array(resource);
+        approvalProgramBytes = await compileProgram(
+          this.v2Client,
+          approvalProgramFile
+        );
       }
       // open and load in clear program
       let clearProgramBytes;
       if (clearProgramFile !== '') {
-        const resource = await loadResource(clearProgramFile);
-        clearProgramBytes = makeUint8Array(resource);
+        clearProgramBytes = await compileProgram(
+          this.v2Client,
+          clearProgramFile
+        );
       }
       // split and process app args
       let appArgs;
@@ -4351,14 +4194,18 @@ module.exports = function getSteps(options) {
       // open and load in approval program
       let approvalProgramBytes;
       if (approvalProgramFile !== '') {
-        const resouce = await loadResource(approvalProgramFile);
-        approvalProgramBytes = makeUint8Array(resouce);
+        approvalProgramBytes = await compileProgram(
+          this.v2Client,
+          approvalProgramFile
+        );
       }
       // open and load in clear program
       let clearProgramBytes;
       if (clearProgramFile !== '') {
-        const resouce = await loadResource(clearProgramFile);
-        clearProgramBytes = makeUint8Array(resouce);
+        clearProgramBytes = await compileProgram(
+          this.v2Client,
+          clearProgramFile
+        );
       }
       // split and process app args
       let appArgs;
@@ -4442,11 +4289,20 @@ module.exports = function getSteps(options) {
     assert.ok(info['confirmed-round'] > 0);
   });
 
+  Given('I reset the array of application IDs to remember.', async function () {
+    this.appIDs = [];
+  });
+
   Given('I remember the new application ID.', async function () {
     const info = await this.v2Client
       .pendingTransactionInformation(this.appTxid.txId)
       .do();
     this.currentApplicationIndex = info['application-index'];
+
+    if (!Object.prototype.hasOwnProperty.call(this, 'appIDs')) {
+      this.appIDs = [];
+    }
+    this.appIDs.push(this.currentApplicationIndex);
   });
 
   Then(
@@ -4764,10 +4620,21 @@ module.exports = function getSteps(options) {
       if (commaSeparatedB64Args.length === 0) {
         return;
       }
-
-      const args = commaSeparatedB64Args
-        .split(',')
-        .map((b64Arg) => makeUint8Array(Buffer.from(b64Arg, 'base64')));
+      const rawArgs = commaSeparatedB64Args.split(',');
+
+      // Optionally parse ctxAppIds
+      const args = [];
+      for (let i = 0; i < rawArgs.length; i++) {
+        let b64Arg = rawArgs[i];
+        if (b64Arg.includes('ctxAppIdx')) {
+          // Retrieve the n'th app id in the saved array of app ids
+          b64Arg = b64Arg.split(':');
+          const appID = this.appIDs[parseInt(b64Arg[1], 10)];
+          args.push(algosdk.encodeUint64(appID));
+        } else {
+          args.push(makeUint8Array(Buffer.from(b64Arg, 'base64')));
+        }
+      }
       this.encodedMethodArguments.push(...args);
     }
   );
@@ -4788,19 +4655,21 @@ module.exports = function getSteps(options) {
     globalInts,
     localBytes,
     localInts,
-    extraPages
+    extraPages,
+    note
   ) {
     // open and load in approval program
     let approvalProgramBytes;
     if (approvalProgramFile !== '') {
-      const resouce = await loadResource(approvalProgramFile);
-      approvalProgramBytes = makeUint8Array(resouce);
+      approvalProgramBytes = await compileProgram(
+        this.v2Client,
+        approvalProgramFile
+      );
     }
     // open and load in clear program
     let clearProgramBytes;
     if (clearProgramFile !== '') {
-      const resouce = await loadResource(clearProgramFile);
-      clearProgramBytes = makeUint8Array(resouce);
+      clearProgramBytes = await compileProgram(this.v2Client, clearProgramFile);
     }
 
     const methodArgs = [];
@@ -4856,6 +4725,7 @@ module.exports = function getSteps(options) {
       numLocalInts: localInts,
       numLocalByteSlices: localBytes,
       extraPages,
+      note,
       signer: this.transactionSigner,
     });
     this.composerMethods.push(this.method);
@@ -4989,6 +4859,30 @@ module.exports = function getSteps(options) {
     }
   );
 
+  Given('I add the nonce {string}', function (nonce) {
+    this.nonce = nonce;
+  });
+
+  Given(
+    'I add a nonced method call with the transient account, the current application, suggested params, on complete {string}, current transaction signer, current method arguments.',
+    async function (onComplete) {
+      const nonce = makeUint8Array(Buffer.from(this.nonce));
+      await addMethodCallToComposer.call(
+        this,
+        this.transientAccount.addr,
+        onComplete,
+        '',
+        '',
+        undefined,
+        undefined,
+        undefined,
+        undefined,
+        undefined,
+        nonce
+      );
+    }
+  );
+
   When(
     'I add the current transaction with signer to the composer.',
     function () {
@@ -5131,6 +5025,153 @@ module.exports = function getSteps(options) {
     }
   );
 
+  Then(
+    'The app should have returned ABI types {string}.',
+    function (expectedAbiTypesString) {
+      // Each return from a unique ABI method call is separated by a colon
+      const expectedAbiTypes = expectedAbiTypesString.split(':');
+
+      const { methodResults } = this.composerExecuteResponse;
+      assert.strictEqual(methodResults.length, expectedAbiTypes.length);
+      assert.strictEqual(methodResults.length, this.composerMethods.length);
+
+      for (let i = 0; i < methodResults.length; i++) {
+        const expectedAbiType = expectedAbiTypes[i];
+        const method = this.composerMethods[i];
+        const actualResult = methodResults[i];
+
+        if (actualResult.decodeError) {
+          throw actualResult.decodeError;
+        }
+
+        const returnType = method.returns.type;
+        if (returnType === 'void') {
+          assert.strictEqual(expectedAbiType, returnType);
+          continue;
+        }
+
+        const expectedType = algosdk.ABIType.from(expectedAbiType);
+        const decodedResult = expectedType.decode(actualResult.rawReturnValue);
+        const roundTripResult = expectedType.encode(decodedResult);
+
+        assert.deepStrictEqual(roundTripResult, actualResult.rawReturnValue);
+      }
+    }
+  );
+
+  Then(
+    'The {int}th atomic result for randomInt\\({int}) proves correct',
+    function (resultIndex, methodArg) {
+      // Return format for randomInt method
+      const methodReturnType = algosdk.ABIType.from('(uint64,byte[17])');
+      const actualResult = this.composerExecuteResponse.methodResults[
+        resultIndex
+      ];
+      const resultArray = methodReturnType.decode(actualResult.rawReturnValue);
+      assert.strictEqual(resultArray.length, 2);
+      const [randomIntResult, witnessResult] = resultArray;
+
+      // Check the random int against the witness
+      const witnessHash = genericHash(witnessResult).slice(0, 8);
+      const witness = algosdk.bytesToBigInt(witnessHash);
+      const quotient = witness % BigInt(methodArg);
+      assert.strictEqual(quotient, randomIntResult);
+    }
+  );
+
+  Then(
+    'The {int}th atomic result for randElement\\({string}) proves correct',
+    function (resultIndex, methodArg) {
+      // Return format for randElement method
+      const methodReturnType = algosdk.ABIType.from('(byte,byte[17])');
+      const actualResult = this.composerExecuteResponse.methodResults[
+        resultIndex
+      ];
+      const resultArray = methodReturnType.decode(actualResult.rawReturnValue);
+      assert.strictEqual(resultArray.length, 2);
+      const [randomResult, witnessResult] = resultArray;
+
+      // Check the random character against the witness
+      const witnessHash = genericHash(witnessResult).slice(0, 8);
+      const witness = algosdk.bytesToBigInt(witnessHash);
+      const quotient = witness % BigInt(methodArg.length);
+      assert.strictEqual(
+        methodArg[quotient],
+        Buffer.from(makeUint8Array([randomResult])).toString('utf-8')
+      );
+    }
+  );
+
+  // Helper function to parse paths with '.' delimiters
+  function glom(result, pathString) {
+    const keys = pathString.split('.');
+    let item = result;
+    for (let i = 0; i < keys.length; i++) {
+      let index = parseInt(keys[i], 10);
+      if (Number.isNaN(index)) {
+        index = keys[i];
+      }
+      item = item[index];
+    }
+    return item;
+  }
+
+  Then(
+    'I can dig the {int}th atomic result with path {string} and see the value {string}',
+    function (index, pathString, expectedResult) {
+      let actualResult = this.composerExecuteResponse.methodResults[index]
+        .txInfo;
+      actualResult = glom(actualResult, pathString);
+
+      assert.strictEqual(expectedResult, actualResult.toString());
+    }
+  );
+
+  Then(
+    'I dig into the paths {string} of the resulting atomic transaction tree I see group ids and they are all the same',
+    function (pathString) {
+      const paths = pathString.split(':').map((p) => p.split(','));
+      let groupID;
+
+      for (let i = 0; i < paths.length; i++) {
+        const pathItem = paths[i];
+        let actualResults = this.composerExecuteResponse.methodResults;
+        for (let j = 0; j < pathItem.length; j++) {
+          const itxnIndex = pathItem[j];
+          if (j === 0) {
+            actualResults = actualResults[itxnIndex].txInfo;
+          } else {
+            actualResults = actualResults['inner-txns'][itxnIndex];
+          }
+
+          const thisGroupID = actualResults.txn.txn.group;
+          if (j === 0) {
+            groupID = thisGroupID;
+          } else {
+            assert.strictEqual(groupID, thisGroupID);
+          }
+        }
+      }
+    }
+  );
+
+  Then(
+    'The {int}th atomic result for {string} satisfies the regex {string}',
+    function (index, method, regexString) {
+      // Only allow the "spin()" method
+      assert.strictEqual(method, 'spin()');
+
+      const abiType = algosdk.ABIType.from(
+        '(byte[3],byte[17],byte[17],byte[17])'
+      );
+      const actualResult = this.composerExecuteResponse.methodResults[index];
+      let spin = abiType.decode(actualResult.rawReturnValue)[0];
+      spin = Buffer.from(spin).toString('utf-8');
+
+      assert.ok(spin.match(regexString));
+    }
+  );
+
   if (!options.ignoreReturn) {
     return steps;
   }