diff --git a/packages/appkit-react/src/features/onramp/widgets/crypto-onramp/utils/map-crypto-onramp-error.ts b/packages/appkit-react/src/features/onramp/widgets/crypto-onramp/utils/map-crypto-onramp-error.ts index 07fa28d87..9c9767360 100644 --- a/packages/appkit-react/src/features/onramp/widgets/crypto-onramp/utils/map-crypto-onramp-error.ts +++ b/packages/appkit-react/src/features/onramp/widgets/crypto-onramp/utils/map-crypto-onramp-error.ts @@ -40,6 +40,8 @@ export const mapCryptoOnrampError = (error: unknown): string => { return 'cryptoOnramp.quoteError'; case CryptoOnrampErrorCode.ProviderError: return 'cryptoOnramp.providerError'; + case CryptoOnrampErrorCode.DepositFailed: + return 'cryptoOnramp.depositFailed'; } } diff --git a/packages/appkit-react/src/locales/en.ts b/packages/appkit-react/src/locales/en.ts index ea491b10a..c26ef2626 100644 --- a/packages/appkit-react/src/locales/en.ts +++ b/packages/appkit-react/src/locales/en.ts @@ -112,6 +112,7 @@ export default { selectMethod: 'Select payment method', searchMethod: 'Search', quoteError: 'Failed to get a quote', + depositFailed: 'Failed to create deposit', tooManyDecimals: 'Too many decimals', providerError: 'Provider error', genericError: 'Something went wrong', diff --git a/packages/walletkit/src/defi/DefiManager.ts b/packages/walletkit/src/defi/DefiManager.ts index e94dc46aa..647dd463c 100644 --- a/packages/walletkit/src/defi/DefiManager.ts +++ b/packages/walletkit/src/defi/DefiManager.ts @@ -11,8 +11,7 @@ import type { DefiProvider } from '../api/interfaces'; import { resolveProvider } from '../types'; import type { ProviderInput } from '../types'; import type { ProviderFactoryContext } from '../types/factory'; -import type { DefiError } from './errors'; -import { DefiErrorCode } from './errors'; +import { DefiError, DefiErrorCode } from './errors'; import type { SharedKitEvents } from '../types/emitter'; import type { EventEmitter } from '../core/EventEmitter'; @@ -24,7 +23,6 @@ export abstract class DefiManager< protected providers: T[] = []; protected defaultProviderId?: string; - protected abstract createError(message: string, code: string, details?: unknown): DefiError; protected eventEmitter: EventEmitter; constructor(createFactoryContext: () => ProviderFactoryContext) { @@ -44,7 +42,7 @@ export abstract class DefiManager< const providerId = provider.providerId; if (!providerId) { - throw this.createError('Provider must have a providerId', DefiErrorCode.InvalidProvider); + throw new DefiError('Provider must have a providerId', DefiErrorCode.InvalidProvider); } const oldProvider = this.providers.find((p) => p.providerId === providerId); @@ -84,7 +82,7 @@ export abstract class DefiManager< const provider = this.providers.find((p) => p.providerId === providerId); if (!provider) { - throw this.createError(`Provider '${providerId}' not found`, DefiErrorCode.ProviderNotFound, { + throw new DefiError(`Provider '${providerId}' not found`, DefiErrorCode.ProviderNotFound, { provider: providerId, registered: this.providers.map((p) => p.providerId), }); @@ -104,15 +102,12 @@ export abstract class DefiManager< const providerName = providerId || this.defaultProviderId; if (!providerName) { - throw this.createError( - 'No default provider set. Register a provider first.', - DefiErrorCode.NoDefaultProvider, - ); + throw new DefiError('No default provider set. Register a provider first.', DefiErrorCode.NoDefaultProvider); } const provider = this.providers.find((p) => p.providerId === providerName); if (!provider) { - throw this.createError(`Provider '${providerName}' not found`, DefiErrorCode.ProviderNotFound, { + throw new DefiError(`Provider '${providerName}' not found`, DefiErrorCode.ProviderNotFound, { provider: providerName, registered: this.providers.map((p) => p.providerId), }); diff --git a/packages/walletkit/src/defi/crypto-onramp/CryptoOnrampManager.ts b/packages/walletkit/src/defi/crypto-onramp/CryptoOnrampManager.ts index 73fd5ab17..fc862188b 100644 --- a/packages/walletkit/src/defi/crypto-onramp/CryptoOnrampManager.ts +++ b/packages/walletkit/src/defi/crypto-onramp/CryptoOnrampManager.ts @@ -17,8 +17,7 @@ import type { CryptoOnrampStatusParams, CryptoOnrampSupportedCurrencies, } from '../../api/models'; -import type { CryptoOnrampErrorCode } from './errors'; -import { CryptoOnrampError } from './errors'; +import { CryptoOnrampError, CryptoOnrampErrorCode } from './errors'; import { globalLogger } from '../../core/Logger'; import { DefiManager } from '../DefiManager'; @@ -113,7 +112,12 @@ export class CryptoOnrampManager extends DefiManager 0 ? (data.quote.receive_amount / data.quote.requested_amount).toString() @@ -320,8 +332,10 @@ export class LayerswapCryptoOnrampProvider extends CryptoOnrampProvider(); + let anyFulfilled = false; for (const result of results) { if (result.status !== 'fulfilled') continue; + anyFulfilled = true; for (const network of result.value) { const caip2 = slugToCaip2[network.name]; if (!caip2) continue; @@ -334,6 +348,17 @@ export class LayerswapCryptoOnrampProvider extends CryptoOnrampProvider 0) { + const firstRejection = results.find( + (result): result is PromiseRejectedResult => result.status === 'rejected', + ); + throw new CryptoOnrampError( + 'Layerswap: failed to fetch supported sources for all destinations', + CryptoOnrampErrorCode.ProviderError, + firstRejection?.reason, + ); + } + return { source: Array.from(sourceMap.values()), destination }; } diff --git a/packages/walletkit/src/defi/crypto-onramp/layerswap/utils.ts b/packages/walletkit/src/defi/crypto-onramp/layerswap/utils.ts index e7552d99b..11004f111 100644 --- a/packages/walletkit/src/defi/crypto-onramp/layerswap/utils.ts +++ b/packages/walletkit/src/defi/crypto-onramp/layerswap/utils.ts @@ -129,33 +129,3 @@ export const mapStatus = (status: LayerswapSwapStatus | string): CryptoOnrampSta return 'pending'; } }; - -/** - * Format a base-units integer string into a decimal token-units string. - * e.g. formatBaseUnits('2000000', 6) === '2' - */ -export const formatBaseUnits = (base: string, decimals: number): string => { - if (!/^\d+$/.test(base)) { - throw new Error(`formatBaseUnits: not a non-negative integer string: "${base}"`); - } - if (decimals === 0) return base; - const padded = base.padStart(decimals + 1, '0'); - const whole = padded.slice(0, padded.length - decimals); - const frac = padded.slice(padded.length - decimals).replace(/0+$/, ''); - return frac.length > 0 ? `${whole}.${frac}` : whole; -}; - -/** - * Scale a decimal token-units string by 10^decimals and return the integer - * base-units string, truncating any excess fractional digits. - */ -export const parseBaseUnits = (value: number | string, decimals: number): string => { - const str = typeof value === 'number' ? value.toString() : value; - if (!/^\d+(\.\d+)?$/.test(str)) { - throw new Error(`parseBaseUnits: not a non-negative decimal: "${str}"`); - } - const [whole, frac = ''] = str.split('.'); - const truncated = frac.slice(0, decimals).padEnd(decimals, '0'); - const combined = `${whole}${truncated}`.replace(/^0+/, ''); - return combined.length > 0 ? combined : '0'; -}; diff --git a/packages/walletkit/src/defi/onramp/OnrampManager.ts b/packages/walletkit/src/defi/onramp/OnrampManager.ts index 822f7fcf9..3e135db11 100644 --- a/packages/walletkit/src/defi/onramp/OnrampManager.ts +++ b/packages/walletkit/src/defi/onramp/OnrampManager.ts @@ -8,7 +8,6 @@ import type { OnrampAPI, OnrampProviderInterface } from '../../api/interfaces'; import type { OnrampParams, OnrampQuote, OnrampQuoteParams } from '../../api/models'; -import { OnrampError } from './errors'; import { globalLogger } from '../../core/Logger'; import { DefiManager } from '../DefiManager'; @@ -124,8 +123,4 @@ export class OnrampManager extends DefiManager implemen throw error; } } - - protected createError(message: string, code: string, details?: unknown): OnrampError { - return new OnrampError(message, code, details); - } } diff --git a/packages/walletkit/src/defi/onramp/errors.ts b/packages/walletkit/src/defi/onramp/errors.ts index 337e919a7..a83067073 100644 --- a/packages/walletkit/src/defi/onramp/errors.ts +++ b/packages/walletkit/src/defi/onramp/errors.ts @@ -8,14 +8,19 @@ import { DefiError } from '../errors'; +export enum OnrampErrorCode { + ProviderError = 'PROVIDER_ERROR', + InvalidParams = 'INVALID_ONRAMP_PARAMS', + QuoteFailed = 'QUOTE_FAILED', + UrlBuildFailed = 'URL_BUILD_FAILED', +} + export class OnrampError extends DefiError { - static readonly PROVIDER_ERROR = 'PROVIDER_ERROR'; - static readonly InvalidParams = 'INVALID_ONRAMP_PARAMS'; - static readonly QUOTE_FAILED = 'QUOTE_FAILED'; - static readonly URL_BUILD_FAILED = 'URL_BUILD_FAILED'; + public readonly code: OnrampErrorCode; - constructor(message: string, code: string, details?: unknown) { + constructor(message: string, code: OnrampErrorCode, details?: unknown) { super(message, code, details); this.name = 'OnrampError'; + this.code = code; } } diff --git a/packages/walletkit/src/defi/onramp/mercuryo/MercuryoProvider.ts b/packages/walletkit/src/defi/onramp/mercuryo/MercuryoProvider.ts index d18c9f15e..ec7974c69 100644 --- a/packages/walletkit/src/defi/onramp/mercuryo/MercuryoProvider.ts +++ b/packages/walletkit/src/defi/onramp/mercuryo/MercuryoProvider.ts @@ -9,7 +9,7 @@ import type { OnrampParams, OnrampQuote, OnrampQuoteParams } from '../../../api/models'; import { Network } from '../../../api/models'; import { OnrampProvider } from '../OnrampProvider'; -import { OnrampError } from '../errors'; +import { OnrampError, OnrampErrorCode } from '../errors'; /** * Custom options for Mercuryo requests @@ -101,7 +101,7 @@ export class MercuryoProvider extends OnrampProvider implem log.debug('Received staking quote', quote); return quote; } catch (error) { - throw this.createError('Failed to get staking quote', StakingErrorCode.InvalidParams, { error, params }); + throw new StakingError('Failed to get staking quote', StakingErrorCode.InvalidParams, { error, params }); } } @@ -60,7 +60,7 @@ export class StakingManager extends DefiManager implem try { return await this.getProvider(providerId).buildStakeTransaction(params); } catch (error) { - throw this.createError('Failed to build staking transaction', StakingErrorCode.InvalidParams, { + throw new StakingError('Failed to build staking transaction', StakingErrorCode.InvalidParams, { error, params, }); @@ -87,7 +87,7 @@ export class StakingManager extends DefiManager implem try { return await this.getProvider(providerId).getStakedBalance(userAddress, network); } catch (error) { - throw this.createError('Failed to get staking balance', StakingErrorCode.InvalidParams, { + throw new StakingError('Failed to get staking balance', StakingErrorCode.InvalidParams, { error, userAddress, network, @@ -109,7 +109,7 @@ export class StakingManager extends DefiManager implem try { return await this.getProvider(providerId).getStakingProviderInfo(network); } catch (error) { - throw this.createError('Failed to get staking info', StakingErrorCode.InvalidParams, { error, network }); + throw new StakingError('Failed to get staking info', StakingErrorCode.InvalidParams, { error, network }); } } @@ -127,18 +127,10 @@ export class StakingManager extends DefiManager implem try { return this.getProvider(providerId).getStakingProviderMetadata(network); } catch (error) { - throw this.createError('Failed to get staking metadata', StakingErrorCode.InvalidParams, { + throw new StakingError('Failed to get staking metadata', StakingErrorCode.InvalidParams, { error, network, }); } } - - protected createError(message: string, code: string, details?: unknown): StakingError { - const errorCode = Object.values(StakingErrorCode).includes(code as StakingErrorCode) - ? (code as StakingErrorCode) - : StakingErrorCode.InvalidParams; - log.error(message, { code, details }); - return new StakingError(message, errorCode, details); - } } diff --git a/packages/walletkit/src/defi/swap/SwapManager.ts b/packages/walletkit/src/defi/swap/SwapManager.ts index 3ae1da73a..7b738d44f 100644 --- a/packages/walletkit/src/defi/swap/SwapManager.ts +++ b/packages/walletkit/src/defi/swap/SwapManager.ts @@ -9,8 +9,6 @@ import type { TransactionRequest } from '../../api/models'; import type { SwapAPI, SwapProviderInterface } from '../../api/interfaces'; import type { SwapQuoteParams, SwapQuote, SwapParams } from '../../api/models'; -import type { SwapErrorCode } from './errors'; -import { SwapError } from './errors'; import { globalLogger } from '../../core/Logger'; import { DefiManager } from '../DefiManager'; import type { ProviderFactoryContext } from '../../types/factory'; @@ -88,8 +86,4 @@ export class SwapManager extends DefiManager implements S throw error; } } - - protected createError(message: string, code: string, details?: unknown): SwapError { - return new SwapError(message, code as SwapErrorCode, details); - } } diff --git a/packages/walletkit/src/index.ts b/packages/walletkit/src/index.ts index b96a68a30..a553750c4 100644 --- a/packages/walletkit/src/index.ts +++ b/packages/walletkit/src/index.ts @@ -22,7 +22,7 @@ export { JettonsManager } from './core/JettonsManager'; export { DefiError, DefiErrorCode } from './defi/errors'; export { SwapManager, SwapProvider, SwapError, SwapErrorCode } from './defi/swap'; export { StakingManager, StakingProvider, StakingError, StakingErrorCode } from './defi/staking'; -export { OnrampManager, OnrampProvider, OnrampError } from './defi/onramp'; +export { OnrampManager, OnrampProvider, OnrampError, OnrampErrorCode } from './defi/onramp'; export { CryptoOnrampManager, CryptoOnrampProvider,