UNPKG

o1js

Version:

TypeScript framework for zk-SNARKs and zkApps

591 lines (560 loc) 13.7 kB
import { Types } from '../../../bindings/mina-transaction/v1/types.js'; import { UInt32 } from '../../provable/int.js'; import { TokenId, type ZkappCommand } from './account-update.js'; import type { ActionStatesStringified } from './fetch.js'; export { accountQuery, currentSlotQuery, genesisConstantsQuery, getActionsQuery, getEventsQuery, lastBlockQuery, lastBlockQueryFailureCheck, removeJsonQuotes, sendZkappQuery, transactionStatusQuery, type ActionQueryResponse, type ActionsQueryInputs, type CurrentSlotResponse, type DepthOptions, type EpochData, type EventActionFilterOptions, type EventQueryResponse, type EventsQueryInputs, type FailureReasonResponse, type FetchedAccount, type FetchedAccountResponse, type FetchedAction, type FetchedBlock, type GenesisConstantsResponse, type LastBlockQueryFailureCheckResponse, type LastBlockQueryResponse, type SendZkAppResponse, type TransactionDepthInfo, type TransactionStatus, type TransactionStatusQueryResponse, }; // removes the quotes on JSON keys function removeJsonQuotes(json: string) { let cleaned = JSON.stringify(JSON.parse(json), null, 2); return cleaned.replace(/\"(\S+)\"\s*:/gm, '$1:'); } type ActionsQueryInputs = { /** Public key of the ZkApp to which actions have been emitted */ publicKey: string; actionStates?: ActionStatesStringified; tokenId?: string; /** Block number to query from */ from?: number; /** Block number to query to */ to?: number; }; type EventsQueryInputs = { /** Public key of the ZkApp to which events have been emitted */ publicKey: string; tokenId?: string; /** Block number to query from */ from?: number; /** Block number to query to */ to?: number; }; type AuthRequired = Types.Json.AuthRequired; // TODO auto-generate this type and the query type FetchedAccount = { publicKey: string; token: string; nonce: string; balance: { total: string }; tokenSymbol: string | null; receiptChainHash: string | null; timing: { initialMinimumBalance: string | null; cliffTime: string | null; cliffAmount: string | null; vestingPeriod: string | null; vestingIncrement: string | null; }; permissions: { editState: AuthRequired; access: AuthRequired; send: AuthRequired; receive: AuthRequired; setDelegate: AuthRequired; setPermissions: AuthRequired; setVerificationKey: { auth: AuthRequired; txnVersion: string; }; setZkappUri: AuthRequired; editActionState: AuthRequired; setTokenSymbol: AuthRequired; incrementNonce: AuthRequired; setVotingFor: AuthRequired; setTiming: AuthRequired; } | null; delegateAccount: { publicKey: string } | null; votingFor: string | null; zkappState: string[] | null; verificationKey: { verificationKey: string; hash: string } | null; actionState: string[] | null; provedState: boolean | null; zkappUri: string | null; }; type FetchedAccountResponse = { account: FetchedAccount; }; type EpochData = { ledger: { hash: string; totalCurrency: string; }; seed: string; startCheckpoint: string; lockCheckpoint: string; epochLength: string; }; type LastBlockQueryResponse = { bestChain: { protocolState: { blockchainState: { snarkedLedgerHash: string; stagedLedgerHash: string; date: string; utcDate: string; stagedLedgerProofEmitted: boolean; }; previousStateHash: string; consensusState: { blockHeight: string; slotSinceGenesis: string; slot: string; nextEpochData: EpochData; stakingEpochData: EpochData; epochCount: string; minWindowDensity: string; totalCurrency: string; epoch: string; }; }; }[]; }; type FailureReasonResponse = { failures: string[]; index: number; }[]; type LastBlockQueryFailureCheckResponse = { bestChain: { transactions: { zkappCommands: { hash: string; failureReason: FailureReasonResponse; }[]; }; stateHash: string; protocolState: { consensusState: { blockHeight: string; epoch: string; slotSinceGenesis: string; }; previousStateHash: string; }; }[]; }; type FetchedBlock = { protocolState: { blockchainState: { snarkedLedgerHash: string; // hash-like encoding stagedLedgerHash: string; // hash-like encoding date: string; // String(Date.now()) utcDate: string; // String(Date.now()) stagedLedgerProofEmitted: boolean; // bool }; previousStateHash: string; // hash-like encoding consensusState: { blockHeight: string; // String(number) slotSinceGenesis: string; // String(number) slot: string; // String(number) nextEpochData: { ledger: { hash: string; // hash-like encoding totalCurrency: string; // String(number) }; seed: string; // hash-like encoding startCheckpoint: string; // hash-like encoding lockCheckpoint: string; // hash-like encoding epochLength: string; // String(number) }; stakingEpochData: { ledger: { hash: string; // hash-like encoding totalCurrency: string; // String(number) }; seed: string; // hash-like encoding startCheckpoint: string; // hash-like encoding lockCheckpoint: string; // hash-like encoding epochLength: string; // String(number) }; epochCount: string; // String(number) minWindowDensity: string; // String(number) totalCurrency: string; // String(number) epoch: string; // String(number) }; }; }; type GenesisConstantsResponse = { genesisConstants: { genesisTimestamp: string; coinbase: string; accountCreationFee: string; }; daemonStatus: { consensusConfiguration: { epochDuration: string; k: string; slotDuration: string; slotsPerEpoch: string; }; }; }; type CurrentSlotResponse = { bestChain: Array<{ protocolState: { consensusState: { slot: number; slotSinceGenesis: number; }; }; }>; }; /** * INCLUDED: A transaction that is on the longest chain * * PENDING: A transaction either in the transition frontier or in transaction pool but is not on the longest chain * * UNKNOWN: The transaction has either been snarked, reached finality through consensus or has been dropped * */ type TransactionStatus = 'INCLUDED' | 'PENDING' | 'UNKNOWN'; type TransactionStatusQueryResponse = { transactionStatus: TransactionStatus; }; /** * Information about a transaction's depth (confirmation count) in the blockchain. * Depth represents how many blocks have been built on top of the block containing the transaction. * * @see https://docs.minaprotocol.com/mina-protocol/lifecycle-of-a-payment */ type TransactionDepthInfo = { /** Number of blocks built on top of the block containing the transaction (0 = just included) */ depth: number; /** Block height at which the transaction was included */ inclusionBlockHeight: number; /** Current block height of the chain */ currentBlockHeight: number; /** Whether the transaction has reached finality (depth >= finalityThreshold) */ isFinalized: boolean; /** The finality threshold used for this calculation */ finalityThreshold: number; }; /** * Options for querying transaction depth. */ type DepthOptions = { /** Number of blocks to search for the transaction (default: 20) */ blockLength?: number; /** * Number of blocks required for finality (default: 15). * Default of 15 blocks provides 99.9% confidence per Mina documentation. * @see https://docs.minaprotocol.com/mina-protocol/lifecycle-of-a-payment */ finalityThreshold?: number; }; type SendZkAppResponse = { sendZkapp: { zkapp: { hash: string; id: string; zkappCommand: ZkappCommand; failureReasons: FailureReasonResponse; }; }; }; type EventQueryResponse = { events: { blockInfo: { distanceFromMaxBlockHeight: number; globalSlotSinceGenesis: number; height: number; stateHash: string; parentHash: string; chainStatus: string; }; eventData: { transactionInfo: { hash: string; memo: string; status: string; }; data: string[]; }[]; }[]; }; type FetchedAction = { blockInfo: { distanceFromMaxBlockHeight: number; }; actionState: { actionStateOne: string; actionStateTwo: string; }; actionData: { accountUpdateId: string; data: string[]; transactionInfo?: { sequenceNumber: number; zkappAccountUpdateIds: number[]; }; }[]; }; type ActionQueryResponse = { actions: FetchedAction[]; }; type EventActionFilterOptions = { to?: UInt32; from?: UInt32; }; const transactionStatusQuery = (txId: string) => `query { transactionStatus(zkappTransaction:"${txId}") }`; const getEventsQuery = (inputs: EventsQueryInputs) => { inputs.tokenId ??= TokenId.toBase58(TokenId.default); const { publicKey, tokenId, to, from } = inputs; let input = `address: "${publicKey}", tokenId: "${tokenId}"`; if (to !== undefined) { input += `, to: ${to.toString()}`; } if (from !== undefined) { input += `, from: ${from.toString()}`; } return `{ events(input: { ${input} }) { blockInfo { distanceFromMaxBlockHeight height globalSlotSinceGenesis stateHash parentHash chainStatus } eventData { transactionInfo { hash memo status } data } } }`; }; const getActionsQuery = (inputs: ActionsQueryInputs) => { inputs.tokenId ??= TokenId.toBase58(TokenId.default); const { publicKey, tokenId, actionStates, from, to } = inputs; const { fromActionState, endActionState } = actionStates ?? {}; let input = `address: "${publicKey}", tokenId: "${tokenId}"`; if (fromActionState !== undefined) { input += `, fromActionState: "${fromActionState}"`; } if (endActionState !== undefined) { input += `, endActionState: "${endActionState}"`; } if (to !== undefined) { input += `, to: ${to}`; } if (from !== undefined) { input += `, from: ${from}`; } return `{ actions(input: { ${input} }) { blockInfo { distanceFromMaxBlockHeight } actionState { actionStateOne actionStateTwo } actionData { accountUpdateId data transactionInfo { sequenceNumber zkappAccountUpdateIds } } } }`; }; const genesisConstantsQuery = `{ genesisConstants { genesisTimestamp coinbase accountCreationFee } daemonStatus { consensusConfiguration { epochDuration k slotDuration slotsPerEpoch } } }`; const lastBlockQuery = `{ bestChain(maxLength: 1) { protocolState { blockchainState { snarkedLedgerHash stagedLedgerHash date utcDate stagedLedgerProofEmitted } previousStateHash consensusState { blockHeight slotSinceGenesis slot nextEpochData { ledger {hash totalCurrency} seed startCheckpoint lockCheckpoint epochLength } stakingEpochData { ledger {hash totalCurrency} seed startCheckpoint lockCheckpoint epochLength } epochCount minWindowDensity totalCurrency epoch } } } }`; const lastBlockQueryFailureCheck = (length: number) => `{ bestChain(maxLength: ${length}) { transactions { zkappCommands { hash failureReason { failures index } } } stateHash protocolState { consensusState { blockHeight epoch slotSinceGenesis } previousStateHash } } }`; // TODO: Decide an appropriate response structure. function sendZkappQuery(json: string) { return `mutation { sendZkapp(input: { zkappCommand: ${removeJsonQuotes(json)} }) { zkapp { hash id failureReason { failures index } zkappCommand { memo feePayer { body { publicKey } } accountUpdates { body { publicKey useFullCommitment incrementNonce } } } } } } `; } const accountQuery = (publicKey: string, tokenId: string) => `{ account(publicKey: "${publicKey}", token: "${tokenId}") { publicKey token nonce balance { total } tokenSymbol receiptChainHash timing { initialMinimumBalance cliffTime cliffAmount vestingPeriod vestingIncrement } permissions { editState access send receive setDelegate setPermissions setVerificationKey { auth txnVersion } setZkappUri editActionState setTokenSymbol incrementNonce setVotingFor setTiming } delegateAccount { publicKey } votingFor zkappState verificationKey { verificationKey hash } actionState provedState zkappUri } } `; const currentSlotQuery = `{ bestChain(maxLength: 1) { protocolState { consensusState { slot slotSinceGenesis } } } }`;