UNPKG

o1js

Version:

TypeScript framework for zk-SNARKs and zkApps

126 lines (112 loc) 4.11 kB
import { Types } from '../../../bindings/mina-transaction/v1/types.js'; import { TokenId } from './account-update.js'; export { humanizeErrors, invalidTransactionError }; const ErrorHandlers = { Invalid_fee_excess({ transaction: { accountUpdates }, isFeePayer, accountCreationFee, }: ErrorHandlerArgs) { // TODO: handle fee payer for Invalid_fee_excess? if (isFeePayer) return; let balances = accountUpdates.map(({ body }) => { if (body.tokenId.equals(TokenId.default).toBoolean()) { return Number(body.balanceChange.toString()) * 1e-9; } }); let sum = balances.reduce((a = 0, b = 0) => a + b) ?? 0; return `Invalid fee excess. This means that balance changes in your transaction do not sum up to the amount of fees needed. Here's the list of balance changes: ${balances .map((balance, i) => { return `Account update #${i + 1}) ${ balance === undefined ? 'not a MINA account' : `${balance.toFixed(2)} MINA` }`; }) .join(`\n`)} Total change: ${sum.toFixed(2)} MINA If there are no new accounts created in your transaction, then this sum should be equal to 0.00 MINA. If you are creating new accounts -- by updating accounts that didn't exist yet -- then keep in mind the ${(Number(accountCreationFee) * 1e-9).toFixed( 2 )} MINA account creation fee, and make sure that the sum equals ${(-Number(accountCreationFee) * 1e-9).toFixed(2)} times the number of newly created accounts.`; }, }; const defaultErrorReplacementRules: ErrorReplacementRule[] = [ { pattern: /\(invalid \(Invalid_proof \\"In progress\\"\)\)/g, replacement: 'Stale verification key detected. Please make sure that deployed verification key reflects latest zkApp changes.', }, ]; type ErrorHandlerArgs = { transaction: Types.ZkappCommand; accountUpdateIndex: number; isFeePayer: boolean; accountCreationFee: string | number; }; type ErrorReplacementRule = { pattern: RegExp; replacement: string; }; function invalidTransactionError( transaction: Types.ZkappCommand, errors: string[][][], additionalContext: { accountCreationFee: string | number } ): string { let errorMessages = []; let rawErrors = JSON.stringify(errors); let n = transaction.accountUpdates.length; // Check if the number of errors match the number of account updates. If there are more, then the fee payer has an error. // We do this check because the fee payer error is not included in network transaction errors and is always present (even if empty) in the local transaction errors. if (errors.length > n) { let errorsForFeePayer = errors.shift() ?? []; for (let [error] of errorsForFeePayer) { let message = ErrorHandlers[error as keyof typeof ErrorHandlers]?.({ transaction, accountUpdateIndex: NaN, isFeePayer: true, ...additionalContext, }); if (message) errorMessages.push(message); } } for (let i = 0; i < errors.length; i++) { let errorsForUpdate = errors[i]; for (let [error] of errorsForUpdate) { let message = ErrorHandlers[error as keyof typeof ErrorHandlers]?.({ transaction, accountUpdateIndex: i, isFeePayer: false, ...additionalContext, }); if (message) errorMessages.push(message); } } if (errorMessages.length > 1) { return [ 'There were multiple errors when applying your transaction:', ...errorMessages.map((msg, i) => `${i + 1}.) ${msg}`), `Raw list of errors: ${rawErrors}`, ].join('\n\n'); } if (errorMessages.length === 1) { return `${errorMessages[0]}\n\nRaw list of errors: ${rawErrors}`; } // fallback if we don't have a good error message yet return rawErrors; } function humanizeErrors( errors: string[], replacements: ErrorReplacementRule[] = defaultErrorReplacementRules ): string[] { return errors.map((error) => { let modifiedError = error; replacements.forEach(({ pattern, replacement }) => { modifiedError = modifiedError.replace(pattern, replacement); }); return modifiedError; }); }