@aboutcircles/sdk-utils 0.1.0

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.
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Conversions between demurrage and inflationary units in V2 – bit‑identical with the
3
+ * Solidity reference implementation (ABDK Math 64.64).
4
+ *
5
+ * All fixed‑point math uses ABDK’s 64.64 format encoded in `bigint`.
6
+ */
7
+ export declare class CirclesConverter {
8
+ /** 1.0 in 64.64 representation */
9
+ static readonly ONE_64: bigint;
10
+ /** GAMMA = (0.93)^(1/365.25) * 2^64 */
11
+ static readonly GAMMA_64: bigint;
12
+ /** BETA = 1 / GAMMA * 2^64 */
13
+ static readonly BETA_64: bigint;
14
+ static readonly SECONDS_PER_DAY: bigint;
15
+ static readonly INFLATION_DAY_ZERO_UNIX: bigint;
16
+ static readonly ATTO_FACTOR: bigint;
17
+ static readonly FACTOR_1E12: bigint;
18
+ static readonly V1_ACCURACY: bigint;
19
+ static readonly V1_INFLATION_PCT_NUM: bigint;
20
+ static readonly V1_INFLATION_PCT_DEN: bigint;
21
+ static readonly PERIOD_SEC: bigint;
22
+ /**
23
+ * Multiplies two 64.64 factors and returns a 64.64 factor.
24
+ * (a * b) >> 64
25
+ */
26
+ private static mul64;
27
+ /**
28
+ * Multiplies a 64.64 factor with an unsigned integer (bigint) and truncates.
29
+ * Equivalent to ABDKMath64x64.mulu.
30
+ */
31
+ private static mulU;
32
+ /**
33
+ * Exponentiation by squaring for 64.64 factors.
34
+ * Equivalent to ABDKMath64x64.pow(base, exp).
35
+ */
36
+ private static pow64;
37
+ /** 1.0 in 1 × 10³⁶ representation. */
38
+ private static readonly ONE_36;
39
+ /** 0.93^(1 / 365.25) scaled to 1e36 (rounded half-up). */
40
+ private static readonly GAMMA_36;
41
+ /** 1 / GAMMA scaled to 1e36 (rounded half-up). */
42
+ private static readonly BETA_36;
43
+ /** (a · b) / 1e36 – stays inside the 1e36 domain. */
44
+ private static mul36;
45
+ /** Exponentiation for 1e36-scaled factors. */
46
+ private static pow36;
47
+ /** Atto‑circles (1e18) → UI circles as JS number (lossless until 2^53‑1). */
48
+ static attoCirclesToCircles(atto: bigint): number;
49
+ /** UI circles → atto‑circles (truncates to match Solidity’s rounding). */
50
+ static circlesToAttoCircles(circles: number): bigint;
51
+ /** Inflationary → demurraged for explicit day index. */
52
+ static inflationaryToDemurrage(inflationary: bigint, day: bigint): bigint;
53
+ /** Demurraged → inflationary for explicit day index. */
54
+ static demurrageToInflationary(demurraged: bigint, day: bigint): bigint;
55
+ /** UNIX timestamp (seconds) → Circles day index. */
56
+ static dayFromTimestamp(unixSeconds: bigint): bigint;
57
+ /** Demurraged → static circles for “today”. */
58
+ static attoCirclesToAttoStaticCircles(demurraged: bigint, nowUnixSeconds?: bigint): bigint;
59
+ /** Static circles → demurraged circles for “today”. */
60
+ static attoStaticCirclesToAttoCircles(staticCircles: bigint, nowUnixSeconds?: bigint): bigint;
61
+ /** Inflationary → demurraged (exact, reversible). */
62
+ static inflationaryToDemurrageExact(inflationary: bigint, day: bigint): bigint;
63
+ /** Demurraged → inflationary (inverse of the above, exact). */
64
+ static demurrageToInflationaryExact(demurraged: bigint, day: bigint): bigint;
65
+ /** Demurraged atto-circles → static atto-circles “today” (loss-less). */
66
+ static attoCirclesToAttoStaticCirclesExact(demurraged: bigint, nowUnixSeconds?: bigint): bigint;
67
+ /** Static atto-circles → demurraged atto-circles “today” (loss-less). */
68
+ static attoStaticCirclesToAttoCirclesExact(staticCircles: bigint, nowUnixSeconds?: bigint): bigint;
69
+ static truncateToInt64(wei: bigint): bigint;
70
+ static blowUpToBigInt(sixDecimals: bigint): bigint;
71
+ static truncateToSixDecimals(wei: bigint): bigint;
72
+ private static v1InflateFactor;
73
+ /** CRC → demurraged Circles for a given timestamp. */
74
+ static attoCrcToAttoCircles(v1Amount: bigint, blockTimestampUtc: bigint): bigint;
75
+ /** Demurraged Circles → CRC (inverse of the above). */
76
+ static attoCirclesToAttoCrc(demurraged: bigint, blockTimestampUtc: bigint): bigint;
77
+ /** Implements the on‑chain formula used during migration. */
78
+ private static v1ToDemurrage;
79
+ }
80
+ //# sourceMappingURL=circlesConverter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"circlesConverter.d.ts","sourceRoot":"","sources":["../src/circlesConverter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Conversions between demurrage and inflationary units in V2 – bit‑identical with the
3
+ * Solidity reference implementation (ABDK Math 64.64).
4
+ *
5
+ * All fixed‑point math uses ABDK’s 64.64 format encoded in `bigint`.
6
+ */
7
+ export class CirclesConverter {
8
+ // ───────────────────────────────── constants ─────────────────────────────────
9
+ /** 1.0 in 64.64 representation */
10
+ static ONE_64 = 1n << 64n;
11
+ /** GAMMA = (0.93)^(1/365.25) * 2^64 */
12
+ static GAMMA_64 = 18443079296116538654n;
13
+ /** BETA = 1 / GAMMA * 2^64 */
14
+ static BETA_64 = 18450409579521241655n;
15
+ static SECONDS_PER_DAY = 86400n;
16
+ static INFLATION_DAY_ZERO_UNIX = 1602720000n; // 2020‑10‑15 00:00:00 UTC
17
+ static ATTO_FACTOR = 1000000000000000000n; // 1e18
18
+ static FACTOR_1E12 = 1000000000000n; // 1e12
19
+ static V1_ACCURACY = 100000000n; // 1e8
20
+ static V1_INFLATION_PCT_NUM = 107n;
21
+ static V1_INFLATION_PCT_DEN = 100n;
22
+ static PERIOD_SEC = 31556952n; // 365.25 days
23
+ // ───────────────────────────── fixed‑point helpers ───────────────────────────
24
+ /**
25
+ * Multiplies two 64.64 factors and returns a 64.64 factor.
26
+ * (a * b) >> 64
27
+ */
28
+ static mul64(a, b) {
29
+ return (a * b) >> 64n;
30
+ }
31
+ /**
32
+ * Multiplies a 64.64 factor with an unsigned integer (bigint) and truncates.
33
+ * Equivalent to ABDKMath64x64.mulu.
34
+ */
35
+ static mulU(factor64x64, value) {
36
+ return (factor64x64 * value) >> 64n;
37
+ }
38
+ /**
39
+ * Exponentiation by squaring for 64.64 factors.
40
+ * Equivalent to ABDKMath64x64.pow(base, exp).
41
+ */
42
+ static pow64(base64x64, exp) {
43
+ let base = base64x64;
44
+ let exponent = exp;
45
+ let result = this.ONE_64;
46
+ while (exponent > 0n) {
47
+ if ((exponent & 1n) === 1n) {
48
+ result = this.mul64(result, base);
49
+ }
50
+ base = this.mul64(base, base);
51
+ exponent >>= 1n;
52
+ }
53
+ return result;
54
+ }
55
+ /** 1.0 in 1 × 10³⁶ representation. */
56
+ static ONE_36 = 1000000000000000000000000000000000000000n; // 1e36
57
+ /** 0.93^(1 / 365.25) scaled to 1e36 (rounded half-up). */
58
+ static GAMMA_36 = 999801332008598957430613406568191166n;
59
+ /** 1 / GAMMA scaled to 1e36 (rounded half-up). */
60
+ static BETA_36 = 1000198707468214629156271489013303962n;
61
+ /** (a · b) / 1e36 – stays inside the 1e36 domain. */
62
+ static mul36(a, b) {
63
+ return (a * b) / this.ONE_36;
64
+ }
65
+ /** Exponentiation for 1e36-scaled factors. */
66
+ static pow36(base36, exp) {
67
+ let result = this.ONE_36;
68
+ let base = base36;
69
+ let e = exp;
70
+ while (e > 0n) {
71
+ const isOdd = (e & 1n) === 1n;
72
+ if (isOdd) {
73
+ result = this.mul36(result, base);
74
+ }
75
+ base = this.mul36(base, base);
76
+ e >>= 1n;
77
+ }
78
+ return result;
79
+ }
80
+ // ───────────────────────────── API: human units ─────────────────────────────
81
+ /** Atto‑circles (1e18) → UI circles as JS number (lossless until 2^53‑1). */
82
+ static attoCirclesToCircles(atto) {
83
+ if (atto === 0n)
84
+ return 0;
85
+ // Split integer and fractional parts before casting to preserve precision.
86
+ const whole = atto / this.ATTO_FACTOR;
87
+ const frac = atto % this.ATTO_FACTOR;
88
+ const MAX_SAFE_INT = BigInt(Number.MAX_SAFE_INTEGER);
89
+ if (whole > MAX_SAFE_INT || whole < -MAX_SAFE_INT) {
90
+ throw new RangeError('Atto value’s integer component exceeds JS double precision.');
91
+ }
92
+ return Number(whole) + Number(frac) / Number(this.ATTO_FACTOR);
93
+ }
94
+ /** UI circles → atto‑circles (truncates to match Solidity’s rounding). */
95
+ static circlesToAttoCircles(circles) {
96
+ return BigInt(Math.trunc(circles * Number(this.ATTO_FACTOR)));
97
+ }
98
+ // ───────────────────────────── API: demurrage math ──────────────────────────
99
+ /** Inflationary → demurraged for explicit day index. */
100
+ static inflationaryToDemurrage(inflationary, day) {
101
+ return this.mulU(this.pow64(this.GAMMA_64, day), inflationary);
102
+ }
103
+ /** Demurraged → inflationary for explicit day index. */
104
+ static demurrageToInflationary(demurraged, day) {
105
+ return this.mulU(this.pow64(this.BETA_64, day), demurraged);
106
+ }
107
+ /** UNIX timestamp (seconds) → Circles day index. */
108
+ static dayFromTimestamp(unixSeconds) {
109
+ return (unixSeconds - this.INFLATION_DAY_ZERO_UNIX) / this.SECONDS_PER_DAY;
110
+ }
111
+ /** Demurraged → static circles for “today”. */
112
+ static attoCirclesToAttoStaticCircles(demurraged, nowUnixSeconds = BigInt(Math.floor(Date.now() / 1000))) {
113
+ return this.demurrageToInflationary(demurraged, this.dayFromTimestamp(nowUnixSeconds));
114
+ }
115
+ /** Static circles → demurraged circles for “today”. */
116
+ static attoStaticCirclesToAttoCircles(staticCircles, nowUnixSeconds = BigInt(Math.floor(Date.now() / 1000))) {
117
+ return this.inflationaryToDemurrage(staticCircles, this.dayFromTimestamp(nowUnixSeconds));
118
+ }
119
+ /** Inflationary → demurraged (exact, reversible). */
120
+ static inflationaryToDemurrageExact(inflationary, day) {
121
+ const factor = this.pow36(this.GAMMA_36, day);
122
+ return (inflationary * factor) / this.ONE_36;
123
+ }
124
+ /** Demurraged → inflationary (inverse of the above, exact). */
125
+ static demurrageToInflationaryExact(demurraged, day) {
126
+ const factor = this.pow36(this.BETA_36, day);
127
+ return (demurraged * factor) / this.ONE_36;
128
+ }
129
+ /** Demurraged atto-circles → static atto-circles “today” (loss-less). */
130
+ static attoCirclesToAttoStaticCirclesExact(demurraged, nowUnixSeconds = BigInt(Math.floor(Date.now() / 1000))) {
131
+ const day = this.dayFromTimestamp(nowUnixSeconds);
132
+ return this.demurrageToInflationaryExact(demurraged, day);
133
+ }
134
+ /** Static atto-circles → demurraged atto-circles “today” (loss-less). */
135
+ static attoStaticCirclesToAttoCirclesExact(staticCircles, nowUnixSeconds = BigInt(Math.floor(Date.now() / 1000))) {
136
+ const day = this.dayFromTimestamp(nowUnixSeconds);
137
+ return this.inflationaryToDemurrageExact(staticCircles, day);
138
+ }
139
+ // ───────────────────── utilities for 6‑decimal truncation ───────────────────
140
+ static truncateToInt64(wei) {
141
+ const truncated = wei / this.FACTOR_1E12;
142
+ const MAX_INT64 = 9223372036854775807n;
143
+ return truncated > MAX_INT64 ? MAX_INT64 : truncated;
144
+ }
145
+ static blowUpToBigInt(sixDecimals) {
146
+ return sixDecimals * this.FACTOR_1E12;
147
+ }
148
+ static truncateToSixDecimals(wei) {
149
+ return this.blowUpToBigInt(this.truncateToInt64(wei));
150
+ }
151
+ // ───────────────────────── v1 → v2 migration helpers ─────────────────────────
152
+ static v1InflateFactor(periodIdx) {
153
+ if (periodIdx === 0n)
154
+ return this.V1_ACCURACY;
155
+ return (this.V1_ACCURACY * this.V1_INFLATION_PCT_NUM ** periodIdx) / (this.V1_INFLATION_PCT_DEN ** periodIdx);
156
+ }
157
+ /** CRC → demurraged Circles for a given timestamp. */
158
+ static attoCrcToAttoCircles(v1Amount, blockTimestampUtc) {
159
+ const secondsSinceEpoch = blockTimestampUtc - this.INFLATION_DAY_ZERO_UNIX;
160
+ const periodIdx = secondsSinceEpoch / this.PERIOD_SEC;
161
+ const secondsIntoPeriod = secondsSinceEpoch % this.PERIOD_SEC;
162
+ const factorCur = this.v1InflateFactor(periodIdx);
163
+ const factorNext = this.v1InflateFactor(periodIdx + 1n);
164
+ return this.v1ToDemurrage(v1Amount, factorCur, factorNext, secondsIntoPeriod, this.PERIOD_SEC);
165
+ }
166
+ /** Demurraged Circles → CRC (inverse of the above). */
167
+ static attoCirclesToAttoCrc(demurraged, blockTimestampUtc) {
168
+ const secondsSinceEpoch = blockTimestampUtc - this.INFLATION_DAY_ZERO_UNIX;
169
+ const periodIdx = secondsSinceEpoch / this.PERIOD_SEC;
170
+ const secondsIntoPeriod = secondsSinceEpoch % this.PERIOD_SEC;
171
+ const factorCur = this.v1InflateFactor(periodIdx);
172
+ const factorNext = this.v1InflateFactor(periodIdx + 1n);
173
+ const rP = factorCur * (this.PERIOD_SEC - secondsIntoPeriod) + factorNext * secondsIntoPeriod;
174
+ return (demurraged * 3n * this.V1_ACCURACY * this.PERIOD_SEC) / rP;
175
+ }
176
+ /** Implements the on‑chain formula used during migration. */
177
+ static v1ToDemurrage(v1Amount, factorCur, factorNext, secondsInto, periodSec) {
178
+ const rP = factorCur * (periodSec - secondsInto) + factorNext * secondsInto;
179
+ return (v1Amount * 3n * this.V1_ACCURACY * periodSec) / rP;
180
+ }
181
+ }
@@ -0,0 +1,9 @@
1
+ import type { Address } from '@aboutcircles/sdk-types';
2
+ /**
3
+ * Common constants used across the SDK
4
+ */
5
+ /**
6
+ * The zero address (0x0000000000000000000000000000000000000000)
7
+ */
8
+ export declare const ZERO_ADDRESS: Address;
9
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAEvD;;GAEG;AAEH;;GAEG;AACH,eAAO,MAAM,YAAY,EAAE,OAAsD,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Common constants used across the SDK
3
+ */
4
+ /**
5
+ * The zero address (0x0000000000000000000000000000000000000000)
6
+ */
7
+ export const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Contract Error Parsing
3
+ * Utilities for parsing and decoding contract errors from transaction failures
4
+ */
5
+ import type { Abi } from 'abitype';
6
+ import type { DecodedContractError } from '@aboutcircles/sdk-types';
7
+ import { CirclesError } from './errors';
8
+ /**
9
+ * Parse contract error from a transaction error
10
+ *
11
+ * @param error - The error object from a failed transaction
12
+ * @param abi - The contract ABI to use for decoding
13
+ * @returns Decoded error information or null if cannot be parsed
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * try {
18
+ * await contract.someFunction();
19
+ * } catch (error) {
20
+ * const decoded = parseContractError(error, hubV2Abi);
21
+ * if (decoded) {
22
+ * console.log(`Contract error: ${decoded.formattedMessage}`);
23
+ * }
24
+ * }
25
+ * ```
26
+ */
27
+ export declare function parseContractError(error: any, abi: Abi): DecodedContractError | null;
28
+ /**
29
+ * Contract error class for Circles SDK
30
+ * Extends CirclesError with contract-specific error information
31
+ */
32
+ export declare class ContractError extends CirclesError<'CORE'> {
33
+ readonly decodedError?: DecodedContractError;
34
+ constructor(message: string, options?: {
35
+ code?: string | number;
36
+ cause?: unknown;
37
+ context?: Record<string, any>;
38
+ decodedError?: DecodedContractError;
39
+ });
40
+ /**
41
+ * Create a ContractError from a transaction error
42
+ */
43
+ static fromTransactionError(error: any, abi: Abi, context?: Record<string, any>): ContractError;
44
+ /**
45
+ * Get formatted error message with details
46
+ */
47
+ toString(): string;
48
+ }
49
+ //# sourceMappingURL=contractErrors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contractErrors.d.ts","sourceRoot":"","sources":["../src/contractErrors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAEpE,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AA6HxC;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,GAAG,EACV,GAAG,EAAE,GAAG,GACP,oBAAoB,GAAG,IAAI,CA4B7B;AAED;;;GAGG;AACH,qBAAa,aAAc,SAAQ,YAAY,CAAC,MAAM,CAAC;IACrD,SAAgB,YAAY,CAAC,EAAE,oBAAoB,CAAC;gBAGlD,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;QACR,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;QACvB,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC9B,YAAY,CAAC,EAAE,oBAAoB,CAAC;KACrC;IAMH;;OAEG;IACH,MAAM,CAAC,oBAAoB,CACzB,KAAK,EAAE,GAAG,EACV,GAAG,EAAE,GAAG,EACR,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC5B,aAAa;IA8BhB;;OAEG;IACH,QAAQ,IAAI,MAAM;CAcnB"}
@@ -0,0 +1,198 @@
1
+ /**
2
+ * Contract Error Parsing
3
+ * Utilities for parsing and decoding contract errors from transaction failures
4
+ */
5
+ import { decodeErrorResult } from './abi';
6
+ import { CirclesError } from './errors';
7
+ /**
8
+ * Contract error codes mapping
9
+ * Maps error codes to human-readable messages
10
+ */
11
+ const ERROR_CODE_MESSAGES = {
12
+ // CirclesErrorNoArgs codes
13
+ 0x00: 'Avatar already registered',
14
+ 0x01: 'Avatar must be registered before inviting',
15
+ 0x02: 'Invalid invitation',
16
+ 0x03: 'Avatar must be registered',
17
+ 0x04: 'Only self can register',
18
+ 0x05: 'Maximum value reached',
19
+ 0x06: 'Inflationary supply already set',
20
+ 0x07: 'Group has no collateral',
21
+ 0xa1: 'Trust to zero address is not allowed', // This is the error from the example
22
+ // CirclesInvalidParameter codes
23
+ 0x10: 'Invalid parameter',
24
+ // @todo Add more
25
+ };
26
+ /**
27
+ * Extract error data from various error types
28
+ * Handles viem errors, raw errors, and other formats
29
+ */
30
+ function extractErrorData(error) {
31
+ // Check for viem-style error with data in details
32
+ if (error?.details && typeof error.details === 'string') {
33
+ const match = error.details.match(/err:\s*(0x[0-9a-fA-F]+)/);
34
+ if (match) {
35
+ return match[1];
36
+ }
37
+ }
38
+ // Check for raw data property
39
+ if (error?.data && typeof error.data === 'string' && error.data.startsWith('0x')) {
40
+ return error.data;
41
+ }
42
+ // Check for revert data in cause chain
43
+ let currentError = error;
44
+ while (currentError) {
45
+ if (currentError.data && typeof currentError.data === 'string') {
46
+ return currentError.data;
47
+ }
48
+ currentError = currentError.cause;
49
+ }
50
+ return null;
51
+ }
52
+ /**
53
+ * Format error message with decoded arguments using ABI information
54
+ */
55
+ function formatErrorMessage(errorName, args, errorAbi) {
56
+ if (!args || args.length === 0) {
57
+ return errorName;
58
+ }
59
+ if (!errorAbi || !errorAbi.inputs) {
60
+ // Fallback if no ABI info
61
+ const formattedArgs = args.map(arg => {
62
+ if (typeof arg === 'bigint') {
63
+ return arg.toString();
64
+ }
65
+ return String(arg);
66
+ }).join(', ');
67
+ return `${errorName}(${formattedArgs})`;
68
+ }
69
+ // Find the "code" parameter if it exists
70
+ const codeIndex = errorAbi.inputs.findIndex((input) => input.name === 'code' || input.name === '' && input.type === 'uint8');
71
+ let baseMessage = errorName;
72
+ let detailsParts = [];
73
+ // If there's a code parameter, try to get human-readable message
74
+ if (codeIndex !== -1) {
75
+ const code = Number(args[codeIndex]);
76
+ const humanMessage = ERROR_CODE_MESSAGES[code];
77
+ if (humanMessage) {
78
+ baseMessage = humanMessage;
79
+ }
80
+ }
81
+ // Format all arguments with their names
82
+ errorAbi.inputs.forEach((input, index) => {
83
+ const arg = args[index];
84
+ const name = input.name || `arg${index}`;
85
+ // Skip empty-named code parameters as they're used for the base message
86
+ if (!input.name && input.type === 'uint8') {
87
+ return;
88
+ }
89
+ let formattedValue;
90
+ if (typeof arg === 'bigint') {
91
+ formattedValue = arg.toString();
92
+ }
93
+ else if (typeof arg === 'string' && arg.startsWith('0x')) {
94
+ formattedValue = arg; // Keep addresses and hex as-is
95
+ }
96
+ else {
97
+ formattedValue = String(arg);
98
+ }
99
+ // Add hex representation for numeric codes
100
+ if (name === 'code' || input.type === 'uint8') {
101
+ formattedValue = `0x${Number(arg).toString(16)}`;
102
+ }
103
+ detailsParts.push(`${name}: ${formattedValue}`);
104
+ });
105
+ // Combine base message with details
106
+ if (detailsParts.length > 0) {
107
+ return `${baseMessage} (${detailsParts.join(', ')})`;
108
+ }
109
+ return baseMessage;
110
+ }
111
+ /**
112
+ * Parse contract error from a transaction error
113
+ *
114
+ * @param error - The error object from a failed transaction
115
+ * @param abi - The contract ABI to use for decoding
116
+ * @returns Decoded error information or null if cannot be parsed
117
+ *
118
+ * @example
119
+ * ```typescript
120
+ * try {
121
+ * await contract.someFunction();
122
+ * } catch (error) {
123
+ * const decoded = parseContractError(error, hubV2Abi);
124
+ * if (decoded) {
125
+ * console.log(`Contract error: ${decoded.formattedMessage}`);
126
+ * }
127
+ * }
128
+ * ```
129
+ */
130
+ export function parseContractError(error, abi) {
131
+ const errorData = extractErrorData(error);
132
+ if (!errorData) {
133
+ return null;
134
+ }
135
+ const decoded = decodeErrorResult({ abi, data: errorData });
136
+ if (!decoded) {
137
+ return null;
138
+ }
139
+ // Find the error ABI definition
140
+ const errorAbi = abi.find(item => item.type === 'error' && item.name === decoded.errorName);
141
+ const selector = errorData.slice(0, 10);
142
+ const formattedMessage = formatErrorMessage(decoded.errorName, decoded.args, errorAbi);
143
+ return {
144
+ errorName: decoded.errorName,
145
+ args: decoded.args,
146
+ selector,
147
+ rawData: errorData,
148
+ formattedMessage,
149
+ };
150
+ }
151
+ /**
152
+ * Contract error class for Circles SDK
153
+ * Extends CirclesError with contract-specific error information
154
+ */
155
+ export class ContractError extends CirclesError {
156
+ decodedError;
157
+ constructor(message, options) {
158
+ super('ContractError', message, { ...options, source: 'CORE' });
159
+ this.decodedError = options?.decodedError;
160
+ }
161
+ /**
162
+ * Create a ContractError from a transaction error
163
+ */
164
+ static fromTransactionError(error, abi, context) {
165
+ const decoded = parseContractError(error, abi);
166
+ if (decoded) {
167
+ return new ContractError(`Transaction failed: ${decoded.formattedMessage}`, {
168
+ code: decoded.selector,
169
+ cause: error,
170
+ context: {
171
+ ...context,
172
+ errorName: decoded.errorName,
173
+ errorArgs: decoded.args,
174
+ },
175
+ decodedError: decoded,
176
+ });
177
+ }
178
+ // Fallback if we can't decode the error
179
+ const message = error?.message || String(error);
180
+ return new ContractError(`Transaction failed: ${message}`, {
181
+ cause: error,
182
+ context,
183
+ });
184
+ }
185
+ /**
186
+ * Get formatted error message with details
187
+ */
188
+ toString() {
189
+ let result = super.toString();
190
+ if (this.decodedError) {
191
+ result += `\nDecoded Error: ${this.decodedError.formattedMessage}`;
192
+ if (this.decodedError.args && this.decodedError.args.length > 0) {
193
+ result += `\nArguments: ${JSON.stringify(this.decodedError.args, (_, v) => typeof v === 'bigint' ? v.toString() : v)}`;
194
+ }
195
+ }
196
+ return result;
197
+ }
198
+ }
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Circles SDK Error Handling
3
+ * Provides a structured error system for consistent error handling across the SDK
4
+ */
5
+ /**
6
+ * Base error source type
7
+ * Each package exports its own specific ErrorSource
8
+ */
9
+ export type BaseErrorSource = 'UTILS' | 'RPC' | 'SDK' | 'CORE' | 'RUNNER' | 'PROFILES' | 'TRANSFERS' | 'EVENTS' | 'PATHFINDER' | 'UNKNOWN';
10
+ /**
11
+ * Utils package error source
12
+ */
13
+ export type UtilsErrorSource = 'UTILS' | 'VALIDATION' | 'CONVERSION' | 'ENCODING';
14
+ /**
15
+ * Base Circles SDK Error
16
+ * All SDK errors extend from this class
17
+ */
18
+ export declare class CirclesError<TSource extends string = string> extends Error {
19
+ readonly name: string;
20
+ readonly code?: string | number;
21
+ readonly source: TSource;
22
+ readonly cause?: unknown;
23
+ readonly context?: Record<string, any>;
24
+ constructor(name: string, message: string, options?: {
25
+ code?: string | number;
26
+ source?: TSource;
27
+ cause?: unknown;
28
+ context?: Record<string, any>;
29
+ });
30
+ /**
31
+ * Convert error to JSON for logging/debugging
32
+ */
33
+ toJSON(): {
34
+ name: string;
35
+ message: string;
36
+ code: string | number | undefined;
37
+ source: TSource;
38
+ context: Record<string, any> | undefined;
39
+ cause: unknown;
40
+ stack: string | undefined;
41
+ };
42
+ /**
43
+ * Get formatted error message with context
44
+ */
45
+ toString(): string;
46
+ }
47
+ /**
48
+ * Validation errors
49
+ */
50
+ export declare class ValidationError extends CirclesError<UtilsErrorSource> {
51
+ constructor(message: string, options?: {
52
+ code?: string | number;
53
+ cause?: unknown;
54
+ context?: Record<string, any>;
55
+ });
56
+ /**
57
+ * Create error for invalid address
58
+ */
59
+ static invalidAddress(address: string): ValidationError;
60
+ /**
61
+ * Create error for invalid amount
62
+ */
63
+ static invalidAmount(amount: any, reason?: string): ValidationError;
64
+ /**
65
+ * Create error for missing parameter
66
+ */
67
+ static missingParameter(paramName: string): ValidationError;
68
+ /**
69
+ * Create error for invalid parameter
70
+ */
71
+ static invalidParameter(paramName: string, value: any, reason?: string): ValidationError;
72
+ }
73
+ /**
74
+ * Encoding/Conversion errors for utils package
75
+ */
76
+ export declare class EncodingError extends CirclesError<UtilsErrorSource> {
77
+ constructor(message: string, options?: {
78
+ code?: string | number;
79
+ cause?: unknown;
80
+ context?: Record<string, any>;
81
+ });
82
+ /**
83
+ * Create error for ABI encoding failures
84
+ */
85
+ static abiEncoding(functionName: string, cause?: unknown): EncodingError;
86
+ /**
87
+ * Create error for CID conversion failures
88
+ */
89
+ static cidConversion(cid: string, cause?: unknown): EncodingError;
90
+ }
91
+ /**
92
+ * Helper to wrap unknown errors into CirclesError
93
+ */
94
+ export declare function wrapError(error: unknown, source?: string): CirclesError;
95
+ /**
96
+ * Type guard to check if error is a CirclesError
97
+ */
98
+ export declare function isCirclesError(error: unknown): error is CirclesError;
99
+ /**
100
+ * Helper to extract error message safely
101
+ */
102
+ export declare function getErrorMessage(error: unknown): string;
103
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;GAGG;AACH,MAAM,MAAM,eAAe,GACvB,OAAO,GACP,KAAK,GACL,KAAK,GACL,MAAM,GACN,QAAQ,GACR,UAAU,GACV,WAAW,GACX,QAAQ,GACR,YAAY,GACZ,SAAS,CAAC;AAEd;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,YAAY,GAAG,YAAY,GAAG,UAAU,CAAC;AAElF;;;GAGG;AACH,qBAAa,YAAY,CAAC,OAAO,SAAS,MAAM,GAAG,MAAM,CAAE,SAAQ,KAAK;IACtE,SAAgB,IAAI,EAAE,MAAM,CAAC;IAC7B,SAAgB,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvC,SAAgB,MAAM,EAAE,OAAO,CAAC;IAChC,SAAgB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChC,SAAgB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;gBAG5C,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;QACR,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;QACvB,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;KAC/B;IAeH;;OAEG;IACH,MAAM;;;;;;;;;IAeN;;OAEG;IACH,QAAQ,IAAI,MAAM;CAUnB;AAED;;GAEG;AACH,qBAAa,eAAgB,SAAQ,YAAY,CAAC,gBAAgB,CAAC;gBAE/D,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;QACR,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;QACvB,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;KAC/B;IAMH;;OAEG;IACH,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe;IAOvD;;OAEG;IACH,MAAM,CAAC,aAAa,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,eAAe;IAUnE;;OAEG;IACH,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,eAAe;IAO3D;;OAEG;IACH,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,eAAe;CASzF;AAED;;GAEG;AACH,qBAAa,aAAc,SAAQ,YAAY,CAAC,gBAAgB,CAAC;gBAE7D,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;QACR,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;QACvB,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;KAC/B;IAKH;;OAEG;IACH,MAAM,CAAC,WAAW,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,aAAa;IAQxE;;OAEG;IACH,MAAM,CAAC,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,aAAa;CAOlE;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,GAAE,MAAkB,GAAG,YAAY,CAwBlF;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,YAAY,CAEpE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAQtD"}