diff --git a/.dockerignore b/.dockerignore
index 8bea0aeeb1a..559b271bf38 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -1,6 +1,15 @@
 Dockerfile*
 .dockerignore
 
+# Yarn
+.pnp.*
+.yarn/*
+!.yarn/patches
+!.yarn/plugins
+!.yarn/releases
+!.yarn/sdks
+!.yarn/versions
+
 packages
 **/package.tgz
 **/target
diff --git a/.github/scripts/acvm_js-build.sh b/.github/scripts/acvm_js-build.sh
new file mode 100755
index 00000000000..0565a9bb89f
--- /dev/null
+++ b/.github/scripts/acvm_js-build.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+set -eu
+
+.github/scripts/install_wasm-bindgen.sh
+yarn workspace @noir-lang/acvm_js build
diff --git a/.github/scripts/acvm_js-test-browser.sh b/.github/scripts/acvm_js-test-browser.sh
new file mode 100755
index 00000000000..598c98dadf2
--- /dev/null
+++ b/.github/scripts/acvm_js-test-browser.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+set -eu
+
+npx playwright install && npx playwright install-deps
+yarn workspace @noir-lang/acvm_js test:browser
diff --git a/.github/scripts/acvm_js-test.sh b/.github/scripts/acvm_js-test.sh
new file mode 100755
index 00000000000..d5519d26cc4
--- /dev/null
+++ b/.github/scripts/acvm_js-test.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+set -eu
+
+yarn workspace @noir-lang/acvm_js test
diff --git a/.github/scripts/backend-barretenberg-build.sh b/.github/scripts/backend-barretenberg-build.sh
new file mode 100755
index 00000000000..d90995397d8
--- /dev/null
+++ b/.github/scripts/backend-barretenberg-build.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+set -eu
+
+yarn workspace @noir-lang/backend_barretenberg build
diff --git a/.github/scripts/backend-barretenberg-test.sh b/.github/scripts/backend-barretenberg-test.sh
new file mode 100755
index 00000000000..1bd6f8e410d
--- /dev/null
+++ b/.github/scripts/backend-barretenberg-test.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+set -eu
+
+yarn workspace @noir-lang/backend_barretenberg test
diff --git a/.github/scripts/install_wasm-bindgen.sh b/.github/scripts/install_wasm-bindgen.sh
new file mode 100755
index 00000000000..b8c41393ab0
--- /dev/null
+++ b/.github/scripts/install_wasm-bindgen.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+set -eu
+
+curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash
+cargo-binstall wasm-bindgen-cli --version 0.2.86 -y
diff --git a/.github/scripts/integration-test.sh b/.github/scripts/integration-test.sh
new file mode 100755
index 00000000000..4e1b52cedf9
--- /dev/null
+++ b/.github/scripts/integration-test.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+set -eu
+
+apt-get install libc++-dev -y
+npx playwright install && npx playwright install-deps
+yarn workspace integration-tests test
\ No newline at end of file
diff --git a/.github/scripts/nargo-build.sh b/.github/scripts/nargo-build.sh
new file mode 100755
index 00000000000..2115732ab7e
--- /dev/null
+++ b/.github/scripts/nargo-build.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+set -eu
+
+export SOURCE_DATE_EPOCH=$(date +%s)
+export GIT_DIRTY=false
+export GIT_COMMIT=$(git rev-parse --verify HEAD)
+
+cargo build --release
diff --git a/.github/scripts/nargo-test.sh b/.github/scripts/nargo-test.sh
new file mode 100755
index 00000000000..9234df7bf5c
--- /dev/null
+++ b/.github/scripts/nargo-test.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+set -eu
+
+apt-get install -y curl libc++-dev
+
+export SOURCE_DATE_EPOCH=$(date +%s)
+export GIT_DIRTY=false
+export GIT_COMMIT=$(git rev-parse --verify HEAD)
+
+cargo test --workspace --locked --release
\ No newline at end of file
diff --git a/.github/scripts/noir-codegen-build.sh b/.github/scripts/noir-codegen-build.sh
new file mode 100755
index 00000000000..d42be4d676e
--- /dev/null
+++ b/.github/scripts/noir-codegen-build.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+set -eu
+
+yarn workspace @noir-lang/noir_codegen build
diff --git a/.github/scripts/noir-codegen-test.sh b/.github/scripts/noir-codegen-test.sh
new file mode 100755
index 00000000000..6f603f65507
--- /dev/null
+++ b/.github/scripts/noir-codegen-test.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+set -eu
+
+yarn workspace @noir-lang/noir_codegen test
\ No newline at end of file
diff --git a/.github/scripts/noir-js-build.sh b/.github/scripts/noir-js-build.sh
new file mode 100755
index 00000000000..04367e41342
--- /dev/null
+++ b/.github/scripts/noir-js-build.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+set -eu
+
+yarn workspace @noir-lang/noir_js build
diff --git a/.github/scripts/noir-js-test.sh b/.github/scripts/noir-js-test.sh
new file mode 100755
index 00000000000..b5fe34038fe
--- /dev/null
+++ b/.github/scripts/noir-js-test.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+set -eu
+
+./scripts/nargo_compile_noir_js_assert_lt.sh
+rm -rf /usr/src/noir/tooling/noir_js/test/noir_compiled_examples/assert_lt/target/debug_assert_lt.json
+yarn workspace @noir-lang/noir_js test
\ No newline at end of file
diff --git a/.github/scripts/noir-js-types-build.sh b/.github/scripts/noir-js-types-build.sh
new file mode 100755
index 00000000000..77b08651d68
--- /dev/null
+++ b/.github/scripts/noir-js-types-build.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+set -eu
+
+yarn workspace @noir-lang/types build
\ No newline at end of file
diff --git a/.github/scripts/noir-wasm-build.sh b/.github/scripts/noir-wasm-build.sh
new file mode 100755
index 00000000000..4523751612d
--- /dev/null
+++ b/.github/scripts/noir-wasm-build.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+set -eu
+
+.github/scripts/install_wasm-bindgen.sh
+yarn workspace @noir-lang/noir_wasm build
\ No newline at end of file
diff --git a/.github/scripts/noir-wasm-test-browser.sh b/.github/scripts/noir-wasm-test-browser.sh
new file mode 100755
index 00000000000..4b584abce23
--- /dev/null
+++ b/.github/scripts/noir-wasm-test-browser.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+set -eu
+
+./scripts/nargo_compile_wasm_fixtures.sh
+npx playwright install && npx playwright install-deps
+yarn workspace @noir-lang/noir_wasm test:browser
\ No newline at end of file
diff --git a/.github/scripts/noir-wasm-test.sh b/.github/scripts/noir-wasm-test.sh
new file mode 100755
index 00000000000..03e1bac2330
--- /dev/null
+++ b/.github/scripts/noir-wasm-test.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+set -eu
+
+./scripts/nargo_compile_wasm_fixtures.sh
+yarn workspace @noir-lang/noir_wasm test:node
+npx playwright install && npx playwright install-deps
+yarn workspace @noir-lang/noir_wasm test:browser
diff --git a/.github/scripts/noirc-abi-build.sh b/.github/scripts/noirc-abi-build.sh
new file mode 100755
index 00000000000..d5da6deaa0f
--- /dev/null
+++ b/.github/scripts/noirc-abi-build.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+set -eu
+
+.github/scripts/install_wasm-bindgen.sh
+yarn workspace @noir-lang/noirc_abi build
diff --git a/.github/scripts/noirc-abi-test-browser.sh b/.github/scripts/noirc-abi-test-browser.sh
new file mode 100755
index 00000000000..7a966cb5e94
--- /dev/null
+++ b/.github/scripts/noirc-abi-test-browser.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+set -eu
+
+npx playwright install && npx playwright install-deps
+yarn workspace @noir-lang/noirc_abi test:browser
diff --git a/.github/scripts/noirc-abi-test.sh b/.github/scripts/noirc-abi-test.sh
new file mode 100755
index 00000000000..39ca0a44b07
--- /dev/null
+++ b/.github/scripts/noirc-abi-test.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+set -eu
+
+yarn workspace @noir-lang/noirc_abi test
diff --git a/.github/workflows/docker-test-flow.yml b/.github/workflows/docker-test-flow.yml
new file mode 100644
index 00000000000..0277812f5ae
--- /dev/null
+++ b/.github/workflows/docker-test-flow.yml
@@ -0,0 +1,733 @@
+name: Test Nargo and JS packages
+
+on:
+  push:
+    branches:
+      - 'master'
+
+concurrency:
+  group: ${{ github.workflow }}-${{ github.head_ref || github.ref || github.run_id }}
+  cancel-in-progress: true
+
+jobs:
+  build-base-nargo:
+    name: Build base nargo docker image
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout code
+        uses: actions/checkout@v4
+      - name: Get current date
+        id: date
+        run: echo "date=$(date +'%Y.%m.%d.%H.%M')" >> $GITHUB_STATE
+      - name: prepare docker images tags
+        id: prep
+        run: |
+          REGISTRY="ghcr.io"
+          IMG_RAW="${REGISTRY}/${{ github.repository }}"
+          IMAGE=$(echo "$IMG_RAW" | tr '[:upper:]' '[:lower:]')
+          TAGS="${IMAGE}:${{ github.sha }}-nargo"
+          FULL_TAGS="${TAGS},${IMAGE}:latest-nargo,${IMAGE}:v${{ steps.date.outputs.date }}-nargo"
+          echo "tags=$FULL_TAGS" >> $GITHUB_OUTPUT
+          echo "image=$IMAGE" >> $GITHUB_OUTPUT
+      - name: Set up Docker Buildx
+        id: buildx
+        uses: docker/setup-buildx-action@v3
+      - name: Login to GitHub Container Registry        
+        uses: docker/login-action@v3
+        with:
+          registry: ghcr.io
+          username: ${{ github.actor }}
+          password: ${{ secrets.GITHUB_TOKEN }}
+      - name: Build nargo base dockerfile
+        uses: docker/build-push-action@v5
+        with:
+          context: .
+          file: Dockerfile.ci
+          tags: ${{ steps.prep.outputs.tags }}
+          target: base-nargo
+          cache-from: type=gha
+          cache-to: type=gha,mode=max
+          push: true
+
+  build-base-js:
+    name: Build base js docker image
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout code
+        uses: actions/checkout@v4
+      - name: Get current date
+        id: date
+        run: echo "date=$(date +'%Y.%m.%d.%H.%M')" >> $GITHUB_STATE
+      - name: Prepare docker image tags
+        id: prep
+        run: |
+          REGISTRY="ghcr.io"
+          IMG_RAW="${REGISTRY}/${{ github.repository }}"
+          IMAGE=$(echo "$IMG_RAW" | tr '[:upper:]' '[:lower:]')
+          TAGS="${IMAGE}:${{ github.sha }}-js"
+          FULL_TAGS="${TAGS},${IMAGE}:latest-js,${IMAGE}:v${{ steps.date.outputs.date }}-js"
+          echo "tags=$FULL_TAGS" >> $GITHUB_OUTPUT
+          echo "image=$IMAGE" >> $GITHUB_OUTPUT
+      - name: Set up Docker Buildx
+        id: buildx
+        uses: docker/setup-buildx-action@v3
+      - name: Login to GitHub Container Registry        
+        uses: docker/login-action@v3
+        with:
+          registry: ghcr.io
+          username: ${{ github.actor }}
+          password: ${{ secrets.GITHUB_TOKEN }}
+      - name: Build js base dockerfile
+        uses: docker/build-push-action@v5
+        with:
+          context: .
+          file: Dockerfile.ci
+          tags: ${{ steps.prep.outputs.tags }}
+          target: base-js
+          cache-from: type=gha
+          cache-to: type=gha,mode=max
+          push: true
+
+  artifact-nargo:
+    name: Artifact nargo 
+    runs-on: ubuntu-latest
+    needs: [build-base-nargo]
+    container:
+      image: ghcr.io/noir-lang/noir:${{ github.sha }}-nargo
+      credentials:
+        username: ${{ github.actor }}
+        password: ${{ secrets.github_token }}
+    steps:
+      - name: Artifact nargo
+        uses: actions/upload-artifact@v4
+        with:
+          name: nargo
+          path: /usr/src/noir/target/release/nargo
+          if-no-files-found: error
+          compression-level: 0
+  
+  test-nargo:
+    name: Test nargo 
+    runs-on: ubuntu-latest
+    needs: [build-base-nargo]
+    container:
+      image: ghcr.io/noir-lang/noir:${{ github.sha }}-nargo
+      credentials:
+        username: ${{ github.actor }}
+        password: ${{ secrets.github_token }}
+    steps:
+      - name: Test
+        working-directory: /usr/src/noir
+        run: |
+          .github/scripts/nargo-test.sh
+
+  build-noir-wasm:
+    name: Build noir wasm
+    runs-on: ubuntu-latest
+    needs: [build-base-js]
+    container:
+      image: ghcr.io/noir-lang/noir:${{ github.sha }}-js
+      credentials:
+        username: ${{ github.actor }}
+        password: ${{ secrets.github_token }}
+    steps:
+      - name: Build
+        working-directory: /usr/src/noir
+        run: |
+          ./.github/scripts/noir-wasm-build.sh
+      - name: Artifact
+        uses: actions/upload-artifact@v4
+        with:
+          name: noir_wasm
+          path: /usr/src/noir/compiler/wasm/outputs/out/noir_wasm
+          retention-days: 10
+      
+  test-noir-wasm:
+    name: Test noir wasm
+    runs-on: ubuntu-latest
+    needs: [build-base-js, artifact-nargo, build-noir-wasm]
+    container:
+      image: ghcr.io/noir-lang/noir:${{ github.sha }}-js
+      credentials:
+        username: ${{ github.actor }}
+        password: ${{ secrets.github_token }}
+    steps:
+      - name: Download nargo
+        uses: actions/download-artifact@v4
+        with:
+          name: nargo
+          path: /usr/src/noir/target/release
+      - name: Prep downloaded artifact
+        run: |
+          chmod +x /usr/src/noir/target/release/nargo
+      - name: Download noir_wasm artifact
+        uses: actions/download-artifact@v4
+        with:
+          name: noir_wasm
+          path: /usr/src/noir/compiler/wasm
+      - name: Test
+        working-directory: /usr/src/noir
+        run: |
+          ./.github/scripts/noir-wasm-test.sh
+
+  test-noir-wasm-browser:
+    name: Test noir wasm browser
+    runs-on: ubuntu-latest
+    needs: [build-base-js, artifact-nargo, build-noir-wasm]
+    container:
+      image: ghcr.io/noir-lang/noir:${{ github.sha }}-js
+      credentials:
+        username: ${{ github.actor }}
+        password: ${{ secrets.github_token }}
+    steps:
+      - name: Download nargo
+        uses: actions/download-artifact@v4
+        with:
+          name: nargo
+          path: /usr/src/noir/target/release
+      - name: Prep downloaded artifact
+        run: |
+          chmod +x /usr/src/noir/target/release/nargo
+      - name: Download noir_wasm artifact
+        uses: actions/download-artifact@v4
+        with:
+          name: noir_wasm
+          path: /usr/src/noir/compiler/wasm
+      - name: Test
+        working-directory: /usr/src/noir
+        run: |
+          ./.github/scripts/noir-wasm-test-browser.sh
+
+  build-acvm_js:
+    name: Build acvm js
+    runs-on: ubuntu-latest
+    needs: [build-base-js]
+    container:
+      image: ghcr.io/noir-lang/noir:${{ github.sha }}-js
+      credentials:
+        username: ${{ github.actor }}
+        password: ${{ secrets.github_token }}
+    steps:
+      - name: Build
+        working-directory: /usr/src/noir
+        run: |
+          ./.github/scripts/acvm_js-build.sh
+      - name: Artifact
+        uses: actions/upload-artifact@v4
+        with:
+          name: acvm_js
+          path:
+            /usr/src/noir/acvm-repo/acvm_js/outputs/out/acvm_js
+          if-no-files-found: error
+          compression-level: 0
+
+  test-acvm_js:
+    name: Test acvm js
+    runs-on: ubuntu-latest
+    needs: [build-base-js, build-acvm_js]
+    container:
+      image: ghcr.io/noir-lang/noir:${{ github.sha }}-js
+      credentials:
+        username: ${{ github.actor }}
+        password: ${{ secrets.github_token }}
+    steps:
+      - name: Download acvm js
+        uses: actions/download-artifact@v4
+        with:
+          name: acvm_js
+          path: |
+            /usr/src/noir/acvm-repo/acvm_js
+      - name: Test
+        working-directory: /usr/src/noir
+        run: |
+          ./.github/scripts/acvm_js-test.sh
+
+  test-acvm_js-browser:
+    name: Test acvm js browser
+    runs-on: ubuntu-latest
+    needs: [build-base-js, build-acvm_js]
+    container:
+      image: ghcr.io/noir-lang/noir:${{ github.sha }}-js
+      credentials:
+        username: ${{ github.actor }}
+        password: ${{ secrets.github_token }}
+    steps:
+      - name: Download acvm js
+        uses: actions/download-artifact@v4
+        with:
+          name: acvm_js
+          path: |
+            /usr/src/noir/acvm-repo/acvm_js
+      - name: Test
+        working-directory: /usr/src/noir
+        run: |
+          ./.github/scripts/acvm_js-test-browser.sh
+
+  build-noirc-abi:
+    name: Build noirc abi
+    runs-on: ubuntu-latest
+    needs: [build-base-js]
+    container:
+      image: ghcr.io/noir-lang/noir:${{ github.sha }}-js
+      credentials:
+        username: ${{ github.actor }}
+        password: ${{ secrets.github_token }}
+    steps:
+      - name: Build
+        working-directory: /usr/src/noir
+        run: |
+          ./.github/scripts/noirc-abi-build.sh
+      - name: Artifact
+        uses: actions/upload-artifact@v4
+        with:
+          name: noirc_abi_wasm
+          path:
+            /usr/src/noir/tooling/noirc_abi_wasm/outputs/out/noirc_abi_wasm
+          if-no-files-found: error
+          compression-level: 0
+
+  test-noirc-abi:
+    name: Test noirc abi
+    runs-on: ubuntu-latest
+    needs: [build-base-js, build-noirc-abi]
+    container:
+      image: ghcr.io/noir-lang/noir:${{ github.sha }}-js
+      credentials:
+        username: ${{ github.actor }}
+        password: ${{ secrets.github_token }}
+    steps:
+      - name: Download noirc abi
+        uses: actions/download-artifact@v4
+        with:
+          name: noirc_abi_wasm
+          path: |
+            /usr/src/noir/tooling/noirc_abi_wasm
+      - name: Test
+        working-directory: /usr/src/noir
+        run: |
+          ./.github/scripts/noirc-abi-test.sh
+
+  test-noirc-abi-browser:
+    name: Test noirc abi browser
+    runs-on: ubuntu-latest
+    needs: [build-base-js, build-noirc-abi]
+    container:
+      image: ghcr.io/noir-lang/noir:${{ github.sha }}-js
+      credentials:
+        username: ${{ github.actor }}
+        password: ${{ secrets.github_token }}
+    steps:
+      - name: Download noirc abi
+        uses: actions/download-artifact@v4
+        with:
+          name: noirc_abi_wasm
+          path: |
+            /usr/src/noir/tooling/noirc_abi_wasm
+      - name: Test
+        working-directory: /usr/src/noir
+        run: |
+          ./.github/scripts/noirc-abi-test-browser.sh
+
+  build-noir-js-types:
+    name: Build noir js types
+    runs-on: ubuntu-latest
+    needs: [build-base-js, build-noirc-abi]
+    container:
+      image: ghcr.io/noir-lang/noir:${{ github.sha }}-js
+      credentials:
+        username: ${{ github.actor }}
+        password: ${{ secrets.github_token }}
+    steps:
+      - name: Download noirc abi
+        uses: actions/download-artifact@v4
+        with:
+          name: noirc_abi_wasm
+          path: |
+            /usr/src/noir/tooling/noirc_abi_wasm
+      - name: Build
+        working-directory: /usr/src/noir
+        run: |
+          ./.github/scripts/noir-js-types-build.sh
+      - name: Artifact
+        uses: actions/upload-artifact@v4
+        with:
+          name: noir-js-types
+          path: |
+            /usr/src/noir/tooling/noir_js_types/lib
+          if-no-files-found: error
+          compression-level: 0
+
+  build-barretenberg-backend:
+    name: Build Barretenberg backend
+    runs-on: ubuntu-latest
+    needs: [build-base-js, build-noirc-abi, build-noir-js-types]
+    container:
+      image: ghcr.io/noir-lang/noir:${{ github.sha }}-js
+      credentials:
+        username: ${{ github.actor }}
+        password: ${{ secrets.github_token }}
+    steps:
+      - name: Download noirc abi
+        uses: actions/download-artifact@v4
+        with:
+          name: noirc_abi_wasm
+          path: |
+            /usr/src/noir/tooling/noirc_abi_wasm
+      - name: Download noir js types
+        uses: actions/download-artifact@v4
+        with:
+          name: noir-js-types
+          path: /usr/src/noir/tooling/noir_js_types/lib/
+      - name: Build
+        working-directory: /usr/src/noir
+        run: |
+          ./.github/scripts/backend-barretenberg-build.sh
+      - name: Artifact
+        uses: actions/upload-artifact@v4
+        with:
+          name: barretenberg-backend
+          path:
+            /usr/src/noir/tooling/noir_js_backend_barretenberg/lib
+          if-no-files-found: error
+          compression-level: 0
+
+  test-barretenberg-backend:
+    name: Test Barretenberg backend
+    runs-on: ubuntu-latest
+    needs: [build-base-js, build-noirc-abi, build-noir-js-types, build-barretenberg-backend]
+    container:
+      image: ghcr.io/noir-lang/noir:${{ github.sha }}-js
+      credentials:
+        username: ${{ github.actor }}
+        password: ${{ secrets.github_token }}
+    steps:
+      - name: Download artifact
+        uses: actions/download-artifact@v4
+        with:
+          name: noirc_abi_wasm
+          path: |
+            /usr/src/noir/tooling/noirc_abi_wasm
+      - name: Download noir js types
+        uses: actions/download-artifact@v4
+        with:
+          name: noir-js-types
+          path: /usr/src/noir/tooling/noir_js_types/lib/
+      - name: Download Backend barretenberg
+        uses: actions/download-artifact@v4
+        with:
+          name: barretenberg-backend
+          path:
+            /usr/src/noir/tooling/noir_js_backend_barretenberg/lib
+      - name: Test
+        working-directory: /usr/src/noir
+        run: |
+          ./.github/scripts/backend-barretenberg-test.sh
+
+  build-noir_js:
+    name: Build noirjs
+    runs-on: ubuntu-latest
+    needs: [build-base-js, artifact-nargo, build-noirc-abi, build-acvm_js, build-barretenberg-backend, build-noir-js-types]
+    container:
+      image: ghcr.io/noir-lang/noir:${{ github.sha }}-js
+      credentials:
+        username: ${{ github.actor }}
+        password: ${{ secrets.github_token }}
+    steps:
+      - name: Download nargo
+        uses: actions/download-artifact@v4
+        with:
+          name: nargo
+          path: /usr/src/noir/target/release
+      - name: prep downloaded artifact
+        run: |
+          chmod +x /usr/src/noir/target/release/nargo
+      - name: Download noirc abi
+        uses: actions/download-artifact@v4
+        with:
+          name: noirc_abi_wasm
+          path: |
+            /usr/src/noir/tooling/noirc_abi_wasm
+      - name: Download acvm js
+        uses: actions/download-artifact@v4
+        with:
+          name: acvm_js
+          path: |
+            /usr/src/noir/acvm-repo/acvm_js
+      - name: Download Barretenberg backend
+        uses: actions/download-artifact@v4
+        with:
+          name: barretenberg-backend
+          path:
+            /usr/src/noir/tooling/noir_js_backend_barretenberg/lib
+      - name: Download noir js types
+        uses: actions/download-artifact@v4
+        with:
+          name: noir-js-types
+          path: |
+            /usr/src/noir/tooling/noir_js_types/lib
+      - name: Build
+        working-directory: /usr/src/noir
+        run: |
+          ./.github/scripts/noir-js-build.sh
+      - name: Artifact
+        uses: actions/upload-artifact@v4
+        with:
+          name: noir_js
+          path:
+            /usr/src/noir/tooling/noir_js/lib
+
+  test-noir_js:
+    name: Test noirjs
+    runs-on: ubuntu-latest
+    needs: [
+      build-base-js,
+      build-noirc-abi,
+      artifact-nargo,
+      build-acvm_js,
+      build-barretenberg-backend,
+      build-noir_js,
+      build-noir-js-types
+    ]
+    container:
+      image: ghcr.io/noir-lang/noir:${{ github.sha }}-js
+      credentials:
+        username: ${{ github.actor }}
+        password: ${{ secrets.github_token }}
+    steps:
+      - name: Download nargo
+        uses: actions/download-artifact@v4
+        with:
+          name: nargo
+          path: /usr/src/noir/target/release
+      - name: Prep downloaded artifact
+        run: |
+          chmod +x /usr/src/noir/target/release/nargo
+      - name: Download noirc abi
+        uses: actions/download-artifact@v4
+        with:
+          name: noirc_abi_wasm
+          path: |
+            /usr/src/noir/tooling/noirc_abi_wasm
+      - name: Download acvm js
+        uses: actions/download-artifact@v4
+        with:
+          name: acvm_js
+          path: |
+            /usr/src/noir/acvm-repo/acvm_js
+      - name: Download Barretenberg backend
+        uses: actions/download-artifact@v4
+        with:
+          name: barretenberg-backend
+          path:
+            /usr/src/noir/tooling/noir_js_backend_barretenberg/lib
+      - name: Download noir js types
+        uses: actions/download-artifact@v4
+        with:
+          name: noir-js-types
+          path: |
+            /usr/src/noir/tooling/noir_js_types/lib
+      - name: Download noir js
+        uses: actions/download-artifact@v4
+        with:
+          name: noir_js
+          path:
+            /usr/src/noir/tooling/noir_js/lib
+      - name: Test
+        working-directory: /usr/src/noir
+        run: |
+          ./.github/scripts/noir-js-test.sh
+
+  build-noir_codegen:
+    name: Build noir codegen
+    runs-on: ubuntu-latest
+    needs: [build-base-js, build-noirc-abi, build-acvm_js,  build-noir-js-types, build-noir_js]
+    container:
+      image: ghcr.io/noir-lang/noir:${{ github.sha }}-js
+      credentials:
+        username: ${{ github.actor }}
+        password: ${{ secrets.github_token }}
+    steps:
+      - name: Download nargo
+        uses: actions/download-artifact@v4
+        with:
+          name: nargo
+          path: /usr/src/noir/target/release
+      - name: Prep downloaded artifact
+        run: |
+          chmod +x /usr/src/noir/target/release/nargo
+      - name: Download noirc abi package
+        uses: actions/download-artifact@v4
+        with:
+          name: noirc_abi_wasm
+          path: /usr/src/noir/tooling/noirc_abi_wasm
+      - name: Download acvm js
+        uses: actions/download-artifact@v4
+        with:
+          name: acvm_js
+          path: /usr/src/noir/acvm-repo/acvm_js
+      - name: Download noir js types
+        uses: actions/download-artifact@v4
+        with:
+          name: noir-js-types
+          path: |
+            /usr/src/noir/tooling/noir_js_types/lib
+      - name: Download noir js
+        uses: actions/download-artifact@v4
+        with:
+          name: noir_js
+          path:
+            /usr/src/noir/tooling/noir_js/lib
+      - name: Build
+        working-directory: /usr/src/noir
+        run: |
+          ./.github/scripts/noir-codegen-build.sh
+      - name: Artifact
+        uses: actions/upload-artifact@v4
+        with:
+          name: noir_codegen
+          path:
+            /usr/src/noir/tooling/noir_codegen/lib
+  
+  test-noir_codegen:
+    name: Test noir codegen
+    runs-on: ubuntu-latest
+    needs: [build-base-js, artifact-nargo, build-noirc-abi, build-acvm_js,  build-noir-js-types, build-noir_js, build-noir_codegen]
+    container:
+      image: ghcr.io/noir-lang/noir:${{ github.sha }}-js
+      credentials:
+        username: ${{ github.actor }}
+        password: ${{ secrets.github_token }}
+    steps:
+      - name: Download nargo
+        uses: actions/download-artifact@v4
+        with:
+          name: nargo
+          path: /usr/src/noir/target/release
+      - name: Prep downloaded artifact
+        run: |
+          chmod +x /usr/src/noir/target/release/nargo
+      - name: Download noirc abi
+        uses: actions/download-artifact@v4
+        with:
+          name: noirc_abi_wasm
+          path: /usr/src/noir/tooling/noirc_abi_wasm
+      - name: Download acvm js
+        uses: actions/download-artifact@v4
+        with:
+          name: acvm_js
+          path: /usr/src/noir/acvm-repo/acvm_js
+      - name: Download noir js types
+        uses: actions/download-artifact@v4
+        with:
+          name: noir-js-types
+          path: |
+            /usr/src/noir/tooling/noir_js_types/lib
+      - name: Download noir js
+        uses: actions/download-artifact@v4
+        with:
+          name: noir_js
+          path:
+            /usr/src/noir/tooling/noir_js/lib
+      - name: Download noir codegen
+        uses: actions/download-artifact@v4
+        with:
+          name: noir_codegen
+          path:
+            /usr/src/noir/tooling/noir_codegen/lib
+      - name: Test
+        working-directory: /usr/src/noir
+        run: |
+          ./.github/scripts/noir-codegen-test.sh
+
+  test-integration:
+    name: Integration test
+    runs-on: ubuntu-latest
+    needs: [
+      build-base-js,
+      artifact-nargo,
+      build-noir-wasm,
+      build-noirc-abi,
+      build-acvm_js,
+      build-noir-js-types,
+      build-noir_js,
+      build-barretenberg-backend
+    ]
+    container:
+      image: ghcr.io/noir-lang/noir:${{ github.sha }}-js
+      credentials:
+        username: ${{ github.actor }}
+        password: ${{ secrets.github_token }}
+    steps:
+      - name: Download nargo
+        uses: actions/download-artifact@v4
+        with:
+          name: nargo
+          path: /usr/src/noir/target/release
+      - name: Prep downloaded artifact
+        run: |
+          chmod +x /usr/src/noir/target/release/nargo
+      - name: Download noir wasm
+        uses: actions/download-artifact@v4
+        with:
+          name: noir_wasm
+          path: /usr/src/noir/compiler/wasm
+      - name: Download noirc abi
+        uses: actions/download-artifact@v4
+        with:
+          name: noirc_abi_wasm
+          path: /usr/src/noir/tooling/noirc_abi_wasm
+      - name: Download acvm js
+        uses: actions/download-artifact@v4
+        with:
+          name: acvm_js
+          path: /usr/src/noir/acvm-repo/acvm_js
+      - name: Download noir js types
+        uses: actions/download-artifact@v4
+        with:
+          name: noir-js-types
+          path: |
+            /usr/src/noir/tooling/noir_js_types/lib
+      - name: Download noir js
+        uses: actions/download-artifact@v4
+        with:
+          name: noir_js
+          path:
+            /usr/src/noir/tooling/noir_js/lib
+      - name: Download Barretenberg backend
+        uses: actions/download-artifact@v4
+        with:
+          name: barretenberg-backend
+          path:
+            /usr/src/noir/tooling/noir_js_backend_barretenberg/lib
+      - name: Test
+        working-directory: /usr/src/noir
+        run: |
+          ./.github/scripts/integration-test.sh
+
+  tests-end:
+    name: End
+    runs-on: ubuntu-latest
+    if: ${{ always() }}
+    needs: 
+      - test-nargo
+      - test-noirc-abi
+      - test-noirc-abi-browser
+      - test-noir-wasm
+      - test-noir-wasm-browser
+      - test-integration
+      - test-noir_codegen
+      - test-acvm_js
+      - test-acvm_js-browser
+      - test-barretenberg-backend
+      - test-noir_js
+    
+    steps:
+        - name: Report overall success
+          run: |
+            if [[ $FAIL == true ]]; then
+                exit 1
+            else
+                exit 0
+            fi
+          env:
+            FAIL: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'skipped') }}
diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml
index 9ea2f1e1491..07b39d7627c 100644
--- a/.github/workflows/publish-docs.yml
+++ b/.github/workflows/publish-docs.yml
@@ -46,6 +46,7 @@ jobs:
           github-token: ${{ secrets.GITHUB_TOKEN }}
           enable-github-deployment: false
           deploy-message: "Deploy from GitHub Actions for tag ${{ inputs.noir-ref }}"
+          enable-commit-comment: false
         env:
           NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
           NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
diff --git a/Cargo.lock b/Cargo.lock
index 7a3fc491912..8aca450c6cd 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -58,7 +58,9 @@ version = "0.38.0"
 dependencies = [
  "acir",
  "blake2",
+ "blake3",
  "k256",
+ "keccak",
  "p256",
  "sha2",
  "sha3",
@@ -344,6 +346,12 @@ dependencies = [
  "rand 0.8.5",
 ]
 
+[[package]]
+name = "arrayref"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545"
+
 [[package]]
 name = "arrayvec"
 version = "0.7.4"
@@ -548,6 +556,19 @@ dependencies = [
  "digest",
 ]
 
+[[package]]
+name = "blake3"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0231f06152bf547e9c2b5194f247cd97aacf6dcd8b15d8e5ec0663f64580da87"
+dependencies = [
+ "arrayref",
+ "arrayvec",
+ "cc",
+ "cfg-if 1.0.0",
+ "constant_time_eq",
+]
+
 [[package]]
 name = "block-buffer"
 version = "0.10.4"
@@ -970,6 +991,12 @@ dependencies = [
  "unicode-xid",
 ]
 
+[[package]]
+name = "constant_time_eq"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2"
+
 [[package]]
 name = "convert_case"
 version = "0.4.0"
@@ -2061,7 +2088,7 @@ dependencies = [
  "httpdate",
  "itoa",
  "pin-project-lite",
- "socket2",
+ "socket2 0.4.9",
  "tokio",
  "tower-service",
  "tracing",
@@ -2443,9 +2470,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
 
 [[package]]
 name = "libc"
-version = "0.2.147"
+version = "0.2.151"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
+checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
 
 [[package]]
 name = "libm"
@@ -2604,9 +2631,9 @@ dependencies = [
 
 [[package]]
 name = "mio"
-version = "0.8.8"
+version = "0.8.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2"
+checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09"
 dependencies = [
  "libc",
  "wasi 0.11.0+wasi-snapshot-preview1",
@@ -2656,7 +2683,6 @@ dependencies = [
  "assert_fs",
  "async-lsp",
  "backend-interface",
- "bb_abstraction_leaks",
  "bn254_blackbox_solver",
  "build-data",
  "clap",
@@ -3196,9 +3222,9 @@ dependencies = [
 
 [[package]]
 name = "pin-project-lite"
-version = "0.2.10"
+version = "0.2.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57"
+checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
 
 [[package]]
 name = "pin-utils"
@@ -4386,6 +4412,16 @@ dependencies = [
  "winapi",
 ]
 
+[[package]]
+name = "socket2"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9"
+dependencies = [
+ "libc",
+ "windows-sys 0.48.0",
+]
+
 [[package]]
 name = "spin"
 version = "0.5.2"
@@ -4677,27 +4713,26 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
 
 [[package]]
 name = "tokio"
-version = "1.29.1"
+version = "1.35.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da"
+checksum = "841d45b238a16291a4e1584e61820b8ae57d696cc5015c459c229ccc6990cc1c"
 dependencies = [
- "autocfg",
  "backtrace",
  "bytes",
  "libc",
  "mio",
  "num_cpus",
  "pin-project-lite",
- "socket2",
+ "socket2 0.5.5",
  "tokio-macros",
  "windows-sys 0.48.0",
 ]
 
 [[package]]
 name = "tokio-macros"
-version = "2.1.0"
+version = "2.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
+checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
 dependencies = [
  "proc-macro2",
  "quote",
diff --git a/Dockerfile.ci b/Dockerfile.ci
index 9ca995fd94f..57dcbe9cfee 100644
--- a/Dockerfile.ci
+++ b/Dockerfile.ci
@@ -1,40 +1,31 @@
-FROM rust:1-slim-bookworm as test-base
+FROM rust:1.71.1-slim-bookworm as base
 RUN apt-get update && apt-get upgrade -y && apt-get install build-essential git -y
 WORKDIR /usr/src/noir
-COPY . .
-RUN ./scripts/bootstrap_native.sh
-ENV PATH="${PATH}:/usr/src/noir/target/release/"
+ENV PATH="${PATH}:/usr/src/noir/target/release"
 
-FROM test-base as test-cargo
-RUN apt-get install -y curl libc++-dev
-RUN ./scripts/test_native.sh
+FROM base as base-nargo
+COPY . .
+RUN .github/scripts/nargo-build.sh
 
-FROM test-base as test-js
-RUN apt-get install pkg-config libssl-dev -y
-RUN ./scripts/install_wasm-bindgen.sh
+FROM base as base-js
 RUN apt-get install -y ca-certificates curl gnupg
 RUN mkdir -p /etc/apt/keyrings
 RUN curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
 RUN echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list
 RUN apt-get update && apt-get install nodejs -y
 RUN corepack enable
-RUN yarn --immutable
 RUN apt-get install -y jq
-RUN yarn build
-RUN yarn workspace @noir-lang/acvm_js test
-RUN npx playwright install && npx playwright install-deps
-RUN yarn workspace @noir-lang/acvm_js test:browser
-RUN yarn workspace @noir-lang/noirc_abi test
-RUN yarn workspace @noir-lang/noirc_abi test:browser
-RUN yarn workspace @noir-lang/backend_barretenberg test
-RUN ./scripts/nargo_compile_noir_js_assert_lt.sh
-RUN rm -rf /usr/src/noir/tooling/noir_js/test/noir_compiled_examples/assert_lt/target/debug_assert_lt.json
-RUN yarn workspace @noir-lang/noir_js test
-RUN ./scripts/nargo_compile_wasm_fixtures.sh
-RUN yarn workspace @noir-lang/noir_wasm test:node
-RUN yarn workspace @noir-lang/noir_wasm test:browser
-RUN ./scripts/nargo_compile_noir_codegen_assert_lt.sh
-RUN rm -rf /usr/src/noir/tooling/noir_codegen/test/assert_lt/target/debug_assert_lt.json
-RUN yarn workspace @noir-lang/noir_codegen test
-RUN apt-get install -y libc++-dev
-RUN yarn test:integration
+COPY yarn.lock package.json .yarnrc.yml ./
+COPY .yarn/ ./.yarn/
+COPY ./acvm-repo/acvm_js/package.json ./acvm-repo/acvm_js/
+COPY ./tooling/noirc_abi_wasm/package.json ./tooling/noirc_abi_wasm/
+COPY ./compiler/wasm/package.json ./compiler/wasm/
+COPY ./tooling/noir_js_types/package.json ./tooling/noir_js_types/
+COPY ./tooling/noir_js_backend_barretenberg/package.json ./tooling/noir_js_backend_barretenberg/
+COPY ./tooling/noir_js/package.json ./tooling/noir_js/
+COPY ./tooling/noir_codegen/package.json ./tooling/noir_codegen/
+COPY ./compiler/integration-tests/package.json ./compiler/integration-tests/
+COPY ./release-tests/package.json ./release-tests/
+COPY ./docs/package.json ./docs/
+RUN yarn --immutable
+COPY . .
diff --git a/acvm-repo/acir/codegen/acir.cpp b/acvm-repo/acir/codegen/acir.cpp
index 21a98b3ee2c..2b217c7e93d 100644
--- a/acvm-repo/acir/codegen/acir.cpp
+++ b/acvm-repo/acir/codegen/acir.cpp
@@ -70,6 +70,15 @@ namespace Circuit {
             static Blake2s bincodeDeserialize(std::vector<uint8_t>);
         };
 
+        struct Blake3 {
+            std::vector<Circuit::FunctionInput> inputs;
+            std::vector<Circuit::Witness> outputs;
+
+            friend bool operator==(const Blake3&, const Blake3&);
+            std::vector<uint8_t> bincodeSerialize() const;
+            static Blake3 bincodeDeserialize(std::vector<uint8_t>);
+        };
+
         struct SchnorrVerify {
             Circuit::FunctionInput public_key_x;
             Circuit::FunctionInput public_key_y;
@@ -102,15 +111,6 @@ namespace Circuit {
             static PedersenHash bincodeDeserialize(std::vector<uint8_t>);
         };
 
-        struct HashToField128Security {
-            std::vector<Circuit::FunctionInput> inputs;
-            Circuit::Witness output;
-
-            friend bool operator==(const HashToField128Security&, const HashToField128Security&);
-            std::vector<uint8_t> bincodeSerialize() const;
-            static HashToField128Security bincodeDeserialize(std::vector<uint8_t>);
-        };
-
         struct EcdsaSecp256k1 {
             std::vector<Circuit::FunctionInput> public_key_x;
             std::vector<Circuit::FunctionInput> public_key_y;
@@ -164,20 +164,27 @@ namespace Circuit {
             static Keccak256VariableLength bincodeDeserialize(std::vector<uint8_t>);
         };
 
+        struct Keccakf1600 {
+            std::vector<Circuit::FunctionInput> inputs;
+            std::vector<Circuit::Witness> outputs;
+
+            friend bool operator==(const Keccakf1600&, const Keccakf1600&);
+            std::vector<uint8_t> bincodeSerialize() const;
+            static Keccakf1600 bincodeDeserialize(std::vector<uint8_t>);
+        };
+
         struct RecursiveAggregation {
             std::vector<Circuit::FunctionInput> verification_key;
             std::vector<Circuit::FunctionInput> proof;
             std::vector<Circuit::FunctionInput> public_inputs;
             Circuit::FunctionInput key_hash;
-            std::optional<std::vector<Circuit::FunctionInput>> input_aggregation_object;
-            std::vector<Circuit::Witness> output_aggregation_object;
 
             friend bool operator==(const RecursiveAggregation&, const RecursiveAggregation&);
             std::vector<uint8_t> bincodeSerialize() const;
             static RecursiveAggregation bincodeDeserialize(std::vector<uint8_t>);
         };
 
-        std::variant<AND, XOR, RANGE, SHA256, Blake2s, SchnorrVerify, PedersenCommitment, PedersenHash, HashToField128Security, EcdsaSecp256k1, EcdsaSecp256r1, FixedBaseScalarMul, Keccak256, Keccak256VariableLength, RecursiveAggregation> value;
+        std::variant<AND, XOR, RANGE, SHA256, Blake2s, Blake3, SchnorrVerify, PedersenCommitment, PedersenHash, EcdsaSecp256k1, EcdsaSecp256r1, FixedBaseScalarMul, Keccak256, Keccak256VariableLength, Keccakf1600, RecursiveAggregation> value;
 
         friend bool operator==(const BlackBoxFuncCall&, const BlackBoxFuncCall&);
         std::vector<uint8_t> bincodeSerialize() const;
@@ -408,15 +415,6 @@ namespace Circuit {
             static Keccak256 bincodeDeserialize(std::vector<uint8_t>);
         };
 
-        struct HashToField128Security {
-            Circuit::HeapVector message;
-            Circuit::RegisterIndex output;
-
-            friend bool operator==(const HashToField128Security&, const HashToField128Security&);
-            std::vector<uint8_t> bincodeSerialize() const;
-            static HashToField128Security bincodeDeserialize(std::vector<uint8_t>);
-        };
-
         struct EcdsaSecp256k1 {
             Circuit::HeapVector hashed_msg;
             Circuit::HeapArray public_key_x;
@@ -483,7 +481,7 @@ namespace Circuit {
             static FixedBaseScalarMul bincodeDeserialize(std::vector<uint8_t>);
         };
 
-        std::variant<Sha256, Blake2s, Keccak256, HashToField128Security, EcdsaSecp256k1, EcdsaSecp256r1, SchnorrVerify, PedersenCommitment, PedersenHash, FixedBaseScalarMul> value;
+        std::variant<Sha256, Blake2s, Keccak256, EcdsaSecp256k1, EcdsaSecp256r1, SchnorrVerify, PedersenCommitment, PedersenHash, FixedBaseScalarMul> value;
 
         friend bool operator==(const BlackBoxOp&, const BlackBoxOp&);
         std::vector<uint8_t> bincodeSerialize() const;
@@ -1839,6 +1837,47 @@ Circuit::BlackBoxFuncCall::Blake2s serde::Deserializable<Circuit::BlackBoxFuncCa
     return obj;
 }
 
+namespace Circuit {
+
+    inline bool operator==(const BlackBoxFuncCall::Blake3 &lhs, const BlackBoxFuncCall::Blake3 &rhs) {
+        if (!(lhs.inputs == rhs.inputs)) { return false; }
+        if (!(lhs.outputs == rhs.outputs)) { return false; }
+        return true;
+    }
+
+    inline std::vector<uint8_t> BlackBoxFuncCall::Blake3::bincodeSerialize() const {
+        auto serializer = serde::BincodeSerializer();
+        serde::Serializable<BlackBoxFuncCall::Blake3>::serialize(*this, serializer);
+        return std::move(serializer).bytes();
+    }
+
+    inline BlackBoxFuncCall::Blake3 BlackBoxFuncCall::Blake3::bincodeDeserialize(std::vector<uint8_t> input) {
+        auto deserializer = serde::BincodeDeserializer(input);
+        auto value = serde::Deserializable<BlackBoxFuncCall::Blake3>::deserialize(deserializer);
+        if (deserializer.get_buffer_offset() < input.size()) {
+            throw serde::deserialization_error("Some input bytes were not read");
+        }
+        return value;
+    }
+
+} // end of namespace Circuit
+
+template <>
+template <typename Serializer>
+void serde::Serializable<Circuit::BlackBoxFuncCall::Blake3>::serialize(const Circuit::BlackBoxFuncCall::Blake3 &obj, Serializer &serializer) {
+    serde::Serializable<decltype(obj.inputs)>::serialize(obj.inputs, serializer);
+    serde::Serializable<decltype(obj.outputs)>::serialize(obj.outputs, serializer);
+}
+
+template <>
+template <typename Deserializer>
+Circuit::BlackBoxFuncCall::Blake3 serde::Deserializable<Circuit::BlackBoxFuncCall::Blake3>::deserialize(Deserializer &deserializer) {
+    Circuit::BlackBoxFuncCall::Blake3 obj;
+    obj.inputs = serde::Deserializable<decltype(obj.inputs)>::deserialize(deserializer);
+    obj.outputs = serde::Deserializable<decltype(obj.outputs)>::deserialize(deserializer);
+    return obj;
+}
+
 namespace Circuit {
 
     inline bool operator==(const BlackBoxFuncCall::SchnorrVerify &lhs, const BlackBoxFuncCall::SchnorrVerify &rhs) {
@@ -1977,47 +2016,6 @@ Circuit::BlackBoxFuncCall::PedersenHash serde::Deserializable<Circuit::BlackBoxF
     return obj;
 }
 
-namespace Circuit {
-
-    inline bool operator==(const BlackBoxFuncCall::HashToField128Security &lhs, const BlackBoxFuncCall::HashToField128Security &rhs) {
-        if (!(lhs.inputs == rhs.inputs)) { return false; }
-        if (!(lhs.output == rhs.output)) { return false; }
-        return true;
-    }
-
-    inline std::vector<uint8_t> BlackBoxFuncCall::HashToField128Security::bincodeSerialize() const {
-        auto serializer = serde::BincodeSerializer();
-        serde::Serializable<BlackBoxFuncCall::HashToField128Security>::serialize(*this, serializer);
-        return std::move(serializer).bytes();
-    }
-
-    inline BlackBoxFuncCall::HashToField128Security BlackBoxFuncCall::HashToField128Security::bincodeDeserialize(std::vector<uint8_t> input) {
-        auto deserializer = serde::BincodeDeserializer(input);
-        auto value = serde::Deserializable<BlackBoxFuncCall::HashToField128Security>::deserialize(deserializer);
-        if (deserializer.get_buffer_offset() < input.size()) {
-            throw serde::deserialization_error("Some input bytes were not read");
-        }
-        return value;
-    }
-
-} // end of namespace Circuit
-
-template <>
-template <typename Serializer>
-void serde::Serializable<Circuit::BlackBoxFuncCall::HashToField128Security>::serialize(const Circuit::BlackBoxFuncCall::HashToField128Security &obj, Serializer &serializer) {
-    serde::Serializable<decltype(obj.inputs)>::serialize(obj.inputs, serializer);
-    serde::Serializable<decltype(obj.output)>::serialize(obj.output, serializer);
-}
-
-template <>
-template <typename Deserializer>
-Circuit::BlackBoxFuncCall::HashToField128Security serde::Deserializable<Circuit::BlackBoxFuncCall::HashToField128Security>::deserialize(Deserializer &deserializer) {
-    Circuit::BlackBoxFuncCall::HashToField128Security obj;
-    obj.inputs = serde::Deserializable<decltype(obj.inputs)>::deserialize(deserializer);
-    obj.output = serde::Deserializable<decltype(obj.output)>::deserialize(deserializer);
-    return obj;
-}
-
 namespace Circuit {
 
     inline bool operator==(const BlackBoxFuncCall::EcdsaSecp256k1 &lhs, const BlackBoxFuncCall::EcdsaSecp256k1 &rhs) {
@@ -2247,6 +2245,47 @@ Circuit::BlackBoxFuncCall::Keccak256VariableLength serde::Deserializable<Circuit
     return obj;
 }
 
+namespace Circuit {
+
+    inline bool operator==(const BlackBoxFuncCall::Keccakf1600 &lhs, const BlackBoxFuncCall::Keccakf1600 &rhs) {
+        if (!(lhs.inputs == rhs.inputs)) { return false; }
+        if (!(lhs.outputs == rhs.outputs)) { return false; }
+        return true;
+    }
+
+    inline std::vector<uint8_t> BlackBoxFuncCall::Keccakf1600::bincodeSerialize() const {
+        auto serializer = serde::BincodeSerializer();
+        serde::Serializable<BlackBoxFuncCall::Keccakf1600>::serialize(*this, serializer);
+        return std::move(serializer).bytes();
+    }
+
+    inline BlackBoxFuncCall::Keccakf1600 BlackBoxFuncCall::Keccakf1600::bincodeDeserialize(std::vector<uint8_t> input) {
+        auto deserializer = serde::BincodeDeserializer(input);
+        auto value = serde::Deserializable<BlackBoxFuncCall::Keccakf1600>::deserialize(deserializer);
+        if (deserializer.get_buffer_offset() < input.size()) {
+            throw serde::deserialization_error("Some input bytes were not read");
+        }
+        return value;
+    }
+
+} // end of namespace Circuit
+
+template <>
+template <typename Serializer>
+void serde::Serializable<Circuit::BlackBoxFuncCall::Keccakf1600>::serialize(const Circuit::BlackBoxFuncCall::Keccakf1600 &obj, Serializer &serializer) {
+    serde::Serializable<decltype(obj.inputs)>::serialize(obj.inputs, serializer);
+    serde::Serializable<decltype(obj.outputs)>::serialize(obj.outputs, serializer);
+}
+
+template <>
+template <typename Deserializer>
+Circuit::BlackBoxFuncCall::Keccakf1600 serde::Deserializable<Circuit::BlackBoxFuncCall::Keccakf1600>::deserialize(Deserializer &deserializer) {
+    Circuit::BlackBoxFuncCall::Keccakf1600 obj;
+    obj.inputs = serde::Deserializable<decltype(obj.inputs)>::deserialize(deserializer);
+    obj.outputs = serde::Deserializable<decltype(obj.outputs)>::deserialize(deserializer);
+    return obj;
+}
+
 namespace Circuit {
 
     inline bool operator==(const BlackBoxFuncCall::RecursiveAggregation &lhs, const BlackBoxFuncCall::RecursiveAggregation &rhs) {
@@ -2254,8 +2293,6 @@ namespace Circuit {
         if (!(lhs.proof == rhs.proof)) { return false; }
         if (!(lhs.public_inputs == rhs.public_inputs)) { return false; }
         if (!(lhs.key_hash == rhs.key_hash)) { return false; }
-        if (!(lhs.input_aggregation_object == rhs.input_aggregation_object)) { return false; }
-        if (!(lhs.output_aggregation_object == rhs.output_aggregation_object)) { return false; }
         return true;
     }
 
@@ -2283,8 +2320,6 @@ void serde::Serializable<Circuit::BlackBoxFuncCall::RecursiveAggregation>::seria
     serde::Serializable<decltype(obj.proof)>::serialize(obj.proof, serializer);
     serde::Serializable<decltype(obj.public_inputs)>::serialize(obj.public_inputs, serializer);
     serde::Serializable<decltype(obj.key_hash)>::serialize(obj.key_hash, serializer);
-    serde::Serializable<decltype(obj.input_aggregation_object)>::serialize(obj.input_aggregation_object, serializer);
-    serde::Serializable<decltype(obj.output_aggregation_object)>::serialize(obj.output_aggregation_object, serializer);
 }
 
 template <>
@@ -2295,8 +2330,6 @@ Circuit::BlackBoxFuncCall::RecursiveAggregation serde::Deserializable<Circuit::B
     obj.proof = serde::Deserializable<decltype(obj.proof)>::deserialize(deserializer);
     obj.public_inputs = serde::Deserializable<decltype(obj.public_inputs)>::deserialize(deserializer);
     obj.key_hash = serde::Deserializable<decltype(obj.key_hash)>::deserialize(deserializer);
-    obj.input_aggregation_object = serde::Deserializable<decltype(obj.input_aggregation_object)>::deserialize(deserializer);
-    obj.output_aggregation_object = serde::Deserializable<decltype(obj.output_aggregation_object)>::deserialize(deserializer);
     return obj;
 }
 
@@ -2465,47 +2498,6 @@ Circuit::BlackBoxOp::Keccak256 serde::Deserializable<Circuit::BlackBoxOp::Keccak
     return obj;
 }
 
-namespace Circuit {
-
-    inline bool operator==(const BlackBoxOp::HashToField128Security &lhs, const BlackBoxOp::HashToField128Security &rhs) {
-        if (!(lhs.message == rhs.message)) { return false; }
-        if (!(lhs.output == rhs.output)) { return false; }
-        return true;
-    }
-
-    inline std::vector<uint8_t> BlackBoxOp::HashToField128Security::bincodeSerialize() const {
-        auto serializer = serde::BincodeSerializer();
-        serde::Serializable<BlackBoxOp::HashToField128Security>::serialize(*this, serializer);
-        return std::move(serializer).bytes();
-    }
-
-    inline BlackBoxOp::HashToField128Security BlackBoxOp::HashToField128Security::bincodeDeserialize(std::vector<uint8_t> input) {
-        auto deserializer = serde::BincodeDeserializer(input);
-        auto value = serde::Deserializable<BlackBoxOp::HashToField128Security>::deserialize(deserializer);
-        if (deserializer.get_buffer_offset() < input.size()) {
-            throw serde::deserialization_error("Some input bytes were not read");
-        }
-        return value;
-    }
-
-} // end of namespace Circuit
-
-template <>
-template <typename Serializer>
-void serde::Serializable<Circuit::BlackBoxOp::HashToField128Security>::serialize(const Circuit::BlackBoxOp::HashToField128Security &obj, Serializer &serializer) {
-    serde::Serializable<decltype(obj.message)>::serialize(obj.message, serializer);
-    serde::Serializable<decltype(obj.output)>::serialize(obj.output, serializer);
-}
-
-template <>
-template <typename Deserializer>
-Circuit::BlackBoxOp::HashToField128Security serde::Deserializable<Circuit::BlackBoxOp::HashToField128Security>::deserialize(Deserializer &deserializer) {
-    Circuit::BlackBoxOp::HashToField128Security obj;
-    obj.message = serde::Deserializable<decltype(obj.message)>::deserialize(deserializer);
-    obj.output = serde::Deserializable<decltype(obj.output)>::deserialize(deserializer);
-    return obj;
-}
-
 namespace Circuit {
 
     inline bool operator==(const BlackBoxOp::EcdsaSecp256k1 &lhs, const BlackBoxOp::EcdsaSecp256k1 &rhs) {
diff --git a/acvm-repo/acir/src/circuit/black_box_functions.rs b/acvm-repo/acir/src/circuit/black_box_functions.rs
index dec60c09077..445da50ac81 100644
--- a/acvm-repo/acir/src/circuit/black_box_functions.rs
+++ b/acvm-repo/acir/src/circuit/black_box_functions.rs
@@ -19,6 +19,8 @@ pub enum BlackBoxFunc {
     SHA256,
     /// Calculates the Blake2s hash of the inputs.
     Blake2s,
+    /// Calculates the Blake3 hash of the inputs.
+    Blake3,
     /// Verifies a Schnorr signature over a curve which is "pairing friendly" with the curve on which the ACIR circuit is defined.
     ///
     /// The exact curve which this signature uses will vary based on the curve being used by ACIR.
@@ -30,12 +32,6 @@ pub enum BlackBoxFunc {
     PedersenCommitment,
     /// Calculates a Pedersen hash to the inputs.
     PedersenHash,
-    /// Hashes a set of inputs and applies the field modulus to the result
-    /// to return a value which can be represented as a [`FieldElement`][acir_field::FieldElement]
-    ///
-    /// This is implemented using the `Blake2s` hash function.
-    /// The "128" in the name specifies that this function should have 128 bits of security.
-    HashToField128Security,
     /// Verifies a ECDSA signature over the secp256k1 curve.
     EcdsaSecp256k1,
     /// Verifies a ECDSA signature over the secp256r1 curve.
@@ -44,6 +40,8 @@ pub enum BlackBoxFunc {
     FixedBaseScalarMul,
     /// Calculates the Keccak256 hash of the inputs.
     Keccak256,
+    /// Keccak Permutation function of 1600 width
+    Keccakf1600,
     /// Compute a recursive aggregation object when verifying a proof inside another circuit.
     /// This outputted aggregation object will then be either checked in a top-level verifier or aggregated upon again.
     RecursiveAggregation,
@@ -61,15 +59,16 @@ impl BlackBoxFunc {
             BlackBoxFunc::SHA256 => "sha256",
             BlackBoxFunc::SchnorrVerify => "schnorr_verify",
             BlackBoxFunc::Blake2s => "blake2s",
+            BlackBoxFunc::Blake3 => "blake3",
             BlackBoxFunc::PedersenCommitment => "pedersen_commitment",
             BlackBoxFunc::PedersenHash => "pedersen_hash",
-            BlackBoxFunc::HashToField128Security => "hash_to_field_128_security",
             BlackBoxFunc::EcdsaSecp256k1 => "ecdsa_secp256k1",
             BlackBoxFunc::FixedBaseScalarMul => "fixed_base_scalar_mul",
             BlackBoxFunc::AND => "and",
             BlackBoxFunc::XOR => "xor",
             BlackBoxFunc::RANGE => "range",
             BlackBoxFunc::Keccak256 => "keccak256",
+            BlackBoxFunc::Keccakf1600 => "keccakf1600",
             BlackBoxFunc::RecursiveAggregation => "recursive_aggregation",
             BlackBoxFunc::EcdsaSecp256r1 => "ecdsa_secp256r1",
         }
@@ -79,9 +78,9 @@ impl BlackBoxFunc {
             "sha256" => Some(BlackBoxFunc::SHA256),
             "schnorr_verify" => Some(BlackBoxFunc::SchnorrVerify),
             "blake2s" => Some(BlackBoxFunc::Blake2s),
+            "blake3" => Some(BlackBoxFunc::Blake3),
             "pedersen_commitment" => Some(BlackBoxFunc::PedersenCommitment),
             "pedersen_hash" => Some(BlackBoxFunc::PedersenHash),
-            "hash_to_field_128_security" => Some(BlackBoxFunc::HashToField128Security),
             "ecdsa_secp256k1" => Some(BlackBoxFunc::EcdsaSecp256k1),
             "ecdsa_secp256r1" => Some(BlackBoxFunc::EcdsaSecp256r1),
             "fixed_base_scalar_mul" => Some(BlackBoxFunc::FixedBaseScalarMul),
@@ -89,6 +88,7 @@ impl BlackBoxFunc {
             "xor" => Some(BlackBoxFunc::XOR),
             "range" => Some(BlackBoxFunc::RANGE),
             "keccak256" => Some(BlackBoxFunc::Keccak256),
+            "keccakf1600" => Some(BlackBoxFunc::Keccakf1600),
             "recursive_aggregation" => Some(BlackBoxFunc::RecursiveAggregation),
             _ => None,
         }
diff --git a/acvm-repo/acir/src/circuit/mod.rs b/acvm-repo/acir/src/circuit/mod.rs
index a1e63b4fe54..b248b30b1d9 100644
--- a/acvm-repo/acir/src/circuit/mod.rs
+++ b/acvm-repo/acir/src/circuit/mod.rs
@@ -250,6 +250,64 @@ mod tests {
             input: FunctionInput { witness: Witness(1), num_bits: 8 },
         })
     }
+    fn keccakf1600_opcode() -> Opcode {
+        Opcode::BlackBoxFuncCall(BlackBoxFuncCall::Keccakf1600 {
+            inputs: vec![
+                FunctionInput { witness: Witness(1), num_bits: 64 },
+                FunctionInput { witness: Witness(2), num_bits: 64 },
+                FunctionInput { witness: Witness(3), num_bits: 64 },
+                FunctionInput { witness: Witness(4), num_bits: 64 },
+                FunctionInput { witness: Witness(5), num_bits: 64 },
+                FunctionInput { witness: Witness(6), num_bits: 64 },
+                FunctionInput { witness: Witness(7), num_bits: 64 },
+                FunctionInput { witness: Witness(8), num_bits: 64 },
+                FunctionInput { witness: Witness(9), num_bits: 64 },
+                FunctionInput { witness: Witness(10), num_bits: 64 },
+                FunctionInput { witness: Witness(11), num_bits: 64 },
+                FunctionInput { witness: Witness(12), num_bits: 64 },
+                FunctionInput { witness: Witness(13), num_bits: 64 },
+                FunctionInput { witness: Witness(14), num_bits: 64 },
+                FunctionInput { witness: Witness(15), num_bits: 64 },
+                FunctionInput { witness: Witness(16), num_bits: 64 },
+                FunctionInput { witness: Witness(17), num_bits: 64 },
+                FunctionInput { witness: Witness(18), num_bits: 64 },
+                FunctionInput { witness: Witness(19), num_bits: 64 },
+                FunctionInput { witness: Witness(20), num_bits: 64 },
+                FunctionInput { witness: Witness(21), num_bits: 64 },
+                FunctionInput { witness: Witness(22), num_bits: 64 },
+                FunctionInput { witness: Witness(23), num_bits: 64 },
+                FunctionInput { witness: Witness(24), num_bits: 64 },
+                FunctionInput { witness: Witness(25), num_bits: 64 },
+            ],
+            outputs: vec![
+                Witness(26),
+                Witness(27),
+                Witness(28),
+                Witness(29),
+                Witness(30),
+                Witness(31),
+                Witness(32),
+                Witness(33),
+                Witness(34),
+                Witness(35),
+                Witness(36),
+                Witness(37),
+                Witness(38),
+                Witness(39),
+                Witness(40),
+                Witness(41),
+                Witness(42),
+                Witness(43),
+                Witness(44),
+                Witness(45),
+                Witness(46),
+                Witness(47),
+                Witness(48),
+                Witness(49),
+                Witness(50),
+            ],
+        })
+    }
 
     #[test]
     fn serialization_roundtrip() {
@@ -284,6 +342,7 @@ mod tests {
                 }),
                 range_opcode(),
                 and_opcode(),
+                keccakf1600_opcode(),
             ],
             private_parameters: BTreeSet::new(),
             public_parameters: PublicInputs(BTreeSet::from_iter(vec![Witness(2)])),
diff --git a/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs b/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs
index 70821913836..6e4d7fdd660 100644
--- a/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs
+++ b/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs
@@ -39,6 +39,10 @@ pub enum BlackBoxFuncCall {
         inputs: Vec<FunctionInput>,
         outputs: Vec<Witness>,
     },
+    Blake3 {
+        inputs: Vec<FunctionInput>,
+        outputs: Vec<Witness>,
+    },
     SchnorrVerify {
         public_key_x: FunctionInput,
         public_key_y: FunctionInput,
@@ -56,12 +60,6 @@ pub enum BlackBoxFuncCall {
         domain_separator: u32,
         output: Witness,
     },
-    // 128 here specifies that this function
-    // should have 128 bits of security
-    HashToField128Security {
-        inputs: Vec<FunctionInput>,
-        output: Witness,
-    },
     EcdsaSecp256k1 {
         public_key_x: Vec<FunctionInput>,
         public_key_y: Vec<FunctionInput>,
@@ -94,6 +92,10 @@ pub enum BlackBoxFuncCall {
         var_message_size: FunctionInput,
         outputs: Vec<Witness>,
     },
+    Keccakf1600 {
+        inputs: Vec<FunctionInput>,
+        outputs: Vec<Witness>,
+    },
     RecursiveAggregation {
         verification_key: Vec<FunctionInput>,
         proof: Vec<FunctionInput>,
@@ -105,17 +107,6 @@ pub enum BlackBoxFuncCall {
         /// The circuit implementing this opcode can use this hash to ensure that the
         /// key provided to the circuit matches the key produced by the circuit creator
         key_hash: FunctionInput,
-        /// An aggregation object is blob of data that the top-level verifier must run some proof system specific
-        /// algorithm on to complete verification. The size is proof system specific and will be set by the backend integrating this opcode.
-        /// The input aggregation object is only not `None` when we are verifying a previous recursive aggregation in
-        /// the current circuit. If this is the first recursive aggregation there is no input aggregation object.
-        /// It is left to the backend to determine how to handle when there is no input aggregation object.
-        input_aggregation_object: Option<Vec<FunctionInput>>,
-        /// This is the result of a recursive aggregation and is what will be fed into the next verifier.
-        /// The next verifier can either perform a final verification (returning true or false)
-        /// or perform another recursive aggregation where this output aggregation object
-        /// will be the input aggregation object of the next recursive aggregation.
-        output_aggregation_object: Vec<Witness>,
     },
 }
 
@@ -127,15 +118,16 @@ impl BlackBoxFuncCall {
             BlackBoxFuncCall::RANGE { .. } => BlackBoxFunc::RANGE,
             BlackBoxFuncCall::SHA256 { .. } => BlackBoxFunc::SHA256,
             BlackBoxFuncCall::Blake2s { .. } => BlackBoxFunc::Blake2s,
+            BlackBoxFuncCall::Blake3 { .. } => BlackBoxFunc::Blake3,
             BlackBoxFuncCall::SchnorrVerify { .. } => BlackBoxFunc::SchnorrVerify,
             BlackBoxFuncCall::PedersenCommitment { .. } => BlackBoxFunc::PedersenCommitment,
             BlackBoxFuncCall::PedersenHash { .. } => BlackBoxFunc::PedersenHash,
-            BlackBoxFuncCall::HashToField128Security { .. } => BlackBoxFunc::HashToField128Security,
             BlackBoxFuncCall::EcdsaSecp256k1 { .. } => BlackBoxFunc::EcdsaSecp256k1,
             BlackBoxFuncCall::EcdsaSecp256r1 { .. } => BlackBoxFunc::EcdsaSecp256r1,
             BlackBoxFuncCall::FixedBaseScalarMul { .. } => BlackBoxFunc::FixedBaseScalarMul,
             BlackBoxFuncCall::Keccak256 { .. } => BlackBoxFunc::Keccak256,
             BlackBoxFuncCall::Keccak256VariableLength { .. } => BlackBoxFunc::Keccak256,
+            BlackBoxFuncCall::Keccakf1600 { .. } => BlackBoxFunc::Keccakf1600,
             BlackBoxFuncCall::RecursiveAggregation { .. } => BlackBoxFunc::RecursiveAggregation,
         }
     }
@@ -148,10 +140,11 @@ impl BlackBoxFuncCall {
         match self {
             BlackBoxFuncCall::SHA256 { inputs, .. }
             | BlackBoxFuncCall::Blake2s { inputs, .. }
+            | BlackBoxFuncCall::Blake3 { inputs, .. }
             | BlackBoxFuncCall::Keccak256 { inputs, .. }
+            | BlackBoxFuncCall::Keccakf1600 { inputs, .. }
             | BlackBoxFuncCall::PedersenCommitment { inputs, .. }
-            | BlackBoxFuncCall::PedersenHash { inputs, .. }
-            | BlackBoxFuncCall::HashToField128Security { inputs, .. } => inputs.to_vec(),
+            | BlackBoxFuncCall::PedersenHash { inputs, .. } => inputs.to_vec(),
             BlackBoxFuncCall::AND { lhs, rhs, .. } | BlackBoxFuncCall::XOR { lhs, rhs, .. } => {
                 vec![*lhs, *rhs]
             }
@@ -219,16 +212,12 @@ impl BlackBoxFuncCall {
                 proof,
                 public_inputs,
                 key_hash,
-                ..
             } => {
                 let mut inputs = Vec::new();
                 inputs.extend(key.iter().copied());
                 inputs.extend(proof.iter().copied());
                 inputs.extend(public_inputs.iter().copied());
                 inputs.push(*key_hash);
-                // NOTE: we do not return an input aggregation object as it will either be non-existent for the first recursive aggregation
-                // or the output aggregation object of a previous recursive aggregation. We do not simulate recursive aggregation
-                // thus the input aggregation object will always be unassigned until proving
                 inputs
             }
         }
@@ -238,20 +227,20 @@ impl BlackBoxFuncCall {
         match self {
             BlackBoxFuncCall::SHA256 { outputs, .. }
             | BlackBoxFuncCall::Blake2s { outputs, .. }
+            | BlackBoxFuncCall::Blake3 { outputs, .. }
             | BlackBoxFuncCall::Keccak256 { outputs, .. }
-            | BlackBoxFuncCall::RecursiveAggregation {
-                output_aggregation_object: outputs, ..
-            } => outputs.to_vec(),
+            | BlackBoxFuncCall::Keccakf1600 { outputs, .. } => outputs.to_vec(),
             BlackBoxFuncCall::AND { output, .. }
             | BlackBoxFuncCall::XOR { output, .. }
-            | BlackBoxFuncCall::HashToField128Security { output, .. }
             | BlackBoxFuncCall::SchnorrVerify { output, .. }
             | BlackBoxFuncCall::EcdsaSecp256k1 { output, .. }
             | BlackBoxFuncCall::PedersenHash { output, .. }
             | BlackBoxFuncCall::EcdsaSecp256r1 { output, .. } => vec![*output],
             BlackBoxFuncCall::FixedBaseScalarMul { outputs, .. }
             | BlackBoxFuncCall::PedersenCommitment { outputs, .. } => vec![outputs.0, outputs.1],
-            BlackBoxFuncCall::RANGE { .. } => vec![],
+            BlackBoxFuncCall::RANGE { .. } | BlackBoxFuncCall::RecursiveAggregation { .. } => {
+                vec![]
+            }
             BlackBoxFuncCall::Keccak256VariableLength { outputs, .. } => outputs.to_vec(),
         }
     }
diff --git a/acvm-repo/acir/tests/test_program_serialization.rs b/acvm-repo/acir/tests/test_program_serialization.rs
index 1f25b665573..7d3b7b32d35 100644
--- a/acvm-repo/acir/tests/test_program_serialization.rs
+++ b/acvm-repo/acir/tests/test_program_serialization.rs
@@ -102,9 +102,9 @@ fn pedersen_circuit() {
     let bytes = Circuit::serialize_circuit(&circuit);
 
     let expected_serialization: Vec<u8> = vec![
-        31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 93, 138, 9, 10, 0, 64, 8, 2, 103, 15, 250, 255, 139,
-        163, 162, 130, 72, 16, 149, 241, 3, 135, 84, 164, 172, 173, 213, 175, 251, 45, 198, 96,
-        243, 211, 50, 152, 67, 220, 211, 92, 0, 0, 0,
+        31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 93, 138, 9, 10, 0, 64, 8, 2, 103, 15, 232, 255, 31, 142,
+        138, 10, 34, 65, 84, 198, 15, 28, 82, 145, 178, 182, 86, 191, 238, 183, 24, 131, 205, 79,
+        203, 0, 166, 242, 158, 93, 92, 0, 0, 0,
     ];
 
     assert_eq!(bytes, expected_serialization)
@@ -145,7 +145,7 @@ fn schnorr_verify_circuit() {
     let expected_serialization: Vec<u8> = vec![
         31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 77, 210, 87, 78, 2, 1, 20, 134, 209, 177, 247, 222, 123,
         71, 68, 68, 68, 68, 68, 68, 68, 68, 68, 221, 133, 251, 95, 130, 145, 27, 206, 36, 78, 50,
-        57, 16, 94, 200, 253, 191, 159, 36, 73, 134, 146, 193, 19, 142, 241, 183, 255, 14, 179,
+        57, 16, 94, 200, 253, 191, 159, 36, 73, 134, 146, 193, 19, 142, 243, 183, 255, 14, 179,
         233, 247, 145, 254, 59, 217, 127, 71, 57, 198, 113, 78, 48, 125, 167, 56, 205, 25, 206,
         114, 142, 243, 92, 224, 34, 151, 184, 204, 21, 174, 114, 141, 235, 220, 224, 38, 183, 184,
         205, 29, 238, 114, 143, 251, 60, 224, 33, 143, 120, 204, 19, 158, 242, 140, 25, 158, 51,
@@ -158,7 +158,7 @@ fn schnorr_verify_circuit() {
         91, 159, 218, 56, 99, 219, 172, 77, 115, 182, 204, 219, 176, 96, 187, 162, 205, 74, 182,
         42, 219, 168, 98, 155, 170, 77, 106, 182, 168, 219, 160, 225, 246, 77, 55, 111, 185, 113,
         219, 109, 59, 110, 218, 117, 203, 158, 27, 166, 55, 75, 239, 150, 184, 101, 250, 252, 1,
-        19, 89, 159, 101, 220, 3, 0, 0,
+        55, 204, 92, 74, 220, 3, 0, 0,
     ];
 
     assert_eq!(bytes, expected_serialization)
diff --git a/acvm-repo/acvm/src/compiler/transformers/mod.rs b/acvm-repo/acvm/src/compiler/transformers/mod.rs
index c9bc91b02c7..d26b3f8bbf3 100644
--- a/acvm-repo/acvm/src/compiler/transformers/mod.rs
+++ b/acvm-repo/acvm/src/compiler/transformers/mod.rs
@@ -104,18 +104,17 @@ pub(super) fn transform_internal(
                     | acir::circuit::opcodes::BlackBoxFuncCall::XOR { output, .. } => {
                         transformer.mark_solvable(*output);
                     }
-                    acir::circuit::opcodes::BlackBoxFuncCall::RANGE { .. } => (),
+                    acir::circuit::opcodes::BlackBoxFuncCall::RANGE { .. }
+                    | acir::circuit::opcodes::BlackBoxFuncCall::RecursiveAggregation { .. } => (),
                     acir::circuit::opcodes::BlackBoxFuncCall::SHA256 { outputs, .. }
                     | acir::circuit::opcodes::BlackBoxFuncCall::Keccak256 { outputs, .. }
                     | acir::circuit::opcodes::BlackBoxFuncCall::Keccak256VariableLength {
                         outputs,
                         ..
                     }
-                    | acir::circuit::opcodes::BlackBoxFuncCall::RecursiveAggregation {
-                        output_aggregation_object: outputs,
-                        ..
-                    }
-                    | acir::circuit::opcodes::BlackBoxFuncCall::Blake2s { outputs, .. } => {
+                    | acir::circuit::opcodes::BlackBoxFuncCall::Keccakf1600 { outputs, .. }
+                    | acir::circuit::opcodes::BlackBoxFuncCall::Blake2s { outputs, .. }
+                    | acir::circuit::opcodes::BlackBoxFuncCall::Blake3 { outputs, .. } => {
                         for witness in outputs {
                             transformer.mark_solvable(*witness);
                         }
@@ -131,11 +130,7 @@ pub(super) fn transform_internal(
                         transformer.mark_solvable(outputs.0);
                         transformer.mark_solvable(outputs.1);
                     }
-                    acir::circuit::opcodes::BlackBoxFuncCall::HashToField128Security {
-                        output,
-                        ..
-                    }
-                    | acir::circuit::opcodes::BlackBoxFuncCall::EcdsaSecp256k1 { output, .. }
+                    acir::circuit::opcodes::BlackBoxFuncCall::EcdsaSecp256k1 { output, .. }
                     | acir::circuit::opcodes::BlackBoxFuncCall::EcdsaSecp256r1 { output, .. }
                     | acir::circuit::opcodes::BlackBoxFuncCall::SchnorrVerify { output, .. }
                     | acir::circuit::opcodes::BlackBoxFuncCall::PedersenHash { output, .. } => {
diff --git a/acvm-repo/acvm/src/pwg/blackbox/hash.rs b/acvm-repo/acvm/src/pwg/blackbox/hash.rs
index 80665a743c4..1ada397fc59 100644
--- a/acvm-repo/acvm/src/pwg/blackbox/hash.rs
+++ b/acvm-repo/acvm/src/pwg/blackbox/hash.rs
@@ -3,26 +3,11 @@ use acir::{
     native_types::{Witness, WitnessMap},
     BlackBoxFunc, FieldElement,
 };
-use acvm_blackbox_solver::{hash_to_field_128_security, BlackBoxResolutionError};
+use acvm_blackbox_solver::BlackBoxResolutionError;
 
 use crate::pwg::{insert_value, witness_to_value};
 use crate::OpcodeResolutionError;
 
-/// Attempts to solve a `HashToField128Security` opcode
-/// If successful, `initial_witness` will be mutated to contain the new witness assignment.
-pub(super) fn solve_hash_to_field(
-    initial_witness: &mut WitnessMap,
-    inputs: &[FunctionInput],
-    output: &Witness,
-) -> Result<(), OpcodeResolutionError> {
-    let message_input = get_hash_input(initial_witness, inputs, None)?;
-    let field = hash_to_field_128_security(&message_input)?;
-
-    insert_value(output, field, initial_witness)?;
-
-    Ok(())
-}
-
 /// Attempts to solve a 256 bit hash function opcode.
 /// If successful, `initial_witness` will be mutated to contain the new witness assignment.
 pub(super) fn solve_generic_256_hash_opcode(
diff --git a/acvm-repo/acvm/src/pwg/blackbox/mod.rs b/acvm-repo/acvm/src/pwg/blackbox/mod.rs
index 7e8ab8b948c..c18a97733b8 100644
--- a/acvm-repo/acvm/src/pwg/blackbox/mod.rs
+++ b/acvm-repo/acvm/src/pwg/blackbox/mod.rs
@@ -3,12 +3,12 @@ use acir::{
     native_types::{Witness, WitnessMap},
     FieldElement,
 };
-use acvm_blackbox_solver::{blake2s, keccak256, sha256};
+use acvm_blackbox_solver::{blake2s, blake3, keccak256, keccakf1600, sha256};
 
 use self::pedersen::pedersen_hash;
 
 use super::{insert_value, OpcodeNotSolvable, OpcodeResolutionError};
-use crate::BlackBoxFunctionSolver;
+use crate::{pwg::witness_to_value, BlackBoxFunctionSolver};
 
 mod fixed_base_scalar_mul;
 mod hash;
@@ -19,7 +19,7 @@ mod signature;
 
 use fixed_base_scalar_mul::fixed_base_scalar_mul;
 // Hash functions should eventually be exposed for external consumers.
-use hash::{solve_generic_256_hash_opcode, solve_hash_to_field};
+use hash::solve_generic_256_hash_opcode;
 use logic::{and, xor};
 use pedersen::pedersen;
 use range::solve_range_opcode;
@@ -83,6 +83,14 @@ pub(crate) fn solve(
             blake2s,
             bb_func.get_black_box_func(),
         ),
+        BlackBoxFuncCall::Blake3 { inputs, outputs } => solve_generic_256_hash_opcode(
+            initial_witness,
+            inputs,
+            None,
+            outputs,
+            blake3,
+            bb_func.get_black_box_func(),
+        ),
         BlackBoxFuncCall::Keccak256 { inputs, outputs } => solve_generic_256_hash_opcode(
             initial_witness,
             inputs,
@@ -101,8 +109,21 @@ pub(crate) fn solve(
                 bb_func.get_black_box_func(),
             )
         }
-        BlackBoxFuncCall::HashToField128Security { inputs, output } => {
-            solve_hash_to_field(initial_witness, inputs, output)
+        BlackBoxFuncCall::Keccakf1600 { inputs, outputs } => {
+            let mut state = [0; 25];
+            for (i, input) in inputs.iter().enumerate() {
+                let witness = input.witness;
+                let num_bits = input.num_bits as usize;
+                assert_eq!(num_bits, 64);
+                let witness_assignment = witness_to_value(initial_witness, witness)?;
+                let lane = witness_assignment.try_to_u64();
+                state[i] = lane.unwrap();
+            }
+            let state = keccakf1600(state)?;
+            for (output_witness, value) in outputs.iter().zip(state.into_iter()) {
+                insert_value(output_witness, FieldElement::from(value as u128), initial_witness)?;
+            }
+            Ok(())
         }
         BlackBoxFuncCall::SchnorrVerify {
             public_key_x,
@@ -156,13 +177,7 @@ pub(crate) fn solve(
         BlackBoxFuncCall::FixedBaseScalarMul { low, high, outputs } => {
             fixed_base_scalar_mul(backend, initial_witness, *low, *high, *outputs)
         }
-        BlackBoxFuncCall::RecursiveAggregation { output_aggregation_object, .. } => {
-            // Solve the output of the recursive aggregation to zero to prevent missing assignment errors
-            // The correct value will be computed by the backend
-            for witness in output_aggregation_object {
-                insert_value(witness, FieldElement::zero(), initial_witness)?;
-            }
-            Ok(())
-        }
+        // Recursive aggregation will be entirely handled by the backend and is not solved by the ACVM
+        BlackBoxFuncCall::RecursiveAggregation { .. } => Ok(()),
     }
 }
diff --git a/acvm-repo/acvm_js/src/black_box_solvers.rs b/acvm-repo/acvm_js/src/black_box_solvers.rs
index cc3edc3de04..fc0e3b28ebf 100644
--- a/acvm-repo/acvm_js/src/black_box_solvers.rs
+++ b/acvm-repo/acvm_js/src/black_box_solvers.rs
@@ -40,21 +40,6 @@ pub fn keccak256(inputs: &[u8]) -> Vec<u8> {
     acvm::blackbox_solver::keccak256(inputs).unwrap().into()
 }
 
-/// Calculates the Blake2s256 hash of the input bytes and represents these as a single field element.
-// #[wasm_bindgen]
-// pub fn hash_to_field_128_security(inputs: Vec<JsString>) -> JsString {
-//     let input_bytes: Vec<u8> = inputs
-//         .into_iter()
-//         .flat_map(|field_string| {
-//             let field_element = js_value_to_field_element(field_string.into()).unwrap();
-//             witness_assignment.fetch_nearest_bytes(FieldElement::max_num_bits());
-//         })
-//         .collect();
-//     field_element_to_js_string(
-//         &acvm::blackbox_solver::hash_to_field_128_security(&input_bytes).unwrap(),
-//     )
-// }
-
 /// Verifies a ECDSA signature over the secp256k1 curve.
 #[wasm_bindgen]
 pub fn ecdsa_secp256k1_verify(
diff --git a/acvm-repo/acvm_js/test/browser/black_box_solvers.test.ts b/acvm-repo/acvm_js/test/browser/black_box_solvers.test.ts
index 9aec1403f6c..3c54fe8e38f 100644
--- a/acvm-repo/acvm_js/test/browser/black_box_solvers.test.ts
+++ b/acvm-repo/acvm_js/test/browser/black_box_solvers.test.ts
@@ -64,18 +64,6 @@ it('successfully calculates the keccak256 hash', async () => {
   }
 });
 
-// it("successfully calculates the hash_to_field_128_security field", async () => {
-//   const { hash_to_field_128_security_test_cases } = await import(
-//     "../shared/black_box_solvers"
-//   );
-
-//   for (const testCase of hash_to_field_128_security_test_cases) {
-//     const [preimage, expectedResult] = testCase;
-//     const hashField = hash_to_field_128_security(preimage);
-//     expect(hashField).to.be.eq(expectedResult);
-//   }
-// });
-
 it('successfully verifies secp256k1 ECDSA signatures', async () => {
   const { ecdsa_secp256k1_test_cases } = await import('../shared/black_box_solvers');
 
diff --git a/acvm-repo/acvm_js/test/shared/black_box_solvers.ts b/acvm-repo/acvm_js/test/shared/black_box_solvers.ts
index a5b6d1ac996..0ab3fc12b72 100644
--- a/acvm-repo/acvm_js/test/shared/black_box_solvers.ts
+++ b/acvm-repo/acvm_js/test/shared/black_box_solvers.ts
@@ -66,13 +66,6 @@ export const keccak256_test_cases: [Uint8Array, Uint8Array][] = [
   ],
 ];
 
-// export const hash_to_field_128_security_test_cases: [string[], string][] = [
-//   [
-//     ["0x0000000000000000000000000000000000000000000000000000000000000001"],
-//     "0x25cebc29ded2fa515a937e2b5f674e3026c012e5b57f8a48d7dce6b7d274f9d9",
-//   ],
-// ];
-
 export const ecdsa_secp256k1_test_cases: [[Uint8Array, Uint8Array, Uint8Array, Uint8Array], boolean][] = [
   [
     [
diff --git a/acvm-repo/acvm_js/test/shared/pedersen.ts b/acvm-repo/acvm_js/test/shared/pedersen.ts
index 668ee2b510b..e35893fc355 100644
--- a/acvm-repo/acvm_js/test/shared/pedersen.ts
+++ b/acvm-repo/acvm_js/test/shared/pedersen.ts
@@ -1,7 +1,7 @@
 // See `pedersen_circuit` integration test in `acir/tests/test_program_serialization.rs`.
 export const bytecode = Uint8Array.from([
-  31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 93, 138, 9, 10, 0, 64, 8, 2, 103, 15, 250, 255, 139, 163, 162, 130, 72, 16, 149,
-  241, 3, 135, 84, 164, 172, 173, 213, 175, 251, 45, 198, 96, 243, 211, 50, 152, 67, 220, 211, 92, 0, 0, 0,
+  31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 93, 138, 9, 10, 0, 64, 8, 2, 103, 15, 232, 255, 31, 142, 138, 10, 34, 65, 84, 198,
+  15, 28, 82, 145, 178, 182, 86, 191, 238, 183, 24, 131, 205, 79, 203, 0, 166, 242, 158, 93, 92, 0, 0, 0,
 ]);
 
 export const initialWitnessMap = new Map([[1, '0x0000000000000000000000000000000000000000000000000000000000000001']]);
diff --git a/acvm-repo/acvm_js/test/shared/schnorr_verify.ts b/acvm-repo/acvm_js/test/shared/schnorr_verify.ts
index f88a70ba4a1..5716cbd30f8 100644
--- a/acvm-repo/acvm_js/test/shared/schnorr_verify.ts
+++ b/acvm-repo/acvm_js/test/shared/schnorr_verify.ts
@@ -2,7 +2,7 @@
 export const bytecode = Uint8Array.from([
   31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 77, 210, 87, 78, 2, 1, 20, 134, 209, 177, 247, 222, 123, 71, 68, 68, 68, 68, 68,
   68, 68, 68, 68, 221, 133, 251, 95, 130, 145, 27, 206, 36, 78, 50, 57, 16, 94, 200, 253, 191, 159, 36, 73, 134, 146,
-  193, 19, 142, 241, 183, 255, 14, 179, 233, 247, 145, 254, 59, 217, 127, 71, 57, 198, 113, 78, 48, 125, 167, 56, 205,
+  193, 19, 142, 243, 183, 255, 14, 179, 233, 247, 145, 254, 59, 217, 127, 71, 57, 198, 113, 78, 48, 125, 167, 56, 205,
   25, 206, 114, 142, 243, 92, 224, 34, 151, 184, 204, 21, 174, 114, 141, 235, 220, 224, 38, 183, 184, 205, 29, 238, 114,
   143, 251, 60, 224, 33, 143, 120, 204, 19, 158, 242, 140, 25, 158, 51, 203, 11, 230, 120, 201, 60, 175, 88, 224, 53,
   139, 188, 97, 137, 183, 44, 243, 142, 21, 222, 179, 202, 7, 214, 248, 200, 58, 159, 216, 224, 51, 155, 124, 97, 235,
@@ -11,7 +11,7 @@ export const bytecode = Uint8Array.from([
   162, 149, 232, 36, 26, 137, 62, 162, 141, 232, 34, 154, 136, 30, 162, 133, 232, 32, 26, 136, 253, 99, 251, 195, 100,
   176, 121, 236, 29, 91, 159, 218, 56, 99, 219, 172, 77, 115, 182, 204, 219, 176, 96, 187, 162, 205, 74, 182, 42, 219,
   168, 98, 155, 170, 77, 106, 182, 168, 219, 160, 225, 246, 77, 55, 111, 185, 113, 219, 109, 59, 110, 218, 117, 203,
-  158, 27, 166, 55, 75, 239, 150, 184, 101, 250, 252, 1, 19, 89, 159, 101, 220, 3, 0, 0,
+  158, 27, 166, 55, 75, 239, 150, 184, 101, 250, 252, 1, 55, 204, 92, 74, 220, 3, 0, 0,
 ]);
 
 export const initialWitnessMap = new Map([
diff --git a/acvm-repo/blackbox_solver/Cargo.toml b/acvm-repo/blackbox_solver/Cargo.toml
index be2a58417f4..749ef8f289a 100644
--- a/acvm-repo/blackbox_solver/Cargo.toml
+++ b/acvm-repo/blackbox_solver/Cargo.toml
@@ -17,8 +17,10 @@ acir.workspace = true
 thiserror.workspace = true
 
 blake2 = "0.10.6"
+blake3 = "1.5.0"
 sha2 = "0.10.6"
 sha3 = "0.10.6"
+keccak = "0.1.4"
 k256 = { version = "0.11.0", features = [
     "ecdsa",
     "ecdsa-core",
diff --git a/acvm-repo/blackbox_solver/src/lib.rs b/acvm-repo/blackbox_solver/src/lib.rs
index 8b7c6343962..9518854de8e 100644
--- a/acvm-repo/blackbox_solver/src/lib.rs
+++ b/acvm-repo/blackbox_solver/src/lib.rs
@@ -59,14 +59,22 @@ pub fn blake2s(inputs: &[u8]) -> Result<[u8; 32], BlackBoxResolutionError> {
         .map_err(|err| BlackBoxResolutionError::Failed(BlackBoxFunc::Blake2s, err))
 }
 
+pub fn blake3(inputs: &[u8]) -> Result<[u8; 32], BlackBoxResolutionError> {
+    Ok(blake3::hash(inputs).into())
+}
+
 pub fn keccak256(inputs: &[u8]) -> Result<[u8; 32], BlackBoxResolutionError> {
     generic_hash_256::<Keccak256>(inputs)
         .map_err(|err| BlackBoxResolutionError::Failed(BlackBoxFunc::Keccak256, err))
 }
 
-pub fn hash_to_field_128_security(inputs: &[u8]) -> Result<FieldElement, BlackBoxResolutionError> {
-    generic_hash_to_field::<Blake2s256>(inputs)
-        .map_err(|err| BlackBoxResolutionError::Failed(BlackBoxFunc::HashToField128Security, err))
+const KECCAK_LANES: usize = 25;
+
+pub fn keccakf1600(
+    mut state: [u64; KECCAK_LANES],
+) -> Result<[u64; KECCAK_LANES], BlackBoxResolutionError> {
+    keccak::f1600(&mut state);
+    Ok(state)
 }
 
 pub fn ecdsa_secp256k1_verify(
@@ -95,14 +103,6 @@ fn generic_hash_256<D: Digest>(message: &[u8]) -> Result<[u8; 32], String> {
     Ok(output_bytes)
 }
 
-/// Does a generic hash of the entire inputs converting the resulting hash into a single output field.
-fn generic_hash_to_field<D: Digest>(message: &[u8]) -> Result<FieldElement, String> {
-    let output_bytes: [u8; 32] =
-        D::digest(message).as_slice().try_into().map_err(|_| "digest should be 256 bits")?;
-
-    Ok(FieldElement::from_be_bytes_reduce(&output_bytes))
-}
-
 fn verify_secp256k1_ecdsa_signature(
     hashed_msg: &[u8],
     public_key_x_bytes: &[u8; 32],
@@ -238,6 +238,79 @@ fn verify_secp256r1_ecdsa_signature(
     }
 }
 
+#[cfg(test)]
+mod keccakf1600_tests {
+    use crate::keccakf1600;
+
+    #[test]
+    fn sanity_check() {
+        // Test vectors are copied from XKCP (eXtended Keccak Code Package)
+        // https://github.com/XKCP/XKCP/blob/master/tests/TestVectors/KeccakF-1600-IntermediateValues.txt
+        let zero_state = [0u64; 25];
+
+        let expected_state_first = [
+            0xF1258F7940E1DDE7,
+            0x84D5CCF933C0478A,
+            0xD598261EA65AA9EE,
+            0xBD1547306F80494D,
+            0x8B284E056253D057,
+            0xFF97A42D7F8E6FD4,
+            0x90FEE5A0A44647C4,
+            0x8C5BDA0CD6192E76,
+            0xAD30A6F71B19059C,
+            0x30935AB7D08FFC64,
+            0xEB5AA93F2317D635,
+            0xA9A6E6260D712103,
+            0x81A57C16DBCF555F,
+            0x43B831CD0347C826,
+            0x01F22F1A11A5569F,
+            0x05E5635A21D9AE61,
+            0x64BEFEF28CC970F2,
+            0x613670957BC46611,
+            0xB87C5A554FD00ECB,
+            0x8C3EE88A1CCF32C8,
+            0x940C7922AE3A2614,
+            0x1841F924A2C509E4,
+            0x16F53526E70465C2,
+            0x75F644E97F30A13B,
+            0xEAF1FF7B5CECA249,
+        ];
+        let expected_state_second = [
+            0x2D5C954DF96ECB3C,
+            0x6A332CD07057B56D,
+            0x093D8D1270D76B6C,
+            0x8A20D9B25569D094,
+            0x4F9C4F99E5E7F156,
+            0xF957B9A2DA65FB38,
+            0x85773DAE1275AF0D,
+            0xFAF4F247C3D810F7,
+            0x1F1B9EE6F79A8759,
+            0xE4FECC0FEE98B425,
+            0x68CE61B6B9CE68A1,
+            0xDEEA66C4BA8F974F,
+            0x33C43D836EAFB1F5,
+            0xE00654042719DBD9,
+            0x7CF8A9F009831265,
+            0xFD5449A6BF174743,
+            0x97DDAD33D8994B40,
+            0x48EAD5FC5D0BE774,
+            0xE3B8C8EE55B7B03C,
+            0x91A0226E649E42E9,
+            0x900E3129E7BADD7B,
+            0x202A9EC5FAA3CCE8,
+            0x5B3402464E1C3DB6,
+            0x609F4E62A44C1059,
+            0x20D06CD26A8FBF5C,
+        ];
+
+        let state_first = keccakf1600(zero_state).unwrap();
+        let state_second = keccakf1600(state_first).unwrap();
+
+        assert_eq!(state_first, expected_state_first);
+        assert_eq!(state_second, expected_state_second);
+    }
+}
+
 #[cfg(test)]
 mod secp256k1_tests {
     use super::verify_secp256k1_ecdsa_signature;
diff --git a/acvm-repo/brillig/src/black_box.rs b/acvm-repo/brillig/src/black_box.rs
index 75fae0a10f0..41e54ab2705 100644
--- a/acvm-repo/brillig/src/black_box.rs
+++ b/acvm-repo/brillig/src/black_box.rs
@@ -11,12 +11,6 @@ pub enum BlackBoxOp {
     Blake2s { message: HeapVector, output: HeapArray },
     /// Calculates the Keccak256 hash of the inputs.
     Keccak256 { message: HeapVector, output: HeapArray },
-    /// Hashes a set of inputs and applies the field modulus to the result
-    /// to return a value which can be represented as a [`FieldElement`][acir_field::FieldElement]
-    ///
-    /// This is implemented using the `Blake2s` hash function.
-    /// The "128" in the name specifies that this function should have 128 bits of security.
-    HashToField128Security { message: HeapVector, output: RegisterIndex },
     /// Verifies a ECDSA signature over the secp256k1 curve.
     EcdsaSecp256k1 {
         hashed_msg: HeapVector,
diff --git a/acvm-repo/brillig_vm/src/black_box.rs b/acvm-repo/brillig_vm/src/black_box.rs
index 32a93298d97..94feb23e1a6 100644
--- a/acvm-repo/brillig_vm/src/black_box.rs
+++ b/acvm-repo/brillig_vm/src/black_box.rs
@@ -1,8 +1,8 @@
 use acir::brillig::{BlackBoxOp, HeapArray, HeapVector, Value};
 use acir::{BlackBoxFunc, FieldElement};
 use acvm_blackbox_solver::{
-    blake2s, ecdsa_secp256k1_verify, ecdsa_secp256r1_verify, hash_to_field_128_security, keccak256,
-    sha256, BlackBoxFunctionSolver, BlackBoxResolutionError,
+    blake2s, ecdsa_secp256k1_verify, ecdsa_secp256r1_verify, keccak256, sha256,
+    BlackBoxFunctionSolver, BlackBoxResolutionError,
 };
 
 use crate::{Memory, Registers};
@@ -64,13 +64,6 @@ pub(crate) fn evaluate_black_box<Solver: BlackBoxFunctionSolver>(
             memory.write_slice(registers.get(output.pointer).to_usize(), &to_value_vec(&bytes));
             Ok(())
         }
-        BlackBoxOp::HashToField128Security { message, output } => {
-            let field = hash_to_field_128_security(&to_u8_vec(read_heap_vector(
-                memory, registers, message,
-            )))?;
-            registers.set(*output, field.into());
-            Ok(())
-        }
         BlackBoxOp::EcdsaSecp256k1 {
             hashed_msg,
             public_key_x,
@@ -179,7 +172,6 @@ fn black_box_function_from_op(op: &BlackBoxOp) -> BlackBoxFunc {
         BlackBoxOp::Sha256 { .. } => BlackBoxFunc::SHA256,
         BlackBoxOp::Blake2s { .. } => BlackBoxFunc::Blake2s,
         BlackBoxOp::Keccak256 { .. } => BlackBoxFunc::Keccak256,
-        BlackBoxOp::HashToField128Security { .. } => BlackBoxFunc::HashToField128Security,
         BlackBoxOp::EcdsaSecp256k1 { .. } => BlackBoxFunc::EcdsaSecp256k1,
         BlackBoxOp::EcdsaSecp256r1 { .. } => BlackBoxFunc::EcdsaSecp256r1,
         BlackBoxOp::SchnorrVerify { .. } => BlackBoxFunc::SchnorrVerify,
diff --git a/aztec_macros/src/lib.rs b/aztec_macros/src/lib.rs
index 95bdce56021..24f0f5a71b6 100644
--- a/aztec_macros/src/lib.rs
+++ b/aztec_macros/src/lib.rs
@@ -252,12 +252,15 @@ fn check_for_storage_definition(module: &SortedModule) -> bool {
     module.types.iter().any(|r#struct| r#struct.name.0.contents == "Storage")
 }
 
-// Check if "compute_note_hash_and_nullifier(Field,Field,Field,[Field; N]) -> [Field; 4]" is defined
+// Check if "compute_note_hash_and_nullifier(AztecAddress,Field,Field,[Field; N]) -> [Field; 4]" is defined
 fn check_for_compute_note_hash_and_nullifier_definition(module: &SortedModule) -> bool {
     module.functions.iter().any(|func| {
         func.def.name.0.contents == "compute_note_hash_and_nullifier"
                 && func.def.parameters.len() == 4
-                && func.def.parameters[0].typ.typ == UnresolvedTypeData::FieldElement
+                && match &func.def.parameters[0].typ.typ {
+                    UnresolvedTypeData::Named(path, _) => path.segments.last().unwrap().0.contents == "AztecAddress",
+                    _ => false,
+                }
                 && func.def.parameters[1].typ.typ == UnresolvedTypeData::FieldElement
                 && func.def.parameters[2].typ.typ == UnresolvedTypeData::FieldElement
                 // checks if the 4th parameter is an array and the Box<UnresolvedType> in
@@ -480,11 +483,12 @@ const SIGNATURE_PLACEHOLDER: &str = "SIGNATURE_PLACEHOLDER";
 
 /// Generates the impl for an event selector
 ///
+/// TODO(https://github.com/AztecProtocol/aztec-packages/issues/3590): Make this point to aztec-nr once the issue is fixed.
 /// Inserts the following code:
 /// ```noir
 /// impl SomeStruct {
-///    fn selector() -> Field {
-///       aztec::oracle::compute_selector::compute_selector("SIGNATURE_PLACEHOLDER")
+///    fn selector() -> FunctionSelector {
+///       protocol_types::abis::function_selector::FunctionSelector::from_signature("SIGNATURE_PLACEHOLDER")
 ///    }
 /// }
 /// ```
@@ -495,18 +499,28 @@ const SIGNATURE_PLACEHOLDER: &str = "SIGNATURE_PLACEHOLDER";
 fn generate_selector_impl(structure: &NoirStruct) -> TypeImpl {
     let struct_type = make_type(UnresolvedTypeData::Named(path(structure.name.clone()), vec![]));
 
+    // TODO(https://github.com/AztecProtocol/aztec-packages/issues/3590): Make this point to aztec-nr once the issue is fixed.
+    let selector_path =
+        chained_path!("protocol_types", "abis", "function_selector", "FunctionSelector");
+    let mut from_signature_path = selector_path.clone();
+    from_signature_path.segments.push(ident("from_signature"));
+
     let selector_fun_body = BlockExpression(vec![make_statement(StatementKind::Expression(call(
-        variable_path(chained_path!("aztec", "selector", "compute_selector")),
+        variable_path(from_signature_path),
         vec![expression(ExpressionKind::Literal(Literal::Str(SIGNATURE_PLACEHOLDER.to_string())))],
     )))]);
 
+    // Define `FunctionSelector` return type
+    let return_type =
+        FunctionReturnType::Ty(make_type(UnresolvedTypeData::Named(selector_path, vec![])));
+
     let mut selector_fn_def = FunctionDefinition::normal(
         &ident("selector"),
         &vec![],
         &[],
         &selector_fun_body,
         &[],
-        &FunctionReturnType::Ty(make_type(UnresolvedTypeData::FieldElement)),
+        &return_type,
     );
 
     selector_fn_def.visibility = FunctionVisibility::Public;
diff --git a/compiler/integration-tests/circuits/recursion/src/main.nr b/compiler/integration-tests/circuits/recursion/src/main.nr
index e60e4e0b61a..173207766fb 100644
--- a/compiler/integration-tests/circuits/recursion/src/main.nr
+++ b/compiler/integration-tests/circuits/recursion/src/main.nr
@@ -1,17 +1,15 @@
 use dep::std;
 
 fn main(
-    verification_key : [Field; 114], 
-    proof : [Field; 94], 
-    public_inputs : [Field; 1], 
-    key_hash : Field, 
-) -> pub [Field;16]{
-    let input_aggregation_object = [0; 16];
+    verification_key: [Field; 114],
+    proof: [Field; 93],
+    public_inputs: [Field; 1],
+    key_hash: Field
+) {
     std::verify_proof(
-        verification_key.as_slice(), 
-        proof.as_slice(), 
-        public_inputs.as_slice(), 
-        key_hash, 
-        input_aggregation_object
+        verification_key.as_slice(),
+        proof.as_slice(),
+        public_inputs.as_slice(),
+        key_hash
     )
 }
diff --git a/compiler/integration-tests/test/browser/recursion.test.ts b/compiler/integration-tests/test/browser/recursion.test.ts
index faa317b2c3c..2097164ebcb 100644
--- a/compiler/integration-tests/test/browser/recursion.test.ts
+++ b/compiler/integration-tests/test/browser/recursion.test.ts
@@ -79,7 +79,6 @@ describe('It compiles noir program code, receiving circuit bytes and abi object.
       proof: proofAsFields,
       public_inputs: [main_inputs.y as Field],
       key_hash: vkHash,
-      input_aggregation_object: ['0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0'],
     };
 
     logger.debug('recursion_inputs', recursion_inputs);
diff --git a/compiler/integration-tests/test/node/onchain_recursive_verification.test.ts b/compiler/integration-tests/test/node/onchain_recursive_verification.test.ts
index 6c20d44882b..7a25669d22a 100644
--- a/compiler/integration-tests/test/node/onchain_recursive_verification.test.ts
+++ b/compiler/integration-tests/test/node/onchain_recursive_verification.test.ts
@@ -12,7 +12,7 @@ import {
   PathToFileSourceMap,
 } from '@noir-lang/noir_wasm';
 import { Noir } from '@noir-lang/noir_js';
-import { BarretenbergBackend, flattenPublicInputs } from '@noir-lang/backend_barretenberg';
+import { BarretenbergBackend } from '@noir-lang/backend_barretenberg';
 import { Field, InputMap } from '@noir-lang/noirc_abi';
 
 compilerLogLevel('INFO');
@@ -71,10 +71,7 @@ it(`smart contract can verify a recursive proof`, async () => {
 
   const contract = await ethers.deployContract('contracts/recursion.sol:UltraVerifier', []);
 
-  const result = await contract.verify.staticCall(
-    recursion_proof.proof,
-    flattenPublicInputs(recursion_proof.publicInputs),
-  );
+  const result = await contract.verify.staticCall(recursion_proof.proof, recursion_proof.publicInputs);
 
   expect(result).to.be.true;
 });
diff --git a/compiler/integration-tests/test/node/smart_contract_verifier.test.ts b/compiler/integration-tests/test/node/smart_contract_verifier.test.ts
index 5b3d0e2d337..a93a2fc1508 100644
--- a/compiler/integration-tests/test/node/smart_contract_verifier.test.ts
+++ b/compiler/integration-tests/test/node/smart_contract_verifier.test.ts
@@ -7,7 +7,7 @@ import toml from 'toml';
 
 import { PathToFileSourceMap, compile, init_log_level as compilerLogLevel } from '@noir-lang/noir_wasm';
 import { Noir } from '@noir-lang/noir_js';
-import { BarretenbergBackend, flattenPublicInputs } from '@noir-lang/backend_barretenberg';
+import { BarretenbergBackend } from '@noir-lang/backend_barretenberg';
 
 compilerLogLevel('INFO');
 
@@ -61,7 +61,7 @@ test_cases.forEach((testInfo) => {
 
     const contract = await ethers.deployContract(testInfo.compiled, []);
 
-    const result = await contract.verify(proofData.proof, flattenPublicInputs(proofData.publicInputs));
+    const result = await contract.verify(proofData.proof, proofData.publicInputs);
 
     expect(result).to.be.true;
   });
diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs
index 542c23fcd2e..a6d3220fa85 100644
--- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs
+++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs
@@ -58,19 +58,6 @@ pub(crate) fn convert_black_box_call(
                 unreachable!("ICE: Keccak256 expects message, message size and result array")
             }
         }
-        BlackBoxFunc::HashToField128Security => {
-            if let ([message], [BrilligVariable::Simple(result_register)]) =
-                (function_arguments, function_results)
-            {
-                let message_vector = convert_array_or_vector(brillig_context, message, bb_func);
-                brillig_context.black_box_op_instruction(BlackBoxOp::HashToField128Security {
-                    message: message_vector.to_heap_vector(),
-                    output: *result_register,
-                });
-            } else {
-                unreachable!("ICE: HashToField128Security expects one array argument and one register result")
-            }
-        }
         BlackBoxFunc::EcdsaSecp256k1 => {
             if let (
                 [BrilligVariable::BrilligArray(public_key_x), BrilligVariable::BrilligArray(public_key_y), BrilligVariable::BrilligArray(signature), message],
@@ -194,6 +181,12 @@ pub(crate) fn convert_black_box_call(
         BlackBoxFunc::RecursiveAggregation => unimplemented!(
             "ICE: `BlackBoxFunc::RecursiveAggregation` is not implemented by the Brillig VM"
         ),
+        BlackBoxFunc::Blake3 => {
+            unimplemented!("ICE: `BlackBoxFunc::Blake3` is not implemented by the Brillig VM")
+        }
+        BlackBoxFunc::Keccakf1600 => {
+            unimplemented!("ICE: `BlackBoxFunc::Keccakf1600` is not implemented by the Brillig VM")
+        }
     }
 }
 
diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs
index 0e06a36fd94..db005d9d438 100644
--- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs
+++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs
@@ -521,7 +521,7 @@ impl<'block> BrilligBlock<'block> {
                     unreachable!("unsupported function call type {:?}", dfg[*func])
                 }
             },
-            Instruction::Truncate { value, .. } => {
+            Instruction::Truncate { value, bit_size, .. } => {
                 let result_ids = dfg.instruction_results(instruction_id);
                 let destination_register = self.variables.define_register_variable(
                     self.function_context,
@@ -530,9 +530,13 @@ impl<'block> BrilligBlock<'block> {
                     dfg,
                 );
                 let source_register = self.convert_ssa_register_value(*value, dfg);
-                self.brillig_context.truncate_instruction(destination_register, source_register);
+                self.brillig_context.truncate_instruction(
+                    destination_register,
+                    source_register,
+                    *bit_size,
+                );
             }
-            Instruction::Cast(value, target_type) => {
+            Instruction::Cast(value, _) => {
                 let result_ids = dfg.instruction_results(instruction_id);
                 let destination_register = self.variables.define_register_variable(
                     self.function_context,
@@ -541,12 +545,7 @@ impl<'block> BrilligBlock<'block> {
                     dfg,
                 );
                 let source_register = self.convert_ssa_register_value(*value, dfg);
-                self.convert_cast(
-                    destination_register,
-                    source_register,
-                    target_type,
-                    &dfg.type_of_value(*value),
-                );
+                self.convert_cast(destination_register, source_register);
             }
             Instruction::ArrayGet { array, index } => {
                 let result_ids = dfg.instruction_results(instruction_id);
@@ -1092,43 +1091,11 @@ impl<'block> BrilligBlock<'block> {
 
     /// Converts an SSA cast to a sequence of Brillig opcodes.
     /// Casting is only necessary when shrinking the bit size of a numeric value.
-    fn convert_cast(
-        &mut self,
-        destination: RegisterIndex,
-        source: RegisterIndex,
-        target_type: &Type,
-        source_type: &Type,
-    ) {
-        fn numeric_to_bit_size(typ: &NumericType) -> u32 {
-            match typ {
-                NumericType::Signed { bit_size } | NumericType::Unsigned { bit_size } => *bit_size,
-                NumericType::NativeField => FieldElement::max_num_bits(),
-            }
-        }
-        // Casting is only valid for numeric types
-        // This should be checked by the frontend, so we panic if this is the case
-        let (source_numeric_type, target_numeric_type) = match (source_type, target_type) {
-            (Type::Numeric(source_numeric_type), Type::Numeric(target_numeric_type)) => {
-                (source_numeric_type, target_numeric_type)
-            }
-            _ => unimplemented!("The cast operation is only valid for integers."),
-        };
-        let source_bit_size = numeric_to_bit_size(source_numeric_type);
-        let target_bit_size = numeric_to_bit_size(target_numeric_type);
-        // Casting from a larger bit size to a smaller bit size (narrowing cast)
-        // requires a cast instruction.
-        // If its a widening cast, ie casting from a smaller bit size to a larger bit size
-        // we simply put a mov instruction as a no-op
-        //
-        // Field elements by construction always have the largest bit size
-        // This means that casting to a Field element, will always be a widening cast
-        // and therefore a no-op. Conversely, casting from a Field element
-        // will always be a narrowing cast and therefore a cast instruction
-        if source_bit_size > target_bit_size {
-            self.brillig_context.cast_instruction(destination, source, target_bit_size);
-        } else {
-            self.brillig_context.mov_instruction(destination, source);
-        }
+    fn convert_cast(&mut self, destination: RegisterIndex, source: RegisterIndex) {
+        // We assume that `source` is a valid `target_type` as it's expected that a truncate instruction was emitted
+        // to ensure this is the case.
+
+        self.brillig_context.mov_instruction(destination, source);
     }
 
     /// Converts the Binary instruction into a sequence of Brillig opcodes.
diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir.rs
index ff182aaa7d2..3c4e77b09ec 100644
--- a/compiler/noirc_evaluator/src/brillig/brillig_ir.rs
+++ b/compiler/noirc_evaluator/src/brillig/brillig_ir.rs
@@ -687,10 +687,29 @@ impl BrilligContext {
         &mut self,
         destination_of_truncated_value: RegisterIndex,
         value_to_truncate: RegisterIndex,
+        bit_size: u32,
     ) {
-        // Effectively a no-op because brillig already has implicit truncation on integer
-        // operations. We need only copy the value to it's destination.
-        self.mov_instruction(destination_of_truncated_value, value_to_truncate);
+        self.debug_show.truncate_instruction(
+            destination_of_truncated_value,
+            value_to_truncate,
+            bit_size,
+        );
+        assert!(
+            bit_size <= BRILLIG_INTEGER_ARITHMETIC_BIT_SIZE,
+            "tried to truncate to a bit size greater than allowed {bit_size}"
+        );
+
+        // The brillig VM performs all arithmetic operations modulo 2**bit_size
+        // So to truncate any value to a target bit size we can just issue a no-op arithmetic operation
+        // With bit size equal to target_bit_size
+        let zero_register = self.make_constant(Value::from(FieldElement::zero()));
+        self.binary_instruction(
+            value_to_truncate,
+            zero_register,
+            destination_of_truncated_value,
+            BrilligBinaryOp::Integer { op: BinaryIntOp::Add, bit_size },
+        );
+        self.deallocate_register(zero_register);
     }
 
     /// Emits a stop instruction
@@ -761,36 +780,6 @@ impl BrilligContext {
         self.deallocate_register(scratch_register_j);
     }
 
-    /// Emits a modulo instruction against 2**target_bit_size
-    ///
-    /// Integer arithmetic in Brillig is currently constrained to 127 bit integers.
-    /// We restrict the cast operation, so that integer types over 127 bits
-    /// cannot be created.
-    pub(crate) fn cast_instruction(
-        &mut self,
-        destination: RegisterIndex,
-        source: RegisterIndex,
-        target_bit_size: u32,
-    ) {
-        self.debug_show.cast_instruction(destination, source, target_bit_size);
-        assert!(
-            target_bit_size <= BRILLIG_INTEGER_ARITHMETIC_BIT_SIZE,
-            "tried to cast to a bit size greater than allowed {target_bit_size}"
-        );
-
-        // The brillig VM performs all arithmetic operations modulo 2**bit_size
-        // So to cast any value to a target bit size we can just issue a no-op arithmetic operation
-        // With bit size equal to target_bit_size
-        let zero_register = self.make_constant(Value::from(FieldElement::zero()));
-        self.binary_instruction(
-            source,
-            zero_register,
-            destination,
-            BrilligBinaryOp::Integer { op: BinaryIntOp::Add, bit_size: target_bit_size },
-        );
-        self.deallocate_register(zero_register);
-    }
-
     /// Adds a unresolved external `Call` instruction to the bytecode.
     /// This calls into another function compiled into this brillig artifact.
     pub(crate) fn add_external_call_instruction<T: ToString>(&mut self, func_label: T) {
diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs
index 65db47dd2e0..74b24b280cd 100644
--- a/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs
+++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs
@@ -326,7 +326,7 @@ impl DebugShow {
     }
 
     /// Debug function for cast_instruction
-    pub(crate) fn cast_instruction(
+    pub(crate) fn truncate_instruction(
         &self,
         destination: RegisterIndex,
         source: RegisterIndex,
@@ -334,7 +334,7 @@ impl DebugShow {
     ) {
         debug_println!(
             self.enable_debug_trace,
-            "  CAST {} FROM {} TO {} BITS",
+            "  TRUNCATE {} FROM {} TO {} BITS",
             destination,
             source,
             target_bit_size
@@ -353,14 +353,6 @@ impl DebugShow {
             BlackBoxOp::Blake2s { message, output } => {
                 debug_println!(self.enable_debug_trace, "  BLAKE2S {} -> {}", message, output);
             }
-            BlackBoxOp::HashToField128Security { message, output } => {
-                debug_println!(
-                    self.enable_debug_trace,
-                    "  HASH_TO_FIELD_128_SECURITY {} -> {}",
-                    message,
-                    output
-                );
-            }
             BlackBoxOp::EcdsaSecp256k1 {
                 hashed_msg,
                 public_key_x,
diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs
index 0ea4d99123c..2506af8c8c8 100644
--- a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs
+++ b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs
@@ -155,10 +155,7 @@ impl GeneratedAcir {
             BlackBoxFunc::Blake2s => {
                 BlackBoxFuncCall::Blake2s { inputs: inputs[0].clone(), outputs }
             }
-            BlackBoxFunc::HashToField128Security => BlackBoxFuncCall::HashToField128Security {
-                inputs: inputs[0].clone(),
-                output: outputs[0],
-            },
+            BlackBoxFunc::Blake3 => BlackBoxFuncCall::Blake3 { inputs: inputs[0].clone(), outputs },
             BlackBoxFunc::SchnorrVerify => {
                 BlackBoxFuncCall::SchnorrVerify {
                     public_key_x: inputs[0][0],
@@ -226,26 +223,15 @@ impl GeneratedAcir {
                     outputs,
                 }
             }
-            BlackBoxFunc::RecursiveAggregation => {
-                let has_previous_aggregation = self.opcodes.iter().any(|op| {
-                    matches!(
-                        op,
-                        AcirOpcode::BlackBoxFuncCall(BlackBoxFuncCall::RecursiveAggregation { .. })
-                    )
-                });
-
-                let input_aggregation_object =
-                    if !has_previous_aggregation { None } else { Some(inputs[4].clone()) };
-
-                BlackBoxFuncCall::RecursiveAggregation {
-                    verification_key: inputs[0].clone(),
-                    proof: inputs[1].clone(),
-                    public_inputs: inputs[2].clone(),
-                    key_hash: inputs[3][0],
-                    input_aggregation_object,
-                    output_aggregation_object: outputs,
-                }
+            BlackBoxFunc::Keccakf1600 => {
+                BlackBoxFuncCall::Keccakf1600 { inputs: inputs[0].clone(), outputs }
             }
+            BlackBoxFunc::RecursiveAggregation => BlackBoxFuncCall::RecursiveAggregation {
+                verification_key: inputs[0].clone(),
+                proof: inputs[1].clone(),
+                public_inputs: inputs[2].clone(),
+                key_hash: inputs[3][0],
+            },
         };
 
         self.push_opcode(AcirOpcode::BlackBoxFuncCall(black_box_func_call));
@@ -573,9 +559,11 @@ fn black_box_func_expected_input_size(name: BlackBoxFunc) -> Option<usize> {
         BlackBoxFunc::Keccak256
         | BlackBoxFunc::SHA256
         | BlackBoxFunc::Blake2s
+        | BlackBoxFunc::Blake3
         | BlackBoxFunc::PedersenCommitment
-        | BlackBoxFunc::PedersenHash
-        | BlackBoxFunc::HashToField128Security => None,
+        | BlackBoxFunc::PedersenHash => None,
+
+        BlackBoxFunc::Keccakf1600 => Some(25),
 
         // Can only apply a range constraint to one
         // witness at a time.
@@ -602,9 +590,11 @@ fn black_box_expected_output_size(name: BlackBoxFunc) -> Option<usize> {
         // or the operation.
         BlackBoxFunc::AND | BlackBoxFunc::XOR => Some(1),
         // 32 byte hash algorithms
-        BlackBoxFunc::Keccak256 | BlackBoxFunc::SHA256 | BlackBoxFunc::Blake2s => Some(32),
-        // Hash to field returns a field element
-        BlackBoxFunc::HashToField128Security => Some(1),
+        BlackBoxFunc::Keccak256
+        | BlackBoxFunc::SHA256
+        | BlackBoxFunc::Blake2s
+        | BlackBoxFunc::Blake3 => Some(32),
+        BlackBoxFunc::Keccakf1600 => Some(25),
         // Pedersen commitment returns a point
         BlackBoxFunc::PedersenCommitment => Some(2),
         // Pedersen hash returns a field
diff --git a/compiler/noirc_evaluator/src/ssa/ir/dfg.rs b/compiler/noirc_evaluator/src/ssa/ir/dfg.rs
index c8343ace2c3..9942a48a38a 100644
--- a/compiler/noirc_evaluator/src/ssa/ir/dfg.rs
+++ b/compiler/noirc_evaluator/src/ssa/ir/dfg.rs
@@ -166,11 +166,31 @@ impl DataFlowGraph {
                 SimplifiedToMultiple(simplification)
             }
             SimplifyResult::Remove => InstructionRemoved,
-            result @ (SimplifyResult::SimplifiedToInstruction(_) | SimplifyResult::None) => {
-                let instruction = result.instruction().unwrap_or(instruction);
-                let id = self.make_instruction(instruction, ctrl_typevars);
-                self.blocks[block].insert_instruction(id);
-                self.locations.insert(id, call_stack);
+            result @ (SimplifyResult::SimplifiedToInstruction(_)
+            | SimplifyResult::SimplifiedToInstructionMultiple(_)
+            | SimplifyResult::None) => {
+                let instructions = result.instructions().unwrap_or(vec![instruction]);
+
+                if instructions.len() > 1 {
+                    // There's currently no way to pass results from one instruction in `instructions` on to the next.
+                    // We then restrict this to only support multiple instructions if they're all `Instruction::Constrain`
+                    // as this instruction type does not have any results.
+                    assert!(
+                        instructions.iter().all(|instruction| matches!(instruction, Instruction::Constrain(..))),
+                        "`SimplifyResult::SimplifiedToInstructionMultiple` only supports `Constrain` instructions"
+                    );
+                }
+
+                let mut last_id = None;
+
+                for instruction in instructions {
+                    let id = self.make_instruction(instruction, ctrl_typevars.clone());
+                    self.blocks[block].insert_instruction(id);
+                    self.locations.insert(id, call_stack.clone());
+                    last_id = Some(id);
+                }
+
+                let id = last_id.expect("There should be at least 1 simplified instruction");
                 InsertInstructionResult::Results(id, self.instruction_results(id))
             }
         }
diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs
index 9691017f04b..f7875a73f6a 100644
--- a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs
+++ b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs
@@ -432,73 +432,11 @@ impl Instruction {
                 }
             }
             Instruction::Constrain(lhs, rhs, msg) => {
-                if dfg.resolve(*lhs) == dfg.resolve(*rhs) {
-                    // Remove trivial case `assert_eq(x, x)`
-                    SimplifyResult::Remove
+                let constraints = decompose_constrain(*lhs, *rhs, msg.clone(), dfg);
+                if constraints.is_empty() {
+                    Remove
                 } else {
-                    match (&dfg[dfg.resolve(*lhs)], &dfg[dfg.resolve(*rhs)]) {
-                        (
-                            Value::NumericConstant { constant, typ },
-                            Value::Instruction { instruction, .. },
-                        )
-                        | (
-                            Value::Instruction { instruction, .. },
-                            Value::NumericConstant { constant, typ },
-                        ) if *typ == Type::bool() => {
-                            match dfg[*instruction] {
-                                Instruction::Binary(Binary {
-                                    lhs,
-                                    rhs,
-                                    operator: BinaryOp::Eq,
-                                }) if constant.is_one() => {
-                                    // Replace an explicit two step equality assertion
-                                    //
-                                    // v2 = eq v0, u32 v1
-                                    // constrain v2 == u1 1
-                                    //
-                                    // with a direct assertion of equality between the two values
-                                    //
-                                    // v2 = eq v0, u32 v1
-                                    // constrain v0 == v1
-                                    //
-                                    // Note that this doesn't remove the value `v2` as it may be used in other instructions, but it
-                                    // will likely be removed through dead instruction elimination.
-
-                                    SimplifiedToInstruction(Instruction::Constrain(
-                                        lhs,
-                                        rhs,
-                                        msg.clone(),
-                                    ))
-                                }
-                                Instruction::Not(value) => {
-                                    // Replace an assertion that a not instruction is truthy
-                                    //
-                                    // v1 = not v0
-                                    // constrain v1 == u1 1
-                                    //
-                                    // with an assertion that the not instruction input is falsy
-                                    //
-                                    // v1 = not v0
-                                    // constrain v0 == u1 0
-                                    //
-                                    // Note that this doesn't remove the value `v1` as it may be used in other instructions, but it
-                                    // will likely be removed through dead instruction elimination.
-                                    let reversed_constant = FieldElement::from(!constant.is_one());
-                                    let reversed_constant =
-                                        dfg.make_constant(reversed_constant, Type::bool());
-                                    SimplifiedToInstruction(Instruction::Constrain(
-                                        value,
-                                        reversed_constant,
-                                        msg.clone(),
-                                    ))
-                                }
-
-                                _ => None,
-                            }
-                        }
-
-                        _ => None,
-                    }
+                    SimplifiedToInstructionMultiple(constraints)
                 }
             }
             Instruction::ArrayGet { array, index } => {
@@ -533,16 +471,43 @@ impl Instruction {
                     let truncated = numeric_constant.to_u128() % integer_modulus;
                     SimplifiedTo(dfg.make_constant(truncated.into(), typ))
                 } else if let Value::Instruction { instruction, .. } = &dfg[dfg.resolve(*value)] {
-                    if let Instruction::Truncate { bit_size: src_bit_size, .. } = &dfg[*instruction]
-                    {
-                        // If we're truncating the value to fit into the same or larger bit size then this is a noop.
-                        if src_bit_size <= bit_size && src_bit_size <= max_bit_size {
-                            SimplifiedTo(*value)
-                        } else {
-                            None
+                    match &dfg[*instruction] {
+                        Instruction::Truncate { bit_size: src_bit_size, .. } => {
+                            // If we're truncating the value to fit into the same or larger bit size then this is a noop.
+                            if src_bit_size <= bit_size && src_bit_size <= max_bit_size {
+                                SimplifiedTo(*value)
+                            } else {
+                                None
+                            }
                         }
-                    } else {
-                        None
+
+                        Instruction::Binary(Binary {
+                            lhs, rhs, operator: BinaryOp::Div, ..
+                        }) if dfg.is_constant(*rhs) => {
+                            // If we're truncating the result of a division by a constant denominator, we can
+                            // reason about the maximum bit size of the result and whether a truncation is necessary.
+
+                            let numerator_type = dfg.type_of_value(*lhs);
+                            let max_numerator_bits = numerator_type.bit_size();
+
+                            let divisor = dfg
+                                .get_numeric_constant(*rhs)
+                                .expect("rhs is checked to be constant.");
+                            let divisor_bits = divisor.num_bits();
+
+                            // 2^{max_quotient_bits} = 2^{max_numerator_bits} / 2^{divisor_bits}
+                            // => max_quotient_bits = max_numerator_bits - divisor_bits
+                            //
+                            // In order for the truncation to be a noop, we then require `max_quotient_bits < bit_size`.
+                            let max_quotient_bits = max_numerator_bits - divisor_bits;
+                            if max_quotient_bits < *bit_size {
+                                SimplifiedTo(*value)
+                            } else {
+                                None
+                            }
+                        }
+
+                        _ => None,
                     }
                 } else {
                     None
@@ -581,6 +546,14 @@ impl Instruction {
 /// that value is returned. Otherwise None is returned.
 fn simplify_cast(value: ValueId, dst_typ: &Type, dfg: &mut DataFlowGraph) -> SimplifyResult {
     use SimplifyResult::*;
+    let value = dfg.resolve(value);
+
+    if let Value::Instruction { instruction, .. } = &dfg[value] {
+        if let Instruction::Cast(original_value, _) = &dfg[*instruction] {
+            return SimplifiedToInstruction(Instruction::Cast(*original_value, dst_typ.clone()));
+        }
+    }
+
     if let Some(constant) = dfg.get_numeric_constant(value) {
         let src_typ = dfg.type_of_value(value);
         match (src_typ, dst_typ) {
@@ -619,6 +592,129 @@ fn simplify_cast(value: ValueId, dst_typ: &Type, dfg: &mut DataFlowGraph) -> Sim
     }
 }
 
+/// Try to decompose this constrain instruction. This constraint will be broken down such that it instead constrains
+/// all the values which are used to compute the values which were being constrained.
+fn decompose_constrain(
+    lhs: ValueId,
+    rhs: ValueId,
+    msg: Option<String>,
+    dfg: &mut DataFlowGraph,
+) -> Vec<Instruction> {
+    let lhs = dfg.resolve(lhs);
+    let rhs = dfg.resolve(rhs);
+
+    if lhs == rhs {
+        // Remove trivial case `assert_eq(x, x)`
+        Vec::new()
+    } else {
+        match (&dfg[lhs], &dfg[rhs]) {
+            (Value::NumericConstant { constant, typ }, Value::Instruction { instruction, .. })
+            | (Value::Instruction { instruction, .. }, Value::NumericConstant { constant, typ })
+                if *typ == Type::bool() =>
+            {
+                match dfg[*instruction] {
+                    Instruction::Binary(Binary { lhs, rhs, operator: BinaryOp::Eq })
+                        if constant.is_one() =>
+                    {
+                        // Replace an explicit two step equality assertion
+                        //
+                        // v2 = eq v0, u32 v1
+                        // constrain v2 == u1 1
+                        //
+                        // with a direct assertion of equality between the two values
+                        //
+                        // v2 = eq v0, u32 v1
+                        // constrain v0 == v1
+                        //
+                        // Note that this doesn't remove the value `v2` as it may be used in other instructions, but it
+                        // will likely be removed through dead instruction elimination.
+
+                        vec![Instruction::Constrain(lhs, rhs, msg)]
+                    }
+
+                    Instruction::Binary(Binary { lhs, rhs, operator: BinaryOp::Mul })
+                        if constant.is_one() && dfg.type_of_value(lhs) == Type::bool() =>
+                    {
+                        // Replace an equality assertion on a boolean multiplication
+                        //
+                        // v2 = mul v0, v1
+                        // constrain v2 == u1 1
+                        //
+                        // with a direct assertion that each value is equal to 1
+                        //
+                        // v2 = mul v0, v1
+                        // constrain v0 == 1
+                        // constrain v1 == 1
+                        //
+                        // This is due to the fact that for `v2` to be 1 then both `v0` and `v1` are 1.
+                        //
+                        // Note that this doesn't remove the value `v2` as it may be used in other instructions, but it
+                        // will likely be removed through dead instruction elimination.
+                        let one = FieldElement::one();
+                        let one = dfg.make_constant(one, Type::bool());
+
+                        [
+                            decompose_constrain(lhs, one, msg.clone(), dfg),
+                            decompose_constrain(rhs, one, msg, dfg),
+                        ]
+                        .concat()
+                    }
+
+                    Instruction::Binary(Binary { lhs, rhs, operator: BinaryOp::Or })
+                        if constant.is_zero() =>
+                    {
+                        // Replace an equality assertion on an OR
+                        //
+                        // v2 = or v0, v1
+                        // constrain v2 == u1 0
+                        //
+                        // with a direct assertion that each value is equal to 0
+                        //
+                        // v2 = or v0, v1
+                        // constrain v0 == 0
+                        // constrain v1 == 0
+                        //
+                        // This is due to the fact that for `v2` to be 0 then both `v0` and `v1` are 0.
+                        //
+                        // Note that this doesn't remove the value `v2` as it may be used in other instructions, but it
+                        // will likely be removed through dead instruction elimination.
+                        let zero = FieldElement::zero();
+                        let zero = dfg.make_constant(zero, dfg.type_of_value(lhs));
+
+                        [
+                            decompose_constrain(lhs, zero, msg.clone(), dfg),
+                            decompose_constrain(rhs, zero, msg, dfg),
+                        ]
+                        .concat()
+                    }
+
+                    Instruction::Not(value) => {
+                        // Replace an assertion that a not instruction is truthy
+                        //
+                        // v1 = not v0
+                        // constrain v1 == u1 1
+                        //
+                        // with an assertion that the not instruction input is falsy
+                        //
+                        // v1 = not v0
+                        // constrain v0 == u1 0
+                        //
+                        // Note that this doesn't remove the value `v1` as it may be used in other instructions, but it
+                        // will likely be removed through dead instruction elimination.
+                        let reversed_constant = FieldElement::from(!constant.is_one());
+                        let reversed_constant = dfg.make_constant(reversed_constant, Type::bool());
+                        decompose_constrain(value, reversed_constant, msg, dfg)
+                    }
+
+                    _ => vec![Instruction::Constrain(lhs, rhs, msg)],
+                }
+            }
+
+            _ => vec![Instruction::Constrain(lhs, rhs, msg)],
+        }
+    }
+}
+
 /// The possible return values for Instruction::return_types
 pub(crate) enum InstructionResultType {
     /// The result type of this instruction matches that of this operand
@@ -1107,6 +1203,10 @@ pub(crate) enum SimplifyResult {
     /// Replace this function with an simpler but equivalent instruction.
     SimplifiedToInstruction(Instruction),
 
+    /// Replace this function with a set of simpler but equivalent instructions.
+    /// This is currently only to be used for [`Instruction::Constrain`].
+    SimplifiedToInstructionMultiple(Vec<Instruction>),
+
     /// Remove the instruction, it is unnecessary
     Remove,
 
@@ -1115,9 +1215,10 @@ pub(crate) enum SimplifyResult {
 }
 
 impl SimplifyResult {
-    pub(crate) fn instruction(self) -> Option<Instruction> {
+    pub(crate) fn instructions(self) -> Option<Vec<Instruction>> {
         match self {
-            SimplifyResult::SimplifiedToInstruction(instruction) => Some(instruction),
+            SimplifyResult::SimplifiedToInstruction(instruction) => Some(vec![instruction]),
+            SimplifyResult::SimplifiedToInstructionMultiple(instructions) => Some(instructions),
             _ => None,
         }
     }
diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs
index edfc50a700f..146a4a8f124 100644
--- a/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs
+++ b/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs
@@ -392,6 +392,8 @@ fn simplify_black_box_func(
     match bb_func {
         BlackBoxFunc::SHA256 => simplify_hash(dfg, arguments, acvm::blackbox_solver::sha256),
         BlackBoxFunc::Blake2s => simplify_hash(dfg, arguments, acvm::blackbox_solver::blake2s),
+        BlackBoxFunc::Blake3 => simplify_hash(dfg, arguments, acvm::blackbox_solver::blake3),
+        BlackBoxFunc::Keccakf1600 => SimplifyResult::None, //TODO(Guillaume)
         BlackBoxFunc::Keccak256 => {
             match (dfg.get_array_constant(arguments[0]), dfg.get_numeric_constant(arguments[1])) {
                 (Some((input, _)), Some(num_bytes)) if array_is_constant(dfg, &input) => {
@@ -411,18 +413,6 @@ fn simplify_black_box_func(
                 _ => SimplifyResult::None,
             }
         }
-        BlackBoxFunc::HashToField128Security => match dfg.get_array_constant(arguments[0]) {
-            Some((input, _)) if array_is_constant(dfg, &input) => {
-                let input_bytes: Vec<u8> = to_u8_vec(dfg, input);
-
-                let field = acvm::blackbox_solver::hash_to_field_128_security(&input_bytes)
-                    .expect("Rust solvable black box function should not fail");
-
-                let field_constant = dfg.make_constant(field, Type::field());
-                SimplifyResult::SimplifiedTo(field_constant)
-            }
-            _ => SimplifyResult::None,
-        },
 
         BlackBoxFunc::EcdsaSecp256k1 => {
             simplify_signature(dfg, arguments, acvm::blackbox_solver::ecdsa_secp256k1_verify)
diff --git a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs
index 7d345a9a4ab..addaee3ba8d 100644
--- a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs
+++ b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs
@@ -184,10 +184,10 @@ mod test {
         function_builder::FunctionBuilder,
         ir::{
             function::RuntimeType,
-            instruction::{BinaryOp, Instruction, TerminatorInstruction},
+            instruction::{Binary, BinaryOp, Instruction, TerminatorInstruction},
             map::Id,
             types::Type,
-            value::Value,
+            value::{Value, ValueId},
         },
     };
 
@@ -247,6 +247,117 @@ mod test {
         }
     }
 
+    #[test]
+    fn redundant_truncation() {
+        // fn main f0 {
+        //   b0(v0: u16, v1: u16):
+        //     v2 = div v0, v1
+        //     v3 = truncate v2 to 8 bits, max_bit_size: 16
+        //     return v3
+        // }
+        //
+        // After constructing this IR, we set the value of v1 to 2^8.
+        // The expected return afterwards should be v2.
+        let main_id = Id::test_new(0);
+
+        // Compiling main
+        let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir);
+        let v0 = builder.add_parameter(Type::unsigned(16));
+        let v1 = builder.add_parameter(Type::unsigned(16));
+
+        // Note that this constant guarantees that `v0/constant < 2^8`. We then do not need to truncate the result.
+        let constant = 2_u128.pow(8);
+        let constant = builder.numeric_constant(constant, Type::field());
+
+        let v2 = builder.insert_binary(v0, BinaryOp::Div, v1);
+        let v3 = builder.insert_truncate(v2, 8, 16);
+        builder.terminate_with_return(vec![v3]);
+
+        let mut ssa = builder.finish();
+        let main = ssa.main_mut();
+        let instructions = main.dfg[main.entry_block()].instructions();
+        assert_eq!(instructions.len(), 2); // The final return is not counted
+
+        // Expected output:
+        //
+        // fn main f0 {
+        //   b0(Field 2: Field):
+        //     return Field 9
+        // }
+        main.dfg.set_value_from_id(v1, constant);
+
+        let ssa = ssa.fold_constants();
+        let main = ssa.main();
+
+        println!("{ssa}");
+
+        let instructions = main.dfg[main.entry_block()].instructions();
+        assert_eq!(instructions.len(), 1);
+        let instruction = &main.dfg[instructions[0]];
+
+        assert_eq!(
+            instruction,
+            &Instruction::Binary(Binary { lhs: v0, operator: BinaryOp::Div, rhs: constant })
+        );
+    }
+
+    #[test]
+    fn non_redundant_truncation() {
+        // fn main f0 {
+        //   b0(v0: u16, v1: u16):
+        //     v2 = div v0, v1
+        //     v3 = truncate v2 to 8 bits, max_bit_size: 16
+        //     return v3
+        // }
+        //
+        // After constructing this IR, we set the value of v1 to 2^8 - 1.
+        // This should not result in the truncation being removed.
+        let main_id = Id::test_new(0);
+
+        // Compiling main
+        let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir);
+        let v0 = builder.add_parameter(Type::unsigned(16));
+        let v1 = builder.add_parameter(Type::unsigned(16));
+
+        // Note that this constant does not guarantee that `v0/constant < 2^8`. We must then truncate the result.
+        let constant = 2_u128.pow(8) - 1;
+        let constant = builder.numeric_constant(constant, Type::field());
+
+        let v2 = builder.insert_binary(v0, BinaryOp::Div, v1);
+        let v3 = builder.insert_truncate(v2, 8, 16);
+        builder.terminate_with_return(vec![v3]);
+
+        let mut ssa = builder.finish();
+        let main = ssa.main_mut();
+        let instructions = main.dfg[main.entry_block()].instructions();
+        assert_eq!(instructions.len(), 2); // The final return is not counted
+
+        // Expected output:
+        //
+        // fn main f0 {
+        //   b0(v0: u16, Field 255: Field):
+        //      v5 = div v0, Field 255
+        //      v6 = truncate v5 to 8 bits, max_bit_size: 16
+        //      return v6
+        // }
+        main.dfg.set_value_from_id(v1, constant);
+
+        let ssa = ssa.fold_constants();
+        let main = ssa.main();
+
+        let instructions = main.dfg[main.entry_block()].instructions();
+        assert_eq!(instructions.len(), 2);
+
+        assert_eq!(
+            &main.dfg[instructions[0]],
+            &Instruction::Binary(Binary { lhs: v0, operator: BinaryOp::Div, rhs: constant })
+        );
+        assert_eq!(
+            &main.dfg[instructions[1]],
+            &Instruction::Truncate { value: ValueId::test_new(5), bit_size: 8, max_bit_size: 16 }
+        );
+    }
+
     #[test]
     fn arrays_elements_are_updated() {
         // fn main f0 {
@@ -334,4 +445,67 @@ mod test {
 
         assert_eq!(instruction, &Instruction::Cast(v0, Type::unsigned(32)));
     }
+
+    #[test]
+    fn constraint_decomposition() {
+        // fn main f0 {
+        //   b0(v0: u1, v1: u1, v2: u1):
+        //     v3 = mul v0 v1
+        //     v4 = not v2
+        //     v5 = mul v3 v4
+        //     constrain v4 u1 1
+        // }
+        //
+        // When constructing this IR, we should automatically decompose the constraint to be in terms of `v0`, `v1` and `v2`.
+        //
+        // The mul instructions are retained and will be removed in the dead instruction elimination pass.
+        let main_id = Id::test_new(0);
+
+        // Compiling main
+        let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir);
+        let v0 = builder.add_parameter(Type::bool());
+        let v1 = builder.add_parameter(Type::bool());
+        let v2 = builder.add_parameter(Type::bool());
+
+        let v3 = builder.insert_binary(v0, BinaryOp::Mul, v1);
+        let v4 = builder.insert_not(v2);
+        let v5 = builder.insert_binary(v3, BinaryOp::Mul, v4);
+
+        // This constraint is automatically decomposed when it is inserted.
+        let v_true = builder.numeric_constant(true, Type::bool());
+        builder.insert_constrain(v5, v_true, None);
+
+        let v_false = builder.numeric_constant(false, Type::bool());
+
+        // Expected output:
+        //
+        // fn main f0 {
+        //   b0(v0: u1, v1: u1, v2: u1):
+        //     v3 = mul v0 v1
+        //     v4 = not v2
+        //     v5 = mul v3 v4
+        //     constrain v0 u1 1
+        //     constrain v1 u1 1
+        //     constrain v2 u1 0
+        // }
+
+        let ssa = builder.finish();
+        let main = ssa.main();
+        let instructions = main.dfg[main.entry_block()].instructions();
+
+        assert_eq!(instructions.len(), 6);
+
+        assert_eq!(
+            main.dfg[instructions[0]],
+            Instruction::Binary(Binary { lhs: v0, operator: BinaryOp::Mul, rhs: v1 })
+        );
+        assert_eq!(main.dfg[instructions[1]], Instruction::Not(v2));
+        assert_eq!(
+            main.dfg[instructions[2]],
+            Instruction::Binary(Binary { lhs: v3, operator: BinaryOp::Mul, rhs: v4 })
+        );
+        assert_eq!(main.dfg[instructions[3]], Instruction::Constrain(v0, v_true, None));
+        assert_eq!(main.dfg[instructions[4]], Instruction::Constrain(v1, v_true, None));
+        assert_eq!(main.dfg[instructions[5]], Instruction::Constrain(v2, v_false, None));
+    }
 }
diff --git a/compiler/noirc_frontend/src/hir/def_map/mod.rs b/compiler/noirc_frontend/src/hir/def_map/mod.rs
index 9c46ef35854..fbf94468c4b 100644
--- a/compiler/noirc_frontend/src/hir/def_map/mod.rs
+++ b/compiler/noirc_frontend/src/hir/def_map/mod.rs
@@ -64,6 +64,7 @@ pub struct CrateDefMap {
 
     pub(crate) krate: CrateId,
 
+    /// Maps an external dependency's name to its root module id.
     pub(crate) extern_prelude: BTreeMap<String, ModuleId>,
 }
 
diff --git a/compiler/noirc_frontend/src/parser/errors.rs b/compiler/noirc_frontend/src/parser/errors.rs
index 09dc6dfff8d..5c869ff4719 100644
--- a/compiler/noirc_frontend/src/parser/errors.rs
+++ b/compiler/noirc_frontend/src/parser/errors.rs
@@ -26,6 +26,8 @@ pub enum ParserErrorReason {
     EarlyReturn,
     #[error("Patterns aren't allowed in a trait's function declarations")]
     PatternInTraitFunctionParameter,
+    #[error("Modifiers are ignored on a trait impl method")]
+    TraitImplFunctionModifiers,
     #[error("comptime keyword is deprecated")]
     ComptimeDeprecated,
     #[error("{0} are experimental and aren't fully supported yet")]
@@ -148,6 +150,11 @@ impl From<ParserError> for Diagnostic {
                         "".into(),
                         error.span,
                     ),
+                    ParserErrorReason::TraitImplFunctionModifiers => Diagnostic::simple_warning(
+                        reason.to_string(),
+                        "".into(),
+                        error.span,
+                    ),
                     ParserErrorReason::ExpectedPatternButFoundType(ty) => {
                         Diagnostic::simple_error("Expected a ; separating these two statements".into(), format!("{ty} is a type and cannot be used as a variable name"), error.span)
                     }
diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs
index b149eb24f07..954b531abff 100644
--- a/compiler/noirc_frontend/src/parser/parser.rs
+++ b/compiler/noirc_frontend/src/parser/parser.rs
@@ -613,7 +613,18 @@ fn trait_implementation() -> impl NoirParser<TopLevelStatement> {
 }
 
 fn trait_implementation_body() -> impl NoirParser<Vec<TraitImplItem>> {
-    let function = function_definition(true).map(TraitImplItem::Function);
+    let function = function_definition(true).validate(|mut f, span, emit| {
+        if f.def().is_internal
+            || f.def().is_unconstrained
+            || f.def().is_open
+            || f.def().visibility != FunctionVisibility::Private
+        {
+            emit(ParserError::with_reason(ParserErrorReason::TraitImplFunctionModifiers, span));
+        }
+        // Trait impl functions are always public
+        f.def_mut().visibility = FunctionVisibility::Public;
+        TraitImplItem::Function(f)
+    });
 
     let alias = keyword(Keyword::Type)
         .ignore_then(ident())
diff --git a/cspell.json b/cspell.json
index 778d559bb12..6864e863740 100644
--- a/cspell.json
+++ b/cspell.json
@@ -94,6 +94,7 @@
         "jsdoc",
         "Jubjub",
         "keccak",
+        "keccakf",
         "krate",
         "lvalue",
         "Maddiaa",
diff --git a/docs/docs/noir/modules_packages_crates/crates_and_packages.md b/docs/docs/noir/modules_packages_crates/crates_and_packages.md
index aae6795b229..760a463094c 100644
--- a/docs/docs/noir/modules_packages_crates/crates_and_packages.md
+++ b/docs/docs/noir/modules_packages_crates/crates_and_packages.md
@@ -24,7 +24,7 @@ _Library crates_ don't have a `main` function and they don't compile down to ACI
 
 #### Contracts
 
-Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/yarn-project/noir-contracts/src/contracts).
+Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/yarn-project/noir-contracts/contracts).
 
 ### Crate Root
 
diff --git a/docs/docs/noir/modules_packages_crates/dependencies.md b/docs/docs/noir/modules_packages_crates/dependencies.md
index 1e93b30781a..a37dc401b7d 100644
--- a/docs/docs/noir/modules_packages_crates/dependencies.md
+++ b/docs/docs/noir/modules_packages_crates/dependencies.md
@@ -35,7 +35,7 @@ If the module is in a subdirectory, you can define a subdirectory in your git re
 # Nargo.toml
 
 [dependencies]
-easy_private_token_contract = {tag ="v0.1.0-alpha62", git = "https://github.com/AztecProtocol/aztec-packages", directory = "yarn-project/noir-contracts/src/contracts/easy_private_token_contract"}
+easy_private_token_contract = {tag ="v0.1.0-alpha62", git = "https://github.com/AztecProtocol/aztec-packages", directory = "yarn-project/noir-contracts/contracts/easy_private_token_contract"}
 ```
 
 ## Specifying a local dependency
diff --git a/docs/docs/noir/standard_library/black_box_fns.md b/docs/docs/noir/standard_library/black_box_fns.md
index e0c6d475c1f..4b1efbd17de 100644
--- a/docs/docs/noir/standard_library/black_box_fns.md
+++ b/docs/docs/noir/standard_library/black_box_fns.md
@@ -31,7 +31,6 @@ Here is a list of the current black box functions that are supported by UltraPlo
 - [Blake2s](./cryptographic_primitives/hashes#blake2s)
 - [Pedersen Hash](./cryptographic_primitives/hashes#pedersen_hash)
 - [Pedersen Commitment](./cryptographic_primitives/hashes#pedersen_commitment)
-- [HashToField128Security](./cryptographic_primitives/hashes#hash_to_field)
 - [ECDSA signature verification](./cryptographic_primitives/ecdsa_sig_verification)
 - [Fixed base scalar multiplication](./cryptographic_primitives/scalar)
 - [Compute merkle root](./merkle_trees#compute_merkle_root)
diff --git a/docs/docs/noir/standard_library/cryptographic_primitives/hashes.mdx b/docs/docs/noir/standard_library/cryptographic_primitives/hashes.mdx
index 9250cb4a0c0..3c5f7f79603 100644
--- a/docs/docs/noir/standard_library/cryptographic_primitives/hashes.mdx
+++ b/docs/docs/noir/standard_library/cryptographic_primitives/hashes.mdx
@@ -165,4 +165,3 @@ fn hash_to_field<N>(_input : [Field; N]) -> Field {}
 Calculates the `blake2s` hash of the inputs and returns the hash modulo the field modulus to return
 a value which can be represented as a `Field`.
 
-<BlackBoxInfo />
diff --git a/docs/docs/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md b/docs/docs/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md
index 0ba5783f0d5..5e3cd53e9d3 100644
--- a/docs/docs/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md
+++ b/docs/docs/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md
@@ -8,7 +8,6 @@ ecdsa_secp256k1_verify(
    signature): boolean
 ```
 
-Calculates the Blake2s256 hash of the input bytes and represents these as a single field element.
 Verifies a ECDSA signature over the secp256k1 curve.
 
 ## Parameters
diff --git a/docs/docs/reference/NoirJS/noir_js/index.md b/docs/docs/reference/NoirJS/noir_js/index.md
index 8b9e35bc9a1..d600e21b299 100644
--- a/docs/docs/reference/NoirJS/noir_js/index.md
+++ b/docs/docs/reference/NoirJS/noir_js/index.md
@@ -26,7 +26,7 @@
 | :------ | :------ |
 | [and](functions/and.md) | Performs a bitwise AND operation between `lhs` and `rhs` |
 | [blake2s256](functions/blake2s256.md) | Calculates the Blake2s256 hash of the input bytes |
-| [ecdsa\_secp256k1\_verify](functions/ecdsa_secp256k1_verify.md) | Calculates the Blake2s256 hash of the input bytes and represents these as a single field element. |
+| [ecdsa\_secp256k1\_verify](functions/ecdsa_secp256k1_verify.md) | Verifies a ECDSA signature over the secp256k1 curve. |
 | [ecdsa\_secp256r1\_verify](functions/ecdsa_secp256r1_verify.md) | Verifies a ECDSA signature over the secp256r1 curve. |
 | [keccak256](functions/keccak256.md) | Calculates the Keccak256 hash of the input bytes |
 | [sha256](functions/sha256.md) | Calculates the SHA256 hash of the input bytes |
diff --git a/docs/src/pages/index.jsx b/docs/src/pages/index.jsx
index 8485a730785..d5cbfcba977 100644
--- a/docs/src/pages/index.jsx
+++ b/docs/src/pages/index.jsx
@@ -38,7 +38,7 @@ export default function Landing() {
           </h1>
           <p className="homepage_p">
             Noir is a Domain Specific Language for SNARK proving systems. It has been designed to use any ACIR
-            compatible proving system. It's design choices are influenced heavily by Rust and focuses on a simple,
+            compatible proving system. Its design choices are influenced heavily by Rust and focuses on a simple,
             familiar syntax.
           </p>
 
diff --git a/docs/versioned_docs/version-v0.17.0/modules_packages_crates/crates_and_packages.md b/docs/versioned_docs/version-v0.17.0/modules_packages_crates/crates_and_packages.md
index fb83a33d94e..744de72bb2c 100644
--- a/docs/versioned_docs/version-v0.17.0/modules_packages_crates/crates_and_packages.md
+++ b/docs/versioned_docs/version-v0.17.0/modules_packages_crates/crates_and_packages.md
@@ -23,7 +23,7 @@ _Library crates_ don't have a `main` function and they don't compile down to ACI
 
 #### Contracts
 
-Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/yarn-project/noir-contracts/src/contracts).
+Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/yarn-project/noir-contracts/contracts).
 
 ### Crate Root
 
diff --git a/docs/versioned_docs/version-v0.17.0/modules_packages_crates/dependencies.md b/docs/versioned_docs/version-v0.17.0/modules_packages_crates/dependencies.md
index 2c188992616..e91e73a4c4f 100644
--- a/docs/versioned_docs/version-v0.17.0/modules_packages_crates/dependencies.md
+++ b/docs/versioned_docs/version-v0.17.0/modules_packages_crates/dependencies.md
@@ -34,7 +34,7 @@ If the module is in a subdirectory, you can define a subdirectory in your git re
 # Nargo.toml
 
 [dependencies]
-easy_private_token_contract = {tag ="v0.1.0-alpha62", git = "https://github.com/AztecProtocol/aztec-packages", directory = "yarn-project/noir-contracts/src/contracts/easy_private_token_contract"}
+easy_private_token_contract = {tag ="v0.1.0-alpha62", git = "https://github.com/AztecProtocol/aztec-packages", directory = "yarn-project/noir-contracts/contracts/easy_private_token_contract"}
 ```
 
 ## Specifying a local dependency
diff --git a/docs/versioned_docs/version-v0.19.0/modules_packages_crates/crates_and_packages.md b/docs/versioned_docs/version-v0.19.0/modules_packages_crates/crates_and_packages.md
index fb83a33d94e..744de72bb2c 100644
--- a/docs/versioned_docs/version-v0.19.0/modules_packages_crates/crates_and_packages.md
+++ b/docs/versioned_docs/version-v0.19.0/modules_packages_crates/crates_and_packages.md
@@ -23,7 +23,7 @@ _Library crates_ don't have a `main` function and they don't compile down to ACI
 
 #### Contracts
 
-Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/yarn-project/noir-contracts/src/contracts).
+Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/yarn-project/noir-contracts/contracts).
 
 ### Crate Root
 
diff --git a/docs/versioned_docs/version-v0.19.0/modules_packages_crates/dependencies.md b/docs/versioned_docs/version-v0.19.0/modules_packages_crates/dependencies.md
index 2c188992616..e91e73a4c4f 100644
--- a/docs/versioned_docs/version-v0.19.0/modules_packages_crates/dependencies.md
+++ b/docs/versioned_docs/version-v0.19.0/modules_packages_crates/dependencies.md
@@ -34,7 +34,7 @@ If the module is in a subdirectory, you can define a subdirectory in your git re
 # Nargo.toml
 
 [dependencies]
-easy_private_token_contract = {tag ="v0.1.0-alpha62", git = "https://github.com/AztecProtocol/aztec-packages", directory = "yarn-project/noir-contracts/src/contracts/easy_private_token_contract"}
+easy_private_token_contract = {tag ="v0.1.0-alpha62", git = "https://github.com/AztecProtocol/aztec-packages", directory = "yarn-project/noir-contracts/contracts/easy_private_token_contract"}
 ```
 
 ## Specifying a local dependency
diff --git a/docs/versioned_docs/version-v0.19.1/modules_packages_crates/crates_and_packages.md b/docs/versioned_docs/version-v0.19.1/modules_packages_crates/crates_and_packages.md
index fb83a33d94e..744de72bb2c 100644
--- a/docs/versioned_docs/version-v0.19.1/modules_packages_crates/crates_and_packages.md
+++ b/docs/versioned_docs/version-v0.19.1/modules_packages_crates/crates_and_packages.md
@@ -23,7 +23,7 @@ _Library crates_ don't have a `main` function and they don't compile down to ACI
 
 #### Contracts
 
-Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/yarn-project/noir-contracts/src/contracts).
+Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/yarn-project/noir-contracts/contracts).
 
 ### Crate Root
 
diff --git a/docs/versioned_docs/version-v0.19.1/modules_packages_crates/dependencies.md b/docs/versioned_docs/version-v0.19.1/modules_packages_crates/dependencies.md
index 2c188992616..e91e73a4c4f 100644
--- a/docs/versioned_docs/version-v0.19.1/modules_packages_crates/dependencies.md
+++ b/docs/versioned_docs/version-v0.19.1/modules_packages_crates/dependencies.md
@@ -34,7 +34,7 @@ If the module is in a subdirectory, you can define a subdirectory in your git re
 # Nargo.toml
 
 [dependencies]
-easy_private_token_contract = {tag ="v0.1.0-alpha62", git = "https://github.com/AztecProtocol/aztec-packages", directory = "yarn-project/noir-contracts/src/contracts/easy_private_token_contract"}
+easy_private_token_contract = {tag ="v0.1.0-alpha62", git = "https://github.com/AztecProtocol/aztec-packages", directory = "yarn-project/noir-contracts/contracts/easy_private_token_contract"}
 ```
 
 ## Specifying a local dependency
diff --git a/docs/versioned_docs/version-v0.19.2/modules_packages_crates/crates_and_packages.md b/docs/versioned_docs/version-v0.19.2/modules_packages_crates/crates_and_packages.md
index fb83a33d94e..744de72bb2c 100644
--- a/docs/versioned_docs/version-v0.19.2/modules_packages_crates/crates_and_packages.md
+++ b/docs/versioned_docs/version-v0.19.2/modules_packages_crates/crates_and_packages.md
@@ -23,7 +23,7 @@ _Library crates_ don't have a `main` function and they don't compile down to ACI
 
 #### Contracts
 
-Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/yarn-project/noir-contracts/src/contracts).
+Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/yarn-project/noir-contracts/contracts).
 
 ### Crate Root
 
diff --git a/docs/versioned_docs/version-v0.19.2/modules_packages_crates/dependencies.md b/docs/versioned_docs/version-v0.19.2/modules_packages_crates/dependencies.md
index 2c188992616..e91e73a4c4f 100644
--- a/docs/versioned_docs/version-v0.19.2/modules_packages_crates/dependencies.md
+++ b/docs/versioned_docs/version-v0.19.2/modules_packages_crates/dependencies.md
@@ -34,7 +34,7 @@ If the module is in a subdirectory, you can define a subdirectory in your git re
 # Nargo.toml
 
 [dependencies]
-easy_private_token_contract = {tag ="v0.1.0-alpha62", git = "https://github.com/AztecProtocol/aztec-packages", directory = "yarn-project/noir-contracts/src/contracts/easy_private_token_contract"}
+easy_private_token_contract = {tag ="v0.1.0-alpha62", git = "https://github.com/AztecProtocol/aztec-packages", directory = "yarn-project/noir-contracts/contracts/easy_private_token_contract"}
 ```
 
 ## Specifying a local dependency
diff --git a/docs/versioned_docs/version-v0.19.3/modules_packages_crates/crates_and_packages.md b/docs/versioned_docs/version-v0.19.3/modules_packages_crates/crates_and_packages.md
index fb83a33d94e..744de72bb2c 100644
--- a/docs/versioned_docs/version-v0.19.3/modules_packages_crates/crates_and_packages.md
+++ b/docs/versioned_docs/version-v0.19.3/modules_packages_crates/crates_and_packages.md
@@ -23,7 +23,7 @@ _Library crates_ don't have a `main` function and they don't compile down to ACI
 
 #### Contracts
 
-Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/yarn-project/noir-contracts/src/contracts).
+Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/yarn-project/noir-contracts/contracts).
 
 ### Crate Root
 
diff --git a/docs/versioned_docs/version-v0.19.3/modules_packages_crates/dependencies.md b/docs/versioned_docs/version-v0.19.3/modules_packages_crates/dependencies.md
index 2c188992616..e91e73a4c4f 100644
--- a/docs/versioned_docs/version-v0.19.3/modules_packages_crates/dependencies.md
+++ b/docs/versioned_docs/version-v0.19.3/modules_packages_crates/dependencies.md
@@ -34,7 +34,7 @@ If the module is in a subdirectory, you can define a subdirectory in your git re
 # Nargo.toml
 
 [dependencies]
-easy_private_token_contract = {tag ="v0.1.0-alpha62", git = "https://github.com/AztecProtocol/aztec-packages", directory = "yarn-project/noir-contracts/src/contracts/easy_private_token_contract"}
+easy_private_token_contract = {tag ="v0.1.0-alpha62", git = "https://github.com/AztecProtocol/aztec-packages", directory = "yarn-project/noir-contracts/contracts/easy_private_token_contract"}
 ```
 
 ## Specifying a local dependency
diff --git a/docs/versioned_docs/version-v0.19.4/modules_packages_crates/crates_and_packages.md b/docs/versioned_docs/version-v0.19.4/modules_packages_crates/crates_and_packages.md
index fb83a33d94e..744de72bb2c 100644
--- a/docs/versioned_docs/version-v0.19.4/modules_packages_crates/crates_and_packages.md
+++ b/docs/versioned_docs/version-v0.19.4/modules_packages_crates/crates_and_packages.md
@@ -23,7 +23,7 @@ _Library crates_ don't have a `main` function and they don't compile down to ACI
 
 #### Contracts
 
-Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/yarn-project/noir-contracts/src/contracts).
+Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/yarn-project/noir-contracts/contracts).
 
 ### Crate Root
 
diff --git a/docs/versioned_docs/version-v0.19.4/modules_packages_crates/dependencies.md b/docs/versioned_docs/version-v0.19.4/modules_packages_crates/dependencies.md
index 2c188992616..e91e73a4c4f 100644
--- a/docs/versioned_docs/version-v0.19.4/modules_packages_crates/dependencies.md
+++ b/docs/versioned_docs/version-v0.19.4/modules_packages_crates/dependencies.md
@@ -34,7 +34,7 @@ If the module is in a subdirectory, you can define a subdirectory in your git re
 # Nargo.toml
 
 [dependencies]
-easy_private_token_contract = {tag ="v0.1.0-alpha62", git = "https://github.com/AztecProtocol/aztec-packages", directory = "yarn-project/noir-contracts/src/contracts/easy_private_token_contract"}
+easy_private_token_contract = {tag ="v0.1.0-alpha62", git = "https://github.com/AztecProtocol/aztec-packages", directory = "yarn-project/noir-contracts/contracts/easy_private_token_contract"}
 ```
 
 ## Specifying a local dependency
diff --git a/docs/versioned_docs/version-v0.22.0/noir/modules_packages_crates/crates_and_packages.md b/docs/versioned_docs/version-v0.22.0/noir/modules_packages_crates/crates_and_packages.md
index aae6795b229..760a463094c 100644
--- a/docs/versioned_docs/version-v0.22.0/noir/modules_packages_crates/crates_and_packages.md
+++ b/docs/versioned_docs/version-v0.22.0/noir/modules_packages_crates/crates_and_packages.md
@@ -24,7 +24,7 @@ _Library crates_ don't have a `main` function and they don't compile down to ACI
 
 #### Contracts
 
-Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/yarn-project/noir-contracts/src/contracts).
+Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/yarn-project/noir-contracts/contracts).
 
 ### Crate Root
 
diff --git a/docs/versioned_docs/version-v0.22.0/noir/modules_packages_crates/dependencies.md b/docs/versioned_docs/version-v0.22.0/noir/modules_packages_crates/dependencies.md
index 1e93b30781a..a37dc401b7d 100644
--- a/docs/versioned_docs/version-v0.22.0/noir/modules_packages_crates/dependencies.md
+++ b/docs/versioned_docs/version-v0.22.0/noir/modules_packages_crates/dependencies.md
@@ -35,7 +35,7 @@ If the module is in a subdirectory, you can define a subdirectory in your git re
 # Nargo.toml
 
 [dependencies]
-easy_private_token_contract = {tag ="v0.1.0-alpha62", git = "https://github.com/AztecProtocol/aztec-packages", directory = "yarn-project/noir-contracts/src/contracts/easy_private_token_contract"}
+easy_private_token_contract = {tag ="v0.1.0-alpha62", git = "https://github.com/AztecProtocol/aztec-packages", directory = "yarn-project/noir-contracts/contracts/easy_private_token_contract"}
 ```
 
 ## Specifying a local dependency
diff --git a/noir_stdlib/src/hash.nr b/noir_stdlib/src/hash.nr
index ad7e4f2e28f..5933209d9bc 100644
--- a/noir_stdlib/src/hash.nr
+++ b/noir_stdlib/src/hash.nr
@@ -7,6 +7,9 @@ pub fn sha256<N>(_input: [u8; N]) -> [u8; 32] {}
 #[foreign(blake2s)]
 pub fn blake2s<N>(_input: [u8; N]) -> [u8; 32] {}
 
+#[foreign(blake3)]
+pub fn blake3<N>(_input: [u8; N]) -> [u8; 32] {}
+
 struct PedersenPoint {
    x : Field,
    y : Field,
diff --git a/noir_stdlib/src/lib.nr b/noir_stdlib/src/lib.nr
index 9b166f6ae94..23a7c71ff45 100644
--- a/noir_stdlib/src/lib.nr
+++ b/noir_stdlib/src/lib.nr
@@ -23,6 +23,7 @@ mod cmp;
 mod ops;
 mod default;
 mod prelude;
+mod uint128;
 
 // Oracle calls are required to be wrapped in an unconstrained function
 // Thus, the only argument to the `println` oracle is expected to always be an ident
@@ -38,13 +39,8 @@ unconstrained pub fn println<T>(input: T) {
 }
 
 #[foreign(recursive_aggregation)]
-pub fn verify_proof<N>(
-    _verification_key: [Field],
-    _proof: [Field],
-    _public_inputs: [Field],
-    _key_hash: Field,
-    _input_aggregation_object: [Field; N]
-) -> [Field; N] {}
+pub fn verify_proof<N>(_verification_key: [Field], _proof: [Field], _public_inputs: [Field], _key_hash: Field) {}
+
 // Asserts that the given value is known at compile-time.
 // Useful for debugging for-loop bounds.
 #[builtin(assert_constant)]
diff --git a/noir_stdlib/src/prelude.nr b/noir_stdlib/src/prelude.nr
index 56020509122..b57ff460371 100644
--- a/noir_stdlib/src/prelude.nr
+++ b/noir_stdlib/src/prelude.nr
@@ -1,5 +1,6 @@
 use crate::collections::vec::Vec;
 use crate::option::Option;
 use crate::{print, println, assert_constant};
+use crate::uint128::U128;
 use crate::cmp::{Eq, Ord};
 use crate::default::Default;
diff --git a/noir_stdlib/src/uint128.nr b/noir_stdlib/src/uint128.nr
new file mode 100644
index 00000000000..4a58b3868be
--- /dev/null
+++ b/noir_stdlib/src/uint128.nr
@@ -0,0 +1,292 @@
+use crate::ops::{Add, Sub, Mul, Div, Rem, BitOr, BitAnd, BitXor, Shl, Shr};
+use crate::cmp::{Eq, Ord, Ordering};
+
+global pow64 : Field = 18446744073709551616; //2^64;
+
+struct U128 {
+    lo: Field,
+    hi: Field,
+}
+
+impl U128 {
+
+    pub fn from_u64s_le(lo: u64, hi: u64) -> U128 {
+        // in order to handle multiplication, we need to represent the product of two u64 without overflow
+        assert(crate::field::modulus_num_bits() as u32 > 128);
+        U128 {
+            lo: lo as Field,
+            hi: hi as Field,
+        }
+    }
+
+    pub fn from_u64s_be(hi: u64, lo: u64) -> U128 {
+        U128::from_u64s_le(lo,hi)
+    }
+
+    pub fn from_le_bytes(bytes: [u8; 16]) -> U128 {
+        let mut lo = 0;
+        let mut base = 1;
+        for i in 0..8 {
+            lo += (bytes[i] as Field)*base;
+            base *= 256;
+        }
+        let mut hi = 0;
+        base = 1;
+        for i in 8..16 {
+            hi += (bytes[i] as Field)*base;
+            base *= 256;
+        }
+        U128 {
+            lo,
+            hi,
+        }
+    }
+
+    pub fn to_le_bytes(self: Self) -> [u8; 16] {
+        let lo = self.lo.to_le_bytes(8);
+        let hi = self.hi.to_le_bytes(8);
+        let mut bytes = [0;16];
+        for i in 0..8 {
+            bytes[i] = lo[i];
+            bytes[i+8] = hi[i];
+        }
+        bytes
+    }
+
+    pub fn from_hex<N>(hex: str<N>) -> U128 {
+        let N = N as u32;
+        let bytes = hex.as_bytes();
+        // string must starts with "0x"
+        assert((bytes[0] == 48) & (bytes[1] == 120), "Invalid hexadecimal string");
+        assert(N < 35, "Input does not fit into a U128");
+
+        let mut lo = 0;
+        let mut hi = 0;
+        let mut base = 1; 
+        if N <= 18 {
+            for i in 0..N-2 {
+                lo += U128::decode_ascii(bytes[N-i-1])*base;
+                base = base*16;
+            }
+        } else {
+            for i in 0..16 {
+                lo += U128::decode_ascii(bytes[N-i-1])*base;
+                base = base*16;
+            }
+            base = 1;
+            for i in 17..N-1 {           
+                hi += U128::decode_ascii(bytes[N-i])*base;
+                base = base*16;
+            }
+        }
+        U128 {
+            lo: lo as Field,
+            hi: hi as Field,
+        }
+    }
+
+    fn decode_ascii(ascii: u8) -> Field {
+        if ascii < 58 {
+            ascii - 48
+        } else {
+            if ascii < 71 {
+                ascii - 55
+            } else {
+                ascii - 87
+            }
+            
+        } as Field
+    }
+
+    unconstrained fn unconstrained_div(self: Self, b: U128) -> (U128, U128) {
+        if self < b {
+            (U128::from_u64s_le(0, 0), self)
+        } else {
+            //TODO check if this can overflow?
+            let (q,r) = self.unconstrained_div(b * U128::from_u64s_le(2,0));
+            let q_mul_2 = q * U128::from_u64s_le(2,0);
+            if r < b {
+                (q_mul_2, r)
+            } else {
+                (q_mul_2 + U128::from_u64s_le(1,0), r - b)
+            }
+
+        }  
+    }
+
+    pub fn from_integer<T>(i: T) -> U128 {
+        let f = crate::as_field(i);
+        let lo = f as u64 as Field;
+        let hi = (f-lo) / pow64;
+        U128 {
+            lo,
+            hi,
+        }
+    }
+
+    pub fn to_integer<T>(self) -> T {
+        crate::from_field(self.lo+self.hi*pow64)
+    }
+
+    fn wrapping_mul(self: Self, b: U128) -> U128 {
+        let low = self.lo*b.lo;
+        let lo = low as u64 as Field;
+        let carry = (low - lo) / pow64;
+        let high = if crate::field::modulus_num_bits() as u32 > 196 {
+            (self.lo+self.hi)*(b.lo+b.hi) - low + carry
+        } else {
+            self.lo*b.hi + self.hi*b.lo + carry
+        };
+        let hi = high as u64 as Field;
+        U128 {
+            lo,
+            hi,
+        }
+    }
+}
+
+impl Add for U128 {
+    pub fn add(self: Self, b: U128) -> U128 {
+        let low = self.lo + b.lo;
+        let lo = low as u64 as Field;
+        let carry = (low - lo) / pow64;  
+        let high = self.hi + b.hi + carry;
+        let hi = high as u64 as Field;
+        assert(hi == high, "attempt to add with overflow");
+        U128 {
+            lo,
+            hi,
+        }
+    }
+}
+
+impl Sub for U128 {
+    pub fn sub(self: Self, b: U128) -> U128 {
+        let low = pow64 + self.lo - b.lo;
+        let lo = low as u64 as Field;
+        let borrow = (low == lo) as Field;
+        let high = self.hi - b.hi - borrow;
+        let hi = high as u64 as Field;
+        assert(hi == high, "attempt to subtract with overflow");
+        U128 {
+            lo,
+            hi,
+        }
+    }
+}
+
+impl Mul for U128 {
+    pub fn mul(self: Self, b: U128) -> U128 {
+        assert(self.hi*b.hi == 0, "attempt to multiply with overflow");
+        let low = self.lo*b.lo;
+        let lo = low as u64 as Field;
+        let carry = (low - lo) / pow64;
+        let high = if crate::field::modulus_num_bits() as u32 > 196 {
+            (self.lo+self.hi)*(b.lo+b.hi) - low + carry
+        } else {
+            self.lo*b.hi + self.hi*b.lo + carry
+        };
+        let hi = high as u64 as Field;
+        assert(hi == high, "attempt to multiply with overflow");
+        U128 {
+            lo,
+            hi,
+        }
+    }
+}
+
+impl Div for U128 {
+    pub fn div(self: Self, b: U128) -> U128 {
+        let (q,r) = self.unconstrained_div(b);
+        let a = b * q + r;
+        assert_eq(self, a);
+        assert(r < b);
+        q
+    }
+}
+
+impl Rem for U128 {
+    pub fn rem(self: Self, b: U128) -> U128 {
+        let (q,r) = self.unconstrained_div(b);
+        let a = b * q + r;
+        assert_eq(self, a);
+        assert(r < b);
+        r
+    }
+}
+
+impl Eq for U128 {
+    pub fn eq(self: Self, b: U128) -> bool {
+        (self.lo == b.lo) & (self.hi == b.hi)
+    }
+}
+
+impl Ord for U128 {
+    fn cmp(self, other: Self) -> Ordering {
+        let hi_ordering = (self.hi as u64).cmp((other.hi as u64));
+        let lo_ordering = (self.lo as u64).cmp((other.lo as u64));
+        
+        if hi_ordering == Ordering::equal() {
+            lo_ordering
+        } else {
+            hi_ordering
+        }
+    }
+}
+
+impl BitOr for U128 { 
+    fn bitor(self, other: U128) -> U128 {
+        U128 {
+            lo: ((self.lo as u64) | (other.lo as u64)) as Field,
+            hi: ((self.hi as u64) | (other.hi as u64))as Field
+        }
+    }
+}
+
+impl BitAnd for U128 {
+    fn bitand(self, other: U128) -> U128 { 
+        U128 {
+            lo: ((self.lo as u64) & (other.lo as u64)) as Field,
+            hi: ((self.hi as u64) & (other.hi as u64)) as Field
+        }
+    }
+}
+
+impl BitXor for U128 {
+    fn bitxor(self, other: U128) -> U128 { 
+        U128 {
+            lo: ((self.lo as u64) ^ (other.lo as u64)) as Field,
+            hi: ((self.hi as u64) ^ (other.hi as u64)) as Field
+        }
+    }
+}
+
+impl Shl for U128 { 
+    fn shl(self, other: U128) -> U128 { 
+        assert(other < U128::from_u64s_le(128,0), "attempt to shift left with overflow");
+        let exp_bits = other.lo.to_be_bits(7);
+
+        let mut r: Field = 2;
+        let mut y: Field = 1;
+        for i in 1..8 {
+            y = (exp_bits[7-i] as Field) * (r * y) + (1 - exp_bits[7-i] as Field) * y;
+            r *= r;
+        }
+        self.wrapping_mul(U128::from_integer(y))
+    } 
+}
+
+impl Shr for U128 { 
+    fn shr(self, other: U128) -> U128 { 
+        assert(other < U128::from_u64s_le(128,0), "attempt to shift right with overflow");
+        let exp_bits = other.lo.to_be_bits(7);
+
+        let mut r: Field = 2;
+        let mut y: Field = 1;
+        for i in 1..8 {
+            y = (exp_bits[7-i] as Field) * (r * y) + (1 - exp_bits[7-i] as Field) * y;
+            r *= r;
+        }
+        self / U128::from_integer(y)
+    } 
+}
\ No newline at end of file
diff --git a/scripts/nargo_compile_noir_codegen_assert_lt.sh b/scripts/nargo_compile_noir_codegen_assert_lt.sh
deleted file mode 100755
index 858a16cf517..00000000000
--- a/scripts/nargo_compile_noir_codegen_assert_lt.sh
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/bash
-
-cd ./tooling/noir_codegen/test/assert_lt
-nargo compile
\ No newline at end of file
diff --git a/test_programs/compile_success_empty/reexports/Nargo.toml b/test_programs/compile_success_empty/reexports/Nargo.toml
new file mode 100644
index 00000000000..4a87f28fd89
--- /dev/null
+++ b/test_programs/compile_success_empty/reexports/Nargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "reexports"
+type = "bin"
+authors = [""]
+
+[dependencies]
+reexporting_lib = { path = "../../test_libraries/reexporting_lib" }
diff --git a/test_programs/compile_success_empty/reexports/src/main.nr b/test_programs/compile_success_empty/reexports/src/main.nr
new file mode 100644
index 00000000000..bb94b21b221
--- /dev/null
+++ b/test_programs/compile_success_empty/reexports/src/main.nr
@@ -0,0 +1,8 @@
+use dep::reexporting_lib::{FooStruct, MyStruct, lib};
+
+fn main() {
+    let x: FooStruct = MyStruct {
+        inner: 0
+    };
+    assert(lib::is_struct_zero(x));
+}
diff --git a/test_programs/execution_success/blake3/Nargo.toml b/test_programs/execution_success/blake3/Nargo.toml
new file mode 100644
index 00000000000..29f6ad5f11c
--- /dev/null
+++ b/test_programs/execution_success/blake3/Nargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "blake3"
+type = "bin"
+authors = [""]
+compiler_version = ">=0.22.0"
+
+[dependencies]
\ No newline at end of file
diff --git a/test_programs/execution_success/blake3/Prover.toml b/test_programs/execution_success/blake3/Prover.toml
new file mode 100644
index 00000000000..c807701479b
--- /dev/null
+++ b/test_programs/execution_success/blake3/Prover.toml
@@ -0,0 +1,37 @@
+# hello as bytes
+# https://connor4312.github.io/blake3/index.html
+x = [104, 101, 108, 108, 111]
+result = [
+       0xea,
+       0x8f,
+       0x16,
+       0x3d,
+       0xb3,
+       0x86,
+       0x82,
+       0x92,
+       0x5e,
+       0x44,
+       0x91,
+       0xc5,
+       0xe5,
+       0x8d,
+       0x4b,
+       0xb3,
+       0x50,
+       0x6e,
+       0xf8,
+       0xc1,
+       0x4e,
+       0xb7,
+       0x8a,
+       0x86,
+       0xe9,
+       0x08,
+       0xc5,
+       0x62,
+       0x4a,
+       0x67,
+       0x20,
+       0x0f,
+]
diff --git a/test_programs/execution_success/blake3/src/main.nr b/test_programs/execution_success/blake3/src/main.nr
new file mode 100644
index 00000000000..3bfea6c5f95
--- /dev/null
+++ b/test_programs/execution_success/blake3/src/main.nr
@@ -0,0 +1,6 @@
+use dep::std;
+
+fn main(x: [u8; 5], result: [u8; 32]) {
+    let digest = std::hash::blake3(x);
+    assert(digest == result);
+}
diff --git a/test_programs/execution_success/double_verify_proof/Prover.toml b/test_programs/execution_success/double_verify_proof/Prover.toml
index 3e6d996d0e1..dff48212e50 100644
--- a/test_programs/execution_success/double_verify_proof/Prover.toml
+++ b/test_programs/execution_success/double_verify_proof/Prover.toml
@@ -1,12 +1,5 @@
-input_aggregation_object = ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]
-# key_hash = "0x17a5d2b205c1bf45b015ba33bc2f0beb7fbb36682f31f953b8d4d093c8644be5"
-# proof = ["0x0000000000000000000000000000000000000000000000000000000000000003","0x0000000000000000000000000000008f66908323784e7c5259f4eefab77ca881","0x0000000000000000000000000000000000109cac7b943f9b737d7b023d4f5d8a","0x000000000000000000000000000000e991d3ac0a68a252bd3cd09cd1b43fe1b4","0x000000000000000000000000000000000014213d346a426777351fdabaa0fa26","0x000000000000000000000000000000e4603692a76af630015380b08d0e13c239","0x0000000000000000000000000000000000149e7046461203c1b72392bb93c262","0x000000000000000000000000000000c27ffc719f223ca6ef5476a5b66f03a4a8","0x000000000000000000000000000000000003718c62098243e3c1f38090e61753","0x000000000000000000000000000000749492aa98716ce5bf7c06e5c2a0a8a528","0x000000000000000000000000000000000018e4c7d33848bccdc3eed924bfaa15","0x0000000000000000000000000000004e10a37f41fd7c4fe32982daa498530d62","0x00000000000000000000000000000000001b76c8c59489c63f11280187109dd7","0x0000000000000000000000000000002a6cd84d3b8537a7c3cb0cb9461f02e4bb","0x0000000000000000000000000000000000197e524fd48ca5ccb30d6c5ffe689d","0x0000000000000000000000000000000013bf25498ce1f51078c06dac450c0325","0x000000000000000000000000000000000018d347b88a0c32e32571deb9b40466","0x00000000000000000000000000000060d496191298eb1b1c2ce18f9a4afcfc55","0x000000000000000000000000000000000024e11b8e8fcb45b8628cb9cc565513","0x00000000000000000000000000000004e976f6d12fff6250eea2d21c570d3d6a","0x00000000000000000000000000000000000967dbd89d2c7dc0121ea71ded7203","0x000000000000000000000000000000d96f810588c0daa43e88d765a3f82ea9b7","0x00000000000000000000000000000000001f69d7015fe6694bd1d4d61049dae9","0x000000000000000000000000000000c539910d0f81a890fa3d996a676db39640","0x000000000000000000000000000000000026d8b64020a669e24f740b4eba633a","0x000000000000000000000000000000c53cc90f99c40eb5d449f38180d9e9c8b6","0x00000000000000000000000000000000001071ddf2bacc2367dfb2c5084b7dd1","0x0000000000000000000000000000001b9791181eb174db1a50d903fa9fea9999","0x0000000000000000000000000000000000118c059d41a95311a5c361c6a9a00d","0x0000000000000000000000000000003caf4ad04551a3ffba19cc6a1fff457370","0x00000000000000000000000000000000001dc4d8be804c5289fbf54183f93149","0x00000000000000000000000000000050766764bb82799df5172043c515956263","0x00000000000000000000000000000000000a5849adbac9c33e53571b29aab672","0x0000000000000000000000000000002edb078e589d44ac93e283680b34adf574","0x000000000000000000000000000000000015e9e187c4fb683ca78d52a2a0301b","0x00000000000000000000000000000048ac0f1db3575ed0f84d61ab6cbdd53d9f","0x00000000000000000000000000000000002ddc4243fbc7104347d29a823194ae","0x00000000000000000000000000000070ad92aeea2bdea4277ffdfa3d3ed93443","0x000000000000000000000000000000000003bad3e3aae806c278094cb682a8e0","0x000000000000000000000000000000fb74b99eb44c80d8f7ba83d7e9e2efa5c0","0x00000000000000000000000000000000002819cc14e399c1dadc4f921e2a58fa","0x000000000000000000000000000000e3938bb3e7866c6499ec44fb72549efca0","0x00000000000000000000000000000000002d8264d5cdc2109da12e1864aca147","0x000000000000000000000000000000b12d7828cacbe86350f0b171b0cb0d1cd4","0x0000000000000000000000000000000000244155cecb315326f05641cac9475c","0x070b059f9471e22eed5a9ea08093dba3c59c941634611884c5f0f1a1a6b93e5c","0x118124ada70b46c7d23a6ca8b90d545f30e028b1689fe5c55c86bf55f42e0401","0x25dca6ad78c03ce1f7783cc39a5ea5ef90b318d5edf4f1367d4451c1db3c113e","0x0d9557b4e661b5c53b84fcb41f05d15c0ca112430db16f56d0ab54032fffe734","0x06aedf13a3b182784f4d64162f4706759f95e42fc8dc17d1b8b5f551dafdc007","0x132f97ab5f1f8817689b17e336125c5273d6970a1b3b0901fd26d193a4d2dce4","0x1b0c9980b5b09343e807d82bad307a06d1bfadcd1fa50be666c2907d31ef43e1","0x1ce7000cb24ecc1f2ff9d9507b2290513fed574a84d893811cb54a3c0bc51ccc","0x2e1df58d36444c2dfda98991847422f56ef66f079d26eb7f8110d0d7c46b2c0c","0x166c2f821be7c3e3e4440980e73770840194f14d003778b7fbcdd2690776255c","0x1ae8390287e2eb117851a5011575ba3801e5ee5c66a8f7432e2a2fb13c276008","0x047c09806bfb272d940d9b802e3e49b40050fd0f66717e8b325c5d4834b13aac","0x08f81300d7f64e5b281b37005c7c936800a1fa1ecce2fd1664b8ba9069627558","0x2ed7260e623b68d580304751341bb72141314b881e9e8dade626bf5cde8a077c","0x23e04c035fd9396ca06cdc0171f24da00287e87b338bf45992e2ea41a589d560","0x285c5583cbd4609a347a7781a030975402d8e58a99fd72e4c53f4789da3b100c","0x2cd85f0437cf87c7c8881301ce6ee1080329e29a006ef16ff79ba4d20eec4ab8","0x12eb74da267caf98c494db16c87f90f510fdca1f8095b40156a6f0bb066e3400","0x2267004535c434df4cbee1a356e48b1f317cb849ac69c3eb94e377d2274f1e08","0x2c9d4ce9d1d8b8cf1cb90cbc3e121f570c8260c53b48ed2070d474d5a6f12c4e","0x2c6c83ffaad6f30da5aa696973ccfbd0337cb7a5e5f9e5fc8e826dce21e8f51c","0x056c23922e9435f93853044ba96a1c12db97f47053987df5187269ce884ec00f","0x09e82d129a8f5d26cc609fcbd7a865c6dbe8f17fba09fc804acec716bcfffabb","0x0e643693068a8454606e3b4c05e6af7adc39ee8f207b7b0b7d2b245ef1b13567","0x12e040137285ab81f47bd6cc3424f92edc8aeb9e86ecf996af8781a726627013","0x00f01a11c2990ecba44568cb7b2bd25edb46f760ed26ff69e6160c86539d8563","0x28a91699dfa4e85e18e8621d39a147a40930701d2d88546e01adb71a1f8e407f","0x000000000000000000000000000000009d7cc0b2d2bdef816f4fb17e7a6f6c08","0x00000000000000000000000000000000bcfc1a7030171f681f2c6e97c61f4e70","0x00000000000000000000000000000000dc7b742d8d704f4ecf092bb111cf30d8","0x13b099dc4869006fde9df04bf36f4c8f08d4491cc6229ac36a98f93214c79b6a","0x008fa95e0d431d617d8d3288fde7f8bbe36492019943e2018564633528575892","0x0fc66c06bdff20dba4dc491d5cd13cc209c4d2d9e29802db665bb397c2a4e754","0x0fe48ae6623efbaadce6d6b75b87be6caa19c2fd4d94a74149ceb6bcb88251e1","0x1bb41738028855cb5e0085edcd62cff208121427ea19a57425a0cf6bb68deb93","0x0fbc646333ddc21ab1a77b01a35973a56d5a617c482a21a231497fd3cc9b74c1","0x19ab9eaa1a902faff2dd9baa19ff00cea9086baa8c28bcdb95f7a3549eaf09b4","0x25e2b7a7643df4d964cd787b593888b00abfe3ce79e8deaa6d68fd1686b84bcb","0x2d134d7eea07414451e54854d61d5b71245434d0638bba9a1184914f65f2521c","0x03df94e38e9eed8586acd277d180d5d515b49d89d37525f871be2ff4552c586c","0x0b102abb146839f073c4a2514e65a8962f48ee8bbd1801e815d9c42d34665ebd","0x000000000000000000000000000000b7a4109cb92b514401fb63667454a9c892","0x0000000000000000000000000000000000016fce7f8ef56fef466636f3fbc3de","0x00000000000000000000000000000005f2d1c401a7aa14ac7e9fce7c21ec2e1a","0x00000000000000000000000000000000000621322c74c5d0da5eb71a4f2b046f","0x00000000000000000000000000000073d774ad7f61b1c1b93800f7838cca6bde","0x00000000000000000000000000000000002d603cc025e6af192394df113d4677","0x00000000000000000000000000000066a2a702b4d4b1a24af9c56cacb18ae4b8","0x00000000000000000000000000000000000124a3c25b427cfb6fca07525c5b8d"]
-# public_inputs = ["0x0000000000000000000000000000000000000000000000000000000000000003"]
-# verification_key = ["0x2b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e80","0x0000000000000000000000000000000000000000000000000000000000000008","0x0000000000000000000000000000000000000000000000000000000000000005","0x0000000000000000000000000000000000000000000000000000000000000008","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x00000000000000000000000000000092139c61bae1a44f0fc7689507414be688","0x00000000000000000000000000000000000160ce4e279582f91bde4f03f5e9a2","0x0000000000000000000000000000005dc2d37f658c3b2d60f24740eb13b65d79","0x000000000000000000000000000000000007e3e8a5d98a1177ec85bf88f163a5","0x000000000000000000000000000000dc3035fbd7ff16412a8fd7da587a935298","0x000000000000000000000000000000000023d08e2817ac16990004ed11d8fc66","0x000000000000000000000000000000356a5ad59c646c746a8d09f5d154e47c4f","0x00000000000000000000000000000000000708529196af3c8e16ffa580c26182","0x0000000000000000000000000000002ddfe70eb7a1280596e8e4a804f118a6dd","0x000000000000000000000000000000000013757e15a0905f298303784a161b21","0x000000000000000000000000000000a23a729df796935c7824e3a26be794829b","0x000000000000000000000000000000000005775b6c146c4a59856e869fe5a70e","0x000000000000000000000000000000eef0c9e088fd2d45aa40311082d1f2809b","0x00000000000000000000000000000000001d539ccbfc556d0ad59307a218de65","0x000000000000000000000000000000a2c848beceb6ab7806fd3b88037b8410fc","0x0000000000000000000000000000000000177004deeb1f9d401fd7b1af1a5ac8","0x0000000000000000000000000000002508eb63672a733f20de1a97644be4f540","0x00000000000000000000000000000000000d82d51f2f75d806285fd248c819b8","0x000000000000000000000000000000d002f9100cbba8a29f13b11513c53c59d0","0x000000000000000000000000000000000006cd3b0e3460533b9e5ea2cdc0fcbb","0x000000000000000000000000000000f45ea38a93b2f810c5633ddb54927c1c96","0x000000000000000000000000000000000021791de65f9a28ec7024b1a87ab4f3","0x000000000000000000000000000000926511a0439502c86885a8c6f0327aa7ad","0x000000000000000000000000000000000029fa14a969c5d81ed3abbbfb11220a","0x000000000000000000000000000000b84c3258e8206f560e5b5b18cbeafef87e","0x00000000000000000000000000000000002a910445cd8fc895e5d235cd8ea185","0x000000000000000000000000000000887e67f15e84bcb8507a5064a363f6043b","0x000000000000000000000000000000000014dc6643d801c3ef27c2066b6e2bb4","0x000000000000000000000000000000e38e900b42c314ba803088e8fbf125203f","0x000000000000000000000000000000000020690fd4869db418306046b38161dc","0x0000000000000000000000000000001e2fa856bf7951b8292b1e88185993629c","0x0000000000000000000000000000000000048a85e0bbac7c60ad3d78f601f63c","0x0000000000000000000000000000006f457719495073d3666d77a625aeab0c51","0x00000000000000000000000000000000002623ad892dc62b1fa7d0a650f0d470","0x000000000000000000000000000000dbfcc8a467e021c03b13f74a9f79c3a10c","0x0000000000000000000000000000000000295f6f10976c37bd9c6f96bb7187d5","0x000000000000000000000000000000c13ef9a937cc12420fb38d9ab8e848e85e","0x000000000000000000000000000000000003560a3b334e887532f605c9cb7628","0x0000000000000000000000000000009bcebf08a4599cdda0fb96312d4dc0c7a9","0x000000000000000000000000000000000015adc8bb1e01c835f48959d1237bd6","0x00000000000000000000000000000047762ab839e4ff63c77605a9f383da37c2","0x000000000000000000000000000000000016a8c3c53d89660cf271522cd301fb","0x000000000000000000000000000000f0c8539a0b5f94420a513f9c305b932bfe","0x00000000000000000000000000000000002957ba01d9de5638f808f88a692533","0x000000000000000000000000000000ab17c6189d67d3bf5dd2f3885de0151b6f","0x0000000000000000000000000000000000060d8aa43fdc434d1942263f364d95","0x0000000000000000000000000000005d292333b3adb497f00b4bc32d45229060","0x00000000000000000000000000000000001a1018a66221883639f2898a66f345","0x00000000000000000000000000000006555a806b1993291deba0dc44e2abf431","0x00000000000000000000000000000000000cacff7099a9d5e35a21f4a00b2dc3","0x000000000000000000000000000000f50c11ba95d349c36d143eefd12e494950","0x00000000000000000000000000000000001022e8c5f02d639bc9dd8bc4407f99","0x000000000000000000000000000000c76828795098eda73d50b4b585c60afc60","0x00000000000000000000000000000000002bf09c0ec7011e93888962f2406630","0x00000000000000000000000000000049e5c83a8978d832fb8e144548e3ca1adb","0x00000000000000000000000000000000000e0ec242c2e160a984f61ca5adf5f5","0x0000000000000000000000000000009c5d6e08a6605ab4513748ac0fa017dd1c","0x00000000000000000000000000000000001f54baa07558e5fb055bd9ba49c067","0x0000000000000000000000000000001e1ee7ee29bbb5e4b080c6091c1433ce62","0x000000000000000000000000000000000024aec62a9d9763499267dc98c33428","0x0000000000000000000000000000001a96755946ff16f0d6632365f0eb0ab4d4","0x000000000000000000000000000000000028cf3e22bcd53782ebc3e0490e27e5","0x00000000000000000000000000000043148d7d8c9ba43f2133fab4201435a364","0x0000000000000000000000000000000000234ce541f1f5117dd404cfaf01a229","0x000000000000000000000000000000a7fb95ffb461d9514a1070e2d2403982ef","0x00000000000000000000000000000000003016955028b6390f446c3fd0c5b424","0x00000000000000000000000000000008863c3b7cd7cddc20ba79ce915051c56e","0x000000000000000000000000000000000013ef666111b0be56a235983d397d2a","0x000000000000000000000000000000e3993f465fc9f56e93ac769e597b752c1c","0x0000000000000000000000000000000000217f7c4235161e9a3c16c45b6ca499","0x0000000000000000000000000000008ffa4cd96bc67b0b7df5678271e1114075","0x0000000000000000000000000000000000256467bfcb63d9fdcb5dde397757ad","0x00000000000000000000000000000054e5eb270bb64bde6e6ececadfd8c3236c","0x00000000000000000000000000000000000e52d1bd75812c33c6f3d79ee4b94c","0x000000000000000000000000000000484a2c641dce55bc2dd64ef0cd790a7fea","0x00000000000000000000000000000000000ff417d256be43e73c8b1aa85bdda3","0x0000000000000000000000000000000b72e7b7713ab5da44e0f864182e748a23","0x00000000000000000000000000000000001a221055f1625ad833a44705f5f74e","0x00000000000000000000000000000067a99a34e9b81a17ad001db02e29bcb82a","0x000000000000000000000000000000000018a6c02e398389827568fa960e86e2","0x000000000000000000000000000000bb29f26f9890d6cc6401f4921d5884edca","0x00000000000000000000000000000000000868357b28039385c5a5058b6d358e","0x00000000000000000000000000000036fb6e229dde8edf7ec858b12d7e8be485","0x00000000000000000000000000000000001060afe929554ca473103f5e68193c","0x00000000000000000000000000000015226e07e207744c0857074dcab883af4a","0x00000000000000000000000000000000000b1c02619282755533457230b19b4a","0x0000000000000000000000000000001f2a0277e4807e6e1cbabca21dde5eb5e1","0x00000000000000000000000000000000000d928deafed363659688ed4ccdef52","0x000000000000000000000000000000363f0c994e91cecad25835338edee2294f","0x00000000000000000000000000000000002eea648c8732596b1314fe2a4d2f05","0x000000000000000000000000000000b2671d2ae51d31c1210433c3972bb64578","0x00000000000000000000000000000000000ab49886c2b94bd0bd3f6ed1dbbe2c"]
-# proof_b = ["0x0000000000000000000000000000000000000000000000000000000000000003","0x0000000000000000000000000000002ab91b132e624f2a408aa8c9bf31cca8d7","0x000000000000000000000000000000000015ad57528e0f065c820cc5ad4eab81","0x0000000000000000000000000000001acb78b1b6a5c9a6ec8bf2272b463014da","0x0000000000000000000000000000000000117fd65346e04bf3666d2ab3f24c90","0x000000000000000000000000000000aad0adaf9a768ba6a178f804edac5c8943","0x000000000000000000000000000000000004a11c7d31f25c20e3af16f9b01f71","0x0000000000000000000000000000001f0ae9bb921893ce2710148eb1fcd99e39","0x0000000000000000000000000000000000123fda5008d3709f5afeda01de1930","0x000000000000000000000000000000971c2a8d0119097fd82b7a8074a14853f8","0x000000000000000000000000000000000009965b998750710678da7891d8aba6","0x0000000000000000000000000000002d6ef3813ba14a5f5202afed6b1c41de1c","0x000000000000000000000000000000000020366bfdb2f9279c43d66f90dfdf4d","0x00000000000000000000000000000041389f221eadec33e1b87518668c3bc92e","0x00000000000000000000000000000000000d3858169bb0432ab761d4be8ef03e","0x000000000000000000000000000000c1dbfe670dc912cb0fa1a0f633f81a4cef","0x00000000000000000000000000000000000fc0c403e668b0f51e07089082c32f","0x0000000000000000000000000000009a4fba9bf1369f637fd295c8bf795c9d02","0x00000000000000000000000000000000001d6d1e7286ce52401e6ea79d2cfa3d","0x0000000000000000000000000000004762bf7702ffe7a2c147e704280cd50bba","0x0000000000000000000000000000000000205797cdeaeff9a8d5ea4b95d41b1a","0x000000000000000000000000000000b3d43cc863ba8d98f51118c0db70761079","0x00000000000000000000000000000000002d2a3d10381bc6b47a693c1692b1b6","0x000000000000000000000000000000d35a69fb0e68729f71e651799c0d19e9eb","0x00000000000000000000000000000000002ade1dc7741b7f397271c10e596557","0x0000000000000000000000000000001a67b44714687085004e4142f700043298","0x00000000000000000000000000000000001bb7bbb7f45876b1d72e5d20cee106","0x00000000000000000000000000000025f1f1cbf43fad70cba255b37a19e88b0c","0x00000000000000000000000000000000000cc46b215fbd8e4b233cc74aab250b","0x0000000000000000000000000000008168026f51135fc1670664bc50e629917f","0x000000000000000000000000000000000004d822d80ba0c1bcbd4b000573c6f9","0x000000000000000000000000000000d85756249b937277eba3f5dcb89c56e7bb","0x000000000000000000000000000000000019a3a7a5b20dac138d7ddb1d499134","0x0000000000000000000000000000007621614c7ebc31a2177011f9da01668eb3","0x000000000000000000000000000000000024e9beb5d616ab120073170fc431e8","0x00000000000000000000000000000031fbf901896e958fbbed3e5c57aebbdd04","0x0000000000000000000000000000000000005ac0f10fcc255e179a40518875d4","0x0000000000000000000000000000002dab820c019bcca563b7dbdd26974653e9","0x00000000000000000000000000000000001a5655ec1a67f722b14c65d5c2197f","0x0000000000000000000000000000008e277e490196db5c19d09a9034e10c6432","0x000000000000000000000000000000000003f13b1af07db07eec88698d0aaf2a","0x0000000000000000000000000000002d618452e2b4c790d0551ea5863ed62e76","0x00000000000000000000000000000000001a7171e790a433a972d80218fb482d","0x0000000000000000000000000000005669975cd5bf65a739c0a35a8ab9b7963b","0x00000000000000000000000000000000000d27ffb6f00c86a0ce76a8067d1bce","0x03a0054fe9f93ab96e7c7ed6ec1ac641dffd99a1c804ee5db52cf1efa1a12c15","0x059324381c89c12c87d0f6c27963c31647721fdb02c125961da1a21cbfb3ed1c","0x04a5ead891b7c3f30329e6abcf2ac6903c3c1d8e68874f6baf3a6fc00e84533a","0x03c02f6b862734acf9d0c5133f8141b3a008c5499336a588b376a5dd86d9c837","0x1dd26b35c21c584c410df89d1fd549e7f5da9bb4fd290b7c528d92fbd652f5ad","0x2c8e7ef6f7a130769ae74d0f47aeab5c443492ef4b1ed0b3a9d61dfca80cbdda","0x2b074486c21c62e6eccf3191b3ab3c8df0fb98f0c44b9f0e9e2c281b908b83a6","0x149a6d620be135bba6bbfe8ac826df37567c8be78007e47cdcf5d6e4683d339e","0x119fdfd330036bde31af71e43bd5e191460605e4760d08a6e0ebddbdb5abfeeb","0x1713efc63c00b2de4f68e696d9d30c5603963484f4829e716de2796640864b09","0x1bb1862114cda3712c177b1b6bca0ecd9de7723925698aee83dc91ade7078d3e","0x049d965ad8ccf092dcae948491f702779a513db430e6ec7d15fa1847a6814235","0x093b2cb5b199e125b95d290923ee04ef34a27b6861cdd8fa2bf4308f4d02846a","0x2710c6cd6f14f8071976509d1002e915bfc182b843a3967134de380302423c72","0x24ecb2d6c6678496e574a4248fb813bcd289eda1873763e8afd0c23d340a11a8","0x298a49319f347529c22338a921af16346cdb2b55b81e5065c5cada84da8b53dd","0x2e27df8c780165bc9ed1cd2db3a618ac072c6909e9053ce2dbc4f2cc810c9612","0x07350f3a2e23c1ccbde0d39370159060de5b8df40ae7c58d3f9852b371f1adac","0x2fdf8bf8e2fa2acad0f6d6a3f73e7dc516e8e2d167128bf3a560972339122835","0x0d3ec457703c228d4b6cd1635df9d9bde51997d0228edef64d667cbd16f3cb70","0x148320b9ceab1f3be840dc38b0344e7db0755283d1eacf2dd472e99ee0fb473f","0x06febdcf4869a6b89fdeb0805612c53e139afc29d119a54bc3d72dc7de0f1a7b","0x0b9c542a2136974b7c8d4504e809c7b5adec39de020091c8d9d1460f84905cb0","0x1039ea84fa0387de593bd9897a00ca2d483d779232e77e45efcb5e572b119ee5","0x14d780dfd2d0787135ea6e0e0bf7cca4e28eb54663ce6ac305c5769ed192e11a","0x026127746f9cb625c3301bfbc41bc2c67949be75a032b8ceaddd1580378dd846","0x123cf1180af5fdf09444de423947c9a71790f2c85468b51ecc25fb7bf075a0d5","0x000000000000000000000000000000008419a4f769ceb72c3ac28f559331a5df","0x000000000000000000000000000000009e852c5c1891a89b79b64599e3d52d72","0x00000000000000000000000000000000b8f0b3c0c7549a0ab8a9fbde3478b505","0x056af493dda97ae84cdbbf9ce379e35bdd66e1223eebacdc4a6c2c92553604f4","0x023624c49a722bc8dc5d945b4b10be8ed6c608020e65038a470b5a407375c8aa","0x0ed9f8dd445bda548ef08b7a2ff233867c41b72786f98054597833a68cc9b900","0x2cbf3d04669aa3a0dcda95e19da879f36029abe28317f1ee69be28ddef2a0b87","0x284ca7049611e293aa4535dd7841a540996609d541814373b387b00069636f14","0x246a69ce4030b1e8a675eec89960bfe188bd4073f07afe74f7a77c0698c80bc5","0x1bbdab5d007c4743fbcbf3cc89252baf0b0e1b645b977434ccd4e7560d124761","0x210427e70ee1b484bbb0b4e98263faf24a45325236eed618d51dcb1cb3a9f60d","0x1fbc24b0bd5b0b8c514e138317cc332962714dd306b34939768d723d6ea2ca8e","0x1e74217a6bd46293e6eb721cad346b607a9d6953d677bc5a17fd195e299b9f0f","0x1d2c1e441a4db99b7c88d0b6429ca39792c984d4a63c2f7ab96cc07ee4947390","0x00000000000000000000000000000005b1e3524625c466540f3f7468172403cb","0x000000000000000000000000000000000013bb985f9d5562699347b5dfbc441e","0x000000000000000000000000000000f4fb87d7f481bb198aa6237a0c9ffd3c22","0x0000000000000000000000000000000000254c5f1b76e278f4c71cf5e71533dd","0x0000000000000000000000000000005a72a28b51be9c538b4d28b5106b9239b8","0x00000000000000000000000000000000000d02d80e1a73c82cb0dd8af1aabb3f","0x000000000000000000000000000000434c46502fc1c425a72a4717a3e44c3415","0x00000000000000000000000000000000001c8d74d967b9b65ff2772592a15d0e"]
-
 key_hash = "0x096129b1c6e108252fc5c829c4cc9b7e8f0d1fd9f29c2532b563d6396645e08f"
-proof = ["0x0000000000000000000000000000000000000000000000000000000000000003","0x000000000000000000000000000000d62b795bec274279129a71195796825fcc","0x00000000000000000000000000000000000793ab763140f20a68a6bd2721fd74","0x00000000000000000000000000000053141d06d3307b36153f321511199e579c","0x00000000000000000000000000000000000a4b55d6c21f98a9c434911dcb5c67","0x0000000000000000000000000000005f9d324c0abd22cec92d99dbec438e9491","0x0000000000000000000000000000000000240dfafe1b53dc27147cbab14ea893","0x000000000000000000000000000000044a61d3aac32c6931247cf334a19d9611","0x000000000000000000000000000000000003f0f8cf4207bfa85c23ec9f8d0c88","0x00000000000000000000000000000002168a470e39ba2ac266f6b474de12045f","0x000000000000000000000000000000000025791e7d3feab542345c00ec5a30df","0x000000000000000000000000000000dcafd76d4c3640969c80e017b951ef6397","0x00000000000000000000000000000000001d27f75a1256771e88e0c86fc42dbc","0x0000000000000000000000000000007347ae7d2d9d7fc2b8f0baa014ee1fed9f","0x000000000000000000000000000000000018bd927f42bf7caf9555f56f09000d","0x000000000000000000000000000000041f765f83cbe5904c8f453f70a4531d10","0x00000000000000000000000000000000001858aabeeb5331a221419f4fed1c19","0x000000000000000000000000000000d254a54caaedf8287b9af951b2f2611121","0x000000000000000000000000000000000005ab493623c9563cf2e55ba5f18200","0x00000000000000000000000000000014f24cddc1a02440dc63637df8032c8074","0x000000000000000000000000000000000011950c16cef98471b1d78b935195a4","0x000000000000000000000000000000b0340b459e6bd5cc8f031c8654a502897f","0x00000000000000000000000000000000000e1cf3968dac4545a76a2ae58e512c","0x0000000000000000000000000000002adf7218aa06ca0d2c2e600dcc39193a2d","0x00000000000000000000000000000000001302e7e4b0f14749bd885ca25588b6","0x00000000000000000000000000000092009ce4056e79ab815d8cdfd4491138ae","0x000000000000000000000000000000000018af11e853c6cf2f0f6274b0da8133","0x000000000000000000000000000000dd3dc6f49232141718527b3a0e4b26e21d","0x00000000000000000000000000000000001a877853348a8b695c4f9a9aa4ce68","0x000000000000000000000000000000aecfc56ba07155450b368140d6324023b5","0x000000000000000000000000000000000029c11052798c57ece614617d33fcc2","0x000000000000000000000000000000eb106ffc816d16fb84e84b0b61157b2603","0x000000000000000000000000000000000026c3cac16206899a21cb5126841446","0x000000000000000000000000000000a782ed54805fe845068b362b58e2fa34ec","0x00000000000000000000000000000000000cf046a1bfcc666b7f28b572676073","0x000000000000000000000000000000b931c8dda60bb4aca4cc817f5540f1209f","0x000000000000000000000000000000000024ad50c3936fafc3d190e6a4874223","0x000000000000000000000000000000cce90cfbaf5671c8c8652db28a3a9566f7","0x000000000000000000000000000000000003574db9d0f84380c9635660f86354","0x0000000000000000000000000000003eb3e1dc31846a90f721e7a08c6d6dc4f7","0x000000000000000000000000000000000028999a700cd1abae1a288eebb9a91c","0x000000000000000000000000000000c1be4d385b11387e14eb9817050d772f78","0x000000000000000000000000000000000003c56b5bad8b4484c66ac921f1f102","0x000000000000000000000000000000ace245cabf0f00dc7fd253dd8af0377a14","0x0000000000000000000000000000000000107f1731fcf34b364c813599fa1df7","0x035b937d404932b542b706eb810ef4a7dca4566d4dde1ad6a8717f46167ead7e","0x17608cef3dc7960f41cb1295706df663727d45ee598a61e05e989d111449fb65","0x054712a950ad67da3aa860e49e6891f99b586b7f37caff94eb013fdb374b61ee","0x04b755083086c769b7f593e0e48d68dc54be808203351380ca5566a48149d8bb","0x17d7670b0915235f626fdc1d7e1134d2be906ef138d7843384b3ebc23b1d630f","0x064cf544ab5f4e3dab47960502cccc83321fb275068dfbdd3a2fcbc6dddcaa65","0x083338262712e2b66769ea40d9f412b18caa1bc81a51ff5a50b6c41f8c4b3d23","0x0cdd38958cab97defde00f4a5961b6fd676e29d9f2c352f6bb2c68b91f83f8af","0x02c8bdd005c2f43a0a8cbb2744916ce5c322dfa5b23367a829c12699f4036d32","0x25bac73c7e7b659fbea3135b7a0decf9db8dc3045bd2837dae337c64cc722546","0x19eb361aa419d37bce3d2e8b2b7692a02a9559e83d7f3d8fe9169970fbbc2cba","0x2494bd5106d00e05c7ea60e632e9fe03773b7f2c5b662aa37ec512a01f4a0775","0x18c52c2f2c6e7be1d7847c15e452a3a9c64316103d12e4b5b9a82fac4e940ee9","0x0e0342810456ef78f498c1bfa085a5f3cbc06db1f32fabd0ea9ad27dccac1680","0x024c13d6ef56af33ed7164ea8e47ddecc8a487b000d8b1b45edcd3895a503ba2","0x26e0d127f626bd39b55bc5d0c131dbf03fe006dc5c3edc57dda1e629799a4317","0x1b1140061bc52b15c4f5e100729a81968ee79dc03deb966a18850335a8e44a8b","0x1bb76f945199e71d531a89288912087a02dd0e83020e65d671485bf2e5e86e1a","0x29269900859c6d86e404185b415bf3b279cd100f38cfdb0077e8d6a299c4fd35","0x22b5e94bae2f6f0cdb424a3b12c4bf82cec3fb228e012c1974ed457827bbe012","0x18d3543a93249778e7a57936170dae85ffc47c2567f2d0076a32c0bb86fcf10a","0x03721dc2670206cde42a175fd56bcce32cf6cb8801450a8e8e4b3d4e07785973","0x2806db136dd214d3ac1478460855cae6a4324ab45cab35320d104fee26c260e8","0x1c3749f1937082afbbae9375b9be708cf339e1983e57ef4447f36cfa560c685c","0x1067b8cfb90ef08bcb48aea56b2716334241787c2004a95682d68a0685566fd0","0x0f41aee4416398f1d48ffc302403273cddef34a41f98507c53682041d82e51ff","0x10d854c9f0bfbdff7ca91a68f4978e9a79e7b14243d92f465f17bdf88d9f64f8","0x00000000000000000000000000000000018938b11099e0cdc05ddab84a153a97","0x0000000000000000000000000000000001d7dda1471f0dc3b3a3d3438c197982","0x00000000000000000000000000000000022682917da43ab9a6e9cbcece1db86d","0x2453913e6b0f36eab883ac4b0e0604d56aaeb9c55e641135173e63c342f1a660","0x05216c1b58dc43a49d01aaba3113b0e86be450fc17d28016e648e7162a1b67fb","0x152b34845a0222a2b41354c0d395a250d8363dc18748647d85acd89d6934ec56","0x1dfc6e971ce82b7dcda1f7f282713c6e22a8c79258a61209bda69719806da544","0x2968dd8b3af8e3953f1fbbd72f4c49b8270597bb27d4037adc157ac6083bee60","0x1b9425b88a4c7d39b3d75afe66917a9aa1d2055724392bc01fb918d84ff1410e","0x04ab571f236d8e750904dc307dd274003d9130f1a7110e4c1521cfb408877c73","0x2ad84f26fdc5831545272d02b806bb0e6dae44e71f73552c4eb9ff06030748c7","0x020e632b99d325db774b8630fb50b9a4e74d35b7f27d9fc02c65087ee747e42c","0x09a8c5a3171268cb61c02515c01c109889200ed13f415ae54df2078bbb887f92","0x1143281a9451abbb4c34c3fa84e7678c2af2e7ea8c05160a6f7f06988fc91af8","0x000000000000000000000000000000cbda736ca5cf6bc75413c2cc9e28ab0a68","0x00000000000000000000000000000000001ee78c9cc56aa5991062ae2e338587","0x000000000000000000000000000000bc9bfcdebb486f4cb314e681d2cc5f8df6","0x00000000000000000000000000000000000ad538431d04771bca7f633cb659ff","0x000000000000000000000000000000d45b317afcefa466a59bba9e171f1af70c","0x0000000000000000000000000000000000133c50180ea17932e4881124e7a7c6","0x000000000000000000000000000000fc9ed37f543775849f3e84eaa06f77f992","0x00000000000000000000000000000000001372873c9c051d1baff99248b8f70e"]
+proof = ["0x000000000000000000000000000000d62b795bec274279129a71195796825fcc","0x00000000000000000000000000000000000793ab763140f20a68a6bd2721fd74","0x00000000000000000000000000000053141d06d3307b36153f321511199e579c","0x00000000000000000000000000000000000a4b55d6c21f98a9c434911dcb5c67","0x0000000000000000000000000000005f9d324c0abd22cec92d99dbec438e9491","0x0000000000000000000000000000000000240dfafe1b53dc27147cbab14ea893","0x000000000000000000000000000000044a61d3aac32c6931247cf334a19d9611","0x000000000000000000000000000000000003f0f8cf4207bfa85c23ec9f8d0c88","0x00000000000000000000000000000002168a470e39ba2ac266f6b474de12045f","0x000000000000000000000000000000000025791e7d3feab542345c00ec5a30df","0x000000000000000000000000000000dcafd76d4c3640969c80e017b951ef6397","0x00000000000000000000000000000000001d27f75a1256771e88e0c86fc42dbc","0x0000000000000000000000000000007347ae7d2d9d7fc2b8f0baa014ee1fed9f","0x000000000000000000000000000000000018bd927f42bf7caf9555f56f09000d","0x000000000000000000000000000000041f765f83cbe5904c8f453f70a4531d10","0x00000000000000000000000000000000001858aabeeb5331a221419f4fed1c19","0x000000000000000000000000000000d254a54caaedf8287b9af951b2f2611121","0x000000000000000000000000000000000005ab493623c9563cf2e55ba5f18200","0x00000000000000000000000000000014f24cddc1a02440dc63637df8032c8074","0x000000000000000000000000000000000011950c16cef98471b1d78b935195a4","0x000000000000000000000000000000b0340b459e6bd5cc8f031c8654a502897f","0x00000000000000000000000000000000000e1cf3968dac4545a76a2ae58e512c","0x0000000000000000000000000000002adf7218aa06ca0d2c2e600dcc39193a2d","0x00000000000000000000000000000000001302e7e4b0f14749bd885ca25588b6","0x00000000000000000000000000000092009ce4056e79ab815d8cdfd4491138ae","0x000000000000000000000000000000000018af11e853c6cf2f0f6274b0da8133","0x000000000000000000000000000000dd3dc6f49232141718527b3a0e4b26e21d","0x00000000000000000000000000000000001a877853348a8b695c4f9a9aa4ce68","0x000000000000000000000000000000aecfc56ba07155450b368140d6324023b5","0x000000000000000000000000000000000029c11052798c57ece614617d33fcc2","0x000000000000000000000000000000eb106ffc816d16fb84e84b0b61157b2603","0x000000000000000000000000000000000026c3cac16206899a21cb5126841446","0x000000000000000000000000000000a782ed54805fe845068b362b58e2fa34ec","0x00000000000000000000000000000000000cf046a1bfcc666b7f28b572676073","0x000000000000000000000000000000b931c8dda60bb4aca4cc817f5540f1209f","0x000000000000000000000000000000000024ad50c3936fafc3d190e6a4874223","0x000000000000000000000000000000cce90cfbaf5671c8c8652db28a3a9566f7","0x000000000000000000000000000000000003574db9d0f84380c9635660f86354","0x0000000000000000000000000000003eb3e1dc31846a90f721e7a08c6d6dc4f7","0x000000000000000000000000000000000028999a700cd1abae1a288eebb9a91c","0x000000000000000000000000000000c1be4d385b11387e14eb9817050d772f78","0x000000000000000000000000000000000003c56b5bad8b4484c66ac921f1f102","0x000000000000000000000000000000ace245cabf0f00dc7fd253dd8af0377a14","0x0000000000000000000000000000000000107f1731fcf34b364c813599fa1df7","0x035b937d404932b542b706eb810ef4a7dca4566d4dde1ad6a8717f46167ead7e","0x17608cef3dc7960f41cb1295706df663727d45ee598a61e05e989d111449fb65","0x054712a950ad67da3aa860e49e6891f99b586b7f37caff94eb013fdb374b61ee","0x04b755083086c769b7f593e0e48d68dc54be808203351380ca5566a48149d8bb","0x17d7670b0915235f626fdc1d7e1134d2be906ef138d7843384b3ebc23b1d630f","0x064cf544ab5f4e3dab47960502cccc83321fb275068dfbdd3a2fcbc6dddcaa65","0x083338262712e2b66769ea40d9f412b18caa1bc81a51ff5a50b6c41f8c4b3d23","0x0cdd38958cab97defde00f4a5961b6fd676e29d9f2c352f6bb2c68b91f83f8af","0x02c8bdd005c2f43a0a8cbb2744916ce5c322dfa5b23367a829c12699f4036d32","0x25bac73c7e7b659fbea3135b7a0decf9db8dc3045bd2837dae337c64cc722546","0x19eb361aa419d37bce3d2e8b2b7692a02a9559e83d7f3d8fe9169970fbbc2cba","0x2494bd5106d00e05c7ea60e632e9fe03773b7f2c5b662aa37ec512a01f4a0775","0x18c52c2f2c6e7be1d7847c15e452a3a9c64316103d12e4b5b9a82fac4e940ee9","0x0e0342810456ef78f498c1bfa085a5f3cbc06db1f32fabd0ea9ad27dccac1680","0x024c13d6ef56af33ed7164ea8e47ddecc8a487b000d8b1b45edcd3895a503ba2","0x26e0d127f626bd39b55bc5d0c131dbf03fe006dc5c3edc57dda1e629799a4317","0x1b1140061bc52b15c4f5e100729a81968ee79dc03deb966a18850335a8e44a8b","0x1bb76f945199e71d531a89288912087a02dd0e83020e65d671485bf2e5e86e1a","0x29269900859c6d86e404185b415bf3b279cd100f38cfdb0077e8d6a299c4fd35","0x22b5e94bae2f6f0cdb424a3b12c4bf82cec3fb228e012c1974ed457827bbe012","0x18d3543a93249778e7a57936170dae85ffc47c2567f2d0076a32c0bb86fcf10a","0x03721dc2670206cde42a175fd56bcce32cf6cb8801450a8e8e4b3d4e07785973","0x2806db136dd214d3ac1478460855cae6a4324ab45cab35320d104fee26c260e8","0x1c3749f1937082afbbae9375b9be708cf339e1983e57ef4447f36cfa560c685c","0x1067b8cfb90ef08bcb48aea56b2716334241787c2004a95682d68a0685566fd0","0x0f41aee4416398f1d48ffc302403273cddef34a41f98507c53682041d82e51ff","0x10d854c9f0bfbdff7ca91a68f4978e9a79e7b14243d92f465f17bdf88d9f64f8","0x00000000000000000000000000000000018938b11099e0cdc05ddab84a153a97","0x0000000000000000000000000000000001d7dda1471f0dc3b3a3d3438c197982","0x00000000000000000000000000000000022682917da43ab9a6e9cbcece1db86d","0x2453913e6b0f36eab883ac4b0e0604d56aaeb9c55e641135173e63c342f1a660","0x05216c1b58dc43a49d01aaba3113b0e86be450fc17d28016e648e7162a1b67fb","0x152b34845a0222a2b41354c0d395a250d8363dc18748647d85acd89d6934ec56","0x1dfc6e971ce82b7dcda1f7f282713c6e22a8c79258a61209bda69719806da544","0x2968dd8b3af8e3953f1fbbd72f4c49b8270597bb27d4037adc157ac6083bee60","0x1b9425b88a4c7d39b3d75afe66917a9aa1d2055724392bc01fb918d84ff1410e","0x04ab571f236d8e750904dc307dd274003d9130f1a7110e4c1521cfb408877c73","0x2ad84f26fdc5831545272d02b806bb0e6dae44e71f73552c4eb9ff06030748c7","0x020e632b99d325db774b8630fb50b9a4e74d35b7f27d9fc02c65087ee747e42c","0x09a8c5a3171268cb61c02515c01c109889200ed13f415ae54df2078bbb887f92","0x1143281a9451abbb4c34c3fa84e7678c2af2e7ea8c05160a6f7f06988fc91af8","0x000000000000000000000000000000cbda736ca5cf6bc75413c2cc9e28ab0a68","0x00000000000000000000000000000000001ee78c9cc56aa5991062ae2e338587","0x000000000000000000000000000000bc9bfcdebb486f4cb314e681d2cc5f8df6","0x00000000000000000000000000000000000ad538431d04771bca7f633cb659ff","0x000000000000000000000000000000d45b317afcefa466a59bba9e171f1af70c","0x0000000000000000000000000000000000133c50180ea17932e4881124e7a7c6","0x000000000000000000000000000000fc9ed37f543775849f3e84eaa06f77f992","0x00000000000000000000000000000000001372873c9c051d1baff99248b8f70e"]
 public_inputs = ["0x0000000000000000000000000000000000000000000000000000000000000003"]
 verification_key = ["0x2b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e80","0x0000000000000000000000000000000000000000000000000000000000000008","0x0000000000000000000000000000000000000000000000000000000000000005","0x0000000000000000000000000000000000000000000000000000000000000008","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x00000000000000000000000000000092139c61bae1a44f0fc7689507414be688","0x00000000000000000000000000000000000160ce4e279582f91bde4f03f5e9a2","0x0000000000000000000000000000005dc2d37f658c3b2d60f24740eb13b65d79","0x000000000000000000000000000000000007e3e8a5d98a1177ec85bf88f163a5","0x000000000000000000000000000000dc3035fbd7ff16412a8fd7da587a935298","0x000000000000000000000000000000000023d08e2817ac16990004ed11d8fc66","0x000000000000000000000000000000356a5ad59c646c746a8d09f5d154e47c4f","0x00000000000000000000000000000000000708529196af3c8e16ffa580c26182","0x0000000000000000000000000000002ddfe70eb7a1280596e8e4a804f118a6dd","0x000000000000000000000000000000000013757e15a0905f298303784a161b21","0x000000000000000000000000000000a23a729df796935c7824e3a26be794829b","0x000000000000000000000000000000000005775b6c146c4a59856e869fe5a70e","0x000000000000000000000000000000eef0c9e088fd2d45aa40311082d1f2809b","0x00000000000000000000000000000000001d539ccbfc556d0ad59307a218de65","0x000000000000000000000000000000a2c848beceb6ab7806fd3b88037b8410fc","0x0000000000000000000000000000000000177004deeb1f9d401fd7b1af1a5ac8","0x0000000000000000000000000000002508eb63672a733f20de1a97644be4f540","0x00000000000000000000000000000000000d82d51f2f75d806285fd248c819b8","0x000000000000000000000000000000d002f9100cbba8a29f13b11513c53c59d0","0x000000000000000000000000000000000006cd3b0e3460533b9e5ea2cdc0fcbb","0x000000000000000000000000000000f45ea38a93b2f810c5633ddb54927c1c96","0x000000000000000000000000000000000021791de65f9a28ec7024b1a87ab4f3","0x000000000000000000000000000000926511a0439502c86885a8c6f0327aa7ad","0x000000000000000000000000000000000029fa14a969c5d81ed3abbbfb11220a","0x000000000000000000000000000000b84c3258e8206f560e5b5b18cbeafef87e","0x00000000000000000000000000000000002a910445cd8fc895e5d235cd8ea185","0x000000000000000000000000000000887e67f15e84bcb8507a5064a363f6043b","0x000000000000000000000000000000000014dc6643d801c3ef27c2066b6e2bb4","0x000000000000000000000000000000e38e900b42c314ba803088e8fbf125203f","0x000000000000000000000000000000000020690fd4869db418306046b38161dc","0x0000000000000000000000000000001e2fa856bf7951b8292b1e88185993629c","0x0000000000000000000000000000000000048a85e0bbac7c60ad3d78f601f63c","0x0000000000000000000000000000006f457719495073d3666d77a625aeab0c51","0x00000000000000000000000000000000002623ad892dc62b1fa7d0a650f0d470","0x000000000000000000000000000000dbfcc8a467e021c03b13f74a9f79c3a10c","0x0000000000000000000000000000000000295f6f10976c37bd9c6f96bb7187d5","0x000000000000000000000000000000c13ef9a937cc12420fb38d9ab8e848e85e","0x000000000000000000000000000000000003560a3b334e887532f605c9cb7628","0x0000000000000000000000000000009bcebf08a4599cdda0fb96312d4dc0c7a9","0x000000000000000000000000000000000015adc8bb1e01c835f48959d1237bd6","0x00000000000000000000000000000047762ab839e4ff63c77605a9f383da37c2","0x000000000000000000000000000000000016a8c3c53d89660cf271522cd301fb","0x000000000000000000000000000000f0c8539a0b5f94420a513f9c305b932bfe","0x00000000000000000000000000000000002957ba01d9de5638f808f88a692533","0x000000000000000000000000000000ab17c6189d67d3bf5dd2f3885de0151b6f","0x0000000000000000000000000000000000060d8aa43fdc434d1942263f364d95","0x0000000000000000000000000000005d292333b3adb497f00b4bc32d45229060","0x00000000000000000000000000000000001a1018a66221883639f2898a66f345","0x00000000000000000000000000000006555a806b1993291deba0dc44e2abf431","0x00000000000000000000000000000000000cacff7099a9d5e35a21f4a00b2dc3","0x000000000000000000000000000000f50c11ba95d349c36d143eefd12e494950","0x00000000000000000000000000000000001022e8c5f02d639bc9dd8bc4407f99","0x000000000000000000000000000000c76828795098eda73d50b4b585c60afc60","0x00000000000000000000000000000000002bf09c0ec7011e93888962f2406630","0x00000000000000000000000000000049e5c83a8978d832fb8e144548e3ca1adb","0x00000000000000000000000000000000000e0ec242c2e160a984f61ca5adf5f5","0x0000000000000000000000000000009c5d6e08a6605ab4513748ac0fa017dd1c","0x00000000000000000000000000000000001f54baa07558e5fb055bd9ba49c067","0x0000000000000000000000000000001e1ee7ee29bbb5e4b080c6091c1433ce62","0x000000000000000000000000000000000024aec62a9d9763499267dc98c33428","0x0000000000000000000000000000001a96755946ff16f0d6632365f0eb0ab4d4","0x000000000000000000000000000000000028cf3e22bcd53782ebc3e0490e27e5","0x00000000000000000000000000000043148d7d8c9ba43f2133fab4201435a364","0x0000000000000000000000000000000000234ce541f1f5117dd404cfaf01a229","0x000000000000000000000000000000a7fb95ffb461d9514a1070e2d2403982ef","0x00000000000000000000000000000000003016955028b6390f446c3fd0c5b424","0x00000000000000000000000000000008863c3b7cd7cddc20ba79ce915051c56e","0x000000000000000000000000000000000013ef666111b0be56a235983d397d2a","0x000000000000000000000000000000e3993f465fc9f56e93ac769e597b752c1c","0x0000000000000000000000000000000000217f7c4235161e9a3c16c45b6ca499","0x0000000000000000000000000000008ffa4cd96bc67b0b7df5678271e1114075","0x0000000000000000000000000000000000256467bfcb63d9fdcb5dde397757ad","0x00000000000000000000000000000054e5eb270bb64bde6e6ececadfd8c3236c","0x00000000000000000000000000000000000e52d1bd75812c33c6f3d79ee4b94c","0x000000000000000000000000000000484a2c641dce55bc2dd64ef0cd790a7fea","0x00000000000000000000000000000000000ff417d256be43e73c8b1aa85bdda3","0x0000000000000000000000000000000b72e7b7713ab5da44e0f864182e748a23","0x00000000000000000000000000000000001a221055f1625ad833a44705f5f74e","0x00000000000000000000000000000067a99a34e9b81a17ad001db02e29bcb82a","0x000000000000000000000000000000000018a6c02e398389827568fa960e86e2","0x000000000000000000000000000000bb29f26f9890d6cc6401f4921d5884edca","0x00000000000000000000000000000000000868357b28039385c5a5058b6d358e","0x00000000000000000000000000000036fb6e229dde8edf7ec858b12d7e8be485","0x00000000000000000000000000000000001060afe929554ca473103f5e68193c","0x00000000000000000000000000000015226e07e207744c0857074dcab883af4a","0x00000000000000000000000000000000000b1c02619282755533457230b19b4a","0x0000000000000000000000000000001f2a0277e4807e6e1cbabca21dde5eb5e1","0x00000000000000000000000000000000000d928deafed363659688ed4ccdef52","0x000000000000000000000000000000363f0c994e91cecad25835338edee2294f","0x00000000000000000000000000000000002eea648c8732596b1314fe2a4d2f05","0x000000000000000000000000000000b2671d2ae51d31c1210433c3972bb64578","0x00000000000000000000000000000000000ab49886c2b94bd0bd3f6ed1dbbe2c"]
-proof_b = ["0x0000000000000000000000000000000000000000000000000000000000000003","0x000000000000000000000000000000f05c69448ca29bdf52076f9b073bb30fed","0x000000000000000000000000000000000028c86bb3e27b4aaaaef126f7df5349","0x00000000000000000000000000000026ae031fc93594375dfc7f3bbe027f97d5","0x000000000000000000000000000000000000dd12c7290fe7f775796a233b8590","0x000000000000000000000000000000c1ee6631704de424d010c5c4ac8293ac49","0x00000000000000000000000000000000002f41818c9aa83f5c8d9bdd128015b9","0x000000000000000000000000000000b50a5801482f7e3a5de8ab3cce0f10b0d3","0x000000000000000000000000000000000022a0bc69c293dbf293b25bc9eef7f8","0x0000000000000000000000000000003b02abf1967ef394154dc15d763135e903","0x00000000000000000000000000000000000d8a2ee46acc6d1ed8d517b56d47c8","0x00000000000000000000000000000039bf0d1b3d8cf9de898f101c626e978d78","0x0000000000000000000000000000000000008faa7df2451a24d291a9b584f1a5","0x000000000000000000000000000000c1dae329ed7adf63a2d89a5f16fb98b6d8","0x00000000000000000000000000000000001ff0bc16fc0bd4aa2d6255690453c2","0x000000000000000000000000000000d12d7589f853a9b472613efa56689beaf1","0x00000000000000000000000000000000002d6fbc798f4403751df6aeee8bedd3","0x0000000000000000000000000000007c1fa069cb17194fecf88db9dd54a4ee36","0x0000000000000000000000000000000000268e026f9814822a42b2d59eec5d24","0x000000000000000000000000000000c3fb56beab774218cd63498fc050a5fd9b","0x00000000000000000000000000000000000071c014d7b5063f005a0bc2ee1af4","0x000000000000000000000000000000ae12b25371c6af42bbe0a85cddd2eaebc7","0x000000000000000000000000000000000026d270e1ffc9c7c344c694dfadda83","0x00000000000000000000000000000080280858c6be461716921caa3c26f3f6f3","0x000000000000000000000000000000000001dcdd3f39e27d0ce6aa5d14dff4c1","0x000000000000000000000000000000080e1d2c913c834ebcf7e0600c076c08fd","0x00000000000000000000000000000000002df3d142217694e65fb7c355d62764","0x000000000000000000000000000000e5e336f3f59d77e500f49771bfbeb12e83","0x000000000000000000000000000000000028fffe08bdc4c0690643d2e1a1275f","0x000000000000000000000000000000db5618b32afc13e18f21b39f3fbede9d11","0x00000000000000000000000000000000001d244818370d43fb7e8bc67e03787b","0x0000000000000000000000000000006bcc1fd3f9f78449ad1df1bc11bc379edd","0x000000000000000000000000000000000009ac9cbb285edbf5b3a973f3f5f1cb","0x000000000000000000000000000000fd885905b6c0fc95bb4dd0b11f6797d4b3","0x000000000000000000000000000000000021f07995cdd835145e19c38127c562","0x000000000000000000000000000000bbbf2b975c2c97ae4b45c4a52059e53ee3","0x000000000000000000000000000000000024158163788841cf4590bbc1e89a90","0x0000000000000000000000000000009aca93d2b1386ea412d4b36ea5bb9894a8","0x00000000000000000000000000000000002532d1d210e8ed4c2f5c00cbaaa475","0x000000000000000000000000000000634a88caa1d77cb6b5fe77cac31458fc31","0x00000000000000000000000000000000000bdf18bae92fce7cfddab5520cac6e","0x000000000000000000000000000000622e9626255170ccec77602c755aa193e1","0x000000000000000000000000000000000001d4edba370e04436a988bad05dada","0x000000000000000000000000000000b52934323a0aec8f803cdaafee2ab7bfb2","0x0000000000000000000000000000000000155312af5e0e25ca9fd61aef9e58ed","0x06270b517855f6f6a608e432883d1d1030a12a1e33022dc142b7728691421da2","0x2af7c794d7b720b25eb1df0afd8c8e3c15b6e518194c3caea7966a5f8210ff04","0x073fe573aeb27d81a5713be93e1365390dcbc3c8e7439ff1d36a84cc014f5642","0x11351b961147431e54535248b58b35cf5cddb9b13827899167617d7a96794d64","0x297c9421c9c3db286770787c35b86bc41583386491b4ae55e5fa81aefa21efc4","0x0f4eeca3ff4a3495f859898937688652d33f9b4dd3e003e12adf15278e0997c3","0x133e3d8b82721d40d919f2326810ba6f07eff3f7d20d86b2bde692a811522019","0x2c502f53c9698b73bb8c8f9b9cf2d705d16a64a7040348b4b39c637a2064316c","0x0cbc1971e1c566cde9d9125c91cdc88e817db182692f836c1a5170a6246eaf73","0x12c47793e7db706c637cd4b4d96d227f569850176b852b1fe8ad522ddb38ef0e","0x0cd7b300e9309a135285be1aeb02b152f97931a7357ab6d609a2cb1970aab877","0x2a7789dfe286c9d0a7592f1c9316e730cb14c9d843aefc4764d76e7f8571c96a","0x248ac54ce3dbf37796621882a4ac76046df5ab680da487fd85cce76b1ae392d3","0x149d1d07cebe320f77b03533e34912545cedeae62bd9778d37724728762b5710","0x00fe29daebdaed61309790e70e2dcefa3f3af4c6c965ce424b8dbcf09b8e4b49","0x2b75b3bace61b731d7f0c003a144b62b0a4fbe9f0d14ca89b0652b70210014b3","0x2588ef27cfb6e0d8c6f9a969b2da44fead30a02ed70a563fd15aa45bb671de1c","0x2b74d7674b55642697b4a1e226eddb0e4918b2d57aa5b99093dc46cadcdea000","0x244c626845d3a5040f08f01e9611f968ad675ca857789149b13a0cfa83a2e064","0x2cb8d02f90cae33fd7bcfb80af4aff067c4f5fc4b3f9228d5b8f768bc8f6c971","0x1372f3d1f04e0c39a50e823d5da03d70bebe19a1b8e28f8c2ff601cc0bfc0095","0x19af6601d2613426a50b7c35d60562a5f2f2634e6af56dac13459632e15570ee","0x13c2a16ed3b65dcd9414659be79af17995d344de34eaf962343b0f1e76c73a57","0x0dd5dcdbd50b8774831d4f01f930804d38b4266dfee085185530880a0c3903c0","0x07e91848d660b11b722638680ac60f20db9507fdc8d610ce762600f5a1aacd29","0x1f9c2a94d10c0a7fb60292cfc46fd3d2501181bea0ffe1f5f2501d474be3a785","0x14edb9c5bd389eae08a5ea2a7a1662894e1e878c142084d966a625bef68cf7c3","0x00000000000000000000000000000000cecd01810814d175f0a533f0067618c4","0x00000000000000000000000000000000f82935013ce5c82720c63e533af41db8","0x000000000000000000000000000000012185688171b6bed850e748b66f7222ac","0x2dd7f5ff2150155c2ac86ebe28d9ecbca2eea812b0021ab2bceae111cfea8325","0x04ea6c2daf2b9e827d2213c3d03953410dcf1ed67ba34a3c00e772be92606a8b","0x163f2bd18dcde52f99b9867c944780fd718d1612927053b139b280fc55013d1b","0x05e388fd160ccac30a8f7b18a4bd042f705e92b5937e8c0e9478e2ff623907c6","0x00ba3f6f527d6ed3ff17a63b1d5be3c42bdfae88fdf63311fc7b871157939309","0x16187d9daa8c2e5a1a9ab15be7ca6a8feebfb31bea76f9a3ca69381881c70561","0x0f64522e4904edb7377b14a7b9dad848829167324ef5c016346b3ad8251191ee","0x273bbe6000a4001dce369e5a36cc0b0ca3fd351665b688238aa8c556a6ca6b8e","0x022d2232efb2faa8307846c9a4c697aabad1b7f1336b35ad72fa8922975b49d9","0x0d82d478bff3955c4b0a34ef94427ca5f9da23147ad953c89f2e428277ec2825","0x18d886be90343010659c231583be61a138e28e37c24771e3cb61fbe2587d0671","0x000000000000000000000000000000196ba6a58dbeb7c34cb1d6287e23d434de","0x00000000000000000000000000000000001df8ae8a1589590f8863c1fefd8dfd","0x000000000000000000000000000000f30e11b2c5fbefa166cbb9f58c5f8e1a4c","0x000000000000000000000000000000000026420ade7666bc0ab1cf1fd9d0c534","0x0000000000000000000000000000000feb5b7d8260d25a1ee1ce76ff461673fc","0x00000000000000000000000000000000002bd2ac6223a80671b777bf5dca70a4","0x000000000000000000000000000000690f757006d2fa1ddb0114c9f268783537","0x000000000000000000000000000000000023ad36feadd91e50118f32e97a0204"]
\ No newline at end of file
+proof_b = ["0x000000000000000000000000000000f05c69448ca29bdf52076f9b073bb30fed","0x000000000000000000000000000000000028c86bb3e27b4aaaaef126f7df5349","0x00000000000000000000000000000026ae031fc93594375dfc7f3bbe027f97d5","0x000000000000000000000000000000000000dd12c7290fe7f775796a233b8590","0x000000000000000000000000000000c1ee6631704de424d010c5c4ac8293ac49","0x00000000000000000000000000000000002f41818c9aa83f5c8d9bdd128015b9","0x000000000000000000000000000000b50a5801482f7e3a5de8ab3cce0f10b0d3","0x000000000000000000000000000000000022a0bc69c293dbf293b25bc9eef7f8","0x0000000000000000000000000000003b02abf1967ef394154dc15d763135e903","0x00000000000000000000000000000000000d8a2ee46acc6d1ed8d517b56d47c8","0x00000000000000000000000000000039bf0d1b3d8cf9de898f101c626e978d78","0x0000000000000000000000000000000000008faa7df2451a24d291a9b584f1a5","0x000000000000000000000000000000c1dae329ed7adf63a2d89a5f16fb98b6d8","0x00000000000000000000000000000000001ff0bc16fc0bd4aa2d6255690453c2","0x000000000000000000000000000000d12d7589f853a9b472613efa56689beaf1","0x00000000000000000000000000000000002d6fbc798f4403751df6aeee8bedd3","0x0000000000000000000000000000007c1fa069cb17194fecf88db9dd54a4ee36","0x0000000000000000000000000000000000268e026f9814822a42b2d59eec5d24","0x000000000000000000000000000000c3fb56beab774218cd63498fc050a5fd9b","0x00000000000000000000000000000000000071c014d7b5063f005a0bc2ee1af4","0x000000000000000000000000000000ae12b25371c6af42bbe0a85cddd2eaebc7","0x000000000000000000000000000000000026d270e1ffc9c7c344c694dfadda83","0x00000000000000000000000000000080280858c6be461716921caa3c26f3f6f3","0x000000000000000000000000000000000001dcdd3f39e27d0ce6aa5d14dff4c1","0x000000000000000000000000000000080e1d2c913c834ebcf7e0600c076c08fd","0x00000000000000000000000000000000002df3d142217694e65fb7c355d62764","0x000000000000000000000000000000e5e336f3f59d77e500f49771bfbeb12e83","0x000000000000000000000000000000000028fffe08bdc4c0690643d2e1a1275f","0x000000000000000000000000000000db5618b32afc13e18f21b39f3fbede9d11","0x00000000000000000000000000000000001d244818370d43fb7e8bc67e03787b","0x0000000000000000000000000000006bcc1fd3f9f78449ad1df1bc11bc379edd","0x000000000000000000000000000000000009ac9cbb285edbf5b3a973f3f5f1cb","0x000000000000000000000000000000fd885905b6c0fc95bb4dd0b11f6797d4b3","0x000000000000000000000000000000000021f07995cdd835145e19c38127c562","0x000000000000000000000000000000bbbf2b975c2c97ae4b45c4a52059e53ee3","0x000000000000000000000000000000000024158163788841cf4590bbc1e89a90","0x0000000000000000000000000000009aca93d2b1386ea412d4b36ea5bb9894a8","0x00000000000000000000000000000000002532d1d210e8ed4c2f5c00cbaaa475","0x000000000000000000000000000000634a88caa1d77cb6b5fe77cac31458fc31","0x00000000000000000000000000000000000bdf18bae92fce7cfddab5520cac6e","0x000000000000000000000000000000622e9626255170ccec77602c755aa193e1","0x000000000000000000000000000000000001d4edba370e04436a988bad05dada","0x000000000000000000000000000000b52934323a0aec8f803cdaafee2ab7bfb2","0x0000000000000000000000000000000000155312af5e0e25ca9fd61aef9e58ed","0x06270b517855f6f6a608e432883d1d1030a12a1e33022dc142b7728691421da2","0x2af7c794d7b720b25eb1df0afd8c8e3c15b6e518194c3caea7966a5f8210ff04","0x073fe573aeb27d81a5713be93e1365390dcbc3c8e7439ff1d36a84cc014f5642","0x11351b961147431e54535248b58b35cf5cddb9b13827899167617d7a96794d64","0x297c9421c9c3db286770787c35b86bc41583386491b4ae55e5fa81aefa21efc4","0x0f4eeca3ff4a3495f859898937688652d33f9b4dd3e003e12adf15278e0997c3","0x133e3d8b82721d40d919f2326810ba6f07eff3f7d20d86b2bde692a811522019","0x2c502f53c9698b73bb8c8f9b9cf2d705d16a64a7040348b4b39c637a2064316c","0x0cbc1971e1c566cde9d9125c91cdc88e817db182692f836c1a5170a6246eaf73","0x12c47793e7db706c637cd4b4d96d227f569850176b852b1fe8ad522ddb38ef0e","0x0cd7b300e9309a135285be1aeb02b152f97931a7357ab6d609a2cb1970aab877","0x2a7789dfe286c9d0a7592f1c9316e730cb14c9d843aefc4764d76e7f8571c96a","0x248ac54ce3dbf37796621882a4ac76046df5ab680da487fd85cce76b1ae392d3","0x149d1d07cebe320f77b03533e34912545cedeae62bd9778d37724728762b5710","0x00fe29daebdaed61309790e70e2dcefa3f3af4c6c965ce424b8dbcf09b8e4b49","0x2b75b3bace61b731d7f0c003a144b62b0a4fbe9f0d14ca89b0652b70210014b3","0x2588ef27cfb6e0d8c6f9a969b2da44fead30a02ed70a563fd15aa45bb671de1c","0x2b74d7674b55642697b4a1e226eddb0e4918b2d57aa5b99093dc46cadcdea000","0x244c626845d3a5040f08f01e9611f968ad675ca857789149b13a0cfa83a2e064","0x2cb8d02f90cae33fd7bcfb80af4aff067c4f5fc4b3f9228d5b8f768bc8f6c971","0x1372f3d1f04e0c39a50e823d5da03d70bebe19a1b8e28f8c2ff601cc0bfc0095","0x19af6601d2613426a50b7c35d60562a5f2f2634e6af56dac13459632e15570ee","0x13c2a16ed3b65dcd9414659be79af17995d344de34eaf962343b0f1e76c73a57","0x0dd5dcdbd50b8774831d4f01f930804d38b4266dfee085185530880a0c3903c0","0x07e91848d660b11b722638680ac60f20db9507fdc8d610ce762600f5a1aacd29","0x1f9c2a94d10c0a7fb60292cfc46fd3d2501181bea0ffe1f5f2501d474be3a785","0x14edb9c5bd389eae08a5ea2a7a1662894e1e878c142084d966a625bef68cf7c3","0x00000000000000000000000000000000cecd01810814d175f0a533f0067618c4","0x00000000000000000000000000000000f82935013ce5c82720c63e533af41db8","0x000000000000000000000000000000012185688171b6bed850e748b66f7222ac","0x2dd7f5ff2150155c2ac86ebe28d9ecbca2eea812b0021ab2bceae111cfea8325","0x04ea6c2daf2b9e827d2213c3d03953410dcf1ed67ba34a3c00e772be92606a8b","0x163f2bd18dcde52f99b9867c944780fd718d1612927053b139b280fc55013d1b","0x05e388fd160ccac30a8f7b18a4bd042f705e92b5937e8c0e9478e2ff623907c6","0x00ba3f6f527d6ed3ff17a63b1d5be3c42bdfae88fdf63311fc7b871157939309","0x16187d9daa8c2e5a1a9ab15be7ca6a8feebfb31bea76f9a3ca69381881c70561","0x0f64522e4904edb7377b14a7b9dad848829167324ef5c016346b3ad8251191ee","0x273bbe6000a4001dce369e5a36cc0b0ca3fd351665b688238aa8c556a6ca6b8e","0x022d2232efb2faa8307846c9a4c697aabad1b7f1336b35ad72fa8922975b49d9","0x0d82d478bff3955c4b0a34ef94427ca5f9da23147ad953c89f2e428277ec2825","0x18d886be90343010659c231583be61a138e28e37c24771e3cb61fbe2587d0671","0x000000000000000000000000000000196ba6a58dbeb7c34cb1d6287e23d434de","0x00000000000000000000000000000000001df8ae8a1589590f8863c1fefd8dfd","0x000000000000000000000000000000f30e11b2c5fbefa166cbb9f58c5f8e1a4c","0x000000000000000000000000000000000026420ade7666bc0ab1cf1fd9d0c534","0x0000000000000000000000000000000feb5b7d8260d25a1ee1ce76ff461673fc","0x00000000000000000000000000000000002bd2ac6223a80671b777bf5dca70a4","0x000000000000000000000000000000690f757006d2fa1ddb0114c9f268783537","0x000000000000000000000000000000000023ad36feadd91e50118f32e97a0204"]
\ No newline at end of file
diff --git a/test_programs/execution_success/double_verify_proof/src/main.nr b/test_programs/execution_success/double_verify_proof/src/main.nr
index 98cd534266a..ce087dc4e61 100644
--- a/test_programs/execution_success/double_verify_proof/src/main.nr
+++ b/test_programs/execution_success/double_verify_proof/src/main.nr
@@ -2,31 +2,27 @@ use dep::std;
 
 fn main(
     verification_key: [Field; 114],
-    proof: [Field; 94],
+    // This is the proof without public inputs attached.
+    // 
+    // This means: the size of this does not change with the number of public inputs.
+    proof: [Field; 93],
     public_inputs: [Field; 1],
+    // This is currently not public. It is fine given that the vk is a part of the circuit definition.
+    // I believe we want to eventually make it public too though.
     key_hash: Field,
-    input_aggregation_object: [Field; 16],
-    proof_b: [Field; 94]
-) -> pub [Field; 16] {
-    let output_aggregation_object_a = std::verify_proof(
+    proof_b: [Field; 93]
+) {
+    std::verify_proof(
         verification_key.as_slice(),
         proof.as_slice(),
         public_inputs.as_slice(),
-        key_hash,
-        input_aggregation_object
+        key_hash
     );
 
-    let output_aggregation_object = std::verify_proof(
+    std::verify_proof(
         verification_key.as_slice(),
         proof_b.as_slice(),
         public_inputs.as_slice(),
-        key_hash,
-        output_aggregation_object_a
+        key_hash
     );
-
-    let mut output = [0; 16];
-    for i in 0..16 {
-        output[i] = output_aggregation_object[i];
-    }
-    output
 }
diff --git a/test_programs/execution_success/u128/Nargo.toml b/test_programs/execution_success/u128/Nargo.toml
new file mode 100644
index 00000000000..c1dcd84db04
--- /dev/null
+++ b/test_programs/execution_success/u128/Nargo.toml
@@ -0,0 +1,6 @@
+[package]
+name = "u128"
+type = "bin"
+authors = [""]
+
+[dependencies]
diff --git a/test_programs/execution_success/u128/Prover.toml b/test_programs/execution_success/u128/Prover.toml
new file mode 100644
index 00000000000..961db9825a7
--- /dev/null
+++ b/test_programs/execution_success/u128/Prover.toml
@@ -0,0 +1,7 @@
+x = "3"
+y = "4"
+z = "7"
+hexa ="0x1f03a"
+[big_int]
+lo = 1
+hi = 2
\ No newline at end of file
diff --git a/test_programs/execution_success/u128/src/main.nr b/test_programs/execution_success/u128/src/main.nr
new file mode 100644
index 00000000000..4c734f3a8f9
--- /dev/null
+++ b/test_programs/execution_success/u128/src/main.nr
@@ -0,0 +1,44 @@
+use dep::std;
+
+fn main(mut x: u32, y: u32, z: u32, big_int: U128, hexa: str<7>) {
+    let a = U128::from_u64s_le(x as u64, x as u64);
+    let b = U128::from_u64s_le(y as u64, x as u64);
+    let c = a + b;
+    assert(c.lo == z as Field);
+    assert(c.hi == 2 * x as Field);
+    assert(U128::from_hex(hexa).lo == 0x1f03a);
+    let t1 = U128::from_hex("0x9d9c7a87771f03a23783f9d9c7a8777");
+    let t2 = U128::from_hex("0x45a26c708BFCF39041");
+    let t = t1 + t2;
+    assert(t.lo == 0xc5e4b029996e17b8);
+    assert(t.hi == 0x09d9c7a87771f07f);
+    let t3 = U128::from_le_bytes(t.to_le_bytes());
+    assert(t == t3);
+
+    let t4 = t - t2;
+    assert(t4 == t1);
+
+    let t5 = U128::from_u64s_le(0, 1);
+    let t6 = U128::from_u64s_le(1, 0);
+    assert((t5 - t6).hi == 0);
+
+    assert(
+        (U128::from_hex("0x71f03a23783f9d9c7a8777") * U128::from_hex("0x8BFCF39041")).hi
+        == U128::from_hex("0x3e4e0471b873470e247c824e61445537").hi
+    );
+    let q = U128::from_hex("0x3e4e0471b873470e247c824e61445537") / U128::from_hex("0x8BFCF39041");
+    assert(q == U128::from_hex("0x71f03a23783f9d9c7a8777"));
+
+    assert(big_int.hi == 2);
+
+    let mut small_int = U128::from_integer(x);
+    assert(small_int.lo == x as Field);
+    assert(x == small_int.to_integer());
+    let shift = small_int << small_int;
+    assert(shift == U128::from_integer(x << x));
+    assert(shift >> small_int == small_int);
+    assert(shift >> U128::from_integer(127) == U128::from_integer(0));
+    assert(shift << U128::from_integer(127) == U128::from_integer(0));
+
+}
+
diff --git a/test_programs/test_libraries/exporting_lib/Nargo.toml b/test_programs/test_libraries/exporting_lib/Nargo.toml
new file mode 100644
index 00000000000..628418c0608
--- /dev/null
+++ b/test_programs/test_libraries/exporting_lib/Nargo.toml
@@ -0,0 +1,6 @@
+[package]
+name = "exporting_lib"
+type = "lib"
+authors = [""]
+
+[dependencies]
diff --git a/test_programs/test_libraries/exporting_lib/src/lib.nr b/test_programs/test_libraries/exporting_lib/src/lib.nr
new file mode 100644
index 00000000000..af1fd7a32de
--- /dev/null
+++ b/test_programs/test_libraries/exporting_lib/src/lib.nr
@@ -0,0 +1,10 @@
+
+struct MyStruct {
+    inner: Field
+}
+
+type FooStruct = MyStruct;
+
+fn is_struct_zero(val: MyStruct) -> bool {
+    val.inner == 0
+}
diff --git a/test_programs/test_libraries/reexporting_lib/Nargo.toml b/test_programs/test_libraries/reexporting_lib/Nargo.toml
new file mode 100644
index 00000000000..c26ce501e56
--- /dev/null
+++ b/test_programs/test_libraries/reexporting_lib/Nargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "reexporting_lib"
+type = "lib"
+authors = [""]
+
+[dependencies]
+exporting_lib = { path = "../exporting_lib" }
diff --git a/test_programs/test_libraries/reexporting_lib/src/lib.nr b/test_programs/test_libraries/reexporting_lib/src/lib.nr
new file mode 100644
index 00000000000..f12dfe01ecd
--- /dev/null
+++ b/test_programs/test_libraries/reexporting_lib/src/lib.nr
@@ -0,0 +1,3 @@
+use dep::exporting_lib::{MyStruct, FooStruct};
+
+use dep::exporting_lib as lib;
diff --git a/tooling/backend_interface/test-binaries/mock_backend/src/info_cmd.rs b/tooling/backend_interface/test-binaries/mock_backend/src/info_cmd.rs
index e9a7842ba24..fd8cf602125 100644
--- a/tooling/backend_interface/test-binaries/mock_backend/src/info_cmd.rs
+++ b/tooling/backend_interface/test-binaries/mock_backend/src/info_cmd.rs
@@ -14,11 +14,11 @@ const INFO_RESPONSE: &str = r#"{
         "range",
         "sha256",
         "blake2s",
+        "blake3",
         "keccak256",
         "schnorr_verify",
         "pedersen",
         "pedersen_hash",
-        "hash_to_field_128_security",
         "ecdsa_secp256k1",
         "ecdsa_secp256r1",
         "fixed_base_scalar_mul",
diff --git a/tooling/bb_abstraction_leaks/build.rs b/tooling/bb_abstraction_leaks/build.rs
index 166e61a5a97..965a57747f9 100644
--- a/tooling/bb_abstraction_leaks/build.rs
+++ b/tooling/bb_abstraction_leaks/build.rs
@@ -10,7 +10,7 @@ use const_format::formatcp;
 
 const USERNAME: &str = "AztecProtocol";
 const REPO: &str = "aztec-packages";
-const VERSION: &str = "0.16.0";
+const VERSION: &str = "0.17.0";
 const TAG: &str = formatcp!("aztec-packages-v{}", VERSION);
 
 const API_URL: &str =
diff --git a/tooling/bb_abstraction_leaks/src/contract.sol b/tooling/bb_abstraction_leaks/src/contract.sol
deleted file mode 100644
index 814c81d235e..00000000000
--- a/tooling/bb_abstraction_leaks/src/contract.sol
+++ /dev/null
@@ -1,2575 +0,0 @@
-/**
- * @title Ultra Plonk proof verification contract
- * @dev Top level Plonk proof verification contract, which allows Plonk proof to be verified
- */
-abstract contract BaseUltraVerifier {
-    // VERIFICATION KEY MEMORY LOCATIONS
-    uint256 internal constant N_LOC = 0x380;
-    uint256 internal constant NUM_INPUTS_LOC = 0x3a0;
-    uint256 internal constant OMEGA_LOC = 0x3c0;
-    uint256 internal constant DOMAIN_INVERSE_LOC = 0x3e0;
-    uint256 internal constant Q1_X_LOC = 0x400;
-    uint256 internal constant Q1_Y_LOC = 0x420;
-    uint256 internal constant Q2_X_LOC = 0x440;
-    uint256 internal constant Q2_Y_LOC = 0x460;
-    uint256 internal constant Q3_X_LOC = 0x480;
-    uint256 internal constant Q3_Y_LOC = 0x4a0;
-    uint256 internal constant Q4_X_LOC = 0x4c0;
-    uint256 internal constant Q4_Y_LOC = 0x4e0;
-    uint256 internal constant QM_X_LOC = 0x500;
-    uint256 internal constant QM_Y_LOC = 0x520;
-    uint256 internal constant QC_X_LOC = 0x540;
-    uint256 internal constant QC_Y_LOC = 0x560;
-    uint256 internal constant QARITH_X_LOC = 0x580;
-    uint256 internal constant QARITH_Y_LOC = 0x5a0;
-    uint256 internal constant QSORT_X_LOC = 0x5c0;
-    uint256 internal constant QSORT_Y_LOC = 0x5e0;
-    uint256 internal constant QELLIPTIC_X_LOC = 0x600;
-    uint256 internal constant QELLIPTIC_Y_LOC = 0x620;
-    uint256 internal constant QAUX_X_LOC = 0x640;
-    uint256 internal constant QAUX_Y_LOC = 0x660;
-    uint256 internal constant SIGMA1_X_LOC = 0x680;
-    uint256 internal constant SIGMA1_Y_LOC = 0x6a0;
-    uint256 internal constant SIGMA2_X_LOC = 0x6c0;
-    uint256 internal constant SIGMA2_Y_LOC = 0x6e0;
-    uint256 internal constant SIGMA3_X_LOC = 0x700;
-    uint256 internal constant SIGMA3_Y_LOC = 0x720;
-    uint256 internal constant SIGMA4_X_LOC = 0x740;
-    uint256 internal constant SIGMA4_Y_LOC = 0x760;
-    uint256 internal constant TABLE1_X_LOC = 0x780;
-    uint256 internal constant TABLE1_Y_LOC = 0x7a0;
-    uint256 internal constant TABLE2_X_LOC = 0x7c0;
-    uint256 internal constant TABLE2_Y_LOC = 0x7e0;
-    uint256 internal constant TABLE3_X_LOC = 0x800;
-    uint256 internal constant TABLE3_Y_LOC = 0x820;
-    uint256 internal constant TABLE4_X_LOC = 0x840;
-    uint256 internal constant TABLE4_Y_LOC = 0x860;
-    uint256 internal constant TABLE_TYPE_X_LOC = 0x880;
-    uint256 internal constant TABLE_TYPE_Y_LOC = 0x8a0;
-    uint256 internal constant ID1_X_LOC = 0x8c0;
-    uint256 internal constant ID1_Y_LOC = 0x8e0;
-    uint256 internal constant ID2_X_LOC = 0x900;
-    uint256 internal constant ID2_Y_LOC = 0x920;
-    uint256 internal constant ID3_X_LOC = 0x940;
-    uint256 internal constant ID3_Y_LOC = 0x960;
-    uint256 internal constant ID4_X_LOC = 0x980;
-    uint256 internal constant ID4_Y_LOC = 0x9a0;
-    uint256 internal constant CONTAINS_RECURSIVE_PROOF_LOC = 0x9c0;
-    uint256 internal constant RECURSIVE_PROOF_PUBLIC_INPUT_INDICES_LOC = 0x9e0;
-    uint256 internal constant G2X_X0_LOC = 0xa00;
-    uint256 internal constant G2X_X1_LOC = 0xa20;
-    uint256 internal constant G2X_Y0_LOC = 0xa40;
-    uint256 internal constant G2X_Y1_LOC = 0xa60;
-
-    // ### PROOF DATA MEMORY LOCATIONS
-    uint256 internal constant W1_X_LOC = 0x1200;
-    uint256 internal constant W1_Y_LOC = 0x1220;
-    uint256 internal constant W2_X_LOC = 0x1240;
-    uint256 internal constant W2_Y_LOC = 0x1260;
-    uint256 internal constant W3_X_LOC = 0x1280;
-    uint256 internal constant W3_Y_LOC = 0x12a0;
-    uint256 internal constant W4_X_LOC = 0x12c0;
-    uint256 internal constant W4_Y_LOC = 0x12e0;
-    uint256 internal constant S_X_LOC = 0x1300;
-    uint256 internal constant S_Y_LOC = 0x1320;
-    uint256 internal constant Z_X_LOC = 0x1340;
-    uint256 internal constant Z_Y_LOC = 0x1360;
-    uint256 internal constant Z_LOOKUP_X_LOC = 0x1380;
-    uint256 internal constant Z_LOOKUP_Y_LOC = 0x13a0;
-    uint256 internal constant T1_X_LOC = 0x13c0;
-    uint256 internal constant T1_Y_LOC = 0x13e0;
-    uint256 internal constant T2_X_LOC = 0x1400;
-    uint256 internal constant T2_Y_LOC = 0x1420;
-    uint256 internal constant T3_X_LOC = 0x1440;
-    uint256 internal constant T3_Y_LOC = 0x1460;
-    uint256 internal constant T4_X_LOC = 0x1480;
-    uint256 internal constant T4_Y_LOC = 0x14a0;
-
-    uint256 internal constant W1_EVAL_LOC = 0x1600;
-    uint256 internal constant W2_EVAL_LOC = 0x1620;
-    uint256 internal constant W3_EVAL_LOC = 0x1640;
-    uint256 internal constant W4_EVAL_LOC = 0x1660;
-    uint256 internal constant S_EVAL_LOC = 0x1680;
-    uint256 internal constant Z_EVAL_LOC = 0x16a0;
-    uint256 internal constant Z_LOOKUP_EVAL_LOC = 0x16c0;
-    uint256 internal constant Q1_EVAL_LOC = 0x16e0;
-    uint256 internal constant Q2_EVAL_LOC = 0x1700;
-    uint256 internal constant Q3_EVAL_LOC = 0x1720;
-    uint256 internal constant Q4_EVAL_LOC = 0x1740;
-    uint256 internal constant QM_EVAL_LOC = 0x1760;
-    uint256 internal constant QC_EVAL_LOC = 0x1780;
-    uint256 internal constant QARITH_EVAL_LOC = 0x17a0;
-    uint256 internal constant QSORT_EVAL_LOC = 0x17c0;
-    uint256 internal constant QELLIPTIC_EVAL_LOC = 0x17e0;
-    uint256 internal constant QAUX_EVAL_LOC = 0x1800;
-    uint256 internal constant TABLE1_EVAL_LOC = 0x1840;
-    uint256 internal constant TABLE2_EVAL_LOC = 0x1860;
-    uint256 internal constant TABLE3_EVAL_LOC = 0x1880;
-    uint256 internal constant TABLE4_EVAL_LOC = 0x18a0;
-    uint256 internal constant TABLE_TYPE_EVAL_LOC = 0x18c0;
-    uint256 internal constant ID1_EVAL_LOC = 0x18e0;
-    uint256 internal constant ID2_EVAL_LOC = 0x1900;
-    uint256 internal constant ID3_EVAL_LOC = 0x1920;
-    uint256 internal constant ID4_EVAL_LOC = 0x1940;
-    uint256 internal constant SIGMA1_EVAL_LOC = 0x1960;
-    uint256 internal constant SIGMA2_EVAL_LOC = 0x1980;
-    uint256 internal constant SIGMA3_EVAL_LOC = 0x19a0;
-    uint256 internal constant SIGMA4_EVAL_LOC = 0x19c0;
-    uint256 internal constant W1_OMEGA_EVAL_LOC = 0x19e0;
-    uint256 internal constant W2_OMEGA_EVAL_LOC = 0x2000;
-    uint256 internal constant W3_OMEGA_EVAL_LOC = 0x2020;
-    uint256 internal constant W4_OMEGA_EVAL_LOC = 0x2040;
-    uint256 internal constant S_OMEGA_EVAL_LOC = 0x2060;
-    uint256 internal constant Z_OMEGA_EVAL_LOC = 0x2080;
-    uint256 internal constant Z_LOOKUP_OMEGA_EVAL_LOC = 0x20a0;
-    uint256 internal constant TABLE1_OMEGA_EVAL_LOC = 0x20c0;
-    uint256 internal constant TABLE2_OMEGA_EVAL_LOC = 0x20e0;
-    uint256 internal constant TABLE3_OMEGA_EVAL_LOC = 0x2100;
-    uint256 internal constant TABLE4_OMEGA_EVAL_LOC = 0x2120;
-
-    uint256 internal constant PI_Z_X_LOC = 0x2300;
-    uint256 internal constant PI_Z_Y_LOC = 0x2320;
-    uint256 internal constant PI_Z_OMEGA_X_LOC = 0x2340;
-    uint256 internal constant PI_Z_OMEGA_Y_LOC = 0x2360;
-
-    // Used for elliptic widget. These are alias names for wire + shifted wire evaluations
-    uint256 internal constant X1_EVAL_LOC = W2_EVAL_LOC;
-    uint256 internal constant X2_EVAL_LOC = W1_OMEGA_EVAL_LOC;
-    uint256 internal constant X3_EVAL_LOC = W2_OMEGA_EVAL_LOC;
-    uint256 internal constant Y1_EVAL_LOC = W3_EVAL_LOC;
-    uint256 internal constant Y2_EVAL_LOC = W4_OMEGA_EVAL_LOC;
-    uint256 internal constant Y3_EVAL_LOC = W3_OMEGA_EVAL_LOC;
-    uint256 internal constant QBETA_LOC = Q3_EVAL_LOC;
-    uint256 internal constant QBETA_SQR_LOC = Q4_EVAL_LOC;
-    uint256 internal constant QSIGN_LOC = Q1_EVAL_LOC;
-
-    // ### CHALLENGES MEMORY OFFSETS
-
-    uint256 internal constant C_BETA_LOC = 0x2600;
-    uint256 internal constant C_GAMMA_LOC = 0x2620;
-    uint256 internal constant C_ALPHA_LOC = 0x2640;
-    uint256 internal constant C_ETA_LOC = 0x2660;
-    uint256 internal constant C_ETA_SQR_LOC = 0x2680;
-    uint256 internal constant C_ETA_CUBE_LOC = 0x26a0;
-
-    uint256 internal constant C_ZETA_LOC = 0x26c0;
-    uint256 internal constant C_CURRENT_LOC = 0x26e0;
-    uint256 internal constant C_V0_LOC = 0x2700;
-    uint256 internal constant C_V1_LOC = 0x2720;
-    uint256 internal constant C_V2_LOC = 0x2740;
-    uint256 internal constant C_V3_LOC = 0x2760;
-    uint256 internal constant C_V4_LOC = 0x2780;
-    uint256 internal constant C_V5_LOC = 0x27a0;
-    uint256 internal constant C_V6_LOC = 0x27c0;
-    uint256 internal constant C_V7_LOC = 0x27e0;
-    uint256 internal constant C_V8_LOC = 0x2800;
-    uint256 internal constant C_V9_LOC = 0x2820;
-    uint256 internal constant C_V10_LOC = 0x2840;
-    uint256 internal constant C_V11_LOC = 0x2860;
-    uint256 internal constant C_V12_LOC = 0x2880;
-    uint256 internal constant C_V13_LOC = 0x28a0;
-    uint256 internal constant C_V14_LOC = 0x28c0;
-    uint256 internal constant C_V15_LOC = 0x28e0;
-    uint256 internal constant C_V16_LOC = 0x2900;
-    uint256 internal constant C_V17_LOC = 0x2920;
-    uint256 internal constant C_V18_LOC = 0x2940;
-    uint256 internal constant C_V19_LOC = 0x2960;
-    uint256 internal constant C_V20_LOC = 0x2980;
-    uint256 internal constant C_V21_LOC = 0x29a0;
-    uint256 internal constant C_V22_LOC = 0x29c0;
-    uint256 internal constant C_V23_LOC = 0x29e0;
-    uint256 internal constant C_V24_LOC = 0x2a00;
-    uint256 internal constant C_V25_LOC = 0x2a20;
-    uint256 internal constant C_V26_LOC = 0x2a40;
-    uint256 internal constant C_V27_LOC = 0x2a60;
-    uint256 internal constant C_V28_LOC = 0x2a80;
-    uint256 internal constant C_V29_LOC = 0x2aa0;
-    uint256 internal constant C_V30_LOC = 0x2ac0;
-
-    uint256 internal constant C_U_LOC = 0x2b00;
-
-    // ### LOCAL VARIABLES MEMORY OFFSETS
-    uint256 internal constant DELTA_NUMERATOR_LOC = 0x3000;
-    uint256 internal constant DELTA_DENOMINATOR_LOC = 0x3020;
-    uint256 internal constant ZETA_POW_N_LOC = 0x3040;
-    uint256 internal constant PUBLIC_INPUT_DELTA_LOC = 0x3060;
-    uint256 internal constant ZERO_POLY_LOC = 0x3080;
-    uint256 internal constant L_START_LOC = 0x30a0;
-    uint256 internal constant L_END_LOC = 0x30c0;
-    uint256 internal constant R_ZERO_EVAL_LOC = 0x30e0;
-
-    uint256 internal constant PLOOKUP_DELTA_NUMERATOR_LOC = 0x3100;
-    uint256 internal constant PLOOKUP_DELTA_DENOMINATOR_LOC = 0x3120;
-    uint256 internal constant PLOOKUP_DELTA_LOC = 0x3140;
-
-    uint256 internal constant ACCUMULATOR_X_LOC = 0x3160;
-    uint256 internal constant ACCUMULATOR_Y_LOC = 0x3180;
-    uint256 internal constant ACCUMULATOR2_X_LOC = 0x31a0;
-    uint256 internal constant ACCUMULATOR2_Y_LOC = 0x31c0;
-    uint256 internal constant PAIRING_LHS_X_LOC = 0x31e0;
-    uint256 internal constant PAIRING_LHS_Y_LOC = 0x3200;
-    uint256 internal constant PAIRING_RHS_X_LOC = 0x3220;
-    uint256 internal constant PAIRING_RHS_Y_LOC = 0x3240;
-
-    // ### SUCCESS FLAG MEMORY LOCATIONS
-    uint256 internal constant GRAND_PRODUCT_SUCCESS_FLAG = 0x3300;
-    uint256 internal constant ARITHMETIC_TERM_SUCCESS_FLAG = 0x3020;
-    uint256 internal constant BATCH_OPENING_SUCCESS_FLAG = 0x3340;
-    uint256 internal constant OPENING_COMMITMENT_SUCCESS_FLAG = 0x3360;
-    uint256 internal constant PAIRING_PREAMBLE_SUCCESS_FLAG = 0x3380;
-    uint256 internal constant PAIRING_SUCCESS_FLAG = 0x33a0;
-    uint256 internal constant RESULT_FLAG = 0x33c0;
-
-    // misc stuff
-    uint256 internal constant OMEGA_INVERSE_LOC = 0x3400;
-    uint256 internal constant C_ALPHA_SQR_LOC = 0x3420;
-    uint256 internal constant C_ALPHA_CUBE_LOC = 0x3440;
-    uint256 internal constant C_ALPHA_QUAD_LOC = 0x3460;
-    uint256 internal constant C_ALPHA_BASE_LOC = 0x3480;
-
-    // ### RECURSION VARIABLE MEMORY LOCATIONS
-    uint256 internal constant RECURSIVE_P1_X_LOC = 0x3500;
-    uint256 internal constant RECURSIVE_P1_Y_LOC = 0x3520;
-    uint256 internal constant RECURSIVE_P2_X_LOC = 0x3540;
-    uint256 internal constant RECURSIVE_P2_Y_LOC = 0x3560;
-
-    uint256 internal constant PUBLIC_INPUTS_HASH_LOCATION = 0x3580;
-
-    // sub-identity storage
-    uint256 internal constant PERMUTATION_IDENTITY = 0x3600;
-    uint256 internal constant PLOOKUP_IDENTITY = 0x3620;
-    uint256 internal constant ARITHMETIC_IDENTITY = 0x3640;
-    uint256 internal constant SORT_IDENTITY = 0x3660;
-    uint256 internal constant ELLIPTIC_IDENTITY = 0x3680;
-    uint256 internal constant AUX_IDENTITY = 0x36a0;
-    uint256 internal constant AUX_NON_NATIVE_FIELD_EVALUATION = 0x36c0;
-    uint256 internal constant AUX_LIMB_ACCUMULATOR_EVALUATION = 0x36e0;
-    uint256 internal constant AUX_RAM_CONSISTENCY_EVALUATION = 0x3700;
-    uint256 internal constant AUX_ROM_CONSISTENCY_EVALUATION = 0x3720;
-    uint256 internal constant AUX_MEMORY_EVALUATION = 0x3740;
-
-    uint256 internal constant QUOTIENT_EVAL_LOC = 0x3760;
-    uint256 internal constant ZERO_POLY_INVERSE_LOC = 0x3780;
-
-    // when hashing public inputs we use memory at NU_CHALLENGE_INPUT_LOC_A, as the hash input size is unknown at compile time
-    uint256 internal constant NU_CHALLENGE_INPUT_LOC_A = 0x37a0;
-    uint256 internal constant NU_CHALLENGE_INPUT_LOC_B = 0x37c0;
-    uint256 internal constant NU_CHALLENGE_INPUT_LOC_C = 0x37e0;
-
-    bytes4 internal constant PUBLIC_INPUT_INVALID_BN128_G1_POINT_SELECTOR = 0xeba9f4a6;
-    bytes4 internal constant PUBLIC_INPUT_GE_P_SELECTOR = 0x374a972f;
-    bytes4 internal constant MOD_EXP_FAILURE_SELECTOR = 0xf894a7bc;
-    bytes4 internal constant EC_SCALAR_MUL_FAILURE_SELECTOR = 0xf755f369;
-    bytes4 internal constant PROOF_FAILURE_SELECTOR = 0x0711fcec;
-
-    uint256 internal constant ETA_INPUT_LENGTH = 0xc0; // W1, W2, W3 = 6 * 0x20 bytes
-
-    // We need to hash 41 field elements when generating the NU challenge
-    // w1, w2, w3, w4, s, z, z_lookup, q1, q2, q3, q4, qm, qc, qarith (14)
-    // qsort, qelliptic, qaux, sigma1, sigma2, sigma, sigma4, (7)
-    // table1, table2, table3, table4, tabletype, id1, id2, id3, id4, (9)
-    // w1_omega, w2_omega, w3_omega, w4_omega, s_omega, z_omega, z_lookup_omega, (7)
-    // table1_omega, table2_omega, table3_omega, table4_omega (4)
-    uint256 internal constant NU_INPUT_LENGTH = 0x520; // 0x520 = 41 * 0x20
-
-    // There are ELEVEN G1 group elements added into the transcript in the `beta` round, that we need to skip over
-    // W1, W2, W3, W4, S, Z, Z_LOOKUP, T1, T2, T3, T4
-    uint256 internal constant NU_CALLDATA_SKIP_LENGTH = 0x2c0; // 11 * 0x40 = 0x2c0
-
-    uint256 internal constant NEGATIVE_INVERSE_OF_2_MODULO_P =
-        0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000;
-    uint256 internal constant LIMB_SIZE = 0x100000000000000000; // 2<<68
-    uint256 internal constant SUBLIMB_SHIFT = 0x4000; // 2<<14
-
-    // y^2 = x^3 + ax + b
-    // for Grumpkin, a = 0 and b = -17. We use b in a custom gate relation that evaluates elliptic curve arithmetic
-    uint256 internal constant GRUMPKIN_CURVE_B_PARAMETER_NEGATED = 17;
-    error PUBLIC_INPUT_COUNT_INVALID(uint256 expected, uint256 actual);
-    error PUBLIC_INPUT_INVALID_BN128_G1_POINT();
-    error PUBLIC_INPUT_GE_P();
-    error MOD_EXP_FAILURE();
-    error EC_SCALAR_MUL_FAILURE();
-    error PROOF_FAILURE();
-
-    function getVerificationKeyHash() public pure virtual returns (bytes32);
-
-    function loadVerificationKey(uint256 _vk, uint256 _omegaInverseLoc) internal pure virtual;
-
-    /**
-     * @notice Verify a Ultra Plonk proof
-     * @param _proof - The serialized proof
-     * @param _publicInputs - An array of the public inputs
-     * @return True if proof is valid, reverts otherwise
-     */
-    function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external view returns (bool) {
-        loadVerificationKey(N_LOC, OMEGA_INVERSE_LOC);
-
-        uint256 requiredPublicInputCount;
-        assembly {
-            requiredPublicInputCount := mload(NUM_INPUTS_LOC)
-        }
-        if (requiredPublicInputCount != _publicInputs.length) {
-            revert PUBLIC_INPUT_COUNT_INVALID(requiredPublicInputCount, _publicInputs.length);
-        }
-
-        assembly {
-            let q := 21888242871839275222246405745257275088696311157297823662689037894645226208583 // EC group order
-            let p := 21888242871839275222246405745257275088548364400416034343698204186575808495617 // Prime field order
-
-            /**
-             * LOAD PROOF FROM CALLDATA
-             */
-            {
-                let data_ptr := add(calldataload(0x04), 0x24)
-
-                mstore(W1_Y_LOC, mod(calldataload(data_ptr), q))
-                mstore(W1_X_LOC, mod(calldataload(add(data_ptr, 0x20)), q))
-
-                mstore(W2_Y_LOC, mod(calldataload(add(data_ptr, 0x40)), q))
-                mstore(W2_X_LOC, mod(calldataload(add(data_ptr, 0x60)), q))
-
-                mstore(W3_Y_LOC, mod(calldataload(add(data_ptr, 0x80)), q))
-                mstore(W3_X_LOC, mod(calldataload(add(data_ptr, 0xa0)), q))
-
-                mstore(W4_Y_LOC, mod(calldataload(add(data_ptr, 0xc0)), q))
-                mstore(W4_X_LOC, mod(calldataload(add(data_ptr, 0xe0)), q))
-
-                mstore(S_Y_LOC, mod(calldataload(add(data_ptr, 0x100)), q))
-                mstore(S_X_LOC, mod(calldataload(add(data_ptr, 0x120)), q))
-                mstore(Z_Y_LOC, mod(calldataload(add(data_ptr, 0x140)), q))
-                mstore(Z_X_LOC, mod(calldataload(add(data_ptr, 0x160)), q))
-                mstore(Z_LOOKUP_Y_LOC, mod(calldataload(add(data_ptr, 0x180)), q))
-                mstore(Z_LOOKUP_X_LOC, mod(calldataload(add(data_ptr, 0x1a0)), q))
-                mstore(T1_Y_LOC, mod(calldataload(add(data_ptr, 0x1c0)), q))
-                mstore(T1_X_LOC, mod(calldataload(add(data_ptr, 0x1e0)), q))
-
-                mstore(T2_Y_LOC, mod(calldataload(add(data_ptr, 0x200)), q))
-                mstore(T2_X_LOC, mod(calldataload(add(data_ptr, 0x220)), q))
-
-                mstore(T3_Y_LOC, mod(calldataload(add(data_ptr, 0x240)), q))
-                mstore(T3_X_LOC, mod(calldataload(add(data_ptr, 0x260)), q))
-
-                mstore(T4_Y_LOC, mod(calldataload(add(data_ptr, 0x280)), q))
-                mstore(T4_X_LOC, mod(calldataload(add(data_ptr, 0x2a0)), q))
-
-                mstore(W1_EVAL_LOC, mod(calldataload(add(data_ptr, 0x2c0)), p))
-                mstore(W2_EVAL_LOC, mod(calldataload(add(data_ptr, 0x2e0)), p))
-                mstore(W3_EVAL_LOC, mod(calldataload(add(data_ptr, 0x300)), p))
-                mstore(W4_EVAL_LOC, mod(calldataload(add(data_ptr, 0x320)), p))
-                mstore(S_EVAL_LOC, mod(calldataload(add(data_ptr, 0x340)), p))
-                mstore(Z_EVAL_LOC, mod(calldataload(add(data_ptr, 0x360)), p))
-                mstore(Z_LOOKUP_EVAL_LOC, mod(calldataload(add(data_ptr, 0x380)), p))
-                mstore(Q1_EVAL_LOC, mod(calldataload(add(data_ptr, 0x3a0)), p))
-                mstore(Q2_EVAL_LOC, mod(calldataload(add(data_ptr, 0x3c0)), p))
-                mstore(Q3_EVAL_LOC, mod(calldataload(add(data_ptr, 0x3e0)), p))
-                mstore(Q4_EVAL_LOC, mod(calldataload(add(data_ptr, 0x400)), p))
-                mstore(QM_EVAL_LOC, mod(calldataload(add(data_ptr, 0x420)), p))
-                mstore(QC_EVAL_LOC, mod(calldataload(add(data_ptr, 0x440)), p))
-                mstore(QARITH_EVAL_LOC, mod(calldataload(add(data_ptr, 0x460)), p))
-                mstore(QSORT_EVAL_LOC, mod(calldataload(add(data_ptr, 0x480)), p))
-                mstore(QELLIPTIC_EVAL_LOC, mod(calldataload(add(data_ptr, 0x4a0)), p))
-                mstore(QAUX_EVAL_LOC, mod(calldataload(add(data_ptr, 0x4c0)), p))
-
-                mstore(SIGMA1_EVAL_LOC, mod(calldataload(add(data_ptr, 0x4e0)), p))
-                mstore(SIGMA2_EVAL_LOC, mod(calldataload(add(data_ptr, 0x500)), p))
-
-                mstore(SIGMA3_EVAL_LOC, mod(calldataload(add(data_ptr, 0x520)), p))
-                mstore(SIGMA4_EVAL_LOC, mod(calldataload(add(data_ptr, 0x540)), p))
-
-                mstore(TABLE1_EVAL_LOC, mod(calldataload(add(data_ptr, 0x560)), p))
-                mstore(TABLE2_EVAL_LOC, mod(calldataload(add(data_ptr, 0x580)), p))
-                mstore(TABLE3_EVAL_LOC, mod(calldataload(add(data_ptr, 0x5a0)), p))
-                mstore(TABLE4_EVAL_LOC, mod(calldataload(add(data_ptr, 0x5c0)), p))
-                mstore(TABLE_TYPE_EVAL_LOC, mod(calldataload(add(data_ptr, 0x5e0)), p))
-
-                mstore(ID1_EVAL_LOC, mod(calldataload(add(data_ptr, 0x600)), p))
-                mstore(ID2_EVAL_LOC, mod(calldataload(add(data_ptr, 0x620)), p))
-                mstore(ID3_EVAL_LOC, mod(calldataload(add(data_ptr, 0x640)), p))
-                mstore(ID4_EVAL_LOC, mod(calldataload(add(data_ptr, 0x660)), p))
-
-                mstore(W1_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x680)), p))
-                mstore(W2_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x6a0)), p))
-                mstore(W3_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x6c0)), p))
-                mstore(W4_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x6e0)), p))
-                mstore(S_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x700)), p))
-
-                mstore(Z_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x720)), p))
-
-                mstore(Z_LOOKUP_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x740)), p))
-                mstore(TABLE1_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x760)), p))
-                mstore(TABLE2_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x780)), p))
-                mstore(TABLE3_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x7a0)), p))
-                mstore(TABLE4_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x7c0)), p))
-
-                mstore(PI_Z_Y_LOC, mod(calldataload(add(data_ptr, 0x7e0)), q))
-                mstore(PI_Z_X_LOC, mod(calldataload(add(data_ptr, 0x800)), q))
-
-                mstore(PI_Z_OMEGA_Y_LOC, mod(calldataload(add(data_ptr, 0x820)), q))
-                mstore(PI_Z_OMEGA_X_LOC, mod(calldataload(add(data_ptr, 0x840)), q))
-            }
-
-            /**
-             * LOAD RECURSIVE PROOF INTO MEMORY
-             */
-            {
-                if mload(CONTAINS_RECURSIVE_PROOF_LOC) {
-                    let public_inputs_ptr := add(calldataload(0x24), 0x24)
-                    let index_counter := add(shl(5, mload(RECURSIVE_PROOF_PUBLIC_INPUT_INDICES_LOC)), public_inputs_ptr)
-
-                    let x0 := calldataload(index_counter)
-                    x0 := add(x0, shl(68, calldataload(add(index_counter, 0x20))))
-                    x0 := add(x0, shl(136, calldataload(add(index_counter, 0x40))))
-                    x0 := add(x0, shl(204, calldataload(add(index_counter, 0x60))))
-                    let y0 := calldataload(add(index_counter, 0x80))
-                    y0 := add(y0, shl(68, calldataload(add(index_counter, 0xa0))))
-                    y0 := add(y0, shl(136, calldataload(add(index_counter, 0xc0))))
-                    y0 := add(y0, shl(204, calldataload(add(index_counter, 0xe0))))
-                    let x1 := calldataload(add(index_counter, 0x100))
-                    x1 := add(x1, shl(68, calldataload(add(index_counter, 0x120))))
-                    x1 := add(x1, shl(136, calldataload(add(index_counter, 0x140))))
-                    x1 := add(x1, shl(204, calldataload(add(index_counter, 0x160))))
-                    let y1 := calldataload(add(index_counter, 0x180))
-                    y1 := add(y1, shl(68, calldataload(add(index_counter, 0x1a0))))
-                    y1 := add(y1, shl(136, calldataload(add(index_counter, 0x1c0))))
-                    y1 := add(y1, shl(204, calldataload(add(index_counter, 0x1e0))))
-                    mstore(RECURSIVE_P1_X_LOC, x0)
-                    mstore(RECURSIVE_P1_Y_LOC, y0)
-                    mstore(RECURSIVE_P2_X_LOC, x1)
-                    mstore(RECURSIVE_P2_Y_LOC, y1)
-
-                    // validate these are valid bn128 G1 points
-                    if iszero(and(and(lt(x0, q), lt(x1, q)), and(lt(y0, q), lt(y1, q)))) {
-                        mstore(0x00, PUBLIC_INPUT_INVALID_BN128_G1_POINT_SELECTOR)
-                        revert(0x00, 0x04)
-                    }
-                }
-            }
-
-            {
-                /**
-                 * Generate initial challenge
-                 */
-                mstore(0x00, shl(224, mload(N_LOC)))
-                mstore(0x04, shl(224, mload(NUM_INPUTS_LOC)))
-                let challenge := keccak256(0x00, 0x08)
-
-                /**
-                 * Generate eta challenge
-                 */
-                mstore(PUBLIC_INPUTS_HASH_LOCATION, challenge)
-                // The public input location is stored at 0x24, we then add 0x24 to skip selector and the length of public inputs
-                let public_inputs_start := add(calldataload(0x24), 0x24)
-                // copy the public inputs over
-                let public_input_size := mul(mload(NUM_INPUTS_LOC), 0x20)
-                calldatacopy(add(PUBLIC_INPUTS_HASH_LOCATION, 0x20), public_inputs_start, public_input_size)
-
-                // copy W1, W2, W3 into challenge. Each point is 0x40 bytes, so load 0xc0 = 3 * 0x40 bytes (ETA input length)
-                let w_start := add(calldataload(0x04), 0x24)
-                calldatacopy(add(add(PUBLIC_INPUTS_HASH_LOCATION, 0x20), public_input_size), w_start, ETA_INPUT_LENGTH)
-
-                // Challenge is the old challenge + public inputs + W1, W2, W3 (0x20 + public_input_size + 0xc0)
-                let challenge_bytes_size := add(0x20, add(public_input_size, ETA_INPUT_LENGTH))
-
-                challenge := keccak256(PUBLIC_INPUTS_HASH_LOCATION, challenge_bytes_size)
-                {
-                    let eta := mod(challenge, p)
-                    mstore(C_ETA_LOC, eta)
-                    mstore(C_ETA_SQR_LOC, mulmod(eta, eta, p))
-                    mstore(C_ETA_CUBE_LOC, mulmod(mload(C_ETA_SQR_LOC), eta, p))
-                }
-
-                /**
-                 * Generate beta challenge
-                 */
-                mstore(0x00, challenge)
-                mstore(0x20, mload(W4_Y_LOC))
-                mstore(0x40, mload(W4_X_LOC))
-                mstore(0x60, mload(S_Y_LOC))
-                mstore(0x80, mload(S_X_LOC))
-                challenge := keccak256(0x00, 0xa0)
-                mstore(C_BETA_LOC, mod(challenge, p))
-
-                /**
-                 * Generate gamma challenge
-                 */
-                mstore(0x00, challenge)
-                mstore8(0x20, 0x01)
-                challenge := keccak256(0x00, 0x21)
-                mstore(C_GAMMA_LOC, mod(challenge, p))
-
-                /**
-                 * Generate alpha challenge
-                 */
-                mstore(0x00, challenge)
-                mstore(0x20, mload(Z_Y_LOC))
-                mstore(0x40, mload(Z_X_LOC))
-                mstore(0x60, mload(Z_LOOKUP_Y_LOC))
-                mstore(0x80, mload(Z_LOOKUP_X_LOC))
-                challenge := keccak256(0x00, 0xa0)
-                mstore(C_ALPHA_LOC, mod(challenge, p))
-
-                /**
-                 * Compute and store some powers of alpha for future computations
-                 */
-                let alpha := mload(C_ALPHA_LOC)
-                mstore(C_ALPHA_SQR_LOC, mulmod(alpha, alpha, p))
-                mstore(C_ALPHA_CUBE_LOC, mulmod(mload(C_ALPHA_SQR_LOC), alpha, p))
-                mstore(C_ALPHA_QUAD_LOC, mulmod(mload(C_ALPHA_CUBE_LOC), alpha, p))
-                mstore(C_ALPHA_BASE_LOC, alpha)
-
-                /**
-                 * Generate zeta challenge
-                 */
-                mstore(0x00, challenge)
-                mstore(0x20, mload(T1_Y_LOC))
-                mstore(0x40, mload(T1_X_LOC))
-                mstore(0x60, mload(T2_Y_LOC))
-                mstore(0x80, mload(T2_X_LOC))
-                mstore(0xa0, mload(T3_Y_LOC))
-                mstore(0xc0, mload(T3_X_LOC))
-                mstore(0xe0, mload(T4_Y_LOC))
-                mstore(0x100, mload(T4_X_LOC))
-
-                challenge := keccak256(0x00, 0x120)
-
-                mstore(C_ZETA_LOC, mod(challenge, p))
-                mstore(C_CURRENT_LOC, challenge)
-            }
-
-            /**
-             * EVALUATE FIELD OPERATIONS
-             */
-
-            /**
-             * COMPUTE PUBLIC INPUT DELTA
-             * ΔPI = ∏ᵢ∈ℓ(wᵢ + β σ(i) + γ) / ∏ᵢ∈ℓ(wᵢ + β σ'(i) + γ)
-             */
-            {
-                let beta := mload(C_BETA_LOC) // β
-                let gamma := mload(C_GAMMA_LOC) // γ
-                let work_root := mload(OMEGA_LOC) // ω
-                let numerator_value := 1
-                let denominator_value := 1
-
-                let p_clone := p // move p to the front of the stack
-                let valid_inputs := true
-
-                // Load the starting point of the public inputs (jump over the selector and the length of public inputs [0x24])
-                let public_inputs_ptr := add(calldataload(0x24), 0x24)
-
-                // endpoint_ptr = public_inputs_ptr + num_inputs * 0x20. // every public input is 0x20 bytes
-                let endpoint_ptr := add(public_inputs_ptr, mul(mload(NUM_INPUTS_LOC), 0x20))
-
-                // root_1 = β * 0x05
-                let root_1 := mulmod(beta, 0x05, p_clone) // k1.β
-                // root_2 = β * 0x0c
-                let root_2 := mulmod(beta, 0x0c, p_clone)
-                // @note 0x05 + 0x07 == 0x0c == external coset generator
-
-                for {} lt(public_inputs_ptr, endpoint_ptr) { public_inputs_ptr := add(public_inputs_ptr, 0x20) } {
-                    /**
-                     * input = public_input[i]
-                     * valid_inputs &= input < p
-                     * temp = input + gamma
-                     * numerator_value *= (β.σ(i) + wᵢ + γ)  // σ(i) = 0x05.ωⁱ
-                     * denominator_value *= (β.σ'(i) + wᵢ + γ) // σ'(i) = 0x0c.ωⁱ
-                     * root_1 *= ω
-                     * root_2 *= ω
-                     */
-
-                    let input := calldataload(public_inputs_ptr)
-                    valid_inputs := and(valid_inputs, lt(input, p_clone))
-                    let temp := addmod(input, gamma, p_clone)
-
-                    numerator_value := mulmod(numerator_value, add(root_1, temp), p_clone)
-                    denominator_value := mulmod(denominator_value, add(root_2, temp), p_clone)
-
-                    root_1 := mulmod(root_1, work_root, p_clone)
-                    root_2 := mulmod(root_2, work_root, p_clone)
-                }
-
-                // Revert if not all public inputs are field elements (i.e. < p)
-                if iszero(valid_inputs) {
-                    mstore(0x00, PUBLIC_INPUT_GE_P_SELECTOR)
-                    revert(0x00, 0x04)
-                }
-
-                mstore(DELTA_NUMERATOR_LOC, numerator_value)
-                mstore(DELTA_DENOMINATOR_LOC, denominator_value)
-            }
-
-            /**
-             * Compute Plookup delta factor [γ(1 + β)]^{n-k}
-             * k = num roots cut out of Z_H = 4
-             */
-            {
-                let delta_base := mulmod(mload(C_GAMMA_LOC), addmod(mload(C_BETA_LOC), 1, p), p)
-                let delta_numerator := delta_base
-                {
-                    let exponent := mload(N_LOC)
-                    let count := 1
-                    for {} lt(count, exponent) { count := add(count, count) } {
-                        delta_numerator := mulmod(delta_numerator, delta_numerator, p)
-                    }
-                }
-                mstore(PLOOKUP_DELTA_NUMERATOR_LOC, delta_numerator)
-
-                let delta_denominator := mulmod(delta_base, delta_base, p)
-                delta_denominator := mulmod(delta_denominator, delta_denominator, p)
-                mstore(PLOOKUP_DELTA_DENOMINATOR_LOC, delta_denominator)
-            }
-            /**
-             * Compute lagrange poly and vanishing poly fractions
-             */
-            {
-                /**
-                 * vanishing_numerator = zeta
-                 * ZETA_POW_N = zeta^n
-                 * vanishing_numerator -= 1
-                 * accumulating_root = omega_inverse
-                 * work_root = p - accumulating_root
-                 * domain_inverse = domain_inverse
-                 * vanishing_denominator = zeta + work_root
-                 * work_root *= accumulating_root
-                 * vanishing_denominator *= (zeta + work_root)
-                 * work_root *= accumulating_root
-                 * vanishing_denominator *= (zeta + work_root)
-                 * vanishing_denominator *= (zeta + (zeta + accumulating_root))
-                 * work_root = omega
-                 * lagrange_numerator = vanishing_numerator * domain_inverse
-                 * l_start_denominator = zeta - 1
-                 * accumulating_root = work_root^2
-                 * l_end_denominator = accumulating_root^2 * work_root * zeta - 1
-                 * Note: l_end_denominator term contains a term \omega^5 to cut out 5 roots of unity from vanishing poly
-                 */
-
-                let zeta := mload(C_ZETA_LOC)
-
-                // compute zeta^n, where n is a power of 2
-                let vanishing_numerator := zeta
-                {
-                    // pow_small
-                    let exponent := mload(N_LOC)
-                    let count := 1
-                    for {} lt(count, exponent) { count := add(count, count) } {
-                        vanishing_numerator := mulmod(vanishing_numerator, vanishing_numerator, p)
-                    }
-                }
-                mstore(ZETA_POW_N_LOC, vanishing_numerator)
-                vanishing_numerator := addmod(vanishing_numerator, sub(p, 1), p)
-
-                let accumulating_root := mload(OMEGA_INVERSE_LOC)
-                let work_root := sub(p, accumulating_root)
-                let domain_inverse := mload(DOMAIN_INVERSE_LOC)
-
-                let vanishing_denominator := addmod(zeta, work_root, p)
-                work_root := mulmod(work_root, accumulating_root, p)
-                vanishing_denominator := mulmod(vanishing_denominator, addmod(zeta, work_root, p), p)
-                work_root := mulmod(work_root, accumulating_root, p)
-                vanishing_denominator := mulmod(vanishing_denominator, addmod(zeta, work_root, p), p)
-                vanishing_denominator :=
-                    mulmod(vanishing_denominator, addmod(zeta, mulmod(work_root, accumulating_root, p), p), p)
-
-                work_root := mload(OMEGA_LOC)
-
-                let lagrange_numerator := mulmod(vanishing_numerator, domain_inverse, p)
-                let l_start_denominator := addmod(zeta, sub(p, 1), p)
-
-                accumulating_root := mulmod(work_root, work_root, p)
-
-                let l_end_denominator :=
-                    addmod(
-                        mulmod(mulmod(mulmod(accumulating_root, accumulating_root, p), work_root, p), zeta, p), sub(p, 1), p
-                    )
-
-                /**
-                 * Compute inversions using Montgomery's batch inversion trick
-                 */
-                let accumulator := mload(DELTA_DENOMINATOR_LOC)
-                let t0 := accumulator
-                accumulator := mulmod(accumulator, vanishing_denominator, p)
-                let t1 := accumulator
-                accumulator := mulmod(accumulator, vanishing_numerator, p)
-                let t2 := accumulator
-                accumulator := mulmod(accumulator, l_start_denominator, p)
-                let t3 := accumulator
-                accumulator := mulmod(accumulator, mload(PLOOKUP_DELTA_DENOMINATOR_LOC), p)
-                let t4 := accumulator
-                {
-                    mstore(0, 0x20)
-                    mstore(0x20, 0x20)
-                    mstore(0x40, 0x20)
-                    mstore(0x60, mulmod(accumulator, l_end_denominator, p))
-                    mstore(0x80, sub(p, 2))
-                    mstore(0xa0, p)
-                    if iszero(staticcall(gas(), 0x05, 0x00, 0xc0, 0x00, 0x20)) {
-                        mstore(0x0, MOD_EXP_FAILURE_SELECTOR)
-                        revert(0x00, 0x04)
-                    }
-                    accumulator := mload(0x00)
-                }
-
-                t4 := mulmod(accumulator, t4, p)
-                accumulator := mulmod(accumulator, l_end_denominator, p)
-
-                t3 := mulmod(accumulator, t3, p)
-                accumulator := mulmod(accumulator, mload(PLOOKUP_DELTA_DENOMINATOR_LOC), p)
-
-                t2 := mulmod(accumulator, t2, p)
-                accumulator := mulmod(accumulator, l_start_denominator, p)
-
-                t1 := mulmod(accumulator, t1, p)
-                accumulator := mulmod(accumulator, vanishing_numerator, p)
-
-                t0 := mulmod(accumulator, t0, p)
-                accumulator := mulmod(accumulator, vanishing_denominator, p)
-
-                accumulator := mulmod(mulmod(accumulator, accumulator, p), mload(DELTA_DENOMINATOR_LOC), p)
-
-                mstore(PUBLIC_INPUT_DELTA_LOC, mulmod(mload(DELTA_NUMERATOR_LOC), accumulator, p))
-                mstore(ZERO_POLY_LOC, mulmod(vanishing_numerator, t0, p))
-                mstore(ZERO_POLY_INVERSE_LOC, mulmod(vanishing_denominator, t1, p))
-                mstore(L_START_LOC, mulmod(lagrange_numerator, t2, p))
-                mstore(PLOOKUP_DELTA_LOC, mulmod(mload(PLOOKUP_DELTA_NUMERATOR_LOC), t3, p))
-                mstore(L_END_LOC, mulmod(lagrange_numerator, t4, p))
-            }
-
-            /**
-             * UltraPlonk Widget Ordering:
-             *
-             * 1. Permutation widget
-             * 2. Plookup widget
-             * 3. Arithmetic widget
-             * 4. Fixed base widget (?)
-             * 5. GenPermSort widget
-             * 6. Elliptic widget
-             * 7. Auxiliary widget
-             */
-
-            /**
-             * COMPUTE PERMUTATION WIDGET EVALUATION
-             */
-            {
-                let alpha := mload(C_ALPHA_LOC)
-                let beta := mload(C_BETA_LOC)
-                let gamma := mload(C_GAMMA_LOC)
-
-                /**
-                 * t1 = (W1 + gamma + beta * ID1) * (W2 + gamma + beta * ID2)
-                 * t2 = (W3 + gamma + beta * ID3) * (W4 + gamma + beta * ID4)
-                 * result = alpha_base * z_eval * t1 * t2
-                 * t1 = (W1 + gamma + beta * sigma_1_eval) * (W2 + gamma + beta * sigma_2_eval)
-                 * t2 = (W2 + gamma + beta * sigma_3_eval) * (W3 + gamma + beta * sigma_4_eval)
-                 * result -= (alpha_base * z_omega_eval * t1 * t2)
-                 */
-                let t1 :=
-                    mulmod(
-                        add(add(mload(W1_EVAL_LOC), gamma), mulmod(beta, mload(ID1_EVAL_LOC), p)),
-                        add(add(mload(W2_EVAL_LOC), gamma), mulmod(beta, mload(ID2_EVAL_LOC), p)),
-                        p
-                    )
-                let t2 :=
-                    mulmod(
-                        add(add(mload(W3_EVAL_LOC), gamma), mulmod(beta, mload(ID3_EVAL_LOC), p)),
-                        add(add(mload(W4_EVAL_LOC), gamma), mulmod(beta, mload(ID4_EVAL_LOC), p)),
-                        p
-                    )
-                let result := mulmod(mload(C_ALPHA_BASE_LOC), mulmod(mload(Z_EVAL_LOC), mulmod(t1, t2, p), p), p)
-                t1 :=
-                    mulmod(
-                        add(add(mload(W1_EVAL_LOC), gamma), mulmod(beta, mload(SIGMA1_EVAL_LOC), p)),
-                        add(add(mload(W2_EVAL_LOC), gamma), mulmod(beta, mload(SIGMA2_EVAL_LOC), p)),
-                        p
-                    )
-                t2 :=
-                    mulmod(
-                        add(add(mload(W3_EVAL_LOC), gamma), mulmod(beta, mload(SIGMA3_EVAL_LOC), p)),
-                        add(add(mload(W4_EVAL_LOC), gamma), mulmod(beta, mload(SIGMA4_EVAL_LOC), p)),
-                        p
-                    )
-                result :=
-                    addmod(
-                        result,
-                        sub(p, mulmod(mload(C_ALPHA_BASE_LOC), mulmod(mload(Z_OMEGA_EVAL_LOC), mulmod(t1, t2, p), p), p)),
-                        p
-                    )
-
-                /**
-                 * alpha_base *= alpha
-                 * result += alpha_base . (L_{n-k}(ʓ) . (z(ʓ.ω) - ∆_{PI}))
-                 * alpha_base *= alpha
-                 * result += alpha_base . (L_1(ʓ)(Z(ʓ) - 1))
-                 * alpha_Base *= alpha
-                 */
-                mstore(C_ALPHA_BASE_LOC, mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_LOC), p))
-                result :=
-                    addmod(
-                        result,
-                        mulmod(
-                            mload(C_ALPHA_BASE_LOC),
-                            mulmod(
-                                mload(L_END_LOC),
-                                addmod(mload(Z_OMEGA_EVAL_LOC), sub(p, mload(PUBLIC_INPUT_DELTA_LOC)), p),
-                                p
-                            ),
-                            p
-                        ),
-                        p
-                    )
-                mstore(C_ALPHA_BASE_LOC, mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_LOC), p))
-                mstore(
-                    PERMUTATION_IDENTITY,
-                    addmod(
-                        result,
-                        mulmod(
-                            mload(C_ALPHA_BASE_LOC),
-                            mulmod(mload(L_START_LOC), addmod(mload(Z_EVAL_LOC), sub(p, 1), p), p),
-                            p
-                        ),
-                        p
-                    )
-                )
-                mstore(C_ALPHA_BASE_LOC, mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_LOC), p))
-            }
-
-            /**
-             * COMPUTE PLOOKUP WIDGET EVALUATION
-             */
-            {
-                /**
-                 * Goal: f = (w1(z) + q2.w1(zω)) + η(w2(z) + qm.w2(zω)) + η²(w3(z) + qc.w_3(zω)) + q3(z).η³
-                 * f = η.q3(z)
-                 * f += (w3(z) + qc.w_3(zω))
-                 * f *= η
-                 * f += (w2(z) + qm.w2(zω))
-                 * f *= η
-                 * f += (w1(z) + q2.w1(zω))
-                 */
-                let f := mulmod(mload(C_ETA_LOC), mload(Q3_EVAL_LOC), p)
-                f :=
-                    addmod(f, addmod(mload(W3_EVAL_LOC), mulmod(mload(QC_EVAL_LOC), mload(W3_OMEGA_EVAL_LOC), p), p), p)
-                f := mulmod(f, mload(C_ETA_LOC), p)
-                f :=
-                    addmod(f, addmod(mload(W2_EVAL_LOC), mulmod(mload(QM_EVAL_LOC), mload(W2_OMEGA_EVAL_LOC), p), p), p)
-                f := mulmod(f, mload(C_ETA_LOC), p)
-                f :=
-                    addmod(f, addmod(mload(W1_EVAL_LOC), mulmod(mload(Q2_EVAL_LOC), mload(W1_OMEGA_EVAL_LOC), p), p), p)
-
-                // t(z) = table4(z).η³ + table3(z).η² + table2(z).η + table1(z)
-                let t :=
-                    addmod(
-                        addmod(
-                            addmod(
-                                mulmod(mload(TABLE4_EVAL_LOC), mload(C_ETA_CUBE_LOC), p),
-                                mulmod(mload(TABLE3_EVAL_LOC), mload(C_ETA_SQR_LOC), p),
-                                p
-                            ),
-                            mulmod(mload(TABLE2_EVAL_LOC), mload(C_ETA_LOC), p),
-                            p
-                        ),
-                        mload(TABLE1_EVAL_LOC),
-                        p
-                    )
-
-                // t(zw) = table4(zw).η³ + table3(zw).η² + table2(zw).η + table1(zw)
-                let t_omega :=
-                    addmod(
-                        addmod(
-                            addmod(
-                                mulmod(mload(TABLE4_OMEGA_EVAL_LOC), mload(C_ETA_CUBE_LOC), p),
-                                mulmod(mload(TABLE3_OMEGA_EVAL_LOC), mload(C_ETA_SQR_LOC), p),
-                                p
-                            ),
-                            mulmod(mload(TABLE2_OMEGA_EVAL_LOC), mload(C_ETA_LOC), p),
-                            p
-                        ),
-                        mload(TABLE1_OMEGA_EVAL_LOC),
-                        p
-                    )
-
-                /**
-                 * Goal: numerator = (TABLE_TYPE_EVAL * f(z) + γ) * (t(z) + βt(zω) + γ(β + 1)) * (β + 1)
-                 * gamma_beta_constant = γ(β + 1)
-                 * numerator = f * TABLE_TYPE_EVAL + gamma
-                 * temp0 = t(z) + t(zω) * β + gamma_beta_constant
-                 * numerator *= temp0
-                 * numerator *= (β + 1)
-                 * temp0 = alpha * l_1
-                 * numerator += temp0
-                 * numerator *= z_lookup(z)
-                 * numerator -= temp0
-                 */
-                let gamma_beta_constant := mulmod(mload(C_GAMMA_LOC), addmod(mload(C_BETA_LOC), 1, p), p)
-                let numerator := addmod(mulmod(f, mload(TABLE_TYPE_EVAL_LOC), p), mload(C_GAMMA_LOC), p)
-                let temp0 := addmod(addmod(t, mulmod(t_omega, mload(C_BETA_LOC), p), p), gamma_beta_constant, p)
-                numerator := mulmod(numerator, temp0, p)
-                numerator := mulmod(numerator, addmod(mload(C_BETA_LOC), 1, p), p)
-                temp0 := mulmod(mload(C_ALPHA_LOC), mload(L_START_LOC), p)
-                numerator := addmod(numerator, temp0, p)
-                numerator := mulmod(numerator, mload(Z_LOOKUP_EVAL_LOC), p)
-                numerator := addmod(numerator, sub(p, temp0), p)
-
-                /**
-                 * Goal: denominator = z_lookup(zω)*[s(z) + βs(zω) + γ(1 + β)] - [z_lookup(zω) - [γ(1 + β)]^{n-k}]*α²L_end(z)
-                 * note: delta_factor = [γ(1 + β)]^{n-k}
-                 * denominator = s(z) + βs(zω) + γ(β + 1)
-                 * temp1 = α²L_end(z)
-                 * denominator -= temp1
-                 * denominator *= z_lookup(zω)
-                 * denominator += temp1 * delta_factor
-                 * PLOOKUP_IDENTITY = (numerator - denominator).alpha_base
-                 * alpha_base *= alpha^3
-                 */
-                let denominator :=
-                    addmod(
-                        addmod(mload(S_EVAL_LOC), mulmod(mload(S_OMEGA_EVAL_LOC), mload(C_BETA_LOC), p), p),
-                        gamma_beta_constant,
-                        p
-                    )
-                let temp1 := mulmod(mload(C_ALPHA_SQR_LOC), mload(L_END_LOC), p)
-                denominator := addmod(denominator, sub(p, temp1), p)
-                denominator := mulmod(denominator, mload(Z_LOOKUP_OMEGA_EVAL_LOC), p)
-                denominator := addmod(denominator, mulmod(temp1, mload(PLOOKUP_DELTA_LOC), p), p)
-
-                mstore(PLOOKUP_IDENTITY, mulmod(addmod(numerator, sub(p, denominator), p), mload(C_ALPHA_BASE_LOC), p))
-
-                // update alpha
-                mstore(C_ALPHA_BASE_LOC, mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_CUBE_LOC), p))
-            }
-
-            /**
-             * COMPUTE ARITHMETIC WIDGET EVALUATION
-             */
-            {
-                /**
-                 * The basic arithmetic gate identity in standard plonk is as follows.
-                 * (w_1 . w_2 . q_m) + (w_1 . q_1) + (w_2 . q_2) + (w_3 . q_3) + (w_4 . q_4) + q_c = 0
-                 * However, for Ultraplonk, we extend this to support "passing" wires between rows (shown without alpha scaling below):
-                 * q_arith * ( ( (-1/2) * (q_arith - 3) * q_m * w_1 * w_2 + q_1 * w_1 + q_2 * w_2 + q_3 * w_3 + q_4 * w_4 + q_c ) +
-                 * (q_arith - 1)*( α * (q_arith - 2) * (w_1 + w_4 - w_1_omega + q_m) + w_4_omega) ) = 0
-                 *
-                 * This formula results in several cases depending on q_arith:
-                 * 1. q_arith == 0: Arithmetic gate is completely disabled
-                 *
-                 * 2. q_arith == 1: Everything in the minigate on the right is disabled. The equation is just a standard plonk equation
-                 * with extra wires: q_m * w_1 * w_2 + q_1 * w_1 + q_2 * w_2 + q_3 * w_3 + q_4 * w_4 + q_c = 0
-                 *
-                 * 3. q_arith == 2: The (w_1 + w_4 - ...) term is disabled. THe equation is:
-                 * (1/2) * q_m * w_1 * w_2 + q_1 * w_1 + q_2 * w_2 + q_3 * w_3 + q_4 * w_4 + q_c + w_4_omega = 0
-                 * It allows defining w_4 at next index (w_4_omega) in terms of current wire values
-                 *
-                 * 4. q_arith == 3: The product of w_1 and w_2 is disabled, but a mini addition gate is enabled. α allows us to split
-                 * the equation into two:
-                 *
-                 * q_1 * w_1 + q_2 * w_2 + q_3 * w_3 + q_4 * w_4 + q_c + 2 * w_4_omega = 0
-                 * and
-                 * w_1 + w_4 - w_1_omega + q_m = 0  (we are reusing q_m here)
-                 *
-                 * 5. q_arith > 3: The product of w_1 and w_2 is scaled by (q_arith - 3), while the w_4_omega term is scaled by (q_arith - 1).
-                 * The equation can be split into two:
-                 *
-                 * (q_arith - 3)* q_m * w_1 * w_ 2 + q_1 * w_1 + q_2 * w_2 + q_3 * w_3 + q_4 * w_4 + q_c + (q_arith - 1) * w_4_omega = 0
-                 * and
-                 * w_1 + w_4 - w_1_omega + q_m = 0
-                 *
-                 * The problem that q_m is used both in both equations can be dealt with by appropriately changing selector values at
-                 * the next gate. Then we can treat (q_arith - 1) as a simulated q_6 selector and scale q_m to handle (q_arith - 3) at
-                 * product.
-                 */
-
-                let w1q1 := mulmod(mload(W1_EVAL_LOC), mload(Q1_EVAL_LOC), p)
-                let w2q2 := mulmod(mload(W2_EVAL_LOC), mload(Q2_EVAL_LOC), p)
-                let w3q3 := mulmod(mload(W3_EVAL_LOC), mload(Q3_EVAL_LOC), p)
-                let w4q3 := mulmod(mload(W4_EVAL_LOC), mload(Q4_EVAL_LOC), p)
-
-                // @todo - Add a explicit test that hits QARITH == 3
-                // w1w2qm := (w_1 . w_2 . q_m . (QARITH_EVAL_LOC - 3)) / 2
-                let w1w2qm :=
-                    mulmod(
-                        mulmod(
-                            mulmod(mulmod(mload(W1_EVAL_LOC), mload(W2_EVAL_LOC), p), mload(QM_EVAL_LOC), p),
-                            addmod(mload(QARITH_EVAL_LOC), sub(p, 3), p),
-                            p
-                        ),
-                        NEGATIVE_INVERSE_OF_2_MODULO_P,
-                        p
-                    )
-
-                // (w_1 . w_2 . q_m . (q_arith - 3)) / -2) + (w_1 . q_1) + (w_2 . q_2) + (w_3 . q_3) + (w_4 . q_4) + q_c
-                let identity :=
-                    addmod(
-                        mload(QC_EVAL_LOC), addmod(w4q3, addmod(w3q3, addmod(w2q2, addmod(w1q1, w1w2qm, p), p), p), p), p
-                    )
-
-                // if q_arith == 3 we evaluate an additional mini addition gate (on top of the regular one), where:
-                // w_1 + w_4 - w_1_omega + q_m = 0
-                // we use this gate to save an addition gate when adding or subtracting non-native field elements
-                // α * (q_arith - 2) * (w_1 + w_4 - w_1_omega + q_m)
-                let extra_small_addition_gate_identity :=
-                    mulmod(
-                        mload(C_ALPHA_LOC),
-                        mulmod(
-                            addmod(mload(QARITH_EVAL_LOC), sub(p, 2), p),
-                            addmod(
-                                mload(QM_EVAL_LOC),
-                                addmod(
-                                    sub(p, mload(W1_OMEGA_EVAL_LOC)), addmod(mload(W1_EVAL_LOC), mload(W4_EVAL_LOC), p), p
-                                ),
-                                p
-                            ),
-                            p
-                        ),
-                        p
-                    )
-
-                // if q_arith == 2 OR q_arith == 3 we add the 4th wire of the NEXT gate into the arithmetic identity
-                // N.B. if q_arith > 2, this wire value will be scaled by (q_arith - 1) relative to the other gate wires!
-                // alpha_base * q_arith * (identity + (q_arith - 1) * (w_4_omega + extra_small_addition_gate_identity))
-                mstore(
-                    ARITHMETIC_IDENTITY,
-                    mulmod(
-                        mload(C_ALPHA_BASE_LOC),
-                        mulmod(
-                            mload(QARITH_EVAL_LOC),
-                            addmod(
-                                identity,
-                                mulmod(
-                                    addmod(mload(QARITH_EVAL_LOC), sub(p, 1), p),
-                                    addmod(mload(W4_OMEGA_EVAL_LOC), extra_small_addition_gate_identity, p),
-                                    p
-                                ),
-                                p
-                            ),
-                            p
-                        ),
-                        p
-                    )
-                )
-
-                // update alpha
-                mstore(C_ALPHA_BASE_LOC, mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_SQR_LOC), p))
-            }
-
-            /**
-             * COMPUTE GENPERMSORT WIDGET EVALUATION
-             */
-            {
-                /**
-                 * D1 = (w2 - w1)
-                 * D2 = (w3 - w2)
-                 * D3 = (w4 - w3)
-                 * D4 = (w1_omega - w4)
-                 *
-                 * α_a = alpha_base
-                 * α_b = alpha_base * α
-                 * α_c = alpha_base * α^2
-                 * α_d = alpha_base * α^3
-                 *
-                 * range_accumulator = (
-                 *   D1(D1 - 1)(D1 - 2)(D1 - 3).α_a +
-                 *   D2(D2 - 1)(D2 - 2)(D2 - 3).α_b +
-                 *   D3(D3 - 1)(D3 - 2)(D3 - 3).α_c +
-                 *   D4(D4 - 1)(D4 - 2)(D4 - 3).α_d +
-                 * ) . q_sort
-                 */
-                let minus_two := sub(p, 2)
-                let minus_three := sub(p, 3)
-                let d1 := addmod(mload(W2_EVAL_LOC), sub(p, mload(W1_EVAL_LOC)), p)
-                let d2 := addmod(mload(W3_EVAL_LOC), sub(p, mload(W2_EVAL_LOC)), p)
-                let d3 := addmod(mload(W4_EVAL_LOC), sub(p, mload(W3_EVAL_LOC)), p)
-                let d4 := addmod(mload(W1_OMEGA_EVAL_LOC), sub(p, mload(W4_EVAL_LOC)), p)
-
-                let range_accumulator :=
-                    mulmod(
-                        mulmod(
-                            mulmod(addmod(mulmod(d1, d1, p), sub(p, d1), p), addmod(d1, minus_two, p), p),
-                            addmod(d1, minus_three, p),
-                            p
-                        ),
-                        mload(C_ALPHA_BASE_LOC),
-                        p
-                    )
-                range_accumulator :=
-                    addmod(
-                        range_accumulator,
-                        mulmod(
-                            mulmod(
-                                mulmod(addmod(mulmod(d2, d2, p), sub(p, d2), p), addmod(d2, minus_two, p), p),
-                                addmod(d2, minus_three, p),
-                                p
-                            ),
-                            mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_LOC), p),
-                            p
-                        ),
-                        p
-                    )
-                range_accumulator :=
-                    addmod(
-                        range_accumulator,
-                        mulmod(
-                            mulmod(
-                                mulmod(addmod(mulmod(d3, d3, p), sub(p, d3), p), addmod(d3, minus_two, p), p),
-                                addmod(d3, minus_three, p),
-                                p
-                            ),
-                            mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_SQR_LOC), p),
-                            p
-                        ),
-                        p
-                    )
-                range_accumulator :=
-                    addmod(
-                        range_accumulator,
-                        mulmod(
-                            mulmod(
-                                mulmod(addmod(mulmod(d4, d4, p), sub(p, d4), p), addmod(d4, minus_two, p), p),
-                                addmod(d4, minus_three, p),
-                                p
-                            ),
-                            mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_CUBE_LOC), p),
-                            p
-                        ),
-                        p
-                    )
-                range_accumulator := mulmod(range_accumulator, mload(QSORT_EVAL_LOC), p)
-
-                mstore(SORT_IDENTITY, range_accumulator)
-
-                // update alpha
-                mstore(C_ALPHA_BASE_LOC, mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_QUAD_LOC), p))
-            }
-
-            /**
-             * COMPUTE ELLIPTIC WIDGET EVALUATION
-             */
-            {
-                /**
-                 * endo_term = (-x_2) * x_1 * (x_3 * 2 + x_1) * q_beta
-                 * endo_sqr_term = x_2^2
-                 * endo_sqr_term *= (x_3 - x_1)
-                 * endo_sqr_term *= q_beta^2
-                 * leftovers = x_2^2
-                 * leftovers *= x_2
-                 * leftovers += x_1^2 * (x_3 + x_1) @follow-up Invalid comment in BB widget
-                 * leftovers -= (y_2^2 + y_1^2)
-                 * sign_term = y_2 * y_1
-                 * sign_term += sign_term
-                 * sign_term *= q_sign
-                 */
-                // q_elliptic * (x3 + x2 + x1)(x2 - x1)(x2 - x1) - y2^2 - y1^2 + 2(y2y1)*q_sign = 0
-                let x_diff := addmod(mload(X2_EVAL_LOC), sub(p, mload(X1_EVAL_LOC)), p)
-                let y2_sqr := mulmod(mload(Y2_EVAL_LOC), mload(Y2_EVAL_LOC), p)
-                let y1_sqr := mulmod(mload(Y1_EVAL_LOC), mload(Y1_EVAL_LOC), p)
-                let y1y2 := mulmod(mulmod(mload(Y1_EVAL_LOC), mload(Y2_EVAL_LOC), p), mload(QSIGN_LOC), p)
-
-                let x_add_identity :=
-                    addmod(
-                        mulmod(
-                            addmod(mload(X3_EVAL_LOC), addmod(mload(X2_EVAL_LOC), mload(X1_EVAL_LOC), p), p),
-                            mulmod(x_diff, x_diff, p),
-                            p
-                        ),
-                        addmod(
-                            sub(
-                                p,
-                                addmod(y2_sqr, y1_sqr, p)
-                            ),
-                            addmod(y1y2, y1y2, p),
-                            p
-                        ),
-                        p
-                    )
-                x_add_identity :=
-                    mulmod(
-                        mulmod(
-                            x_add_identity,
-                            addmod(
-                                1,
-                                sub(p, mload(QM_EVAL_LOC)),
-                                p
-                            ),
-                            p
-                        ),
-                        mload(C_ALPHA_BASE_LOC),
-                        p
-                    )
-
-                // q_elliptic * (x3 + x2 + x1)(x2 - x1)(x2 - x1) - y2^2 - y1^2 + 2(y2y1)*q_sign = 0
-                let y1_plus_y3 := addmod(
-                    mload(Y1_EVAL_LOC),
-                    mload(Y3_EVAL_LOC),
-                    p
-                )
-                let y_diff := addmod(mulmod(mload(Y2_EVAL_LOC), mload(QSIGN_LOC), p), sub(p, mload(Y1_EVAL_LOC)), p)
-                let y_add_identity :=
-                    addmod(
-                        mulmod(y1_plus_y3, x_diff, p),
-                        mulmod(addmod(mload(X3_EVAL_LOC), sub(p, mload(X1_EVAL_LOC)), p), y_diff, p),
-                        p
-                    )
-                y_add_identity :=
-                    mulmod(
-                        mulmod(y_add_identity, addmod(1, sub(p, mload(QM_EVAL_LOC)), p), p),
-                        mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_LOC), p),
-                        p
-                    )
-
-                // ELLIPTIC_IDENTITY = (x_identity + y_identity) * Q_ELLIPTIC_EVAL
-                mstore(
-                    ELLIPTIC_IDENTITY, mulmod(addmod(x_add_identity, y_add_identity, p), mload(QELLIPTIC_EVAL_LOC), p)
-                )
-            }
-            {
-                /**
-                 * x_pow_4 = (y_1_sqr - curve_b) * x_1;
-                 * y_1_sqr_mul_4 = y_1_sqr + y_1_sqr;
-                 * y_1_sqr_mul_4 += y_1_sqr_mul_4;
-                 * x_1_pow_4_mul_9 = x_pow_4;
-                 * x_1_pow_4_mul_9 += x_1_pow_4_mul_9;
-                 * x_1_pow_4_mul_9 += x_1_pow_4_mul_9;
-                 * x_1_pow_4_mul_9 += x_1_pow_4_mul_9;
-                 * x_1_pow_4_mul_9 += x_pow_4;
-                 * x_1_sqr_mul_3 = x_1_sqr + x_1_sqr + x_1_sqr;
-                 * x_double_identity = (x_3 + x_1 + x_1) * y_1_sqr_mul_4 - x_1_pow_4_mul_9;
-                 * y_double_identity = x_1_sqr_mul_3 * (x_1 - x_3) - (y_1 + y_1) * (y_1 + y_3);
-                 */
-                // (x3 + x1 + x1) (4y1*y1) - 9 * x1 * x1 * x1 * x1 = 0
-                let x1_sqr := mulmod(mload(X1_EVAL_LOC), mload(X1_EVAL_LOC), p)
-                let y1_sqr := mulmod(mload(Y1_EVAL_LOC), mload(Y1_EVAL_LOC), p)
-                let x_pow_4 := mulmod(addmod(y1_sqr, GRUMPKIN_CURVE_B_PARAMETER_NEGATED, p), mload(X1_EVAL_LOC), p)
-                let y1_sqr_mul_4 := mulmod(y1_sqr, 4, p)
-                let x1_pow_4_mul_9 := mulmod(x_pow_4, 9, p)
-                let x1_sqr_mul_3 := mulmod(x1_sqr, 3, p)
-                let x_double_identity :=
-                    addmod(
-                        mulmod(
-                            addmod(mload(X3_EVAL_LOC), addmod(mload(X1_EVAL_LOC), mload(X1_EVAL_LOC), p), p),
-                            y1_sqr_mul_4,
-                            p
-                        ),
-                        sub(p, x1_pow_4_mul_9),
-                        p
-                    )
-                // (y1 + y1) (2y1) - (3 * x1 * x1)(x1 - x3) = 0
-                let y_double_identity :=
-                    addmod(
-                        mulmod(x1_sqr_mul_3, addmod(mload(X1_EVAL_LOC), sub(p, mload(X3_EVAL_LOC)), p), p),
-                        sub(
-                            p,
-                            mulmod(
-                                addmod(mload(Y1_EVAL_LOC), mload(Y1_EVAL_LOC), p),
-                                addmod(mload(Y1_EVAL_LOC), mload(Y3_EVAL_LOC), p),
-                                p
-                            )
-                        ),
-                        p
-                    )
-                x_double_identity := mulmod(x_double_identity, mload(C_ALPHA_BASE_LOC), p)
-                y_double_identity :=
-                    mulmod(y_double_identity, mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_LOC), p), p)
-                x_double_identity := mulmod(x_double_identity, mload(QM_EVAL_LOC), p)
-                y_double_identity := mulmod(y_double_identity, mload(QM_EVAL_LOC), p)
-                // ELLIPTIC_IDENTITY += (x_double_identity + y_double_identity) * Q_DOUBLE_EVAL
-                mstore(
-                    ELLIPTIC_IDENTITY,
-                    addmod(
-                        mload(ELLIPTIC_IDENTITY),
-                        mulmod(addmod(x_double_identity, y_double_identity, p), mload(QELLIPTIC_EVAL_LOC), p),
-                        p
-                    )
-                )
-
-                // update alpha
-                mstore(C_ALPHA_BASE_LOC, mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_QUAD_LOC), p))
-            }
-
-            /**
-             * COMPUTE AUXILIARY WIDGET EVALUATION
-             */
-            {
-                {
-                    /**
-                     * Non native field arithmetic gate 2
-                     *             _                                                                               _
-                     *            /   _                   _                               _       14                \
-                     * q_2 . q_4 |   (w_1 . w_2) + (w_1 . w_2) + (w_1 . w_4 + w_2 . w_3 - w_3) . 2    - w_3 - w_4   |
-                     *            \_                                                                               _/
-                     *
-                     * limb_subproduct = w_1 . w_2_omega + w_1_omega . w_2
-                     * non_native_field_gate_2 = w_1 * w_4 + w_4 * w_3 - w_3_omega
-                     * non_native_field_gate_2 = non_native_field_gate_2 * limb_size
-                     * non_native_field_gate_2 -= w_4_omega
-                     * non_native_field_gate_2 += limb_subproduct
-                     * non_native_field_gate_2 *= q_4
-                     * limb_subproduct *= limb_size
-                     * limb_subproduct += w_1_omega * w_2_omega
-                     * non_native_field_gate_1 = (limb_subproduct + w_3 + w_4) * q_3
-                     * non_native_field_gate_3 = (limb_subproduct + w_4 - (w_3_omega + w_4_omega)) * q_m
-                     * non_native_field_identity = (non_native_field_gate_1 + non_native_field_gate_2 + non_native_field_gate_3) * q_2
-                     */
-
-                    let limb_subproduct :=
-                        addmod(
-                            mulmod(mload(W1_EVAL_LOC), mload(W2_OMEGA_EVAL_LOC), p),
-                            mulmod(mload(W1_OMEGA_EVAL_LOC), mload(W2_EVAL_LOC), p),
-                            p
-                        )
-
-                    let non_native_field_gate_2 :=
-                        addmod(
-                            addmod(
-                                mulmod(mload(W1_EVAL_LOC), mload(W4_EVAL_LOC), p),
-                                mulmod(mload(W2_EVAL_LOC), mload(W3_EVAL_LOC), p),
-                                p
-                            ),
-                            sub(p, mload(W3_OMEGA_EVAL_LOC)),
-                            p
-                        )
-                    non_native_field_gate_2 := mulmod(non_native_field_gate_2, LIMB_SIZE, p)
-                    non_native_field_gate_2 := addmod(non_native_field_gate_2, sub(p, mload(W4_OMEGA_EVAL_LOC)), p)
-                    non_native_field_gate_2 := addmod(non_native_field_gate_2, limb_subproduct, p)
-                    non_native_field_gate_2 := mulmod(non_native_field_gate_2, mload(Q4_EVAL_LOC), p)
-                    limb_subproduct := mulmod(limb_subproduct, LIMB_SIZE, p)
-                    limb_subproduct :=
-                        addmod(limb_subproduct, mulmod(mload(W1_OMEGA_EVAL_LOC), mload(W2_OMEGA_EVAL_LOC), p), p)
-                    let non_native_field_gate_1 :=
-                        mulmod(
-                            addmod(limb_subproduct, sub(p, addmod(mload(W3_EVAL_LOC), mload(W4_EVAL_LOC), p)), p),
-                            mload(Q3_EVAL_LOC),
-                            p
-                        )
-                    let non_native_field_gate_3 :=
-                        mulmod(
-                            addmod(
-                                addmod(limb_subproduct, mload(W4_EVAL_LOC), p),
-                                sub(p, addmod(mload(W3_OMEGA_EVAL_LOC), mload(W4_OMEGA_EVAL_LOC), p)),
-                                p
-                            ),
-                            mload(QM_EVAL_LOC),
-                            p
-                        )
-                    let non_native_field_identity :=
-                        mulmod(
-                            addmod(addmod(non_native_field_gate_1, non_native_field_gate_2, p), non_native_field_gate_3, p),
-                            mload(Q2_EVAL_LOC),
-                            p
-                        )
-
-                    mstore(AUX_NON_NATIVE_FIELD_EVALUATION, non_native_field_identity)
-                }
-
-                {
-                    /**
-                     * limb_accumulator_1 = w_2_omega;
-                     * limb_accumulator_1 *= SUBLIMB_SHIFT;
-                     * limb_accumulator_1 += w_1_omega;
-                     * limb_accumulator_1 *= SUBLIMB_SHIFT;
-                     * limb_accumulator_1 += w_3;
-                     * limb_accumulator_1 *= SUBLIMB_SHIFT;
-                     * limb_accumulator_1 += w_2;
-                     * limb_accumulator_1 *= SUBLIMB_SHIFT;
-                     * limb_accumulator_1 += w_1;
-                     * limb_accumulator_1 -= w_4;
-                     * limb_accumulator_1 *= q_4;
-                     */
-                    let limb_accumulator_1 := mulmod(mload(W2_OMEGA_EVAL_LOC), SUBLIMB_SHIFT, p)
-                    limb_accumulator_1 := addmod(limb_accumulator_1, mload(W1_OMEGA_EVAL_LOC), p)
-                    limb_accumulator_1 := mulmod(limb_accumulator_1, SUBLIMB_SHIFT, p)
-                    limb_accumulator_1 := addmod(limb_accumulator_1, mload(W3_EVAL_LOC), p)
-                    limb_accumulator_1 := mulmod(limb_accumulator_1, SUBLIMB_SHIFT, p)
-                    limb_accumulator_1 := addmod(limb_accumulator_1, mload(W2_EVAL_LOC), p)
-                    limb_accumulator_1 := mulmod(limb_accumulator_1, SUBLIMB_SHIFT, p)
-                    limb_accumulator_1 := addmod(limb_accumulator_1, mload(W1_EVAL_LOC), p)
-                    limb_accumulator_1 := addmod(limb_accumulator_1, sub(p, mload(W4_EVAL_LOC)), p)
-                    limb_accumulator_1 := mulmod(limb_accumulator_1, mload(Q4_EVAL_LOC), p)
-
-                    /**
-                     * limb_accumulator_2 = w_3_omega;
-                     * limb_accumulator_2 *= SUBLIMB_SHIFT;
-                     * limb_accumulator_2 += w_2_omega;
-                     * limb_accumulator_2 *= SUBLIMB_SHIFT;
-                     * limb_accumulator_2 += w_1_omega;
-                     * limb_accumulator_2 *= SUBLIMB_SHIFT;
-                     * limb_accumulator_2 += w_4;
-                     * limb_accumulator_2 *= SUBLIMB_SHIFT;
-                     * limb_accumulator_2 += w_3;
-                     * limb_accumulator_2 -= w_4_omega;
-                     * limb_accumulator_2 *= q_m;
-                     */
-                    let limb_accumulator_2 := mulmod(mload(W3_OMEGA_EVAL_LOC), SUBLIMB_SHIFT, p)
-                    limb_accumulator_2 := addmod(limb_accumulator_2, mload(W2_OMEGA_EVAL_LOC), p)
-                    limb_accumulator_2 := mulmod(limb_accumulator_2, SUBLIMB_SHIFT, p)
-                    limb_accumulator_2 := addmod(limb_accumulator_2, mload(W1_OMEGA_EVAL_LOC), p)
-                    limb_accumulator_2 := mulmod(limb_accumulator_2, SUBLIMB_SHIFT, p)
-                    limb_accumulator_2 := addmod(limb_accumulator_2, mload(W4_EVAL_LOC), p)
-                    limb_accumulator_2 := mulmod(limb_accumulator_2, SUBLIMB_SHIFT, p)
-                    limb_accumulator_2 := addmod(limb_accumulator_2, mload(W3_EVAL_LOC), p)
-                    limb_accumulator_2 := addmod(limb_accumulator_2, sub(p, mload(W4_OMEGA_EVAL_LOC)), p)
-                    limb_accumulator_2 := mulmod(limb_accumulator_2, mload(QM_EVAL_LOC), p)
-
-                    mstore(
-                        AUX_LIMB_ACCUMULATOR_EVALUATION,
-                        mulmod(addmod(limb_accumulator_1, limb_accumulator_2, p), mload(Q3_EVAL_LOC), p)
-                    )
-                }
-
-                {
-                    /**
-                     * memory_record_check = w_3;
-                     * memory_record_check *= eta;
-                     * memory_record_check += w_2;
-                     * memory_record_check *= eta;
-                     * memory_record_check += w_1;
-                     * memory_record_check *= eta;
-                     * memory_record_check += q_c;
-                     *
-                     * partial_record_check = memory_record_check;
-                     *
-                     * memory_record_check -= w_4;
-                     */
-
-                    let memory_record_check := mulmod(mload(W3_EVAL_LOC), mload(C_ETA_LOC), p)
-                    memory_record_check := addmod(memory_record_check, mload(W2_EVAL_LOC), p)
-                    memory_record_check := mulmod(memory_record_check, mload(C_ETA_LOC), p)
-                    memory_record_check := addmod(memory_record_check, mload(W1_EVAL_LOC), p)
-                    memory_record_check := mulmod(memory_record_check, mload(C_ETA_LOC), p)
-                    memory_record_check := addmod(memory_record_check, mload(QC_EVAL_LOC), p)
-
-                    let partial_record_check := memory_record_check
-                    memory_record_check := addmod(memory_record_check, sub(p, mload(W4_EVAL_LOC)), p)
-
-                    mstore(AUX_MEMORY_EVALUATION, memory_record_check)
-
-                    // index_delta = w_1_omega - w_1
-                    let index_delta := addmod(mload(W1_OMEGA_EVAL_LOC), sub(p, mload(W1_EVAL_LOC)), p)
-                    // record_delta = w_4_omega - w_4
-                    let record_delta := addmod(mload(W4_OMEGA_EVAL_LOC), sub(p, mload(W4_EVAL_LOC)), p)
-                    // index_is_monotonically_increasing = index_delta * (index_delta - 1)
-                    let index_is_monotonically_increasing := mulmod(index_delta, addmod(index_delta, sub(p, 1), p), p)
-
-                    // adjacent_values_match_if_adjacent_indices_match = record_delta * (1 - index_delta)
-                    let adjacent_values_match_if_adjacent_indices_match :=
-                        mulmod(record_delta, addmod(1, sub(p, index_delta), p), p)
-
-                    // AUX_ROM_CONSISTENCY_EVALUATION = ((adjacent_values_match_if_adjacent_indices_match * alpha) + index_is_monotonically_increasing) * alpha + partial_record_check
-                    mstore(
-                        AUX_ROM_CONSISTENCY_EVALUATION,
-                        addmod(
-                            mulmod(
-                                addmod(
-                                    mulmod(adjacent_values_match_if_adjacent_indices_match, mload(C_ALPHA_LOC), p),
-                                    index_is_monotonically_increasing,
-                                    p
-                                ),
-                                mload(C_ALPHA_LOC),
-                                p
-                            ),
-                            memory_record_check,
-                            p
-                        )
-                    )
-
-                    {
-                        /**
-                         * next_gate_access_type = w_3_omega;
-                         * next_gate_access_type *= eta;
-                         * next_gate_access_type += w_2_omega;
-                         * next_gate_access_type *= eta;
-                         * next_gate_access_type += w_1_omega;
-                         * next_gate_access_type *= eta;
-                         * next_gate_access_type = w_4_omega - next_gate_access_type;
-                         */
-                        let next_gate_access_type := mulmod(mload(W3_OMEGA_EVAL_LOC), mload(C_ETA_LOC), p)
-                        next_gate_access_type := addmod(next_gate_access_type, mload(W2_OMEGA_EVAL_LOC), p)
-                        next_gate_access_type := mulmod(next_gate_access_type, mload(C_ETA_LOC), p)
-                        next_gate_access_type := addmod(next_gate_access_type, mload(W1_OMEGA_EVAL_LOC), p)
-                        next_gate_access_type := mulmod(next_gate_access_type, mload(C_ETA_LOC), p)
-                        next_gate_access_type := addmod(mload(W4_OMEGA_EVAL_LOC), sub(p, next_gate_access_type), p)
-
-                        // value_delta = w_3_omega - w_3
-                        let value_delta := addmod(mload(W3_OMEGA_EVAL_LOC), sub(p, mload(W3_EVAL_LOC)), p)
-                        //  adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = (1 - index_delta) * value_delta * (1 - next_gate_access_type);
-
-                        let adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation :=
-                            mulmod(
-                                addmod(1, sub(p, index_delta), p),
-                                mulmod(value_delta, addmod(1, sub(p, next_gate_access_type), p), p),
-                                p
-                            )
-
-                        // AUX_RAM_CONSISTENCY_EVALUATION
-
-                        /**
-                         * access_type = w_4 - partial_record_check
-                         * access_check = access_type^2 - access_type
-                         * next_gate_access_type_is_boolean = next_gate_access_type^2 - next_gate_access_type
-                         * RAM_consistency_check_identity = adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation;
-                         * RAM_consistency_check_identity *= alpha;
-                         * RAM_consistency_check_identity += index_is_monotonically_increasing;
-                         * RAM_consistency_check_identity *= alpha;
-                         * RAM_consistency_check_identity += next_gate_access_type_is_boolean;
-                         * RAM_consistency_check_identity *= alpha;
-                         * RAM_consistency_check_identity += access_check;
-                         */
-
-                        let access_type := addmod(mload(W4_EVAL_LOC), sub(p, partial_record_check), p)
-                        let access_check := mulmod(access_type, addmod(access_type, sub(p, 1), p), p)
-                        let next_gate_access_type_is_boolean :=
-                            mulmod(next_gate_access_type, addmod(next_gate_access_type, sub(p, 1), p), p)
-                        let RAM_cci :=
-                            mulmod(
-                                adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation,
-                                mload(C_ALPHA_LOC),
-                                p
-                            )
-                        RAM_cci := addmod(RAM_cci, index_is_monotonically_increasing, p)
-                        RAM_cci := mulmod(RAM_cci, mload(C_ALPHA_LOC), p)
-                        RAM_cci := addmod(RAM_cci, next_gate_access_type_is_boolean, p)
-                        RAM_cci := mulmod(RAM_cci, mload(C_ALPHA_LOC), p)
-                        RAM_cci := addmod(RAM_cci, access_check, p)
-
-                        mstore(AUX_RAM_CONSISTENCY_EVALUATION, RAM_cci)
-                    }
-
-                    {
-                        // timestamp_delta = w_2_omega - w_2
-                        let timestamp_delta := addmod(mload(W2_OMEGA_EVAL_LOC), sub(p, mload(W2_EVAL_LOC)), p)
-
-                        // RAM_timestamp_check_identity = (1 - index_delta) * timestamp_delta - w_3
-                        let RAM_timestamp_check_identity :=
-                            addmod(
-                                mulmod(timestamp_delta, addmod(1, sub(p, index_delta), p), p), sub(p, mload(W3_EVAL_LOC)), p
-                            )
-
-                        /**
-                         * memory_identity = ROM_consistency_check_identity * q_2;
-                         * memory_identity += RAM_timestamp_check_identity * q_4;
-                         * memory_identity += memory_record_check * q_m;
-                         * memory_identity *= q_1;
-                         * memory_identity += (RAM_consistency_check_identity * q_arith);
-                         *
-                         * auxiliary_identity = memory_identity + non_native_field_identity + limb_accumulator_identity;
-                         * auxiliary_identity *= q_aux;
-                         * auxiliary_identity *= alpha_base;
-                         */
-                        let memory_identity := mulmod(mload(AUX_ROM_CONSISTENCY_EVALUATION), mload(Q2_EVAL_LOC), p)
-                        memory_identity :=
-                            addmod(memory_identity, mulmod(RAM_timestamp_check_identity, mload(Q4_EVAL_LOC), p), p)
-                        memory_identity :=
-                            addmod(memory_identity, mulmod(mload(AUX_MEMORY_EVALUATION), mload(QM_EVAL_LOC), p), p)
-                        memory_identity := mulmod(memory_identity, mload(Q1_EVAL_LOC), p)
-                        memory_identity :=
-                            addmod(
-                                memory_identity, mulmod(mload(AUX_RAM_CONSISTENCY_EVALUATION), mload(QARITH_EVAL_LOC), p), p
-                            )
-
-                        let auxiliary_identity := addmod(memory_identity, mload(AUX_NON_NATIVE_FIELD_EVALUATION), p)
-                        auxiliary_identity := addmod(auxiliary_identity, mload(AUX_LIMB_ACCUMULATOR_EVALUATION), p)
-                        auxiliary_identity := mulmod(auxiliary_identity, mload(QAUX_EVAL_LOC), p)
-                        auxiliary_identity := mulmod(auxiliary_identity, mload(C_ALPHA_BASE_LOC), p)
-
-                        mstore(AUX_IDENTITY, auxiliary_identity)
-
-                        // update alpha
-                        mstore(C_ALPHA_BASE_LOC, mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_CUBE_LOC), p))
-                    }
-                }
-            }
-
-            {
-                /**
-                 * quotient = ARITHMETIC_IDENTITY
-                 * quotient += PERMUTATION_IDENTITY
-                 * quotient += PLOOKUP_IDENTITY
-                 * quotient += SORT_IDENTITY
-                 * quotient += ELLIPTIC_IDENTITY
-                 * quotient += AUX_IDENTITY
-                 * quotient *= ZERO_POLY_INVERSE
-                 */
-                mstore(
-                    QUOTIENT_EVAL_LOC,
-                    mulmod(
-                        addmod(
-                            addmod(
-                                addmod(
-                                    addmod(
-                                        addmod(mload(PERMUTATION_IDENTITY), mload(PLOOKUP_IDENTITY), p),
-                                        mload(ARITHMETIC_IDENTITY),
-                                        p
-                                    ),
-                                    mload(SORT_IDENTITY),
-                                    p
-                                ),
-                                mload(ELLIPTIC_IDENTITY),
-                                p
-                            ),
-                            mload(AUX_IDENTITY),
-                            p
-                        ),
-                        mload(ZERO_POLY_INVERSE_LOC),
-                        p
-                    )
-                )
-            }
-
-            /**
-             * GENERATE NU AND SEPARATOR CHALLENGES
-             */
-            {
-                let current_challenge := mload(C_CURRENT_LOC)
-                // get a calldata pointer that points to the start of the data we want to copy
-                let calldata_ptr := add(calldataload(0x04), 0x24)
-
-                calldata_ptr := add(calldata_ptr, NU_CALLDATA_SKIP_LENGTH)
-
-                mstore(NU_CHALLENGE_INPUT_LOC_A, current_challenge)
-                mstore(NU_CHALLENGE_INPUT_LOC_B, mload(QUOTIENT_EVAL_LOC))
-                calldatacopy(NU_CHALLENGE_INPUT_LOC_C, calldata_ptr, NU_INPUT_LENGTH)
-
-                // hash length = (0x20 + num field elements), we include the previous challenge in the hash
-                let challenge := keccak256(NU_CHALLENGE_INPUT_LOC_A, add(NU_INPUT_LENGTH, 0x40))
-
-                mstore(C_V0_LOC, mod(challenge, p))
-                // We need THIRTY-ONE independent nu challenges!
-                mstore(0x00, challenge)
-                mstore8(0x20, 0x01)
-                mstore(C_V1_LOC, mod(keccak256(0x00, 0x21), p))
-                mstore8(0x20, 0x02)
-                mstore(C_V2_LOC, mod(keccak256(0x00, 0x21), p))
-                mstore8(0x20, 0x03)
-                mstore(C_V3_LOC, mod(keccak256(0x00, 0x21), p))
-                mstore8(0x20, 0x04)
-                mstore(C_V4_LOC, mod(keccak256(0x00, 0x21), p))
-                mstore8(0x20, 0x05)
-                mstore(C_V5_LOC, mod(keccak256(0x00, 0x21), p))
-                mstore8(0x20, 0x06)
-                mstore(C_V6_LOC, mod(keccak256(0x00, 0x21), p))
-                mstore8(0x20, 0x07)
-                mstore(C_V7_LOC, mod(keccak256(0x00, 0x21), p))
-                mstore8(0x20, 0x08)
-                mstore(C_V8_LOC, mod(keccak256(0x00, 0x21), p))
-                mstore8(0x20, 0x09)
-                mstore(C_V9_LOC, mod(keccak256(0x00, 0x21), p))
-                mstore8(0x20, 0x0a)
-                mstore(C_V10_LOC, mod(keccak256(0x00, 0x21), p))
-                mstore8(0x20, 0x0b)
-                mstore(C_V11_LOC, mod(keccak256(0x00, 0x21), p))
-                mstore8(0x20, 0x0c)
-                mstore(C_V12_LOC, mod(keccak256(0x00, 0x21), p))
-                mstore8(0x20, 0x0d)
-                mstore(C_V13_LOC, mod(keccak256(0x00, 0x21), p))
-                mstore8(0x20, 0x0e)
-                mstore(C_V14_LOC, mod(keccak256(0x00, 0x21), p))
-                mstore8(0x20, 0x0f)
-                mstore(C_V15_LOC, mod(keccak256(0x00, 0x21), p))
-                mstore8(0x20, 0x10)
-                mstore(C_V16_LOC, mod(keccak256(0x00, 0x21), p))
-                mstore8(0x20, 0x11)
-                mstore(C_V17_LOC, mod(keccak256(0x00, 0x21), p))
-                mstore8(0x20, 0x12)
-                mstore(C_V18_LOC, mod(keccak256(0x00, 0x21), p))
-                mstore8(0x20, 0x13)
-                mstore(C_V19_LOC, mod(keccak256(0x00, 0x21), p))
-                mstore8(0x20, 0x14)
-                mstore(C_V20_LOC, mod(keccak256(0x00, 0x21), p))
-                mstore8(0x20, 0x15)
-                mstore(C_V21_LOC, mod(keccak256(0x00, 0x21), p))
-                mstore8(0x20, 0x16)
-                mstore(C_V22_LOC, mod(keccak256(0x00, 0x21), p))
-                mstore8(0x20, 0x17)
-                mstore(C_V23_LOC, mod(keccak256(0x00, 0x21), p))
-                mstore8(0x20, 0x18)
-                mstore(C_V24_LOC, mod(keccak256(0x00, 0x21), p))
-                mstore8(0x20, 0x19)
-                mstore(C_V25_LOC, mod(keccak256(0x00, 0x21), p))
-                mstore8(0x20, 0x1a)
-                mstore(C_V26_LOC, mod(keccak256(0x00, 0x21), p))
-                mstore8(0x20, 0x1b)
-                mstore(C_V27_LOC, mod(keccak256(0x00, 0x21), p))
-                mstore8(0x20, 0x1c)
-                mstore(C_V28_LOC, mod(keccak256(0x00, 0x21), p))
-                mstore8(0x20, 0x1d)
-                mstore(C_V29_LOC, mod(keccak256(0x00, 0x21), p))
-
-                // @follow-up - Why are both v29 and v30 using appending 0x1d to the prior challenge and hashing, should it not change?
-                mstore8(0x20, 0x1d)
-                challenge := keccak256(0x00, 0x21)
-                mstore(C_V30_LOC, mod(challenge, p))
-
-                // separator
-                mstore(0x00, challenge)
-                mstore(0x20, mload(PI_Z_Y_LOC))
-                mstore(0x40, mload(PI_Z_X_LOC))
-                mstore(0x60, mload(PI_Z_OMEGA_Y_LOC))
-                mstore(0x80, mload(PI_Z_OMEGA_X_LOC))
-
-                mstore(C_U_LOC, mod(keccak256(0x00, 0xa0), p))
-            }
-
-            let success := 0
-            // VALIDATE T1
-            {
-                let x := mload(T1_X_LOC)
-                let y := mload(T1_Y_LOC)
-                let xx := mulmod(x, x, q)
-                // validate on curve
-                success := eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))
-                mstore(ACCUMULATOR_X_LOC, x)
-                mstore(add(ACCUMULATOR_X_LOC, 0x20), y)
-            }
-            // VALIDATE T2
-            {
-                let x := mload(T2_X_LOC) // 0x1400
-                let y := mload(T2_Y_LOC) // 0x1420
-                let xx := mulmod(x, x, q)
-                // validate on curve
-                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
-                mstore(0x00, x)
-                mstore(0x20, y)
-            }
-            mstore(0x40, mload(ZETA_POW_N_LOC))
-            // accumulator_2 = [T2].zeta^n
-            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
-            // accumulator = [T1] + accumulator_2
-            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
-
-            // VALIDATE T3
-            {
-                let x := mload(T3_X_LOC)
-                let y := mload(T3_Y_LOC)
-                let xx := mulmod(x, x, q)
-                // validate on curve
-                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
-                mstore(0x00, x)
-                mstore(0x20, y)
-            }
-            mstore(0x40, mulmod(mload(ZETA_POW_N_LOC), mload(ZETA_POW_N_LOC), p))
-            // accumulator_2 = [T3].zeta^{2n}
-            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
-            // accumulator = accumulator + accumulator_2
-            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
-
-            // VALIDATE T4
-            {
-                let x := mload(T4_X_LOC)
-                let y := mload(T4_Y_LOC)
-                let xx := mulmod(x, x, q)
-                // validate on curve
-                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
-                mstore(0x00, x)
-                mstore(0x20, y)
-            }
-            mstore(0x40, mulmod(mulmod(mload(ZETA_POW_N_LOC), mload(ZETA_POW_N_LOC), p), mload(ZETA_POW_N_LOC), p))
-            // accumulator_2 = [T4].zeta^{3n}
-            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
-            // accumulator = accumulator + accumulator_2
-            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
-
-            // VALIDATE W1
-            {
-                let x := mload(W1_X_LOC)
-                let y := mload(W1_Y_LOC)
-                let xx := mulmod(x, x, q)
-                // validate on curve
-                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
-                mstore(0x00, x)
-                mstore(0x20, y)
-            }
-            mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V0_LOC), p))
-            // accumulator_2 = v0.(u + 1).[W1]
-            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
-            // accumulator = accumulator + accumulator_2
-            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
-
-            // VALIDATE W2
-            {
-                let x := mload(W2_X_LOC)
-                let y := mload(W2_Y_LOC)
-                let xx := mulmod(x, x, q)
-                // validate on curve
-                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
-                mstore(0x00, x)
-                mstore(0x20, y)
-            }
-            mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V1_LOC), p))
-            // accumulator_2 = v1.(u + 1).[W2]
-            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
-            // accumulator = accumulator + accumulator_2
-            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
-
-            // VALIDATE W3
-            {
-                let x := mload(W3_X_LOC)
-                let y := mload(W3_Y_LOC)
-                let xx := mulmod(x, x, q)
-                // validate on curve
-                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
-                mstore(0x00, x)
-                mstore(0x20, y)
-            }
-            mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V2_LOC), p))
-            // accumulator_2 = v2.(u + 1).[W3]
-            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
-            // accumulator = accumulator + accumulator_2
-            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
-
-            // VALIDATE W4
-            {
-                let x := mload(W4_X_LOC)
-                let y := mload(W4_Y_LOC)
-                let xx := mulmod(x, x, q)
-                // validate on curve
-                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
-                mstore(0x00, x)
-                mstore(0x20, y)
-            }
-            mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V3_LOC), p))
-            // accumulator_2 = v3.(u + 1).[W4]
-            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
-            // accumulator = accumulator + accumulator_2
-            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
-
-            // VALIDATE S
-            {
-                let x := mload(S_X_LOC)
-                let y := mload(S_Y_LOC)
-                let xx := mulmod(x, x, q)
-                // validate on curve
-                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
-                mstore(0x00, x)
-                mstore(0x20, y)
-            }
-            mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V4_LOC), p))
-            // accumulator_2 = v4.(u + 1).[S]
-            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
-            // accumulator = accumulator + accumulator_2
-            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
-
-            // VALIDATE Z
-            {
-                let x := mload(Z_X_LOC)
-                let y := mload(Z_Y_LOC)
-                let xx := mulmod(x, x, q)
-                // validate on curve
-                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
-                mstore(0x00, x)
-                mstore(0x20, y)
-            }
-            mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V5_LOC), p))
-            // accumulator_2 = v5.(u + 1).[Z]
-            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
-            // accumulator = accumulator + accumulator_2
-            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
-
-            // VALIDATE Z_LOOKUP
-            {
-                let x := mload(Z_LOOKUP_X_LOC)
-                let y := mload(Z_LOOKUP_Y_LOC)
-                let xx := mulmod(x, x, q)
-                // validate on curve
-                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
-                mstore(0x00, x)
-                mstore(0x20, y)
-            }
-            mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V6_LOC), p))
-            // accumulator_2 = v6.(u + 1).[Z_LOOKUP]
-            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
-            // accumulator = accumulator + accumulator_2
-            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
-
-            // VALIDATE Q1
-            {
-                let x := mload(Q1_X_LOC)
-                let y := mload(Q1_Y_LOC)
-                let xx := mulmod(x, x, q)
-                // validate on curve
-                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
-                mstore(0x00, x)
-                mstore(0x20, y)
-            }
-            mstore(0x40, mload(C_V7_LOC))
-            // accumulator_2 = v7.[Q1]
-            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
-            // accumulator = accumulator + accumulator_2
-            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
-
-            // VALIDATE Q2
-            {
-                let x := mload(Q2_X_LOC)
-                let y := mload(Q2_Y_LOC)
-                let xx := mulmod(x, x, q)
-                // validate on curve
-                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
-                mstore(0x00, x)
-                mstore(0x20, y)
-            }
-            mstore(0x40, mload(C_V8_LOC))
-            // accumulator_2 = v8.[Q2]
-            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
-            // accumulator = accumulator + accumulator_2
-            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
-
-            // VALIDATE Q3
-            {
-                let x := mload(Q3_X_LOC)
-                let y := mload(Q3_Y_LOC)
-                let xx := mulmod(x, x, q)
-                // validate on curve
-                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
-                mstore(0x00, x)
-                mstore(0x20, y)
-            }
-            mstore(0x40, mload(C_V9_LOC))
-            // accumulator_2 = v9.[Q3]
-            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
-            // accumulator = accumulator + accumulator_2
-            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
-
-            // VALIDATE Q4
-            {
-                let x := mload(Q4_X_LOC)
-                let y := mload(Q4_Y_LOC)
-                let xx := mulmod(x, x, q)
-                // validate on curve
-                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
-                mstore(0x00, x)
-                mstore(0x20, y)
-            }
-            mstore(0x40, mload(C_V10_LOC))
-            // accumulator_2 = v10.[Q4]
-            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
-            // accumulator = accumulator + accumulator_2
-            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
-
-            // VALIDATE QM
-            {
-                let x := mload(QM_X_LOC)
-                let y := mload(QM_Y_LOC)
-                let xx := mulmod(x, x, q)
-                // validate on curve
-                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
-                mstore(0x00, x)
-                mstore(0x20, y)
-            }
-            mstore(0x40, mload(C_V11_LOC))
-            // accumulator_2 = v11.[Q;]
-            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
-            // accumulator = accumulator + accumulator_2
-            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
-
-            // VALIDATE QC
-            {
-                let x := mload(QC_X_LOC)
-                let y := mload(QC_Y_LOC)
-                let xx := mulmod(x, x, q)
-                // validate on curve
-                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
-                mstore(0x00, x)
-                mstore(0x20, y)
-            }
-            mstore(0x40, mload(C_V12_LOC))
-            // accumulator_2 = v12.[QC]
-            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
-            // accumulator = accumulator + accumulator_2
-            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
-
-            // VALIDATE QARITH
-            {
-                let x := mload(QARITH_X_LOC)
-                let y := mload(QARITH_Y_LOC)
-                let xx := mulmod(x, x, q)
-                // validate on curve
-                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
-                mstore(0x00, x)
-                mstore(0x20, y)
-            }
-            mstore(0x40, mload(C_V13_LOC))
-            // accumulator_2 = v13.[QARITH]
-            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
-            // accumulator = accumulator + accumulator_2
-            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
-
-            // VALIDATE QSORT
-            {
-                let x := mload(QSORT_X_LOC)
-                let y := mload(QSORT_Y_LOC)
-                let xx := mulmod(x, x, q)
-                // validate on curve
-                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
-                mstore(0x00, x)
-                mstore(0x20, y)
-            }
-            mstore(0x40, mload(C_V14_LOC))
-            // accumulator_2 = v14.[QSORT]
-            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
-            // accumulator = accumulator + accumulator_2
-            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
-
-            // VALIDATE QELLIPTIC
-            {
-                let x := mload(QELLIPTIC_X_LOC)
-                let y := mload(QELLIPTIC_Y_LOC)
-                let xx := mulmod(x, x, q)
-                // validate on curve
-                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
-                mstore(0x00, x)
-                mstore(0x20, y)
-            }
-            mstore(0x40, mload(C_V15_LOC))
-            // accumulator_2 = v15.[QELLIPTIC]
-            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
-            // accumulator = accumulator + accumulator_2
-            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
-
-            // VALIDATE QAUX
-            {
-                let x := mload(QAUX_X_LOC)
-                let y := mload(QAUX_Y_LOC)
-                let xx := mulmod(x, x, q)
-                // validate on curve
-                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
-                mstore(0x00, x)
-                mstore(0x20, y)
-            }
-            mstore(0x40, mload(C_V16_LOC))
-            // accumulator_2 = v15.[Q_AUX]
-            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
-            // accumulator = accumulator + accumulator_2
-            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
-
-            // VALIDATE SIGMA1
-            {
-                let x := mload(SIGMA1_X_LOC)
-                let y := mload(SIGMA1_Y_LOC)
-                let xx := mulmod(x, x, q)
-                // validate on curve
-                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
-                mstore(0x00, x)
-                mstore(0x20, y)
-            }
-            mstore(0x40, mload(C_V17_LOC))
-            // accumulator_2 = v17.[sigma1]
-            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
-            // accumulator = accumulator + accumulator_2
-            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
-
-            // VALIDATE SIGMA2
-            {
-                let x := mload(SIGMA2_X_LOC)
-                let y := mload(SIGMA2_Y_LOC)
-                let xx := mulmod(x, x, q)
-                // validate on curve
-                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
-                mstore(0x00, x)
-                mstore(0x20, y)
-            }
-            mstore(0x40, mload(C_V18_LOC))
-            // accumulator_2 = v18.[sigma2]
-            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
-            // accumulator = accumulator + accumulator_2
-            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
-
-            // VALIDATE SIGMA3
-            {
-                let x := mload(SIGMA3_X_LOC)
-                let y := mload(SIGMA3_Y_LOC)
-                let xx := mulmod(x, x, q)
-                // validate on curve
-                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
-                mstore(0x00, x)
-                mstore(0x20, y)
-            }
-            mstore(0x40, mload(C_V19_LOC))
-            // accumulator_2 = v19.[sigma3]
-            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
-            // accumulator = accumulator + accumulator_2
-            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
-
-            // VALIDATE SIGMA4
-            {
-                let x := mload(SIGMA4_X_LOC)
-                let y := mload(SIGMA4_Y_LOC)
-                let xx := mulmod(x, x, q)
-                // validate on curve
-                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
-                mstore(0x00, x)
-                mstore(0x20, y)
-            }
-            mstore(0x40, mload(C_V20_LOC))
-            // accumulator_2 = v20.[sigma4]
-            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
-            // accumulator = accumulator + accumulator_2
-            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
-
-            // VALIDATE TABLE1
-            {
-                let x := mload(TABLE1_X_LOC)
-                let y := mload(TABLE1_Y_LOC)
-                let xx := mulmod(x, x, q)
-                // validate on curve
-                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
-                mstore(0x00, x)
-                mstore(0x20, y)
-            }
-            mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V21_LOC), p))
-            // accumulator_2 = u.[table1]
-            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
-            // accumulator = accumulator + accumulator_2
-            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
-
-            // VALIDATE TABLE2
-            {
-                let x := mload(TABLE2_X_LOC)
-                let y := mload(TABLE2_Y_LOC)
-                let xx := mulmod(x, x, q)
-                // validate on curve
-                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
-                mstore(0x00, x)
-                mstore(0x20, y)
-            }
-            mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V22_LOC), p))
-            // accumulator_2 = u.[table2]
-            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
-            // accumulator = accumulator + accumulator_2
-            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
-
-            // VALIDATE TABLE3
-            {
-                let x := mload(TABLE3_X_LOC)
-                let y := mload(TABLE3_Y_LOC)
-                let xx := mulmod(x, x, q)
-                // validate on curve
-                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
-                mstore(0x00, x)
-                mstore(0x20, y)
-            }
-            mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V23_LOC), p))
-            // accumulator_2 = u.[table3]
-            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
-            // accumulator = accumulator + accumulator_2
-            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
-
-            // VALIDATE TABLE4
-            {
-                let x := mload(TABLE4_X_LOC)
-                let y := mload(TABLE4_Y_LOC)
-                let xx := mulmod(x, x, q)
-                // validate on curve
-                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
-                mstore(0x00, x)
-                mstore(0x20, y)
-            }
-            mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V24_LOC), p))
-            // accumulator_2 = u.[table4]
-            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
-            // accumulator = accumulator + accumulator_2
-            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
-
-            // VALIDATE TABLE_TYPE
-            {
-                let x := mload(TABLE_TYPE_X_LOC)
-                let y := mload(TABLE_TYPE_Y_LOC)
-                let xx := mulmod(x, x, q)
-                // validate on curve
-                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
-                mstore(0x00, x)
-                mstore(0x20, y)
-            }
-            mstore(0x40, mload(C_V25_LOC))
-            // accumulator_2 = v25.[TableType]
-            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
-            // accumulator = accumulator + accumulator_2
-            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
-
-            // VALIDATE ID1
-            {
-                let x := mload(ID1_X_LOC)
-                let y := mload(ID1_Y_LOC)
-                let xx := mulmod(x, x, q)
-                // validate on curve
-                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
-                mstore(0x00, x)
-                mstore(0x20, y)
-            }
-            mstore(0x40, mload(C_V26_LOC))
-            // accumulator_2 = v26.[ID1]
-            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
-            // accumulator = accumulator + accumulator_2
-            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
-
-            // VALIDATE ID2
-            {
-                let x := mload(ID2_X_LOC)
-                let y := mload(ID2_Y_LOC)
-                let xx := mulmod(x, x, q)
-                // validate on curve
-                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
-                mstore(0x00, x)
-                mstore(0x20, y)
-            }
-            mstore(0x40, mload(C_V27_LOC))
-            // accumulator_2 = v27.[ID2]
-            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
-            // accumulator = accumulator + accumulator_2
-            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
-
-            // VALIDATE ID3
-            {
-                let x := mload(ID3_X_LOC)
-                let y := mload(ID3_Y_LOC)
-                let xx := mulmod(x, x, q)
-                // validate on curve
-                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
-                mstore(0x00, x)
-                mstore(0x20, y)
-            }
-            mstore(0x40, mload(C_V28_LOC))
-            // accumulator_2 = v28.[ID3]
-            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
-            // accumulator = accumulator + accumulator_2
-            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
-
-            // VALIDATE ID4
-            {
-                let x := mload(ID4_X_LOC)
-                let y := mload(ID4_Y_LOC)
-                let xx := mulmod(x, x, q)
-                // validate on curve
-                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
-                mstore(0x00, x)
-                mstore(0x20, y)
-            }
-            mstore(0x40, mload(C_V29_LOC))
-            // accumulator_2 = v29.[ID4]
-            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
-            // accumulator = accumulator + accumulator_2
-            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
-
-            /**
-             * COMPUTE BATCH EVALUATION SCALAR MULTIPLIER
-             */
-            {
-                /**
-                 * batch_evaluation = v0 * (w_1_omega * u + w_1_eval)
-                 * batch_evaluation += v1 * (w_2_omega * u + w_2_eval)
-                 * batch_evaluation += v2 * (w_3_omega * u + w_3_eval)
-                 * batch_evaluation += v3 * (w_4_omega * u + w_4_eval)
-                 * batch_evaluation += v4 * (s_omega_eval * u + s_eval)
-                 * batch_evaluation += v5 * (z_omega_eval * u + z_eval)
-                 * batch_evaluation += v6 * (z_lookup_omega_eval * u + z_lookup_eval)
-                 */
-                let batch_evaluation :=
-                    mulmod(
-                        mload(C_V0_LOC),
-                        addmod(mulmod(mload(W1_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(W1_EVAL_LOC), p),
-                        p
-                    )
-                batch_evaluation :=
-                    addmod(
-                        batch_evaluation,
-                        mulmod(
-                            mload(C_V1_LOC),
-                            addmod(mulmod(mload(W2_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(W2_EVAL_LOC), p),
-                            p
-                        ),
-                        p
-                    )
-                batch_evaluation :=
-                    addmod(
-                        batch_evaluation,
-                        mulmod(
-                            mload(C_V2_LOC),
-                            addmod(mulmod(mload(W3_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(W3_EVAL_LOC), p),
-                            p
-                        ),
-                        p
-                    )
-                batch_evaluation :=
-                    addmod(
-                        batch_evaluation,
-                        mulmod(
-                            mload(C_V3_LOC),
-                            addmod(mulmod(mload(W4_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(W4_EVAL_LOC), p),
-                            p
-                        ),
-                        p
-                    )
-                batch_evaluation :=
-                    addmod(
-                        batch_evaluation,
-                        mulmod(
-                            mload(C_V4_LOC),
-                            addmod(mulmod(mload(S_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(S_EVAL_LOC), p),
-                            p
-                        ),
-                        p
-                    )
-                batch_evaluation :=
-                    addmod(
-                        batch_evaluation,
-                        mulmod(
-                            mload(C_V5_LOC),
-                            addmod(mulmod(mload(Z_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(Z_EVAL_LOC), p),
-                            p
-                        ),
-                        p
-                    )
-                batch_evaluation :=
-                    addmod(
-                        batch_evaluation,
-                        mulmod(
-                            mload(C_V6_LOC),
-                            addmod(mulmod(mload(Z_LOOKUP_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(Z_LOOKUP_EVAL_LOC), p),
-                            p
-                        ),
-                        p
-                    )
-
-                /**
-                 * batch_evaluation += v7 * Q1_EVAL
-                 * batch_evaluation += v8 * Q2_EVAL
-                 * batch_evaluation += v9 * Q3_EVAL
-                 * batch_evaluation += v10 * Q4_EVAL
-                 * batch_evaluation += v11 * QM_EVAL
-                 * batch_evaluation += v12 * QC_EVAL
-                 * batch_evaluation += v13 * QARITH_EVAL
-                 * batch_evaluation += v14 * QSORT_EVAL_LOC
-                 * batch_evaluation += v15 * QELLIPTIC_EVAL_LOC
-                 * batch_evaluation += v16 * QAUX_EVAL_LOC
-                 * batch_evaluation += v17 * SIGMA1_EVAL_LOC
-                 * batch_evaluation += v18 * SIGMA2_EVAL_LOC
-                 * batch_evaluation += v19 * SIGMA3_EVAL_LOC
-                 * batch_evaluation += v20 * SIGMA4_EVAL_LOC
-                 */
-                batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V7_LOC), mload(Q1_EVAL_LOC), p), p)
-                batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V8_LOC), mload(Q2_EVAL_LOC), p), p)
-                batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V9_LOC), mload(Q3_EVAL_LOC), p), p)
-                batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V10_LOC), mload(Q4_EVAL_LOC), p), p)
-                batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V11_LOC), mload(QM_EVAL_LOC), p), p)
-                batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V12_LOC), mload(QC_EVAL_LOC), p), p)
-                batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V13_LOC), mload(QARITH_EVAL_LOC), p), p)
-                batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V14_LOC), mload(QSORT_EVAL_LOC), p), p)
-                batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V15_LOC), mload(QELLIPTIC_EVAL_LOC), p), p)
-                batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V16_LOC), mload(QAUX_EVAL_LOC), p), p)
-                batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V17_LOC), mload(SIGMA1_EVAL_LOC), p), p)
-                batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V18_LOC), mload(SIGMA2_EVAL_LOC), p), p)
-                batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V19_LOC), mload(SIGMA3_EVAL_LOC), p), p)
-                batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V20_LOC), mload(SIGMA4_EVAL_LOC), p), p)
-
-                /**
-                 * batch_evaluation += v21 * (table1(zw) * u + table1(z))
-                 * batch_evaluation += v22 * (table2(zw) * u + table2(z))
-                 * batch_evaluation += v23 * (table3(zw) * u + table3(z))
-                 * batch_evaluation += v24 * (table4(zw) * u + table4(z))
-                 * batch_evaluation += v25 * table_type_eval
-                 * batch_evaluation += v26 * id1_eval
-                 * batch_evaluation += v27 * id2_eval
-                 * batch_evaluation += v28 * id3_eval
-                 * batch_evaluation += v29 * id4_eval
-                 * batch_evaluation += quotient_eval
-                 */
-                batch_evaluation :=
-                    addmod(
-                        batch_evaluation,
-                        mulmod(
-                            mload(C_V21_LOC),
-                            addmod(mulmod(mload(TABLE1_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(TABLE1_EVAL_LOC), p),
-                            p
-                        ),
-                        p
-                    )
-                batch_evaluation :=
-                    addmod(
-                        batch_evaluation,
-                        mulmod(
-                            mload(C_V22_LOC),
-                            addmod(mulmod(mload(TABLE2_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(TABLE2_EVAL_LOC), p),
-                            p
-                        ),
-                        p
-                    )
-                batch_evaluation :=
-                    addmod(
-                        batch_evaluation,
-                        mulmod(
-                            mload(C_V23_LOC),
-                            addmod(mulmod(mload(TABLE3_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(TABLE3_EVAL_LOC), p),
-                            p
-                        ),
-                        p
-                    )
-                batch_evaluation :=
-                    addmod(
-                        batch_evaluation,
-                        mulmod(
-                            mload(C_V24_LOC),
-                            addmod(mulmod(mload(TABLE4_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(TABLE4_EVAL_LOC), p),
-                            p
-                        ),
-                        p
-                    )
-                batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V25_LOC), mload(TABLE_TYPE_EVAL_LOC), p), p)
-                batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V26_LOC), mload(ID1_EVAL_LOC), p), p)
-                batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V27_LOC), mload(ID2_EVAL_LOC), p), p)
-                batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V28_LOC), mload(ID3_EVAL_LOC), p), p)
-                batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V29_LOC), mload(ID4_EVAL_LOC), p), p)
-                batch_evaluation := addmod(batch_evaluation, mload(QUOTIENT_EVAL_LOC), p)
-
-                mstore(0x00, 0x01) // [1].x
-                mstore(0x20, 0x02) // [1].y
-                mstore(0x40, sub(p, batch_evaluation))
-                // accumulator_2 = -[1].(batch_evaluation)
-                success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
-                // accumulator = accumulator + accumulator_2
-                success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
-
-                mstore(OPENING_COMMITMENT_SUCCESS_FLAG, success)
-            }
-
-            /**
-             * PERFORM PAIRING PREAMBLE
-             */
-            {
-                let u := mload(C_U_LOC)
-                let zeta := mload(C_ZETA_LOC)
-                // VALIDATE PI_Z
-                {
-                    let x := mload(PI_Z_X_LOC)
-                    let y := mload(PI_Z_Y_LOC)
-                    let xx := mulmod(x, x, q)
-                    // validate on curve
-                    success := eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))
-                    mstore(0x00, x)
-                    mstore(0x20, y)
-                }
-                // compute zeta.[PI_Z] and add into accumulator
-                mstore(0x40, zeta)
-                success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
-                // accumulator = accumulator + accumulator_2
-                success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
-
-                // VALIDATE PI_Z_OMEGA
-                {
-                    let x := mload(PI_Z_OMEGA_X_LOC)
-                    let y := mload(PI_Z_OMEGA_Y_LOC)
-                    let xx := mulmod(x, x, q)
-                    // validate on curve
-                    success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
-                    mstore(0x00, x)
-                    mstore(0x20, y)
-                }
-                mstore(0x40, mulmod(mulmod(u, zeta, p), mload(OMEGA_LOC), p))
-                // accumulator_2 = u.zeta.omega.[PI_Z_OMEGA]
-                success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
-                // PAIRING_RHS = accumulator + accumulator_2
-                success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, PAIRING_RHS_X_LOC, 0x40))
-
-                mstore(0x00, mload(PI_Z_X_LOC))
-                mstore(0x20, mload(PI_Z_Y_LOC))
-                mstore(0x40, mload(PI_Z_OMEGA_X_LOC))
-                mstore(0x60, mload(PI_Z_OMEGA_Y_LOC))
-                mstore(0x80, u)
-                success := and(success, staticcall(gas(), 7, 0x40, 0x60, 0x40, 0x40))
-                // PAIRING_LHS = [PI_Z] + [PI_Z_OMEGA] * u
-                success := and(success, staticcall(gas(), 6, 0x00, 0x80, PAIRING_LHS_X_LOC, 0x40))
-                // negate lhs y-coordinate
-                mstore(PAIRING_LHS_Y_LOC, sub(q, mload(PAIRING_LHS_Y_LOC)))
-
-                if mload(CONTAINS_RECURSIVE_PROOF_LOC) {
-                    // VALIDATE RECURSIVE P1
-                    {
-                        let x := mload(RECURSIVE_P1_X_LOC)
-                        let y := mload(RECURSIVE_P1_Y_LOC)
-                        let xx := mulmod(x, x, q)
-                        // validate on curve
-                        success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
-                        mstore(0x00, x)
-                        mstore(0x20, y)
-                    }
-
-                    // compute u.u.[recursive_p1] and write into 0x60
-                    mstore(0x40, mulmod(u, u, p))
-                    success := and(success, staticcall(gas(), 7, 0x00, 0x60, 0x60, 0x40))
-                    // VALIDATE RECURSIVE P2
-                    {
-                        let x := mload(RECURSIVE_P2_X_LOC)
-                        let y := mload(RECURSIVE_P2_Y_LOC)
-                        let xx := mulmod(x, x, q)
-                        // validate on curve
-                        success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
-                        mstore(0x00, x)
-                        mstore(0x20, y)
-                    }
-                    // compute u.u.[recursive_p2] and write into 0x00
-                    // 0x40 still contains u*u
-                    success := and(success, staticcall(gas(), 7, 0x00, 0x60, 0x00, 0x40))
-
-                    // compute u.u.[recursiveP1] + rhs and write into rhs
-                    mstore(0xa0, mload(PAIRING_RHS_X_LOC))
-                    mstore(0xc0, mload(PAIRING_RHS_Y_LOC))
-                    success := and(success, staticcall(gas(), 6, 0x60, 0x80, PAIRING_RHS_X_LOC, 0x40))
-
-                    // compute u.u.[recursiveP2] + lhs and write into lhs
-                    mstore(0x40, mload(PAIRING_LHS_X_LOC))
-                    mstore(0x60, mload(PAIRING_LHS_Y_LOC))
-                    success := and(success, staticcall(gas(), 6, 0x00, 0x80, PAIRING_LHS_X_LOC, 0x40))
-                }
-
-                if iszero(success) {
-                    mstore(0x0, EC_SCALAR_MUL_FAILURE_SELECTOR)
-                    revert(0x00, 0x04)
-                }
-                mstore(PAIRING_PREAMBLE_SUCCESS_FLAG, success)
-            }
-
-            /**
-             * PERFORM PAIRING
-             */
-            {
-                // rhs paired with [1]_2
-                // lhs paired with [x]_2
-
-                mstore(0x00, mload(PAIRING_RHS_X_LOC))
-                mstore(0x20, mload(PAIRING_RHS_Y_LOC))
-                mstore(0x40, 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2) // this is [1]_2
-                mstore(0x60, 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed)
-                mstore(0x80, 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b)
-                mstore(0xa0, 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa)
-
-                mstore(0xc0, mload(PAIRING_LHS_X_LOC))
-                mstore(0xe0, mload(PAIRING_LHS_Y_LOC))
-                mstore(0x100, mload(G2X_X0_LOC))
-                mstore(0x120, mload(G2X_X1_LOC))
-                mstore(0x140, mload(G2X_Y0_LOC))
-                mstore(0x160, mload(G2X_Y1_LOC))
-
-                success := staticcall(gas(), 8, 0x00, 0x180, 0x00, 0x20)
-                mstore(PAIRING_SUCCESS_FLAG, success)
-                mstore(RESULT_FLAG, mload(0x00))
-            }
-            if iszero(
-                and(
-                    and(and(mload(PAIRING_SUCCESS_FLAG), mload(RESULT_FLAG)), mload(PAIRING_PREAMBLE_SUCCESS_FLAG)),
-                    mload(OPENING_COMMITMENT_SUCCESS_FLAG)
-                )
-            ) {
-                mstore(0x0, PROOF_FAILURE_SELECTOR)
-                revert(0x00, 0x04)
-            }
-            {
-                mstore(0x00, 0x01)
-                return(0x00, 0x20) // Proof succeeded!
-            }
-        }
-    }
-}
-
-contract UltraVerifier is BaseUltraVerifier {
-    function getVerificationKeyHash() public pure override(BaseUltraVerifier) returns (bytes32) {
-        return UltraVerificationKey.verificationKeyHash();
-    }
-
-    function loadVerificationKey(uint256 vk, uint256 _omegaInverseLoc) internal pure virtual override(BaseUltraVerifier) {
-        UltraVerificationKey.loadVerificationKey(vk, _omegaInverseLoc);
-    }
-}
diff --git a/tooling/bb_abstraction_leaks/src/lib.rs b/tooling/bb_abstraction_leaks/src/lib.rs
index fec53809ad4..56a4f58cd21 100644
--- a/tooling/bb_abstraction_leaks/src/lib.rs
+++ b/tooling/bb_abstraction_leaks/src/lib.rs
@@ -7,13 +7,6 @@ pub const ACVM_BACKEND_BARRETENBERG: &str = "acvm-backend-barretenberg";
 pub const BB_DOWNLOAD_URL: &str = env!("BB_BINARY_URL");
 pub const BB_VERSION: &str = env!("BB_VERSION");
 
-/// Embed the Solidity verifier file
-const ULTRA_VERIFIER_CONTRACT: &str = include_str!("contract.sol");
-
-pub fn complete_barretenberg_verifier_contract(contract: String) -> String {
-    format!("{contract}{ULTRA_VERIFIER_CONTRACT}")
-}
-
 /// Removes the public inputs which are prepended to a proof by Barretenberg.
 pub fn remove_public_inputs(num_pub_inputs: usize, proof: &[u8]) -> Vec<u8> {
     // Barretenberg prepends the public inputs onto the proof so we need to remove
diff --git a/tooling/nargo_cli/Cargo.toml b/tooling/nargo_cli/Cargo.toml
index f280682e15c..2652adaf327 100644
--- a/tooling/nargo_cli/Cargo.toml
+++ b/tooling/nargo_cli/Cargo.toml
@@ -51,7 +51,6 @@ dap.workspace = true
 
 # Backends
 backend-interface = { path = "../backend_interface" }
-bb_abstraction_leaks.workspace = true
 
 # Logs
 tracing-subscriber.workspace = true
diff --git a/tooling/nargo_cli/src/cli/codegen_verifier_cmd.rs b/tooling/nargo_cli/src/cli/codegen_verifier_cmd.rs
index fe79c0b8c23..1eb8153ce9b 100644
--- a/tooling/nargo_cli/src/cli/codegen_verifier_cmd.rs
+++ b/tooling/nargo_cli/src/cli/codegen_verifier_cmd.rs
@@ -7,7 +7,6 @@ use crate::backends::Backend;
 use crate::errors::CliError;
 
 use acvm::ExpressionWidth;
-use bb_abstraction_leaks::ACVM_BACKEND_BARRETENBERG;
 use clap::Args;
 use fm::FileManager;
 use nargo::insert_all_files_for_workspace_into_file_manager;
@@ -83,12 +82,5 @@ fn smart_contract_for_package(
     let program =
         compile_bin_package(file_manager, workspace, package, compile_options, expression_width)?;
 
-    let mut smart_contract_string = backend.eth_contract(&program.circuit)?;
-
-    if backend.name() == ACVM_BACKEND_BARRETENBERG {
-        smart_contract_string =
-            bb_abstraction_leaks::complete_barretenberg_verifier_contract(smart_contract_string);
-    }
-
-    Ok(smart_contract_string)
+    Ok(backend.eth_contract(&program.circuit)?)
 }
diff --git a/tooling/nargo_cli/src/cli/lsp_cmd.rs b/tooling/nargo_cli/src/cli/lsp_cmd.rs
index 4a2801eaf6e..1428b8070c8 100644
--- a/tooling/nargo_cli/src/cli/lsp_cmd.rs
+++ b/tooling/nargo_cli/src/cli/lsp_cmd.rs
@@ -42,6 +42,8 @@ pub(crate) fn run(
                 .service(router)
         });
 
+        eprintln!("LSP starting...");
+
         // Prefer truly asynchronous piped stdin/stdout without blocking tasks.
         #[cfg(unix)]
         let (stdin, stdout) = (
diff --git a/tooling/nargo_fmt/tests/expected/contract.nr b/tooling/nargo_fmt/tests/expected/contract.nr
index d288b1af7eb..0313da832a8 100644
--- a/tooling/nargo_fmt/tests/expected/contract.nr
+++ b/tooling/nargo_fmt/tests/expected/contract.nr
@@ -3,6 +3,8 @@
 // Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. 
 // Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
 contract Benchmarking {
+    use dep::protocol_types::abis::function_selector::FunctionSelector;
+
     use dep::value_note::{
         utils::{increment, decrement},
         value_note::{VALUE_NOTE_LEN, ValueNote, ValueNoteMethods},
@@ -11,7 +13,6 @@ contract Benchmarking {
     use dep::aztec::{
         context::{Context},
         note::{utils as note_utils, note_getter_options::NoteGetterOptions, note_header::NoteHeader},
-        selector::compute_selector,
         log::emit_unencrypted_log,
         state_vars::{map::Map, public_state::PublicState, set::Set},
         types::type_serialization::field_serialization::{FieldSerializationMethods, FIELD_SERIALIZED_LEN},
@@ -59,7 +60,7 @@ contract Benchmarking {
         storage.balances.at(owner).write(current + value);
         let _callStackItem1 = context.call_public_function(
             context.this_address(),
-            compute_selector("broadcast(Field)"),
+            FunctionSelector::from_signature("broadcast(Field)"),
             [owner]
         );
     }
@@ -71,7 +72,7 @@ contract Benchmarking {
     }
 
     unconstrained fn compute_note_hash_and_nullifier(
-        contract_address: Field,
+        contract_address: AztecAddress,
         nonce: Field,
         storage_slot: Field,
         preimage: [Field; VALUE_NOTE_LEN]
diff --git a/tooling/nargo_fmt/tests/input/contract.nr b/tooling/nargo_fmt/tests/input/contract.nr
index 6bc5c552110..58ae0e909a1 100644
--- a/tooling/nargo_fmt/tests/input/contract.nr
+++ b/tooling/nargo_fmt/tests/input/contract.nr
@@ -3,6 +3,8 @@
 // Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. 
 // Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
 contract Benchmarking {
+    use dep::protocol_types::abis::function_selector::FunctionSelector;
+
     use dep::value_note::{
         utils::{increment, decrement},
         value_note::{VALUE_NOTE_LEN, ValueNote, ValueNoteMethods},
@@ -11,7 +13,6 @@ contract Benchmarking {
     use dep::aztec::{
         context::{Context},
         note::{utils as note_utils, note_getter_options::NoteGetterOptions, note_header::NoteHeader},
-        selector::compute_selector,
         log::emit_unencrypted_log,
         state_vars::{map::Map, public_state::PublicState, set::Set},
         types::type_serialization::field_serialization::{FieldSerializationMethods, FIELD_SERIALIZED_LEN},
@@ -57,7 +58,7 @@ contract Benchmarking {
     fn increment_balance(owner: Field, value: Field) {
         let current = storage.balances.at(owner).read();
         storage.balances.at(owner).write(current + value);
-        let _callStackItem1 = context.call_public_function(context.this_address(), compute_selector("broadcast(Field)"), [owner]);
+        let _callStackItem1 = context.call_public_function(context.this_address(), FunctionSelector::from_signature("broadcast(Field)"), [owner]);
     }
 
     // Est ultricies integer quis auctor elit sed. In nibh mauris cursus mattis molestie a iaculis.
@@ -66,7 +67,7 @@ contract Benchmarking {
         emit_unencrypted_log(&mut context, storage.balances.at(owner).read());
     }
 
-    unconstrained fn compute_note_hash_and_nullifier(contract_address: Field, nonce: Field, storage_slot: Field, preimage: [Field; VALUE_NOTE_LEN]) -> [Field; 4] {
+    unconstrained fn compute_note_hash_and_nullifier(contract_address: AztecAddress, nonce: Field, storage_slot: Field, preimage: [Field; VALUE_NOTE_LEN]) -> [Field; 4] {
         let note_header = NoteHeader::new(contract_address, nonce, storage_slot);
         note_utils::compute_note_hash_and_nullifier(ValueNoteMethods, note_header, preimage)
     }
diff --git a/tooling/noir_codegen/package.json b/tooling/noir_codegen/package.json
index 9a39f6c7ca0..7d76b1a9138 100644
--- a/tooling/noir_codegen/package.json
+++ b/tooling/noir_codegen/package.json
@@ -19,7 +19,6 @@
   "dependencies": {
     "@noir-lang/types": "workspace:*",
     "glob": "^10.3.10",
-    "lodash": "^4.17.21",
     "ts-command-line-args": "^2.5.1"
   },
   "files": [
@@ -49,7 +48,6 @@
   "devDependencies": {
     "@noir-lang/noir_js": "workspace:*",
     "@types/chai": "^4",
-    "@types/lodash": "^4",
     "@types/mocha": "^10.0.1",
     "@types/node": "^20.6.2",
     "@types/prettier": "^3",
diff --git a/tooling/noir_codegen/src/utils/glob.ts b/tooling/noir_codegen/src/utils/glob.ts
index 15deaf72e44..a1e4c3b1ea1 100644
--- a/tooling/noir_codegen/src/utils/glob.ts
+++ b/tooling/noir_codegen/src/utils/glob.ts
@@ -1,9 +1,6 @@
 import { sync as globSync } from 'glob';
-import _ from 'lodash';
-const { flatten, uniq } = _;
 
 export function glob(cwd: string, patternsOrFiles: string[]): string[] {
   const matches = patternsOrFiles.map((p) => globSync(p, { ignore: 'node_modules/**', absolute: true, cwd }));
-
-  return uniq(flatten(matches));
+  return [...new Set(matches.flat())];
 }
diff --git a/tooling/noir_js_backend_barretenberg/package.json b/tooling/noir_js_backend_barretenberg/package.json
index 93fdc856338..1688db1ab2d 100644
--- a/tooling/noir_js_backend_barretenberg/package.json
+++ b/tooling/noir_js_backend_barretenberg/package.json
@@ -42,7 +42,7 @@
     "lint": "NODE_NO_WARNINGS=1 eslint . --ext .ts --ignore-path ./.eslintignore  --max-warnings 0"
   },
   "dependencies": {
-    "@aztec/bb.js": "0.16.0",
+    "@aztec/bb.js": "0.17.0",
     "@noir-lang/types": "workspace:*",
     "fflate": "^0.8.0"
   },
diff --git a/tooling/noir_js_backend_barretenberg/src/index.ts b/tooling/noir_js_backend_barretenberg/src/index.ts
index 100418debd0..6e619fd59cf 100644
--- a/tooling/noir_js_backend_barretenberg/src/index.ts
+++ b/tooling/noir_js_backend_barretenberg/src/index.ts
@@ -5,8 +5,7 @@ import { Backend, CompiledCircuit, ProofData } from '@noir-lang/types';
 import { BackendOptions } from './types.js';
 import { deflattenPublicInputs, flattenPublicInputsAsArray } from './public_inputs.js';
 
-export { flattenPublicInputs } from './public_inputs.js';
-
+export { publicInputsToWitnessMap } from './public_inputs.js';
 // This is the number of bytes in a UltraPlonk proof
 // minus the public inputs.
 const numBytesInProofWithoutPublicInputs: number = 2144;
@@ -34,7 +33,7 @@ export class BarretenbergBackend implements Backend {
       // eslint-disable-next-line @typescript-eslint/ban-ts-comment
       //@ts-ignore
       const { Barretenberg, RawBuffer, Crs } = await import('@aztec/bb.js');
-      const api = await Barretenberg.new(this.options.threads);
+      const api = await Barretenberg.new({ threads: this.options.threads });
 
       const [_exact, _total, subgroupSize] = await api.acirGetCircuitSizes(this.acirUncompressedBytecode);
       const crs = await Crs.new(subgroupSize + 1);
@@ -95,7 +94,7 @@ export class BarretenbergBackend implements Backend {
 
     const publicInputsConcatenated = proofWithPublicInputs.slice(0, splitIndex);
     const proof = proofWithPublicInputs.slice(splitIndex);
-    const publicInputs = deflattenPublicInputs(publicInputsConcatenated, this.acirCircuit.abi);
+    const publicInputs = deflattenPublicInputs(publicInputsConcatenated);
 
     return { proof, publicInputs };
   }
@@ -127,7 +126,9 @@ export class BarretenbergBackend implements Backend {
   }> {
     await this.instantiate();
     const proof = reconstructProofWithPublicInputs(proofData);
-    const proofAsFields = await this.api.acirSerializeProofIntoFields(this.acirComposer, proof, numOfPublicInputs);
+    const proofAsFields = (
+      await this.api.acirSerializeProofIntoFields(this.acirComposer, proof, numOfPublicInputs)
+    ).slice(numOfPublicInputs);
 
     // TODO: perhaps we should put this in the init function. Need to benchmark
     // TODO how long it takes.
diff --git a/tooling/noir_js_backend_barretenberg/src/public_inputs.ts b/tooling/noir_js_backend_barretenberg/src/public_inputs.ts
index 37bc5b13012..75ee0de6800 100644
--- a/tooling/noir_js_backend_barretenberg/src/public_inputs.ts
+++ b/tooling/noir_js_backend_barretenberg/src/public_inputs.ts
@@ -1,18 +1,11 @@
 import { Abi, WitnessMap } from '@noir-lang/types';
 
-export function flattenPublicInputs(publicInputs: WitnessMap): string[] {
-  const publicInputIndices = [...publicInputs.keys()].sort((a, b) => a - b);
-  const flattenedPublicInputs = publicInputIndices.map((index) => publicInputs.get(index) as string);
-  return flattenedPublicInputs;
-}
-
-export function flattenPublicInputsAsArray(publicInputs: WitnessMap): Uint8Array {
-  const flatPublicInputs = flattenPublicInputs(publicInputs);
-  const flattenedPublicInputs = flatPublicInputs.map(hexToUint8Array);
+export function flattenPublicInputsAsArray(publicInputs: string[]): Uint8Array {
+  const flattenedPublicInputs = publicInputs.map(hexToUint8Array);
   return flattenUint8Arrays(flattenedPublicInputs);
 }
 
-export function deflattenPublicInputs(flattenedPublicInputs: Uint8Array, abi: Abi): WitnessMap {
+export function deflattenPublicInputs(flattenedPublicInputs: Uint8Array): string[] {
   const publicInputSize = 32;
   const chunkedFlattenedPublicInputs: Uint8Array[] = [];
 
@@ -21,6 +14,16 @@ export function deflattenPublicInputs(flattenedPublicInputs: Uint8Array, abi: Ab
     chunkedFlattenedPublicInputs.push(publicInput);
   }
 
+  return chunkedFlattenedPublicInputs.map(uint8ArrayToHex);
+}
+
+export function witnessMapToPublicInputs(publicInputs: WitnessMap): string[] {
+  const publicInputIndices = [...publicInputs.keys()].sort((a, b) => a - b);
+  const flattenedPublicInputs = publicInputIndices.map((index) => publicInputs.get(index) as string);
+  return flattenedPublicInputs;
+}
+
+export function publicInputsToWitnessMap(publicInputs: string[], abi: Abi): WitnessMap {
   const return_value_witnesses = abi.return_witnesses;
   const public_parameters = abi.parameters.filter((param) => param.visibility === 'public');
   const public_parameter_witnesses: number[] = public_parameters.flatMap((param) =>
@@ -35,13 +38,13 @@ export function deflattenPublicInputs(flattenedPublicInputs: Uint8Array, abi: Ab
     (a, b) => a - b,
   );
 
-  const publicInputs: WitnessMap = new Map();
+  const witnessMap: WitnessMap = new Map();
   public_input_witnesses.forEach((witness_index, index) => {
-    const witness_value = uint8ArrayToHex(chunkedFlattenedPublicInputs[index]);
-    publicInputs.set(witness_index, witness_value);
+    const witness_value = publicInputs[index];
+    witnessMap.set(witness_index, witness_value);
   });
 
-  return publicInputs;
+  return witnessMap;
 }
 
 function flattenUint8Arrays(arrays: Uint8Array[]): Uint8Array {
diff --git a/tooling/noir_js_backend_barretenberg/test/public_input_deflattening.test.ts b/tooling/noir_js_backend_barretenberg/test/public_input_deflattening.test.ts
index dab1c56436a..079a1ad268b 100644
--- a/tooling/noir_js_backend_barretenberg/test/public_input_deflattening.test.ts
+++ b/tooling/noir_js_backend_barretenberg/test/public_input_deflattening.test.ts
@@ -1,6 +1,6 @@
 import { Abi } from '@noir-lang/types';
 import { expect } from 'chai';
-import { flattenPublicInputsAsArray, deflattenPublicInputs, flattenPublicInputs } from '../src/public_inputs.js';
+import { witnessMapToPublicInputs, publicInputsToWitnessMap } from '../src/public_inputs.js';
 
 const abi: Abi = {
   parameters: [
@@ -69,7 +69,7 @@ it('flattens a witness map in order of its witness indices', async () => {
     ]),
   );
 
-  const flattened_public_inputs = flattenPublicInputs(witness_map);
+  const flattened_public_inputs = witnessMapToPublicInputs(witness_map);
   expect(flattened_public_inputs).to.be.deep.eq([
     '0x0000000000000000000000000000000000000000000000000000000000000002',
     '0x000000000000000000000000000000000000000000000000000000000000000b',
@@ -89,8 +89,8 @@ it('recovers the original witness map when deflattening a public input array', a
     ]),
   );
 
-  const flattened_public_inputs = flattenPublicInputsAsArray(witness_map);
-  const deflattened_public_inputs = deflattenPublicInputs(flattened_public_inputs, abi);
+  const flattened_public_inputs = witnessMapToPublicInputs(witness_map);
+  const deflattened_public_inputs = publicInputsToWitnessMap(flattened_public_inputs, abi);
 
   expect(deflattened_public_inputs).to.be.deep.eq(witness_map);
 });
diff --git a/tooling/noir_js_types/src/types.ts b/tooling/noir_js_types/src/types.ts
index b997d92425d..ee4921bd606 100644
--- a/tooling/noir_js_types/src/types.ts
+++ b/tooling/noir_js_types/src/types.ts
@@ -1,4 +1,4 @@
-import { Abi, WitnessMap } from '@noir-lang/noirc_abi';
+import { Abi } from '@noir-lang/noirc_abi';
 
 export { Abi, WitnessMap } from '@noir-lang/noirc_abi';
 
@@ -45,7 +45,7 @@ export interface Backend {
  * */
 export type ProofData = {
   /** @description Public inputs of a proof */
-  publicInputs: WitnessMap;
+  publicInputs: string[];
   /** @description An byte array representing the proof */
   proof: Uint8Array;
 };
diff --git a/yarn.lock b/yarn.lock
index 75c772227b4..4923ffed425 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -235,6 +235,20 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@aztec/bb.js@npm:0.17.0":
+  version: 0.17.0
+  resolution: "@aztec/bb.js@npm:0.17.0"
+  dependencies:
+    comlink: ^4.4.1
+    commander: ^10.0.1
+    debug: ^4.3.4
+    tslib: ^2.4.0
+  bin:
+    bb.js: dest/node/main.js
+  checksum: 459838076e4db7e6ca17f95acbd5c83028ea3b5c21e016a1561cec065a95d20e8d65906ad63e44e071556bc331070ceda6d48c058f5accdb76b3c6daab8ef1a5
+  languageName: node
+  linkType: hard
+
 "@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.11, @babel/code-frame@npm:^7.16.0, @babel/code-frame@npm:^7.22.13, @babel/code-frame@npm:^7.23.5, @babel/code-frame@npm:^7.8.3":
   version: 7.23.5
   resolution: "@babel/code-frame@npm:7.23.5"
@@ -4414,7 +4428,7 @@ __metadata:
   version: 0.0.0-use.local
   resolution: "@noir-lang/backend_barretenberg@workspace:tooling/noir_js_backend_barretenberg"
   dependencies:
-    "@aztec/bb.js": 0.16.0
+    "@aztec/bb.js": 0.17.0
     "@noir-lang/types": "workspace:*"
     "@types/node": ^20.6.2
     "@types/prettier": ^3
@@ -4436,7 +4450,6 @@ __metadata:
     "@noir-lang/noir_js": "workspace:*"
     "@noir-lang/types": "workspace:*"
     "@types/chai": ^4
-    "@types/lodash": ^4
     "@types/mocha": ^10.0.1
     "@types/node": ^20.6.2
     "@types/prettier": ^3
@@ -4444,7 +4457,6 @@ __metadata:
     eslint: ^8.50.0
     eslint-plugin-prettier: ^5.0.0
     glob: ^10.3.10
-    lodash: ^4.17.21
     mocha: ^10.2.0
     prettier: 3.0.3
     ts-command-line-args: ^2.5.1
@@ -5797,13 +5809,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@types/lodash@npm:^4":
-  version: 4.14.202
-  resolution: "@types/lodash@npm:4.14.202"
-  checksum: a91acf3564a568c6f199912f3eb2c76c99c5a0d7e219394294213b3f2d54f672619f0fde4da22b29dc5d4c31457cd799acc2e5cb6bd90f9af04a1578483b6ff7
-  languageName: node
-  linkType: hard
-
 "@types/lru-cache@npm:^5.1.0":
   version: 5.1.1
   resolution: "@types/lru-cache@npm:5.1.1"