From f7d6943d3e0fded74fbceb42c626df2b062ac440 Mon Sep 17 00:00:00 2001 From: Chralu Date: Tue, 27 Aug 2024 16:04:21 +0200 Subject: [PATCH 1/3] feat: :sparkles: Improve error handling. --- example/lib/actions/add_token.dart | 3 +- example/lib/actions/read_contract.dart | 3 +- example/lib/actions/write_contract.dart | 3 +- example/pubspec.lock | 2 +- lib/src/js/models/error.js.dart | 18 +- lib/src/models/error.dart | 182 ++++++- lib/src/wagmi_core.dart | 667 +++++++++++++----------- lib/wagmi_flutter_web.dart | 1 + typescript/src/wagmi_core.ts | 72 +-- 9 files changed, 580 insertions(+), 371 deletions(-) diff --git a/example/lib/actions/add_token.dart b/example/lib/actions/add_token.dart index c6da8c4..53fedfd 100644 --- a/example/lib/actions/add_token.dart +++ b/example/lib/actions/add_token.dart @@ -3,6 +3,7 @@ import 'package:example/actions/components/spacer.dart'; import 'package:example/actions/components/tab_header.dart'; import 'package:flutter/material.dart'; import 'package:wagmi_flutter_web/wagmi_flutter_web.dart' as wagmi; +import 'package:wagmi_flutter_web/wagmi_flutter_web.dart'; class AddTokenExample extends StatefulWidget { const AddTokenExample({super.key}); @@ -78,7 +79,7 @@ class _AddTokenExampleState extends State { ), ); _operationSucceed('Token added'); - } catch (e) { + } on WagmiError catch (e) { _operationFailed(e.toString()); } }, diff --git a/example/lib/actions/read_contract.dart b/example/lib/actions/read_contract.dart index 75b240f..dae63e8 100644 --- a/example/lib/actions/read_contract.dart +++ b/example/lib/actions/read_contract.dart @@ -6,6 +6,7 @@ import 'package:example/actions/components/spacer.dart'; import 'package:example/actions/components/tab_header.dart'; import 'package:flutter/material.dart'; import 'package:wagmi_flutter_web/wagmi_flutter_web.dart' as wagmi; +import 'package:wagmi_flutter_web/wagmi_flutter_web.dart'; class ReadContractExample extends StatefulWidget { const ReadContractExample({super.key}); @@ -175,7 +176,7 @@ class _ReadContractExampleState extends State { ), ); _operationSucceed(result.toString()); - } catch (e) { + } on WagmiError catch (e) { _operationFailed(e.toString()); } }, diff --git a/example/lib/actions/write_contract.dart b/example/lib/actions/write_contract.dart index 01720d8..90daf06 100644 --- a/example/lib/actions/write_contract.dart +++ b/example/lib/actions/write_contract.dart @@ -6,6 +6,7 @@ import 'package:example/actions/components/spacer.dart'; import 'package:example/actions/components/tab_header.dart'; import 'package:flutter/material.dart'; import 'package:wagmi_flutter_web/wagmi_flutter_web.dart' as wagmi; +import 'package:wagmi_flutter_web/wagmi_flutter_web.dart'; class WriteContractExample extends StatefulWidget { const WriteContractExample({super.key}); @@ -188,7 +189,7 @@ class _WriteContractExampleState extends State { ), ); _operationSucceed(result); - } catch (e) { + } on WagmiError catch (e) { _operationFailed(e.toString()); } }, diff --git a/example/pubspec.lock b/example/pubspec.lock index 4f1ef70..02175be 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -619,7 +619,7 @@ packages: path: ".." relative: true source: path - version: "0.0.1-dev.7" + version: "0.0.1-dev.8" watcher: dependency: transitive description: diff --git a/lib/src/js/models/error.js.dart b/lib/src/js/models/error.js.dart index ceb3013..7858d4e 100644 --- a/lib/src/js/models/error.js.dart +++ b/lib/src/js/models/error.js.dart @@ -11,8 +11,24 @@ extension JSErrorExt on JSError { /// Get the error message external String? get name; + external JSArray? get metaMessages; + external JSString? get shortMessage; + external JSString? get version; + external JSError? get cause; + external JSString? get docsPath; + WagmiError get toDart => WagmiError( message: message, - name: name, + name: WagmiErrors.values.firstWhere((value) => value.name == name), + metaMessages: metaMessages?.toDart + .map( + (message) => message.toDart, + ) + .toList(), + shortMessage: shortMessage?.toDart, + version: version?.toDart, + cause: cause?.toDart, + docsPath: docsPath?.toDart, + details: toMap(), ); } diff --git a/lib/src/models/error.dart b/lib/src/models/error.dart index 2a0d219..927a16d 100644 --- a/lib/src/models/error.dart +++ b/lib/src/models/error.dart @@ -1,12 +1,188 @@ +// ignore_for_file: constant_identifier_names + class WagmiError implements Exception { WagmiError({ + required this.name, this.message, - this.name, + this.metaMessages, + this.shortMessage, + this.version, + this.cause, + this.docsPath, + this.details, }); + final WagmiErrors name; final String? message; - final String? name; + final List? metaMessages; + final String? shortMessage; + final String? version; + final WagmiError? cause; + final String? docsPath; + + /// The raw error object + final Map? details; @override - String toString() => '$name : $message'; + String toString() => '$name : ${message ?? shortMessage}'; +} + +enum WagmiErrors { + /// [abi](https://github.com/wevm/viem/blob/main/src/errors/abi.ts) + AbiConstructorNotFoundError, + AbiConstructorParamsNotFoundError, + AbiDecodingDataSizeInvalidError, + AbiDecodingDataSizeTooSmallError, + AbiDecodingZeroDataError, + AbiEncodingArrayLengthMismatchError, + AbiEncodingBytesSizeMismatchError, + AbiEncodingLengthMismatchError, + AbiErrorInputsNotFoundError, + AbiErrorNotFoundError, + AbiErrorSignatureNotFoundError, + AbiEventSignatureEmptyTopicsError, + AbiEventSignatureNotFoundError, + AbiEventNotFoundError, + AbiFunctionNotFoundError, + AbiFunctionOutputsNotFoundError, + AbiFunctionSignatureNotFoundError, + AbiItemAmbiguityError, + BytesSizeMismatchError, + InvalidAbiEncodingTypeError, + InvalidAbiDecodingTypeError, + InvalidArrayError, + InvalidDefinitionTypeError, + + /// [account](https://github.com/wevm/viem/blob/main/src/errors/account.ts) + AccountNotFoundError, + AccountTypeNotSupportedError, + + /// [address](https://github.com/wevm/viem/blob/main/src/errors/address.ts) + InvalidAddressError, + + /// [blob](https://github.com/wevm/viem/blob/main/src/errors/blob.ts) + BlobSizeTooLargeError, + EmptyBlobError, + InvalidVersionedHashSizeError, + InvalidVersionedHashVersionError, + + /// [block](https://github.com/wevm/viem/blob/main/src/errors/block.ts) + BlockNotFoundError, + + /// [ccip](https://github.com/wevm/viem/blob/main/src/errors/ccip.ts) + OffchainLookupError, + OffchainLookupResponseMalformedError, + OffchainLookupSenderMismatchError, + + /// [chain](https://github.com/wevm/viem/blob/main/src/errors/chain.ts) + ChainMismatchError, + ChainNotFoundError, + ClientChainNotConfiguredError, + InvalidChainIdError, + + /// [contract](https://github.com/wevm/viem/blob/main/src/errors/contract.ts) + CallExecutionError, + ContractFunctionExecutionError, + ContractFunctionRevertedError, + ContractFunctionZeroDataError, + CounterfactualDeploymentFailedError, + RawContractError, + + /// [cursor](https://github.com/wevm/viem/blob/main/src/errors/cursor.ts) + NegativeOffsetError, + PositionOutOfBoundsError, + RecursiveReadLimitExceededError, + + /// [data](https://github.com/wevm/viem/blob/main/src/errors/data.ts) + SliceOffsetOutOfBoundsError, + SizeExceedsPaddingSizeError, + InvalidBytesLengthError, + + /// [encoding](https://github.com/wevm/viem/blob/main/src/errors/encoding.ts) + IntegerOutOfRangeError, + InvalidBytesBooleanError, + InvalidHexBooleanError, + InvalidHexValueError, + SizeOverflowError, + + /// [ens](https://github.com/wevm/viem/blob/main/src/errors/ens.ts) + EnsAvatarInvalidMetadataError, + EnsAvatarInvalidNftUriError, + EnsAvatarUriResolutionError, + EnsAvatarUnsupportedNamespaceError, + + /// [estimateGas](https://github.com/wevm/viem/blob/main/src/errors/estimateGas.ts) + EstimateGasExecutionError, + + /// [fee](https://github.com/wevm/viem/blob/main/src/errors/fee.ts) + BaseFeeScalarError, + MaxFeePerGasTooLowError, + + /// [log](https://github.com/wevm/viem/blob/main/src/errors/log.ts) + FilterTypeNotSupportedError, + + /// [node](https://github.com/wevm/viem/blob/main/src/errors/node.ts) + ExecutionRevertedError, + FeeCapTooHighError, + FeeCapTooLowError, + NonceTooHighError, + NonceTooLowError, + NonceMaxValueError, + InsufficientFundsError, + IntrinsicGasTooHighError, + IntrinsicGasTooLowError, + TransactionTypeNotSupportedError, + TipAboveFeeCapError, + UnknownNodeError, + + /// [request](https://github.com/wevm/viem/blob/main/src/errors/request.ts) + HttpRequestError, + WebSocketRequestError, + RpcRequestError, + SocketClosedError, + TimeoutError, + + /// [rpc](https://github.com/wevm/viem/blob/main/src/errors/rpc.ts) + ProviderRpcError, + ParseRpcError, + InvalidRequestRpcError, + MethodNotFoundRpcError, + InvalidParamsRpcError, + InternalRpcError, + InvalidInputRpcError, + ResourceNotFoundRpcError, + ResourceUnavailableRpcError, + TransactionRejectedRpcError, + MethodNotSupportedRpcError, + LimitExceededRpcError, + JsonRpcVersionUnsupportedError, + UserRejectedRequestError, + UnauthorizedProviderError, + UnsupportedProviderMethodError, + ProviderDisconnectedError, + ChainDisconnectedError, + SwitchChainError, + UnknownRpcError, + + /// [siwe](https://github.com/wevm/viem/blob/main/src/errors/siwe.ts) + SiweInvalidMessageFieldError, + + /// [stateOverride](https://github.com/wevm/viem/blob/main/src/errors/stateOverride.ts) + AccountStateConflictError, + StateAssignmentConflictError, + + /// [transaction](https://github.com/wevm/viem/blob/main/src/errors/transaction.ts) + FeeConflictError, + InvalidLegacyVError, + InvalidSerializableTransactionError, + InvalidSerializedTransactionTypeError, + InvalidSerializedTransactionError, + InvalidStorageKeySizeError, + TransactionExecutionError, + TransactionNotFoundError, + TransactionReceiptNotFoundError, + WaitForTransactionReceiptTimeoutError, + + /// [transport](https://github.com/wevm/viem/blob/main/src/errors/transport.ts) + UrlRequiredError, } diff --git a/lib/src/wagmi_core.dart b/lib/src/wagmi_core.dart index 0d9ffbd..c10d717 100644 --- a/lib/src/wagmi_core.dart +++ b/lib/src/wagmi_core.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:js_interop'; import 'package:wagmi_flutter_web/src/actions/call.dart'; @@ -39,465 +40,517 @@ import 'package:wagmi_flutter_web/src/models/chain.dart'; import 'package:wagmi_flutter_web/src/utils/utils_js.dart'; class Core { - static Account getAccount() { - return window.wagmiCore.getAccount().toDart; - } + static Account getAccount() => _guard(() { + return window.wagmiCore.getAccount().toDart; + }); - static int getChainId() { - return window.wagmiCore.getChainId().toDartInt; - } + static int getChainId() => _guard(() { + return window.wagmiCore.getChainId().toDartInt; + }); - static List getChains() { - final result = window.wagmiCore.getChains(); + static List getChains() => _guard(() { + final result = window.wagmiCore.getChains(); - return result.toDart.map((item) { - return Chain.fromMap(item.toMap()); - }).toList(); - } + return result.toDart.map((item) { + return Chain.fromMap(item.toMap()); + }).toList(); + }); static Future getBlockNumber( GetBlockNumberParameters getBlockNumberParameters, { String configKey = 'default', - }) async { - final result = await window.wagmiCore - .getBlockNumber( - configKey.toJS, - getBlockNumberParameters.toJS, - ) - .toDart; - return result.toDart; - } + }) => + _guardFuture(() async { + final result = await window.wagmiCore + .getBlockNumber( + configKey.toJS, + getBlockNumberParameters.toJS, + ) + .toDart; + return result.toDart; + }); static Future getGasPrice( GetGasPriceParameters getGasPriceParameters, { String configKey = 'default', - }) async { - final result = await window.wagmiCore - .getGasPrice( - configKey.toJS, - getGasPriceParameters.toJS, - ) - .toDart; - return result.toDart; - } + }) => + _guardFuture(() async { + final result = await window.wagmiCore + .getGasPrice( + configKey.toJS, + getGasPriceParameters.toJS, + ) + .toDart; + return result.toDart; + }); static Future getBalance( GetBalanceParameters getBalanceParameters, { String configKey = 'default', - }) async { - final result = await window.wagmiCore - .getBalance( - configKey.toJS, - getBalanceParameters.toJS, - ) - .toDart; - return result.toDart; - } + }) => + _guardFuture(() async { + final result = await window.wagmiCore + .getBalance( + configKey.toJS, + getBalanceParameters.toJS, + ) + .toDart; + return result.toDart; + }); static Future getTransactionCount( GetTransactionCountParameters getTransactionCountParameters, { String configKey = 'default', - }) async { - final result = await window.wagmiCore - .getTransactionCount( - configKey.toJS, - getTransactionCountParameters.toJS, - ) - .toDart; - return result.toDartInt; - } + }) => + _guardFuture(() async { + final result = await window.wagmiCore + .getTransactionCount( + configKey.toJS, + getTransactionCountParameters.toJS, + ) + .toDart; + return result.toDartInt; + }); static Future getToken( GetTokenParameters getTokenParameters, { String configKey = 'default', - }) async { - final result = await window.wagmiCore - .getToken( - configKey.toJS, - getTokenParameters.toJS, - ) - .toDart; + }) => + _guardFuture(() async { + final result = await window.wagmiCore + .getToken( + configKey.toJS, + getTokenParameters.toJS, + ) + .toDart; - return result.toDart; - } + return result.toDart; + }); static Future signMessage( SignMessageParameters signMessageParameters, { String configKey = 'default', - }) async { - final result = await window.wagmiCore - .signMessage( - configKey.toJS, - signMessageParameters.toJS, - ) - .toDart; - return result.toDart; - } + }) => + _guardFuture(() async { + final result = await window.wagmiCore + .signMessage( + configKey.toJS, + signMessageParameters.toJS, + ) + .toDart; + return result.toDart; + }); // read contract static Future readContract( ReadContractParameters readContractParameters, { String configKey = 'default', - }) async { - final result = await window.wagmiCore - .readContract( - configKey.toJS, - readContractParameters.toJS, - ) - .toDart; - return UtilsJS.dartify(result); - } + }) => + _guardFuture(() async { + final result = await window.wagmiCore + .readContract( + configKey.toJS, + readContractParameters.toJS, + ) + .toDart; + return UtilsJS.dartify(result); + }); static Future>> readContracts( ReadContractsParameters readContractsParameters, { String configKey = 'default', - }) async { - final result = await window.wagmiCore - .readContracts( - configKey.toJS, - readContractsParameters.toJS, - ) - .toDart; - return result.toDartDynamicList.cast>(); - } + }) => + _guardFuture(() async { + final result = await window.wagmiCore + .readContracts( + configKey.toJS, + readContractsParameters.toJS, + ) + .toDart; + return result.toDartDynamicList.cast>(); + }); // get transaction receipt static Future getTransactionReceipt( GetTransactionReceiptParameters getTransactionReceiptParameters, { String configKey = 'default', - }) async { - final result = await window.wagmiCore - .getTransactionReceipt( - configKey.toJS, - getTransactionReceiptParameters.toJS, - ) - .toDart; - return result.toDart; - } + }) => + _guardFuture(() async { + final result = await window.wagmiCore + .getTransactionReceipt( + configKey.toJS, + getTransactionReceiptParameters.toJS, + ) + .toDart; + return result.toDart; + }); static Future watchChainId( WatchChainIdParameters watchChainIdParameters, { String configKey = 'default', - }) async { - final result = await window.wagmiCore - .watchChainId( - configKey.toJS, - watchChainIdParameters.toJS, - ) - .toDart; - return result.toDart; - } + }) => + _guardFuture(() async { + final result = await window.wagmiCore + .watchChainId( + configKey.toJS, + watchChainIdParameters.toJS, + ) + .toDart; + return result.toDart; + }); static Future sendTransaction( SendTransactionParameters sendTransactionParameters, { String configKey = 'default', - }) async { - final result = await window.wagmiCore - .sendTransaction( - configKey.toJS, - sendTransactionParameters.toJS, - ) - .toDart; - return result.toDart; - } + }) => + _guardFuture(() async { + final result = await window.wagmiCore + .sendTransaction( + configKey.toJS, + sendTransactionParameters.toJS, + ) + .toDart; + return result.toDart; + }); static Future writeContract( WriteContractParameters writeContractParameters, { String configKey = 'default', - }) async { - final result = await window.wagmiCore - .writeContract( - configKey.toJS, - writeContractParameters.toJS, - ) - .toDart; - return result.toDart; - } + }) => + _guardFuture(() async { + final result = await window.wagmiCore + .writeContract( + configKey.toJS, + writeContractParameters.toJS, + ) + .toDart; + return result.toDart; + }); static Future estimateGas( EstimateGasParameters estimateGasParameters, { String configKey = 'default', - }) async { - final result = await window.wagmiCore - .estimateGas( - configKey.toJS, - estimateGasParameters.toJS, - ) - .toDart; - return result.toDart; - } + }) => + _guardFuture(() async { + final result = await window.wagmiCore + .estimateGas( + configKey.toJS, + estimateGasParameters.toJS, + ) + .toDart; + return result.toDart; + }); static Future getTransaction( GetTransactionParameters getTransactionParameters, { String configKey = 'default', - }) async { - final result = await window.wagmiCore - .getTransaction( - configKey.toJS, - getTransactionParameters.toJS, - ) - .toDart; - return result.toDart; - } + }) => + _guardFuture(() async { + final result = await window.wagmiCore + .getTransaction( + configKey.toJS, + getTransactionParameters.toJS, + ) + .toDart; + return result.toDart; + }); static Future watchContractEvent( WatchContractEventParameters watchContractEventParameters, { String configKey = 'default', - }) async { - final result = await window.wagmiCore - .watchContractEvent( - configKey.toJS, - watchContractEventParameters.toJS, - ) - .toDart; - return result.toDart; - } + }) => + _guardFuture(() async { + final result = await window.wagmiCore + .watchContractEvent( + configKey.toJS, + watchContractEventParameters.toJS, + ) + .toDart; + return result.toDart; + }); static Future getTransactionConfirmations( GetTransactionConfirmationsParameters getTransactionConfirmationsParameters, { String configKey = 'default', - }) async { - final result = await window.wagmiCore - .getTransactionConfirmations( - configKey.toJS, - getTransactionConfirmationsParameters.toJS, - ) - .toDart; - return result.toDart; - } + }) => + _guardFuture(() async { + final result = await window.wagmiCore + .getTransactionConfirmations( + configKey.toJS, + getTransactionConfirmationsParameters.toJS, + ) + .toDart; + return result.toDart; + }); static Future getBlock( GetBlockParameters getBlockParameters, { String configKey = 'default', - }) async { - final result = await window.wagmiCore - .getBlock( - configKey.toJS, - getBlockParameters.toJS, - ) - .toDart; - return result.toDart; - } + }) => + _guardFuture(() async { + final result = await window.wagmiCore + .getBlock( + configKey.toJS, + getBlockParameters.toJS, + ) + .toDart; + return result.toDart; + }); static Future getBlockTransactionCount( GetBlockTransactionCountParameters getBlockTransactionCountParameters, { String configKey = 'default', - }) async { - final result = await window.wagmiCore - .getBlockTransactionCount( - configKey.toJS, - getBlockTransactionCountParameters.toJS, - ) - .toDart; - return result.toDartInt; - } + }) => + _guardFuture(() async { + final result = await window.wagmiCore + .getBlockTransactionCount( + configKey.toJS, + getBlockTransactionCountParameters.toJS, + ) + .toDart; + return result.toDartInt; + }); // call function static Future call( CallParameters callParameters, { String configKey = 'default', - }) async { - final result = await window.wagmiCore - .call( - configKey.toJS, - callParameters.toJS, - ) - .toDart; - return result.toDart; - } + }) => + _guardFuture(() async { + final result = await window.wagmiCore + .call( + configKey.toJS, + callParameters.toJS, + ) + .toDart; + return result.toDart; + }); // estimate fees per gas static Future estimateFeesPerGas( EstimateFeesPerGasParameters estimateFeesPerGasParameters, { String configKey = 'default', - }) async { - final result = await window.wagmiCore - .estimateFeesPerGas( - configKey.toJS, - estimateFeesPerGasParameters.toJS, - ) - .toDart; - return result.toDart; - } + }) => + _guardFuture(() async { + final result = await window.wagmiCore + .estimateFeesPerGas( + configKey.toJS, + estimateFeesPerGasParameters.toJS, + ) + .toDart; + return result.toDart; + }); // estimate max priority fee per gas static Future estimateMaxPriorityFeePerGas( EstimateMaxPriorityFeePerGasParameters estimateMaxPriorityFeePerGasParameters, { String configKey = 'default', - }) async { - final result = await window.wagmiCore - .estimateMaxPriorityFeePerGas( - configKey.toJS, - estimateMaxPriorityFeePerGasParameters.toJS, - ) - .toDart; - return result.toDart; - } + }) => + _guardFuture(() async { + final result = await window.wagmiCore + .estimateMaxPriorityFeePerGas( + configKey.toJS, + estimateMaxPriorityFeePerGasParameters.toJS, + ) + .toDart; + return result.toDart; + }); // get byte code static Future getBytecode( GetByteCodeParameters getByteCodeParameters, { String configKey = 'default', - }) async { - final result = await window.wagmiCore - .getBytecode( - configKey.toJS, - getByteCodeParameters.toJS, - ) - .toDart; - return result.toDart; - } + }) => + _guardFuture(() async { + final result = await window.wagmiCore + .getBytecode( + configKey.toJS, + getByteCodeParameters.toJS, + ) + .toDart; + return result.toDart; + }); // disconnect static Future disconnect( DisconnectParameters disconnectParameters, { String configKey = 'default', - }) async { - await window.wagmiCore - .disconnect( - configKey.toJS, - disconnectParameters.toJS, - ) - .toDart; - } + }) => + _guardFuture(() async { + await window.wagmiCore + .disconnect( + configKey.toJS, + disconnectParameters.toJS, + ) + .toDart; + }); static Future waitForTransactionReceipt( WaitForTransactionReceiptParameters waitForTransactionReceiptParameters, { String configKey = 'default', - }) async { - final result = await window.wagmiCore - .waitForTransactionReceipt( - configKey.toJS, - waitForTransactionReceiptParameters.toJS, - ) - .toDart; - return result.toDart; - } + }) => + _guardFuture(() async { + final result = await window.wagmiCore + .waitForTransactionReceipt( + configKey.toJS, + waitForTransactionReceiptParameters.toJS, + ) + .toDart; + return result.toDart; + }); // get fee history static Future getFeeHistory( GetFeeHistoryParameters getFeeHistoryParameters, { String configKey = 'default', - }) async { - final result = await window.wagmiCore - .getFeeHistory( - configKey.toJS, - getFeeHistoryParameters.toJS, - ) - .toDart; - return result.toDart; - } + }) => + _guardFuture(() async { + final result = await window.wagmiCore + .getFeeHistory( + configKey.toJS, + getFeeHistoryParameters.toJS, + ) + .toDart; + return result.toDart; + }); static Future> switchChain( SwitchChainParameters switchChainParameters, { String configKey = 'default', - }) async { - final result = await window.wagmiCore - .switchChain( - configKey.toJS, - switchChainParameters.toJS, - ) - .toDart; - return result.toMap(); - } + }) => + _guardFuture(() async { + final result = await window.wagmiCore + .switchChain( + configKey.toJS, + switchChainParameters.toJS, + ) + .toDart; + return result.toMap(); + }); // switch account static Future> switchAccount( SwitchAccountParameters switchAccountParameters, { String configKey = 'default', - }) async { - final result = await window.wagmiCore - .switchAccount( - configKey.toJS, - switchAccountParameters.toJS, - ) - .toDart; - return result.toMap(); - } + }) => + _guardFuture(() async { + final result = await window.wagmiCore + .switchAccount( + configKey.toJS, + switchAccountParameters.toJS, + ) + .toDart; + return result.toMap(); + }); // verify message static Future verifyMessage( VerifyMessageParameters verifyMessageParameters, { String configKey = 'default', - }) async { - final result = await window.wagmiCore - .verifyMessage( - configKey.toJS, - verifyMessageParameters.toJS, - ) - .toDart; - return result.toDart; - } + }) => + _guardFuture(() async { + final result = await window.wagmiCore + .verifyMessage( + configKey.toJS, + verifyMessageParameters.toJS, + ) + .toDart; + return result.toDart; + }); // watch account static Future watchAccount( WatchAccountParameters watchAccountParameters, { String configKey = 'default', - }) async { - final result = await window.wagmiCore - .watchAccount( - configKey.toJS, - watchAccountParameters.toJS1, - ) - .toDart; - return result.toDart; - } + }) => + _guardFuture(() async { + final result = await window.wagmiCore + .watchAccount( + configKey.toJS, + watchAccountParameters.toJS1, + ) + .toDart; + return result.toDart; + }); // watch connections static Future watchConnections( WatchConnectionsParameters watchConnectionsParameters, { String configKey = 'default', - }) async { - final result = await window.wagmiCore - .watchConnections( - configKey.toJS, - watchConnectionsParameters.toJS2, - ) - .toDart; - return result.toDart; - } + }) => + _guardFuture(() async { + final result = await window.wagmiCore + .watchConnections( + configKey.toJS, + watchConnectionsParameters.toJS2, + ) + .toDart; + return result.toDart; + }); // getWalletClient static Future getWalletClient( GetWalletClientParameters getWalletClientParameters, { String configKey = 'default', - }) async { - final result = await window.wagmiCore - .getWalletClient( - configKey.toJS, - getWalletClientParameters.toJS, - ) - .toDart; - return result.toDart; - } + }) => + _guardFuture(() async { + final result = await window.wagmiCore + .getWalletClient( + configKey.toJS, + getWalletClientParameters.toJS, + ) + .toDart; + return result.toDart; + }); //deployContract static Future deployContract( DeployContractParameters deployContractParameters, { String configKey = 'default', - }) async { - final result = await window.wagmiCore - .deployContract( - configKey.toJS, - deployContractParameters.toJS, - ) - .toDart; - return result.toDart; - } + }) => + _guardFuture(() async { + final result = await window.wagmiCore + .deployContract( + configKey.toJS, + deployContractParameters.toJS, + ) + .toDart; + return result.toDart; + }); // watchAsset static Future watchAsset( WatchAssetParameters watchAssetParameters, { String configKey = 'default', - }) async { - final result = await window.wagmiCore - .watchAsset( - configKey.toJS, - watchAssetParameters.toJS, - ) - .toDart; - return result.toDart; + }) => + _guardFuture(() async { + final result = await window.wagmiCore + .watchAsset( + configKey.toJS, + watchAssetParameters.toJS, + ) + .toDart; + return result.toDart; + }); + + /// Catches and transforms errors properly + static Future _guardFuture(Future Function() action) async { + try { + return await action(); + } catch (e) { + e as JSError; + throw e.toDart; + } + } + + static T _guard(T Function() action) { + try { + return action(); + } catch (e) { + e as JSError; + throw e.toDart; + } } } diff --git a/lib/wagmi_flutter_web.dart b/lib/wagmi_flutter_web.dart index 2e95aec..08b0aa7 100644 --- a/lib/wagmi_flutter_web.dart +++ b/lib/wagmi_flutter_web.dart @@ -55,6 +55,7 @@ export 'src/models/chain_rpc_urls.dart'; export 'src/models/chain_serializers.dart'; export 'src/models/config.dart'; export 'src/models/connector.dart'; +export 'src/models/error.dart'; export 'src/models/fees_values.dart'; export 'src/models/format_unit.dart'; export 'src/models/message_to_sign.dart'; diff --git a/typescript/src/wagmi_core.ts b/typescript/src/wagmi_core.ts index 7f47dcf..c7681c7 100644 --- a/typescript/src/wagmi_core.ts +++ b/typescript/src/wagmi_core.ts @@ -1,6 +1,7 @@ import { CallParameters, Config, + DeployContractParameters, DisconnectParameters, EstimateFeesPerGasParameters, EstimateGasParameters, @@ -18,22 +19,22 @@ import { GetTransactionParameters, GetTransactionReceiptParameters, GetWalletClientParameters, - DeployContractParameters, - WatchAssetParameters, ReadContractParameters, ReadContractsParameters, SendTransactionParameters, - WatchConnectionsParameters, - WatchAccountParameters, - SwitchChainParameters, + SignMessageParameters, SwitchAccountParameters, + SwitchChainParameters, VerifyMessageParameters, - SignMessageParameters, WaitForTransactionReceiptParameters, + WatchAccountParameters, + WatchAssetParameters, WatchChainIdParameters, + WatchConnectionsParameters, WatchContractEventParameters, WriteContractParameters, call, + deployContract, disconnect, estimateFeesPerGas, estimateGas, @@ -53,22 +54,21 @@ import { getTransactionConfirmations, getTransactionCount, getTransactionReceipt, + getWalletClient, readContract, readContracts, sendTransaction, signMessage, + switchAccount, + switchChain, + verifyMessage, waitForTransactionReceipt, + watchAccount, + watchAsset, watchChainId, + watchConnections, watchContractEvent, writeContract, - watchConnections, - switchChain, - switchAccount, - watchAccount, - verifyMessage, - getWalletClient, - deployContract, - watchAsset, } from "@wagmi/core"; import { InvalidAddressError } from "viem"; import { JSWagmiContext } from "./context"; @@ -88,7 +88,6 @@ export class JSWagmiCore { getBlockNumber = this.#guard( - 'getBlockNumber', async (configKey: string, params: GetBlockNumberParameters) => getBlockNumber( this.getConfig(configKey), params @@ -96,7 +95,6 @@ export class JSWagmiCore { ) getGasPrice = this.#guard( - 'getGasPrice', async (configKey: string, params: GetGasPriceParameters) => getGasPrice( this.getConfig(configKey), params @@ -104,7 +102,6 @@ export class JSWagmiCore { ) getBalance = this.#guard( - 'getBalance', async (configKey: string, params: GetBalanceParameters) => { if (!params.address || !/^0x[a-fA-F0-9]{40}$/.test(params.address)) { console.error("Invalid address provided") @@ -117,7 +114,6 @@ export class JSWagmiCore { } ) getBlock = this.#guard( - 'getBlock', async (configKey: string, params: GetBlockParameters) => { return getBlock( this.getConfig(configKey), @@ -127,7 +123,6 @@ export class JSWagmiCore { ) getBlockTransactionCount = this.#guard( - 'getBlockTransactionCount', async (configKey: string, params: GetBlockTransactionCountParameters) => getBlockTransactionCount( this.getConfig(configKey), params @@ -135,7 +130,6 @@ export class JSWagmiCore { ) getTransaction = this.#guard( - 'getTransaction', async (configKey: string, params: GetTransactionParameters) => getTransaction( this.getConfig(configKey), params @@ -143,7 +137,6 @@ export class JSWagmiCore { ) call = this.#guard( - 'call', async (configKey: string, params: CallParameters) => call( this.getConfig(configKey), params @@ -151,7 +144,6 @@ export class JSWagmiCore { ) getTransactionConfirmations = this.#guard( - 'getTransactionConfirmations', async (configKey: string, params: GetTransactionConfirmationsParameters) => getTransactionConfirmations( this.getConfig(configKey), params @@ -159,7 +151,6 @@ export class JSWagmiCore { ) getTransactionCount = this.#guard( - 'getTransactionCount', async (configKey: string, params: GetTransactionCountParameters) => { if (!params.address || !/^0x[a-fA-F0-9]{40}$/.test(params.address)) { console.error("Invalid address provided") @@ -174,7 +165,6 @@ export class JSWagmiCore { ) getToken = this.#guard( - 'getToken', async (configKey: string, params: GetTokenParameters) => getToken( this.getConfig(configKey), params @@ -183,7 +173,6 @@ export class JSWagmiCore { signMessage = this.#guard( - 'signMessage', async (configKey: string, params: SignMessageParameters) => { if (!params.message) { console.error("No message provided") @@ -197,7 +186,6 @@ export class JSWagmiCore { ) readContract = this.#guard( - 'readContract', async (configKey: string, params: ReadContractParameters) => readContract( this.getConfig(configKey), params @@ -205,7 +193,6 @@ export class JSWagmiCore { ) readContracts = this.#guard( - 'readContracts', async (configKey: string, params: ReadContractsParameters) => readContracts( this.getConfig(configKey), params @@ -213,7 +200,6 @@ export class JSWagmiCore { ) getTransactionReceipt = this.#guard( - 'getTransactionReceipt', async (configKey: string, params: GetTransactionReceiptParameters) => getTransactionReceipt( this.getConfig(configKey), params @@ -221,7 +207,6 @@ export class JSWagmiCore { ) sendTransaction = this.#guard( - 'sendTransaction', async (configKey: string, params: SendTransactionParameters) => sendTransaction( this.getConfig(configKey), params @@ -229,7 +214,6 @@ export class JSWagmiCore { ) watchChainId = this.#guard( - 'watchChainId', async (configKey: string, params: WatchChainIdParameters) => watchChainId( this.getConfig(configKey), params @@ -237,7 +221,6 @@ export class JSWagmiCore { ) watchContractEvent = this.#guard( - 'watchContractEvent', async (configKey: string, params: WatchContractEventParameters) => watchContractEvent( this.getConfig(configKey), params @@ -245,7 +228,6 @@ export class JSWagmiCore { ) writeContract = this.#guard( - 'writeContract', async (configKey: string, params: WriteContractParameters) => writeContract( this.getConfig(configKey), params @@ -253,7 +235,6 @@ export class JSWagmiCore { ) estimateGas = this.#guard( - 'estimateGas', async (configKey: string, params: EstimateGasParameters) => estimateGas( this.getConfig(configKey), params @@ -261,7 +242,6 @@ export class JSWagmiCore { ) estimateFeesPerGas = this.#guard( - 'estimateFeesPerGas', async (configKey: string, params: EstimateFeesPerGasParameters) => estimateFeesPerGas( this.getConfig(configKey), params @@ -269,7 +249,6 @@ export class JSWagmiCore { ) estimateMaxPriorityFeePerGas = this.#guard( - 'estimateMaxPriorityFeePerGas', async (configKey: string, params: EstimateMaxPriorityFeePerGasParameters) => estimateMaxPriorityFeePerGas( this.getConfig(configKey), params @@ -278,7 +257,6 @@ export class JSWagmiCore { ) getBytecode = this.#guard( - 'getBytecode', async (configKey: string, params: GetBytecodeParameters) => getBytecode( this.getConfig(configKey), params @@ -286,7 +264,6 @@ export class JSWagmiCore { ) disconnect = this.#guard( - 'disconnect', async (configKey: string, params: DisconnectParameters) => disconnect( this.getConfig(configKey), params @@ -294,7 +271,6 @@ export class JSWagmiCore { ) waitForTransactionReceipt = this.#guard( - 'waitForTransactionReceipt', async (configKey: string, params: WaitForTransactionReceiptParameters) => waitForTransactionReceipt( this.getConfig(configKey), params @@ -302,7 +278,6 @@ export class JSWagmiCore { ) getFeeHistory = this.#guard( - 'getFeeHistory', async (configKey: string, params: GetFeeHistoryParameters) => getFeeHistory( this.getConfig(configKey), params @@ -310,42 +285,36 @@ export class JSWagmiCore { ) watchConnections = this.#guard( - 'watchConnections', async (configKey: string, params: WatchConnectionsParameters) => watchConnections( this.getConfig(configKey), params ) ) switchChain = this.#guard( - 'switchChain', async (configKey: string, params: SwitchChainParameters) => switchChain( this.getConfig(configKey), params ) ) switchAccount = this.#guard( - 'switchAccount', async (configKey: string, params: SwitchAccountParameters) => switchAccount( this.getConfig(configKey), params ) ) watchAccount = this.#guard( - 'watchAccount', async (configKey: string, params: WatchAccountParameters) => watchAccount( this.getConfig(configKey), params ) ) verifyMessage = this.#guard( - 'verifyMessage', async (configKey: string, params: VerifyMessageParameters) => verifyMessage( this.getConfig(configKey), params ) ) getWalletClient = this.#guard( - 'getWalletClient', async (configKey: string, params: GetWalletClientParameters) => { return await getWalletClient( this.getConfig(configKey), @@ -354,7 +323,6 @@ export class JSWagmiCore { } ) deployContract = this.#guard( - 'deployContract', async (configKey: string, params: DeployContractParameters) => { return await deployContract( this.getConfig(configKey), @@ -363,7 +331,6 @@ export class JSWagmiCore { } ) watchAsset = this.#guard( - 'watchAsset', async (configKey: string, params: WatchAssetParameters) => { return await watchAsset( this.getConfig(configKey), @@ -386,14 +353,7 @@ export class JSWagmiCore { } - #guard(actionName: string, action: (configKey: string, params: ParamsT) => Promise): (configKey: string, params: ParamsT) => Promise { - return async (configKey: string, params: ParamsT) => { - try { - return await action(configKey, illegalNullsToUndefined(params)) - } catch (error) { - console.error(`Error ${actionName} (${JSON.stringify(params)}) : `, error) - throw error - } - } + #guard(action: (configKey: string, params: ParamsT) => Promise): (configKey: string, params: ParamsT) => Promise { + return async (configKey: string, params: ParamsT) => await action(configKey, illegalNullsToUndefined(params)) } } From fbab652d8aba4ad2d8395a520c43411caad3d90d Mon Sep 17 00:00:00 2001 From: Chralu Date: Tue, 1 Oct 2024 13:52:39 +0200 Subject: [PATCH 2/3] fix: :bug: Accepts errors with unknown error name. Adds a way to find an error among nested errors. --- lib/src/js/models/error.js.dart | 9 ++++----- lib/src/js/wagmi.js.dart | 20 ++++++++++---------- lib/src/models/error.dart | 13 +++++++++++-- 3 files changed, 25 insertions(+), 17 deletions(-) diff --git a/lib/src/js/models/error.js.dart b/lib/src/js/models/error.js.dart index 7858d4e..fdc2245 100644 --- a/lib/src/js/models/error.js.dart +++ b/lib/src/js/models/error.js.dart @@ -1,10 +1,8 @@ part of '../wagmi.js.dart'; /// Generic JSError -extension type JSError._(JSObject _) implements JSObject {} - -/// Generic indexed DB/Javascript error. -extension JSErrorExt on JSError { +@JS() +extension type JSError._(JSObject _) implements JSObject { /// Get the error message external String? get message; @@ -19,7 +17,8 @@ extension JSErrorExt on JSError { WagmiError get toDart => WagmiError( message: message, - name: WagmiErrors.values.firstWhere((value) => value.name == name), + name: + WagmiErrors.values.firstWhereOrNull((value) => value.name == name), metaMessages: metaMessages?.toDart .map( (message) => message.toDart, diff --git a/lib/src/js/wagmi.js.dart b/lib/src/js/wagmi.js.dart index e7185b7..6d5b892 100644 --- a/lib/src/js/wagmi.js.dart +++ b/lib/src/js/wagmi.js.dart @@ -2,12 +2,13 @@ import 'dart:js_interop'; // ignore: avoid_web_libraries_in_flutter import 'dart:js_util' as js_util; +import 'package:collection/collection.dart'; import 'package:wagmi_flutter_web/src/models/connections.dart'; -import 'package:wagmi_flutter_web/src/models/error.dart'; import 'package:wagmi_flutter_web/src/models/log.dart'; import 'package:wagmi_flutter_web/wagmi_flutter_web.dart'; part 'actions/call.js.dart'; +part 'actions/deploy_contract.js.dart'; part 'actions/disconnect.js.dart'; part 'actions/estimate_fees_per_gas.js.dart'; part 'actions/estimate_gas.js.dart'; @@ -26,30 +27,29 @@ part 'actions/get_transaction.js.dart'; part 'actions/get_transaction_confirmations.js.dart'; part 'actions/get_transaction_count.js.dart'; part 'actions/get_transaction_receipt.js.dart'; +part 'actions/get_wallet_client.js.dart'; part 'actions/read_contract.js.dart'; part 'actions/read_contracts.js.dart'; part 'actions/send_transaction.js.dart'; part 'actions/sign_message.js.dart'; -part 'actions/watch_asset.js.dart'; +part 'actions/switch_account.js.dart'; +part 'actions/switch_chain.js.dart'; +part 'actions/verify_message.js.dart'; part 'actions/wait_for_transaction_receipt.js.dart'; +part 'actions/watch_account.js.dart'; +part 'actions/watch_asset.js.dart'; part 'actions/watch_chain_id.js.dart'; +part 'actions/watch_connections.js.dart'; part 'actions/watch_contract_event.js.dart'; part 'actions/write_contract.js.dart'; -part 'actions/watch_account.js.dart'; -part 'actions/watch_connections.js.dart'; -part 'actions/get_wallet_client.js.dart'; -part 'actions/deploy_contract.js.dart'; -part 'actions/verify_message.js.dart'; -part 'actions/switch_account.js.dart'; -part 'actions/switch_chain.js.dart'; part 'models/account.js.dart'; part 'models/appkit.js.dart'; part 'models/bigint.js.dart'; part 'models/block_tag.js.dart'; part 'models/chain.js.dart'; part 'models/config.js.dart'; -part 'models/connector.js.dart'; part 'models/connections.js.dart'; +part 'models/connector.js.dart'; part 'models/data_image.js.dart'; part 'models/error.js.dart'; part 'models/format_unit.js.dart'; diff --git a/lib/src/models/error.dart b/lib/src/models/error.dart index 927a16d..aafe39b 100644 --- a/lib/src/models/error.dart +++ b/lib/src/models/error.dart @@ -2,7 +2,7 @@ class WagmiError implements Exception { WagmiError({ - required this.name, + this.name, this.message, this.metaMessages, this.shortMessage, @@ -12,7 +12,7 @@ class WagmiError implements Exception { this.details, }); - final WagmiErrors name; + final WagmiErrors? name; final String? message; final List? metaMessages; final String? shortMessage; @@ -23,6 +23,15 @@ class WagmiError implements Exception { /// The raw error object final Map? details; + /// Returns the first error or cause matching given type. + WagmiError? findError(WagmiErrors errorName) { + if (name == errorName) return this; + + if (cause == null) return null; + + return cause!.findError(errorName); + } + @override String toString() => '$name : ${message ?? shortMessage}'; } From 282e7c7708315e349c928a1d5e7125371adde5f0 Mon Sep 17 00:00:00 2001 From: redDwarf03 Date: Tue, 1 Oct 2024 22:23:21 +0200 Subject: [PATCH 3/3] chore: :memo: Documentation of Error management --- README.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/README.md b/README.md index bf896f7..99645a3 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,29 @@ wagmi.Web3Modal.init( wagmi.Web3Modal.open(); ``` +### Error management + +Errors from wagmi can be handled using the `WagmiError` object. +Wagmi provides an error stack through the `cause` property. +The `findError` method with the error type allows checking if a specific error is in the error stack. + +The different exceptions are available in the `WagmiErrors` enumeration + +```dart + try { + final transactionHash = await wagmi.Core.writeContract( + parameters, + ); + } on wagmi.WagmiError catch (e, stackTrace) { + if (e.findError(wagmi.WagmiErrors.UserRejectedRequestError) != null) { + throw Exception('userRejected'); + } + if (e.findError(wagmi.WagmiErrors.InsufficientFundsError) != null) { + throw Exception('insufficientFunds'); + } + throw Exception('${e.shortMessage}'); + } +``` ## Available Actions