From 3af55881ec60801c527cd2d4e2c6b7234048809e Mon Sep 17 00:00:00 2001
From: Gregor <gregor.mitscha-baude@gmx.at>
Date: Mon, 1 Jul 2024 18:25:49 +0200
Subject: [PATCH 01/32] make provable class only accept nested provables

---
 src/lib/provable/types/provable-derivers.ts | 22 ++++++++++++++++-----
 1 file changed, 17 insertions(+), 5 deletions(-)

diff --git a/src/lib/provable/types/provable-derivers.ts b/src/lib/provable/types/provable-derivers.ts
index b417b23163..d24fc76861 100644
--- a/src/lib/provable/types/provable-derivers.ts
+++ b/src/lib/provable/types/provable-derivers.ts
@@ -7,9 +7,13 @@ import {
   InferJson,
   InferredProvable as GenericInferredProvable,
   IsPure as GenericIsPure,
+  NestedProvable as GenericNestedProvable,
   createHashInput,
   Constructor,
   InferValue,
+  InferJsonNested,
+  InferValueNested,
+  InferProvableNested,
 } from '../../../bindings/lib/provable-generic.js';
 import { Tuple } from '../../util/types.js';
 import { GenericHashInput } from '../../../bindings/lib/generic.js';
@@ -31,6 +35,7 @@ export {
   InferJson,
   InferredProvable,
   IsPure,
+  NestedProvable,
 };
 
 type ProvableExtension<T, TJson = any> = {
@@ -54,6 +59,8 @@ type IsPure<T> = GenericIsPure<T, Field>;
 type HashInput = GenericHashInput<Field>;
 const HashInput = createHashInput<Field>();
 
+type NestedProvable = GenericNestedProvable<Field>;
+
 const { provable } = createDerivers<Field>();
 
 function provablePure<A>(
@@ -66,13 +73,18 @@ function provableTuple<T extends Tuple<any>>(types: T): InferredProvable<T> {
   return provable(types) as any;
 }
 
-function provableFromClass<A, T extends InferProvable<A>>(
+function provableFromClass<
+  A extends NestedProvable,
+  T extends InferProvableNested<Field, A>,
+  V extends InferValueNested<Field, A>,
+  J extends InferJsonNested<Field, A>
+>(
   Class: Constructor<T> & { check?: (x: T) => void; empty?: () => T },
   typeObj: A
 ): IsPure<A> extends true
-  ? ProvablePureExtended<T, InferValue<A>, InferJson<A>>
-  : ProvableExtended<T, InferValue<A>, InferJson<A>> {
-  let raw = provable(typeObj);
+  ? ProvablePureExtended<T, V, J>
+  : ProvableExtended<T, V, J> {
+  let raw: ProvableExtended<T, V, J> = provable(typeObj) as any;
   return {
     sizeInFields: raw.sizeInFields,
     toFields: raw.toFields,
@@ -101,7 +113,7 @@ function provableFromClass<A, T extends InferProvable<A>>(
         ? Class.empty()
         : construct(Class, raw.empty());
     },
-  } satisfies ProvableExtended<T, InferValue<A>, InferJson<A>> as any;
+  } satisfies ProvableExtended<T, V, J> as any;
 }
 
 function construct<Raw, T extends Raw>(Class: Constructor<T>, value: Raw): T {

From 277eb68c397dfc4d88697055a6e9311593865a28 Mon Sep 17 00:00:00 2001
From: Gregor <gregor.mitscha-baude@gmx.at>
Date: Mon, 1 Jul 2024 18:27:20 +0200
Subject: [PATCH 02/32] provable map draft

---
 src/lib/provable/types/provable-derivers.ts | 31 +++++++++++++++++++++
 1 file changed, 31 insertions(+)

diff --git a/src/lib/provable/types/provable-derivers.ts b/src/lib/provable/types/provable-derivers.ts
index d24fc76861..e6a69a93ab 100644
--- a/src/lib/provable/types/provable-derivers.ts
+++ b/src/lib/provable/types/provable-derivers.ts
@@ -25,6 +25,7 @@ export {
   provablePure,
   provableTuple,
   provableFromClass,
+  provableMap,
 };
 
 // internal API
@@ -120,3 +121,33 @@ function construct<Raw, T extends Raw>(Class: Constructor<T>, value: Raw): T {
   let instance = Object.create(Class.prototype);
   return Object.assign(instance, value);
 }
+
+function provableMap<
+  A extends Provable<any>,
+  S,
+  T extends InferProvable<A> = InferProvable<A>
+>(base: A, there: (t: T) => S, back: (s: S) => T): Provable<S, InferValue<A>> {
+  return {
+    sizeInFields() {
+      return base.sizeInFields();
+    },
+    toFields(value) {
+      return base.toFields(back(value));
+    },
+    toAuxiliary(value) {
+      return base.toAuxiliary(value === undefined ? undefined : back(value));
+    },
+    fromFields(fields, aux) {
+      return there(base.fromFields(fields, aux));
+    },
+    check(value) {
+      base.check(back(value));
+    },
+    toValue(value) {
+      return base.toValue(back(value));
+    },
+    fromValue(value) {
+      return there(base.fromValue(value));
+    },
+  };
+}

From dea94bc2c961ef2e9a763c9326ea22b5ecd51230 Mon Sep 17 00:00:00 2001
From: Gregor <gregor.mitscha-baude@gmx.at>
Date: Mon, 1 Jul 2024 18:27:23 +0200
Subject: [PATCH 03/32] bindings

---
 src/bindings | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/bindings b/src/bindings
index 1e29577237..dfbbf21788 160000
--- a/src/bindings
+++ b/src/bindings
@@ -1 +1 @@
-Subproject commit 1e29577237e331a1e0040398cc929ff295fd3383
+Subproject commit dfbbf21788c0a178cf483ef7f1b914ec79974f68

From 33236f391ca4391924c3691e74d57b7548604d57 Mon Sep 17 00:00:00 2001
From: Gregor <gregor.mitscha-baude@gmx.at>
Date: Tue, 2 Jul 2024 10:27:52 +0200
Subject: [PATCH 04/32] extend provable class

---
 src/lib/provable/types/provable-derivers.ts | 53 +++++++++++++++++++--
 src/lib/provable/types/provable-intf.ts     | 12 ++++-
 2 files changed, 61 insertions(+), 4 deletions(-)

diff --git a/src/lib/provable/types/provable-derivers.ts b/src/lib/provable/types/provable-derivers.ts
index e6a69a93ab..60b0c18208 100644
--- a/src/lib/provable/types/provable-derivers.ts
+++ b/src/lib/provable/types/provable-derivers.ts
@@ -1,4 +1,4 @@
-import { Provable, ProvablePure } from './provable-intf.js';
+import { Provable, ProvableHashable, ProvablePure } from './provable-intf.js';
 import type { Field } from '../wrapped.js';
 import {
   createDerivers,
@@ -26,6 +26,7 @@ export {
   provableTuple,
   provableFromClass,
   provableMap,
+  provableExtends,
 };
 
 // internal API
@@ -123,10 +124,14 @@ function construct<Raw, T extends Raw>(Class: Constructor<T>, value: Raw): T {
 }
 
 function provableMap<
-  A extends Provable<any>,
+  A extends ProvableHashable<any>,
   S,
   T extends InferProvable<A> = InferProvable<A>
->(base: A, there: (t: T) => S, back: (s: S) => T): Provable<S, InferValue<A>> {
+>(
+  base: A,
+  there: (t: T) => S,
+  back: (s: S) => T
+): ProvableHashable<S, InferValue<A>> {
   return {
     sizeInFields() {
       return base.sizeInFields();
@@ -149,5 +154,47 @@ function provableMap<
     fromValue(value) {
       return there(base.fromValue(value));
     },
+    empty() {
+      return there(base.empty());
+    },
+    toInput(value) {
+      return base.toInput(back(value));
+    },
   };
 }
+
+function provableExtends<
+  A extends ProvableHashable<any>,
+  T extends InferProvable<A>,
+  S extends T
+>(S: new (t: T) => S, base: A) {
+  return {
+    sizeInFields() {
+      return base.sizeInFields();
+    },
+    toFields(value: S | T) {
+      return base.toFields(value);
+    },
+    toAuxiliary(value?: S | T) {
+      return base.toAuxiliary(value);
+    },
+    fromFields(fields, aux) {
+      return new S(base.fromFields(fields, aux));
+    },
+    check(value: S | T) {
+      base.check(value);
+    },
+    toValue(value: S | T) {
+      return base.toValue(value);
+    },
+    fromValue(value) {
+      return new S(base.fromValue(value));
+    },
+    empty() {
+      return new S(base.empty());
+    },
+    toInput(value: S | T) {
+      return base.toInput(value);
+    },
+  } satisfies ProvableHashable<S, InferValue<A>>;
+}
diff --git a/src/lib/provable/types/provable-intf.ts b/src/lib/provable/types/provable-intf.ts
index e3c1ea5beb..51d5c43831 100644
--- a/src/lib/provable/types/provable-intf.ts
+++ b/src/lib/provable/types/provable-intf.ts
@@ -1,6 +1,6 @@
 import type { Field } from '../field.js';
 
-export { Provable, ProvablePure };
+export { Provable, ProvablePure, ProvableWithEmpty, ProvableHashable };
 
 /**
  * `Provable<T>` is the general interface for provable types in o1js.
@@ -86,3 +86,13 @@ type Provable<T, TValue = any> = {
 type ProvablePure<T, TValue = any> = Omit<Provable<T, TValue>, 'fromFields'> & {
   fromFields: (fields: Field[]) => T;
 };
+
+type ProvableWithEmpty<T, TValue = any> = Provable<T, TValue> & {
+  empty: () => T;
+};
+
+type HashInput = { fields?: Field[]; packed?: [Field, number][] };
+
+type ProvableHashable<T, TValue = any> = ProvableWithEmpty<T, TValue> & {
+  toInput: (x: T) => HashInput;
+};

From b745f2a4eda7d253ee889186b87bbe8231a0be63 Mon Sep 17 00:00:00 2001
From: Gregor <gregor.mitscha-baude@gmx.at>
Date: Tue, 2 Jul 2024 10:33:01 +0200
Subject: [PATCH 05/32] extend account update forest with more convenient
 methods

---
 src/lib/mina/account-update.ts        | 41 ++++++++++++++++++++++++---
 src/lib/mina/token/forest-iterator.ts |  7 +++--
 2 files changed, 42 insertions(+), 6 deletions(-)

diff --git a/src/lib/mina/account-update.ts b/src/lib/mina/account-update.ts
index 7f31de3985..d92e07fbb2 100644
--- a/src/lib/mina/account-update.ts
+++ b/src/lib/mina/account-update.ts
@@ -3,14 +3,18 @@ import {
   FlexibleProvable,
   StructNoJson,
 } from '../provable/types/struct.js';
-import { provable, provablePure } from '../provable/types/provable-derivers.js';
+import {
+  provable,
+  provableExtends,
+  provablePure,
+} from '../provable/types/provable-derivers.js';
 import {
   memoizationContext,
   memoizeWitness,
   Provable,
 } from '../provable/provable.js';
 import { Field, Bool } from '../provable/wrapped.js';
-import { Pickles, Test } from '../../snarky.js';
+import { Pickles } from '../../snarky.js';
 import { jsLayout } from '../../bindings/mina-transaction/gen/js-layout.js';
 import {
   Types,
@@ -1343,10 +1347,29 @@ class AccountUpdateForest extends MerkleList.create(
   AccountUpdateTreeBase,
   merkleListHash
 ) {
+  static provable = provableExtends(AccountUpdateForest, super.provable);
+
+  push(update: AccountUpdate | AccountUpdateTreeBase) {
+    return super.push(
+      update instanceof AccountUpdate ? AccountUpdateTree.from(update) : update
+    );
+  }
+  pushIf(condition: Bool, update: AccountUpdate | AccountUpdateTreeBase) {
+    return super.pushIf(
+      condition,
+      update instanceof AccountUpdate ? AccountUpdateTree.from(update) : update
+    );
+  }
+
   static fromFlatArray(updates: AccountUpdate[]): AccountUpdateForest {
     let simpleForest = accountUpdatesToCallForest(updates);
     return this.fromSimpleForest(simpleForest);
   }
+
+  toFlatArray(mutate = true, depth = 0) {
+    return AccountUpdateForest.toFlatArray(this, mutate, depth);
+  }
+
   static toFlatArray(
     forest: AccountUpdateForestBase,
     mutate = true,
@@ -1385,6 +1408,17 @@ class AccountUpdateForest extends MerkleList.create(
       });
     });
   }
+
+  // fix static methods
+  static empty() {
+    return AccountUpdateForest.provable.empty();
+  }
+  static from(array: AccountUpdateTreeBase[]) {
+    return new AccountUpdateForest(super.from(array));
+  }
+  static fromReverse(array: AccountUpdateTreeBase[]) {
+    return new AccountUpdateForest(super.fromReverse(array));
+  }
 }
 
 /**
@@ -1590,8 +1624,7 @@ class UnfinishedForest {
   }
 
   toFlatArray(mutate = true, depth = 0): AccountUpdate[] {
-    if (this.isFinal())
-      return AccountUpdateForest.toFlatArray(this.final, mutate, depth);
+    if (this.isFinal()) return this.final.toFlatArray(mutate, depth);
     assert(this.isMutable(), 'final or mutable');
     let flatUpdates: AccountUpdate[] = [];
     for (let node of this.mutable) {
diff --git a/src/lib/mina/token/forest-iterator.ts b/src/lib/mina/token/forest-iterator.ts
index 7918cc629a..aa95731eb0 100644
--- a/src/lib/mina/token/forest-iterator.ts
+++ b/src/lib/mina/token/forest-iterator.ts
@@ -12,8 +12,11 @@ import { MerkleListIterator, MerkleList } from '../../provable/merkle-list.js';
 
 export { TokenAccountUpdateIterator };
 
-const AccountUpdateIterator =
-  MerkleListIterator.createFromList(AccountUpdateForest);
+const AccountUpdateIterator = MerkleListIterator.create(
+  AccountUpdateForest.prototype.innerProvable,
+  AccountUpdateForest._nextHash,
+  AccountUpdateForest.emptyHash
+);
 
 class Layer extends Struct({
   forest: AccountUpdateIterator.provable,

From c93cf54a868360c9d7038926451f266002141fcb Mon Sep 17 00:00:00 2001
From: Gregor Mitscha-Baude <gregor.mitscha-baude@gmx.at>
Date: Wed, 3 Jul 2024 00:29:06 +0200
Subject: [PATCH 06/32] unwrap unconstrained when converting to value

---
 .../mina/actions/offchain-state-serialization.ts    |  2 +-
 src/lib/provable/merkle-tree-indexed.ts             |  6 +-----
 src/lib/provable/types/unconstrained.ts             | 13 ++++++-------
 3 files changed, 8 insertions(+), 13 deletions(-)

diff --git a/src/lib/mina/actions/offchain-state-serialization.ts b/src/lib/mina/actions/offchain-state-serialization.ts
index 2bc1df8dd9..e939a49bf4 100644
--- a/src/lib/mina/actions/offchain-state-serialization.ts
+++ b/src/lib/mina/actions/offchain-state-serialization.ts
@@ -314,7 +314,7 @@ function updateMerkleMap(
 
       // update the intermediate tree, save updates for final tree
       intermediateTree.set(key, value);
-      updates.push({ key, fullValue: prefix.get() });
+      updates.push({ key, fullValue: prefix });
     }
 
     if (isValidUpdate) {
diff --git a/src/lib/provable/merkle-tree-indexed.ts b/src/lib/provable/merkle-tree-indexed.ts
index 262e788dfc..c3b5160ed9 100644
--- a/src/lib/provable/merkle-tree-indexed.ts
+++ b/src/lib/provable/merkle-tree-indexed.ts
@@ -677,11 +677,7 @@ class Leaf extends Struct({
   }
 
   static fromStored(leaf: StoredLeaf, sortedIndex: number) {
-    return {
-      ...leaf,
-      index: Unconstrained.from(leaf.index),
-      sortedIndex: Unconstrained.from(sortedIndex),
-    };
+    return { ...leaf, sortedIndex: sortedIndex };
   }
 }
 
diff --git a/src/lib/provable/types/unconstrained.ts b/src/lib/provable/types/unconstrained.ts
index 857a1d9665..c4a7a4c662 100644
--- a/src/lib/provable/types/unconstrained.ts
+++ b/src/lib/provable/types/unconstrained.ts
@@ -111,7 +111,7 @@ and Provable.asProver() blocks, which execute outside the proof.
     });
   }
 
-  static provable: Provable<Unconstrained<any>, Unconstrained<any>> & {
+  static provable: UnconstrainedProvable<any> & {
     toInput: (x: Unconstrained<any>) => {
       fields?: Field[];
       packed?: [Field, number][];
@@ -123,18 +123,15 @@ and Provable.asProver() blocks, which execute outside the proof.
     toAuxiliary: (t?: any) => [t ?? new Unconstrained(false)],
     fromFields: (_, [t]) => t,
     check: () => {},
-    toValue: (t) => t,
-    fromValue: (t) => t,
+    toValue: (t) => t.get(),
+    fromValue: (t) => (t instanceof Unconstrained ? t : Unconstrained.from(t)),
     toInput: () => ({}),
     empty: (): any => {
       throw Error('There is no default empty value for Unconstrained.');
     },
   };
 
-  static provableWithEmpty<T>(empty: T): Provable<
-    Unconstrained<T>,
-    Unconstrained<T>
-  > & {
+  static provableWithEmpty<T>(empty: T): Provable<Unconstrained<T>, T> & {
     toInput: (x: Unconstrained<T>) => {
       fields?: Field[];
       packed?: [Field, number][];
@@ -147,3 +144,5 @@ and Provable.asProver() blocks, which execute outside the proof.
     };
   }
 }
+
+type UnconstrainedProvable<T> = Provable<Unconstrained<T>, T>;

From 375bbaaaa4d7c91c13b96c0fa3e3100ccb4873fd Mon Sep 17 00:00:00 2001
From: Gregor Mitscha-Baude <gregor.mitscha-baude@gmx.at>
Date: Wed, 3 Jul 2024 00:37:50 +0200
Subject: [PATCH 07/32] fixup

---
 src/lib/provable/merkle-list.ts | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/lib/provable/merkle-list.ts b/src/lib/provable/merkle-list.ts
index 6144b42e53..5febbe6ffe 100644
--- a/src/lib/provable/merkle-list.ts
+++ b/src/lib/provable/merkle-list.ts
@@ -283,6 +283,9 @@ class MerkleList<T> implements MerkleListBase<T> {
         assert(this._provable !== undefined, 'MerkleList not initialized');
         return this._provable;
       }
+      static set provable(_provable: ProvableHashable<MerkleList<T>>) {
+        this._provable = _provable;
+      }
     }
     // override `instanceof` for subclasses
     return class MerkleListT extends MerkleListTBase {

From 744ec3ef2c19d7ab3f5bf4e25f39769dd32dc313 Mon Sep 17 00:00:00 2001
From: Gregor Mitscha-Baude <gregor.mitscha-baude@gmx.at>
Date: Wed, 3 Jul 2024 00:49:57 +0200
Subject: [PATCH 08/32] simplify

---
 src/lib/provable/types/unconstrained.ts | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/src/lib/provable/types/unconstrained.ts b/src/lib/provable/types/unconstrained.ts
index c4a7a4c662..fb923f2517 100644
--- a/src/lib/provable/types/unconstrained.ts
+++ b/src/lib/provable/types/unconstrained.ts
@@ -95,10 +95,7 @@ and Provable.asProver() blocks, which execute outside the proof.
    * Create an `Unconstrained` from a witness computation.
    */
   static witness<T>(compute: () => T): Unconstrained<T> {
-    return witness(
-      Unconstrained.provable,
-      () => new Unconstrained(true, compute())
-    );
+    return witness(Unconstrained.provable, compute);
   }
 
   /**
@@ -124,7 +121,7 @@ and Provable.asProver() blocks, which execute outside the proof.
     fromFields: (_, [t]) => t,
     check: () => {},
     toValue: (t) => t.get(),
-    fromValue: (t) => (t instanceof Unconstrained ? t : Unconstrained.from(t)),
+    fromValue: (t) => Unconstrained.from(t),
     toInput: () => ({}),
     empty: (): any => {
       throw Error('There is no default empty value for Unconstrained.');

From f7985547cf88420ab6bbdb700881e3ee690cd9c1 Mon Sep 17 00:00:00 2001
From: Gregor Mitscha-Baude <gregor.mitscha-baude@gmx.at>
Date: Wed, 3 Jul 2024 01:11:33 +0200
Subject: [PATCH 09/32] allow leaving out .provable in witness

---
 src/lib/provable/types/provable-intf.ts | 23 ++++++++++++++++++-
 src/lib/provable/types/unconstrained.ts |  2 +-
 src/lib/provable/types/witness.ts       | 30 +++++++++++++------------
 3 files changed, 39 insertions(+), 16 deletions(-)

diff --git a/src/lib/provable/types/provable-intf.ts b/src/lib/provable/types/provable-intf.ts
index 51d5c43831..100cf8df0c 100644
--- a/src/lib/provable/types/provable-intf.ts
+++ b/src/lib/provable/types/provable-intf.ts
@@ -1,6 +1,13 @@
 import type { Field } from '../field.js';
 
-export { Provable, ProvablePure, ProvableWithEmpty, ProvableHashable };
+export {
+  Provable,
+  ProvablePure,
+  ProvableWithEmpty,
+  ProvableHashable,
+  ProvableType,
+  ToProvable,
+};
 
 /**
  * `Provable<T>` is the general interface for provable types in o1js.
@@ -96,3 +103,17 @@ type HashInput = { fields?: Field[]; packed?: [Field, number][] };
 type ProvableHashable<T, TValue = any> = ProvableWithEmpty<T, TValue> & {
   toInput: (x: T) => HashInput;
 };
+
+// helpers to accept { provable: Type } instead of Type
+
+type ProvableType<T, V = any> = Provable<T, V> | { provable: Provable<T, V> };
+
+type ToProvable<A extends ProvableType<any>> = A extends { provable: infer P }
+  ? P
+  : A;
+
+const ProvableType = {
+  get<T, V>(type: ProvableType<T, V>): Provable<T, V> {
+    return 'provable' in type ? type.provable : type;
+  },
+};
diff --git a/src/lib/provable/types/unconstrained.ts b/src/lib/provable/types/unconstrained.ts
index fb923f2517..1278aaa7e7 100644
--- a/src/lib/provable/types/unconstrained.ts
+++ b/src/lib/provable/types/unconstrained.ts
@@ -95,7 +95,7 @@ and Provable.asProver() blocks, which execute outside the proof.
    * Create an `Unconstrained` from a witness computation.
    */
   static witness<T>(compute: () => T): Unconstrained<T> {
-    return witness(Unconstrained.provable, compute);
+    return witness(Unconstrained, compute);
   }
 
   /**
diff --git a/src/lib/provable/types/witness.ts b/src/lib/provable/types/witness.ts
index 15efb266f4..63de0d75d0 100644
--- a/src/lib/provable/types/witness.ts
+++ b/src/lib/provable/types/witness.ts
@@ -1,36 +1,38 @@
 import type { Field } from '../field.js';
 import type { FlexibleProvable, InferProvable } from './struct.js';
-import type { Provable } from './provable-intf.js';
+import { Provable, ProvableType, ToProvable } from './provable-intf.js';
 import {
   inCheckedComputation,
   snarkContext,
 } from '../core/provable-context.js';
 import { exists, existsAsync } from '../core/exists.js';
-import { From } from '../../../bindings/lib/provable-generic.js';
+import { From, InferValue } from '../../../bindings/lib/provable-generic.js';
 import { TupleN } from '../../util/types.js';
 import { createField } from '../core/field-constructor.js';
 
 export { witness, witnessAsync, witnessFields };
 
-function witness<A extends Provable<any, any>, T extends From<A> = From<A>>(
-  type: A,
-  compute: () => T
-): InferProvable<A> {
-  type S = InferProvable<A>;
+function witness<
+  A extends ProvableType<any, any>,
+  T extends From<ToProvable<A>> = From<ToProvable<A>>
+>(type: A, compute: () => T): InferProvable<ToProvable<A>> {
+  type S = InferProvable<ToProvable<A>>;
+  const provable: Provable<S> = ProvableType.get(type);
   let ctx = snarkContext.get();
 
   // outside provable code, we just call the callback and return its cloned result
   if (!inCheckedComputation() || ctx.inWitnessBlock) {
-    return clone(type, type.fromValue(compute()));
+    return clone(provable, provable.fromValue(compute()));
   }
   let proverValue: S | undefined = undefined;
   let fields: Field[];
 
   let id = snarkContext.enter({ ...ctx, inWitnessBlock: true });
   try {
-    fields = exists(type.sizeInFields(), () => {
-      proverValue = type.fromValue(compute());
-      let fields = type.toFields(proverValue);
+    fields = exists(provable.sizeInFields(), () => {
+      let value = provable.fromValue(compute());
+      proverValue = value;
+      let fields = provable.toFields(value);
       return fields.map((x) => x.toBigInt());
     });
   } finally {
@@ -38,11 +40,11 @@ function witness<A extends Provable<any, any>, T extends From<A> = From<A>>(
   }
 
   // rebuild the value from its fields (which are now variables) and aux data
-  let aux = type.toAuxiliary(proverValue);
-  let value = (type as Provable<S>).fromFields(fields, aux);
+  let aux = provable.toAuxiliary(proverValue);
+  let value = provable.fromFields(fields, aux);
 
   // add type-specific constraints
-  type.check(value);
+  provable.check(value);
 
   return value;
 }

From 07d6b647c833d609ab8e2d56eab425d10b844ffb Mon Sep 17 00:00:00 2001
From: Gregor Mitscha-Baude <gregor.mitscha-baude@gmx.at>
Date: Wed, 3 Jul 2024 01:18:24 +0200
Subject: [PATCH 10/32] match behaviour in provable async

---
 src/lib/provable/types/witness.ts | 26 +++++++++++++++-----------
 1 file changed, 15 insertions(+), 11 deletions(-)

diff --git a/src/lib/provable/types/witness.ts b/src/lib/provable/types/witness.ts
index 63de0d75d0..69251ef0a3 100644
--- a/src/lib/provable/types/witness.ts
+++ b/src/lib/provable/types/witness.ts
@@ -50,25 +50,29 @@ function witness<
 }
 
 async function witnessAsync<
-  T,
-  S extends FlexibleProvable<T> = FlexibleProvable<T>
->(type: S, compute: () => Promise<T>): Promise<T> {
+  A extends ProvableType<any, any>,
+  T extends From<ToProvable<A>> = From<ToProvable<A>>
+>(type: A, compute: () => Promise<T>): Promise<T> {
+  type S = InferProvable<ToProvable<A>>;
+  const provable: Provable<S> = ProvableType.get(type);
+
   let ctx = snarkContext.get();
 
   // outside provable code, we just call the callback and return its cloned result
   if (!inCheckedComputation() || ctx.inWitnessBlock) {
     let value: T = await compute();
-    return clone(type, value);
+    return clone(provable, provable.fromValue(value));
   }
-  let proverValue: T | undefined = undefined;
+  let proverValue: S | undefined = undefined;
   let fields: Field[];
 
   // call into `existsAsync` to witness the raw field elements
   let id = snarkContext.enter({ ...ctx, inWitnessBlock: true });
   try {
-    fields = await existsAsync(type.sizeInFields(), async () => {
-      proverValue = await compute();
-      let fields = type.toFields(proverValue);
+    fields = await existsAsync(provable.sizeInFields(), async () => {
+      let value: S = provable.fromValue(await compute());
+      proverValue = value;
+      let fields = provable.toFields(value);
       return fields.map((x) => x.toBigInt());
     });
   } finally {
@@ -76,11 +80,11 @@ async function witnessAsync<
   }
 
   // rebuild the value from its fields (which are now variables) and aux data
-  let aux = type.toAuxiliary(proverValue);
-  let value = (type as Provable<T>).fromFields(fields, aux);
+  let aux = provable.toAuxiliary(proverValue);
+  let value = provable.fromFields(fields, aux);
 
   // add type-specific constraints
-  type.check(value);
+  provable.check(value);
 
   return value;
 }

From d25d20573671c404a2ce680d357d9cad348e0e34 Mon Sep 17 00:00:00 2001
From: Gregor Mitscha-Baude <gregor.mitscha-baude@gmx.at>
Date: Wed, 3 Jul 2024 01:26:22 +0200
Subject: [PATCH 11/32] start using it

---
 benchmark/benchmarks/ecdsa.ts             |  6 +++---
 src/examples/benchmarks/foreign-field.ts  | 10 ++--------
 src/examples/benchmarks/keccak-witness.ts |  2 +-
 src/lib/mina/account-update.ts            |  4 +---
 src/lib/mina/actions/offchain-state.ts    |  2 +-
 src/lib/mina/actions/reducer.ts           |  2 +-
 src/lib/provable/crypto/foreign-ecdsa.ts  |  4 ++--
 src/lib/provable/gadgets/comparison.ts    |  2 +-
 8 files changed, 12 insertions(+), 20 deletions(-)

diff --git a/benchmark/benchmarks/ecdsa.ts b/benchmark/benchmarks/ecdsa.ts
index 35f54b9871..ed7cf80d21 100644
--- a/benchmark/benchmarks/ecdsa.ts
+++ b/benchmark/benchmarks/ecdsa.ts
@@ -27,9 +27,9 @@ const EcdsaBenchmarks = benchmark(
 
     tic('witness generation');
     await Provable.runAndCheck(async () => {
-      let message_ = Provable.witness(Bytes32.provable, () => message);
-      let signature_ = Provable.witness(Ecdsa.provable, () => signature);
-      let publicKey_ = Provable.witness(Secp256k1.provable, () => publicKey);
+      let message_ = Provable.witness(Bytes32, () => message);
+      let signature_ = Provable.witness(Ecdsa, () => signature);
+      let publicKey_ = Provable.witness(Secp256k1, () => publicKey);
       await keccakAndEcdsa.rawMethods.verifyEcdsa(
         message_,
         signature_,
diff --git a/src/examples/benchmarks/foreign-field.ts b/src/examples/benchmarks/foreign-field.ts
index 30190324ef..8f7f8a31fe 100644
--- a/src/examples/benchmarks/foreign-field.ts
+++ b/src/examples/benchmarks/foreign-field.ts
@@ -5,14 +5,8 @@ class ForeignScalar extends createForeignField(
 ) {}
 
 function main() {
-  let s = Provable.witness(
-    ForeignScalar.Canonical.provable,
-    ForeignScalar.random
-  );
-  let t = Provable.witness(
-    ForeignScalar.Canonical.provable,
-    ForeignScalar.random
-  );
+  let s = Provable.witness(ForeignScalar.Canonical, ForeignScalar.random);
+  let t = Provable.witness(ForeignScalar.Canonical, ForeignScalar.random);
   s.mul(t);
 }
 
diff --git a/src/examples/benchmarks/keccak-witness.ts b/src/examples/benchmarks/keccak-witness.ts
index e4509c5019..f37c16893f 100644
--- a/src/examples/benchmarks/keccak-witness.ts
+++ b/src/examples/benchmarks/keccak-witness.ts
@@ -4,7 +4,7 @@ let Bytes32 = Bytes(32);
 
 console.time('keccak witness');
 await Provable.runAndCheck(() => {
-  let bytes = Provable.witness(Bytes32.provable, () => Bytes32.random());
+  let bytes = Provable.witness(Bytes32, () => Bytes32.random());
   Hash.Keccak256.hash(bytes);
 });
 console.timeEnd('keccak witness');
diff --git a/src/lib/mina/account-update.ts b/src/lib/mina/account-update.ts
index d92e07fbb2..8c0568b400 100644
--- a/src/lib/mina/account-update.ts
+++ b/src/lib/mina/account-update.ts
@@ -1579,9 +1579,7 @@ class UnfinishedForest {
   }
 
   witnessHash(): UnfinishedForestFinal {
-    let final = Provable.witness(AccountUpdateForest.provable, () =>
-      this.finalize()
-    );
+    let final = Provable.witness(AccountUpdateForest, () => this.finalize());
     return this.setFinal(final);
   }
 
diff --git a/src/lib/mina/actions/offchain-state.ts b/src/lib/mina/actions/offchain-state.ts
index 18a5064415..2e42f73a5f 100644
--- a/src/lib/mina/actions/offchain-state.ts
+++ b/src/lib/mina/actions/offchain-state.ts
@@ -237,7 +237,7 @@ function OffchainState<
 
     // witness the merkle map & anchor against the onchain root
     let map = await Provable.witnessAsync(
-      IndexedMerkleMapN.provable,
+      IndexedMerkleMapN,
       async () => (await merkleMaps()).merkleMap
     );
     map.root.assertEquals(stateRoot, 'root mismatch');
diff --git a/src/lib/mina/actions/reducer.ts b/src/lib/mina/actions/reducer.ts
index 8be11506fb..43c36f98f0 100644
--- a/src/lib/mina/actions/reducer.ts
+++ b/src/lib/mina/actions/reducer.ts
@@ -268,7 +268,7 @@ class ${contract.constructor.name} extends SmartContract {
         config?.fromActionState ?? Actions.emptyActionState()
       ) {}
 
-      let actions = Provable.witness(MerkleActions.provable, () => {
+      let actions = Provable.witness(MerkleActions, () => {
         let actionFields = Mina.getActions(
           contract.address,
           config,
diff --git a/src/lib/provable/crypto/foreign-ecdsa.ts b/src/lib/provable/crypto/foreign-ecdsa.ts
index d9840edfe2..bd97164050 100644
--- a/src/lib/provable/crypto/foreign-ecdsa.ts
+++ b/src/lib/provable/crypto/foreign-ecdsa.ts
@@ -106,9 +106,9 @@ class EcdsaSignature {
    *
    * // ...
    * // in provable code: create input witnesses (or use method inputs, or constants)
-   * let pk = Provable.witness(Secp256k1.provable, () => publicKey);
+   * let pk = Provable.witness(Secp256k1, () => publicKey);
    * let msg = Provable.witness(Provable.Array(Field, 9), () => messageBytes.map(Field));
-   * let sig = Provable.witness(Ecdsa.provable, () => signature);
+   * let sig = Provable.witness(Ecdsa, () => signature);
    *
    * // verify signature
    * let isValid = sig.verify(msg, pk);
diff --git a/src/lib/provable/gadgets/comparison.ts b/src/lib/provable/gadgets/comparison.ts
index e784f54caf..648b7507bd 100644
--- a/src/lib/provable/gadgets/comparison.ts
+++ b/src/lib/provable/gadgets/comparison.ts
@@ -235,7 +235,7 @@ function isOddAndHigh(x: Field) {
 function fieldToField3(x: Field) {
   if (x.isConstant()) return Field3.from(x.toBigInt());
 
-  let xBig = witness(Field3.provable, () => Field3.from(x.toBigInt()));
+  let xBig = witness(Field3, () => x.toBigInt());
   multiRangeCheck(xBig);
   let [x0, x1, x2] = xBig;
 

From 30002309abc84a435b1f071e236a01e3ef8f197e Mon Sep 17 00:00:00 2001
From: Gregor <gregor.mitscha-baude@gmx.at>
Date: Wed, 3 Jul 2024 16:00:39 +0200
Subject: [PATCH 12/32] (maybe revert) some experimenting

---
 src/examples/crypto/rsa/rsa.ts    | 59 ++++++++++++++++++++++---------
 src/index.ts                      |  2 ++
 src/lib/provable/foreign-field.ts |  2 +-
 3 files changed, 46 insertions(+), 17 deletions(-)

diff --git a/src/examples/crypto/rsa/rsa.ts b/src/examples/crypto/rsa/rsa.ts
index 8d54376426..78de61b6b9 100644
--- a/src/examples/crypto/rsa/rsa.ts
+++ b/src/examples/crypto/rsa/rsa.ts
@@ -7,7 +7,7 @@ import {
   Provable,
   Struct,
   Unconstrained,
-  provable,
+  provableExtends,
 } from 'o1js';
 
 export { Bigint2048, rsaVerify65537 };
@@ -19,10 +19,40 @@ const mask = (1n << 116n) - 1n;
  */
 const Field18 = Provable.Array(Field, 18);
 
-class Bigint2048 extends Struct({
-  fields: Field18,
-  value: Unconstrained.provable as Provable<Unconstrained<bigint>>,
-}) {
+class Bigint2048 {
+  fields: Field[];
+  value: Unconstrained<bigint>;
+
+  // TODO this could be simplified with a Struct-like base class
+  // TODO map the value type to `bigint`
+  static provable = provableExtends(
+    Bigint2048,
+    // TODO this wrapping Struct should be unnecessary
+    class extends Struct({
+      fields: Field18,
+      value: Unconstrained.provableWithEmpty(0n),
+    }) {
+      // TODO where to add the custom check()?
+      static check({ fields }: { fields: Field[] }) {
+        for (let x of fields) {
+          rangeCheck116(x);
+        }
+      }
+    }
+  );
+
+  // TODO constructor could be removed with a Struct-like base class
+  constructor({
+    fields,
+    value,
+  }: {
+    fields: Field[];
+    value: Unconstrained<bigint>;
+  }) {
+    this.fields = fields;
+    this.value = value;
+  }
+
   modMul(x: Bigint2048, y: Bigint2048) {
     return multiply(x, y, this);
   }
@@ -36,19 +66,13 @@ class Bigint2048 extends Struct({
   }
 
   static from(x: bigint) {
-    let fields = [];
+    let fields: bigint[] = [];
     let value = x;
     for (let i = 0; i < 18; i++) {
-      fields.push(Field(x & mask));
+      fields.push(x & mask);
       x >>= 116n;
     }
-    return new Bigint2048({ fields, value: Unconstrained.from(value) });
-  }
-
-  static check(x: { fields: Field[] }) {
-    for (let i = 0; i < 18; i++) {
-      rangeCheck116(x.fields[i]);
-    }
+    return Bigint2048.provable.fromValue({ fields, value });
   }
 }
 
@@ -66,12 +90,15 @@ function multiply(
   // witness q, r so that x*y = q*p + r
   // this also adds the range checks in `check()`
   let { q, r } = Provable.witness(
-    provable({ q: Bigint2048, r: Bigint2048 }),
+    // TODO Struct() should be unnecessary
+    // TODO .provable should be unnecessary
+    Struct({ q: Bigint2048.provable, r: Bigint2048.provable }),
     () => {
       let xy = x.toBigint() * y.toBigint();
       let p0 = p.toBigint();
       let q = xy / p0;
       let r = xy - q * p0;
+      // TODO Bigint2048.from() should be unnecessary
       return { q: Bigint2048.from(q), r: Bigint2048.from(r) };
     }
   );
@@ -143,7 +170,7 @@ function rsaVerify65537(
   x = modulus.modMul(x, signature);
 
   // check that x == message
-  Provable.assertEqual(Bigint2048, message, x);
+  Provable.assertEqual(Bigint2048.provable, message, x);
 }
 
 /**
diff --git a/src/index.ts b/src/index.ts
index 3778236825..0300581ffe 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -39,6 +39,8 @@ export type {
 export {
   provable,
   provablePure,
+  provableMap,
+  provableExtends,
 } from './lib/provable/types/provable-derivers.js';
 export { Struct } from './lib/provable/types/struct.js';
 export { Unconstrained } from './lib/provable/types/unconstrained.js';
diff --git a/src/lib/provable/foreign-field.ts b/src/lib/provable/foreign-field.ts
index 802d064a61..167dedb94d 100644
--- a/src/lib/provable/foreign-field.ts
+++ b/src/lib/provable/foreign-field.ts
@@ -622,7 +622,7 @@ function isConstant(x: bigint | number | string | ForeignField) {
  * ```
  *
  * Similarly, there is a separate class {@link CanonicalForeignField} which represents fully reduced, "canonical" field elements.
- * To convert to a canonical field element, use {@link ForeignField.assertCanonical}:
+ * To convert to a canonical field element, use `ForeignField.assertCanonical()`:
  *
  * ```ts
  * x.assertCanonical(); // asserts x < p; returns `CanonicalForeignField`

From 50248551fd8336cfb26052498fe041835ac2dd35 Mon Sep 17 00:00:00 2001
From: Gregor <gregor.mitscha-baude@gmx.at>
Date: Tue, 16 Jul 2024 10:25:25 +0200
Subject: [PATCH 13/32] remove .provable where not needed

---
 src/examples/crypto/rsa/run.ts                |  6 ++++-
 .../mina/actions/batch-reducer.unit-test.ts   |  4 +---
 src/lib/provable/foreign-field.ts             |  2 +-
 src/lib/provable/gadgets/gadgets.ts           | 22 +++++++++----------
 src/lib/provable/merkle-tree-indexed.ts       |  2 +-
 src/lib/provable/scalar-field.ts              |  4 +---
 .../test/custom-gates-recursion.unit-test.ts  |  9 +++-----
 src/lib/provable/test/ecdsa.unit-test.ts      | 18 +++++----------
 .../provable/test/foreign-curve.unit-test.ts  |  6 ++---
 .../test/foreign-field-gadgets.unit-test.ts   |  8 +++----
 .../provable/test/merkle-tree.unit-test.ts    |  2 +-
 src/lib/provable/types/witness.ts             |  2 +-
 .../vk-regression/plain-constraint-system.ts  |  8 +++----
 13 files changed, 42 insertions(+), 51 deletions(-)

diff --git a/src/examples/crypto/rsa/run.ts b/src/examples/crypto/rsa/run.ts
index 3d27fb7ed9..c2c9b7ed53 100644
--- a/src/examples/crypto/rsa/run.ts
+++ b/src/examples/crypto/rsa/run.ts
@@ -7,7 +7,11 @@ let rsaZkProgram = ZkProgram({
 
   methods: {
     verifyRsa65537: {
-      privateInputs: [Bigint2048, Bigint2048, Bigint2048],
+      privateInputs: [
+        Bigint2048.provable,
+        Bigint2048.provable,
+        Bigint2048.provable,
+      ],
 
       async method(
         message: Bigint2048,
diff --git a/src/lib/mina/actions/batch-reducer.unit-test.ts b/src/lib/mina/actions/batch-reducer.unit-test.ts
index 98b35e3bb3..7e98a96f40 100644
--- a/src/lib/mina/actions/batch-reducer.unit-test.ts
+++ b/src/lib/mina/actions/batch-reducer.unit-test.ts
@@ -89,9 +89,7 @@ class UnsafeAirdrop extends SmartContract {
   @method.returns(MerkleMap.provable)
   async settleClaims(batch: Batch, proof: BatchProof) {
     // witness merkle map and require that it matches the onchain root
-    let eligibleMap = Provable.witness(MerkleMap.provable, () =>
-      eligible.clone()
-    );
+    let eligibleMap = Provable.witness(MerkleMap, () => eligible.clone());
     this.eligibleRoot.requireEquals(eligibleMap.root);
     this.eligibleLength.requireEquals(eligibleMap.length);
 
diff --git a/src/lib/provable/foreign-field.ts b/src/lib/provable/foreign-field.ts
index 167dedb94d..1c743713df 100644
--- a/src/lib/provable/foreign-field.ts
+++ b/src/lib/provable/foreign-field.ts
@@ -614,7 +614,7 @@ function isConstant(x: bigint | number | string | ForeignField) {
  * If you want to do multiplication, you have two options:
  * - create your field elements using the {@link ForeignField.AlmostReduced} constructor, or using the `.provable` type on that class.
  * ```ts
- * let x = Provable.witness(ForeignField.AlmostReduced.provable, () => ForeignField.from(5));
+ * let x = Provable.witness(ForeignField.AlmostReduced, () => 5n);
  * ```
  * - create your field elements normally and convert them using `x.assertAlmostReduced()`.
  * ```ts
diff --git a/src/lib/provable/gadgets/gadgets.ts b/src/lib/provable/gadgets/gadgets.ts
index c28c3d8ce5..205a0990a6 100644
--- a/src/lib/provable/gadgets/gadgets.ts
+++ b/src/lib/provable/gadgets/gadgets.ts
@@ -554,8 +554,8 @@ const Gadgets = {
      *
      * @example
      * ```ts
-     * let x = Provable.witness(Field3.provable, () => Field3.from(9n));
-     * let y = Provable.witness(Field3.provable, () => Field3.from(10n));
+     * let x = Provable.witness(Field3, () => 9n);
+     * let y = Provable.witness(Field3, () => 10n);
      *
      * // range check x and y
      * Gadgets.multiRangeCheck(x);
@@ -616,9 +616,9 @@ const Gadgets = {
      *
      * @example
      * ```ts
-     * let x = Provable.witness(Field3.provable, () => Field3.from(4n));
-     * let y = Provable.witness(Field3.provable, () => Field3.from(5n));
-     * let z = Provable.witness(Field3.provable, () => Field3.from(10n));
+     * let x = Provable.witness(Field3, () => 4n);
+     * let y = Provable.witness(Field3, () => 5n);
+     * let z = Provable.witness(Field3, () => 10n);
      *
      * // range check x, y, z
      * Gadgets.multiRangeCheck(x);
@@ -656,8 +656,8 @@ const Gadgets = {
      * // example modulus: secp256k1 prime
      * let f = (1n << 256n) - (1n << 32n) - 0b1111010001n;
      *
-     * let x = Provable.witness(Field3.provable, () => Field3.from(f - 1n));
-     * let y = Provable.witness(Field3.provable, () => Field3.from(f - 2n));
+     * let x = Provable.witness(Field3, () => f - 1n);
+     * let y = Provable.witness(Field3, () => f - 2n);
      *
      * // range check x, y and prove additional bounds x[2] <= f[2]
      * ForeignField.assertAlmostReduced([x, y], f);
@@ -758,9 +758,9 @@ const Gadgets = {
      *
      * @example
      * ```ts
-     * let x = Provable.witness(Field3.provable, () => Field3.from(4n));
-     * let y = Provable.witness(Field3.provable, () => Field3.from(5n));
-     * let z = Provable.witness(Field3.provable, () => Field3.from(10n));
+     * let x = Provable.witness(Field3, () => 4n);
+     * let y = Provable.witness(Field3, () => 5n);
+     * let z = Provable.witness(Field3, () => 10n);
      *
      * ForeignField.assertAlmostReduced([x, y, z], f);
      *
@@ -790,7 +790,7 @@ const Gadgets = {
      *
      * @example
      * ```ts
-     * let x = Provable.witness(Field3.provable, () => Field3.from(0x1235n));
+     * let x = Provable.witness(Field3, () => 0x1235n);
      *
      *  // range check limbs of x
      * Gadgets.multiRangeCheck(x);
diff --git a/src/lib/provable/merkle-tree-indexed.ts b/src/lib/provable/merkle-tree-indexed.ts
index 662e82ac12..4010ee309c 100644
--- a/src/lib/provable/merkle-tree-indexed.ts
+++ b/src/lib/provable/merkle-tree-indexed.ts
@@ -693,7 +693,7 @@ class Leaf extends Struct({
   }
 
   static fromStored(leaf: StoredLeaf, sortedIndex: number) {
-    return { ...leaf, sortedIndex: sortedIndex };
+    return { ...leaf, sortedIndex };
   }
 }
 
diff --git a/src/lib/provable/scalar-field.ts b/src/lib/provable/scalar-field.ts
index 91c085a025..b1f3343ced 100644
--- a/src/lib/provable/scalar-field.ts
+++ b/src/lib/provable/scalar-field.ts
@@ -35,9 +35,7 @@ class ScalarField extends createForeignField(Fq.modulus) {
     if (s.lowBit.isConstant() && s.high254.isConstant()) {
       return new ScalarField(s.toBigInt());
     }
-    const field = Provable.witness(ScalarField.provable, () => {
-      return s.toBigInt();
-    });
+    const field = Provable.witness(ScalarField, () => s.toBigInt());
     const foreignField = new ScalarField(field);
     const scalar = foreignField.toScalar();
     Provable.assertEqual(Scalar, s, scalar);
diff --git a/src/lib/provable/test/custom-gates-recursion.unit-test.ts b/src/lib/provable/test/custom-gates-recursion.unit-test.ts
index 7d1a06eb55..34ed06c60e 100644
--- a/src/lib/provable/test/custom-gates-recursion.unit-test.ts
+++ b/src/lib/provable/test/custom-gates-recursion.unit-test.ts
@@ -39,12 +39,9 @@ let program = ZkProgram({
       privateInputs: [EmptyProof],
       async method(proof: EmptyProof) {
         proof.verify();
-        let signature_ = Provable.witness(
-          Ecdsa.Signature.provable,
-          () => signature
-        );
-        let msgHash_ = Provable.witness(Field3.provable, () => msgHash);
-        let publicKey_ = Provable.witness(Point.provable, () => publicKey);
+        let signature_ = Provable.witness(Ecdsa.Signature, () => signature);
+        let msgHash_ = Provable.witness(Field3, () => msgHash);
+        let publicKey_ = Provable.witness(Point, () => publicKey);
 
         return Ecdsa.verifyV2(Secp256k1, signature_, msgHash_, publicKey_);
       },
diff --git a/src/lib/provable/test/ecdsa.unit-test.ts b/src/lib/provable/test/ecdsa.unit-test.ts
index 89545d9645..68067a60cb 100644
--- a/src/lib/provable/test/ecdsa.unit-test.ts
+++ b/src/lib/provable/test/ecdsa.unit-test.ts
@@ -147,12 +147,9 @@ let deprecatedVerify = ZkProgram({
     ecdsa: {
       privateInputs: [],
       async method() {
-        let signature_ = Provable.witness(
-          Ecdsa.Signature.provable,
-          () => signature
-        );
-        let msgHash_ = Provable.witness(Field3.provable, () => msgHash);
-        let publicKey_ = Provable.witness(Point.provable, () => publicKey);
+        let signature_ = Provable.witness(Ecdsa.Signature, () => signature);
+        let msgHash_ = Provable.witness(Field3, () => msgHash);
+        let publicKey_ = Provable.witness(Point, () => publicKey);
 
         return Ecdsa.verify(
           Secp256k1,
@@ -173,12 +170,9 @@ let program = ZkProgram({
     ecdsa: {
       privateInputs: [],
       async method() {
-        let signature_ = Provable.witness(
-          Ecdsa.Signature.provable,
-          () => signature
-        );
-        let msgHash_ = Provable.witness(Field3.provable, () => msgHash);
-        let publicKey_ = Provable.witness(Point.provable, () => publicKey);
+        let signature_ = Provable.witness(Ecdsa.Signature, () => signature);
+        let msgHash_ = Provable.witness(Field3, () => msgHash);
+        let publicKey_ = Provable.witness(Point, () => publicKey);
 
         return Ecdsa.verifyV2(
           Secp256k1,
diff --git a/src/lib/provable/test/foreign-curve.unit-test.ts b/src/lib/provable/test/foreign-curve.unit-test.ts
index 24ce76d979..c7bdaf9c4d 100644
--- a/src/lib/provable/test/foreign-curve.unit-test.ts
+++ b/src/lib/provable/test/foreign-curve.unit-test.ts
@@ -14,15 +14,15 @@ let scalar = Field.random().toBigInt();
 let p = V.toAffine(V.scale(V.fromAffine(h), scalar));
 
 function main() {
-  let g0 = Provable.witness(Vesta.provable, () => new Vesta(g));
-  let one = Provable.witness(Vesta.provable, () => Vesta.generator);
+  let g0 = Provable.witness(Vesta, () => g);
+  let one = Provable.witness(Vesta, () => Vesta.generator);
   let h0 = g0.add(one).double().negate();
   Provable.assertEqual(Vesta.provable, h0, new Vesta(h));
 
   h0.assertOnCurve();
   h0.assertInSubgroup();
 
-  let scalar0 = Provable.witness(Fp.provable, () => new Fp(scalar));
+  let scalar0 = Provable.witness(Fp, () => scalar);
   let p0 = h0.scale(scalar0);
   Provable.assertEqual(Vesta.provable, p0, new Vesta(p));
 }
diff --git a/src/lib/provable/test/foreign-field-gadgets.unit-test.ts b/src/lib/provable/test/foreign-field-gadgets.unit-test.ts
index c4f4c517d1..211f8fd122 100644
--- a/src/lib/provable/test/foreign-field-gadgets.unit-test.ts
+++ b/src/lib/provable/test/foreign-field-gadgets.unit-test.ts
@@ -306,8 +306,8 @@ await equivalentAsync({ from: [f, f], to: unit }, { runs })(
 
 function assertMulExample(x: Field3, y: Field3, f: bigint) {
   // witness x^2, y^2
-  let x2 = Provable.witness(Field3.provable, () => ForeignField.mul(x, x, f));
-  let y2 = Provable.witness(Field3.provable, () => ForeignField.mul(y, y, f));
+  let x2 = Provable.witness(Field3, () => ForeignField.mul(x, x, f));
+  let y2 = Provable.witness(Field3, () => ForeignField.mul(y, y, f));
 
   // assert (x - y) * (x + y) = x^2 - y^2
   let xMinusY = ForeignField.Sum(x).sub(y);
@@ -318,8 +318,8 @@ function assertMulExample(x: Field3, y: Field3, f: bigint) {
 
 function assertMulExampleNaive(x: Field3, y: Field3, f: bigint) {
   // witness x^2, y^2
-  let x2 = Provable.witness(Field3.provable, () => ForeignField.mul(x, x, f));
-  let y2 = Provable.witness(Field3.provable, () => ForeignField.mul(y, y, f));
+  let x2 = Provable.witness(Field3, () => ForeignField.mul(x, x, f));
+  let y2 = Provable.witness(Field3, () => ForeignField.mul(y, y, f));
 
   // assert (x - y) * (x + y) = x^2 - y^2
   let lhs = ForeignField.mul(
diff --git a/src/lib/provable/test/merkle-tree.unit-test.ts b/src/lib/provable/test/merkle-tree.unit-test.ts
index 010706179e..8a0e180cc8 100644
--- a/src/lib/provable/test/merkle-tree.unit-test.ts
+++ b/src/lib/provable/test/merkle-tree.unit-test.ts
@@ -209,7 +209,7 @@ test(
 
     // pass the map to a circuit
     runAndCheckSync(() => {
-      map = Provable.witness(MerkleMap.provable, () => map);
+      map = Provable.witness(MerkleMap, () => map);
       let initialKeysF = initialKeys.map(witness);
       let keysF = keys.map(witness);
       let valuesF = values.map(witness);
diff --git a/src/lib/provable/types/witness.ts b/src/lib/provable/types/witness.ts
index 69251ef0a3..2e86cac798 100644
--- a/src/lib/provable/types/witness.ts
+++ b/src/lib/provable/types/witness.ts
@@ -6,7 +6,7 @@ import {
   snarkContext,
 } from '../core/provable-context.js';
 import { exists, existsAsync } from '../core/exists.js';
-import { From, InferValue } from '../../../bindings/lib/provable-generic.js';
+import { From } from '../../../bindings/lib/provable-generic.js';
 import { TupleN } from '../../util/types.js';
 import { createField } from '../core/field-constructor.js';
 
diff --git a/tests/vk-regression/plain-constraint-system.ts b/tests/vk-regression/plain-constraint-system.ts
index 740abf170b..b0030807db 100644
--- a/tests/vk-regression/plain-constraint-system.ts
+++ b/tests/vk-regression/plain-constraint-system.ts
@@ -107,22 +107,22 @@ const bytes32 = Bytes32.from([]);
 
 const HashCS = constraintSystem('Hashes', {
   SHA256() {
-    let xs = Provable.witness(Bytes32.provable, () => bytes32);
+    let xs = Provable.witness(Bytes32, () => bytes32);
     Hash.SHA3_256.hash(xs);
   },
 
   SHA384() {
-    let xs = Provable.witness(Bytes32.provable, () => bytes32);
+    let xs = Provable.witness(Bytes32, () => bytes32);
     Hash.SHA3_384.hash(xs);
   },
 
   SHA512() {
-    let xs = Provable.witness(Bytes32.provable, () => bytes32);
+    let xs = Provable.witness(Bytes32, () => bytes32);
     Hash.SHA3_512.hash(xs);
   },
 
   Keccak256() {
-    let xs = Provable.witness(Bytes32.provable, () => bytes32);
+    let xs = Provable.witness(Bytes32, () => bytes32);
     Hash.Keccak256.hash(xs);
   },
 

From 6cf7a65ebbaa616b103a23f41e21502e4e08c5ed Mon Sep 17 00:00:00 2001
From: Gregor <gregor.mitscha-baude@gmx.at>
Date: Tue, 16 Jul 2024 11:01:58 +0200
Subject: [PATCH 14/32] support no .provable in zkprogram public inputs

---
 src/lib/proof-system/zkprogram.ts           | 42 +++++++++++++++------
 src/lib/provable/types/provable-derivers.ts | 10 ++++-
 src/lib/provable/types/provable-intf.ts     |  9 ++++-
 3 files changed, 47 insertions(+), 14 deletions(-)

diff --git a/src/lib/proof-system/zkprogram.ts b/src/lib/proof-system/zkprogram.ts
index 8a36c4559c..54644ddbc1 100644
--- a/src/lib/proof-system/zkprogram.ts
+++ b/src/lib/proof-system/zkprogram.ts
@@ -9,7 +9,11 @@ import {
   ProvablePureExtended,
   Struct,
 } from '../provable/types/struct.js';
-import { provable, provablePure } from '../provable/types/provable-derivers.js';
+import {
+  InferProvableType,
+  provable,
+  provablePure,
+} from '../provable/types/provable-derivers.js';
 import { Provable } from '../provable/provable.js';
 import { assert, prettifyStacktracePromise } from '../util/errors.js';
 import { snarkContext } from '../provable/core/provable-context.js';
@@ -34,7 +38,12 @@ import {
   setSrsCache,
   unsetSrsCache,
 } from '../../bindings/crypto/bindings/srs.js';
-import { ProvablePure } from '../provable/types/provable-intf.js';
+import {
+  ProvablePure,
+  ProvableType,
+  ProvableTypePure,
+  ToProvable,
+} from '../provable/types/provable-intf.js';
 import { prefixToField } from '../../bindings/lib/binable.js';
 import { prefixes } from '../../bindings/crypto/constants.js';
 
@@ -530,8 +539,8 @@ let SideloadedTag = {
 
 function ZkProgram<
   StatementType extends {
-    publicInput?: FlexibleProvablePure<any>;
-    publicOutput?: FlexibleProvablePure<any>;
+    publicInput?: ProvableTypePure;
+    publicOutput?: ProvableTypePure;
   },
   Types extends {
     // TODO: how to prevent a method called `compile` from type-checking?
@@ -588,8 +597,12 @@ function ZkProgram<
   >;
 } {
   let methods = config.methods;
-  let publicInputType: ProvablePure<any> = config.publicInput ?? Undefined;
-  let publicOutputType: ProvablePure<any> = config.publicOutput ?? Void;
+  let publicInputType: ProvablePure<any> = ProvableType.getPure(
+    config.publicInput ?? Undefined
+  );
+  let publicOutputType: ProvablePure<any> = ProvableType.getPure(
+    config.publicOutput ?? Void
+  );
 
   let selfTag = { name: config.name };
   type PublicInput = InferProvableOrUndefined<
@@ -1470,13 +1483,18 @@ type Prover<
       ...args: TupleToInstances<Args>
     ) => Promise<Proof<PublicInput, PublicOutput>>;
 
-type ProvableOrUndefined<A> = A extends undefined ? typeof Undefined : A;
-type ProvableOrVoid<A> = A extends undefined ? typeof Void : A;
+type ProvableOrUndefined<A extends ProvableType | undefined> =
+  A extends ProvableType ? ToProvable<A> : typeof Undefined;
+type ProvableOrVoid<A extends ProvableType | undefined> = A extends ProvableType
+  ? ToProvable<A>
+  : typeof Void;
+
+type InferProvableOrUndefined<A extends ProvableType | undefined> =
+  A extends ProvableType ? InferProvableType<A> : undefined;
 
-type InferProvableOrUndefined<A> = A extends undefined
-  ? undefined
-  : InferProvable<A>;
-type InferProvableOrVoid<A> = A extends undefined ? void : InferProvable<A>;
+type InferProvableOrVoid<A> = A extends ProvableType
+  ? InferProvableType<A>
+  : void;
 
 type UnwrapPromise<P> = P extends Promise<infer T> ? T : never;
 
diff --git a/src/lib/provable/types/provable-derivers.ts b/src/lib/provable/types/provable-derivers.ts
index ff6f7720d9..5e2476b330 100644
--- a/src/lib/provable/types/provable-derivers.ts
+++ b/src/lib/provable/types/provable-derivers.ts
@@ -1,4 +1,10 @@
-import { Provable, ProvableHashable, ProvablePure } from './provable-intf.js';
+import {
+  Provable,
+  ProvableHashable,
+  ProvablePure,
+  ProvableType,
+  ToProvable,
+} from './provable-intf.js';
 import type { Field } from '../wrapped.js';
 import {
   createDerivers,
@@ -35,6 +41,7 @@ export {
   NonMethods,
   HashInput,
   InferProvable,
+  InferProvableType,
   InferJson,
   InferredProvable,
   IsPure,
@@ -56,6 +63,7 @@ type ProvablePureExtended<T, TValue = any, TJson = any> = ProvablePure<
   ProvableExtension<T, TJson>;
 
 type InferProvable<T> = GenericInferProvable<T, Field>;
+type InferProvableType<T extends ProvableType> = InferProvable<ToProvable<T>>;
 type InferredProvable<T> = GenericInferredProvable<T, Field>;
 type IsPure<T> = GenericIsPure<T, Field>;
 type ProvableInferPureFrom<A, T, V> = IsPure<A> extends true
diff --git a/src/lib/provable/types/provable-intf.ts b/src/lib/provable/types/provable-intf.ts
index 100cf8df0c..08cd6bcf5b 100644
--- a/src/lib/provable/types/provable-intf.ts
+++ b/src/lib/provable/types/provable-intf.ts
@@ -6,6 +6,7 @@ export {
   ProvableWithEmpty,
   ProvableHashable,
   ProvableType,
+  ProvableTypePure,
   ToProvable,
 };
 
@@ -106,7 +107,10 @@ type ProvableHashable<T, TValue = any> = ProvableWithEmpty<T, TValue> & {
 
 // helpers to accept { provable: Type } instead of Type
 
-type ProvableType<T, V = any> = Provable<T, V> | { provable: Provable<T, V> };
+type SelfOrOnProperty<A> = { provable: A } | A;
+
+type ProvableType<T = any, V = any> = SelfOrOnProperty<Provable<T, V>>;
+type ProvableTypePure<T = any, V = any> = SelfOrOnProperty<ProvablePure<T, V>>;
 
 type ToProvable<A extends ProvableType<any>> = A extends { provable: infer P }
   ? P
@@ -116,4 +120,7 @@ const ProvableType = {
   get<T, V>(type: ProvableType<T, V>): Provable<T, V> {
     return 'provable' in type ? type.provable : type;
   },
+  getPure<T, V>(type: ProvableTypePure<T, V>): ProvablePure<T, V> {
+    return 'provable' in type ? type.provable : type;
+  },
 };

From 7ba888e5556f7c82a988fff083cf6927ddcc0aa4 Mon Sep 17 00:00:00 2001
From: Gregor <gregor.mitscha-baude@gmx.at>
Date: Tue, 16 Jul 2024 11:02:11 +0200
Subject: [PATCH 15/32] remove .provable in zkprogram public inputs

---
 src/examples/crypto/ecdsa/ecdsa.ts             |  4 ++--
 src/examples/crypto/sha256/sha256.ts           |  2 +-
 .../test/foreign-field-gadgets.unit-test.ts    |  2 +-
 src/lib/provable/test/keccak.unit-test.ts      |  4 ++--
 src/lib/provable/test/sha256.unit-test.ts      |  2 +-
 src/tests/inductive-proofs-small.ts            |  7 +++++--
 src/tests/inductive-proofs.ts                  | 18 ++++++++++++------
 7 files changed, 24 insertions(+), 15 deletions(-)

diff --git a/src/examples/crypto/ecdsa/ecdsa.ts b/src/examples/crypto/ecdsa/ecdsa.ts
index 3d7c1a9d5c..19afc9f071 100644
--- a/src/examples/crypto/ecdsa/ecdsa.ts
+++ b/src/examples/crypto/ecdsa/ecdsa.ts
@@ -16,7 +16,7 @@ class Bytes32 extends Bytes(32) {}
 
 const keccakAndEcdsa = ZkProgram({
   name: 'ecdsa',
-  publicInput: Bytes32.provable,
+  publicInput: Bytes32,
   publicOutput: Bool,
 
   methods: {
@@ -31,7 +31,7 @@ const keccakAndEcdsa = ZkProgram({
 
 const ecdsa = ZkProgram({
   name: 'ecdsa-only',
-  publicInput: Scalar.provable,
+  publicInput: Scalar,
   publicOutput: Bool,
 
   methods: {
diff --git a/src/examples/crypto/sha256/sha256.ts b/src/examples/crypto/sha256/sha256.ts
index a6e335fd7f..c8e83c6483 100644
--- a/src/examples/crypto/sha256/sha256.ts
+++ b/src/examples/crypto/sha256/sha256.ts
@@ -6,7 +6,7 @@ class Bytes12 extends Bytes(12) {}
 
 let SHA256Program = ZkProgram({
   name: 'sha256',
-  publicOutput: Bytes(32).provable,
+  publicOutput: Bytes(32),
   methods: {
     sha256: {
       privateInputs: [Bytes12.provable],
diff --git a/src/lib/provable/test/foreign-field-gadgets.unit-test.ts b/src/lib/provable/test/foreign-field-gadgets.unit-test.ts
index 211f8fd122..85154874d4 100644
--- a/src/lib/provable/test/foreign-field-gadgets.unit-test.ts
+++ b/src/lib/provable/test/foreign-field-gadgets.unit-test.ts
@@ -173,7 +173,7 @@ let signs = [1n, -1n, -1n, 1n] satisfies (-1n | 1n)[];
 
 let ffProgram = ZkProgram({
   name: 'foreign-field',
-  publicOutput: Field3.provable,
+  publicOutput: Field3,
   methods: {
     sumchain: {
       privateInputs: [Provable.Array(Field3.provable, chainLength)],
diff --git a/src/lib/provable/test/keccak.unit-test.ts b/src/lib/provable/test/keccak.unit-test.ts
index 1678b0a231..92e9eb8854 100644
--- a/src/lib/provable/test/keccak.unit-test.ts
+++ b/src/lib/provable/test/keccak.unit-test.ts
@@ -121,8 +121,8 @@ const preImageLength = 32;
 // No need to test Ethereum because it's just a special case of preNist
 const KeccakProgram = ZkProgram({
   name: `keccak-test-${digestLength}`,
-  publicInput: Bytes(preImageLength).provable,
-  publicOutput: Bytes(digestLengthBytes).provable,
+  publicInput: Bytes(preImageLength),
+  publicOutput: Bytes(digestLengthBytes),
   methods: {
     nistSha3: {
       privateInputs: [],
diff --git a/src/lib/provable/test/sha256.unit-test.ts b/src/lib/provable/test/sha256.unit-test.ts
index c18d661752..097c6fefdb 100644
--- a/src/lib/provable/test/sha256.unit-test.ts
+++ b/src/lib/provable/test/sha256.unit-test.ts
@@ -23,7 +23,7 @@ sample(Random.nat(400), 5).forEach((preimageLength) => {
 
 const Sha256Program = ZkProgram({
   name: `sha256`,
-  publicOutput: Bytes(32).provable,
+  publicOutput: Bytes(32),
   methods: {
     sha256: {
       privateInputs: [Bytes(192).provable],
diff --git a/src/tests/inductive-proofs-small.ts b/src/tests/inductive-proofs-small.ts
index acde6a86f6..f20f7b4fc8 100644
--- a/src/tests/inductive-proofs-small.ts
+++ b/src/tests/inductive-proofs-small.ts
@@ -9,7 +9,7 @@ let MaxProofsVerifiedOne = ZkProgram({
     baseCase: {
       privateInputs: [],
 
-      method(publicInput: Field) {
+      async method(publicInput: Field) {
         publicInput.assertEquals(Field(0));
       },
     },
@@ -17,7 +17,10 @@ let MaxProofsVerifiedOne = ZkProgram({
     mergeOne: {
       privateInputs: [SelfProof],
 
-      method(publicInput: Field, earlierProof: SelfProof<Field, undefined>) {
+      async method(
+        publicInput: Field,
+        earlierProof: SelfProof<Field, undefined>
+      ) {
         earlierProof.verify();
         earlierProof.publicInput.add(1).assertEquals(publicInput);
       },
diff --git a/src/tests/inductive-proofs.ts b/src/tests/inductive-proofs.ts
index 101487dae5..ebf22754f5 100644
--- a/src/tests/inductive-proofs.ts
+++ b/src/tests/inductive-proofs.ts
@@ -9,7 +9,7 @@ let MaxProofsVerifiedZero = ZkProgram({
     baseCase: {
       privateInputs: [],
 
-      method(publicInput: Field) {
+      async method(publicInput: Field) {
         publicInput.assertEquals(Field(0));
       },
     },
@@ -24,7 +24,7 @@ let MaxProofsVerifiedOne = ZkProgram({
     baseCase: {
       privateInputs: [],
 
-      method(publicInput: Field) {
+      async method(publicInput: Field) {
         publicInput.assertEquals(Field(0));
       },
     },
@@ -32,7 +32,10 @@ let MaxProofsVerifiedOne = ZkProgram({
     mergeOne: {
       privateInputs: [SelfProof],
 
-      method(publicInput: Field, earlierProof: SelfProof<Field, undefined>) {
+      async method(
+        publicInput: Field,
+        earlierProof: SelfProof<Field, undefined>
+      ) {
         earlierProof.verify();
         earlierProof.publicInput.add(1).assertEquals(publicInput);
       },
@@ -48,7 +51,7 @@ let MaxProofsVerifiedTwo = ZkProgram({
     baseCase: {
       privateInputs: [],
 
-      method(publicInput: Field) {
+      async method(publicInput: Field) {
         publicInput.assertEquals(Field(0));
       },
     },
@@ -56,7 +59,10 @@ let MaxProofsVerifiedTwo = ZkProgram({
     mergeOne: {
       privateInputs: [SelfProof],
 
-      method(publicInput: Field, earlierProof: SelfProof<Field, undefined>) {
+      async method(
+        publicInput: Field,
+        earlierProof: SelfProof<Field, undefined>
+      ) {
         earlierProof.verify();
         earlierProof.publicInput.add(1).assertEquals(publicInput);
       },
@@ -65,7 +71,7 @@ let MaxProofsVerifiedTwo = ZkProgram({
     mergeTwo: {
       privateInputs: [SelfProof, SelfProof],
 
-      method(
+      async method(
         publicInput: Field,
         p1: SelfProof<Field, undefined>,
         p2: SelfProof<Field, undefined>

From aaf0263e624c5c8a60d4dd55b7fb6f34a1d78477 Mon Sep 17 00:00:00 2001
From: Gregor <gregor.mitscha-baude@gmx.at>
Date: Tue, 16 Jul 2024 11:18:18 +0200
Subject: [PATCH 16/32] support no .provable in zkprogram private inputs

---
 src/lib/proof-system/zkprogram.ts | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/src/lib/proof-system/zkprogram.ts b/src/lib/proof-system/zkprogram.ts
index 54644ddbc1..11f183a342 100644
--- a/src/lib/proof-system/zkprogram.ts
+++ b/src/lib/proof-system/zkprogram.ts
@@ -544,6 +544,7 @@ function ZkProgram<
   },
   Types extends {
     // TODO: how to prevent a method called `compile` from type-checking?
+    // TODO: solution: put method calls on a separate namespace! like `await program.prove.myMethod()`
     [I in string]: Tuple<PrivateInput>;
   }
 >(
@@ -777,8 +778,8 @@ function ZkProgram<
 
 type ZkProgram<
   S extends {
-    publicInput?: FlexibleProvablePure<any>;
-    publicOutput?: FlexibleProvablePure<any>;
+    publicInput?: ProvableTypePure;
+    publicOutput?: ProvableTypePure;
   },
   T extends {
     [I in string]: Tuple<PrivateInput>;
@@ -1438,7 +1439,9 @@ function Prover<ProverData>() {
 
 type Infer<T> = T extends Subclass<typeof ProofBase>
   ? InstanceType<T>
-  : InferProvable<T>;
+  : T extends ProvableType
+  ? InferProvableType<T>
+  : never;
 
 type Tuple<T> = [T, ...T[]] | [];
 type TupleToInstances<T> = {
@@ -1451,7 +1454,7 @@ type Subclass<Class extends new (...args: any) => any> = (new (
   [K in keyof Class]: Class[K];
 } & { prototype: InstanceType<Class> };
 
-type PrivateInput = Provable<any> | Subclass<typeof ProofBase>;
+type PrivateInput = ProvableType | Subclass<typeof ProofBase>;
 
 type Method<
   PublicInput,

From 9f6eec2f1b17529dc2050264842ffa5850aa4a52 Mon Sep 17 00:00:00 2001
From: Gregor <gregor.mitscha-baude@gmx.at>
Date: Tue, 16 Jul 2024 11:18:30 +0200
Subject: [PATCH 17/32] remove .provable in zkprogram private inputs

---
 src/examples/crypto/ecdsa/ecdsa.ts                     |  4 ++--
 src/examples/crypto/rsa/run.ts                         |  6 +-----
 src/examples/crypto/sha256/sha256.ts                   |  2 +-
 src/lib/provable/merkle-tree-indexed.ts                |  2 +-
 src/lib/provable/test/arithmetic.unit-test.ts          |  5 +----
 .../provable/test/foreign-field-gadgets.unit-test.ts   | 10 +++++-----
 src/lib/provable/test/sha256.unit-test.ts              |  2 +-
 tests/vk-regression/diverse-zk-program.ts              |  8 ++------
 8 files changed, 14 insertions(+), 25 deletions(-)

diff --git a/src/examples/crypto/ecdsa/ecdsa.ts b/src/examples/crypto/ecdsa/ecdsa.ts
index 19afc9f071..020dbf9d83 100644
--- a/src/examples/crypto/ecdsa/ecdsa.ts
+++ b/src/examples/crypto/ecdsa/ecdsa.ts
@@ -21,7 +21,7 @@ const keccakAndEcdsa = ZkProgram({
 
   methods: {
     verifyEcdsa: {
-      privateInputs: [Ecdsa.provable, Secp256k1.provable],
+      privateInputs: [Ecdsa, Secp256k1],
       async method(message: Bytes32, signature: Ecdsa, publicKey: Secp256k1) {
         return signature.verifyV2(message, publicKey);
       },
@@ -36,7 +36,7 @@ const ecdsa = ZkProgram({
 
   methods: {
     verifySignedHash: {
-      privateInputs: [Ecdsa.provable, Secp256k1.provable],
+      privateInputs: [Ecdsa, Secp256k1],
       async method(message: Scalar, signature: Ecdsa, publicKey: Secp256k1) {
         return signature.verifySignedHashV2(message, publicKey);
       },
diff --git a/src/examples/crypto/rsa/run.ts b/src/examples/crypto/rsa/run.ts
index c2c9b7ed53..3d27fb7ed9 100644
--- a/src/examples/crypto/rsa/run.ts
+++ b/src/examples/crypto/rsa/run.ts
@@ -7,11 +7,7 @@ let rsaZkProgram = ZkProgram({
 
   methods: {
     verifyRsa65537: {
-      privateInputs: [
-        Bigint2048.provable,
-        Bigint2048.provable,
-        Bigint2048.provable,
-      ],
+      privateInputs: [Bigint2048, Bigint2048, Bigint2048],
 
       async method(
         message: Bigint2048,
diff --git a/src/examples/crypto/sha256/sha256.ts b/src/examples/crypto/sha256/sha256.ts
index c8e83c6483..5ffe43c333 100644
--- a/src/examples/crypto/sha256/sha256.ts
+++ b/src/examples/crypto/sha256/sha256.ts
@@ -9,7 +9,7 @@ let SHA256Program = ZkProgram({
   publicOutput: Bytes(32),
   methods: {
     sha256: {
-      privateInputs: [Bytes12.provable],
+      privateInputs: [Bytes12],
       async method(xs: Bytes12) {
         return Gadgets.SHA256.hash(xs);
       },
diff --git a/src/lib/provable/merkle-tree-indexed.ts b/src/lib/provable/merkle-tree-indexed.ts
index 4010ee309c..568a47798a 100644
--- a/src/lib/provable/merkle-tree-indexed.ts
+++ b/src/lib/provable/merkle-tree-indexed.ts
@@ -36,7 +36,7 @@ export { Leaf };
  * ZkProgram({
  *   methods: {
  *     test: {
- *       privateInputs: [MerkleMap.provable, Field],
+ *       privateInputs: [MerkleMap, Field],
  *
  *       method(map: MerkleMap, key: Field) {
  *         // get the value associated with `key`
diff --git a/src/lib/provable/test/arithmetic.unit-test.ts b/src/lib/provable/test/arithmetic.unit-test.ts
index ec02667fc3..3ce9ce07f0 100644
--- a/src/lib/provable/test/arithmetic.unit-test.ts
+++ b/src/lib/provable/test/arithmetic.unit-test.ts
@@ -12,10 +12,7 @@ import { assert } from '../gadgets/common.js';
 
 let Arithmetic = ZkProgram({
   name: 'arithmetic',
-  publicOutput: provable({
-    remainder: Field,
-    quotient: Field,
-  }),
+  publicOutput: provable({ remainder: Field, quotient: Field }),
   methods: {
     divMod32: {
       privateInputs: [Field],
diff --git a/src/lib/provable/test/foreign-field-gadgets.unit-test.ts b/src/lib/provable/test/foreign-field-gadgets.unit-test.ts
index 85154874d4..460e84c611 100644
--- a/src/lib/provable/test/foreign-field-gadgets.unit-test.ts
+++ b/src/lib/provable/test/foreign-field-gadgets.unit-test.ts
@@ -182,32 +182,32 @@ let ffProgram = ZkProgram({
       },
     },
     mulWithBoundsCheck: {
-      privateInputs: [Field3.provable, Field3.provable],
+      privateInputs: [Field3, Field3],
       async method(x, y) {
         ForeignField.assertAlmostReduced([x, y], F.modulus);
         return ForeignField.mul(x, y, F.modulus);
       },
     },
     mul: {
-      privateInputs: [Field3.provable, Field3.provable],
+      privateInputs: [Field3, Field3],
       async method(x, y) {
         return ForeignField.mul(x, y, F.modulus);
       },
     },
     inv: {
-      privateInputs: [Field3.provable],
+      privateInputs: [Field3],
       async method(x) {
         return ForeignField.inv(x, F.modulus);
       },
     },
     div: {
-      privateInputs: [Field3.provable, Field3.provable],
+      privateInputs: [Field3, Field3],
       async method(x, y) {
         return ForeignField.div(x, y, F.modulus);
       },
     },
     assertLessThan: {
-      privateInputs: [Field3.provable, Field3.provable],
+      privateInputs: [Field3, Field3],
       async method(x, y) {
         ForeignField.assertLessThan(x, y);
         return x;
diff --git a/src/lib/provable/test/sha256.unit-test.ts b/src/lib/provable/test/sha256.unit-test.ts
index 097c6fefdb..16233721e9 100644
--- a/src/lib/provable/test/sha256.unit-test.ts
+++ b/src/lib/provable/test/sha256.unit-test.ts
@@ -26,7 +26,7 @@ const Sha256Program = ZkProgram({
   publicOutput: Bytes(32),
   methods: {
     sha256: {
-      privateInputs: [Bytes(192).provable],
+      privateInputs: [Bytes(192)],
       async method(preImage: Bytes) {
         return Gadgets.SHA256.hash(preImage);
       },
diff --git a/tests/vk-regression/diverse-zk-program.ts b/tests/vk-regression/diverse-zk-program.ts
index 69333903cf..08649dc844 100644
--- a/tests/vk-regression/diverse-zk-program.ts
+++ b/tests/vk-regression/diverse-zk-program.ts
@@ -32,11 +32,7 @@ const diverse = ZkProgram({
   methods: {
     // foreign field / curve ops, multi-range checks
     ecdsa: {
-      privateInputs: [
-        Secp256k1Scalar.provable,
-        Secp256k1Signature.provable,
-        Secp256k1.provable,
-      ],
+      privateInputs: [Secp256k1Scalar, Secp256k1Signature, Secp256k1],
       async method(
         message: Secp256k1Scalar,
         signature: Secp256k1Signature,
@@ -48,7 +44,7 @@ const diverse = ZkProgram({
 
     // bitwise gadgets
     sha3: {
-      privateInputs: [Bytes128.provable],
+      privateInputs: [Bytes128],
       async method(xs: Bytes128) {
         Hash.SHA3_256.hash(xs);
       },

From 46f017821ca920eb414dedc2e19729c5fc42a033 Mon Sep 17 00:00:00 2001
From: Gregor <gregor.mitscha-baude@gmx.at>
Date: Tue, 16 Jul 2024 11:56:04 +0200
Subject: [PATCH 18/32] support no .provable in Provable utils

---
 src/lib/provable/provable.ts     | 64 +++++++++++++++++++-------------
 src/lib/provable/types/struct.ts |  4 +-
 2 files changed, 42 insertions(+), 26 deletions(-)

diff --git a/src/lib/provable/provable.ts b/src/lib/provable/provable.ts
index 58f98565b7..456ea56007 100644
--- a/src/lib/provable/provable.ts
+++ b/src/lib/provable/provable.ts
@@ -5,13 +5,17 @@
  */
 import { Bool } from './bool.js';
 import { Field } from './field.js';
-import type { Provable as Provable_ } from './types/provable-intf.js';
-import type { FlexibleProvable, ProvableExtended } from './types/struct.js';
+import { Provable as Provable_, ProvableType } from './types/provable-intf.js';
+import type {
+  FlexibleProvable,
+  FlexibleProvableType,
+  ProvableExtended,
+} from './types/struct.js';
 import { Context } from '../util/global-context.js';
 import {
   HashInput,
   InferJson,
-  InferProvable,
+  InferProvableType,
   InferredProvable,
 } from './types/provable-derivers.js';
 import {
@@ -23,6 +27,7 @@ import {
 } from './core/provable-context.js';
 import { witness, witnessAsync, witnessFields } from './types/witness.js';
 import { InferValue } from '../../bindings/lib/provable-generic.js';
+import { ToProvable } from '../../lib/provable/types/provable-intf.js';
 
 // external API
 export { Provable };
@@ -229,7 +234,8 @@ const Provable = {
   /**
    * Returns a constant version of a provable type.
    */
-  toConstant<T>(type: Provable<T>, value: T) {
+  toConstant<T>(type: ProvableType<T>, value: T) {
+    type = ProvableType.get(type);
     return type.fromFields(
       type.toFields(value).map((x) => x.toConstant()),
       type.toAuxiliary(value)
@@ -241,7 +247,7 @@ type ToFieldable = { toFields(): Field[] };
 
 // general provable methods
 
-function assertEqual<T>(type: FlexibleProvable<T>, x: T, y: T): void;
+function assertEqual<T>(type: FlexibleProvableType<T>, x: T, y: T): void;
 function assertEqual<T extends ToFieldable>(x: T, y: T): void;
 function assertEqual(typeOrX: any, xOrY: any, yOrUndefined?: any) {
   if (yOrUndefined === undefined) {
@@ -258,7 +264,8 @@ function assertEqualImplicit<T extends ToFieldable>(x: T, y: T) {
     xs[i].assertEquals(ys[i]);
   }
 }
-function assertEqualExplicit<T>(type: Provable<T>, x: T, y: T) {
+function assertEqualExplicit<T>(type: ProvableType<T>, x: T, y: T) {
+  type = ProvableType.get(type);
   let xs = type.toFields(x);
   let ys = type.toFields(y);
   for (let i = 0; i < xs.length; i++) {
@@ -266,7 +273,7 @@ function assertEqualExplicit<T>(type: Provable<T>, x: T, y: T) {
   }
 }
 
-function equal<T>(type: FlexibleProvable<T>, x: T, y: T): Bool;
+function equal<T>(type: FlexibleProvableType<T>, x: T, y: T): Bool;
 function equal<T extends ToFieldable>(x: T, y: T): Bool;
 function equal(typeOrX: any, xOrY: any, yOrUndefined?: any) {
   if (yOrUndefined === undefined) {
@@ -284,13 +291,14 @@ function equalImplicit<T extends ToFieldable>(x: T, y: T) {
   checkLength('Provable.equal', xs, ys);
   return xs.map((x, i) => x.equals(ys[i])).reduce(Bool.and);
 }
-function equalExplicit<T>(type: Provable<T>, x: T, y: T) {
+function equalExplicit<T>(type: ProvableType<T>, x: T, y: T) {
+  type = ProvableType.get(type);
   let xs = type.toFields(x);
   let ys = type.toFields(y);
   return xs.map((x, i) => x.equals(ys[i])).reduce(Bool.and);
 }
 
-function if_<T>(condition: Bool, type: FlexibleProvable<T>, x: T, y: T): T;
+function if_<T>(condition: Bool, type: FlexibleProvableType<T>, x: T, y: T): T;
 function if_<T extends ToFieldable>(condition: Bool, x: T, y: T): T;
 function if_(condition: Bool, typeOrX: any, xOrY: any, yOrUndefined?: any) {
   if (yOrUndefined === undefined) {
@@ -312,7 +320,8 @@ function ifField(b: Field, x: Field, y: Field) {
   return b.mul(x.sub(y)).add(y).seal();
 }
 
-function ifExplicit<T>(condition: Bool, type: Provable<T>, x: T, y: T): T {
+function ifExplicit<T>(condition: Bool, type: ProvableType<T>, x: T, y: T): T {
+  type = ProvableType.get(type);
   let xs = type.toFields(x);
   let ys = type.toFields(y);
   let b = condition.toField();
@@ -352,12 +361,13 @@ function ifImplicit<T extends ToFieldable>(condition: Bool, x: T, y: T): T {
   return ifExplicit(condition, type as any as Provable<T>, x, y);
 }
 
-function switch_<T, A extends FlexibleProvable<T>>(
+function switch_<T, A extends FlexibleProvableType<T>>(
   mask: Bool[],
   type: A,
   values: T[],
   { allowNonExclusive = false } = {}
 ): T {
+  let type_ = ProvableType.get(type as ProvableType<T>);
   // picks the value at the index where mask is true
   let nValues = values.length;
   if (mask.length !== nValues)
@@ -375,35 +385,37 @@ function switch_<T, A extends FlexibleProvable<T>>(
   };
   if (mask.every((b) => b.toField().isConstant())) checkMask();
   else Provable.asProver(checkMask);
-  let size = type.sizeInFields();
+  let size = type_.sizeInFields();
   let fields = Array(size).fill(new Field(0));
   for (let i = 0; i < nValues; i++) {
-    let valueFields = type.toFields(values[i]);
+    let valueFields = type_.toFields(values[i]);
     let maskField = mask[i].toField();
     for (let j = 0; j < size; j++) {
       let maybeField = valueFields[j].mul(maskField);
       fields[j] = fields[j].add(maybeField);
     }
   }
-  let aux = auxiliary(type as Provable<T>, () => {
+  let aux = auxiliary(type_, () => {
     let i = mask.findIndex((b) => b.toBoolean());
     if (i === -1) return undefined;
     return values[i];
   });
-  return (type as Provable<T>).fromFields(fields, aux);
+  return type_.fromFields(fields, aux);
 }
 
 function assertEqualIf<
-  A extends Provable<any>,
-  T extends InferProvable<A> = InferProvable<A>
+  A extends ProvableType<any>,
+  T extends InferProvableType<A> = InferProvableType<A>
 >(enabled: Bool, type: A, x: T, y: T) {
   // if the condition is disabled, we check the trivial identity x === x instead
   let xOrY = ifExplicit<T>(enabled, type, y, x);
   assertEqual(type, x, xOrY);
 }
 
-function isConstant<T>(type: Provable<T>, x: T): boolean {
-  return type.toFields(x).every((x) => x.isConstant());
+function isConstant<T>(type: ProvableType<T>, x: T): boolean {
+  return ProvableType.get(type)
+    .toFields(x)
+    .every((x) => x.isConstant());
 }
 
 // logging in provable code
@@ -498,14 +510,16 @@ function getBlindingValue() {
 }
 
 // TODO this should return a class, like Struct, so you can just use `class Array3 extends Provable.Array(Field, 3) {}`
-function provableArray<A extends FlexibleProvable<any>>(
+function provableArray<A extends FlexibleProvableType<any>>(
   elementType: A,
   length: number
-): InferredProvable<A[]> {
-  type T = InferProvable<A>;
-  type TValue = InferValue<A>;
-  type TJson = InferJson<A>;
-  let type = elementType as ProvableExtended<T, TValue, TJson>;
+): InferredProvable<ToProvable<A>[]> {
+  type T = InferProvableType<A>;
+  type TValue = InferValue<ToProvable<A>>;
+  type TJson = InferJson<ToProvable<A>>;
+  let type = ProvableType.get(
+    elementType as ProvableType<T>
+  ) as ProvableExtended<T, TValue, TJson>;
   return {
     /**
      * Returns the size of this structure in {@link Field} elements.
diff --git a/src/lib/provable/types/struct.ts b/src/lib/provable/types/struct.ts
index 96d962d4e6..4569997eeb 100644
--- a/src/lib/provable/types/struct.ts
+++ b/src/lib/provable/types/struct.ts
@@ -13,7 +13,7 @@ import type {
 } from './provable-derivers.js';
 import { Provable } from '../provable.js';
 import { DynamicProof, Proof } from '../../proof-system/zkprogram.js';
-import { ProvablePure } from './provable-intf.js';
+import { ProvablePure, ProvableType } from './provable-intf.js';
 import { From, InferValue } from '../../../bindings/lib/provable-generic.js';
 
 // external API
@@ -23,6 +23,7 @@ export {
   Struct,
   FlexibleProvable,
   FlexibleProvablePure,
+  FlexibleProvableType,
 };
 
 // internal API
@@ -59,6 +60,7 @@ type StructPure<T> = ProvablePureExtended<NonMethods<T>> &
   Constructor<T> & { _isStruct: true };
 type FlexibleProvable<T> = Provable<T> | Struct<T>;
 type FlexibleProvablePure<T> = ProvablePure<T> | StructPure<T>;
+type FlexibleProvableType<T> = ProvableType<T> | Struct<T>;
 
 type Constructor<T> = new (...args: any) => T;
 type AnyConstructor = Constructor<any>;

From 6aa3cabbb9bbe8b66ba85fe8a7770bda0229d0d3 Mon Sep 17 00:00:00 2001
From: Gregor <gregor.mitscha-baude@gmx.at>
Date: Tue, 16 Jul 2024 11:56:12 +0200
Subject: [PATCH 19/32] remove .provable in Provable utils

---
 src/examples/crypto/rsa/rsa.ts                    |  2 +-
 src/lib/mina/actions/batch-reducer.ts             |  2 +-
 src/lib/mina/actions/offchain-state-rollup.ts     |  8 ++------
 src/lib/provable/crypto/foreign-curve.ts          |  2 +-
 src/lib/provable/foreign-field.ts                 |  8 ++------
 src/lib/provable/gadgets/elliptic-curve.ts        | 15 +++++++--------
 src/lib/provable/test/foreign-curve.unit-test.ts  |  4 ++--
 .../test/foreign-field-gadgets.unit-test.ts       |  4 ++--
 src/lib/provable/test/merkle-tree.unit-test.ts    |  2 +-
 9 files changed, 19 insertions(+), 28 deletions(-)

diff --git a/src/examples/crypto/rsa/rsa.ts b/src/examples/crypto/rsa/rsa.ts
index 78de61b6b9..50db5691e0 100644
--- a/src/examples/crypto/rsa/rsa.ts
+++ b/src/examples/crypto/rsa/rsa.ts
@@ -170,7 +170,7 @@ function rsaVerify65537(
   x = modulus.modMul(x, signature);
 
   // check that x == message
-  Provable.assertEqual(Bigint2048.provable, message, x);
+  Provable.assertEqual(Bigint2048, message, x);
 }
 
 /**
diff --git a/src/lib/mina/actions/batch-reducer.ts b/src/lib/mina/actions/batch-reducer.ts
index b02351c25e..17f2a49db4 100644
--- a/src/lib/mina/actions/batch-reducer.ts
+++ b/src/lib/mina/actions/batch-reducer.ts
@@ -385,7 +385,7 @@ class BatchReducer<
       // we make it easier to write the reducer code by making sure dummy actions have dummy values
       hashedAction = Provable.if(
         isDummy,
-        HashedActionT.provable,
+        HashedActionT,
         emptyHashedAction,
         hashedAction
       );
diff --git a/src/lib/mina/actions/offchain-state-rollup.ts b/src/lib/mina/actions/offchain-state-rollup.ts
index 133d5d75bb..411606a819 100644
--- a/src/lib/mina/actions/offchain-state-rollup.ts
+++ b/src/lib/mina/actions/offchain-state-rollup.ts
@@ -169,7 +169,7 @@ function OffchainStateRollup({
        */
       firstBatch: {
         // [actions, tree]
-        privateInputs: [ActionIterator.provable, IndexedMerkleMapN.provable],
+        privateInputs: [ActionIterator, IndexedMerkleMapN],
 
         async method(
           stateA: OffchainStateCommitments,
@@ -189,11 +189,7 @@ function OffchainStateRollup({
        */
       nextBatch: {
         // [actions, tree, proof]
-        privateInputs: [
-          ActionIterator.provable,
-          IndexedMerkleMapN.provable,
-          SelfProof,
-        ],
+        privateInputs: [ActionIterator, IndexedMerkleMapN, SelfProof],
 
         async method(
           stateA: OffchainStateCommitments,
diff --git a/src/lib/provable/crypto/foreign-curve.ts b/src/lib/provable/crypto/foreign-curve.ts
index 323478b87a..855e3e9514 100644
--- a/src/lib/provable/crypto/foreign-curve.ts
+++ b/src/lib/provable/crypto/foreign-curve.ts
@@ -97,7 +97,7 @@ class ForeignCurve {
    * See {@link FieldVar} to understand constants vs variables.
    */
   isConstant() {
-    return Provable.isConstant(this.Constructor.provable, this);
+    return Provable.isConstant(this.Constructor, this);
   }
 
   /**
diff --git a/src/lib/provable/foreign-field.ts b/src/lib/provable/foreign-field.ts
index 1c743713df..4f6a736c5a 100644
--- a/src/lib/provable/foreign-field.ts
+++ b/src/lib/provable/foreign-field.ts
@@ -303,11 +303,7 @@ class ForeignField {
         }
         return new this.Constructor.Canonical(this.value);
       }
-      Provable.assertEqual(
-        this.Constructor.provable,
-        this,
-        new this.Constructor(y)
-      );
+      Provable.assertEqual(this.Constructor, this, new this.Constructor(y));
       if (isConstant(y) || y instanceof this.Constructor.Canonical) {
         return new this.Constructor.Canonical(this.value);
       } else if (y instanceof this.Constructor.AlmostReduced) {
@@ -612,7 +608,7 @@ function isConstant(x: bigint | number | string | ForeignField) {
  *
  * This function returns the `Unreduced` class, which will cause the minimum amount of range checks to be created by default.
  * If you want to do multiplication, you have two options:
- * - create your field elements using the {@link ForeignField.AlmostReduced} constructor, or using the `.provable` type on that class.
+ * - create your field elements using the {@link ForeignField.AlmostReduced} constructor.
  * ```ts
  * let x = Provable.witness(ForeignField.AlmostReduced, () => 5n);
  * ```
diff --git a/src/lib/provable/gadgets/elliptic-curve.ts b/src/lib/provable/gadgets/elliptic-curve.ts
index afab631f6e..2b70b82b87 100644
--- a/src/lib/provable/gadgets/elliptic-curve.ts
+++ b/src/lib/provable/gadgets/elliptic-curve.ts
@@ -281,7 +281,7 @@ function verifyEcdsaGeneric(
   // if we allowed non-canonical Rx, the prover could make verify() return false on a valid signature, by adding a multiple of `Curve.order` to Rx.
   ForeignField.assertLessThan(Rx, Curve.order);
 
-  return Provable.equal(Field3.provable, Rx, r);
+  return Provable.equal(Field3, Rx, r);
 }
 
 /**
@@ -545,7 +545,7 @@ function multiScalarMul(
         let added = add(sum, sjP, Curve);
 
         // handle degenerate case (if sj = 0, Gj is all zeros and the add result is garbage)
-        sum = Provable.if(sj.equals(0), Point.provable, sum, added);
+        sum = Provable.if(sj.equals(0), Point, sum, added);
       }
     }
 
@@ -576,7 +576,7 @@ function multiScalarMul(
 function negateIf(condition: Field, P: Point, f: bigint) {
   let y = Provable.if(
     Bool.Unsafe.fromField(condition),
-    Field3.provable,
+    Field3,
     ForeignField.negate(P.y, f),
     P.y
   );
@@ -621,13 +621,13 @@ function decomposeNoRangeCheck(Curve: CurveAffine, s: Field3) {
   // prove that s1*lambda = s - s0
   let lambda = Provable.if(
     Bool.Unsafe.fromField(s1Negative),
-    Field3.provable,
+    Field3,
     Field3.from(Curve.Scalar.negate(Curve.Endo.scalar)),
     Field3.from(Curve.Endo.scalar)
   );
   let rhs = Provable.if(
     Bool.Unsafe.fromField(s0Negative),
-    Field3.provable,
+    Field3,
     ForeignField.Sum(s).add(s0).finish(Curve.order),
     ForeignField.Sum(s).sub(s0).finish(Curve.order)
   );
@@ -768,7 +768,7 @@ const Point = {
   toBigint({ x, y }: Point) {
     return { x: Field3.toBigint(x), y: Field3.toBigint(y), infinity: false };
   },
-  isConstant: (P: Point) => Provable.isConstant(Point.provable, P),
+  isConstant: (P: Point) => Provable.isConstant(Point, P),
 
   /**
    * Random point on the curve.
@@ -787,8 +787,7 @@ const EcdsaSignature = {
   toBigint({ r, s }: Ecdsa.Signature): Ecdsa.signature {
     return { r: Field3.toBigint(r), s: Field3.toBigint(s) };
   },
-  isConstant: (S: Ecdsa.Signature) =>
-    Provable.isConstant(EcdsaSignature.provable, S),
+  isConstant: (S: Ecdsa.Signature) => Provable.isConstant(EcdsaSignature, S),
 
   /**
    * Create an {@link EcdsaSignature} from a raw 130-char hex string as used in
diff --git a/src/lib/provable/test/foreign-curve.unit-test.ts b/src/lib/provable/test/foreign-curve.unit-test.ts
index c7bdaf9c4d..80921d0ea0 100644
--- a/src/lib/provable/test/foreign-curve.unit-test.ts
+++ b/src/lib/provable/test/foreign-curve.unit-test.ts
@@ -17,14 +17,14 @@ function main() {
   let g0 = Provable.witness(Vesta, () => g);
   let one = Provable.witness(Vesta, () => Vesta.generator);
   let h0 = g0.add(one).double().negate();
-  Provable.assertEqual(Vesta.provable, h0, new Vesta(h));
+  Provable.assertEqual(Vesta, h0, new Vesta(h));
 
   h0.assertOnCurve();
   h0.assertInSubgroup();
 
   let scalar0 = Provable.witness(Fp, () => scalar);
   let p0 = h0.scale(scalar0);
-  Provable.assertEqual(Vesta.provable, p0, new Vesta(p));
+  Provable.assertEqual(Vesta, p0, new Vesta(p));
 }
 
 console.time('running constant version');
diff --git a/src/lib/provable/test/foreign-field-gadgets.unit-test.ts b/src/lib/provable/test/foreign-field-gadgets.unit-test.ts
index 460e84c611..d5ec5724ba 100644
--- a/src/lib/provable/test/foreign-field-gadgets.unit-test.ts
+++ b/src/lib/provable/test/foreign-field-gadgets.unit-test.ts
@@ -176,7 +176,7 @@ let ffProgram = ZkProgram({
   publicOutput: Field3,
   methods: {
     sumchain: {
-      privateInputs: [Provable.Array(Field3.provable, chainLength)],
+      privateInputs: [Provable.Array(Field3, chainLength)],
       async method(xs) {
         return ForeignField.sum(xs, signs, F.modulus);
       },
@@ -328,7 +328,7 @@ function assertMulExampleNaive(x: Field3, y: Field3, f: bigint) {
     f
   );
   let rhs = ForeignField.sub(x2, y2, f);
-  Provable.assertEqual(Field3.provable, lhs, rhs);
+  Provable.assertEqual(Field3, lhs, rhs);
 }
 
 let from2 = { from: [f, f] satisfies AnyTuple };
diff --git a/src/lib/provable/test/merkle-tree.unit-test.ts b/src/lib/provable/test/merkle-tree.unit-test.ts
index 8a0e180cc8..427fdfef53 100644
--- a/src/lib/provable/test/merkle-tree.unit-test.ts
+++ b/src/lib/provable/test/merkle-tree.unit-test.ts
@@ -249,7 +249,7 @@ test(
 
       // move the map back to constants
       Provable.asProver(() => {
-        map = Provable.toConstant(MerkleMap.provable, map);
+        map = Provable.toConstant(MerkleMap, map);
       });
     });
 

From 161e92cf72380939ef07b2caf6d644105ca32773 Mon Sep 17 00:00:00 2001
From: Gregor <gregor.mitscha-baude@gmx.at>
Date: Tue, 16 Jul 2024 12:52:52 +0200
Subject: [PATCH 20/32] accept no .provable in merkle list and some other
 generic provable types

---
 src/lib/proof-system/zkprogram.ts          |  4 ++--
 src/lib/provable/gadgets/elliptic-curve.ts | 14 +++++------
 src/lib/provable/merkle-list.ts            | 28 ++++++++++++++--------
 src/lib/provable/packed.ts                 | 23 +++++++++++-------
 src/lib/provable/types/provable-intf.ts    | 24 ++++++++++++-------
 5 files changed, 55 insertions(+), 38 deletions(-)

diff --git a/src/lib/proof-system/zkprogram.ts b/src/lib/proof-system/zkprogram.ts
index 11f183a342..4f9057ba1b 100644
--- a/src/lib/proof-system/zkprogram.ts
+++ b/src/lib/proof-system/zkprogram.ts
@@ -598,10 +598,10 @@ function ZkProgram<
   >;
 } {
   let methods = config.methods;
-  let publicInputType: ProvablePure<any> = ProvableType.getPure(
+  let publicInputType: ProvablePure<any> = ProvableType.get(
     config.publicInput ?? Undefined
   );
-  let publicOutputType: ProvablePure<any> = ProvableType.getPure(
+  let publicOutputType: ProvablePure<any> = ProvableType.get(
     config.publicOutput ?? Void
   );
 
diff --git a/src/lib/provable/gadgets/elliptic-curve.ts b/src/lib/provable/gadgets/elliptic-curve.ts
index 2b70b82b87..91806c9e62 100644
--- a/src/lib/provable/gadgets/elliptic-curve.ts
+++ b/src/lib/provable/gadgets/elliptic-curve.ts
@@ -21,6 +21,7 @@ import { arrayGet, assertNotVectorEquals } from './basic.js';
 import { sliceField3 } from './bit-slices.js';
 import { Hashed } from '../packed.js';
 import { exists } from '../core/exists.js';
+import { ProvableType } from '../types/provable-intf.js';
 
 // external API
 export { EllipticCurve, Point, Ecdsa };
@@ -502,7 +503,7 @@ function multiScalarMul(
 
   // hash points to make array access more efficient
   // a Point is 6 field elements, the hash is just 1 field element
-  const HashedPoint = Hashed.create(Point.provable);
+  const HashedPoint = Hashed.create(Point);
 
   // initialize sum to the initial aggregator, which is expected to be unrelated to any point that this gadget is used with
   // note: this is a trick to ensure _completeness_ of the gadget
@@ -529,16 +530,12 @@ function multiScalarMul(
           sjP =
             windowSize === 1
               ? points[j]
-              : arrayGetGeneric(
-                  HashedPoint.provable,
-                  hashedTables[j],
-                  sj
-                ).unhash();
+              : arrayGetGeneric(HashedPoint, hashedTables[j], sj).unhash();
         } else {
           sjP =
             windowSize === 1
               ? points[j]
-              : arrayGetGeneric(Point.provable, tables[j], sj);
+              : arrayGetGeneric(Point, tables[j], sj);
         }
 
         // ec addition
@@ -743,7 +740,8 @@ function simpleMapToCurve(x: bigint, Curve: CurveAffine) {
  *
  * Assumes that index is in [0, n), returns an unconstrained result otherwise.
  */
-function arrayGetGeneric<T>(type: Provable<T>, array: T[], index: Field) {
+function arrayGetGeneric<T>(type: ProvableType<T>, array: T[], index: Field) {
+  type = ProvableType.get(type);
   // witness result
   let a = Provable.witness(type, () => array[Number(index)]);
   let aFields = type.toFields(a);
diff --git a/src/lib/provable/merkle-list.ts b/src/lib/provable/merkle-list.ts
index 839501f573..7222907c00 100644
--- a/src/lib/provable/merkle-list.ts
+++ b/src/lib/provable/merkle-list.ts
@@ -5,6 +5,7 @@ import { assert } from './gadgets/common.js';
 import { provableFromClass } from './types/provable-derivers.js';
 import { Poseidon, packToFields, ProvableHashable } from './crypto/poseidon.js';
 import { Unconstrained } from './types/unconstrained.js';
+import { ProvableType, WithProvable } from './types/provable-intf.js';
 
 export {
   MerkleListBase,
@@ -280,8 +281,10 @@ class MerkleList<T> implements MerkleListBase<T> {
    * ```
    */
   static create<T>(
-    type: ProvableHashable<T>,
-    nextHash: (hash: Field, value: T) => Field = merkleListHash(type),
+    type: WithProvable<ProvableHashable<T>>,
+    nextHash: (hash: Field, value: T) => Field = merkleListHash(
+      ProvableType.get(type)
+    ),
     emptyHash_ = emptyHash
   ): typeof MerkleList<T> & {
     // override static methods with strict types
@@ -290,8 +293,10 @@ class MerkleList<T> implements MerkleListBase<T> {
     fromReverse: (array: T[]) => MerkleList<T>;
     provable: ProvableHashable<MerkleList<T>>;
   } {
+    let provable = ProvableType.get(type);
+
     class MerkleListTBase extends MerkleList<T> {
-      static _innerProvable = type;
+      static _innerProvable = provable;
 
       static _provable = provableFromClass(MerkleListTBase, {
         hash: Field,
@@ -309,7 +314,7 @@ class MerkleList<T> implements MerkleListBase<T> {
         array = [...array].reverse();
         let { hash, data } = withHashes(array, nextHash, emptyHash_);
         let unconstrained = Unconstrained.witness(() =>
-          data.map((x) => toConstant(type, x))
+          data.map((x) => toConstant(provable, x))
         );
         return new this({ data: unconstrained, hash });
       }
@@ -317,7 +322,7 @@ class MerkleList<T> implements MerkleListBase<T> {
       static fromReverse(array: T[]): MerkleList<T> {
         let { hash, data } = withHashes(array, nextHash, emptyHash_);
         let unconstrained = Unconstrained.witness(() =>
-          data.map((x) => toConstant(type, x))
+          data.map((x) => toConstant(provable, x))
         );
         return new this({ data: unconstrained, hash });
       }
@@ -618,8 +623,10 @@ class MerkleListIterator<T> implements MerkleListIteratorBase<T> {
    * Create a Merkle array type
    */
   static create<T>(
-    type: ProvableHashable<T>,
-    nextHash: (hash: Field, value: T) => Field = merkleListHash(type),
+    type: WithProvable<ProvableHashable<T>>,
+    nextHash: (hash: Field, value: T) => Field = merkleListHash(
+      ProvableType.get(type)
+    ),
     emptyHash_ = emptyHash
   ): typeof MerkleListIterator<T> & {
     from: (array: T[]) => MerkleListIterator<T>;
@@ -628,8 +635,9 @@ class MerkleListIterator<T> implements MerkleListIteratorBase<T> {
     empty: () => MerkleListIterator<T>;
     provable: ProvableHashable<MerkleListIterator<T>>;
   } {
+    let provable = ProvableType.get(type);
     return class Iterator extends MerkleListIterator<T> {
-      static _innerProvable = type;
+      static _innerProvable = ProvableType.get(provable);
 
       static _provable = provableFromClass(Iterator, {
         hash: Field,
@@ -646,7 +654,7 @@ class MerkleListIterator<T> implements MerkleListIteratorBase<T> {
       static from(array: T[]): MerkleListIterator<T> {
         let { hash, data } = withHashes(array, nextHash, emptyHash_);
         let unconstrained = Unconstrained.witness(() =>
-          data.map((x) => toConstant(type, x))
+          data.map((x) => toConstant(provable, x))
         );
         return this.startIterating({ data: unconstrained, hash });
       }
@@ -655,7 +663,7 @@ class MerkleListIterator<T> implements MerkleListIteratorBase<T> {
         array = [...array].reverse();
         let { hash, data } = withHashes(array, nextHash, emptyHash_);
         let unconstrained = Unconstrained.witness(() =>
-          data.map((x) => toConstant(type, x))
+          data.map((x) => toConstant(provable, x))
         );
         return this.startIteratingFromLast({ data: unconstrained, hash });
       }
diff --git a/src/lib/provable/packed.ts b/src/lib/provable/packed.ts
index aa59809ff0..f7cf395736 100644
--- a/src/lib/provable/packed.ts
+++ b/src/lib/provable/packed.ts
@@ -6,6 +6,7 @@ import { assert } from './gadgets/common.js';
 import { Poseidon, ProvableHashable, packToFields } from './crypto/poseidon.js';
 import { Provable } from './provable.js';
 import { fields, modifiedField } from './types/fields.js';
+import { ProvableType, WithProvable } from './types/provable-intf.js';
 
 export { Packed, Hashed };
 
@@ -49,22 +50,25 @@ class Packed<T> {
   /**
    * Create a packed representation of `type`. You can then use `PackedType.pack(x)` to pack a value.
    */
-  static create<T>(type: ProvableExtended<T>): typeof Packed<T> & {
+  static create<T>(
+    type: WithProvable<ProvableExtended<T>>
+  ): typeof Packed<T> & {
     provable: ProvableHashable<Packed<T>>;
   } {
+    let provable = ProvableType.get(type);
     // compute size of packed representation
-    let input = type.toInput(type.empty());
+    let input = provable.toInput(provable.empty());
     let packedSize = countFields(input);
 
     return class Packed_ extends Packed<T> {
-      static _innerProvable = type;
+      static _innerProvable = provable;
       static _provable = provableFromClass(Packed_, {
         packed: fields(packedSize),
         value: Unconstrained.provable,
       }) as ProvableHashable<Packed<T>>;
 
       static empty(): Packed<T> {
-        return Packed_.pack(type.empty());
+        return Packed_.pack(provable.empty());
       }
 
       static get provable() {
@@ -179,25 +183,26 @@ class Hashed<T> {
    * Create a hashed representation of `type`. You can then use `HashedType.hash(x)` to wrap a value in a `Hashed`.
    */
   static create<T>(
-    type: ProvableHashable<T>,
+    type: WithProvable<ProvableHashable<T>>,
     hash?: (t: T) => Field
   ): typeof Hashed<T> & {
     provable: ProvableHashable<Hashed<T>>;
     empty(): Hashed<T>;
   } {
-    let _hash = hash ?? ((t: T) => Poseidon.hashPacked(type, t));
+    let provable = ProvableType.get(type);
+    let _hash = hash ?? ((t: T) => Poseidon.hashPacked(provable, t));
 
     return class Hashed_ extends Hashed<T> {
-      static _innerProvable = type;
+      static _innerProvable = provable;
       static _provable = provableFromClass(Hashed_, {
-        hash: modifiedField({ empty: () => _hash(type.empty()) }),
+        hash: modifiedField({ empty: () => _hash(provable.empty()) }),
         value: Unconstrained.provable,
       }) as ProvableHashable<Hashed<T>>;
 
       static _hash = _hash satisfies (t: T) => Field;
 
       static empty(): Hashed<T> {
-        let empty = type.empty();
+        let empty = provable.empty();
         return new this(_hash(empty), Unconstrained.from(empty));
       }
 
diff --git a/src/lib/provable/types/provable-intf.ts b/src/lib/provable/types/provable-intf.ts
index 08cd6bcf5b..0a1b582017 100644
--- a/src/lib/provable/types/provable-intf.ts
+++ b/src/lib/provable/types/provable-intf.ts
@@ -8,6 +8,7 @@ export {
   ProvableType,
   ProvableTypePure,
   ToProvable,
+  WithProvable,
 };
 
 /**
@@ -107,20 +108,25 @@ type ProvableHashable<T, TValue = any> = ProvableWithEmpty<T, TValue> & {
 
 // helpers to accept { provable: Type } instead of Type
 
-type SelfOrOnProperty<A> = { provable: A } | A;
+type WithProvable<A> = { provable: A } | A;
 
-type ProvableType<T = any, V = any> = SelfOrOnProperty<Provable<T, V>>;
-type ProvableTypePure<T = any, V = any> = SelfOrOnProperty<ProvablePure<T, V>>;
+type ProvableType<T = any, V = any> = WithProvable<Provable<T, V>>;
+type ProvableTypePure<T = any, V = any> = WithProvable<ProvablePure<T, V>>;
 
-type ToProvable<A extends ProvableType<any>> = A extends { provable: infer P }
+type ToProvable<A extends WithProvable<any>> = A extends {
+  provable: infer P;
+}
   ? P
   : A;
 
 const ProvableType = {
-  get<T, V>(type: ProvableType<T, V>): Provable<T, V> {
-    return 'provable' in type ? type.provable : type;
-  },
-  getPure<T, V>(type: ProvableTypePure<T, V>): ProvablePure<T, V> {
-    return 'provable' in type ? type.provable : type;
+  get<A extends WithProvable<any>>(type: A): ToProvable<A> {
+    return (
+      (typeof type === 'object' || typeof type === 'function') &&
+      type !== null &&
+      'provable' in type
+        ? type.provable
+        : type
+    ) as ToProvable<A>;
   },
 };

From ca43a435db62f3dc3e29139833c02c7ceefd9d6e Mon Sep 17 00:00:00 2001
From: Gregor <gregor.mitscha-baude@gmx.at>
Date: Tue, 16 Jul 2024 12:53:23 +0200
Subject: [PATCH 21/32] remove .provable in merkle lists

---
 src/lib/mina/actions/action-types.ts                 | 10 +++-------
 src/lib/mina/actions/offchain-state-rollup.ts        |  4 ++--
 src/lib/mina/actions/offchain-state-serialization.ts |  2 +-
 src/lib/mina/actions/reducer.ts                      |  6 +++---
 4 files changed, 9 insertions(+), 13 deletions(-)

diff --git a/src/lib/mina/actions/action-types.ts b/src/lib/mina/actions/action-types.ts
index 604e344f51..7048edc321 100644
--- a/src/lib/mina/actions/action-types.ts
+++ b/src/lib/mina/actions/action-types.ts
@@ -23,7 +23,7 @@ function MerkleActions<A extends Actionable<any>>(
   fromActionState?: Field
 ) {
   return MerkleList.create(
-    MerkleActionList(actionType).provable,
+    MerkleActionList(actionType),
     (hash, actions) =>
       hashWithPrefix(prefixes.sequenceEvents, [hash, actions.hash]),
     fromActionState ?? emptyActionState
@@ -35,7 +35,7 @@ type MerkleActionList<T> = MerkleList<Hashed<T>>;
 
 function MerkleActionList<A extends Actionable<any>>(actionType: A) {
   return MerkleList.create(
-    HashedAction(actionType).provable,
+    HashedAction(actionType),
     (hash, action) =>
       hashWithPrefix(prefixes.sequenceEvents, [hash, action.hash]),
     emptyActionsHash
@@ -92,9 +92,5 @@ function MerkleActionHashes(fromActionState?: Field) {
 type FlatActions<T> = MerkleList<Hashed<T>>;
 
 function FlatActions<A extends Actionable<any>>(actionType: A) {
-  const HashedAction = Hashed.create(
-    actionType as Actionable<InferProvable<A>>,
-    (action) => hashWithPrefix(prefixes.event, actionType.toFields(action))
-  );
-  return MerkleList.create(HashedAction.provable);
+  return MerkleList.create(HashedAction(actionType));
 }
diff --git a/src/lib/mina/actions/offchain-state-rollup.ts b/src/lib/mina/actions/offchain-state-rollup.ts
index 411606a819..4335c46c18 100644
--- a/src/lib/mina/actions/offchain-state-rollup.ts
+++ b/src/lib/mina/actions/offchain-state-rollup.ts
@@ -22,7 +22,7 @@ import { getProofsEnabled } from '../mina.js';
 export { OffchainStateRollup, OffchainStateCommitments };
 
 class ActionIterator extends MerkleListIterator.create(
-  ActionList.provable,
+  ActionList,
   (hash: Field, actions: ActionList) =>
     Actions.updateSequenceState(hash, actions.hash),
   // we don't have to care about the initial hash here because we will just step forward
@@ -319,7 +319,7 @@ function OffchainStateRollup({
 // also moves the original iterator forward to start after the slice
 function sliceActions(actions: ActionIterator, batchSize: number) {
   class ActionListsList extends MerkleList.create(
-    ActionList.provable,
+    ActionList,
     (hash: Field, actions: ActionList) =>
       Actions.updateSequenceState(hash, actions.hash),
     actions.currentHash
diff --git a/src/lib/mina/actions/offchain-state-serialization.ts b/src/lib/mina/actions/offchain-state-serialization.ts
index e939a49bf4..c7a66be90a 100644
--- a/src/lib/mina/actions/offchain-state-serialization.ts
+++ b/src/lib/mina/actions/offchain-state-serialization.ts
@@ -230,7 +230,7 @@ async function fetchMerkleLeaves(
   }
 ): Promise<MerkleList<MerkleList<MerkleLeaf>>> {
   class MerkleActions extends MerkleList.create(
-    ActionList.provable,
+    ActionList,
     (hash: Field, actions: ActionList) =>
       Actions.updateSequenceState(hash, actions.hash),
     // if no "start" action hash was specified, this means we are fetching the entire history of actions, which started from the empty action state hash
diff --git a/src/lib/mina/actions/reducer.ts b/src/lib/mina/actions/reducer.ts
index f006244f78..106bb9cb11 100644
--- a/src/lib/mina/actions/reducer.ts
+++ b/src/lib/mina/actions/reducer.ts
@@ -65,8 +65,8 @@ type ReducerReturn<Action> = {
    * );
    * ```
    *
-   * Warning: The reducer API in o1js is currently not safe to use in production applications. The `reduce()` 
-   * method breaks if more than the hard-coded number (default: 32) of actions are pending. Work is actively 
+   * Warning: The reducer API in o1js is currently not safe to use in production applications. The `reduce()`
+   * method breaks if more than the hard-coded number (default: 32) of actions are pending. Work is actively
    * in progress to mitigate this limitation.
    */
   reduce<State>(
@@ -262,7 +262,7 @@ class ${contract.constructor.name} extends SmartContract {
       ) {}
 
       class MerkleActions extends MerkleList.create(
-        ActionList.provable,
+        ActionList,
         (hash: Field, actions: ActionList) =>
           Actions.updateSequenceState(hash, actions.hash),
         // if no "start" action hash was specified, this means we are fetching the entire history of actions, which started from the empty action state hash

From b77ce2a49e2f91644051f9d4e025f13b213a9eac Mon Sep 17 00:00:00 2001
From: Gregor <gregor.mitscha-baude@gmx.at>
Date: Tue, 16 Jul 2024 13:16:18 +0200
Subject: [PATCH 22/32] handle .provable in provableClass and nested types

---
 src/bindings                             |  2 +-
 src/lib/provable/crypto/foreign-curve.ts | 10 ++--------
 src/lib/provable/crypto/foreign-ecdsa.ts |  8 ++++----
 src/lib/provable/merkle-list.ts          | 10 ++++++----
 src/lib/provable/packed.ts               |  8 ++++----
 5 files changed, 17 insertions(+), 21 deletions(-)

diff --git a/src/bindings b/src/bindings
index 0609a32ba8..da12fd9782 160000
--- a/src/bindings
+++ b/src/bindings
@@ -1 +1 @@
-Subproject commit 0609a32ba8e9ca53a9c72d8166afffedab473c5a
+Subproject commit da12fd9782abd699f3f5e1225963d735c0c55dfe
diff --git a/src/lib/provable/crypto/foreign-curve.ts b/src/lib/provable/crypto/foreign-curve.ts
index 855e3e9514..af2604f53c 100644
--- a/src/lib/provable/crypto/foreign-curve.ts
+++ b/src/lib/provable/crypto/foreign-curve.ts
@@ -318,10 +318,7 @@ function createForeignCurve(params: CurveParams): typeof ForeignCurve {
     static _Bigint = BigintCurve;
     static _Field = Field;
     static _Scalar = Scalar;
-    static _provable = provableFromClass(Curve, {
-      x: Field.provable,
-      y: Field.provable,
-    });
+    static _provable = provableFromClass(Curve, { x: Field, y: Field });
   }
 
   return Curve;
@@ -354,10 +351,7 @@ function createForeignCurveV2(params: CurveParams): typeof ForeignCurveV2 {
     static _Bigint = BigintCurve;
     static _Field = Field;
     static _Scalar = Scalar;
-    static _provable = provableFromClass(Curve, {
-      x: Field.provable,
-      y: Field.provable,
-    });
+    static _provable = provableFromClass(Curve, { x: Field, y: Field });
   }
 
   return Curve;
diff --git a/src/lib/provable/crypto/foreign-ecdsa.ts b/src/lib/provable/crypto/foreign-ecdsa.ts
index bd97164050..300da146c2 100644
--- a/src/lib/provable/crypto/foreign-ecdsa.ts
+++ b/src/lib/provable/crypto/foreign-ecdsa.ts
@@ -245,8 +245,8 @@ function createEcdsa(
   class Signature extends EcdsaSignature {
     static _Curve = Curve;
     static _provable = provableFromClass(Signature, {
-      r: Curve.Scalar.provable,
-      s: Curve.Scalar.provable,
+      r: Curve.Scalar,
+      s: Curve.Scalar,
     });
   }
 
@@ -266,8 +266,8 @@ function createEcdsaV2(
   class Signature extends EcdsaSignatureV2 {
     static _Curve = Curve;
     static _provable = provableFromClass(Signature, {
-      r: Curve.Scalar.provable,
-      s: Curve.Scalar.provable,
+      r: Curve.Scalar,
+      s: Curve.Scalar,
     });
   }
 
diff --git a/src/lib/provable/merkle-list.ts b/src/lib/provable/merkle-list.ts
index 7222907c00..79208b2bd4 100644
--- a/src/lib/provable/merkle-list.ts
+++ b/src/lib/provable/merkle-list.ts
@@ -300,8 +300,10 @@ class MerkleList<T> implements MerkleListBase<T> {
 
       static _provable = provableFromClass(MerkleListTBase, {
         hash: Field,
-        data: Unconstrained.provable,
-      }) as ProvableHashable<MerkleList<T>>;
+        data: Unconstrained,
+      }) satisfies ProvableHashable<MerkleList<T>> as ProvableHashable<
+        MerkleList<T>
+      >;
 
       static _nextHash = nextHash;
       static _emptyHash = emptyHash_;
@@ -641,9 +643,9 @@ class MerkleListIterator<T> implements MerkleListIteratorBase<T> {
 
       static _provable = provableFromClass(Iterator, {
         hash: Field,
-        data: Unconstrained.provable,
+        data: Unconstrained,
         currentHash: Field,
-        currentIndex: Unconstrained.provable,
+        currentIndex: Unconstrained,
       }) satisfies ProvableHashable<MerkleListIterator<T>> as ProvableHashable<
         MerkleListIterator<T>
       >;
diff --git a/src/lib/provable/packed.ts b/src/lib/provable/packed.ts
index f7cf395736..e1b4cfe682 100644
--- a/src/lib/provable/packed.ts
+++ b/src/lib/provable/packed.ts
@@ -64,8 +64,8 @@ class Packed<T> {
       static _innerProvable = provable;
       static _provable = provableFromClass(Packed_, {
         packed: fields(packedSize),
-        value: Unconstrained.provable,
-      }) as ProvableHashable<Packed<T>>;
+        value: Unconstrained,
+      }) satisfies ProvableHashable<Packed<T>> as ProvableHashable<Packed<T>>;
 
       static empty(): Packed<T> {
         return Packed_.pack(provable.empty());
@@ -196,8 +196,8 @@ class Hashed<T> {
       static _innerProvable = provable;
       static _provable = provableFromClass(Hashed_, {
         hash: modifiedField({ empty: () => _hash(provable.empty()) }),
-        value: Unconstrained.provable,
-      }) as ProvableHashable<Hashed<T>>;
+        value: Unconstrained,
+      }) satisfies ProvableHashable<Hashed<T>> as ProvableHashable<Hashed<T>>;
 
       static _hash = _hash satisfies (t: T) => Field;
 

From 30c454e9c226fad556b2ca47c6706e7ad80bec79 Mon Sep 17 00:00:00 2001
From: Gregor <gregor.mitscha-baude@gmx.at>
Date: Tue, 16 Jul 2024 13:48:35 +0200
Subject: [PATCH 23/32] unrelated fix

---
 src/lib/provable/gadgets/foreign-field.ts | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/src/lib/provable/gadgets/foreign-field.ts b/src/lib/provable/gadgets/foreign-field.ts
index 611189c18a..db48d1c4f1 100644
--- a/src/lib/provable/gadgets/foreign-field.ts
+++ b/src/lib/provable/gadgets/foreign-field.ts
@@ -432,11 +432,7 @@ function equals(x: Field3, c: bigint, f: bigint) {
   }
 }
 
-const provableLimb = modifiedField({
-  toInput(x) {
-    return { packed: [[x, Number(l)]] };
-  },
-});
+const provableLimb = modifiedField({});
 
 const Field3 = {
   /**

From 38d8feafbd07930bfd636e801a1aea7aeaf3ebae Mon Sep 17 00:00:00 2001
From: Gregor <gregor.mitscha-baude@gmx.at>
Date: Tue, 16 Jul 2024 13:53:32 +0200
Subject: [PATCH 24/32] handle .provable in struct and provable()

---
 src/bindings                                  | 2 +-
 src/lib/mina/account-update.ts                | 6 +++---
 src/lib/mina/actions/batch-reducer.ts         | 2 +-
 src/lib/mina/actions/offchain-state-rollup.ts | 2 +-
 src/lib/mina/token/forest-iterator.ts         | 2 +-
 src/lib/mina/zkapp.ts                         | 2 +-
 src/lib/provable/gadgets/elliptic-curve.ts    | 4 ++--
 src/lib/provable/merkle-list.ts               | 2 +-
 src/lib/provable/types/unconstrained.ts       | 1 +
 9 files changed, 12 insertions(+), 11 deletions(-)

diff --git a/src/bindings b/src/bindings
index da12fd9782..671219a155 160000
--- a/src/bindings
+++ b/src/bindings
@@ -1 +1 @@
-Subproject commit da12fd9782abd699f3f5e1225963d735c0c55dfe
+Subproject commit 671219a155311b8335db535596b4dfa3b61fef11
diff --git a/src/lib/mina/account-update.ts b/src/lib/mina/account-update.ts
index 3b45304b5f..5c5ca57250 100644
--- a/src/lib/mina/account-update.ts
+++ b/src/lib/mina/account-update.ts
@@ -1357,7 +1357,7 @@ type AccountUpdateForestBase = MerkleListBase<AccountUpdateTreeBase>;
 
 const AccountUpdateTreeBase = StructNoJson({
   id: RandomId,
-  accountUpdate: HashedAccountUpdate.provable,
+  accountUpdate: HashedAccountUpdate,
   children: MerkleListBase<AccountUpdateTreeBase>(),
 });
 
@@ -1467,8 +1467,8 @@ class AccountUpdateForest extends MerkleList.create(
  */
 class AccountUpdateTree extends StructNoJson({
   id: RandomId,
-  accountUpdate: HashedAccountUpdate.provable,
-  children: AccountUpdateForest.provable,
+  accountUpdate: HashedAccountUpdate,
+  children: AccountUpdateForest,
 }) {
   /**
    * Create a tree of account updates which only consists of a root.
diff --git a/src/lib/mina/actions/batch-reducer.ts b/src/lib/mina/actions/batch-reducer.ts
index 17f2a49db4..cf7c26da0e 100644
--- a/src/lib/mina/actions/batch-reducer.ts
+++ b/src/lib/mina/actions/batch-reducer.ts
@@ -549,7 +549,7 @@ function ActionBatch<A extends Actionable<any>>(actionType: A) {
     processedActionState: Field,
     onchainActionState: Field,
     onchainStack: Field,
-    stack: MerkleActions(actionType).provable,
+    stack: MerkleActions(actionType),
     isRecursive: Bool,
     witnesses: Unconstrained.provableWithEmpty<ActionWitnesses>([]),
   });
diff --git a/src/lib/mina/actions/offchain-state-rollup.ts b/src/lib/mina/actions/offchain-state-rollup.ts
index 4335c46c18..d69595ad7b 100644
--- a/src/lib/mina/actions/offchain-state-rollup.ts
+++ b/src/lib/mina/actions/offchain-state-rollup.ts
@@ -42,7 +42,7 @@ class OffchainStateCommitments extends Struct({
   root: Field,
   length: Field,
   // TODO: make zkprogram support auxiliary data in public inputs
-  // actionState: ActionIterator.provable,
+  // actionState: ActionIterator,
   actionState: Field,
 }) {
   static emptyFromHeight(height: number) {
diff --git a/src/lib/mina/token/forest-iterator.ts b/src/lib/mina/token/forest-iterator.ts
index aa95731eb0..257a19c984 100644
--- a/src/lib/mina/token/forest-iterator.ts
+++ b/src/lib/mina/token/forest-iterator.ts
@@ -19,7 +19,7 @@ const AccountUpdateIterator = MerkleListIterator.create(
 );
 
 class Layer extends Struct({
-  forest: AccountUpdateIterator.provable,
+  forest: AccountUpdateIterator,
   mayUseToken: AccountUpdate.MayUseToken.type,
 }) {}
 const ParentLayers = MerkleList.create<Layer>(Layer);
diff --git a/src/lib/mina/zkapp.ts b/src/lib/mina/zkapp.ts
index e453c561e1..94c71a809d 100644
--- a/src/lib/mina/zkapp.ts
+++ b/src/lib/mina/zkapp.ts
@@ -462,7 +462,7 @@ function wrapMethod(
       }>(
         provable({
           result: methodIntf.returnType ?? provable(null),
-          children: AccountUpdateForest.provable,
+          children: AccountUpdateForest,
         }),
         runCalledContract,
         { skipCheck: true }
diff --git a/src/lib/provable/gadgets/elliptic-curve.ts b/src/lib/provable/gadgets/elliptic-curve.ts
index 91806c9e62..c0e8f515bc 100644
--- a/src/lib/provable/gadgets/elliptic-curve.ts
+++ b/src/lib/provable/gadgets/elliptic-curve.ts
@@ -775,7 +775,7 @@ const Point = {
     return Point.from(random(Curve));
   },
 
-  provable: provable({ x: Field3.provable, y: Field3.provable }),
+  provable: provable({ x: Field3, y: Field3 }),
 };
 
 const EcdsaSignature = {
@@ -804,7 +804,7 @@ const EcdsaSignature = {
     return EcdsaSignature.from({ r, s });
   },
 
-  provable: provable({ r: Field3.provable, s: Field3.provable }),
+  provable: provable({ r: Field3, s: Field3 }),
 };
 
 const Ecdsa = {
diff --git a/src/lib/provable/merkle-list.ts b/src/lib/provable/merkle-list.ts
index 79208b2bd4..24b3ae7851 100644
--- a/src/lib/provable/merkle-list.ts
+++ b/src/lib/provable/merkle-list.ts
@@ -44,7 +44,7 @@ type MerkleListBase<T> = {
 };
 
 function MerkleListBase<T>(): ProvableHashable<MerkleListBase<T>> {
-  return class extends Struct({ hash: Field, data: Unconstrained.provable }) {
+  return class extends Struct({ hash: Field, data: Unconstrained }) {
     static empty(): MerkleListBase<T> {
       return { hash: emptyHash, data: Unconstrained.from([]) };
     }
diff --git a/src/lib/provable/types/unconstrained.ts b/src/lib/provable/types/unconstrained.ts
index 1278aaa7e7..1532065d3c 100644
--- a/src/lib/provable/types/unconstrained.ts
+++ b/src/lib/provable/types/unconstrained.ts
@@ -128,6 +128,7 @@ and Provable.asProver() blocks, which execute outside the proof.
     },
   };
 
+  // TODO rename
   static provableWithEmpty<T>(empty: T): Provable<Unconstrained<T>, T> & {
     toInput: (x: Unconstrained<T>) => {
       fields?: Field[];

From 049582966be6676eff5320a8290c4c9be9e65ebd Mon Sep 17 00:00:00 2001
From: Gregor <gregor.mitscha-baude@gmx.at>
Date: Tue, 16 Jul 2024 13:58:55 +0200
Subject: [PATCH 25/32] rename

---
 src/examples/crypto/rsa/rsa.ts                       |  5 ++---
 src/lib/mina/actions/batch-reducer.ts                |  4 ++--
 src/lib/mina/actions/offchain-state-serialization.ts |  2 +-
 src/lib/provable/merkle-tree-indexed.ts              |  6 +++---
 src/lib/provable/types/unconstrained.ts              | 10 ++++++++--
 5 files changed, 16 insertions(+), 11 deletions(-)

diff --git a/src/examples/crypto/rsa/rsa.ts b/src/examples/crypto/rsa/rsa.ts
index 50db5691e0..95be654ac3 100644
--- a/src/examples/crypto/rsa/rsa.ts
+++ b/src/examples/crypto/rsa/rsa.ts
@@ -30,7 +30,7 @@ class Bigint2048 {
     // TODO this wrapping Struct should be unnecessary
     class extends Struct({
       fields: Field18,
-      value: Unconstrained.provableWithEmpty(0n),
+      value: Unconstrained.withEmpty(0n),
     }) {
       // TODO where to add the custom check()?
       static check({ fields }: { fields: Field[] }) {
@@ -91,8 +91,7 @@ function multiply(
   // this also adds the range checks in `check()`
   let { q, r } = Provable.witness(
     // TODO Struct() should be unnecessary
-    // TODO .provable should be unnecessary
-    Struct({ q: Bigint2048.provable, r: Bigint2048.provable }),
+    Struct({ q: Bigint2048, r: Bigint2048 }),
     () => {
       let xy = x.toBigint() * y.toBigint();
       let p0 = p.toBigint();
diff --git a/src/lib/mina/actions/batch-reducer.ts b/src/lib/mina/actions/batch-reducer.ts
index cf7c26da0e..90fb2da199 100644
--- a/src/lib/mina/actions/batch-reducer.ts
+++ b/src/lib/mina/actions/batch-reducer.ts
@@ -551,7 +551,7 @@ function ActionBatch<A extends Actionable<any>>(actionType: A) {
     onchainStack: Field,
     stack: MerkleActions(actionType),
     isRecursive: Bool,
-    witnesses: Unconstrained.provableWithEmpty<ActionWitnesses>([]),
+    witnesses: Unconstrained.withEmpty<ActionWitnesses>([]),
   });
 }
 
@@ -769,7 +769,7 @@ function actionStackProgram(maxUpdatesPerProof: number) {
         privateInputs: [
           SelfProof,
           Bool,
-          Unconstrained.provableWithEmpty<ActionWitnesses>([]),
+          Unconstrained.withEmpty<ActionWitnesses>([]),
         ],
 
         async method(
diff --git a/src/lib/mina/actions/offchain-state-serialization.ts b/src/lib/mina/actions/offchain-state-serialization.ts
index c7a66be90a..c9d46d9626 100644
--- a/src/lib/mina/actions/offchain-state-serialization.ts
+++ b/src/lib/mina/actions/offchain-state-serialization.ts
@@ -137,7 +137,7 @@ class MerkleLeaf extends Struct({
   value: Field,
   usesPreviousValue: Bool,
   previousValue: Field,
-  prefix: Unconstrained.provableWithEmpty<Field[]>([]),
+  prefix: Unconstrained.withEmpty<Field[]>([]),
 }) {
   static fromAction(action: Field[]) {
     assert(action.length >= 4, 'invalid action size');
diff --git a/src/lib/provable/merkle-tree-indexed.ts b/src/lib/provable/merkle-tree-indexed.ts
index 568a47798a..0389a85f68 100644
--- a/src/lib/provable/merkle-tree-indexed.ts
+++ b/src/lib/provable/merkle-tree-indexed.ts
@@ -73,7 +73,7 @@ function IndexedMerkleMap(height: number): typeof IndexedMerkleMapBase {
 const provableBase = {
   root: Field,
   length: Field,
-  data: Unconstrained.provableWithEmpty({
+  data: Unconstrained.withEmpty({
     nodes: [] as (bigint | undefined)[][],
     sortedLeaves: [] as StoredLeaf[],
   }),
@@ -656,8 +656,8 @@ class Leaf extends Struct({
   nextKey: Field,
 
   // auxiliary data that tells us where the leaf is stored
-  index: Unconstrained.provableWithEmpty(0),
-  sortedIndex: Unconstrained.provableWithEmpty(0),
+  index: Unconstrained.withEmpty(0),
+  sortedIndex: Unconstrained.withEmpty(0),
 }) {
   /**
    * Compute a leaf node: the hash of a leaf that becomes part of the Merkle tree.
diff --git a/src/lib/provable/types/unconstrained.ts b/src/lib/provable/types/unconstrained.ts
index 1532065d3c..efb3cd4eba 100644
--- a/src/lib/provable/types/unconstrained.ts
+++ b/src/lib/provable/types/unconstrained.ts
@@ -128,8 +128,7 @@ and Provable.asProver() blocks, which execute outside the proof.
     },
   };
 
-  // TODO rename
-  static provableWithEmpty<T>(empty: T): Provable<Unconstrained<T>, T> & {
+  static withEmpty<T>(empty: T): Provable<Unconstrained<T>, T> & {
     toInput: (x: Unconstrained<T>) => {
       fields?: Field[];
       packed?: [Field, number][];
@@ -141,6 +140,13 @@ and Provable.asProver() blocks, which execute outside the proof.
       empty: () => Unconstrained.from(empty),
     };
   }
+
+  /**
+   * @deprecated
+   */
+  static provableWithEmpty<T>(empty: T) {
+    return Unconstrained.withEmpty(empty);
+  }
 }
 
 type UnconstrainedProvable<T> = Provable<Unconstrained<T>, T>;

From 0f4e42ddc6e2d647af02847f1e4843601dc4406f Mon Sep 17 00:00:00 2001
From: Gregor <gregor.mitscha-baude@gmx.at>
Date: Tue, 16 Jul 2024 14:21:14 +0200
Subject: [PATCH 26/32] support no .provable in state, offchain state, method
 returns and batch reducer

---
 src/examples/crypto/foreign-field.ts              |  2 +-
 src/lib/mina/actions/action-types.ts              | 15 +++++++++------
 src/lib/mina/actions/batch-reducer.ts             | 10 ++++++++--
 src/lib/mina/actions/batch-reducer.unit-test.ts   |  2 +-
 .../mina/actions/offchain-state-serialization.ts  | 14 +++++++++++---
 src/lib/mina/actions/offchain-state.ts            |  4 ++++
 src/lib/mina/state.ts                             | 10 ++++++++--
 src/lib/mina/zkapp.ts                             | 11 ++++++++---
 src/lib/provable/crypto/poseidon.ts               |  5 +++--
 src/lib/provable/option.ts                        |  5 +++--
 10 files changed, 56 insertions(+), 22 deletions(-)

diff --git a/src/examples/crypto/foreign-field.ts b/src/examples/crypto/foreign-field.ts
index 044d8ebda6..c8860275c8 100644
--- a/src/examples/crypto/foreign-field.ts
+++ b/src/examples/crypto/foreign-field.ts
@@ -88,7 +88,7 @@ assert(uCanonical instanceof SmallField.Canonical);
 class AlmostSmallField extends SmallField.AlmostReduced {}
 
 class MyContract extends SmartContract {
-  @state(AlmostSmallField.provable) x = State<AlmostSmallField>();
+  @state(AlmostSmallField) x = State<AlmostSmallField>();
 
   @method async myMethod(y: AlmostSmallField) {
     let x = y.mul(2);
diff --git a/src/lib/mina/actions/action-types.ts b/src/lib/mina/actions/action-types.ts
index 7048edc321..fc0d26b00a 100644
--- a/src/lib/mina/actions/action-types.ts
+++ b/src/lib/mina/actions/action-types.ts
@@ -6,6 +6,7 @@ import { Actions } from '../account-update.js';
 import { Hashed } from '../../provable/packed.js';
 import { hashWithPrefix } from '../../provable/crypto/poseidon.js';
 import { prefixes } from '../../../bindings/crypto/constants.js';
+import { ProvableType } from '../../provable/types/provable-intf.js';
 
 export { MerkleActions, MerkleActionHashes, HashedAction, FlatActions };
 export { emptyActionState, emptyActionsHash };
@@ -45,8 +46,9 @@ function MerkleActionList<A extends Actionable<any>>(actionType: A) {
 type HashedAction<T> = Hashed<T>;
 
 function HashedAction<A extends Actionable<any>>(actionType: A) {
-  return Hashed.create(actionType as Actionable<InferProvable<A>>, (action) =>
-    hashWithPrefix(prefixes.event, actionType.toFields(action))
+  let type = ProvableType.get(actionType as Actionable<InferProvable<A>>);
+  return Hashed.create(type, (action) =>
+    hashWithPrefix(prefixes.event, type.toFields(action))
   );
 }
 
@@ -55,14 +57,15 @@ function actionFieldsToMerkleList<T>(
   fields: bigint[][][],
   fromActionState?: bigint
 ) {
-  const HashedActionT = HashedAction(actionType);
-  const MerkleActionListT = MerkleActionList(actionType);
+  let type = ProvableType.get(actionType);
+  const HashedActionT = HashedAction(type);
+  const MerkleActionListT = MerkleActionList(type);
   const MerkleActionsT = MerkleActions(
-    actionType,
+    type,
     fromActionState ? Field(fromActionState) : undefined
   );
   let actions = fields.map((event) =>
-    event.map((action) => actionType.fromFields(action.map(Field)))
+    event.map((action) => type.fromFields(action.map(Field)))
   );
   let hashes = actions.map((as) => as.map((a) => HashedActionT.hash(a)));
   return MerkleActionsT.from(hashes.map((h) => MerkleActionListT.from(h)));
diff --git a/src/lib/mina/actions/batch-reducer.ts b/src/lib/mina/actions/batch-reducer.ts
index 90fb2da199..c29cb040e1 100644
--- a/src/lib/mina/actions/batch-reducer.ts
+++ b/src/lib/mina/actions/batch-reducer.ts
@@ -24,6 +24,11 @@ import {
   MerkleActions,
   emptyActionState,
 } from './action-types.js';
+import {
+  ProvableHashable,
+  ProvablePure,
+  ProvableType,
+} from '../../provable/types/provable-intf.js';
 
 // external API
 export { BatchReducer, ActionBatch };
@@ -57,7 +62,7 @@ class BatchReducer<
   Action = InferProvable<ActionType>
 > {
   batchSize: BatchSize;
-  actionType: Actionable<Action>;
+  actionType: ProvableHashable<Action> & ProvablePure<Action>;
   Batch: ReturnType<typeof ActionBatch>;
 
   program: ActionStackProgram;
@@ -133,7 +138,8 @@ class BatchReducer<
     maxActionsPerUpdate?: number;
   }) {
     this.batchSize = batchSize;
-    this.actionType = actionType as Actionable<Action>;
+    this.actionType = ProvableType.get(actionType) as ProvableHashable<Action> &
+      ProvablePure<Action>;
     this.Batch = ActionBatch(this.actionType);
 
     this.maxUpdatesFinalProof = maxUpdatesFinalProof;
diff --git a/src/lib/mina/actions/batch-reducer.unit-test.ts b/src/lib/mina/actions/batch-reducer.unit-test.ts
index 7e98a96f40..ff06ffd90f 100644
--- a/src/lib/mina/actions/batch-reducer.unit-test.ts
+++ b/src/lib/mina/actions/batch-reducer.unit-test.ts
@@ -86,7 +86,7 @@ class UnsafeAirdrop extends SmartContract {
    *
    * Note: This two-step process is necessary so that multiple users can claim concurrently.
    */
-  @method.returns(MerkleMap.provable)
+  @method.returns(MerkleMap)
   async settleClaims(batch: Batch, proof: BatchProof) {
     // witness merkle map and require that it matches the onchain root
     let eligibleMap = Provable.witness(MerkleMap, () => eligible.clone());
diff --git a/src/lib/mina/actions/offchain-state-serialization.ts b/src/lib/mina/actions/offchain-state-serialization.ts
index c9d46d9626..bf76b73311 100644
--- a/src/lib/mina/actions/offchain-state-serialization.ts
+++ b/src/lib/mina/actions/offchain-state-serialization.ts
@@ -6,7 +6,11 @@
  * if we only need to prove that (key, value) are part of it.
  */
 
-import { ProvablePure } from '../../provable/types/provable-intf.js';
+import {
+  ProvablePure,
+  ProvableType,
+  WithProvable,
+} from '../../provable/types/provable-intf.js';
 import {
   Poseidon,
   ProvableHashable,
@@ -45,7 +49,9 @@ export {
 };
 
 type Action = [...Field[], Field, Field];
-type Actionable<T, V = any> = ProvableHashable<T, V> & ProvablePure<T, V>;
+type Actionable<T, V = any> = WithProvable<
+  ProvableHashable<T, V> & ProvablePure<T, V>
+>;
 
 function toKeyHash<K, KeyType extends Actionable<K> | undefined>(
   prefix: Field,
@@ -70,6 +76,7 @@ function toAction<K, V, KeyType extends Actionable<K> | undefined>({
   value: V;
   previousValue?: Option<V>;
 }): Action {
+  valueType = ProvableType.get(valueType);
   let valueSize = valueType.sizeInFields();
   let padding = valueSize % 2 === 0 ? [] : [Field(0)];
 
@@ -100,6 +107,7 @@ function fromActionWithoutHashes<V>(
   valueType: Actionable<V>,
   action: Field[]
 ): V {
+  valueType = ProvableType.get(valueType);
   let valueSize = valueType.sizeInFields();
   let paddingSize = valueSize % 2 === 0 ? 0 : 1;
   assert(action.length === valueSize + paddingSize, 'invalid action size');
@@ -121,7 +129,7 @@ function hashPackedWithPrefix<T, Type extends Actionable<T> | undefined>(
 
   // hash value if a type was passed in
   if (type !== undefined) {
-    let input = type.toInput(value as T);
+    let input = ProvableType.get(type).toInput(value as T);
     let packed = packToFields(input);
     state = Poseidon.update(state, packed);
   }
diff --git a/src/lib/mina/actions/offchain-state.ts b/src/lib/mina/actions/offchain-state.ts
index 2b5f598b59..090b7d3496 100644
--- a/src/lib/mina/actions/offchain-state.ts
+++ b/src/lib/mina/actions/offchain-state.ts
@@ -27,6 +27,7 @@ import { Poseidon } from '../../provable/crypto/poseidon.js';
 import { contract } from '../smart-contract-context.js';
 import { IndexedMerkleMap } from '../../provable/merkle-tree-indexed.js';
 import { assertDefined } from '../../util/assert.js';
+import { ProvableType } from '../../provable/types/provable-intf.js';
 
 // external API
 export { OffchainState, OffchainStateCommitments };
@@ -291,6 +292,7 @@ function OffchainState<
     index: number,
     type: Actionable<T, TValue>
   ): OffchainField<T, TValue> {
+    type = ProvableType.get(type);
     const prefix = Field(index);
     let optionType = Option(type);
 
@@ -340,6 +342,8 @@ function OffchainState<
     keyType: Actionable<K>,
     valueType: Actionable<V, VValue>
   ): OffchainMap<K, V, VValue> {
+    keyType = ProvableType.get(keyType);
+    valueType = ProvableType.get(valueType);
     const prefix = Field(index);
     let optionType = Option(valueType);
 
diff --git a/src/lib/mina/state.ts b/src/lib/mina/state.ts
index 9c9f39eb09..43b480cb2e 100644
--- a/src/lib/mina/state.ts
+++ b/src/lib/mina/state.ts
@@ -7,7 +7,11 @@ import { SmartContract } from './zkapp.js';
 import { Account } from './account.js';
 import { Provable } from '../provable/provable.js';
 import { Field } from '../provable/wrapped.js';
-import { ProvablePure } from '../provable/types/provable-intf.js';
+import {
+  ProvablePure,
+  ProvableType,
+  ProvableTypePure,
+} from '../provable/types/provable-intf.js';
 import { ensureConsistentPrecondition } from './precondition.js';
 import { Bool } from '../provable/wrapped.js';
 
@@ -98,7 +102,9 @@ function State<A>(defaultValue?: A): State<A> {
  * ```
  *
  */
-function state<A>(stateType: FlexibleProvablePure<A>) {
+function state<A>(type: ProvableTypePure<A> | FlexibleProvablePure<A>) {
+  let stateType = ProvableType.get(type);
+
   return function (
     target: SmartContract & { constructor: any },
     key: string,
diff --git a/src/lib/mina/zkapp.ts b/src/lib/mina/zkapp.ts
index 94c71a809d..7b58bfc361 100644
--- a/src/lib/mina/zkapp.ts
+++ b/src/lib/mina/zkapp.ts
@@ -74,7 +74,7 @@ import {
   smartContractContext,
 } from './smart-contract-context.js';
 import { assertPromise } from '../util/assert.js';
-import { ProvablePure } from '../provable/types/provable-intf.js';
+import { ProvablePure, ProvableType } from '../provable/types/provable-intf.js';
 import { getReducer, Reducer } from './actions/reducer.js';
 import { provable } from '../provable/types/provable-derivers.js';
 
@@ -174,7 +174,7 @@ function method<K extends string, T extends SmartContract>(
 method.returns = function <
   K extends string,
   T extends SmartContract,
-  R extends Provable<any>
+  R extends ProvableType
 >(returnType: R) {
   return function decorateMethod(
     target: T & {
@@ -183,7 +183,12 @@ method.returns = function <
     methodName: K & string & keyof T,
     descriptor: PropertyDescriptor
   ) {
-    return method(target as any, methodName, descriptor, returnType);
+    return method(
+      target as any,
+      methodName,
+      descriptor,
+      ProvableType.get(returnType)
+    );
   };
 };
 
diff --git a/src/lib/provable/crypto/poseidon.ts b/src/lib/provable/crypto/poseidon.ts
index c3425f0f66..7d74672820 100644
--- a/src/lib/provable/crypto/poseidon.ts
+++ b/src/lib/provable/crypto/poseidon.ts
@@ -9,6 +9,7 @@ import { assert } from '../../util/errors.js';
 import { rangeCheckN } from '../gadgets/range-check.js';
 import { TupleN } from '../../util/types.js';
 import { Group } from '../group.js';
+import { ProvableType, WithProvable } from '../types/provable-intf.js';
 
 // external API
 export { Poseidon, TokenSymbol };
@@ -136,8 +137,8 @@ const Poseidon = {
    * field elements as possible. This saves constraints because packing has a much
    * lower per-field element cost than hashing.
    */
-  hashPacked<T>(type: Hashable<T>, value: T) {
-    let input = type.toInput(value);
+  hashPacked<T>(type: WithProvable<Hashable<T>>, value: T) {
+    let input = ProvableType.get(type).toInput(value);
     let packed = packToFields(input);
     return Poseidon.hash(packed);
   },
diff --git a/src/lib/provable/option.ts b/src/lib/provable/option.ts
index cfd324e07f..86a71fb524 100644
--- a/src/lib/provable/option.ts
+++ b/src/lib/provable/option.ts
@@ -4,6 +4,7 @@ import { Provable } from './provable.js';
 import { InferProvable, Struct } from './types/struct.js';
 import { provable, ProvableInferPureFrom } from './types/provable-derivers.js';
 import { Bool } from './wrapped.js';
+import { ProvableType } from './types/provable-intf.js';
 
 export { Option, OptionOrValue };
 
@@ -34,7 +35,7 @@ type OptionOrValue<T, V> =
  * let zero: UInt64 = none.orElse(0n); // specify a default value
  * ```
  */
-function Option<A extends Provable<any, any>>(
+function Option<A extends ProvableType>(
   type: A
 ): ProvableInferPureFrom<
   A,
@@ -59,7 +60,7 @@ function Option<A extends Provable<any, any>>(
   } {
   type T = InferProvable<A>;
   type V = InferValue<A>;
-  let strictType: Provable<T, V> = type;
+  let strictType: Provable<T, V> = ProvableType.get(type);
 
   // construct a provable with a JS type of `T | undefined`
   const PlainOption: Provable<

From 8c3410ca901580facd8e7d84867f5dcec30b2109 Mon Sep 17 00:00:00 2001
From: Gregor <gregor.mitscha-baude@gmx.at>
Date: Tue, 16 Jul 2024 14:24:50 +0200
Subject: [PATCH 27/32] Revert "(maybe revert) some experimenting"

This reverts commit 30002309abc84a435b1f071e236a01e3ef8f197e.
---
 src/examples/crypto/rsa/rsa.ts | 60 ++++++++--------------------------
 src/index.ts                   |  2 --
 2 files changed, 14 insertions(+), 48 deletions(-)

diff --git a/src/examples/crypto/rsa/rsa.ts b/src/examples/crypto/rsa/rsa.ts
index 95be654ac3..7e2a17395d 100644
--- a/src/examples/crypto/rsa/rsa.ts
+++ b/src/examples/crypto/rsa/rsa.ts
@@ -1,14 +1,7 @@
 /**
  * RSA signature verification with o1js
  */
-import {
-  Field,
-  Gadgets,
-  Provable,
-  Struct,
-  Unconstrained,
-  provableExtends,
-} from 'o1js';
+import { Field, Gadgets, Provable, Struct, Unconstrained } from 'o1js';
 
 export { Bigint2048, rsaVerify65537 };
 
@@ -19,40 +12,10 @@ const mask = (1n << 116n) - 1n;
  */
 const Field18 = Provable.Array(Field, 18);
 
-class Bigint2048 {
-  fields: Field[];
-  value: Unconstrained<bigint>;
-
-  // TODO this could be simplified with a Struct-like base class
-  // TODO map the value type to `bigint`
-  static provable = provableExtends(
-    Bigint2048,
-    // TODO this wrapping Struct should be unnecessary
-    class extends Struct({
-      fields: Field18,
-      value: Unconstrained.withEmpty(0n),
-    }) {
-      // TODO where to add the custom check()?
-      static check({ fields }: { fields: Field[] }) {
-        for (let x of fields) {
-          rangeCheck116(x);
-        }
-      }
-    }
-  );
-
-  // TODO constructor could be removed with a Struct-like base class
-  constructor({
-    fields,
-    value,
-  }: {
-    fields: Field[];
-    value: Unconstrained<bigint>;
-  }) {
-    this.fields = fields;
-    this.value = value;
-  }
-
+class Bigint2048 extends Struct({
+  fields: Field18,
+  value: Unconstrained.withEmpty(0n),
+}) {
   modMul(x: Bigint2048, y: Bigint2048) {
     return multiply(x, y, this);
   }
@@ -66,13 +29,19 @@ class Bigint2048 {
   }
 
   static from(x: bigint) {
-    let fields: bigint[] = [];
+    let fields = [];
     let value = x;
     for (let i = 0; i < 18; i++) {
-      fields.push(x & mask);
+      fields.push(Field(x & mask));
       x >>= 116n;
     }
-    return Bigint2048.provable.fromValue({ fields, value });
+    return new Bigint2048({ fields, value: Unconstrained.from(value) });
+  }
+
+  static check(x: { fields: Field[] }) {
+    for (let i = 0; i < 18; i++) {
+      rangeCheck116(x.fields[i]);
+    }
   }
 }
 
@@ -97,7 +66,6 @@ function multiply(
       let p0 = p.toBigint();
       let q = xy / p0;
       let r = xy - q * p0;
-      // TODO Bigint2048.from() should be unnecessary
       return { q: Bigint2048.from(q), r: Bigint2048.from(r) };
     }
   );
diff --git a/src/index.ts b/src/index.ts
index 5819569f66..0cd002a58d 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -40,8 +40,6 @@ export type {
 export {
   provable,
   provablePure,
-  provableMap,
-  provableExtends,
 } from './lib/provable/types/provable-derivers.js';
 export { Struct } from './lib/provable/types/struct.js';
 export { Unconstrained } from './lib/provable/types/unconstrained.js';

From 440a22c2ad0fd9a27eb0a0139a1ae02e31d6e753 Mon Sep 17 00:00:00 2001
From: Gregor <gregor.mitscha-baude@gmx.at>
Date: Tue, 16 Jul 2024 14:34:13 +0200
Subject: [PATCH 28/32] revert unecessary diff

---
 src/lib/proof-system/zkprogram.ts | 21 +++++++++------------
 1 file changed, 9 insertions(+), 12 deletions(-)

diff --git a/src/lib/proof-system/zkprogram.ts b/src/lib/proof-system/zkprogram.ts
index 4f9057ba1b..fe497a7a65 100644
--- a/src/lib/proof-system/zkprogram.ts
+++ b/src/lib/proof-system/zkprogram.ts
@@ -1486,18 +1486,15 @@ type Prover<
       ...args: TupleToInstances<Args>
     ) => Promise<Proof<PublicInput, PublicOutput>>;
 
-type ProvableOrUndefined<A extends ProvableType | undefined> =
-  A extends ProvableType ? ToProvable<A> : typeof Undefined;
-type ProvableOrVoid<A extends ProvableType | undefined> = A extends ProvableType
-  ? ToProvable<A>
-  : typeof Void;
-
-type InferProvableOrUndefined<A extends ProvableType | undefined> =
-  A extends ProvableType ? InferProvableType<A> : undefined;
-
-type InferProvableOrVoid<A> = A extends ProvableType
-  ? InferProvableType<A>
-  : void;
+type ProvableOrUndefined<A> = A extends undefined
+  ? typeof Undefined
+  : ToProvable<A>;
+type ProvableOrVoid<A> = A extends undefined ? typeof Void : ToProvable<A>;
+
+type InferProvableOrUndefined<A> = A extends undefined
+  ? undefined
+  : InferProvable<A>;
+type InferProvableOrVoid<A> = A extends undefined ? void : InferProvable<A>;
 
 type UnwrapPromise<P> = P extends Promise<infer T> ? T : never;
 

From 02ec0ff66426d4c031ac758749b0bc39b22fcd31 Mon Sep 17 00:00:00 2001
From: Gregor <gregor.mitscha-baude@gmx.at>
Date: Tue, 16 Jul 2024 14:34:46 +0200
Subject: [PATCH 29/32] delete unused and obvious function

---
 src/lib/provable/types/provable-derivers.ts | 41 ---------------------
 1 file changed, 41 deletions(-)

diff --git a/src/lib/provable/types/provable-derivers.ts b/src/lib/provable/types/provable-derivers.ts
index 5e2476b330..4cb5178d47 100644
--- a/src/lib/provable/types/provable-derivers.ts
+++ b/src/lib/provable/types/provable-derivers.ts
@@ -32,7 +32,6 @@ export {
   provablePure,
   provableTuple,
   provableFromClass,
-  provableMap,
   provableExtends,
 };
 
@@ -135,46 +134,6 @@ function construct<Raw, T extends Raw>(Class: Constructor<T>, value: Raw): T {
   return Object.assign(instance, value);
 }
 
-function provableMap<
-  A extends ProvableHashable<any>,
-  S,
-  T extends InferProvable<A> = InferProvable<A>
->(
-  base: A,
-  there: (t: T) => S,
-  back: (s: S) => T
-): ProvableHashable<S, InferValue<A>> {
-  return {
-    sizeInFields() {
-      return base.sizeInFields();
-    },
-    toFields(value) {
-      return base.toFields(back(value));
-    },
-    toAuxiliary(value) {
-      return base.toAuxiliary(value === undefined ? undefined : back(value));
-    },
-    fromFields(fields, aux) {
-      return there(base.fromFields(fields, aux));
-    },
-    check(value) {
-      base.check(back(value));
-    },
-    toValue(value) {
-      return base.toValue(back(value));
-    },
-    fromValue(value) {
-      return there(base.fromValue(value));
-    },
-    empty() {
-      return there(base.empty());
-    },
-    toInput(value) {
-      return base.toInput(back(value));
-    },
-  };
-}
-
 function provableExtends<
   A extends ProvableHashable<any>,
   T extends InferProvable<A>,

From a1e269ae0d5e1949d65ba7bef3b949a57d00876a Mon Sep 17 00:00:00 2001
From: Gregor <gregor.mitscha-baude@gmx.at>
Date: Tue, 16 Jul 2024 15:03:13 +0200
Subject: [PATCH 30/32] add and fix test

---
 src/bindings                              | 2 +-
 src/lib/provable/test/struct.unit-test.ts | 8 ++++----
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/src/bindings b/src/bindings
index 671219a155..30d98a1cfd 160000
--- a/src/bindings
+++ b/src/bindings
@@ -1 +1 @@
-Subproject commit 671219a155311b8335db535596b4dfa3b61fef11
+Subproject commit 30d98a1cfd065728fe6e7d16038a6f2f01e53d7d
diff --git a/src/lib/provable/test/struct.unit-test.ts b/src/lib/provable/test/struct.unit-test.ts
index 8aaef12fe8..0acbd21192 100644
--- a/src/lib/provable/test/struct.unit-test.ts
+++ b/src/lib/provable/test/struct.unit-test.ts
@@ -24,8 +24,8 @@ import { modifiedField } from '../types/fields.js';
 let type = provable({
   nested: { a: Number, b: Boolean },
   other: String,
-  pk: PublicKey,
-  bool: Bool,
+  pk: { provable: PublicKey },
+  bool: { provable: Bool },
   uint: [UInt32, UInt32],
 });
 
@@ -132,14 +132,14 @@ class MyStruct extends Struct({
   nested: { a: Number, b: Boolean },
   other: String,
   pk: PublicKey,
-  uint: [UInt32, UInt32],
+  uint: [UInt32, { provable: UInt32 }],
 }) {}
 
 class MyStructPure extends Struct({
   nested: { a: Field, b: UInt32 },
   other: Field,
   pk: PublicKey,
-  uint: [UInt32, UInt32],
+  uint: [UInt32, { provable: UInt32 }],
 }) {}
 
 // Struct.fromValue() works on both js and provable inputs

From 852a7125391a59e578f6a880be88e84fc917e8ca Mon Sep 17 00:00:00 2001
From: Gregor <gregor.mitscha-baude@gmx.at>
Date: Tue, 16 Jul 2024 16:53:25 +0200
Subject: [PATCH 31/32] revert fix because it's breaking, stupid me

---
 src/lib/provable/gadgets/foreign-field.ts | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/src/lib/provable/gadgets/foreign-field.ts b/src/lib/provable/gadgets/foreign-field.ts
index db48d1c4f1..948a9b77f7 100644
--- a/src/lib/provable/gadgets/foreign-field.ts
+++ b/src/lib/provable/gadgets/foreign-field.ts
@@ -431,8 +431,13 @@ function equals(x: Field3, c: bigint, f: bigint) {
     return x012.equals(c);
   }
 }
-
-const provableLimb = modifiedField({});
+// TODO: remove this in v2!!!
+// having a `toInput()` method without a corresponding `check()` is begging for a vulnerability (which the current code has)
+const provableLimb = modifiedField({
+  toInput(x) {
+    return { packed: [[x, Number(l)]] };
+  },
+});
 
 const Field3 = {
   /**

From 301226dcfd89ad45589e6fdefc38141edf2b0041 Mon Sep 17 00:00:00 2001
From: Gregor Mitscha-Baude <gregor.mitscha-baude@gmx.at>
Date: Tue, 23 Jul 2024 10:30:10 +0200
Subject: [PATCH 32/32] changelog

---
 CHANGELOG.md | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 635cc09787..4733f19576 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -23,7 +23,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
 
 ### Changed
 
-- Reduced maximum bit length for `xor`, `not`, and `and`, operations from 254 to 240 bits to improve performance and simplify implementation. https://github.com/o1-labs/o1js/pull/1745
+- Reduced maximum bit length for `xor`, `not`, and `and`, operations from 254 to 240 bits to prevent overflow vulnerabilities. https://github.com/o1-labs/o1js/pull/1745
+- Allow using `Type` instead of `Type.provable` in APIs that expect a provable type https://github.com/o1-labs/o1js/pull/1751
+  - Example: `Provable.witness(Bytes32, () => bytes)`
+- Automatically wrap and unwrap `Unconstrained` in `fromValue` and `toValue`, so that we don't need to deal with "unconstrained" values outside provable code https://github.com/o1-labs/o1js/pull/1751
 
 ## [1.5.0](https://github.com/o1-labs/o1js/compare/ed198f305...1c736add) - 2024-07-09