o1js
Version:
TypeScript framework for zk-SNARKs and zkApps
89 lines (72 loc) • 2.57 kB
text/typescript
import {
SmartContract,
method,
state,
PublicKey,
UInt64,
Experimental,
} from '../../../../../index.js';
export { offchainState, StateProof, ExampleContract };
const { OffchainState } = Experimental;
const offchainState = OffchainState(
{
accounts: OffchainState.Map(PublicKey, UInt64),
totalSupply: OffchainState.Field(UInt64),
},
{ logTotalCapacity: 10, maxActionsPerProof: 5 }
);
class StateProof extends offchainState.Proof {}
// example contract that interacts with offchain state
class ExampleContract extends SmartContract {
(OffchainState.Commitments) offchainStateCommitments = offchainState.emptyCommitments();
// o1js memoizes the offchain state by contract address so that this pattern works
offchainState = offchainState.init(this);
async createAccount(address: PublicKey, amountToMint: UInt64) {
// setting `from` to `undefined` means that the account must not exist yet
this.offchainState.fields.accounts.update(address, {
from: undefined,
to: amountToMint,
});
// TODO using `update()` on the total supply means that this method
// can only be called once every settling cycle
let totalSupplyOption = await this.offchainState.fields.totalSupply.get();
let totalSupply = totalSupplyOption.orElse(0n);
this.offchainState.fields.totalSupply.update({
from: totalSupplyOption,
to: totalSupply.add(amountToMint),
});
}
async transfer(from: PublicKey, to: PublicKey, amount: UInt64) {
let fromOption = await this.offchainState.fields.accounts.get(from);
let fromBalance = fromOption.assertSome('sender account exists');
let toOption = await this.offchainState.fields.accounts.get(to);
let toBalance = toOption.orElse(0n);
/**
* Update both accounts atomically.
*
* This is safe, because both updates will only be accepted if both previous balances are still correct.
*/
this.offchainState.fields.accounts.update(from, {
from: fromOption,
to: fromBalance.sub(amount),
});
this.offchainState.fields.accounts.update(to, {
from: toOption,
to: toBalance.add(amount),
});
}
.returns(UInt64)
async getSupply() {
return (await this.offchainState.fields.totalSupply.get()).orElse(0n);
}
.returns(UInt64)
async getBalance(address: PublicKey) {
return (await this.offchainState.fields.accounts.get(address)).orElse(0n);
}
async settle(proof: StateProof) {
await this.offchainState.settle(proof);
}
}