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.