Skip to main content

Error Handling

Contract calls can revert with on-chain errors. The SDK decodes raw revert data into typed error classes with decoded arguments and human-readable recovery hints.

Decoding revert errors

Use parseContractError() to decode raw revert data from a failed transaction or simulation:

import { parseContractError, getRevertData } from '@oaknetwork/contracts';

function handleError(err) {
// If the error is already a typed SDK error (thrown by simulate methods)
if (typeof err?.recoveryHint === 'string') {
console.error('Reverted:', err.name);
console.error('Args:', err.args);
console.error('Hint:', err.recoveryHint);
return;
}
// Otherwise extract raw revert hex from the viem error chain and decode it
const revertData = getRevertData(err);
const parsed = parseContractError(revertData ?? '');
if (parsed) {
console.error('Reverted:', parsed.name);
console.error('Args:', parsed.args);
if (parsed.recoveryHint) console.error('Hint:', parsed.recoveryHint);
return;
}
console.error('Unknown error:', err.message);
}

try {
const txHash = await factory.createCampaign({ ... });
} catch (err) {
handleError(err);
}

getRevertData(error)

Walks the error cause chain and extracts the first 0x-prefixed hex string from error.data. Returns null if no revert data is found.

parseContractError(revertData)

Takes a hex string (selector + encoded args) and returns a typed ContractErrorBase or null if the error is not recognized.

ContractErrorBase

All typed errors implement this interface:

interface ContractErrorBase {
readonly name: string; // e.g. "GlobalParamsUnauthorized"
readonly args: Record<string, unknown>; // decoded error arguments
readonly recoveryHint?: string; // human-readable suggestion
}

Simulation with error decoding

Use simulateWithErrorDecode() to wrap a simulateContract call. It catches reverts, decodes them, and re-throws as typed SDK errors:

import { simulateWithErrorDecode } from '@oaknetwork/contracts';

try {
await simulateWithErrorDecode(() =>
gp.simulate.enlistPlatform(platformHash, admin, fee, adapter)
);
// Safe to send
const txHash = await gp.enlistPlatform(platformHash, admin, fee, adapter);
} catch (err) {
if ('recoveryHint' in err) {
console.error(err.name, '-', err.recoveryHint);
}
}

Or use the simulate namespace directly on any entity — it already throws typed errors:

try {
await gp.simulate.enlistPlatform(platformHash, admin, fee, adapter);
} catch (err) {
console.error(err.name, err.recoveryHint);
}

Error types by contract

GlobalParams errors

ErrorWhen it occursRecovery hint
GlobalParamsInvalidInputInvalid address, fee, or bytes inputCheck addresses, fee percent, and platform bytes
GlobalParamsPlatformAlreadyListedEnlisting a platform that existsUse a different platform hash or update
GlobalParamsPlatformNotListedOperating on unlisted platformEnlist the platform first
GlobalParamsUnauthorizedCaller is not protocol adminUse the admin account
GlobalParamsPlatformAdminNotSetPlatform admin not configuredSet the platform admin address first
GlobalParamsPlatformFeePercentIsZeroZero fee percentFee must be greater than zero
GlobalParamsCurrencyHasNoTokensCurrency has no accepted tokensAdd at least one token
GlobalParamsTokenNotInCurrencyToken not in currency listUse an approved token
GlobalParamsPlatformLineItemTypeNotFoundLine item type not registeredRegister the line item type first
GlobalParamsPlatformDataAlreadySetData key already existsUse a different key
GlobalParamsPlatformDataNotSetData key not foundAdd platform data first
GlobalParamsPlatformDataSlotTakenData slot occupiedUse a different key
GlobalParamsCurrencyTokenLengthMismatchArray length mismatchMatch currencies and tokens array lengths

CampaignInfoFactory errors

ErrorWhen it occursRecovery hint
CampaignInfoFactoryInvalidInputInvalid campaign creation paramsCheck creator, platforms, and data
CampaignInfoFactoryPlatformNotListedSelected platform not in GlobalParamsEnlist the platform first
CampaignInfoFactoryCampaignWithSameIdentifierExistsDuplicate identifier hashUse a different identifier
CampaignInfoFactoryCampaignInitializationFailedClone failed to initializeCheck campaign data and implementation
CampaignInfoInvalidTokenListCurrency tokens don't match GlobalParamsFix token list for campaign currency

CampaignInfo errors

ErrorWhen it occursRecovery hint
CampaignInfoInvalidInputInvalid update inputCheck input values
CampaignInfoIsLockedCampaign is lockedCannot modify locked campaigns
CampaignInfoUnauthorizedCaller not authorizedUse the campaign owner account
CampaignInfoPlatformNotSelectedPlatform not selectedSelect the platform first
CampaignInfoPlatformAlreadyApprovedPlatform already approvedPlatform is already set up
CampaignInfoInvalidPlatformUpdateInvalid platform updateCheck platform hash and data

PaymentTreasury errors

ErrorWhen it occursRecovery hint
PaymentTreasuryInvalidInputInvalid payment parametersCheck payment data
PaymentTreasuryPaymentAlreadyExistDuplicate payment IDUse a unique payment ID
PaymentTreasuryPaymentNotExistPayment not foundCheck the payment ID
PaymentTreasuryPaymentAlreadyConfirmedPayment already confirmedCannot re-confirm
PaymentTreasuryPaymentAlreadyExpiredPayment expiredCreate a new payment
PaymentTreasuryPaymentNotConfirmedPayment not yet confirmedConfirm the payment first
PaymentTreasuryPaymentNotClaimablePayment does not meet refund eligibilityMay not be confirmed, expired, or claim window not reached
PaymentTreasuryTokenNotAcceptedToken not acceptedUse an accepted token
PaymentTreasuryUnAuthorizedCaller not authorizedUse authorized account
PaymentTreasuryCampaignInfoIsPausedCampaign is pausedUnpause the campaign first
PaymentTreasurySuccessConditionNotFulfilledGoal not reachedGoal amount must be reached before withdrawing
PaymentTreasuryCryptoPaymentWrong flow for crypto paymentUse processCryptoPayment() instead
PaymentTreasuryInsufficientBalanceInsufficient token balanceFund the account
PaymentTreasuryInsufficientFundsForFeeInsufficient funds for withdrawal feeEnsure treasury has enough balance for the fee
PaymentTreasuryFeeNotDisbursedFees not yet disbursedCall disburseFees() first
PaymentTreasuryAlreadyWithdrawnAlready withdrawnCannot withdraw twice
PaymentTreasuryExpirationExceedsMaxPayment expiration too longUse a shorter expiration time
PaymentTreasuryClaimWindowNotReachedClaim attempted too earlyWait until the claimableAt timestamp
PaymentTreasuryNoFundsToClaimNo funds availableNo refundable funds

AllOrNothing errors

ErrorWhen it occursRecovery hint
AllOrNothingInvalidInputInvalid parametersCheck input values
AllOrNothingNotSuccessfulGoal not metCannot withdraw — goal was not reached
AllOrNothingNotClaimableRefund not availableRefund conditions not met
AllOrNothingRewardExistsDuplicate reward nameUse a different reward name
AllOrNothingTokenNotAcceptedToken not acceptedUse an accepted token
AllOrNothingUnAuthorizedCaller not authorizedUse authorized account
AllOrNothingFeeAlreadyDisbursedFees already disbursedAlready processed
AllOrNothingFeeNotDisbursedFees not disbursedCall disburseFees() first
AllOrNothingTransferFailedToken transfer failedCheck balances and allowances
TreasurySuccessConditionNotFulfilledGoal not metGoal amount must be reached before withdrawing

KeepWhatsRaised errors

ErrorWhen it occursRecovery hint
KeepWhatsRaisedInvalidInputInvalid parametersCheck input values
KeepWhatsRaisedConfigLockedConfiguration is lockedWait for lock period to expire
KeepWhatsRaisedDisabledFeature is disabledCannot perform this operation
KeepWhatsRaisedAlreadyEnabledAlready enabledAlready configured
KeepWhatsRaisedAlreadyClaimedAlready claimedCannot claim twice
KeepWhatsRaisedAlreadyWithdrawnAlready withdrawnCannot withdraw twice
KeepWhatsRaisedNotClaimableNot claimableConditions not met for claim
KeepWhatsRaisedNotClaimableAdminAdmin claim conditions not metEnsure campaign is in the correct state
KeepWhatsRaisedRewardExistsDuplicate reward nameUse a different reward name
KeepWhatsRaisedTokenNotAcceptedToken not acceptedUse an accepted token
KeepWhatsRaisedUnAuthorizedCaller not authorizedUse authorized account
KeepWhatsRaisedPledgeAlreadyProcessedPledge already processedCannot re-process
KeepWhatsRaisedInsufficientFundsForFeeInsufficient funds for feeEnsure sufficient balance
KeepWhatsRaisedInsufficientFundsForWithdrawalAndFeeInsufficient funds for withdrawal + feeReduce withdrawal or wait for more pledges
KeepWhatsRaisedDisbursementBlockedDisbursement blockedConditions not met

TreasuryFactory errors

ErrorWhen it occursRecovery hint
TreasuryFactoryUnauthorizedCaller not authorizedUse authorized account
TreasuryFactoryInvalidAddressInvalid contract addressCheck the address
TreasuryFactoryInvalidKeyInvalid implementation keyCheck implementation ID
TreasuryFactoryImplementationNotSetImplementation not registeredRegister the implementation first
TreasuryFactoryImplementationNotSetOrApprovedNot set or not approvedRegister and approve the implementation
TreasuryFactoryTreasuryCreationFailedTreasury deploy failedCheck campaign and implementation
TreasuryFactoryTreasuryInitializationFailedTreasury init failedCheck configuration
TreasuryFactorySettingPlatformInfoFailedPlatform info update failedCheck platform configuration

Shared errors (cross-contract)

ErrorWhen it occursRecovery hint
AccessCheckerUnauthorizedGeneric access control failureCheck permissions
AdminAccessCheckerUnauthorizedNot an adminOnly admins can do this
CurrentTimeIsGreaterTimestamp in the pastProvide a future timestamp
CurrentTimeIsLessTime not yet reachedWait until the specified time
CurrentTimeIsNotWithinRangeOutside allowed time windowOperation only valid in range
TreasuryCampaignInfoIsPausedCampaign is pausedUnpause the campaign first
TreasuryFeeNotDisbursedFees not disbursedCall disburseFees() first
TreasuryTransferFailedToken transfer failedCheck balances and allowances

Checking error types

import {
GlobalParamsPlatformNotListedError,
CampaignInfoFactoryInvalidInputError,
} from '@oaknetwork/contracts';

try {
await factory.createCampaign(params);
} catch (err) {
if (err instanceof GlobalParamsPlatformNotListedError) {
console.error('Platform not listed:', err.args.platformHash);
} else if (err instanceof CampaignInfoFactoryInvalidInputError) {
console.error('Invalid input:', err.recoveryHint);
}
}

getRecoveryHint

Convenience function to extract the recovery hint from any typed error:

import { getRecoveryHint } from '@oaknetwork/contracts';

const hint = getRecoveryHint(err);
if (hint) console.log('Suggestion:', hint);