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;
};
Defined in: lib/provable/types/struct.ts:125
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.
Type Parameters
A
A
T
T
extends unknown
= InferProvable
<A
>
V
V
extends unknown
= InferValue
<A
>
J
J
extends unknown
= InferJson
<A
>
Pure
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
Example
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 reuse 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!
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.