@byzantine/vault-sdk
Version:
Byzantine Vault SDK for creating and managing vaults on Ethereum for restaking strategies
116 lines (115 loc) • 4.29 kB
JavaScript
;
/**
* Contract Error Handler Utility
*
* Helper functions to standardize contract error handling across the SDK
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.executeContractMethod = executeContractMethod;
exports.callContractMethod = callContractMethod;
exports.formatContractError = formatContractError;
const errorCodeMapping_1 = require("./errorCodeMapping");
/**
* Execute a contract method with proper error handling
* Uses staticCall to get meaningful errors before executing transaction
*
* @param contract - Ethers contract instance
* @param method - Method name to call
* @param args - Arguments to pass to the method
* @returns Promise resolving to transaction response
*/
async function executeContractMethod(contract, method, ...args) {
try {
// Separate transaction options from method arguments
// The last argument might be transaction options if it has transaction properties
let methodArgs = args;
let txOptions = {};
// Check if the last argument looks like transaction options
if (args.length > 0) {
const lastArg = args[args.length - 1];
if (lastArg &&
typeof lastArg === "object" &&
(lastArg.gasLimit ||
lastArg.gasPrice ||
lastArg.value ||
lastArg.nonce ||
lastArg.type ||
lastArg.maxFeePerGas ||
lastArg.maxPriorityFeePerGas)) {
txOptions = lastArg;
methodArgs = args.slice(0, -1);
}
}
// First try with staticCall to get a meaningful error (without tx options)
await contract[method].staticCall(...methodArgs);
// If staticCall succeeds, execute the real transaction with options
return await contract[method](...methodArgs, txOptions);
}
catch (error) {
throw formatContractError(method, error);
}
}
/**
* Execute a read-only contract method with proper error handling
*
* @param contract - Ethers contract instance
* @param method - Method name to call
* @param args - Arguments to pass to the method
* @returns Promise resolving to the method's return value
*/
async function callContractMethod(contract, method, ...args) {
try {
return await contract[method](...args);
}
catch (error) {
throw formatContractError(method, error);
}
}
/**
* Format a contract error into a more meaningful message
*
* @param method - Contract method name
* @param error - Error object
* @returns Error with improved message
*/
function formatContractError(method, error) {
// console.log("Going to format contract error", method, error);
// console.log("Error revert", error.revert);
// console.log("Error reason", error.reason);
// console.log("Error message", error.message);
// console.log("Error stack", error.stack);
// console.log("Error code", error.code);
// console.log("Error data", error.data);
// console.log("Error error", error.error);
// Extract error reason from revert data if available
if (error.revert) {
const errorName = error.revert.name;
const errorArgs = error.revert.args;
let argsStr = "";
try {
// Format args as string, handling different types
argsStr = errorArgs
.map((arg) => {
if (typeof arg === "bigint") {
return arg.toString();
}
return arg;
})
.join(", ");
}
catch (e) {
argsStr = "unparsable args";
}
return new Error(`Contract error: ${errorName}(${argsStr})`);
}
// If we got a standard reason
if (error.reason) {
return new Error(`Contract error: ${error.reason}`);
}
// Check if error has data property and it's in our error mapping
if (error.data && errorCodeMapping_1.ErrorCodeMapping[error.data]) {
return new Error(`Contract error: ${errorCodeMapping_1.ErrorCodeMapping[error.data]}`);
}
// For any other error
return new Error(`Failed to execute ${method}: ${error.message || "Unknown error"}`);
}