@raac/rpc 1.1.0-beta.53 → 1.1.0-beta.55

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -120,6 +120,7 @@ const vaultOpened_1 = __importDefault(require("./pools/lendingPool/vaultOpened")
120
120
  const getWithdrawRequestInfo_2 = require("./pools/stabilityPool/getWithdrawRequestInfo");
121
121
  const requestWithdraw_2 = require("./pools/stabilityPool/requestWithdraw");
122
122
  const artifacts_1 = require("./utils/artifacts");
123
+ const errorDecoder_1 = require("./utils/errorDecoder");
123
124
  class RPCLibrary {
124
125
  signer;
125
126
  isConnected;
@@ -139,6 +140,7 @@ class RPCLibrary {
139
140
  checkAllowance;
140
141
  getChainsConfig;
141
142
  setChainsConfig;
143
+ errors;
142
144
  constructor(privateKey) {
143
145
  this.signer = null;
144
146
  this.isConnected = false;
@@ -282,6 +284,13 @@ class RPCLibrary {
282
284
  createZeno: createZeno_1.default,
283
285
  getZenos: getZenos_1.default,
284
286
  };
287
+ this.errors = {
288
+ decodeErrorName: errorDecoder_1.decodeErrorName,
289
+ getErrorInfo: errorDecoder_1.getErrorInfo,
290
+ attachDecodedError: errorDecoder_1.attachDecodedError,
291
+ decodeErrorMessage: errorDecoder_1.decodeErrorMessage,
292
+ attachDecodedErrorMessage: errorDecoder_1.attachDecodedErrorMessage,
293
+ };
285
294
  }
286
295
  async getWallet(privateKey, provider) {
287
296
  // @ts-ignore
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const ethers_1 = require("ethers");
4
4
  const _helpers_1 = require("./_helpers");
5
+ const errorDecoder_1 = require("../../utils/errorDecoder");
5
6
  // checked
6
7
  async function depositToLendingPool(chainId, amount, signer) {
7
8
  if (!signer) {
@@ -22,7 +23,8 @@ async function depositToLendingPool(chainId, amount, signer) {
22
23
  }
23
24
  catch (error) {
24
25
  console.error(`Lending Pool Deposit error:`, error);
25
- throw new Error(`Lending Pool Deposit failed: ${error.message}`);
26
+ const message = (0, errorDecoder_1.attachDecodedError)("Lending Pool Deposit failed", error);
27
+ throw new Error(`${message}: ${error.message}`);
26
28
  }
27
29
  }
28
30
  exports.default = depositToLendingPool;
@@ -0,0 +1,180 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const fs_1 = __importDefault(require("fs"));
7
+ const path_1 = __importDefault(require("path"));
8
+ const ethers_1 = require("ethers");
9
+ // Custom error messages based on error names
10
+ const ERROR_MESSAGES = {
11
+ // Access Control
12
+ 'AccessControlBadConfirmation': 'Invalid access control confirmation',
13
+ 'AccessControlUnauthorizedAccount': 'Account does not have required permissions',
14
+ 'AccessDenied': 'Access denied - insufficient permissions',
15
+ // Adapter Errors
16
+ 'AdapterAlreadySupported': 'This adapter is already supported',
17
+ 'AdapterNotSupported': 'This adapter is not supported',
18
+ // Address Errors
19
+ 'AddressCannotBeZero': 'Address cannot be zero',
20
+ 'AddressEmptyCode': 'Target address has no code',
21
+ 'AddressInsufficientBalance': 'Insufficient balance for operation',
22
+ // Amount Errors
23
+ 'AmountDifferentThanRequested': 'Amount differs from requested amount',
24
+ 'ApprovalFailed': 'Token approval failed',
25
+ // Compliance
26
+ 'BlacklistedAddress': 'Address is blacklisted',
27
+ // Pool Errors
28
+ 'BorrowCapReached': 'Borrow cap has been reached',
29
+ 'BorrowThresholdTooHigh': 'Borrow threshold is too high',
30
+ 'CannotBorrowUnderLiquidation': 'Cannot borrow while under liquidation',
31
+ 'CannotDepositWhenUnderLiquidation': 'Cannot deposit while under liquidation',
32
+ 'CannotRepayUnderLiquidationWithoutInsurance': 'Cannot repay under liquidation without insurance',
33
+ 'CannotWithdrawInSameBlock': 'Cannot withdraw in the same block',
34
+ 'CannotWithdrawUnderLiquidation': 'Cannot withdraw while under liquidation',
35
+ 'CapExceedSupply': 'Cap exceeds available supply',
36
+ 'CapZero': 'Cap cannot be zero',
37
+ 'DebtNotZero': 'Debt must be zero for this operation',
38
+ 'SupplyCapReached': 'Supply cap has been reached',
39
+ // Pause Errors
40
+ 'EnforcedPause': 'Contract is paused',
41
+ 'ExpectedPause': 'Contract should be paused',
42
+ // General Errors
43
+ 'FailedInnerCall': 'Internal call failed',
44
+ 'GracePeriodExpired': 'Grace period has expired',
45
+ 'InsufficientAllowance': 'Insufficient allowance for operation',
46
+ 'InsufficientBalance': 'Insufficient balance for operation',
47
+ 'InvalidAmount': 'Invalid amount provided',
48
+ 'InvalidCollateralRatio': 'Invalid collateral ratio',
49
+ 'InvalidHealthFactor': 'Invalid health factor',
50
+ 'InvalidLiquidationThreshold': 'Invalid liquidation threshold',
51
+ 'InvalidParameter': 'Invalid parameter provided',
52
+ 'InvalidPrice': 'Invalid price provided',
53
+ 'InvalidToken': 'Invalid token address',
54
+ 'LiquidationNotAllowed': 'Liquidation is not allowed',
55
+ 'NoLiquidationAvailable': 'No liquidation available',
56
+ 'NotEnoughLiquidity': 'Not enough liquidity available',
57
+ 'OperationNotAllowed': 'Operation is not allowed',
58
+ 'Overflow': 'Arithmetic overflow occurred',
59
+ 'Paused': 'Contract is paused',
60
+ 'ReentrancyGuardReentrantCall': 'Reentrant call detected',
61
+ 'ReserveAlreadyInitialized': 'Reserve is already initialized',
62
+ 'ReserveNotActive': 'Reserve is not active',
63
+ 'ReserveNotInitialized': 'Reserve is not initialized',
64
+ 'SafeERC20FailedOperation': 'SafeERC20 operation failed',
65
+ 'TransferFailed': 'Token transfer failed',
66
+ 'Unauthorized': 'Unauthorized operation',
67
+ 'Underflow': 'Arithmetic underflow occurred',
68
+ 'VaultNotOpen': 'Vault is not open',
69
+ 'WithdrawalNotAllowed': 'Withdrawal is not allowed',
70
+ // Custom contract-specific errors
71
+ 'InvalidOraclePrice': 'Invalid oracle price received',
72
+ 'PriceStale': 'Oracle price is stale',
73
+ 'InvalidRandomSeed': 'Invalid random seed provided',
74
+ 'RandomSeedNotReady': 'Random seed is not ready',
75
+ 'NFTNotAvailable': 'NFT is not available',
76
+ 'InvalidNFTId': 'Invalid NFT ID provided',
77
+ 'NFTAlreadyDeposited': 'NFT is already deposited',
78
+ 'NFTNotDeposited': 'NFT is not deposited',
79
+ 'InvalidVaultPosition': 'Invalid vault position',
80
+ 'PositionNotOpen': 'Position is not open',
81
+ 'InvalidCollateral': 'Invalid collateral provided',
82
+ 'CollateralNotSupported': 'Collateral is not supported',
83
+ 'InvalidDebtToken': 'Invalid debt token',
84
+ 'DebtTokenNotSupported': 'Debt token is not supported',
85
+ 'InvalidLiquidationProxy': 'Invalid liquidation proxy',
86
+ 'InvalidVaultProxy': 'Invalid vault proxy',
87
+ 'InvalidComplianceRegistry': 'Invalid compliance registry',
88
+ 'InvalidPrimeRate': 'Invalid prime rate',
89
+ 'InvalidAdmin': 'Invalid admin address',
90
+ 'InvalidReserveAsset': 'Invalid reserve asset',
91
+ 'InvalidRToken': 'Invalid R token',
92
+ 'InvalidDebtTokenAddress': 'Invalid debt token address',
93
+ 'InvalidLiquidationProxyAddress': 'Invalid liquidation proxy address',
94
+ 'InvalidVaultProxyAddress': 'Invalid vault proxy address',
95
+ 'InvalidComplianceRegistryAddress': 'Invalid compliance registry address',
96
+ 'InvalidInitialPrimeRate': 'Invalid initial prime rate',
97
+ 'InvalidAdminAddress': 'Invalid admin address',
98
+ };
99
+ function walkDir(dir) {
100
+ const files = [];
101
+ const items = fs_1.default.readdirSync(dir, { withFileTypes: true });
102
+ for (const item of items) {
103
+ const fullPath = path_1.default.join(dir, item.name);
104
+ if (item.isDirectory()) {
105
+ files.push(...walkDir(fullPath));
106
+ }
107
+ else if (item.name.endsWith('.json')) {
108
+ files.push(fullPath);
109
+ }
110
+ }
111
+ return files;
112
+ }
113
+ function generateErrorMap() {
114
+ const errorMap = {};
115
+ const artifactsDir = path_1.default.join(process.cwd(), 'artifacts');
116
+ if (!fs_1.default.existsSync(artifactsDir)) {
117
+ console.error('Artifacts directory not found');
118
+ process.exit(1);
119
+ }
120
+ const jsonFiles = walkDir(artifactsDir);
121
+ for (const filePath of jsonFiles) {
122
+ try {
123
+ const content = fs_1.default.readFileSync(filePath, 'utf8');
124
+ const json = JSON.parse(content);
125
+ if (!json.abi || !Array.isArray(json.abi))
126
+ continue;
127
+ const contractName = json.contractName || path_1.default.basename(filePath, '.json');
128
+ for (const item of json.abi) {
129
+ if (item.type !== 'error')
130
+ continue;
131
+ const inputs = item.inputs || [];
132
+ const signature = `${item.name}(${inputs.map((input) => input.type).join(',')})`;
133
+ const selector = (0, ethers_1.id)(signature).slice(0, 10).toLowerCase();
134
+ // Skip if already processed (first occurrence wins)
135
+ if (errorMap[selector])
136
+ continue;
137
+ const customMessage = ERROR_MESSAGES[item.name] ||
138
+ `${item.name.replace(/([A-Z])/g, ' $1').trim()} error occurred`;
139
+ errorMap[selector] = {
140
+ name: item.name,
141
+ signature,
142
+ message: customMessage,
143
+ contract: contractName,
144
+ inputs: inputs.map((input) => ({
145
+ name: input.name || '',
146
+ type: input.type,
147
+ internalType: input.internalType || input.type,
148
+ })),
149
+ };
150
+ }
151
+ }
152
+ catch (error) {
153
+ console.warn(`Failed to process ${filePath}:`, error);
154
+ }
155
+ }
156
+ return errorMap;
157
+ }
158
+ function main() {
159
+ console.log('🔍 Scanning ABIs for error definitions...');
160
+ const errorMap = generateErrorMap();
161
+ const outputPath = path_1.default.join(process.cwd(), 'generated', 'errorMap.json');
162
+ // Ensure generated directory exists
163
+ const generatedDir = path_1.default.dirname(outputPath);
164
+ if (!fs_1.default.existsSync(generatedDir)) {
165
+ fs_1.default.mkdirSync(generatedDir, { recursive: true });
166
+ }
167
+ // Write the error map
168
+ fs_1.default.writeFileSync(outputPath, JSON.stringify(errorMap, null, 2));
169
+ console.log(`✅ Generated error map with ${Object.keys(errorMap).length} errors`);
170
+ console.log(`📁 Output: ${outputPath}`);
171
+ // Show some examples
172
+ const examples = Object.entries(errorMap).slice(0, 5);
173
+ console.log('\n📋 Examples:');
174
+ for (const [selector, info] of examples) {
175
+ console.log(` ${selector} → ${info.name}: ${info.message}`);
176
+ }
177
+ }
178
+ if (require.main === module) {
179
+ main();
180
+ }
@@ -33,9 +33,13 @@ async function createWallet(mnemonic, provider) {
33
33
  }
34
34
  }
35
35
  async function setup() {
36
+ const rpc = new RPCLibrary_1.default();
37
+ const error = rpc.errors.getErrorInfo("0xa9b65aba");
38
+ console.log(error);
39
+ return;
36
40
  const provider = await getProvider();
37
41
  const signer = await createWallet(TEST_MNEMONIC, provider);
38
- const rpc = new RPCLibrary_1.default();
42
+ // const rpc = new RPCLibrary();
39
43
  rpc.address = signer.address;
40
44
  rpc.provider = provider;
41
45
  rpc.chainId = chainId;
@@ -95,6 +95,7 @@ import vaultOpened from "./pools/lendingPool/vaultOpened";
95
95
  import { getWithdrawRequestInfo as getStabilityPoolWithdrawRequestInfo } from "./pools/stabilityPool/getWithdrawRequestInfo";
96
96
  import { requestWithdraw as requestStabilityPoolWithdraw } from "./pools/stabilityPool/requestWithdraw";
97
97
  import { getABI } from "./utils/artifacts";
98
+ import { decodeErrorName, getErrorInfo, attachDecodedError, decodeErrorMessage, attachDecodedErrorMessage } from "./utils/errorDecoder";
98
99
  declare class RPCLibrary {
99
100
  signer: Signer | null;
100
101
  isConnected: boolean;
@@ -236,6 +237,13 @@ declare class RPCLibrary {
236
237
  checkAllowance: typeof checkAllowance;
237
238
  getChainsConfig: typeof getChainsConfig;
238
239
  setChainsConfig: typeof setChainConfig;
240
+ errors: {
241
+ decodeErrorName: typeof decodeErrorName;
242
+ getErrorInfo: typeof getErrorInfo;
243
+ attachDecodedError: typeof attachDecodedError;
244
+ decodeErrorMessage: typeof decodeErrorMessage;
245
+ attachDecodedErrorMessage: typeof attachDecodedErrorMessage;
246
+ };
239
247
  constructor(privateKey?: string);
240
248
  getWallet(privateKey: string, provider: Provider): Promise<ethers.JsonRpcSigner | ethers.Wallet>;
241
249
  getProvider(chainId: ChainId, providerRpc?: string): Promise<ethers.BrowserProvider | ethers.JsonRpcProvider>;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,37 @@
1
+ interface GeneratedErrorInfo {
2
+ name: string;
3
+ signature: string;
4
+ message: string;
5
+ contract?: string;
6
+ inputs?: Array<{
7
+ name: string;
8
+ type: string;
9
+ internalType: string;
10
+ }>;
11
+ }
12
+ /**
13
+ * Returns detailed information about the error encoded in the given data blob.
14
+ * @param data The revert data returned by the EVM (e.g., error.data).
15
+ */
16
+ export declare function getErrorInfo(data?: string): GeneratedErrorInfo | null;
17
+ /**
18
+ * Returns only the error name, or null if it cannot be decoded.
19
+ */
20
+ export declare function decodeErrorName(data?: string): string | null;
21
+ /**
22
+ * Returns the custom error message, or null if it cannot be decoded.
23
+ */
24
+ export declare function decodeErrorMessage(data?: string): string | null;
25
+ /**
26
+ * Builds a user-friendly error prefix with decoded error name (if available).
27
+ * Usage example:
28
+ * const msg = attachDecodedError("Deposit failed", caughtError);
29
+ */
30
+ export declare function attachDecodedError(prefix: string, error: any): string;
31
+ /**
32
+ * Builds a user-friendly error message with custom error description (if available).
33
+ * Usage example:
34
+ * const msg = attachDecodedErrorMessage("Deposit failed", caughtError);
35
+ */
36
+ export declare function attachDecodedErrorMessage(prefix: string, error: any): string;
37
+ export {};
@@ -0,0 +1,137 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getErrorInfo = getErrorInfo;
4
+ exports.decodeErrorName = decodeErrorName;
5
+ exports.decodeErrorMessage = decodeErrorMessage;
6
+ exports.attachDecodedError = attachDecodedError;
7
+ exports.attachDecodedErrorMessage = attachDecodedErrorMessage;
8
+ const ethers_1 = require("ethers");
9
+ const artifacts_1 = require("./artifacts");
10
+ // Try to load the generated error map, fallback to runtime scanning
11
+ let ERROR_SIGNATURE_MAP = {};
12
+ try {
13
+ const generatedMap = require('../generated/errorMap.json');
14
+ ERROR_SIGNATURE_MAP = generatedMap;
15
+ }
16
+ catch (error) {
17
+ console.warn('Generated error map not found, falling back to runtime scanning...');
18
+ // Fallback to runtime scanning (original implementation)
19
+ (function buildErrorSignatureMap() {
20
+ for (const abiKey in artifacts_1.ABIS) {
21
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
22
+ // @ts-ignore – ABIS may contain unknown items
23
+ const abi = artifacts_1.ABIS[abiKey];
24
+ if (!Array.isArray(abi))
25
+ continue;
26
+ for (const item of abi) {
27
+ if (item.type !== "error")
28
+ continue;
29
+ const inputs = (item.inputs || []);
30
+ const signature = `${item.name}(${inputs.map((input) => input.type).join(",")})`;
31
+ const selector = (0, ethers_1.id)(signature).slice(0, 10).toLowerCase();
32
+ if (!ERROR_SIGNATURE_MAP[selector]) {
33
+ ERROR_SIGNATURE_MAP[selector] = {
34
+ name: item.name,
35
+ signature,
36
+ message: `${item.name.replace(/([A-Z])/g, ' $1').trim()} error occurred`,
37
+ contract: abiKey,
38
+ inputs: inputs.map((input) => ({
39
+ name: input.name || '',
40
+ type: input.type,
41
+ internalType: input.internalType || input.type,
42
+ })),
43
+ };
44
+ }
45
+ }
46
+ }
47
+ })();
48
+ }
49
+ /**
50
+ * Returns detailed information about the error encoded in the given data blob.
51
+ * @param data The revert data returned by the EVM (e.g., error.data).
52
+ */
53
+ function getErrorInfo(data) {
54
+ if (!data || typeof data !== "string")
55
+ return null;
56
+ let selector;
57
+ if (data.startsWith("0x")) {
58
+ selector = data.slice(0, 10).toLowerCase();
59
+ }
60
+ else {
61
+ // look for first 4-byte hex inside the string (e.g., error message)
62
+ const match = data.match(/0x[0-9a-fA-F]{8}/);
63
+ if (match)
64
+ selector = match[0].toLowerCase();
65
+ }
66
+ if (!selector)
67
+ return null;
68
+ return ERROR_SIGNATURE_MAP[selector] ?? null;
69
+ }
70
+ /**
71
+ * Returns only the error name, or null if it cannot be decoded.
72
+ */
73
+ function decodeErrorName(data) {
74
+ const info = getErrorInfo(data);
75
+ return info?.name ?? null;
76
+ }
77
+ /**
78
+ * Returns the custom error message, or null if it cannot be decoded.
79
+ */
80
+ function decodeErrorMessage(data) {
81
+ const info = getErrorInfo(data);
82
+ return info?.message ?? null;
83
+ }
84
+ /**
85
+ * Builds a user-friendly error prefix with decoded error name (if available).
86
+ * Usage example:
87
+ * const msg = attachDecodedError("Deposit failed", caughtError);
88
+ */
89
+ function attachDecodedError(prefix, error) {
90
+ // ethers@6 nests the original error under error.error
91
+ // viem/ethcall surface it directly on error.data
92
+ const data = (error && ((error.data ?? error.error?.data) ||
93
+ // viem error format: error.shortMessage
94
+ undefined));
95
+ // If data not found, attempt to extract selector from message like
96
+ // "Encoded error signature \"0xa9b65aba\" not found on ABI"
97
+ let selector;
98
+ if (data && data.startsWith("0x")) {
99
+ selector = data.slice(0, 10).toLowerCase();
100
+ }
101
+ else if (typeof error?.message === "string") {
102
+ const SIGNATURE_REGEX = /Encoded error signature \"(0x[0-9a-fA-F]{8})\"/;
103
+ const match = error.message.match(SIGNATURE_REGEX);
104
+ if (match)
105
+ selector = match[1].toLowerCase();
106
+ }
107
+ if (!selector)
108
+ return prefix; // unable to determine
109
+ const decodedName = ERROR_SIGNATURE_MAP[selector]?.name ?? null;
110
+ // console.debug("Decoded", selector, decodedName);
111
+ return decodedName ? `${prefix} (${decodedName})` : prefix;
112
+ }
113
+ /**
114
+ * Builds a user-friendly error message with custom error description (if available).
115
+ * Usage example:
116
+ * const msg = attachDecodedErrorMessage("Deposit failed", caughtError);
117
+ */
118
+ function attachDecodedErrorMessage(prefix, error) {
119
+ const data = (error && ((error.data ?? error.error?.data) ||
120
+ undefined));
121
+ let selector;
122
+ if (data && data.startsWith("0x")) {
123
+ selector = data.slice(0, 10).toLowerCase();
124
+ }
125
+ else if (typeof error?.message === "string") {
126
+ const SIGNATURE_REGEX = /Encoded error signature \"(0x[0-9a-fA-F]{8})\"/;
127
+ const match = error.message.match(SIGNATURE_REGEX);
128
+ if (match)
129
+ selector = match[1].toLowerCase();
130
+ }
131
+ if (!selector)
132
+ return prefix;
133
+ const errorInfo = ERROR_SIGNATURE_MAP[selector];
134
+ if (!errorInfo)
135
+ return prefix;
136
+ return `${prefix}: ${errorInfo.message}`;
137
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@raac/rpc",
3
- "version": "1.1.0-beta.53",
3
+ "version": "1.1.0-beta.55",
4
4
  "description": "RPC Library for RAAC ",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/types/index.d.ts",
@@ -9,12 +9,13 @@
9
9
  ],
10
10
  "scripts": {
11
11
  "dev": "ts-node-dev --transpile-only --env-file=.env scripts/index.ts",
12
- "build": "tsc && cp -r typechain-types dist/types",
12
+ "build": "npm run generate:errors && tsc && cp -r typechain-types dist/types",
13
13
  "build:watch": "tsc --watch",
14
14
  "test": "echo \"Error: no test specified\" && exit 1",
15
15
  "sync": "bash setup.sh",
16
16
  "clean": "rm -rf dist",
17
- "prebuild": "npm run clean"
17
+ "prebuild": "npm run clean",
18
+ "generate:errors": "ts-node scripts/generateErrorMap.ts"
18
19
  },
19
20
  "keywords": [
20
21
  "RAAC",