Skip to main content

Struct

function Struct<A, T, V, J, Pure>(type: A): (value: T) => T & {
"_isStruct": true;
} & Pure extends true ? ProvablePure<T, V> : Provable<T, V> & {
"empty": () => T;
"fromJSON": (x: J) => T;
"fromValue": (value: From<A>) => T;
"toInput": (x: T) => {
"fields": Field[];
"packed": [Field, number][];
};
"toJSON": (x: T) => J;
}

Struct lets you declare composite types for use in o1js circuits.

These composite types can be passed in as arguments to smart contract methods, used for on-chain state variables or as event / action types.

Here's an example of creating a "Voter" struct, which holds a public key and a collection of votes on 3 different proposals:

let Vote = { hasVoted: Bool, inFavor: Bool };

class Voter extends Struct({
publicKey: PublicKey,
votes: [Vote, Vote, Vote]
}) {}

// use Voter as SmartContract input:
class VoterContract extends SmartContract {
\@method register(voter: Voter) {
// ...
}
}

In this example, there are no instance methods on the class. This makes Voter type-compatible with an anonymous object of the form { publicKey: PublicKey, votes: Vote[] }. This mean you don't have to create instances by using new Voter(...), you can operate with plain objects:

voterContract.register({ publicKey, votes });

On the other hand, you can also add your own methods:

class Voter extends Struct({
publicKey: PublicKey,
votes: [Vote, Vote, Vote]
}) {
vote(index: number, inFavor: Bool) {
let vote = this.votes[i];
vote.hasVoted = Bool(true);
vote.inFavor = inFavor;
}
}

In this case, you'll need the constructor to create instances of Voter. It always takes as input the plain object:

let emptyVote = { hasVoted: Bool(false), inFavor: Bool(false) };
let voter = new Voter({ publicKey, votes: Array(3).fill(emptyVote) });
voter.vote(1, Bool(true));

In addition to creating types composed of Field elements, you can also include auxiliary data which does not become part of the proof. This, for example, allows you to re-use the same type outside o1js methods, where you might want to store additional metadata.

To declare non-proof values of type string, number, etc, you can use the built-in objects String, Number, etc. Here's how we could add the voter's name (a string) as auxiliary data:

class Voter extends Struct({
publicKey: PublicKey,
votes: [Vote, Vote, Vote],
fullName: String
}) {}

Again, it's important to note that this doesn't enable you to prove anything about the fullName string. From the circuit point of view, it simply doesn't exist!

Type parameters

A

T extends unknown = InferProvable\<A>

V extends unknown = InferValue\<A>

J extends unknown = InferJson\<A>

Pure extends boolean = IsPure\<A>

Parameters

type: A

Object specifying the layout of the Struct

Returns

(value: T) => T & { "_isStruct": true; } & Pure extends true ? ProvablePure\<T, V> : Provable\<T, V> & { "empty": () => T; "fromJSON": (x: J) => T; "fromValue": (value: From\<A>) => T; "toInput": (x: T) => { "fields": Field[]; "packed": [Field, number][]; }; "toJSON": (x: T) => J; }

Class which you can extend

Note

Ensure you do not use or extend Struct as a type directly. Instead, always call it as a function to construct a type. Struct is not a valid provable type itself, types created with Struct(...) are.

Source

lib/provable/types/struct.ts:140