@blockassetlabs/minter
Version:
Blockasset Minter
345 lines (337 loc) • 9.58 kB
text/typescript
import type { Idl } from "@project-serum/anchor";
import type { PublicKey, SendTransactionError } from "@solana/web3.js";
import {
MINTER_ADDRESS,
MINTER_IDL,
} from "./programs/blockasset-minter/constants";
type ErrorCode = {
code: string;
message: string;
};
export const NATIVE_ERRORS: ErrorCode[] = [
{
code: "WalletSignTransactionError",
message: "User rejected the request.",
},
{
code: "failed to get recent blockhash",
message:
"Solana is experiencing degrading performance. You transaction failed to execute.",
},
{
code: "Blockhash not found",
message:
"Solana is experiencing degrading performance. Transaction may or may not have gone through.",
},
{
code: "Transaction was not confirmed in",
message:
"Transaction timed out waiting on confirmation from Solana. It may or may not have gone through.",
},
{
code: "Attempt to debit an account but found no record of a prior credit",
message: "Wallet has never had any sol before. Try adding sol first.",
},
{
code: "Provided owner is not allowed",
message: "Token account is already created for this user",
},
{
code: "not associated with",
message: "Account not associated with this mint",
},
{
code: "rent-exempt",
message:
"Insufficient funds. User does not have enough sol to complete the transaction",
},
// token program errors
{
code: "insufficient funds",
message:
"Insufficient funds. User does not have enough balance of token to complete the transaction",
},
// token program errors
{
code: "0x1",
message:
"Insufficient funds. User does not have enough balance of token to complete the transaction",
},
{
code: "0x4",
message:
"Invalid owner. The user is likely not mint authority of this token.",
},
{
code: "91",
message: "Token is not ellgible for original receipts",
},
// anchor errors
{
code: "100",
message: "InstructionMissing: 8 byte instruction identifier not provided",
},
{
code: "101",
message:
"InstructionFallbackNotFound: Fallback functions are not supported",
},
{
code: "102",
message:
"InstructionDidNotDeserialize: The program could not deserialize the given instruction",
},
{
code: "103",
message:
"InstructionDidNotSerialize: The program could not serialize the given instruction",
},
{
code: "1000",
message:
"IdlInstructionStub: The program was compiled without idl instructions",
},
{
code: "1001",
message:
"IdlInstructionInvalidProgram: Invalid program given to the IDL instruction",
},
{ code: "2000", message: "ConstraintMut: A mut constraint was violated" },
{
code: "2001",
message: "ConstraintHasOne: A has one constraint was violated",
},
{
code: "2002",
message: "ConstraintSigner: A signer constraint as violated",
},
{ code: "2003", message: "ConstraintRaw: A raw constraint was violated" },
{
code: "2004",
message: "ConstraintOwner: An owner constraint was violated",
},
{
code: "2005",
message: "ConstraintRentExempt: A rent exemption constraint was violated",
},
{
code: "2006",
message: "ConstraintSeeds: A seeds constraint was violated",
},
{
code: "2007",
message: "ConstraintExecutable: An executable constraint was violated",
},
{
code: "2008",
message: "ConstraintState: A state constraint was violated",
},
{
code: "2009",
message: "ConstraintAssociated: An associated constraint was violated",
},
{
code: "2010",
message:
"ConstraintAssociatedInit: An associated init constraint was violated",
},
{
code: "2011",
message: "ConstraintClose: A close constraint was violated",
},
{
code: "2012",
message: "ConstraintAddress: An address constraint was violated",
},
{
code: "2013",
message: "ConstraintZero: Expected zero account discriminant",
},
{
code: "2014",
message: "ConstraintTokenMint: A token mint constraint was violated",
},
{
code: "2015",
message: "ConstraintTokenOwner: A token owner constraint was violated",
},
{
code: "2016",
message:
"ConstraintMintMintAuthority: A mint mint authority constraint was violated",
},
{
code: "2017",
message:
"ConstraintMintFreezeAuthority: A mint freeze authority constraint was violated",
},
{
code: "2018",
message: "ConstraintMintDecimals: A mint decimals constraint was violated",
},
{
code: "2019",
message: "ConstraintSpace: A space constraint was violated",
},
{
code: "3000",
message:
"AccountDiscriminatorAlreadySet: The account discriminator was already set on this account",
},
{
code: "3001",
message:
"AccountDiscriminatorNotFound: No 8 byte discriminator was found on the account",
},
{
code: "3002",
message:
"AccountDiscriminatorMismatch: 8 byte discriminator did not match what was expected",
},
{
code: "3003",
message: "AccountDidNotDeserialize: Failed to deserialize the account",
},
{
code: "3004",
message: "AccountDidNotSerialize: Failed to serialize the account",
},
{
code: "3005",
message:
"AccountNotEnoughKeys: Not enough account keys given to the instruction",
},
{
code: "3006",
message: "AccountNotMutable: The given account is not mutable",
},
{
code: "3007",
message:
"AccountNotProgramOwned: The given account is not owned by the executing program",
},
{
code: "3008",
message: "InvalidProgramId: Program ID was not as expected",
},
{
code: "3009",
message: "InvalidProgramExecutable: Program account is not executable",
},
{
code: "3010",
message: "AccountNotSigner: The given account did not sign",
},
{
code: "3011",
message:
"AccountNotSystemOwned: The given account is not owned by the system program",
},
{
code: "3012",
message:
"AccountNotInitialized: The program expected this account to be already initialized",
},
{
code: "3013",
message:
"AccountNotProgramData: The given account is not a program data account",
},
{
code: "3014",
message:
"AccountNotAssociatedTokenAccount: The given account is not the associated token account",
},
{
code: "4000",
message:
"StateInvalidAddress: The given state account does not have the correct address",
},
{
code: "5000",
message:
"Deprecated: The API being used is deprecated and should no longer be used",
},
].reverse();
export type ErrorOptions = {
/** ProgramIdls in priority order */
programIdls?: { idl: Idl; programId: PublicKey }[];
/** Additional errors by code */
additionalErrors?: ErrorCode[];
};
export const handleError = (
e: unknown,
fallBackMessage = "Transaction failed",
// programIdls in priority order
options: ErrorOptions = {
programIdls: [{ programId: MINTER_ADDRESS, idl: MINTER_IDL }],
additionalErrors: NATIVE_ERRORS,
}
): string => {
const programIdls = options.programIdls ?? [];
const additionalErrors = options.additionalErrors ?? [];
const hex = (e as SendTransactionError)?.message?.split(" ").at(-1);
const dec = parseInt(hex || "", 16);
const logs =
(e as SendTransactionError)?.logs ?? [
(e as SendTransactionError)?.message,
] ?? [(e as Error).toString()] ??
[];
const matchedErrors: { programMatch?: boolean; errorMatch?: string }[] = [
...[
...programIdls.map(({ idl, programId }) => ({
// match program on any log that includes programId and 'failed'
programMatch: logs?.some(
(l) => l?.includes(programId.toString()) && l.includes("failed")
),
// match error with decimal
errorMatch: idl.errors?.find((err) => err.code === dec)?.msg,
})),
{
// match native error with decimal
errorMatch: additionalErrors.find((err) => err.code === dec.toString())
?.message,
},
],
...[
{
programMatch: true,
errorMatch: additionalErrors.find(
(err) =>
// message includes error
(e as SendTransactionError)?.message?.includes(err.code) ||
// toString includes error
(e as Error).toString().includes(err.code) ||
// any log includes error
(e as SendTransactionError)?.logs?.some((l) =>
l.toString().includes(err.code)
)
)?.message,
},
...programIdls.map(({ idl, programId }) => ({
// match program on any log that includes programId and 'failed'
programMatch: logs?.some(
(l) => l?.includes(programId.toString()) && l.includes("failed")
),
errorMatch: idl.errors?.find(
(err) =>
// message includes error
(e as SendTransactionError)?.message?.includes(
err.code.toString()
) ||
// toString includes error
(e as Error).toString().includes(err.code.toString()) ||
// any log includes error
(e as SendTransactionError)?.logs?.some((l) =>
l.toString().includes(err.code.toString())
)
)?.msg,
})),
],
];
return (
matchedErrors.find((e) => e.programMatch && e.errorMatch)?.errorMatch ||
matchedErrors.find((e) => e.errorMatch)?.errorMatch ||
fallBackMessage
);
};