From 65a77f83525094558e0c706532b8015c764d7713 Mon Sep 17 00:00:00 2001 From: Patrick Niemeyer Date: Fri, 20 Sep 2024 15:31:30 -0500 Subject: [PATCH 1/5] dapps: Update WalletConnect libs. Improvements to the staking dapp. --- gui-orchid/lib/api/orchid_crypto.dart | 6 + gui-orchid/lib/api/orchid_eth/abi_encode.dart | 24 + gui-orchid/lib/api/orchid_eth/token_type.dart | 2 +- .../orchid_labeled_token_value_field.dart | 4 +- web-ethereum/account_dapp/flutter_web3.sh | 2 + .../dapp/orchid/dapp_transaction_list.dart | 71 +- .../dapp/orchid/dapp_wallet_info_panel.dart | 7 +- .../lib/dapp/orchid_web3/orchid_erc20.dart | 13 +- .../dapp/orchid_web3/orchid_web3_context.dart | 13 +- .../dapp/orchid_web3/v0/orchid_web3_v0.dart | 7 +- .../dapp/preferences/dapp_transaction.dart | 32 +- .../preferences/user_preferences_dapp.dart | 12 +- .../lib/pages/dapp_home_base.dart | 56 + .../lib/pages/dapp_home_header.dart | 53 +- .../account_dapp/walletconnect/README.txt | 8 +- .../walletconnect/package-lock.json | 3189 +++++++++-------- .../account_dapp/walletconnect/package.json | 7 +- .../stake_dapp/lib/pages/stake_dapp_home.dart | 208 +- .../lib/pages/tabs/add_stake_panel.dart | 67 +- .../lib/pages/tabs/pull_stake_panel.dart | 5 +- .../stake_dapp/lib/pages/tabs/stake_tabs.dart | 3 + .../lib/stake_dapp/orchid_web3_stake_v0.dart | 127 +- 22 files changed, 2223 insertions(+), 1693 deletions(-) diff --git a/gui-orchid/lib/api/orchid_crypto.dart b/gui-orchid/lib/api/orchid_crypto.dart index 154db4de9..c56e24bd2 100644 --- a/gui-orchid/lib/api/orchid_crypto.dart +++ b/gui-orchid/lib/api/orchid_crypto.dart @@ -1,3 +1,4 @@ +import 'package:orchid/api/orchid_eth/abi_encode.dart'; import 'package:orchid/api/orchid_log.dart'; import 'dart:math'; import 'dart:typed_data'; @@ -166,6 +167,11 @@ class EthereumAddress { final BigInt value; + // Convert to Uint8List + Uint8List get bytes { + return value.toBytesUint160(); + } + EthereumAddress(BigInt value) : // Allow the string parser to validate. diff --git a/gui-orchid/lib/api/orchid_eth/abi_encode.dart b/gui-orchid/lib/api/orchid_eth/abi_encode.dart index 622eae887..b51f5980b 100644 --- a/gui-orchid/lib/api/orchid_eth/abi_encode.dart +++ b/gui-orchid/lib/api/orchid_eth/abi_encode.dart @@ -111,4 +111,28 @@ extension BigIntExtension on BigInt { } return result; } + + Uint8List toBytesUint160() { + final number = this; + // Assert the number is non-negative and fits within 160 bits + assert(number >= BigInt.zero && number < (BigInt.one << 160), + 'Number must be non-negative and less than 2^160'); + var byteData = number.toRadixString(16).padLeft(40, '0'); // Ensure 20 bytes + var result = Uint8List(20); + for (int i = 0; i < byteData.length; i += 2) { + var byteString = byteData.substring(i, i + 2); + var byteValue = int.parse(byteString, radix: 16); + result[i ~/ 2] = byteValue; + } + return result; + } + + // For a BigInt representing an Ethereum Address (20 bytes) + Uint8List toAddress() { + return toBytesUint160(); + } +} + +Uint8List tie(Uint8List a, Uint8List b) { + return Uint8List.fromList(a + b); } diff --git a/gui-orchid/lib/api/orchid_eth/token_type.dart b/gui-orchid/lib/api/orchid_eth/token_type.dart index 3f05f74dc..809185b86 100644 --- a/gui-orchid/lib/api/orchid_eth/token_type.dart +++ b/gui-orchid/lib/api/orchid_eth/token_type.dart @@ -139,7 +139,7 @@ class Token { int? minPrecision, bool showPrecisionIndicator = false, }) { - print("XXX: format decimal value: $decimalValue"); + // print("XXX: format decimal value: $decimalValue"); return units.formatDecimal(decimalValue, locale: locale, precision: precision, diff --git a/gui-orchid/lib/orchid/field/orchid_labeled_token_value_field.dart b/gui-orchid/lib/orchid/field/orchid_labeled_token_value_field.dart index b454b2a8d..258acaa1f 100644 --- a/gui-orchid/lib/orchid/field/orchid_labeled_token_value_field.dart +++ b/gui-orchid/lib/orchid/field/orchid_labeled_token_value_field.dart @@ -24,6 +24,7 @@ class OrchidLabeledTokenValueField extends StatelessWidget { final Widget? trailing; final Widget? bottom; final Widget? bottomBanner; + final Color? backgroundColor; // TODO: enhance this to include a message final bool error; @@ -43,6 +44,7 @@ class OrchidLabeledTokenValueField extends StatelessWidget { this.bottom, this.bottomBanner, this.error = false, + this.backgroundColor, }) : super(key: key); @override @@ -55,7 +57,7 @@ class OrchidLabeledTokenValueField extends StatelessWidget { child: Column( children: [ Container( - color: OrchidColors.dark_background_2, + color: backgroundColor ?? OrchidColors.dark_background_2, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ diff --git a/web-ethereum/account_dapp/flutter_web3.sh b/web-ethereum/account_dapp/flutter_web3.sh index 98654b1ba..2ba36fe32 100755 --- a/web-ethereum/account_dapp/flutter_web3.sh +++ b/web-ethereum/account_dapp/flutter_web3.sh @@ -3,6 +3,8 @@ cd $(dirname "$0") # Create our patched version of the flutter_web3 package. +# See the following issue for an explanation of the patch: +# "Support for Wallet Connect 2.x." @patniemeyer # https://github.com/y-pakorn/flutter_web3/issues/56 # if [ -d flutter_web3 ] diff --git a/web-ethereum/account_dapp/lib/dapp/orchid/dapp_transaction_list.dart b/web-ethereum/account_dapp/lib/dapp/orchid/dapp_transaction_list.dart index c6945cd49..bad0fd2e4 100644 --- a/web-ethereum/account_dapp/lib/dapp/orchid/dapp_transaction_list.dart +++ b/web-ethereum/account_dapp/lib/dapp/orchid/dapp_transaction_list.dart @@ -1,9 +1,13 @@ import 'dart:math'; +import 'package:orchid/api/orchid_eth/chains.dart'; +import 'package:orchid/dapp/preferences/dapp_transaction.dart'; import 'package:orchid/orchid/orchid.dart'; import 'package:orchid/dapp/orchid_web3/orchid_web3_context.dart'; import 'package:orchid/dapp/preferences/user_preferences_dapp.dart'; import 'package:orchid/dapp/orchid/transaction_status_panel.dart'; +// Show pending and recent transactions in the dapp. +// The side scrolling page view of transaction status panels. class DappTransactionList extends StatefulWidget { final OrchidWeb3Context? web3Context; final VoidCallback refreshUserData; @@ -35,13 +39,34 @@ class _DappTransactionListState extends State { txs = (txs ?? []) .where((tx) => tx.chainId == widget.web3Context?.chain.chainId) .toList(); - if (txs.isEmpty) { - return Container(); - } // REMOVE: TESTING // txs = txs + txs + txs + txs; + // REMOVE: TESTING + // txs += [ + // DappTransaction( + // transactionHash: "0x10942af0a19066e4c27b1eb6c8a7edd2e7f76a68ac147384786ee6b5fd02e78f", + // chainId: Chains.GNOSIS_CHAINID, + // type: DappTransactionType.addFunds, + // subtype: "approve", + // series_index: 1, + // series_total: 2, + // ), + // DappTransaction( + // transactionHash: "0x10942af0a19066e4c27b1eb6c8a7edd2e7f76a68ac147384786ee6b5fd02e78f", + // chainId: Chains.GNOSIS_CHAINID, + // type: DappTransactionType.addFunds, + // subtype: "push", + // series_index: 2, + // series_total: 2, + // ), + // ]; + + if (txs.isEmpty) { + return Container(); + } + var txWidgets = txs .map((tx) => TransactionStatusPanel( context: widget.web3Context, @@ -63,17 +88,39 @@ class _DappTransactionListState extends State { duration: millis(400), child: SizedBox( height: 180, - child: PageView.builder( - itemCount: txWidgets.length, - controller: PageController(viewportFraction: viewportFraction), - onPageChanged: (int index) => - setState(() => _txStatusIndex = index), - itemBuilder: (_, i) { - return AnimatedScale( + child: ShaderMask( + shaderCallback: (Rect bounds) { + return LinearGradient( + begin: Alignment.centerLeft, + end: Alignment.centerRight, + colors: [ + Colors.transparent, // Start fully transparent (left side) + Colors.black, // Fully visible in the middle + Colors.black, // Fully visible in the middle + Colors.transparent, // End fully transparent (right side) + ], + stops: [ + 0.0, + 0.2, + 0.8, + 1.0 + ], // Adjust stops to control fade amount + ).createShader(bounds); + }, + blendMode: BlendMode.dstIn, // Use blend mode for transparency + child: PageView.builder( + itemCount: txWidgets.length, + controller: PageController(viewportFraction: viewportFraction), + onPageChanged: (int index) => + setState(() => _txStatusIndex = index), + itemBuilder: (_, i) { + return AnimatedScale( duration: millis(300), scale: i == _txStatusIndex ? 1 : 0.9, - child: Center(child: txWidgets[i])); - }, + child: Center(child: txWidgets[i]), + ); + }, + ), ), ), ); diff --git a/web-ethereum/account_dapp/lib/dapp/orchid/dapp_wallet_info_panel.dart b/web-ethereum/account_dapp/lib/dapp/orchid/dapp_wallet_info_panel.dart index 622da47ec..0faa274ba 100644 --- a/web-ethereum/account_dapp/lib/dapp/orchid/dapp_wallet_info_panel.dart +++ b/web-ethereum/account_dapp/lib/dapp/orchid/dapp_wallet_info_panel.dart @@ -30,13 +30,18 @@ class DappWalletInfoPanel extends StatelessWidget { } Widget _buildContent(BuildContext context) { + final isWalletConnect = web3Context?.walletConnectProvider != null; final link = web3Context?.chain.explorerUrl; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ - Text(context.s.connectedWithMetamask, style: _textStyle), + Text( + isWalletConnect + ? "Connected with WalletConnect" // TODO: Localize + : context.s.connectedWithMetamask, + style: _textStyle), ], ).height(26), _buildWalletAddressRow().top(16), diff --git a/web-ethereum/account_dapp/lib/dapp/orchid_web3/orchid_erc20.dart b/web-ethereum/account_dapp/lib/dapp/orchid_web3/orchid_erc20.dart index daba0f8ba..281a36ef4 100644 --- a/web-ethereum/account_dapp/lib/dapp/orchid_web3/orchid_erc20.dart +++ b/web-ethereum/account_dapp/lib/dapp/orchid_web3/orchid_erc20.dart @@ -46,7 +46,7 @@ class OrchidERC20 { // This mitigates the potential for rounding errors in calculated amounts. amount = Token.min(amount, walletBalance); - log('XXX: do approve: ${[spender.toString(), amount.intValue.toString()]}'); + log('approveERC20: do approve: ${[spender.toString(), amount.intValue.toString()]}'); // approve(address spender, uint256 amount) external returns (bool) var contract = _contract.connect(context.web3.getSigner()); @@ -71,3 +71,14 @@ class OrchidERC20 { 'function transferFrom(address sender, address recipient, uint256 amount) external returns (bool)', ]; } + +// For a transaction that uses an ERC20 token, the transaction may require an approval +class ERC20PayableTransactionCallbacks { + final Future Function(String approvalHash) onApproval; + final Future Function(String txHash) onTransaction; + + ERC20PayableTransactionCallbacks({ + required this.onApproval, + required this.onTransaction, + }); +} \ No newline at end of file diff --git a/web-ethereum/account_dapp/lib/dapp/orchid_web3/orchid_web3_context.dart b/web-ethereum/account_dapp/lib/dapp/orchid_web3/orchid_web3_context.dart index 7e0e4baa3..4ca92de17 100644 --- a/web-ethereum/account_dapp/lib/dapp/orchid_web3/orchid_web3_context.dart +++ b/web-ethereum/account_dapp/lib/dapp/orchid_web3/orchid_web3_context.dart @@ -245,10 +245,17 @@ class OrchidWeb3Context { void removeAllListeners() { //log("XXX: context ($id) removing listeners"); - ethereumProvider?.removeAllListeners(); - walletConnectProvider?.removeAllListeners(); + try { + ethereumProvider?.removeAllListeners(); + } catch (err) { + log("XXX: exception in removing ethereum listeners: $err"); + } + try { + walletConnectProvider?.removeAllListeners(); + } catch (err) { + log("XXX: exception in removing wallet connect listeners: $err"); + } _walletUpdateListener = null; - //log("XXX: after removing listeners: ${ethereumProvider.listenerCount()}"); } void disconnect() async { diff --git a/web-ethereum/account_dapp/lib/dapp/orchid_web3/v0/orchid_web3_v0.dart b/web-ethereum/account_dapp/lib/dapp/orchid_web3/v0/orchid_web3_v0.dart index ac02e5800..3a01d77e9 100644 --- a/web-ethereum/account_dapp/lib/dapp/orchid_web3/v0/orchid_web3_v0.dart +++ b/web-ethereum/account_dapp/lib/dapp/orchid_web3/v0/orchid_web3_v0.dart @@ -53,7 +53,7 @@ class OrchidWeb3V0 { // Don't attempt to add more than the wallet balance. // This mitigates the potential for rounding errors in calculated amounts. var totalOXT = Token.min(addBalance.add(addEscrow), walletBalance); - log("Add funds signer: $signer, amount: ${totalOXT.subtract(addEscrow)}, escrow: $addEscrow"); + log("Add funds: signer: $signer, amount: ${totalOXT.subtract(addEscrow)}, escrow: $addEscrow"); List txHashes = []; @@ -64,18 +64,19 @@ class OrchidWeb3V0 { spender: OrchidContractV0.lotteryContractAddressV0, ); if (oxtAllowance < totalOXT) { - log("XXX: oxtAllowance increase required: $oxtAllowance < $totalOXT"); + log("Add funds: oxtAllowance increase required: $oxtAllowance < $totalOXT"); var approveTxHash = await _oxt.approveERC20( owner: wallet.address!, spender: OrchidContractV0.lotteryContractAddressV0, amount: totalOXT); txHashes.add(approveTxHash); } else { - log("XXX: oxtAllowance sufficient: $oxtAllowance"); + log("Add funds: oxtAllowance already sufficient: $oxtAllowance"); } // Do the add call var contract = _lotteryContract.connect(context.web3.getSigner()); + log("Add funds: do push, totalOXT: $totalOXT, addEscrow: $addEscrow"); TransactionResponse tx = await contract.send( 'push', [ diff --git a/web-ethereum/account_dapp/lib/dapp/preferences/dapp_transaction.dart b/web-ethereum/account_dapp/lib/dapp/preferences/dapp_transaction.dart index ef7c46675..56bca0e78 100644 --- a/web-ethereum/account_dapp/lib/dapp/preferences/dapp_transaction.dart +++ b/web-ethereum/account_dapp/lib/dapp/preferences/dapp_transaction.dart @@ -22,21 +22,39 @@ class DappTransaction { final int chainId; final DappTransactionType? type; + // An optional subtype for components of a composite transaction. + // e.g. ERC20 transfer requiring an approval and a push. + final String? subtype; + + // If this is part of a series of transactions, the index and total count. + final int? series_index; + final int? series_total; + DappTransaction({ this.transactionHash, required this.chainId, this.type, + this.subtype, + this.series_index = null, + this.series_total = null, }); DappTransaction.fromJson(Map json) : this.transactionHash = json['tx'], this.chainId = json['chainId'], - this.type = toTransactionType(json['type']); // handles null + // toTransactionType() handles nulls + this.type = toTransactionType(json['type']), + this.subtype = json['subtype'], + this.series_index = json['series_index'], + this.series_total = json['series_total']; Map toJson() => { 'tx': transactionHash, 'chainId': chainId, 'type': (type ?? DappTransactionType.unknown).name, + 'subtype': subtype, + 'series_index': series_index, + 'series_total': series_total, }; static List fromList(List list) { @@ -56,7 +74,15 @@ class DappTransaction { } String description(BuildContext context) { - return descriptionForType(context, type); + String text = descriptionForType(context, type); + if (subtype != null) { + if (series_index != null && series_total != null) { + text += " ($subtype, $series_index/$series_total)"; + } else { + text += " ($subtype)"; + } + } + return text; } static String descriptionForType( @@ -82,7 +108,7 @@ class DappTransaction { case DappTransactionType.accountChanges: return s.accountChanges; case DappTransactionType.pullFunds: - return context.s.withdrawFunds2; + return "Pull Funds"; case DappTransactionType.moveLocation: return "Move Location"; case DappTransactionType.pokeLocation: diff --git a/web-ethereum/account_dapp/lib/dapp/preferences/user_preferences_dapp.dart b/web-ethereum/account_dapp/lib/dapp/preferences/user_preferences_dapp.dart index 77752764a..675549cea 100644 --- a/web-ethereum/account_dapp/lib/dapp/preferences/user_preferences_dapp.dart +++ b/web-ethereum/account_dapp/lib/dapp/preferences/user_preferences_dapp.dart @@ -28,18 +28,18 @@ class UserPreferencesDapp { return _setTransactions(txs); }); - void addTransaction(DappTransaction tx) { - transactions.set(transactions.get()! + [tx]); + Future addTransaction(DappTransaction tx) async { + await transactions.set(transactions.get()! + [tx]); } - void addTransactions(Iterable txs) { - transactions.set(transactions.get()! + txs.toList()); + Future addTransactions(Iterable txs) async { + await transactions.set(transactions.get()! + txs.toList()); } - void removeTransaction(String txHash) { + Future removeTransaction(String txHash) async { var list = transactions.get()!; list.removeWhere((tx) => tx.transactionHash == txHash); - transactions.set(list); + await transactions.set(list); } static List _getTransactions() { diff --git a/web-ethereum/account_dapp/lib/pages/dapp_home_base.dart b/web-ethereum/account_dapp/lib/pages/dapp_home_base.dart index a445601c2..bbf7ec0bc 100644 --- a/web-ethereum/account_dapp/lib/pages/dapp_home_base.dart +++ b/web-ethereum/account_dapp/lib/pages/dapp_home_base.dart @@ -1,3 +1,4 @@ +import 'package:orchid/api/orchid_eth/chains.dart'; import 'package:orchid/dapp/orchid_web3/v1/orchid_contract_deployment_v1.dart'; import 'package:orchid/orchid/orchid.dart'; import 'package:flutter_web3/flutter_web3.dart'; @@ -5,10 +6,16 @@ import 'package:orchid/api/orchid_eth/v1/orchid_eth_v1.dart'; import 'package:orchid/dapp/orchid_web3/orchid_web3_context.dart'; import 'package:orchid/common/app_dialogs.dart'; import 'package:orchid/dapp/orchid_web3/v1/orchid_eth_v1_web3.dart'; +import 'package:orchid/dapp/orchid_web3/wallet_connect_eth_provider.dart'; +// Base class containing web3 connection state for Orchid dapps class DappHomeStateBase extends State { OrchidWeb3Context? web3Context; + bool get isConnected => web3Context != null; + + bool get isNotConnected => !isConnected; + /// The contract version defaulted or selected by the user. /// Null if no contacts are available. int? _contractVersionSelectedValue; @@ -33,6 +40,17 @@ class DappHomeStateBase extends State { /// If the user has previously connected accounts reconnect without requiring /// the user to hit the connect button. Future checkForExistingConnectedAccounts() async { + await checkForExistingWalletConnectAccounts(); + if (isNotConnected) { + await checkForExistingConnectedEthereumAccounts(); + } + } + + Future checkForExistingWalletConnectAccounts() async { + log('checkForExistingWalletConnectAccounts TBD'); + } + + Future checkForExistingConnectedEthereumAccounts() async { try { var accounts = await ethereum?.getAccounts() ?? []; if (accounts.isNotEmpty) { @@ -95,6 +113,44 @@ class DappHomeStateBase extends State { setNewContext(web3); } + Future connectWalletConnect(Chain? userSelectedChain) async { + log("XXX: connectWalletConnect"); + // const walletConnectProjectId = 'bd5be579e9cae68defff05a6fa7b0049'; // test + const walletConnectProjectId = 'afe2e392884aefdae72d4babb5482ced'; + + final chain = userSelectedChain ?? Chains.Ethereum; + var wc = await WalletConnectEthereumProvider.init( + projectId: walletConnectProjectId, + rpcMap: { + chain.chainId: chain.providerUrl, + }, + chains: [chain.chainId], + // This does not seem to work, otherwise we could simply connect to all + // of our chains as optional and allow more straightforward switching. + // optionalChains: [Chains.Gnosis.chainId, 31411234], + showQrModal: true, + ); + // consoleLog(wc.impl); + try { + log('Before wc connect'); + await wc.connect(); + log('After wc connect'); + } catch (err) { + log('wc connect/init, err = $err'); + return; + } + if (!wc.connected) { + AppDialogs.showAppDialog( + context: context, + title: s.error, + bodyText: s.failedToConnectToWalletconnect); + return; + } + + var web3 = await OrchidWeb3Context.fromWalletConnect(wc); + setNewContext(web3); + } + // Init a new context, disconnecting any old context and registering listeners void setNewContext(OrchidWeb3Context? newWeb3Context) async { log('set new context: $newWeb3Context'); diff --git a/web-ethereum/account_dapp/lib/pages/dapp_home_header.dart b/web-ethereum/account_dapp/lib/pages/dapp_home_header.dart index 80fcc2641..fd30a9b88 100644 --- a/web-ethereum/account_dapp/lib/pages/dapp_home_header.dart +++ b/web-ethereum/account_dapp/lib/pages/dapp_home_header.dart @@ -5,7 +5,6 @@ import 'package:orchid/dapp/orchid_web3/orchid_web3_context.dart'; import 'package:orchid/common/app_dialogs.dart'; import 'package:orchid/common/app_sizes.dart'; import 'package:orchid/pages/settings/logging_page.dart'; -import 'package:orchid/dapp/orchid_web3/wallet_connect_eth_provider.dart'; import 'package:orchid/util/gestures.dart'; import '../orchid/menu/orchid_chain_selector_menu.dart'; import '../dapp/orchid/dapp_settings_button.dart'; @@ -24,18 +23,18 @@ class DappHomeHeader extends StatefulWidget { final void Function(int? version)? _selectContractVersion; final Future Function() _disconnect; final VoidCallback connectEthereum; - final void Function(OrchidWeb3Context? web3Context) setNewContext; + final Future Function(Chain? userSelectedChain) connectWalletConnect; final bool showChainSelector; const DappHomeHeader({ super.key, required OrchidWeb3Context? web3Context, - required this.setNewContext, Set? contractVersionsAvailable, int? contractVersionSelected, void Function(int? version)? selectContractVersion, VoidCallback? deployContract, required this.connectEthereum, + required this.connectWalletConnect, required Future Function() disconnect, this.showChainSelector = true, }) : this._web3Context = web3Context, @@ -163,8 +162,7 @@ class _DappHomeHeaderState extends State { selectedTextStyle: selectedTextStyle, backgroundColor: backgroundColor, connectMetamask: widget.connectEthereum, - connectWalletConnect: () => - _uiGuardConnectingState(_connectWalletConnect), + connectWalletConnect: () => _guardConnectWalletConnect(_userDefaultChainSelection), ), ), ); @@ -230,11 +228,16 @@ class _DappHomeHeaderState extends State { setState(() { _userDefaultChainSelection = chain; }); - await _uiGuardConnectingState(_connectWalletConnect); + await _guardConnectWalletConnect(chain); }); }); } + Future _guardConnectWalletConnect(Chain? chain) async { + log("XXX: guardConnectWalletConnect: chain=$chain"); + await _uiGuardConnectingState(() => widget.connectWalletConnect(chain)); + } + void _addChain(Chain chain) async { if (widget._web3Context == null) { throw Exception("Cannot add chain without a web3 context"); @@ -281,42 +284,4 @@ class _DappHomeHeaderState extends State { }); } } - - Future _connectWalletConnect() async { - log("XXX: connectWalletConnect"); - // const walletConnectProjectId = 'bd5be579e9cae68defff05a6fa7b0049'; // test - const walletConnectProjectId = 'afe2e392884aefdae72d4babb5482ced'; - - final chain = _userDefaultChainSelection ?? Chains.Ethereum; - var wc = await WalletConnectEthereumProvider.init( - projectId: walletConnectProjectId, - rpcMap: { - chain.chainId: chain.providerUrl, - }, - chains: [chain.chainId], - // This does not seem to work, otherwise we could simply connect to all - // of our chains as optional and allow more straightforward switching. - // optionalChains: [Chains.Gnosis.chainId, 31411234], - showQrModal: true, - ); - // consoleLog(wc.impl); - try { - log('Before wc connect'); - await wc.connect(); - log('After wc connect'); - } catch (err) { - log('wc connect/init, err = $err'); - return; - } - if (!wc.connected) { - AppDialogs.showAppDialog( - context: context, - title: s.error, - bodyText: s.failedToConnectToWalletconnect); - return; - } - - var web3 = await OrchidWeb3Context.fromWalletConnect(wc); - widget.setNewContext(web3); - } } diff --git a/web-ethereum/account_dapp/walletconnect/README.txt b/web-ethereum/account_dapp/walletconnect/README.txt index e50a24239..f7bf27cb0 100644 --- a/web-ethereum/account_dapp/walletconnect/README.txt +++ b/web-ethereum/account_dapp/walletconnect/README.txt @@ -1,4 +1,8 @@ -This is a minimal JS entry point that exports the WalletConnectEthereumProvider from @walletconnect/ethereum-provider into the window so that Flutter can interact with it. +This is a minimal JS entry point that exports the WalletConnectEthereumProvider +from the @walletconnect/ethereum-provider package into the environment so that +Flutter can interact with it through JS interop. + +The alternative might be to use a published UMD package like the one used with +Wallet Connect v1. However, I was unable to find one. -The alternative might be to use a published UMD package like the one we used previously for Wallet Connect v1. However I was unable to find one. diff --git a/web-ethereum/account_dapp/walletconnect/package-lock.json b/web-ethereum/account_dapp/walletconnect/package-lock.json index 523c3c70c..058c3b1f6 100644 --- a/web-ethereum/account_dapp/walletconnect/package-lock.json +++ b/web-ethereum/account_dapp/walletconnect/package-lock.json @@ -8,23 +8,11 @@ "name": "ethprovider", "version": "1.0.0", "dependencies": { - "@walletconnect/ethereum-provider": "^2.4.7", - "@web3modal/standalone": "^2.1.3" + "@walletconnect/ethereum-provider": "^2.16.1" }, "devDependencies": { - "webpack": "^5.77.0", - "webpack-cli": "^5.0.1" - } - }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" + "webpack": "^5.94.0", + "webpack-cli": "^5.1.4" } }, "node_modules/@discoveryjs/json-ext": { @@ -37,229 +25,301 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", "dev": true, + "license": "MIT", "dependencies": { - "@jridgewell/set-array": "^1.0.1", + "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/source-map": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", - "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", "dev": true, + "license": "MIT", "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "node_modules/@json-rpc-tools/provider": { - "version": "1.7.6", - "resolved": "https://registry.npmjs.org/@json-rpc-tools/provider/-/provider-1.7.6.tgz", - "integrity": "sha512-z7D3xvJ33UfCGv77n40lbzOYjZKVM3k2+5cV7xS8G6SCvKTzMkhkUYuD/qzQUNT4cG/lv0e9mRToweEEVLVVmA==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", - "dependencies": { - "@json-rpc-tools/utils": "^1.7.6", - "axios": "^0.21.0", - "safe-json-utils": "^1.1.1", - "ws": "^7.4.0" - } - }, - "node_modules/@json-rpc-tools/types": { - "version": "1.7.6", - "resolved": "https://registry.npmjs.org/@json-rpc-tools/types/-/types-1.7.6.tgz", - "integrity": "sha512-nDSqmyRNEqEK9TZHtM15uNnDljczhCUdBmRhpNZ95bIPKEDQ+nTDmGMFd2lLin3upc5h2VVVd9tkTDdbXUhDIQ==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", - "dependencies": { - "keyvaluestorage-interface": "^1.0.0" - } - }, - "node_modules/@json-rpc-tools/utils": { - "version": "1.7.6", - "resolved": "https://registry.npmjs.org/@json-rpc-tools/utils/-/utils-1.7.6.tgz", - "integrity": "sha512-HjA8x/U/Q78HRRe19yh8HVKoZ+Iaoo3YZjakJYxR+rw52NHo6jM+VE9b8+7ygkCFXl/EHID5wh/MkXaE/jGyYw==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", "dependencies": { - "@json-rpc-tools/types": "^1.7.6", - "@pedrouid/environment": "^1.0.1" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, "node_modules/@lit-labs/ssr-dom-shim": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.1.0.tgz", - "integrity": "sha512-92uQ5ARf7UXYrzaFcAX3T2rTvaS9Z1//ukV+DqjACM4c8s0ZBQd7ayJU5Dh2AFLD/Ayuyz4uMmxQec8q3U4Ong==" + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.2.1.tgz", + "integrity": "sha512-wx4aBmgeGvFmOKucFKY+8VFJSYZxs9poN3SDNQFF6lT6NrQUnHiPB2PWz2sc4ieEcAaYYzN+1uWahEeTq2aRIQ==", + "license": "BSD-3-Clause" }, "node_modules/@lit/reactive-element": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-1.6.1.tgz", - "integrity": "sha512-va15kYZr7KZNNPZdxONGQzpUr+4sxVu7V/VG7a8mRfPPXUyhEYj5RzXCQmGrlP3tAh0L3HHm5AjBMFYRqlM9SA==", + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-1.6.3.tgz", + "integrity": "sha512-QuTgnG52Poic7uM1AN5yJ09QMe0O28e10XzSvWDz02TJiiKee4stsiownEIadWm8nYzyDAyT+gKzUoZmiWQtsQ==", + "license": "BSD-3-Clause", "dependencies": { "@lit-labs/ssr-dom-shim": "^1.0.0" } }, "node_modules/@motionone/animation": { - "version": "10.15.1", - "resolved": "https://registry.npmjs.org/@motionone/animation/-/animation-10.15.1.tgz", - "integrity": "sha512-mZcJxLjHor+bhcPuIFErMDNyrdb2vJur8lSfMCsuCB4UyV8ILZLvK+t+pg56erv8ud9xQGK/1OGPt10agPrCyQ==", - "dependencies": { - "@motionone/easing": "^10.15.1", - "@motionone/types": "^10.15.1", - "@motionone/utils": "^10.15.1", + "version": "10.18.0", + "resolved": "https://registry.npmjs.org/@motionone/animation/-/animation-10.18.0.tgz", + "integrity": "sha512-9z2p5GFGCm0gBsZbi8rVMOAJCtw1WqBTIPw3ozk06gDvZInBPIsQcHgYogEJ4yuHJ+akuW8g1SEIOpTOvYs8hw==", + "license": "MIT", + "dependencies": { + "@motionone/easing": "^10.18.0", + "@motionone/types": "^10.17.1", + "@motionone/utils": "^10.18.0", "tslib": "^2.3.1" } }, "node_modules/@motionone/animation/node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "license": "0BSD" }, "node_modules/@motionone/dom": { - "version": "10.15.5", - "resolved": "https://registry.npmjs.org/@motionone/dom/-/dom-10.15.5.tgz", - "integrity": "sha512-Xc5avlgyh3xukU9tydh9+8mB8+2zAq+WlLsC3eEIp7Ax7DnXgY7Bj/iv0a4X2R9z9ZFZiaXK3BO0xMYHKbAAdA==", - "dependencies": { - "@motionone/animation": "^10.15.1", - "@motionone/generators": "^10.15.1", - "@motionone/types": "^10.15.1", - "@motionone/utils": "^10.15.1", + "version": "10.18.0", + "resolved": "https://registry.npmjs.org/@motionone/dom/-/dom-10.18.0.tgz", + "integrity": "sha512-bKLP7E0eyO4B2UaHBBN55tnppwRnaE3KFfh3Ps9HhnAkar3Cb69kUCJY9as8LrccVYKgHA+JY5dOQqJLOPhF5A==", + "license": "MIT", + "dependencies": { + "@motionone/animation": "^10.18.0", + "@motionone/generators": "^10.18.0", + "@motionone/types": "^10.17.1", + "@motionone/utils": "^10.18.0", "hey-listen": "^1.0.8", "tslib": "^2.3.1" } }, "node_modules/@motionone/dom/node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "license": "0BSD" }, "node_modules/@motionone/easing": { - "version": "10.15.1", - "resolved": "https://registry.npmjs.org/@motionone/easing/-/easing-10.15.1.tgz", - "integrity": "sha512-6hIHBSV+ZVehf9dcKZLT7p5PEKHGhDwky2k8RKkmOvUoYP3S+dXsKupyZpqx5apjd9f+php4vXk4LuS+ADsrWw==", + "version": "10.18.0", + "resolved": "https://registry.npmjs.org/@motionone/easing/-/easing-10.18.0.tgz", + "integrity": "sha512-VcjByo7XpdLS4o9T8t99JtgxkdMcNWD3yHU/n6CLEz3bkmKDRZyYQ/wmSf6daum8ZXqfUAgFeCZSpJZIMxaCzg==", + "license": "MIT", "dependencies": { - "@motionone/utils": "^10.15.1", + "@motionone/utils": "^10.18.0", "tslib": "^2.3.1" } }, "node_modules/@motionone/easing/node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "license": "0BSD" }, "node_modules/@motionone/generators": { - "version": "10.15.1", - "resolved": "https://registry.npmjs.org/@motionone/generators/-/generators-10.15.1.tgz", - "integrity": "sha512-67HLsvHJbw6cIbLA/o+gsm7h+6D4Sn7AUrB/GPxvujse1cGZ38F5H7DzoH7PhX+sjvtDnt2IhFYF2Zp1QTMKWQ==", + "version": "10.18.0", + "resolved": "https://registry.npmjs.org/@motionone/generators/-/generators-10.18.0.tgz", + "integrity": "sha512-+qfkC2DtkDj4tHPu+AFKVfR/C30O1vYdvsGYaR13W/1cczPrrcjdvYCj0VLFuRMN+lP1xvpNZHCRNM4fBzn1jg==", + "license": "MIT", "dependencies": { - "@motionone/types": "^10.15.1", - "@motionone/utils": "^10.15.1", + "@motionone/types": "^10.17.1", + "@motionone/utils": "^10.18.0", "tslib": "^2.3.1" } }, "node_modules/@motionone/generators/node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "license": "0BSD" }, "node_modules/@motionone/svelte": { - "version": "10.15.5", - "resolved": "https://registry.npmjs.org/@motionone/svelte/-/svelte-10.15.5.tgz", - "integrity": "sha512-Xyxtgp7BlVnSBwcoFmXGHUVnpNktzeXsEifu2NJJWc7VGuxutDsBZxNdz80qvpLIC5MeBa1wh7GGegZzTm1msg==", + "version": "10.16.4", + "resolved": "https://registry.npmjs.org/@motionone/svelte/-/svelte-10.16.4.tgz", + "integrity": "sha512-zRVqk20lD1xqe+yEDZhMYgftsuHc25+9JSo+r0a0OWUJFocjSV9D/+UGhX4xgJsuwB9acPzXLr20w40VnY2PQA==", + "license": "MIT", "dependencies": { - "@motionone/dom": "^10.15.5", + "@motionone/dom": "^10.16.4", "tslib": "^2.3.1" } }, "node_modules/@motionone/svelte/node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "license": "0BSD" }, "node_modules/@motionone/types": { - "version": "10.15.1", - "resolved": "https://registry.npmjs.org/@motionone/types/-/types-10.15.1.tgz", - "integrity": "sha512-iIUd/EgUsRZGrvW0jqdst8st7zKTzS9EsKkP+6c6n4MPZoQHwiHuVtTQLD6Kp0bsBLhNzKIBlHXponn/SDT4hA==" + "version": "10.17.1", + "resolved": "https://registry.npmjs.org/@motionone/types/-/types-10.17.1.tgz", + "integrity": "sha512-KaC4kgiODDz8hswCrS0btrVrzyU2CSQKO7Ps90ibBVSQmjkrt2teqta6/sOG59v7+dPnKMAg13jyqtMKV2yJ7A==", + "license": "MIT" }, "node_modules/@motionone/utils": { - "version": "10.15.1", - "resolved": "https://registry.npmjs.org/@motionone/utils/-/utils-10.15.1.tgz", - "integrity": "sha512-p0YncgU+iklvYr/Dq4NobTRdAPv9PveRDUXabPEeOjBLSO/1FNB2phNTZxOxpi1/GZwYpAoECEa0Wam+nsmhSw==", + "version": "10.18.0", + "resolved": "https://registry.npmjs.org/@motionone/utils/-/utils-10.18.0.tgz", + "integrity": "sha512-3XVF7sgyTSI2KWvTf6uLlBJ5iAgRgmvp3bpuOiQJvInd4nZ19ET8lX5unn30SlmRH7hXbBbH+Gxd0m0klJ3Xtw==", + "license": "MIT", "dependencies": { - "@motionone/types": "^10.15.1", + "@motionone/types": "^10.17.1", "hey-listen": "^1.0.8", "tslib": "^2.3.1" } }, "node_modules/@motionone/utils/node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "license": "0BSD" }, "node_modules/@motionone/vue": { - "version": "10.15.5", - "resolved": "https://registry.npmjs.org/@motionone/vue/-/vue-10.15.5.tgz", - "integrity": "sha512-cUENrLYAolUacHvCgU+8wF9OgSlVutfWbHMLERI/bElCJ+e2YVQvG/CpGhIM5fYOOJzuvg2T2wHmLLmvJoavEw==", + "version": "10.16.4", + "resolved": "https://registry.npmjs.org/@motionone/vue/-/vue-10.16.4.tgz", + "integrity": "sha512-z10PF9JV6SbjFq+/rYabM+8CVlMokgl8RFGvieSGNTmrkQanfHn+15XBrhG3BgUfvmTeSeyShfOHpG0i9zEdcg==", + "deprecated": "Motion One for Vue is deprecated. Use Oku Motion instead https://oku-ui.com/motion", + "license": "MIT", "dependencies": { - "@motionone/dom": "^10.15.5", + "@motionone/dom": "^10.16.4", "tslib": "^2.3.1" } }, "node_modules/@motionone/vue/node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "license": "0BSD" }, - "node_modules/@pedrouid/environment": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@pedrouid/environment/-/environment-1.0.1.tgz", - "integrity": "sha512-HaW78NszGzRZd9SeoI3JD11JqY+lubnaOx7Pewj5pfjqWXOEATpeKIFb9Z4t2WBUK2iryiXX3lzWwmYWgUL0Ug==" + "node_modules/@parcel/watcher": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.4.1.tgz", + "integrity": "sha512-HNjmfLQEVRZmHRET336f20H/8kOozUGwk7yajvsonjNxbj2wBTK1WsQuHkD5yYh9RxFGL2EyDHryOihOwUoKDA==", + "license": "MIT", + "dependencies": { + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.4.1", + "@parcel/watcher-darwin-arm64": "2.4.1", + "@parcel/watcher-darwin-x64": "2.4.1", + "@parcel/watcher-freebsd-x64": "2.4.1", + "@parcel/watcher-linux-arm-glibc": "2.4.1", + "@parcel/watcher-linux-arm64-glibc": "2.4.1", + "@parcel/watcher-linux-arm64-musl": "2.4.1", + "@parcel/watcher-linux-x64-glibc": "2.4.1", + "@parcel/watcher-linux-x64-musl": "2.4.1", + "@parcel/watcher-win32-arm64": "2.4.1", + "@parcel/watcher-win32-ia32": "2.4.1", + "@parcel/watcher-win32-x64": "2.4.1" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.4.1.tgz", + "integrity": "sha512-ln41eihm5YXIY043vBrrHfn94SIBlqOWmoROhsMVTSXGh0QahKGy77tfEywQ7v3NywyxBBkGIfrWRHm0hsKtzA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-wasm": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-wasm/-/watcher-wasm-2.4.1.tgz", + "integrity": "sha512-/ZR0RxqxU/xxDGzbzosMjh4W6NdYFMqq2nvo2b8SLi7rsl/4jkL8S5stIikorNkdR50oVDvqb/3JT05WM+CRRA==", + "bundleDependencies": [ + "napi-wasm" + ], + "license": "MIT", + "dependencies": { + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "napi-wasm": "^1.1.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-wasm/node_modules/napi-wasm": { + "version": "1.1.0", + "inBundle": true, + "license": "MIT" }, "node_modules/@stablelib/aead": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@stablelib/aead/-/aead-1.0.1.tgz", - "integrity": "sha512-q39ik6sxGHewqtO0nP4BuSe3db5G1fEJE8ukvngS2gLkBXyy6E7pLubhbYgnkDFv6V8cWaxcE4Xn0t6LWcJkyg==" + "integrity": "sha512-q39ik6sxGHewqtO0nP4BuSe3db5G1fEJE8ukvngS2gLkBXyy6E7pLubhbYgnkDFv6V8cWaxcE4Xn0t6LWcJkyg==", + "license": "MIT" }, "node_modules/@stablelib/binary": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@stablelib/binary/-/binary-1.0.1.tgz", "integrity": "sha512-ClJWvmL6UBM/wjkvv/7m5VP3GMr9t0osr4yVgLZsLCOz4hGN9gIAFEqnJ0TsSMAN+n840nf2cHZnA5/KFqHC7Q==", + "license": "MIT", "dependencies": { "@stablelib/int": "^1.0.1" } @@ -267,12 +327,14 @@ "node_modules/@stablelib/bytes": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@stablelib/bytes/-/bytes-1.0.1.tgz", - "integrity": "sha512-Kre4Y4kdwuqL8BR2E9hV/R5sOrUj6NanZaZis0V6lX5yzqC3hBuVSDXUIBqQv/sCpmuWRiHLwqiT1pqqjuBXoQ==" + "integrity": "sha512-Kre4Y4kdwuqL8BR2E9hV/R5sOrUj6NanZaZis0V6lX5yzqC3hBuVSDXUIBqQv/sCpmuWRiHLwqiT1pqqjuBXoQ==", + "license": "MIT" }, "node_modules/@stablelib/chacha": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@stablelib/chacha/-/chacha-1.0.1.tgz", "integrity": "sha512-Pmlrswzr0pBzDofdFuVe1q7KdsHKhhU24e8gkEwnTGOmlC7PADzLVxGdn2PoNVBBabdg0l/IfLKg6sHAbTQugg==", + "license": "MIT", "dependencies": { "@stablelib/binary": "^1.0.1", "@stablelib/wipe": "^1.0.1" @@ -282,6 +344,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@stablelib/chacha20poly1305/-/chacha20poly1305-1.0.1.tgz", "integrity": "sha512-MmViqnqHd1ymwjOQfghRKw2R/jMIGT3wySN7cthjXCBdO+qErNPUBnRzqNpnvIwg7JBCg3LdeCZZO4de/yEhVA==", + "license": "MIT", "dependencies": { "@stablelib/aead": "^1.0.1", "@stablelib/binary": "^1.0.1", @@ -294,12 +357,14 @@ "node_modules/@stablelib/constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@stablelib/constant-time/-/constant-time-1.0.1.tgz", - "integrity": "sha512-tNOs3uD0vSJcK6z1fvef4Y+buN7DXhzHDPqRLSXUel1UfqMB1PWNsnnAezrKfEwTLpN0cGH2p9NNjs6IqeD0eg==" + "integrity": "sha512-tNOs3uD0vSJcK6z1fvef4Y+buN7DXhzHDPqRLSXUel1UfqMB1PWNsnnAezrKfEwTLpN0cGH2p9NNjs6IqeD0eg==", + "license": "MIT" }, "node_modules/@stablelib/ed25519": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@stablelib/ed25519/-/ed25519-1.0.3.tgz", "integrity": "sha512-puIMWaX9QlRsbhxfDc5i+mNPMY+0TmQEskunY1rZEBPi1acBCVQAhnsk/1Hk50DGPtVsZtAWQg4NHGlVaO9Hqg==", + "license": "MIT", "dependencies": { "@stablelib/random": "^1.0.2", "@stablelib/sha512": "^1.0.1", @@ -309,12 +374,14 @@ "node_modules/@stablelib/hash": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@stablelib/hash/-/hash-1.0.1.tgz", - "integrity": "sha512-eTPJc/stDkdtOcrNMZ6mcMK1e6yBbqRBaNW55XA1jU8w/7QdnCF0CmMmOD1m7VSkBR44PWrMHU2l6r8YEQHMgg==" + "integrity": "sha512-eTPJc/stDkdtOcrNMZ6mcMK1e6yBbqRBaNW55XA1jU8w/7QdnCF0CmMmOD1m7VSkBR44PWrMHU2l6r8YEQHMgg==", + "license": "MIT" }, "node_modules/@stablelib/hkdf": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@stablelib/hkdf/-/hkdf-1.0.1.tgz", "integrity": "sha512-SBEHYE16ZXlHuaW5RcGk533YlBj4grMeg5TooN80W3NpcHRtLZLLXvKyX0qcRFxf+BGDobJLnwkvgEwHIDBR6g==", + "license": "MIT", "dependencies": { "@stablelib/hash": "^1.0.1", "@stablelib/hmac": "^1.0.1", @@ -325,6 +392,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@stablelib/hmac/-/hmac-1.0.1.tgz", "integrity": "sha512-V2APD9NSnhVpV/QMYgCVMIYKiYG6LSqw1S65wxVoirhU/51ACio6D4yDVSwMzuTJXWZoVHbDdINioBwKy5kVmA==", + "license": "MIT", "dependencies": { "@stablelib/constant-time": "^1.0.1", "@stablelib/hash": "^1.0.1", @@ -334,12 +402,14 @@ "node_modules/@stablelib/int": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@stablelib/int/-/int-1.0.1.tgz", - "integrity": "sha512-byr69X/sDtDiIjIV6m4roLVWnNNlRGzsvxw+agj8CIEazqWGOQp2dTYgQhtyVXV9wpO6WyXRQUzLV/JRNumT2w==" + "integrity": "sha512-byr69X/sDtDiIjIV6m4roLVWnNNlRGzsvxw+agj8CIEazqWGOQp2dTYgQhtyVXV9wpO6WyXRQUzLV/JRNumT2w==", + "license": "MIT" }, "node_modules/@stablelib/keyagreement": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@stablelib/keyagreement/-/keyagreement-1.0.1.tgz", "integrity": "sha512-VKL6xBwgJnI6l1jKrBAfn265cspaWBPAPEc62VBQrWHLqVgNRE09gQ/AnOEyKUWrrqfD+xSQ3u42gJjLDdMDQg==", + "license": "MIT", "dependencies": { "@stablelib/bytes": "^1.0.1" } @@ -348,6 +418,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@stablelib/poly1305/-/poly1305-1.0.1.tgz", "integrity": "sha512-1HlG3oTSuQDOhSnLwJRKeTRSAdFNVB/1djy2ZbS35rBSJ/PFqx9cf9qatinWghC2UbfOYD8AcrtbUQl8WoxabA==", + "license": "MIT", "dependencies": { "@stablelib/constant-time": "^1.0.1", "@stablelib/wipe": "^1.0.1" @@ -357,6 +428,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/@stablelib/random/-/random-1.0.2.tgz", "integrity": "sha512-rIsE83Xpb7clHPVRlBj8qNe5L8ISQOzjghYQm/dZ7VaM2KHYwMW5adjQjrzTZCchFnNCNhkwtnOBa9HTMJCI8w==", + "license": "MIT", "dependencies": { "@stablelib/binary": "^1.0.1", "@stablelib/wipe": "^1.0.1" @@ -366,6 +438,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@stablelib/sha256/-/sha256-1.0.1.tgz", "integrity": "sha512-GIIH3e6KH+91FqGV42Kcj71Uefd/QEe7Dy42sBTeqppXV95ggCcxLTk39bEr+lZfJmp+ghsR07J++ORkRELsBQ==", + "license": "MIT", "dependencies": { "@stablelib/binary": "^1.0.1", "@stablelib/hash": "^1.0.1", @@ -376,6 +449,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@stablelib/sha512/-/sha512-1.0.1.tgz", "integrity": "sha512-13gl/iawHV9zvDKciLo1fQ8Bgn2Pvf7OV6amaRVKiq3pjQ3UmEpXxWiAfV8tYjUpeZroBxtyrwtdooQT/i3hzw==", + "license": "MIT", "dependencies": { "@stablelib/binary": "^1.0.1", "@stablelib/hash": "^1.0.1", @@ -385,252 +459,255 @@ "node_modules/@stablelib/wipe": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@stablelib/wipe/-/wipe-1.0.1.tgz", - "integrity": "sha512-WfqfX/eXGiAd3RJe4VU2snh/ZPwtSjLG4ynQ/vYzvghTh7dHFcI1wl+nrkWG6lGhukOxOsUHfv8dUXr58D0ayg==" + "integrity": "sha512-WfqfX/eXGiAd3RJe4VU2snh/ZPwtSjLG4ynQ/vYzvghTh7dHFcI1wl+nrkWG6lGhukOxOsUHfv8dUXr58D0ayg==", + "license": "MIT" }, "node_modules/@stablelib/x25519": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@stablelib/x25519/-/x25519-1.0.3.tgz", "integrity": "sha512-KnTbKmUhPhHavzobclVJQG5kuivH+qDLpe84iRqX3CLrKp881cF160JvXJ+hjn1aMyCwYOKeIZefIH/P5cJoRw==", + "license": "MIT", "dependencies": { "@stablelib/keyagreement": "^1.0.1", "@stablelib/random": "^1.0.2", "@stablelib/wipe": "^1.0.1" } }, - "node_modules/@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==" - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==" - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==" - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", - "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==" + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true, + "license": "MIT" }, - "node_modules/@types/eslint": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.37.0.tgz", - "integrity": "sha512-Piet7dG2JBuDIfohBngQ3rCt7MgO9xCO4xIMKxBThCq5PNRB91IjlJ10eJVwfoNtvTErmxLzwBZ7rHZtbOMmFQ==", + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true, - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } + "license": "MIT" }, - "node_modules/@types/eslint-scope": { - "version": "3.7.4", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", - "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "node_modules/@types/node": { + "version": "22.5.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.4.tgz", + "integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==", "dev": true, + "license": "MIT", "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" + "undici-types": "~6.19.2" } }, - "node_modules/@types/estree": { - "version": "0.0.51", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", - "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", - "dev": true - }, - "node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true - }, - "node_modules/@types/node": { - "version": "18.15.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz", - "integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==" - }, "node_modules/@types/trusted-types": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.3.tgz", - "integrity": "sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==" + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT" }, "node_modules/@walletconnect/core": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/@walletconnect/core/-/core-2.5.2.tgz", - "integrity": "sha512-R0D9NKgHBpdun65q+1L49GOIGDLaIodnyb+Dq0tXGVzvXzy2lkXOlh2e9am61ixaVrUsHt7b96b318geqsuk4Q==", - "dependencies": { - "@walletconnect/heartbeat": "1.2.0", - "@walletconnect/jsonrpc-provider": "1.0.10", - "@walletconnect/jsonrpc-utils": "^1.0.4", - "@walletconnect/jsonrpc-ws-connection": "1.0.10", - "@walletconnect/keyvaluestorage": "^1.0.2", - "@walletconnect/logger": "^2.0.1", - "@walletconnect/relay-api": "^1.0.9", - "@walletconnect/relay-auth": "^1.0.4", - "@walletconnect/safe-json": "^1.0.1", - "@walletconnect/time": "^1.0.2", - "@walletconnect/types": "2.5.2", - "@walletconnect/utils": "2.5.2", - "events": "^3.3.0", + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/@walletconnect/core/-/core-2.16.1.tgz", + "integrity": "sha512-UlsnEMT5wwFvmxEjX8s4oju7R3zadxNbZgsFeHEsjh7uknY2zgmUe1Lfc5XU6zyPb1Jx7Nqpdx1KN485ee8ogw==", + "license": "Apache-2.0", + "dependencies": { + "@walletconnect/heartbeat": "1.2.2", + "@walletconnect/jsonrpc-provider": "1.0.14", + "@walletconnect/jsonrpc-types": "1.0.4", + "@walletconnect/jsonrpc-utils": "1.0.8", + "@walletconnect/jsonrpc-ws-connection": "1.0.14", + "@walletconnect/keyvaluestorage": "1.1.1", + "@walletconnect/logger": "2.1.2", + "@walletconnect/relay-api": "1.0.11", + "@walletconnect/relay-auth": "1.0.4", + "@walletconnect/safe-json": "1.0.2", + "@walletconnect/time": "1.0.2", + "@walletconnect/types": "2.16.1", + "@walletconnect/utils": "2.16.1", + "events": "3.3.0", "lodash.isequal": "4.5.0", - "pino": "7.11.0", - "uint8arrays": "^3.1.0" + "uint8arrays": "3.1.0" + }, + "engines": { + "node": ">=18" } }, "node_modules/@walletconnect/environment": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@walletconnect/environment/-/environment-1.0.1.tgz", "integrity": "sha512-T426LLZtHj8e8rYnKfzsw1aG6+M0BT1ZxayMdv/p8yM0MU+eJDISqNY3/bccxRr4LrF9csq02Rhqt08Ibl0VRg==", + "license": "MIT", "dependencies": { "tslib": "1.14.1" } }, "node_modules/@walletconnect/ethereum-provider": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/@walletconnect/ethereum-provider/-/ethereum-provider-2.5.2.tgz", - "integrity": "sha512-WEN85tsuHgvoiMK4KpsRsOgsKB0QLCctSwxTqyWDybBbXuJRJGWXkZ6Oma9VSmUR0MgPSjiGmOFgY4ybMlhEMA==", - "dependencies": { - "@walletconnect/jsonrpc-http-connection": "^1.0.4", - "@walletconnect/jsonrpc-provider": "^1.0.6", - "@walletconnect/jsonrpc-types": "^1.0.2", - "@walletconnect/jsonrpc-utils": "^1.0.4", - "@walletconnect/sign-client": "2.5.2", - "@walletconnect/types": "2.5.2", - "@walletconnect/universal-provider": "2.5.2", - "@walletconnect/utils": "2.5.2", - "events": "^3.3.0" - }, - "peerDependencies": { - "@web3modal/standalone": ">=2" - }, - "peerDependenciesMeta": { - "@web3modal/standalone": { - "optional": true - } + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/@walletconnect/ethereum-provider/-/ethereum-provider-2.16.1.tgz", + "integrity": "sha512-oD7DNCssUX3plS5gGUZ9JQ63muQB/vxO68X6RzD2wd8gBsYtSPw4BqYFc7KTO6dUizD6gfPirw32yW2pTvy92w==", + "license": "Apache-2.0", + "dependencies": { + "@walletconnect/jsonrpc-http-connection": "1.0.8", + "@walletconnect/jsonrpc-provider": "1.0.14", + "@walletconnect/jsonrpc-types": "1.0.4", + "@walletconnect/jsonrpc-utils": "1.0.8", + "@walletconnect/modal": "2.6.2", + "@walletconnect/sign-client": "2.16.1", + "@walletconnect/types": "2.16.1", + "@walletconnect/universal-provider": "2.16.1", + "@walletconnect/utils": "2.16.1", + "events": "3.3.0" } }, "node_modules/@walletconnect/events": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@walletconnect/events/-/events-1.0.1.tgz", "integrity": "sha512-NPTqaoi0oPBVNuLv7qPaJazmGHs5JGyO8eEAk5VGKmJzDR7AHzD4k6ilox5kxk1iwiOnFopBOOMLs86Oa76HpQ==", + "license": "MIT", "dependencies": { "keyvaluestorage-interface": "^1.0.0", "tslib": "1.14.1" } }, "node_modules/@walletconnect/heartbeat": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@walletconnect/heartbeat/-/heartbeat-1.2.0.tgz", - "integrity": "sha512-0vbzTa/ARrpmMmOD+bQMxPvFYKtOLQZObgZakrYr0aODiMOO71CmPVNV2eAqXnw9rMmcP+z91OybLeIFlwTjjA==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@walletconnect/heartbeat/-/heartbeat-1.2.2.tgz", + "integrity": "sha512-uASiRmC5MwhuRuf05vq4AT48Pq8RMi876zV8rr8cV969uTOzWdB/k+Lj5yI2PBtB1bGQisGen7MM1GcZlQTBXw==", + "license": "MIT", "dependencies": { "@walletconnect/events": "^1.0.1", "@walletconnect/time": "^1.0.2", - "chai": "^4.3.7", - "mocha": "^10.2.0", - "ts-node": "^10.9.1", - "tslib": "1.14.1" + "events": "^3.3.0" } }, "node_modules/@walletconnect/jsonrpc-http-connection": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@walletconnect/jsonrpc-http-connection/-/jsonrpc-http-connection-1.0.6.tgz", - "integrity": "sha512-/3zSqDi7JDN06E4qm0NmVYMitngXfh21UWwy8zeJcBeJc+Jcs094EbLsIxtziIIKTCCbT88lWuTjl1ZujxN7cw==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@walletconnect/jsonrpc-http-connection/-/jsonrpc-http-connection-1.0.8.tgz", + "integrity": "sha512-+B7cRuaxijLeFDJUq5hAzNyef3e3tBDIxyaCNmFtjwnod5AGis3RToNqzFU33vpVcxFhofkpE7Cx+5MYejbMGw==", + "license": "MIT", "dependencies": { "@walletconnect/jsonrpc-utils": "^1.0.6", "@walletconnect/safe-json": "^1.0.1", "cross-fetch": "^3.1.4", - "tslib": "1.14.1" + "events": "^3.3.0" } }, "node_modules/@walletconnect/jsonrpc-provider": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/@walletconnect/jsonrpc-provider/-/jsonrpc-provider-1.0.10.tgz", - "integrity": "sha512-g0ffPSpY3P6GqGjWGHsr3yqvQUhj7q2k6pAikoXv5XTXWaJRzFvrlbFkSgxziXsBrwrMZn0qvPufvpN4mMZ5FA==", + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/@walletconnect/jsonrpc-provider/-/jsonrpc-provider-1.0.14.tgz", + "integrity": "sha512-rtsNY1XqHvWj0EtITNeuf8PHMvlCLiS3EjQL+WOkxEOA4KPxsohFnBDeyPYiNm4ZvkQdLnece36opYidmtbmow==", + "license": "MIT", "dependencies": { - "@walletconnect/jsonrpc-utils": "^1.0.6", - "@walletconnect/safe-json": "^1.0.1", - "tslib": "1.14.1" + "@walletconnect/jsonrpc-utils": "^1.0.8", + "@walletconnect/safe-json": "^1.0.2", + "events": "^3.3.0" } }, "node_modules/@walletconnect/jsonrpc-types": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@walletconnect/jsonrpc-types/-/jsonrpc-types-1.0.2.tgz", - "integrity": "sha512-CZe8tjJX73OWdHjrBHy7HtAapJ2tT0Q3TYhPBhRxi3643lwPIQWC9En45ldY14TZwgSewkbZ0FtGBZK0G7Bbyg==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@walletconnect/jsonrpc-types/-/jsonrpc-types-1.0.4.tgz", + "integrity": "sha512-P6679fG/M+wuWg9TY8mh6xFSdYnFyFjwFelxyISxMDrlbXokorEVXYOxiqEbrU3x1BmBoCAJJ+vtEaEoMlpCBQ==", + "license": "MIT", "dependencies": { - "keyvaluestorage-interface": "^1.0.0", - "tslib": "1.14.1" + "events": "^3.3.0", + "keyvaluestorage-interface": "^1.0.0" } }, "node_modules/@walletconnect/jsonrpc-utils": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@walletconnect/jsonrpc-utils/-/jsonrpc-utils-1.0.6.tgz", - "integrity": "sha512-snp0tfkjPiDLQp/jrBewI+9SM33GPV4+Gjgldod6XQ7rFyQ5FZjnBxUkY4xWH0+arNxzQSi6v5iDXjCjSaorpg==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@walletconnect/jsonrpc-utils/-/jsonrpc-utils-1.0.8.tgz", + "integrity": "sha512-vdeb03bD8VzJUL6ZtzRYsFMq1eZQcM3EAzT0a3st59dyLfJ0wq+tKMpmGH7HlB7waD858UWgfIcudbPFsbzVdw==", + "license": "MIT", "dependencies": { "@walletconnect/environment": "^1.0.1", - "@walletconnect/jsonrpc-types": "^1.0.2", + "@walletconnect/jsonrpc-types": "^1.0.3", "tslib": "1.14.1" } }, "node_modules/@walletconnect/jsonrpc-ws-connection": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/@walletconnect/jsonrpc-ws-connection/-/jsonrpc-ws-connection-1.0.10.tgz", - "integrity": "sha512-/tidvjfCXZuYugjF5fOswsNDPoMo9QRML3DFQ0dfNUarL4f5HGqu8NDGerr2n0+4MOX23GsT6Vv2POSwFbvgGw==", + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/@walletconnect/jsonrpc-ws-connection/-/jsonrpc-ws-connection-1.0.14.tgz", + "integrity": "sha512-Jsl6fC55AYcbkNVkwNM6Jo+ufsuCQRqViOQ8ZBPH9pRREHH9welbBiszuTLqEJiQcO/6XfFDl6bzCJIkrEi8XA==", + "license": "MIT", "dependencies": { "@walletconnect/jsonrpc-utils": "^1.0.6", - "@walletconnect/safe-json": "^1.0.1", + "@walletconnect/safe-json": "^1.0.2", "events": "^3.3.0", - "tslib": "1.14.1", "ws": "^7.5.1" } }, "node_modules/@walletconnect/keyvaluestorage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@walletconnect/keyvaluestorage/-/keyvaluestorage-1.0.2.tgz", - "integrity": "sha512-U/nNG+VLWoPFdwwKx0oliT4ziKQCEoQ27L5Hhw8YOFGA2Po9A9pULUYNWhDgHkrb0gYDNt//X7wABcEWWBd3FQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@walletconnect/keyvaluestorage/-/keyvaluestorage-1.1.1.tgz", + "integrity": "sha512-V7ZQq2+mSxAq7MrRqDxanTzu2RcElfK1PfNYiaVnJgJ7Q7G7hTVwF8voIBx92qsRyGHZihrwNPHuZd1aKkd0rA==", + "license": "MIT", "dependencies": { - "safe-json-utils": "^1.1.1", - "tslib": "1.14.1" + "@walletconnect/safe-json": "^1.0.1", + "idb-keyval": "^6.2.1", + "unstorage": "^1.9.0" }, "peerDependencies": { - "@react-native-async-storage/async-storage": "1.x", - "lokijs": "1.x" + "@react-native-async-storage/async-storage": "1.x" }, "peerDependenciesMeta": { "@react-native-async-storage/async-storage": { "optional": true - }, - "lokijs": { - "optional": true } } }, "node_modules/@walletconnect/logger": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@walletconnect/logger/-/logger-2.0.1.tgz", - "integrity": "sha512-SsTKdsgWm+oDTBeNE/zHxxr5eJfZmE9/5yp/Ku+zJtcTAjELb3DXueWkDXmE9h8uHIbJzIb5wj5lPdzyrjT6hQ==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@walletconnect/logger/-/logger-2.1.2.tgz", + "integrity": "sha512-aAb28I3S6pYXZHQm5ESB+V6rDqIYfsnHaQyzFbwUUBFY4H0OXx/YtTl8lvhUNhMMfb9UxbwEBS253TlXUYJWSw==", + "license": "MIT", "dependencies": { - "pino": "7.11.0", - "tslib": "1.14.1" + "@walletconnect/safe-json": "^1.0.2", + "pino": "7.11.0" + } + }, + "node_modules/@walletconnect/modal": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@walletconnect/modal/-/modal-2.6.2.tgz", + "integrity": "sha512-eFopgKi8AjKf/0U4SemvcYw9zlLpx9njVN8sf6DAkowC2Md0gPU/UNEbH1Wwj407pEKnEds98pKWib1NN1ACoA==", + "license": "Apache-2.0", + "dependencies": { + "@walletconnect/modal-core": "2.6.2", + "@walletconnect/modal-ui": "2.6.2" + } + }, + "node_modules/@walletconnect/modal-core": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@walletconnect/modal-core/-/modal-core-2.6.2.tgz", + "integrity": "sha512-cv8ibvdOJQv2B+nyxP9IIFdxvQznMz8OOr/oR/AaUZym4hjXNL/l1a2UlSQBXrVjo3xxbouMxLb3kBsHoYP2CA==", + "license": "Apache-2.0", + "dependencies": { + "valtio": "1.11.2" + } + }, + "node_modules/@walletconnect/modal-ui": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@walletconnect/modal-ui/-/modal-ui-2.6.2.tgz", + "integrity": "sha512-rbdstM1HPGvr7jprQkyPggX7rP4XiCG85ZA+zWBEX0dVQg8PpAgRUqpeub4xQKDgY7pY/xLRXSiCVdWGqvG2HA==", + "license": "Apache-2.0", + "dependencies": { + "@walletconnect/modal-core": "2.6.2", + "lit": "2.8.0", + "motion": "10.16.2", + "qrcode": "1.5.3" } }, "node_modules/@walletconnect/relay-api": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@walletconnect/relay-api/-/relay-api-1.0.9.tgz", - "integrity": "sha512-Q3+rylJOqRkO1D9Su0DPE3mmznbAalYapJ9qmzDgK28mYF9alcP3UwG/og5V7l7CFOqzCLi7B8BvcBUrpDj0Rg==", + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@walletconnect/relay-api/-/relay-api-1.0.11.tgz", + "integrity": "sha512-tLPErkze/HmC9aCmdZOhtVmYZq1wKfWTJtygQHoWtgg722Jd4homo54Cs4ak2RUFUZIGO2RsOpIcWipaua5D5Q==", + "license": "MIT", "dependencies": { - "@walletconnect/jsonrpc-types": "^1.0.2", - "tslib": "1.14.1" + "@walletconnect/jsonrpc-types": "^1.0.2" } }, "node_modules/@walletconnect/relay-auth": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@walletconnect/relay-auth/-/relay-auth-1.0.4.tgz", "integrity": "sha512-kKJcS6+WxYq5kshpPaxGHdwf5y98ZwbfuS4EE/NkQzqrDFm5Cj+dP8LofzWvjrrLkZq7Afy7WrQMXdLy8Sx7HQ==", + "license": "MIT", "dependencies": { "@stablelib/ed25519": "^1.0.2", "@stablelib/random": "^1.0.1", @@ -641,95 +718,100 @@ } }, "node_modules/@walletconnect/safe-json": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@walletconnect/safe-json/-/safe-json-1.0.1.tgz", - "integrity": "sha512-Fm7e31oSYY15NQr8SsLJheKAy5L744udZf2lJKcz6wFmPJEzf7hOF0866o/rrldRzJnjZ4H2GJ45pFudsnLW5A==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@walletconnect/safe-json/-/safe-json-1.0.2.tgz", + "integrity": "sha512-Ogb7I27kZ3LPC3ibn8ldyUr5544t3/STow9+lzz7Sfo808YD7SBWk7SAsdBFlYgP2zDRy2hS3sKRcuSRM0OTmA==", + "license": "MIT", "dependencies": { "tslib": "1.14.1" } }, "node_modules/@walletconnect/sign-client": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/@walletconnect/sign-client/-/sign-client-2.5.2.tgz", - "integrity": "sha512-eKUnGCVgYqN+6b4gm27ML/064m0c/2hTlTHy6tbUszYtEPTzb+q4fvpnWs6blaOjzc18l8NFwX3c1+MHxVdQUQ==", - "dependencies": { - "@walletconnect/core": "2.5.2", - "@walletconnect/events": "^1.0.1", - "@walletconnect/heartbeat": "1.2.0", - "@walletconnect/jsonrpc-utils": "^1.0.4", - "@walletconnect/logger": "^2.0.1", - "@walletconnect/time": "^1.0.2", - "@walletconnect/types": "2.5.2", - "@walletconnect/utils": "2.5.2", - "events": "^3.3.0", - "pino": "7.11.0" + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/@walletconnect/sign-client/-/sign-client-2.16.1.tgz", + "integrity": "sha512-s2Tx2n2duxt+sHtuWXrN9yZVaHaYqcEcjwlTD+55/vs5NUPlISf+fFmZLwSeX1kUlrSBrAuxPUcqQuRTKcjLOA==", + "license": "Apache-2.0", + "dependencies": { + "@walletconnect/core": "2.16.1", + "@walletconnect/events": "1.0.1", + "@walletconnect/heartbeat": "1.2.2", + "@walletconnect/jsonrpc-utils": "1.0.8", + "@walletconnect/logger": "2.1.2", + "@walletconnect/time": "1.0.2", + "@walletconnect/types": "2.16.1", + "@walletconnect/utils": "2.16.1", + "events": "3.3.0" } }, "node_modules/@walletconnect/time": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@walletconnect/time/-/time-1.0.2.tgz", "integrity": "sha512-uzdd9woDcJ1AaBZRhqy5rNC9laqWGErfc4dxA9a87mPdKOgWMD85mcFo9dIYIts/Jwocfwn07EC6EzclKubk/g==", + "license": "MIT", "dependencies": { "tslib": "1.14.1" } }, "node_modules/@walletconnect/types": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/@walletconnect/types/-/types-2.5.2.tgz", - "integrity": "sha512-VnV43qs4f2hwv6wGQ9ZSE+smP0z2oVy2XaVO5Szd2fmOx9bB+ov+sQzh9xeoQ+DhjNrbJhUaecW/peE6CPPSag==", + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/@walletconnect/types/-/types-2.16.1.tgz", + "integrity": "sha512-9P4RG4VoDEF+yBF/n2TF12gsvT/aTaeZTVDb/AOayafqiPnmrQZMKmNCJJjq1sfdsDcHXFcZWMGsuCeSJCmrXA==", + "license": "Apache-2.0", "dependencies": { - "@walletconnect/events": "^1.0.1", - "@walletconnect/heartbeat": "1.2.0", - "@walletconnect/jsonrpc-types": "^1.0.2", - "@walletconnect/keyvaluestorage": "^1.0.2", - "@walletconnect/logger": "^2.0.1", - "events": "^3.3.0" + "@walletconnect/events": "1.0.1", + "@walletconnect/heartbeat": "1.2.2", + "@walletconnect/jsonrpc-types": "1.0.4", + "@walletconnect/keyvaluestorage": "1.1.1", + "@walletconnect/logger": "2.1.2", + "events": "3.3.0" } }, "node_modules/@walletconnect/universal-provider": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/@walletconnect/universal-provider/-/universal-provider-2.5.2.tgz", - "integrity": "sha512-R61VL02zvcljwSC+FJVzxGswbN21tokQLG0IQL1tVq30+KfkZOt0y/UxsDNvgHNGleGgfoQZzOWsfSLgp5pcBQ==", - "dependencies": { - "@walletconnect/jsonrpc-http-connection": "^1.0.4", - "@walletconnect/jsonrpc-provider": "^1.0.6", - "@walletconnect/jsonrpc-types": "^1.0.2", - "@walletconnect/jsonrpc-utils": "^1.0.4", - "@walletconnect/logger": "^2.0.1", - "@walletconnect/sign-client": "2.5.2", - "@walletconnect/types": "2.5.2", - "@walletconnect/utils": "2.5.2", - "eip1193-provider": "1.0.1", - "events": "^3.3.0", - "pino": "7.11.0" + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/@walletconnect/universal-provider/-/universal-provider-2.16.1.tgz", + "integrity": "sha512-q/tyWUVNenizuClEiaekx9FZj/STU1F3wpDK4PUIh3xh+OmUI5fw2dY3MaNDjyb5AyrS0M8BuQDeuoSuOR/Q7w==", + "license": "Apache-2.0", + "dependencies": { + "@walletconnect/jsonrpc-http-connection": "1.0.8", + "@walletconnect/jsonrpc-provider": "1.0.14", + "@walletconnect/jsonrpc-types": "1.0.4", + "@walletconnect/jsonrpc-utils": "1.0.8", + "@walletconnect/logger": "2.1.2", + "@walletconnect/sign-client": "2.16.1", + "@walletconnect/types": "2.16.1", + "@walletconnect/utils": "2.16.1", + "events": "3.3.0" } }, "node_modules/@walletconnect/utils": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/@walletconnect/utils/-/utils-2.5.2.tgz", - "integrity": "sha512-s5bpY5q/RaXMc6LgPp+E7qPbKhrff9TjrLRjN2m9COnt9cERowpQEFrPzWmh10FatRZ7dNrudJ5I/c36nFc+hw==", + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/@walletconnect/utils/-/utils-2.16.1.tgz", + "integrity": "sha512-aoQirVoDoiiEtYeYDtNtQxFzwO/oCrz9zqeEEXYJaAwXlGVTS34KFe7W3/Rxd/pldTYKFOZsku2EzpISfH8Wsw==", + "license": "Apache-2.0", "dependencies": { "@stablelib/chacha20poly1305": "1.0.1", "@stablelib/hkdf": "1.0.1", - "@stablelib/random": "^1.0.2", + "@stablelib/random": "1.0.2", "@stablelib/sha256": "1.0.1", - "@stablelib/x25519": "^1.0.3", - "@walletconnect/jsonrpc-utils": "^1.0.4", - "@walletconnect/relay-api": "^1.0.9", - "@walletconnect/safe-json": "^1.0.1", - "@walletconnect/time": "^1.0.2", - "@walletconnect/types": "2.5.2", - "@walletconnect/window-getters": "^1.0.1", - "@walletconnect/window-metadata": "^1.0.1", + "@stablelib/x25519": "1.0.3", + "@walletconnect/relay-api": "1.0.11", + "@walletconnect/relay-auth": "1.0.4", + "@walletconnect/safe-json": "1.0.2", + "@walletconnect/time": "1.0.2", + "@walletconnect/types": "2.16.1", + "@walletconnect/window-getters": "1.0.1", + "@walletconnect/window-metadata": "1.0.1", "detect-browser": "5.3.0", - "query-string": "7.1.1", - "uint8arrays": "^3.1.0" + "elliptic": "^6.5.7", + "query-string": "7.1.3", + "uint8arrays": "3.1.0" } }, "node_modules/@walletconnect/window-getters": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@walletconnect/window-getters/-/window-getters-1.0.1.tgz", "integrity": "sha512-vHp+HqzGxORPAN8gY03qnbTMnhqIwjeRJNOMOAzePRg4xVEEE2WvYsI9G2NMjOknA8hnuYbU3/hwLcKbjhc8+Q==", + "license": "MIT", "dependencies": { "tslib": "1.14.1" } @@ -738,191 +820,179 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@walletconnect/window-metadata/-/window-metadata-1.0.1.tgz", "integrity": "sha512-9koTqyGrM2cqFRW517BPY/iEtUDx2r1+Pwwu5m7sJ7ka79wi3EyqhqcICk/yDmv6jAS1rjKgTKXlEhanYjijcA==", + "license": "MIT", "dependencies": { "@walletconnect/window-getters": "^1.0.1", "tslib": "1.14.1" } }, - "node_modules/@web3modal/core": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@web3modal/core/-/core-2.1.3.tgz", - "integrity": "sha512-hE/Rn8mT+r9M+EgO0WrCiIsMl/QCk4dXUgTpA4E+Dc/Z8JQhQP+VCU7FJNH632idmcFQPKgEf4DnoKQo5cuwEQ==", - "dependencies": { - "buffer": "6.0.3", - "valtio": "1.9.0" - } - }, - "node_modules/@web3modal/standalone": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@web3modal/standalone/-/standalone-2.1.3.tgz", - "integrity": "sha512-xvBGWVv+zw6bcaTB9vfq2/3vKobvfGRbdD/sKn32x4IxEjCaQO6RFmp9yYhj07S6gQ7Cq8iOxlBUBdfnbkH3Ew==", - "dependencies": { - "@web3modal/core": "2.1.3", - "@web3modal/ui": "2.1.3" - } - }, - "node_modules/@web3modal/ui": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@web3modal/ui/-/ui-2.1.3.tgz", - "integrity": "sha512-GvZ53Vk9plSpJjmwGAJyGc5hngN6nr5r3mqUNASIo52Kvr2kqfgPOcbZO/7W1vfAbjZwes6K3Ai2h5P7WZ6Zkg==", - "dependencies": { - "@web3modal/core": "2.1.3", - "lit": "2.6.1", - "motion": "10.15.5", - "qrcode": "1.5.1" - } - }, "node_modules/@webassemblyjs/ast": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", - "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", + "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", "dev": true, + "license": "MIT", "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" } }, "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", - "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", - "dev": true + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", + "dev": true, + "license": "MIT" }, "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", - "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", - "dev": true + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", + "dev": true, + "license": "MIT" }, "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", - "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", - "dev": true + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", + "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==", + "dev": true, + "license": "MIT" }, "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", - "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", + "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", "dev": true, + "license": "MIT", "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", "@xtuc/long": "4.2.2" } }, "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", - "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", - "dev": true + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", + "dev": true, + "license": "MIT" }, "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", - "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", + "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", "dev": true, + "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1" + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.12.1" } }, "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", - "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", "dev": true, + "license": "MIT", "dependencies": { "@xtuc/ieee754": "^1.2.0" } }, "node_modules/@webassemblyjs/leb128": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", - "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@xtuc/long": "4.2.2" } }, "node_modules/@webassemblyjs/utf8": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", - "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", - "dev": true + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", + "dev": true, + "license": "MIT" }, "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", - "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", + "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", "dev": true, + "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/helper-wasm-section": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-opt": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "@webassemblyjs/wast-printer": "1.11.1" + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-opt": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1", + "@webassemblyjs/wast-printer": "1.12.1" } }, "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", - "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", + "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", "dev": true, + "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" } }, "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", - "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", + "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", "dev": true, + "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1" + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1" } }, "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", - "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", + "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", "dev": true, + "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" } }, "node_modules/@webassemblyjs/wast-printer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", - "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", + "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", "dev": true, + "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/ast": "1.12.1", "@xtuc/long": "4.2.2" } }, "node_modules/@webpack-cli/configtest": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.0.1.tgz", - "integrity": "sha512-njsdJXJSiS2iNbQVS0eT8A/KPnmyH4pv1APj2K0d1wrZcBLw+yppxOy4CGqa0OxDJkzfL/XELDhD8rocnIwB5A==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz", + "integrity": "sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==", "dev": true, + "license": "MIT", "engines": { "node": ">=14.15.0" }, @@ -932,10 +1002,11 @@ } }, "node_modules/@webpack-cli/info": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.1.tgz", - "integrity": "sha512-fE1UEWTwsAxRhrJNikE7v4EotYflkEhBL7EbajfkPlf6E37/2QshOy/D48Mw8G5XMFlQtS6YV42vtbG9zBpIQA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz", + "integrity": "sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==", "dev": true, + "license": "MIT", "engines": { "node": ">=14.15.0" }, @@ -945,10 +1016,11 @@ } }, "node_modules/@webpack-cli/serve": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.1.tgz", - "integrity": "sha512-0G7tNyS+yW8TdgHwZKlDWYXFA6OJQnoLCQvYKkQP0Q2X205PSQ6RNUj0M+1OB/9gRQaUZ/ccYfaxd0nhaWKfjw==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz", + "integrity": "sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=14.15.0" }, @@ -966,18 +1038,21 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/@xtuc/long": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true + "dev": true, + "license": "Apache-2.0" }, "node_modules/acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "license": "MIT", "bin": { "acorn": "bin/acorn" }, @@ -985,28 +1060,22 @@ "node": ">=0.4.0" } }, - "node_modules/acorn-import-assertions": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", - "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", "dev": true, + "license": "MIT", "peerDependencies": { "acorn": "^8" } }, - "node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -1023,22 +1092,16 @@ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "dev": true, + "license": "MIT", "peerDependencies": { "ajv": "^6.9.1" } }, - "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "engines": { - "node": ">=6" - } - }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", "engines": { "node": ">=8" } @@ -1047,6 +1110,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -1061,6 +1125,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "license": "ISC", "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -1069,100 +1134,55 @@ "node": ">= 8" } }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "node_modules/assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "engines": { - "node": "*" - } - }, "node_modules/atomic-sleep": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", + "license": "MIT", "engines": { "node": ">=8.0.0" } }, - "node_modules/axios": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", - "dependencies": { - "follow-redirects": "^1.14.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "license": "MIT", "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dependencies": { - "balanced-match": "^1.0.0" - } + "node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "license": "MIT" }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" } }, - "node_modules/browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==" + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", + "license": "MIT" }, "node_modules/browserslist": { - "version": "4.21.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", - "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz", + "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==", "dev": true, "funding": [ { @@ -1172,13 +1192,18 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001449", - "electron-to-chromium": "^1.4.284", - "node-releases": "^2.0.8", - "update-browserslist-db": "^1.0.10" + "caniuse-lite": "^1.0.30001646", + "electron-to-chromium": "^1.5.4", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.0" }, "bin": { "browserslist": "cli.js" @@ -1187,50 +1212,26 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "license": "MIT", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=6" } }, "node_modules/caniuse-lite": { - "version": "1.0.30001473", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001473.tgz", - "integrity": "sha512-ewDad7+D2vlyy+E4UJuVfiBsU69IL+8oVmTuZnH5Q6CIUbxNfI50uVpRHbUPDD6SUaN2o0Lh4DhTrvLG/Tn1yg==", + "version": "1.0.30001660", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001660.tgz", + "integrity": "sha512-GacvNTTuATm26qC74pt+ad1fW15mlQ/zuTzzY1ZoIzECTP8HURDfF43kNxPgf7H1jmelCBQTTbBNxdSXOA7Bqg==", "dev": true, "funding": [ { @@ -1245,69 +1246,14 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ] - }, - "node_modules/chai": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz", - "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==", - "dependencies": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^4.1.2", - "get-func-name": "^2.0.0", - "loupe": "^2.3.1", - "pathval": "^1.1.1", - "type-detect": "^4.0.5" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", - "engines": { - "node": "*" - } + ], + "license": "CC-BY-4.0" }, "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "license": "MIT", "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -1320,6 +1266,9 @@ "engines": { "node": ">= 8.10.0" }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, "optionalDependencies": { "fsevents": "~2.3.2" } @@ -1333,14 +1282,41 @@ "node": ">=6.0" } }, + "node_modules/citty": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", + "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", + "license": "MIT", + "dependencies": { + "consola": "^3.2.3" + } + }, + "node_modules/clipboardy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-4.0.0.tgz", + "integrity": "sha512-5mOlNS0mhX0707P2I0aZ2V/cmHUEO/fL7VFLqszkhUsxt7RwnmrInf/eEQKlf5GzvYeHIjT+Ov1HRfNmymlG0w==", + "license": "MIT", + "dependencies": { + "execa": "^8.0.1", + "is-wsl": "^3.1.0", + "is64bit": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "license": "ISC", "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" + "wrap-ansi": "^6.2.0" } }, "node_modules/clone-deep": { @@ -1361,6 +1337,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -1371,7 +1348,8 @@ "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" }, "node_modules/colorette": { "version": "2.0.19", @@ -1383,31 +1361,43 @@ "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true + "dev": true, + "license": "MIT" }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + "node_modules/confbox": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.7.tgz", + "integrity": "sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==", + "license": "MIT" }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" + "node_modules/consola": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.2.3.tgz", + "integrity": "sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==", + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/cookie-es": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.2.2.tgz", + "integrity": "sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==", + "license": "MIT" }, "node_modules/cross-fetch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", - "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz", + "integrity": "sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==", + "license": "MIT", "dependencies": { - "node-fetch": "2.6.7" + "node-fetch": "^2.6.12" } }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -1417,123 +1407,135 @@ "node": ">= 8" } }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" + "node_modules/crossws": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/crossws/-/crossws-0.2.4.tgz", + "integrity": "sha512-DAxroI2uSOgUKLz00NX6A8U/8EE3SZHmIND+10jkVSaypvyt57J5JEOxAQOL6lQxyzi/wZbTIwssU1uy69h5Vg==", + "license": "MIT", + "peerDependencies": { + "uWebSockets.js": "*" }, "peerDependenciesMeta": { - "supports-color": { + "uWebSockets.js": { "optional": true } } }, - "node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, "node_modules/decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "license": "MIT", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, "node_modules/decode-uri-component": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "license": "MIT", "engines": { "node": ">=0.10" } }, - "node_modules/deep-eql": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", - "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", - "dependencies": { - "type-detect": "^4.0.0" - }, - "engines": { - "node": ">=6" - } + "node_modules/defu": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", + "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", + "license": "MIT" + }, + "node_modules/destr": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.3.tgz", + "integrity": "sha512-2N3BOUU4gYMpTP24s5rF5iP7BDr7uNTCs4ozw3kf/eKfvWSIu93GEBi5m427YoyJoeOzQ5smuu4nNAPGb8idSQ==", + "license": "MIT" }, "node_modules/detect-browser": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/detect-browser/-/detect-browser-5.3.0.tgz", - "integrity": "sha512-53rsFbGdwMwlF7qvCt0ypLM5V5/Mbl0szB7GPN8y9NCcbknYOeVVXdrXEq+90IwAfrrzt6Hd+u2E2ntakICU8w==" + "integrity": "sha512-53rsFbGdwMwlF7qvCt0ypLM5V5/Mbl0szB7GPN8y9NCcbknYOeVVXdrXEq+90IwAfrrzt6Hd+u2E2ntakICU8w==", + "license": "MIT" }, - "node_modules/diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "license": "Apache-2.0", + "bin": { + "detect-libc": "bin/detect-libc.js" + }, "engines": { - "node": ">=0.3.1" + "node": ">=0.10" } }, "node_modules/dijkstrajs": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.2.tgz", - "integrity": "sha512-QV6PMaHTCNmKSeP6QoXhVTw9snc9VD8MulTT0Bd99Pacp4SS1cjcrYPgBPmibqKVtMJJfqC6XvOXgPMEEPH/fg==" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", + "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==", + "license": "MIT" }, "node_modules/duplexify": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz", - "integrity": "sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz", + "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==", + "license": "MIT", "dependencies": { "end-of-stream": "^1.4.1", "inherits": "^2.0.3", "readable-stream": "^3.1.1", - "stream-shift": "^1.0.0" + "stream-shift": "^1.0.2" } }, - "node_modules/eip1193-provider": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/eip1193-provider/-/eip1193-provider-1.0.1.tgz", - "integrity": "sha512-kSuqwQ26d7CzuS/t3yRXo2Su2cVH0QfvyKbr2H7Be7O5YDyIq4hQGCNTo5wRdP07bt+E2R/8nPCzey4ojBHf7g==", + "node_modules/electron-to-chromium": { + "version": "1.5.19", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.19.tgz", + "integrity": "sha512-kpLJJi3zxTR1U828P+LIUDZ5ohixyo68/IcYOHLqnbTPr/wdgn4i1ECvmALN9E16JPA6cvCG5UG79gVwVdEK5w==", + "dev": true, + "license": "ISC" + }, + "node_modules/elliptic": { + "version": "6.5.7", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.7.tgz", + "integrity": "sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q==", + "license": "MIT", "dependencies": { - "@json-rpc-tools/provider": "^1.5.5" + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" } }, - "node_modules/electron-to-chromium": { - "version": "1.4.348", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.348.tgz", - "integrity": "sha512-gM7TdwuG3amns/1rlgxMbeeyNoBFPa+4Uu0c7FeROWh4qWmvSOnvcslKmWy51ggLKZ2n/F/4i2HJ+PVNxH9uCQ==", - "dev": true - }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" }, "node_modules/encode-utf8": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/encode-utf8/-/encode-utf8-1.0.3.tgz", - "integrity": "sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==" + "integrity": "sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==", + "license": "MIT" }, "node_modules/end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "license": "MIT", "dependencies": { "once": "^1.4.0" } }, "node_modules/enhanced-resolve": { - "version": "5.12.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz", - "integrity": "sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==", + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", + "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -1555,30 +1557,22 @@ } }, "node_modules/es-module-lexer": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", - "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", - "dev": true + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz", + "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==", + "dev": true, + "license": "MIT" }, "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -1630,22 +1624,48 @@ "node": ">=0.8.x" } }, + "node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fast-redact": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.1.2.tgz", - "integrity": "sha512-+0em+Iya9fKGfEQGcd62Yv6onjBmmhV1uh86XVfOU8VwAe6kaFdQCWI9s0/Nnugx5Vd9tdbZ7e6gE2tR9dzXdw==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz", + "integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==", + "license": "MIT", "engines": { "node": ">=6" } @@ -1660,9 +1680,10 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -1674,68 +1695,22 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==", + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "license": "MIT", "dependencies": { - "locate-path": "^6.0.0", + "locate-path": "^5.0.0", "path-exists": "^4.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "bin": { - "flat": "cli.js" - } - }, - "node_modules/follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "node": ">=8" } }, "node_modules/function-bind": { @@ -1748,41 +1723,34 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", - "engines": { - "node": "*" - } + "node_modules/get-port-please": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/get-port-please/-/get-port-please-3.1.2.tgz", + "integrity": "sha512-Gxc29eLs1fbn6LQ4jSU4vXjlwyZhF5HsGuMAa7gqBP4Rw4yxxltyDUuF5MBclFzDTXO+ACchGQoeela4DSfzdQ==", + "license": "MIT" }, - "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, + "node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "license": "MIT", "engines": { - "node": "*" + "node": ">=16" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", "dependencies": { "is-glob": "^4.0.1" }, @@ -1794,33 +1762,33 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true - }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } + "dev": true, + "license": "BSD-2-Clause" }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true + "dev": true, + "license": "ISC" + }, + "node_modules/h3": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/h3/-/h3-1.12.0.tgz", + "integrity": "sha512-Zi/CcNeWBXDrFNlV0hUBJQR9F7a96RjMeAZweW/ZWkR9fuXrMcvKnSA63f/zZ9l0GgQOZDVHGvXivNN9PWOwhA==", + "license": "MIT", + "dependencies": { + "cookie-es": "^1.1.0", + "crossws": "^0.2.4", + "defu": "^6.1.4", + "destr": "^2.0.3", + "iron-webcrypto": "^1.1.1", + "ohash": "^1.1.3", + "radix3": "^1.1.2", + "ufo": "^1.5.3", + "uncrypto": "^0.1.3", + "unenv": "^1.9.0" + } }, "node_modules/has": { "version": "1.0.3", @@ -1838,41 +1806,63 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "bin": { - "he": "bin/he" + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" } }, "node_modules/hey-listen": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/hey-listen/-/hey-listen-1.0.8.tgz", - "integrity": "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==" + "integrity": "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==", + "license": "MIT" }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "license": "MIT", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/http-shutdown": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/http-shutdown/-/http-shutdown-1.2.2.tgz", + "integrity": "sha512-S9wWkJ/VSY9/k4qcjG318bqJNruzE4HySUhFYknwmu6LBP97KLLfwNf+n4V1BHurvFNkSKLFnK/RsuUnRTf9Vw==", + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/idb-keyval": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.1.tgz", + "integrity": "sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg==", + "license": "Apache-2.0" }, "node_modules/import-local": { "version": "3.1.0", @@ -1893,19 +1883,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" }, "node_modules/interpret": { "version": "3.1.1", @@ -1916,10 +1898,20 @@ "node": ">=10.13.0" } }, + "node_modules/iron-webcrypto": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz", + "integrity": "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/brc-dd" + } + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "license": "MIT", "dependencies": { "binary-extensions": "^2.0.0" }, @@ -1939,10 +1931,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -1951,6 +1959,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", "engines": { "node": ">=8" } @@ -1959,6 +1968,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -1966,20 +1976,31 @@ "node": ">=0.10.0" } }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, "engines": { - "node": ">=0.12.0" + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=0.12.0" } }, "node_modules/is-plain-object": { @@ -1994,12 +2015,43 @@ "node": ">=0.10.0" } }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "license": "MIT", "engines": { - "node": ">=10" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is64bit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is64bit/-/is64bit-2.0.0.tgz", + "integrity": "sha512-jv+8jaWCl0g2lSBkNSVXdzfBA0npK1HGC2KtWM9FumFRoGS94g3NbCCLVnCYHLjp4GrW2KZeeSTMo5ddtznmGw==", + "license": "MIT", + "dependencies": { + "system-architecture": "^0.1.0" + }, + "engines": { + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -2008,8 +2060,7 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "node_modules/isobject": { "version": "3.0.1", @@ -2025,6 +2076,7 @@ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*", "merge-stream": "^2.0.0", @@ -2034,23 +2086,22 @@ "node": ">= 10.13.0" } }, + "node_modules/jiti": { + "version": "1.21.6", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", + "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT", "peer": true }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -2061,12 +2112,14 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/keyvaluestorage-interface": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/keyvaluestorage-interface/-/keyvaluestorage-interface-1.0.0.tgz", - "integrity": "sha512-8t6Q3TclQ4uZynJY9IGr2+SsIGwK9JHcO6ootkHCGA0CrQCRy+VkouYNO2xicET6b9al7QKzpebNow+gkpCL8g==" + "integrity": "sha512-8t6Q3TclQ4uZynJY9IGr2+SsIGwK9JHcO6ootkHCGA0CrQCRy+VkouYNO2xicET6b9al7QKzpebNow+gkpCL8g==", + "license": "MIT" }, "node_modules/kind-of": { "version": "6.0.3", @@ -2077,30 +2130,63 @@ "node": ">=0.10.0" } }, + "node_modules/listhen": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/listhen/-/listhen-1.7.2.tgz", + "integrity": "sha512-7/HamOm5YD9Wb7CFgAZkKgVPA96WwhcTQoqtm2VTZGVbVVn3IWKRBTgrU7cchA3Q8k9iCsG8Osoi9GX4JsGM9g==", + "license": "MIT", + "dependencies": { + "@parcel/watcher": "^2.4.1", + "@parcel/watcher-wasm": "^2.4.1", + "citty": "^0.1.6", + "clipboardy": "^4.0.0", + "consola": "^3.2.3", + "crossws": "^0.2.0", + "defu": "^6.1.4", + "get-port-please": "^3.1.2", + "h3": "^1.10.2", + "http-shutdown": "^1.2.2", + "jiti": "^1.21.0", + "mlly": "^1.6.1", + "node-forge": "^1.3.1", + "pathe": "^1.1.2", + "std-env": "^3.7.0", + "ufo": "^1.4.0", + "untun": "^0.1.3", + "uqr": "^0.1.2" + }, + "bin": { + "listen": "bin/listhen.mjs", + "listhen": "bin/listhen.mjs" + } + }, "node_modules/lit": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/lit/-/lit-2.6.1.tgz", - "integrity": "sha512-DT87LD64f8acR7uVp7kZfhLRrHkfC/N4BVzAtnw9Yg8087mbBJ//qedwdwX0kzDbxgPccWRW6mFwGbRQIxy0pw==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/lit/-/lit-2.8.0.tgz", + "integrity": "sha512-4Sc3OFX9QHOJaHbmTMk28SYgVxLN3ePDjg7hofEft2zWlehFL3LiAuapWc4U/kYwMYJSh2hTCPZ6/LIC7ii0MA==", + "license": "BSD-3-Clause", "dependencies": { "@lit/reactive-element": "^1.6.0", - "lit-element": "^3.2.0", - "lit-html": "^2.6.0" + "lit-element": "^3.3.0", + "lit-html": "^2.8.0" } }, "node_modules/lit-element": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-3.3.0.tgz", - "integrity": "sha512-M3OIoblNS7LZdRxOIk8g0wyLEA/lRw/UGJ1TX+767OpkuDsRdSoxBIvewpWqCo7sMd9xt1XedUNZIr9jUO1X3g==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-3.3.3.tgz", + "integrity": "sha512-XbeRxmTHubXENkV4h8RIPyr8lXc+Ff28rkcQzw3G6up2xg5E8Zu1IgOWIwBLEQsu3cOVFqdYwiVi0hv0SlpqUA==", + "license": "BSD-3-Clause", "dependencies": { "@lit-labs/ssr-dom-shim": "^1.1.0", "@lit/reactive-element": "^1.3.0", - "lit-html": "^2.7.0" + "lit-html": "^2.8.0" } }, "node_modules/lit-html": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-2.7.0.tgz", - "integrity": "sha512-/zPOl8EfeB3HHpTzINSpnWgvgQ8N07g/j272EOAIyB0Ys2RzBqTVT23i+JZuUlNbB2WHHeSsTCFi92NtWrtpqQ==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-2.8.0.tgz", + "integrity": "sha512-o9t+MQM3P4y7M7yNzqAyjp7z+mQGa4NS4CxiyLqFPyFWyc4O+nodLrkrxSaCTrla6M5YOLaT3RpbbqjszB5g3Q==", + "license": "BSD-3-Clause", "dependencies": { "@types/trusted-types": "^2.0.2" } @@ -2115,43 +2201,28 @@ } }, "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "license": "MIT", "dependencies": { - "p-locate": "^5.0.0" + "p-locate": "^4.1.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, "node_modules/lodash.isequal": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" - }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "license": "MIT" }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", "peer": true, "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" @@ -2160,24 +2231,41 @@ "loose-envify": "cli.js" } }, - "node_modules/loupe": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz", - "integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==", - "dependencies": { - "get-func-name": "^2.0.0" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=10.0.0" + } }, "node_modules/mime-db": { "version": "1.52.0", @@ -2200,89 +2288,70 @@ "node": ">= 0.6" } }, - "node_modules/minimatch": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", - "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mocha": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", - "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", - "dependencies": { - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.4", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "5.0.1", - "ms": "2.1.3", - "nanoid": "3.3.3", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "workerpool": "6.2.1", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "license": "MIT", "engines": { - "node": ">= 14.0.0" + "node": ">=12" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mochajs" + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "license": "ISC" + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", + "license": "MIT" + }, + "node_modules/mlly": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.1.tgz", + "integrity": "sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==", + "license": "MIT", + "dependencies": { + "acorn": "^8.11.3", + "pathe": "^1.1.2", + "pkg-types": "^1.1.1", + "ufo": "^1.5.3" } }, "node_modules/motion": { - "version": "10.15.5", - "resolved": "https://registry.npmjs.org/motion/-/motion-10.15.5.tgz", - "integrity": "sha512-ejP6KioN4pigTGxL93APzOnvtLklParL59UQB2T3HWXQBxFcIp5/7YXFmkgiA6pNKKzjvnLhnonRBN5iSFMnNw==", + "version": "10.16.2", + "resolved": "https://registry.npmjs.org/motion/-/motion-10.16.2.tgz", + "integrity": "sha512-p+PurYqfUdcJZvtnmAqu5fJgV2kR0uLFQuBKtLeFVTrYEVllI99tiOTSefVNYuip9ELTEkepIIDftNdze76NAQ==", + "license": "MIT", "dependencies": { "@motionone/animation": "^10.15.1", - "@motionone/dom": "^10.15.5", - "@motionone/svelte": "^10.15.5", + "@motionone/dom": "^10.16.2", + "@motionone/svelte": "^10.16.2", "@motionone/types": "^10.15.1", "@motionone/utils": "^10.15.1", - "@motionone/vue": "^10.15.5" + "@motionone/vue": "^10.16.2" } }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "license": "MIT", + "engines": { + "node": ">=4" + } }, "node_modules/multiformats": { "version": "9.9.0", "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz", - "integrity": "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==" - }, - "node_modules/nanoid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", - "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } + "integrity": "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==", + "license": "(Apache-2.0 AND MIT)" }, "node_modules/neo-async": { "version": "2.6.2", @@ -2290,10 +2359,17 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "license": "MIT" + }, "node_modules/node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", "dependencies": { "whatwg-url": "^5.0.0" }, @@ -2309,65 +2385,143 @@ } } }, + "node_modules/node-fetch-native": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.4.tgz", + "integrity": "sha512-IhOigYzAKHd244OC0JIMIUrjzctirCmPkaIfhDeGcEETWof5zKYUW7e7MYvChGWh/4CJeXEgsRyGzuF334rOOQ==", + "license": "MIT" + }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "license": "(BSD-3-Clause OR GPL-2.0)", + "engines": { + "node": ">= 6.13.0" + } + }, "node_modules/node-releases": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", - "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", - "dev": true + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "dev": true, + "license": "MIT" }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", "engines": { "node": ">=0.10.0" } }, + "node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ofetch": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/ofetch/-/ofetch-1.3.4.tgz", + "integrity": "sha512-KLIET85ik3vhEfS+3fDlc/BAZiAp+43QEC/yCo5zkNoY2YaKvNkOaFr/6wCFgFH1kuYQM5pMNi0Tg8koiIemtw==", + "license": "MIT", + "dependencies": { + "destr": "^2.0.3", + "node-fetch-native": "^1.6.3", + "ufo": "^1.5.3" + } + }, + "node_modules/ohash": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-1.1.3.tgz", + "integrity": "sha512-zuHHiGTYTA1sYJ/wZN+t5HKZaH23i4yI1HMwbuXm24Nid7Dv0KcuRlKoNKS9UNfAVSBlnGLcuQrnOKWOZoEGaw==", + "license": "MIT" + }, "node_modules/on-exit-leak-free": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-0.2.0.tgz", - "integrity": "sha512-dqaz3u44QbRXQooZLTUKU41ZrzYrcvLISVgbrzbyCMxpmSLJvZ3ZamIJIZ29P6OhZIkNIQKosdeM6t1LYbA9hg==" + "integrity": "sha512-dqaz3u44QbRXQooZLTUKU41ZrzYrcvLISVgbrzbyCMxpmSLJvZ3ZamIJIZ29P6OhZIkNIQKosdeM6t1LYbA9hg==", + "license": "MIT" }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", "dependencies": { "wrappy": "1" } }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "license": "MIT", "dependencies": { - "yocto-queue": "^0.1.0" + "mimic-fn": "^4.0.0" }, "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", "dependencies": { - "p-limit": "^3.0.2" + "p-try": "^2.0.0" }, "engines": { - "node": ">=10" + "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "license": "MIT", "engines": { "node": ">=6" } @@ -2380,19 +2534,10 @@ "node": ">=8" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "engines": { "node": ">=8" } @@ -2403,24 +2548,24 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, - "node_modules/pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", - "engines": { - "node": "*" - } + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "license": "MIT" }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", + "dev": true, + "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", "engines": { "node": ">=8.6" }, @@ -2432,6 +2577,7 @@ "version": "7.11.0", "resolved": "https://registry.npmjs.org/pino/-/pino-7.11.0.tgz", "integrity": "sha512-dMACeu63HtRLmCG8VKdy4cShCPKaYDR4youZqoSWLxl5Gu99HUw8bw75thbPv9Nip+H+QYX8o3ZJbTdVZZ2TVg==", + "license": "MIT", "dependencies": { "atomic-sleep": "^1.0.0", "fast-redact": "^3.0.0", @@ -2451,257 +2597,99 @@ }, "node_modules/pino-abstract-transport": { "version": "0.5.0", - "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-0.5.0.tgz", - "integrity": "sha512-+KAgmVeqXYbTtU2FScx1XS3kNyfZ5TrXY07V96QnUSFqo2gAqlvmaxH67Lj7SWazqsMabf+58ctdTcBgnOLUOQ==", - "dependencies": { - "duplexify": "^4.1.2", - "split2": "^4.0.0" - } - }, - "node_modules/pino-std-serializers": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-4.0.0.tgz", - "integrity": "sha512-cK0pekc1Kjy5w9V2/n+8MkZwusa6EyyxfeQCB799CQRhRt/CqYKiWs5adeu8Shve2ZNffvfC/7J64A2PJo1W/Q==" - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pngjs": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", - "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/process-warning": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-1.0.0.tgz", - "integrity": "sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q==" - }, - "node_modules/proxy-compare": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/proxy-compare/-/proxy-compare-2.4.0.tgz", - "integrity": "sha512-FD8KmQUQD6Mfpd0hywCOzcon/dbkFP8XBd9F1ycbKtvVsfv6TsFUKJ2eC0Iz2y+KzlkdT1Z8SY6ZSgm07zOyqg==" - }, - "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/qrcode": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.1.tgz", - "integrity": "sha512-nS8NJ1Z3md8uTjKtP+SGGhfqmTCs5flU/xR623oI0JX+Wepz9R8UrRVCTBTJm3qGw3rH6jJ6MUHjkDx15cxSSg==", - "dependencies": { - "dijkstrajs": "^1.0.1", - "encode-utf8": "^1.0.3", - "pngjs": "^5.0.0", - "yargs": "^15.3.1" - }, - "bin": { - "qrcode": "bin/qrcode" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/qrcode/node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/qrcode/node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "node_modules/qrcode/node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/qrcode/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/qrcode/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/qrcode/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-0.5.0.tgz", + "integrity": "sha512-+KAgmVeqXYbTtU2FScx1XS3kNyfZ5TrXY07V96QnUSFqo2gAqlvmaxH67Lj7SWazqsMabf+58ctdTcBgnOLUOQ==", + "license": "MIT", "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "duplexify": "^4.1.2", + "split2": "^4.0.0" } }, - "node_modules/qrcode/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "node_modules/pino-std-serializers": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-4.0.0.tgz", + "integrity": "sha512-cK0pekc1Kjy5w9V2/n+8MkZwusa6EyyxfeQCB799CQRhRt/CqYKiWs5adeu8Shve2ZNffvfC/7J64A2PJo1W/Q==", + "license": "MIT" + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, "dependencies": { - "p-limit": "^2.2.0" + "find-up": "^4.0.0" }, "engines": { "node": ">=8" } }, - "node_modules/qrcode/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "node_modules/pkg-types": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.2.0.tgz", + "integrity": "sha512-+ifYuSSqOQ8CqP4MbZA5hDpb97n3E8SVWdJe+Wms9kj745lmd3b7EZJiqvmLwAlmRfjrI7Hi5z3kdBJ93lFNPA==", + "license": "MIT", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, + "confbox": "^0.1.7", + "mlly": "^1.7.1", + "pathe": "^1.1.2" + } + }, + "node_modules/pngjs": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", + "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=10.13.0" } }, - "node_modules/qrcode/node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" + "node_modules/process-warning": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-1.0.0.tgz", + "integrity": "sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q==", + "license": "MIT" }, - "node_modules/qrcode/node_modules/yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - }, + "node_modules/proxy-compare": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/proxy-compare/-/proxy-compare-2.5.1.tgz", + "integrity": "sha512-oyfc0Tx87Cpwva5ZXezSp5V9vht1c7dZBhvuV/y3ctkgMVUmiAGDVeeB0dKhGSyT0v1ZTEQYpe/RXlBVBNuCLA==", + "license": "MIT" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=6" } }, - "node_modules/qrcode/node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "node_modules/qrcode": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.3.tgz", + "integrity": "sha512-puyri6ApkEHYiVl4CFzo1tDkAZ+ATcnbJrJ6RiBM1Fhctdn/ix9MTE3hRph33omisEbC/2fcfemsseiKgBPKZg==", + "license": "MIT", "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" + "dijkstrajs": "^1.0.1", + "encode-utf8": "^1.0.3", + "pngjs": "^5.0.0", + "yargs": "^15.3.1" + }, + "bin": { + "qrcode": "bin/qrcode" }, "engines": { - "node": ">=6" + "node": ">=10.13.0" } }, "node_modules/query-string": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.1.tgz", - "integrity": "sha512-MplouLRDHBZSG9z7fpuAAcI7aAYjDLhtsiVZsevsfaHWDS2IDdORKbSd1kWUA+V4zyva/HZoSfpwnYMMQDhb0w==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz", + "integrity": "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==", + "license": "MIT", "dependencies": { - "decode-uri-component": "^0.2.0", + "decode-uri-component": "^0.2.2", "filter-obj": "^1.1.0", "split-on-first": "^1.0.0", "strict-uri-encode": "^2.0.0" @@ -2716,20 +2704,30 @@ "node_modules/quick-format-unescaped": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", - "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==" + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", + "license": "MIT" + }, + "node_modules/radix3": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/radix3/-/radix3-1.1.2.tgz", + "integrity": "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==", + "license": "MIT" }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "license": "MIT", "dependencies": { "safe-buffer": "^5.1.0" } }, "node_modules/react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", "peer": true, "dependencies": { "loose-envify": "^1.1.0" @@ -2742,6 +2740,7 @@ "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -2755,6 +2754,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "license": "MIT", "dependencies": { "picomatch": "^2.2.1" }, @@ -2766,6 +2766,7 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.1.0.tgz", "integrity": "sha512-r/H9MzAWtrv8aSVjPCMFpDMl5q66GqtmmRkRjpHTsp4zBAa+snZyiQNlMONiUmEJcsnaw0wCauJ2GWODr/aFkg==", + "license": "MIT", "engines": { "node": ">= 12.13.0" } @@ -2786,6 +2787,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -2793,7 +2795,8 @@ "node_modules/require-main-filename": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "license": "ISC" }, "node_modules/resolve": { "version": "1.22.1", @@ -2852,24 +2855,21 @@ } ] }, - "node_modules/safe-json-utils": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/safe-json-utils/-/safe-json-utils-1.1.1.tgz", - "integrity": "sha512-SAJWGKDs50tAbiDXLf89PDwt9XYkWyANFWVzn4dTXl5QyI8t2o/bW5/OJl3lvc2WVU4MEpTo9Yz5NVFNsp+OJQ==" - }, "node_modules/safe-stable-stringify": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", - "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "license": "MIT", "engines": { "node": ">=10" } }, "node_modules/schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dev": true, + "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", @@ -2884,9 +2884,11 @@ } }, "node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "license": "BSD-3-Clause", "dependencies": { "randombytes": "^2.1.0" } @@ -2894,7 +2896,8 @@ "node_modules/set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "license": "ISC" }, "node_modules/shallow-clone": { "version": "3.0.1", @@ -2912,7 +2915,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -2924,15 +2926,27 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "engines": { "node": ">=8" } }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/sonic-boom": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-2.8.0.tgz", "integrity": "sha512-kuonw1YOYYNOve5iHdSahXPOK49GqwA+LZhI6Wz/l0rP57iKyXXIHaRagOBHAPmGwJC6od2Z9zgvZ5loSgMlVg==", + "license": "MIT", "dependencies": { "atomic-sleep": "^1.0.0" } @@ -2942,6 +2956,7 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -2951,6 +2966,7 @@ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, + "license": "MIT", "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -2960,6 +2976,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", + "license": "MIT", "engines": { "node": ">=6" } @@ -2968,19 +2985,28 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", "engines": { "node": ">= 10.x" } }, + "node_modules/std-env": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz", + "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==", + "license": "MIT" + }, "node_modules/stream-shift": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", - "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", + "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==", + "license": "MIT" }, "node_modules/strict-uri-encode": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==", + "license": "MIT", "engines": { "node": ">=4" } @@ -2989,6 +3015,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", "dependencies": { "safe-buffer": "~5.2.0" } @@ -2997,6 +3024,7 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -3010,6 +3038,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -3017,12 +3046,13 @@ "node": ">=8" } }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -3032,6 +3062,8 @@ "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -3054,23 +3086,37 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/system-architecture": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/system-architecture/-/system-architecture-0.1.0.tgz", + "integrity": "sha512-ulAk51I9UVUyJgxlv9M6lFot2WP3e7t8Kz9+IS6D4rVba1tR9kON+Ey69f+1R4Q8cd45Lod6a4IcJIxnzGc/zA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/terser": { - "version": "5.16.8", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.8.tgz", - "integrity": "sha512-QI5g1E/ef7d+PsDifb+a6nnVgC4F22Bg6T0xrBrz6iloVB4PUkkunp6V8nzoOOZJIzjWVdAGqCdlKlhLq/TbIA==", + "version": "5.32.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.32.0.tgz", + "integrity": "sha512-v3Gtw3IzpBJ0ugkxEX8U0W6+TnPKRRCWGh1jC/iM/e3Ki5+qvO1L1EAZ56bZasc64aXHwRHNIQEzm6//i5cemQ==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, @@ -3082,16 +3128,17 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.7", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.7.tgz", - "integrity": "sha512-AfKwIktyP7Cu50xNjXF/6Qb5lBNzYaWpU6YfoX3uZicTx0zTy0stDDCsvjDapKsSDvOeWo5MEq4TmdBy2cNoHw==", + "version": "5.3.10", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", + "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", "dev": true, + "license": "MIT", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.17", + "@jridgewell/trace-mapping": "^0.3.20", "jest-worker": "^27.4.5", "schema-utils": "^3.1.1", "serialize-javascript": "^6.0.1", - "terser": "^5.16.5" + "terser": "^5.26.0" }, "engines": { "node": ">= 10.13.0" @@ -3115,29 +3162,11 @@ } } }, - "node_modules/terser-webpack-plugin/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" - } - }, - "node_modules/terser-webpack-plugin/node_modules/serialize-javascript": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", - "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", - "dev": true, - "dependencies": { - "randombytes": "^2.1.0" - } - }, "node_modules/thread-stream": { "version": "0.15.2", "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-0.15.2.tgz", "integrity": "sha512-UkEhKIg2pD+fjkHQKyJO3yoIvAP3N6RlNFt2dUhcS1FGvCD1cQa1M/PGknCLFIyZdtJOWQjejp7bdNqmN7zwdA==", + "license": "MIT", "dependencies": { "real-require": "^0.1.0" } @@ -3146,6 +3175,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -3156,96 +3186,148 @@ "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "node_modules/ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "node_modules/ts-node/node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "engines": { - "node": ">=0.3.1" - } + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" }, "node_modules/tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "license": "0BSD" }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "engines": { - "node": ">=4" + "node_modules/ufo": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz", + "integrity": "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==", + "license": "MIT" + }, + "node_modules/uint8arrays": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/uint8arrays/-/uint8arrays-3.1.0.tgz", + "integrity": "sha512-ei5rfKtoRO8OyOIor2Rz5fhzjThwIHJZ3uyDPnDHTXbP0aMQ1RN/6AI5B5d9dBxJOU+BvOAk7ZQ1xphsX8Lrog==", + "license": "MIT", + "dependencies": { + "multiformats": "^9.4.2" } }, - "node_modules/typescript": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.3.tgz", - "integrity": "sha512-xv8mOEDnigb/tN9PSMTwSEqAnUvkoXMQlicOb0IUVDBSQCgBSaAAROUZYy2IcUy5qU6XajK5jjjO7TMWqBTKZA==", - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" + "node_modules/uncrypto": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/uncrypto/-/uncrypto-0.1.3.tgz", + "integrity": "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==", + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true, + "license": "MIT" + }, + "node_modules/unenv": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/unenv/-/unenv-1.10.0.tgz", + "integrity": "sha512-wY5bskBQFL9n3Eca5XnhH6KbUo/tfvkwm9OpcdCvLaeA7piBNbavbOKJySEwQ1V0RH6HvNlSAFRTpvTqgKRQXQ==", + "license": "MIT", + "dependencies": { + "consola": "^3.2.3", + "defu": "^6.1.4", + "mime": "^3.0.0", + "node-fetch-native": "^1.6.4", + "pathe": "^1.1.2" + } + }, + "node_modules/unstorage": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.12.0.tgz", + "integrity": "sha512-ARZYTXiC+e8z3lRM7/qY9oyaOkaozCeNd2xoz7sYK9fv7OLGhVsf+BZbmASqiK/HTZ7T6eAlnVq9JynZppyk3w==", + "license": "MIT", + "dependencies": { + "anymatch": "^3.1.3", + "chokidar": "^3.6.0", + "destr": "^2.0.3", + "h3": "^1.12.0", + "listhen": "^1.7.2", + "lru-cache": "^10.4.3", + "mri": "^1.2.0", + "node-fetch-native": "^1.6.4", + "ofetch": "^1.3.4", + "ufo": "^1.5.4" }, - "engines": { - "node": ">=12.20" + "peerDependencies": { + "@azure/app-configuration": "^1.7.0", + "@azure/cosmos": "^4.1.1", + "@azure/data-tables": "^13.2.2", + "@azure/identity": "^4.4.1", + "@azure/keyvault-secrets": "^4.8.0", + "@azure/storage-blob": "^12.24.0", + "@capacitor/preferences": "^6.0.2", + "@netlify/blobs": "^6.5.0 || ^7.0.0", + "@planetscale/database": "^1.19.0", + "@upstash/redis": "^1.34.0", + "@vercel/kv": "^1.0.1", + "idb-keyval": "^6.2.1", + "ioredis": "^5.4.1" + }, + "peerDependenciesMeta": { + "@azure/app-configuration": { + "optional": true + }, + "@azure/cosmos": { + "optional": true + }, + "@azure/data-tables": { + "optional": true + }, + "@azure/identity": { + "optional": true + }, + "@azure/keyvault-secrets": { + "optional": true + }, + "@azure/storage-blob": { + "optional": true + }, + "@capacitor/preferences": { + "optional": true + }, + "@netlify/blobs": { + "optional": true + }, + "@planetscale/database": { + "optional": true + }, + "@upstash/redis": { + "optional": true + }, + "@vercel/kv": { + "optional": true + }, + "idb-keyval": { + "optional": true + }, + "ioredis": { + "optional": true + } } }, - "node_modules/uint8arrays": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/uint8arrays/-/uint8arrays-3.1.1.tgz", - "integrity": "sha512-+QJa8QRnbdXVpHYjLoTpJIdCTiw9Ir62nocClWuXIq2JIh4Uta0cQsTSpFL678p2CN8B+XSApwcU+pQEqVpKWg==", + "node_modules/untun": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/untun/-/untun-0.1.3.tgz", + "integrity": "sha512-4luGP9LMYszMRZwsvyUd9MrxgEGZdZuZgpVQHEEX0lCYFESasVRvZd0EYpCkOIbJKHMuv0LskpXc/8Un+MJzEQ==", + "license": "MIT", "dependencies": { - "multiformats": "^9.4.2" + "citty": "^0.1.5", + "consola": "^3.2.3", + "pathe": "^1.1.1" + }, + "bin": { + "untun": "bin/untun.mjs" } }, "node_modules/update-browserslist-db": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", - "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", + "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", "dev": true, "funding": [ { @@ -3255,24 +3337,36 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" + "escalade": "^3.1.2", + "picocolors": "^1.0.1" }, "bin": { - "browserslist-lint": "cli.js" + "update-browserslist-db": "cli.js" }, "peerDependencies": { "browserslist": ">= 4.21.0" } }, + "node_modules/uqr": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/uqr/-/uqr-0.1.2.tgz", + "integrity": "sha512-MJu7ypHq6QasgF5YRTjqscSzQp/W11zoUk6kvmlH+fmWEs63Y0Eib13hYFwAzagRJcVY8WVnlV+eBDUGMJ5IbA==", + "license": "MIT" + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" } @@ -3281,6 +3375,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "license": "MIT", "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } @@ -3288,38 +3383,40 @@ "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==" + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" }, "node_modules/valtio": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/valtio/-/valtio-1.9.0.tgz", - "integrity": "sha512-mQLFsAlKbYascZygFQh6lXuDjU5WHLoeZ8He4HqMnWfasM96V6rDbeFkw1XeG54xycmDonr/Jb4xgviHtuySrA==", + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/valtio/-/valtio-1.11.2.tgz", + "integrity": "sha512-1XfIxnUXzyswPAPXo1P3Pdx2mq/pIqZICkWN60Hby0d9Iqb+MEIpqgYVlbflvHdrp2YR/q3jyKWRPJJ100yxaw==", + "license": "MIT", "dependencies": { - "proxy-compare": "2.4.0", + "proxy-compare": "2.5.1", "use-sync-external-store": "1.2.0" }, "engines": { "node": ">=12.20.0" }, "peerDependencies": { + "@types/react": ">=16.8", "react": ">=16.8" }, "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, "react": { "optional": true } } }, "node_modules/watchpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", + "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", "dev": true, + "license": "MIT", "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" @@ -3331,37 +3428,38 @@ "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" }, "node_modules/webpack": { - "version": "5.77.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.77.0.tgz", - "integrity": "sha512-sbGNjBr5Ya5ss91yzjeJTLKyfiwo5C628AFjEa6WSXcZa4E+F57om3Cc8xLb1Jh0b243AWuSYRf3dn7HVeFQ9Q==", + "version": "5.94.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz", + "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==", "dev": true, + "license": "MIT", "dependencies": { - "@types/eslint-scope": "^3.7.3", - "@types/estree": "^0.0.51", - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/wasm-edit": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", + "@types/estree": "^1.0.5", + "@webassemblyjs/ast": "^1.12.1", + "@webassemblyjs/wasm-edit": "^1.12.1", + "@webassemblyjs/wasm-parser": "^1.12.1", "acorn": "^8.7.1", - "acorn-import-assertions": "^1.7.6", - "browserslist": "^4.14.5", + "acorn-import-attributes": "^1.9.5", + "browserslist": "^4.21.10", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.10.0", - "es-module-lexer": "^0.9.0", + "enhanced-resolve": "^5.17.1", + "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", + "graceful-fs": "^4.2.11", "json-parse-even-better-errors": "^2.3.1", "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^3.1.0", + "schema-utils": "^3.2.0", "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.3", - "watchpack": "^2.4.0", + "terser-webpack-plugin": "^5.3.10", + "watchpack": "^2.4.1", "webpack-sources": "^3.2.3" }, "bin": { @@ -3381,17 +3479,18 @@ } }, "node_modules/webpack-cli": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.0.1.tgz", - "integrity": "sha512-S3KVAyfwUqr0Mo/ur3NzIp6jnerNpo7GUO6so51mxLi1spqsA17YcMXy0WOIJtBSnj748lthxC6XLbNKh/ZC+A==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz", + "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", "dev": true, + "license": "MIT", "dependencies": { "@discoveryjs/json-ext": "^0.5.0", - "@webpack-cli/configtest": "^2.0.1", - "@webpack-cli/info": "^2.0.1", - "@webpack-cli/serve": "^2.0.1", + "@webpack-cli/configtest": "^2.1.1", + "@webpack-cli/info": "^2.0.2", + "@webpack-cli/serve": "^2.0.5", "colorette": "^2.0.14", - "commander": "^9.4.1", + "commander": "^10.0.1", "cross-spawn": "^7.0.3", "envinfo": "^7.7.3", "fastest-levenshtein": "^1.0.12", @@ -3426,12 +3525,13 @@ } }, "node_modules/webpack-cli/node_modules/commander": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", - "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", "dev": true, + "license": "MIT", "engines": { - "node": "^12.20.0 || >=14" + "node": ">=14" } }, "node_modules/webpack-merge": { @@ -3460,6 +3560,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" @@ -3469,7 +3570,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "dependencies": { "isexe": "^2.0.0" }, @@ -3481,9 +3581,10 @@ } }, "node_modules/which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "license": "ISC" }, "node_modules/wildcard": { "version": "2.0.0", @@ -3491,36 +3592,31 @@ "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", "dev": true }, - "node_modules/workerpool": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", - "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==" - }, "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "node": ">=8" } }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" }, "node_modules/ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "license": "MIT", "engines": { "node": ">=8.3.0" }, @@ -3538,70 +3634,45 @@ } }, "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "engines": { - "node": ">=10" - } + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "license": "ISC" }, "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "license": "MIT", "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" }, "engines": { - "node": ">=10" + "node": ">=8" } }, "node_modules/yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "license": "ISC", "dependencies": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "engines": { "node": ">=6" } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } } } } diff --git a/web-ethereum/account_dapp/walletconnect/package.json b/web-ethereum/account_dapp/walletconnect/package.json index 3b4343c6d..1ab40a7e8 100644 --- a/web-ethereum/account_dapp/walletconnect/package.json +++ b/web-ethereum/account_dapp/walletconnect/package.json @@ -7,11 +7,10 @@ "build": "npx webpack ./main.js" }, "dependencies": { - "@walletconnect/ethereum-provider": "^2.4.7", - "@web3modal/standalone": "^2.1.3" + "@walletconnect/ethereum-provider": "^2.16.1" }, "devDependencies": { - "webpack": "^5.77.0", - "webpack-cli": "^5.0.1" + "webpack": "^5.94.0", + "webpack-cli": "^5.1.4" } } diff --git a/web-ethereum/stake_dapp/lib/pages/stake_dapp_home.dart b/web-ethereum/stake_dapp/lib/pages/stake_dapp_home.dart index 8d1165430..c03067d0f 100644 --- a/web-ethereum/stake_dapp/lib/pages/stake_dapp_home.dart +++ b/web-ethereum/stake_dapp/lib/pages/stake_dapp_home.dart @@ -1,3 +1,4 @@ +import 'package:intl/intl.dart'; import 'package:orchid/api/orchid_eth/chains.dart'; import 'package:orchid/api/orchid_eth/token_type.dart'; import 'package:orchid/api/orchid_eth/tokens.dart'; @@ -36,7 +37,15 @@ class _StakeDappHomeState extends DappHomeStateBase { final _stakeeField = AddressValueFieldController(); final _scrollController = ScrollController(); - Token? _currentStake; + // The total stake staked for the stakee by all stakers. + Token? _currentStakeTotal; + + // The amount and delay staked for the stakee by the current staker (wallet). + StakeResult? _currentStakeStaker; + + // The amount and expiration of the pulled stake pending withdrawal for the first n indexes. + List? _currentStakePendingStaker; + Location? _currentLocation; @override @@ -74,12 +83,12 @@ class _StakeDappHomeState extends DappHomeStateBase { children: [ DappHomeHeader( web3Context: web3Context, - setNewContext: setNewContext, contractVersionsAvailable: contractVersionsAvailable, contractVersionSelected: contractVersionSelected, selectContractVersion: selectContractVersion, // deployContract: deployContract, connectEthereum: connectEthereum, + connectWalletConnect: connectWalletConnect, disconnect: disconnect, showChainSelector: false, ).padx(24).top(30).bottom(24), @@ -161,42 +170,149 @@ class _StakeDappHomeState extends DappHomeStateBase { ).top(24).padx(8), // Current stake - AnimatedVisibility( - show: _stakee != null && _currentStake != null, - child: Column( - children: [ - Row( - children: [ - Text("Current Stake").title.white, - ], - ), - TokenValueWidgetRow( - tokenType: Tokens.OXT, - value: _currentStake, - context: context, - child: Text(_currentStake?.formatCurrency( - locale: context.locale, precision: 2) ?? - '') - .title - .white, - price: price, - ).top(8), - ], - ).top(24).padx(24), - ), + _buildCurrentStakePanel(price), // Tabs StakeTabs( web3Context: web3Context, stakee: _stakee, - currentStake: _currentStake, + currentStake: _currentStakeStaker?.amount, price: price, + currentStakeDelay: _currentStakeStaker?.delay, ).top(40), ], ), ); } + AnimatedVisibility _buildCurrentStakePanel(USD? price) { + // Format the delay (seconds) as a human-readable string + final delaySeconds = _currentStakeStaker?.delay; + String delayString = _formatDelay(delaySeconds); + final showDelay = delaySeconds != null && delaySeconds > BigInt.zero; + return AnimatedVisibility( + show: _stakee != null && _currentStakeTotal != null, + child: Column( + children: [ + // Total stake + Column( + children: [ + Row( + children: [ + Text("Total Stake (All Stakers)").title.white, + ], + ), + TokenValueWidgetRow( + tokenType: Tokens.OXT, + value: _currentStakeTotal, + context: context, + child: Text(_currentStakeTotal?.formatCurrency( + locale: context.locale, precision: 2) ?? + '') + .title + .white, + price: price, + ).top(0), + ], + ).top(32).padx(24), + + // Staker stake + Column( + children: [ + Row( + children: [ + Text("Current Stake (This Wallet)").title.white, + ], + ), + TokenValueWidgetRow( + tokenType: Tokens.OXT, + value: _currentStakeStaker?.amount, + context: context, + child: Text(_currentStakeStaker?.amount.formatCurrency( + locale: context.locale, precision: 2) ?? + '') + .title + .white, + price: price, + ).top(0), + if (showDelay) + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text("Delay").title.white, + Text(delayString).title.white, + ], + ), + ], + ).top(16).padx(24).bottom(12), + + ..._buildPendingPulls(price), + ], + ), + ); + } + + List _buildPendingPulls(USD? price) { + return (_currentStakePendingStaker ?? []) + .where((pending) => pending.amount.isNotZero()) + .mapIndexed((pending, index) { + final expireDate = + DateTime.fromMillisecondsSinceEpoch(pending.expire.toInt() * 1000); + final showExpire = expireDate.isAfter(DateTime.now()); + final expireString = showExpire + ? DateFormat('MM/dd/yyyy HH:mm').format(expireDate.toLocal()) + : ''; + final expireLabel = showExpire ? "Locked Until" : "Ready to Withdraw"; + + return RoundedRect( + backgroundColor: OrchidColors.new_purple, + child: Column( + children: [ + Row( + children: [ + Text("Pending Pull Request (Index: $index)").title.white, + ], + ), + TokenValueWidgetRow( + tokenType: Tokens.OXT, + value: pending.amount, + context: context, + child: Text(pending.amount + .formatCurrency(locale: context.locale, precision: 2)) + .title + .white, + price: price, + ).top(0), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(expireLabel).title.white, + Text(expireString).title.white, + ], + ), + ], + ).top(16).padx(24).bottom(16), + ); + }).toList(); + } + + String _formatDelay(BigInt? delaySeconds) { + String delayString = "..."; + if (delaySeconds != null) { + final delayDaysInt = delaySeconds.toInt() ~/ 86400; + if (delayDaysInt >= 1) { + delayString = "${delayDaysInt} days"; + } else { + if (delaySeconds > BigInt.zero) { + delayString = "${delaySeconds.toInt()} seconds"; + } else { + delayString = "None"; + } + } + } + return delayString; + } + Widget _buildProviderView() { return ConstrainedBox( constraints: BoxConstraints(maxWidth: altColumnWidth), @@ -234,11 +350,43 @@ class _StakeDappHomeState extends DappHomeStateBase { void _updateStake() async { if (_stakee != null && web3Context != null) { - _currentStake = - await OrchidWeb3StakeV0(web3Context!).orchidGetStake(_stakee!); - log("XXX: heft = $_currentStake"); + // Get the total stake for all stakers (heft) + final orchidWeb3 = OrchidWeb3StakeV0(web3Context!); + _currentStakeTotal = await orchidWeb3.orchidGetTotalStake(_stakee!); + log("XXX: heft = $_currentStakeTotal"); + + // Get the stake for this staker (wallet) + try { + _currentStakeStaker = await orchidWeb3.orchidGetStakeForStaker( + staker: web3Context!.walletAddress!, + stakee: _stakee!, + ); + log("XXX: staker stake = $_currentStakeStaker"); + } catch (err, stack) { + log("Error getting stake for staker: $err"); + log(stack.toString()); + } + + // Get the pending stake withdrawals for this staker (wallet) + try { + List pendingList = []; + for (var i = 0; i < 3; i++) { + final pending = await orchidWeb3.orchidGetPendingWithdrawal( + staker: web3Context!.walletAddress!, + index: i, + ); + pendingList.add(pending); + } + _currentStakePendingStaker = pendingList; + log("XXX: pending = $_currentStakePendingStaker"); + } catch (err, stack) { + log("Error getting stake for staker: $err"); + log(stack.toString()); + } } else { - _currentStake = null; + _currentStakeTotal = null; + _currentStakeStaker = null; + _currentStakePendingStaker = null; } setState(() {}); } diff --git a/web-ethereum/stake_dapp/lib/pages/tabs/add_stake_panel.dart b/web-ethereum/stake_dapp/lib/pages/tabs/add_stake_panel.dart index 276453f90..fc5933953 100644 --- a/web-ethereum/stake_dapp/lib/pages/tabs/add_stake_panel.dart +++ b/web-ethereum/stake_dapp/lib/pages/tabs/add_stake_panel.dart @@ -4,6 +4,7 @@ import 'package:orchid/api/orchid_eth/tokens.dart'; import 'package:orchid/api/pricing/usd.dart'; import 'package:orchid/dapp/orchid/dapp_button.dart'; import 'package:orchid/dapp/orchid/dapp_tab_context.dart'; +import 'package:orchid/dapp/orchid_web3/orchid_erc20.dart'; import 'package:orchid/dapp/orchid_web3/orchid_web3_context.dart'; import 'package:orchid/dapp/preferences/dapp_transaction.dart'; import 'package:orchid/dapp/preferences/user_preferences_dapp.dart'; @@ -17,6 +18,7 @@ class AddStakePanel extends StatefulWidget { final Token? currentStake; final USD? price; final bool enabled; + final BigInt? currentStakeDelay; const AddStakePanel({ super.key, @@ -25,6 +27,7 @@ class AddStakePanel extends StatefulWidget { required this.currentStake, required this.price, required this.enabled, + required this.currentStakeDelay, }); @override @@ -40,6 +43,18 @@ class _AddStakePanelState extends State @override TokenType get tokenType => Tokens.OXT; + // non-null and zero + bool get _currentStakeDelayIsZero { + return widget.currentStakeDelay != null && + widget.currentStakeDelay! == BigInt.zero; + } + + // non-null and non-zero + bool get _currentStakeDelayIsNonZero { + return widget.currentStakeDelay != null && + widget.currentStakeDelay! > BigInt.zero; + } + @override void initState() { super.initState(); @@ -59,7 +74,16 @@ class _AddStakePanelState extends State error: _addStakeFieldError, usdPrice: widget.price, ).top(32).padx(8), - Text("Delay is zero").white.caption.top(16), + + // Delay label (if non-null) + // if (_currentStakeDelayIsZero) + // Text("Added funds will be staked with no withdrawal delay.").white.caption.top(16), + // if (_currentStakeDelayIsNonZero) + Text("This UI does not support adding funds to an existing stake with a non-zero delay.") + .caption + .error + .top(24), + DappButton( text: s.addFunds, onPressed: _formEnabled ? _addStake : null, @@ -75,6 +99,7 @@ class _AddStakePanelState extends State bool get _formEnabled { return !txPending && + _currentStakeDelayIsZero && _addStakeFieldValid && _addToStakeAmountController.value!.gtZero(); } @@ -103,20 +128,44 @@ class _AddStakePanelState extends State setState(() { txPending = true; }); + + bool hasApproval = false; + final progress = ERC20PayableTransactionCallbacks( + onApproval: (txHash) async { + hasApproval = true; + await UserPreferencesDapp().addTransaction(DappTransaction( + transactionHash: txHash, + chainId: web3Context!.chain.chainId, + // always Ethereum + type: DappTransactionType.addFunds, + subtype: "approve", + // TODO: Localize + series_index: 1, + series_total: 2, + )); + }, + onTransaction: (txHash) async { + await UserPreferencesDapp().addTransaction(DappTransaction( + transactionHash: txHash, + chainId: web3Context!.chain.chainId, + // always Ethereum + type: DappTransactionType.addFunds, + subtype: "push", + // TODO: Localize + series_index: hasApproval ? 2 : null, + series_total: hasApproval ? 2 : null, + )); + }, + ); + try { - var txHashes = await OrchidWeb3StakeV0(web3Context!).orchidStakePushFunds( + await OrchidWeb3StakeV0(web3Context!).orchidStakePushFunds( wallet: wallet, stakee: stakee, amount: amount, + callbacks: progress, ); - UserPreferencesDapp() - .addTransactions(txHashes.map((hash) => DappTransaction( - transactionHash: hash, - chainId: web3Context!.chain.chainId, // always Ethereum - type: DappTransactionType.addFunds, - ))); - _addToStakeAmountController.clear(); setState(() {}); } catch (err) { diff --git a/web-ethereum/stake_dapp/lib/pages/tabs/pull_stake_panel.dart b/web-ethereum/stake_dapp/lib/pages/tabs/pull_stake_panel.dart index 6d9b1fc0e..fd1b6da45 100644 --- a/web-ethereum/stake_dapp/lib/pages/tabs/pull_stake_panel.dart +++ b/web-ethereum/stake_dapp/lib/pages/tabs/pull_stake_panel.dart @@ -9,7 +9,6 @@ import 'package:orchid/dapp/preferences/dapp_transaction.dart'; import 'package:orchid/dapp/preferences/user_preferences_dapp.dart'; import 'package:orchid/orchid/field/orchid_labeled_numeric_field.dart'; import 'package:orchid/orchid/field/orchid_labeled_token_value_field.dart'; -import 'package:orchid/orchid/menu/expanding_popup_menu_item.dart'; import 'package:orchid/orchid/orchid.dart'; import 'package:orchid/stake_dapp/orchid_web3_stake_v0.dart'; @@ -98,7 +97,9 @@ class _PullStakePanelState extends State bool get _pullStakeFieldValid { var value = _pullStakeAmountController.value; return value != null && - value <= (widget.currentStake ?? tokenType.zero); + value <= (widget.currentStake ?? tokenType.zero) && + // Limit indices to the ones that we currently check (by brute force). + (_indexController.value ?? 0).toInt() < 3; } void _pullStake() async { diff --git a/web-ethereum/stake_dapp/lib/pages/tabs/stake_tabs.dart b/web-ethereum/stake_dapp/lib/pages/tabs/stake_tabs.dart index 2fe290b77..1dfe330fa 100644 --- a/web-ethereum/stake_dapp/lib/pages/tabs/stake_tabs.dart +++ b/web-ethereum/stake_dapp/lib/pages/tabs/stake_tabs.dart @@ -12,6 +12,7 @@ class StakeTabs extends StatefulWidget { final OrchidWeb3Context? web3Context; final EthereumAddress? stakee; final Token? currentStake; + final BigInt? currentStakeDelay; final USD? price; const StakeTabs({ @@ -20,6 +21,7 @@ class StakeTabs extends StatefulWidget { required this.stakee, required this.currentStake, required this.price, + required this.currentStakeDelay, }) : super(key: key); @override @@ -76,6 +78,7 @@ class _StakeTabsState extends State { stakee: widget.stakee, currentStake: widget.currentStake, price: widget.price, + currentStakeDelay: widget.currentStakeDelay, ); } diff --git a/web-ethereum/stake_dapp/lib/stake_dapp/orchid_web3_stake_v0.dart b/web-ethereum/stake_dapp/lib/stake_dapp/orchid_web3_stake_v0.dart index 76beaa427..c83e6e49d 100644 --- a/web-ethereum/stake_dapp/lib/stake_dapp/orchid_web3_stake_v0.dart +++ b/web-ethereum/stake_dapp/lib/stake_dapp/orchid_web3_stake_v0.dart @@ -7,6 +7,12 @@ import 'package:orchid/api/orchid_log.dart'; import 'package:orchid/dapp/orchid_web3/orchid_erc20.dart'; import 'package:orchid/dapp/orchid_web3/orchid_web3_context.dart'; +import 'dart:typed_data'; +import 'package:convert/convert.dart'; +import 'package:orchid/gui-orchid/lib/api/orchid_eth/abi_encode.dart'; +import 'package:orchid/gui-orchid/lib/util/hex.dart'; +import 'package:web3dart/crypto.dart'; + class OrchidWeb3StakeV0 { final OrchidWeb3Context context; final Contract _directoryContract; @@ -19,7 +25,9 @@ class OrchidWeb3StakeV0 { context.web3), this._oxt = OrchidERC20(context: context, tokenType: Tokens.OXT); - Future orchidGetStake(EthereumAddress stakee) async { + // Return the total stake for the specified stakee address. + // This is the aggregate of all stakes for the address as returned by the heft() call. + Future orchidGetTotalStake(EthereumAddress stakee) async { log("Get stake for: $stakee"); var result = await _directoryContract.call('heft', [ stakee.toString(), // 0x ? @@ -33,14 +41,88 @@ class OrchidWeb3StakeV0 { } } + // Get the stake for the specified staker and stakee addresses. + // Note: This method uses getStorageAt to retrieve the stake amount and delay since + // Note: there is no public method to fetch them for a specific staker/stakee pair. + Future orchidGetStakeForStaker({ + required EthereumAddress staker, + required EthereumAddress stakee, + }) async { + BigInt calculateStorageSlot( + EthereumAddress staker, EthereumAddress stakee) { + final Uint8List keccakStakerStakee = + keccak256(tie(staker.bytes, stakee.bytes)); + // 2 is the index of the stakes_ mapping in the Directory contract + final Uint8List slot = + keccak256(tie(keccakStakerStakee, BigInt.two.toBytesUint256())); + return BigInt.parse(hex.encode(slot), radix: 16); + } + + // Calculate the storage slots for the stake amount and delay + BigInt stakeSlot = calculateStorageSlot(staker, stakee); + BigInt amountSlot = stakeSlot + BigInt.two; // `stakeSlot + 2` + BigInt delaySlot = stakeSlot + BigInt.from(3); // `stakeSlot + 3` + + // Invoke eth_getStorageAt to get the stake amount and delay + String amount = await _directoryGetStorageAt(amountSlot); + String delay = await _directoryGetStorageAt(delaySlot); + + return StakeResult( + Tokens.OXT.fromIntString(amount), + BigInt.parse(delay), + ); + } + + // Note: This method uses getStorageAt to retrieve the pending data. + // Note: There is no public method to fetch this information. + Future orchidGetPendingWithdrawal({ + required EthereumAddress staker, + required int index, + }) async { + // Calculate the storage slot for the pending withdrawal amount + BigInt calculatePendingStorageSlot(EthereumAddress staker, int index) { + // (staker, contract slot index) + final Uint8List keccakStakerSlot = keccak256( + tie(staker.value.toBytesUint256(), BigInt.from(4).toBytesUint256())); + // (array index, slot) + final Uint8List slot = + keccak256(tie(BigInt.from(index).toBytesUint256(), keccakStakerSlot)); + return BigInt.parse(hex.encode(slot), radix: 16); + } + + // Calculate the storage slots for the Pending struct fields + BigInt pendingSlot = calculatePendingStorageSlot(staker, index); + + // 0: expire_ (uint256) + // 1: stakee_ (address) + // 2: amount_ (uint256) + BigInt expireSlot = pendingSlot; + BigInt amountSlot = pendingSlot + BigInt.two; + String expire = await _directoryGetStorageAt(expireSlot); + log("XXX: pending expire = $expire"); + String amount = await _directoryGetStorageAt(amountSlot); + log("XXX: pending amount = $amount"); + + return StakePendingResult( + Tokens.OXT.fromIntString(amount), + BigInt.parse(expire), + ); + } + + Future _directoryGetStorageAt(BigInt slot) { + return _directoryContract.provider + .call('getStorageAt', [_directoryContract.address, Hex.hex(slot)]); + } + /// Transfer the int amount from the user to the specified directory address. /// Amount won't exceed walletBalance. // 'function push(address stakee, uint256 amount, uint128 delay)', - Future /*TransactionId*/ > orchidStakePushFunds({ + Future orchidStakePushFunds({ required OrchidWallet wallet, required EthereumAddress stakee, required Token amount, BigInt? delay, + ERC20PayableTransactionCallbacks? callbacks, }) async { delay ??= BigInt.zero; // Currently disallow staking delay. @@ -48,13 +130,11 @@ class OrchidWeb3StakeV0 { throw Exception("Staking delay not allowed."); } amount.assertType(Tokens.OXT); - log("Stake funds amount: $amount, stakee: $stakee, delay: $delay"); + log("Stake funds: amount: $amount, stakee: $stakee, delay: $delay"); final funder = wallet.address!; var walletBalance = await _oxt.getERC20Balance(funder); amount = Token.min(amount, walletBalance); - List txHashes = []; - // Check allowance and skip approval if sufficient. // function allowance(address owner, address spender) external view returns (uint256) Token oxtAllowance = await _oxt.getERC20Allowance( @@ -62,21 +142,22 @@ class OrchidWeb3StakeV0 { spender: OrchidContractV0.lotteryContractAddressV0, ); if (oxtAllowance < amount) { - log("oxtAllowance increase required: $oxtAllowance < $amount"); + log("Stake funds: oxtAllowance increase required: $oxtAllowance < $amount"); var approveTxHash = await _oxt.approveERC20( owner: funder, spender: OrchidContractV0.directoryContractAddress, amount: amount, // Amount is the new total approval amount ); - txHashes.add(approveTxHash); + callbacks?.onApproval(approveTxHash); await Future.delayed(Duration(milliseconds: 1000)); } else { - log("oxtAllowance sufficient: $oxtAllowance"); + log("Stake funds: oxtAllowance sufficient: $oxtAllowance"); } // Do the stake call var contract = _directoryContract.connect(context.web3.getSigner()); - TransactionResponse tx = await contract.send( + log("Stake funds: do push, amount: $amount, stakee: $stakee, delay: $delay"); + TransactionResponse pushTx = await contract.send( 'push', [ stakee.toString(), @@ -86,8 +167,7 @@ class OrchidWeb3StakeV0 { TransactionOverride( gasLimit: BigInt.from(OrchidContractV0.gasLimitDirectoryPush)), ); - txHashes.add(tx.hash); - return txHashes; + callbacks?.onTransaction(pushTx.hash); } /// Pull funds to the specified index. @@ -117,7 +197,6 @@ class OrchidWeb3StakeV0 { return txHashes; } - // Withdraw from index // 'function take(uint256 index, uint256 amount, address payable target)', Future /*TransactionId*/ > orchidStakeWithdrawFunds({ @@ -145,3 +224,27 @@ class OrchidWeb3StakeV0 { return txHashes; } } + +class StakeResult { + final Token amount; + final BigInt delay; + + StakeResult(this.amount, this.delay); + + @override + String toString() { + return "StakeResult(amount: $amount, delay: $delay)"; + } +} + +class StakePendingResult { + final Token amount; + final BigInt expire; + + StakePendingResult(this.amount, this.expire); + + @override + String toString() { + return "StakePendingResult(expire: $expire, amount: $amount)"; + } +} From 9fdb5a1027460482afcb343075f703b038b5a56f Mon Sep 17 00:00:00 2001 From: Patrick Niemeyer Date: Wed, 25 Sep 2024 18:25:41 -0500 Subject: [PATCH 2/5] dapp: Improve responsiveness of the stake info in the staking dapp. --- gui-orchid/lib/util/listenable_builder.dart | 1 + gui-orchid/lib/util/poller.dart | 17 ++- .../account_dapp/lib/pages/dapp_home.dart | 2 +- .../stake_dapp/lib/pages/stake_dapp_home.dart | 90 ++++++------ .../lib/pages/tabs/add_stake_panel.dart | 2 +- .../lib/pages/tabs/withdraw_stake_panel.dart | 2 + .../lib/stake_dapp/stake_detail.dart | 139 ++++++++++++++++++ 7 files changed, 198 insertions(+), 55 deletions(-) create mode 100644 web-ethereum/stake_dapp/lib/stake_dapp/stake_detail.dart diff --git a/gui-orchid/lib/util/listenable_builder.dart b/gui-orchid/lib/util/listenable_builder.dart index f8db7fdd1..a23b90645 100644 --- a/gui-orchid/lib/util/listenable_builder.dart +++ b/gui-orchid/lib/util/listenable_builder.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; // TODO: This is no longer needed in later versions of Flutter. Remove when redundant. +/// Buildier that can listen to any Listenable including ChangeNotifier, ValueNotifier, etc. /// This is a trivial subclass of AnimatedBuilder that serves to rename /// it more appropriately for use as a plain listenable builder. /// (This really should be the name of the base class in Flutter core.) diff --git a/gui-orchid/lib/util/poller.dart b/gui-orchid/lib/util/poller.dart index eb444c0a8..0ad2c1dce 100644 --- a/gui-orchid/lib/util/poller.dart +++ b/gui-orchid/lib/util/poller.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import '../api/orchid_log.dart'; /* Poller.call(foo).every(seconds: 5).dispose(disposal); @@ -21,10 +22,12 @@ class Poller { Poller every({int? seconds, int? minutes, int? hours}) { assert(seconds != null || minutes != null || hours != null); _timer?.cancel(); - _timer = Timer.periodic( - Duration( - seconds: seconds ?? 0, minutes: minutes ?? 0, hours: hours ?? 0), - _poll); + var duration = Duration( + seconds: seconds ?? 0, minutes: minutes ?? 0, hours: hours ?? 0); + if (duration.inMilliseconds <= 0) { + throw Exception("invalid duration: $duration"); + } + _timer = Timer.periodic(duration, _poll); return this; } @@ -35,7 +38,11 @@ class Poller { } void _poll(_) { - func(); + try { + func(); + } catch (e) { + log("Poller error: $e"); + } } Poller dispose(List disposal) { diff --git a/web-ethereum/account_dapp/lib/pages/dapp_home.dart b/web-ethereum/account_dapp/lib/pages/dapp_home.dart index 61c4dcbd9..c351e226c 100644 --- a/web-ethereum/account_dapp/lib/pages/dapp_home.dart +++ b/web-ethereum/account_dapp/lib/pages/dapp_home.dart @@ -117,12 +117,12 @@ class DappHomeState extends DappHomeStateBase { children: [ DappHomeHeader( web3Context: web3Context, - setNewContext: setNewContext, contractVersionsAvailable: contractVersionsAvailable, contractVersionSelected: contractVersionSelected, selectContractVersion: selectContractVersion, deployContract: deployContract, connectEthereum: connectEthereum, + connectWalletConnect: connectWalletConnect, disconnect: disconnect, ).padx(24).top(30).bottom(24), _buildMainColumn(), diff --git a/web-ethereum/stake_dapp/lib/pages/stake_dapp_home.dart b/web-ethereum/stake_dapp/lib/pages/stake_dapp_home.dart index c03067d0f..68e53b2f5 100644 --- a/web-ethereum/stake_dapp/lib/pages/stake_dapp_home.dart +++ b/web-ethereum/stake_dapp/lib/pages/stake_dapp_home.dart @@ -16,8 +16,9 @@ import 'package:orchid/common/app_dialogs.dart'; import 'package:orchid/orchid/field/orchid_labeled_address_field.dart'; import 'package:orchid/pages/tabs/location_panel.dart'; import 'package:orchid/pages/tabs/stake_tabs.dart'; -import 'package:orchid/stake_dapp/orchid_web3_stake_v0.dart'; import 'package:orchid/stake_dapp/orchid_web3_location_v0.dart'; +import 'package:orchid/stake_dapp/orchid_web3_stake_v0.dart'; +import 'package:orchid/stake_dapp/stake_detail.dart'; import 'dapp_home_base.dart'; import 'dapp_home_header.dart'; @@ -37,16 +38,20 @@ class _StakeDappHomeState extends DappHomeStateBase { final _stakeeField = AddressValueFieldController(); final _scrollController = ScrollController(); + Location? _currentLocation; + + // The current stake details for the staker/stakee pair. + StakeDetailPoller? _stakeDetail; + // The total stake staked for the stakee by all stakers. - Token? _currentStakeTotal; + Token? get _currentStakeTotal => _stakeDetail?.currentStakeTotal; // The amount and delay staked for the stakee by the current staker (wallet). - StakeResult? _currentStakeStaker; + StakeResult? get _currentStakeStaker => _stakeDetail?.currentStakeStaker; // The amount and expiration of the pulled stake pending withdrawal for the first n indexes. - List? _currentStakePendingStaker; - - Location? _currentLocation; + List? get _currentStakePendingStaker => + _stakeDetail?.currentStakePendingStaker; @override void initState() { @@ -176,8 +181,8 @@ class _StakeDappHomeState extends DappHomeStateBase { StakeTabs( web3Context: web3Context, stakee: _stakee, - currentStake: _currentStakeStaker?.amount, price: price, + currentStake: _currentStakeStaker?.amount, currentStakeDelay: _currentStakeStaker?.delay, ).top(40), ], @@ -339,55 +344,42 @@ class _StakeDappHomeState extends DappHomeStateBase { } // Start polling the correct account - // TODO: Poll this void _selectedStakeeChanged() async { if (isStakerView) { - _updateStake(); + _updateStakePoller(); } else { _updateLocation(); } } - void _updateStake() async { - if (_stakee != null && web3Context != null) { - // Get the total stake for all stakers (heft) - final orchidWeb3 = OrchidWeb3StakeV0(web3Context!); - _currentStakeTotal = await orchidWeb3.orchidGetTotalStake(_stakee!); - log("XXX: heft = $_currentStakeTotal"); - - // Get the stake for this staker (wallet) - try { - _currentStakeStaker = await orchidWeb3.orchidGetStakeForStaker( - staker: web3Context!.walletAddress!, - stakee: _stakee!, - ); - log("XXX: staker stake = $_currentStakeStaker"); - } catch (err, stack) { - log("Error getting stake for staker: $err"); - log(stack.toString()); - } + // Create a new poller for the current staker/stakee pair + void _updateStakePoller() { + // Cancel any existing poller + _clearStakePoller(); - // Get the pending stake withdrawals for this staker (wallet) - try { - List pendingList = []; - for (var i = 0; i < 3; i++) { - final pending = await orchidWeb3.orchidGetPendingWithdrawal( - staker: web3Context!.walletAddress!, - index: i, - ); - pendingList.add(pending); - } - _currentStakePendingStaker = pendingList; - log("XXX: pending = $_currentStakePendingStaker"); - } catch (err, stack) { - log("Error getting stake for staker: $err"); - log(stack.toString()); - } - } else { - _currentStakeTotal = null; - _currentStakeStaker = null; - _currentStakePendingStaker = null; + // Start a new poller if we have context, staker, and stakee + final staker = web3Context?.walletAddress; + final stakee = _stakee; + if (stakee != null && staker != null) { + _stakeDetail = StakeDetailPoller( + pollingPeriod: const Duration(seconds: 10), + web3Context: web3Context!, + staker: staker, + stakee: stakee, + ); + _stakeDetail?.addListener(_stakeUpdated); + _stakeDetail?.startPolling(); } + } + + void _clearStakePoller() { + _stakeDetail?.cancel(); + _stakeDetail?.removeListener(_stakeUpdated); + _stakeDetail = null; + } + + // Called when the stake poller has an update + void _stakeUpdated() { setState(() {}); } @@ -437,12 +429,14 @@ class _StakeDappHomeState extends DappHomeStateBase { // setState(() { // _clearAccountDetail(); // }); - super.disconnect(); + await super.disconnect(); + _updateStakePoller(); // allow the poller to cancel itself } @override void dispose() { _stakeeField.removeListener(_stakeeFieldChanged); + _clearStakePoller(); super.dispose(); } diff --git a/web-ethereum/stake_dapp/lib/pages/tabs/add_stake_panel.dart b/web-ethereum/stake_dapp/lib/pages/tabs/add_stake_panel.dart index fc5933953..7200ca056 100644 --- a/web-ethereum/stake_dapp/lib/pages/tabs/add_stake_panel.dart +++ b/web-ethereum/stake_dapp/lib/pages/tabs/add_stake_panel.dart @@ -78,7 +78,7 @@ class _AddStakePanelState extends State // Delay label (if non-null) // if (_currentStakeDelayIsZero) // Text("Added funds will be staked with no withdrawal delay.").white.caption.top(16), - // if (_currentStakeDelayIsNonZero) + if (_currentStakeDelayIsNonZero) Text("This UI does not support adding funds to an existing stake with a non-zero delay.") .caption .error diff --git a/web-ethereum/stake_dapp/lib/pages/tabs/withdraw_stake_panel.dart b/web-ethereum/stake_dapp/lib/pages/tabs/withdraw_stake_panel.dart index 905597d03..010a8db18 100644 --- a/web-ethereum/stake_dapp/lib/pages/tabs/withdraw_stake_panel.dart +++ b/web-ethereum/stake_dapp/lib/pages/tabs/withdraw_stake_panel.dart @@ -144,6 +144,8 @@ class _WithdrawStakePanelState extends State ))); _withdrawStakeAmountController.clear(); + _indexController.clear(); + _targetController.clear(); setState(() {}); } catch (err) { log('Error on withdraw funds: $err'); diff --git a/web-ethereum/stake_dapp/lib/stake_dapp/stake_detail.dart b/web-ethereum/stake_dapp/lib/stake_dapp/stake_detail.dart new file mode 100644 index 000000000..9202783ce --- /dev/null +++ b/web-ethereum/stake_dapp/lib/stake_dapp/stake_detail.dart @@ -0,0 +1,139 @@ +import 'dart:async'; +import 'package:flutter/widgets.dart'; +import 'package:orchid/api/orchid_crypto.dart'; +import 'package:orchid/api/orchid_eth/token_type.dart'; +import 'package:orchid/api/orchid_log.dart'; +import 'package:orchid/dapp/orchid_web3/orchid_web3_context.dart'; +import 'orchid_web3_stake_v0.dart'; + +class StakeDetailPoller extends ChangeNotifier { + final OrchidWeb3Context web3Context; + final EthereumAddress stakee; + final EthereumAddress staker; // wallet + + // The total stake staked for the stakee by all stakers. + Token? currentStakeTotal; + + // The amount and delay staked for the stakee by the current staker (wallet). + StakeResult? currentStakeStaker; + + // The amount and expiration of the pulled stake pending withdrawal for the first n indexes. + List? currentStakePendingStaker; + + // Manage polling state + static int nextId = 0; + final int id; + final Duration pollingPeriod; + Timer? _timer; + bool _pollInProgress = false; + bool _isCancelled = false; + DateTime? lastUpdate; + + StakeDetailPoller({ + required this.web3Context, + required this.staker, + required this.stakee, + this.pollingPeriod = const Duration(seconds: 30), + }) : this.id = nextId++ { + log("XXX: StakeDetailPoller $id created."); + } + + /// Start periodic polling + Future startPolling() async { + _timer = Timer.periodic(pollingPeriod, (_) { + _pollStake(); + }); + return _pollStake(); // kick one off immediately + } + + /// Load data once + Future pollOnce() async { + return _pollStake(); + } + + /// Load data updating caches + Future refresh() async { + return _pollStake(refresh: true); + } + + Future _pollStake({bool refresh = false}) async { + if (_isCancelled || _pollInProgress) { + log("XXX: call to _pollStake with cancelled timer or poll in progress, pollInProgress=$_pollInProgress"); + return; + } + _pollInProgress = true; + try { + await _pollStakeImpl(refresh); + } catch (err) { + log("Error polling stake details: $err"); + } finally { + _pollInProgress = false; + lastUpdate = DateTime.now(); + } + } + + // 'refresh' can be used to defeat caching, if any. + Future _pollStakeImpl(bool refresh) async { + final orchidWeb3 = OrchidWeb3StakeV0(web3Context); + + // Get the total stake for all stakers (heft) + try { + currentStakeTotal = await orchidWeb3.orchidGetTotalStake(stakee); + log("XXX: heft = $currentStakeTotal"); + } catch (err) { + log("Error getting heft for stakee: $err"); + currentStakeTotal = null; + } + this.notifyListeners(); + + // Get the stake for this staker (wallet) + try { + currentStakeStaker = await orchidWeb3.orchidGetStakeForStaker( + staker: staker, + stakee: stakee, + ); + log("XXX: staker stake = $currentStakeStaker"); + } catch (err, stack) { + log("Error getting stake for staker: $err"); + log(stack.toString()); + currentStakeStaker = null; + } + this.notifyListeners(); + + // Get the pending stake withdrawals for this staker (wallet) + try { + List pendingList = []; + for (var i = 0; i < 3; i++) { + final pending = await orchidWeb3.orchidGetPendingWithdrawal( + staker: staker, + index: i, + ); + pendingList.add(pending); + } + currentStakePendingStaker = pendingList; + log("XXX: pending = $currentStakePendingStaker"); + } catch (err, stack) { + log("Error getting stake for staker: $err"); + log(stack.toString()); + currentStakePendingStaker = null; + } + this.notifyListeners(); + } + + void cancel() { + _isCancelled = true; + _timer?.cancel(); + log("XXX: stake detail $id poller cancelled"); + } + + void dispose() { + cancel(); + super.dispose(); + } + + @override + String toString() { + return 'StakeDetailPoller{id: $id, stakee: $stakee, staker: $staker, currentStakeTotal: $currentStakeTotal, currentStakeStaker: $currentStakeStaker, currentStakePendingStaker: $currentStakePendingStaker}'; + } +} + From bd659d92c001e0ba551c9398f07d4bb0bc8da2d9 Mon Sep 17 00:00:00 2001 From: Patrick Niemeyer Date: Wed, 2 Oct 2024 14:39:25 -0500 Subject: [PATCH 3/5] dapp: Improve UI feedback during pending transactions. --- gui-orchid/lib/util/format_decimal.dart | 2 - .../lib/dapp/orchid/dapp_button.dart | 44 ++++++++++++++- .../dapp/orchid/dapp_transaction_list.dart | 2 - .../lib/dapp/orchid_web3/orchid_erc20.dart | 23 ++++++-- .../dapp/orchid_web3/v0/orchid_web3_v0.dart | 10 ++-- .../dapp/orchid_web3/v1/orchid_web3_v1.dart | 4 +- .../lib/pages/dapp_add_funds.dart | 56 ++++++++++++++----- .../lib/pages/v0/dapp_lock_warn_v0.dart | 35 +++++++----- .../lib/pages/v0/dapp_move_funds_v0.dart | 12 ++-- .../lib/pages/v0/dapp_tabs_v0.dart | 5 +- .../lib/pages/v0/dapp_withdraw_funds_v0.dart | 8 ++- .../lib/pages/v1/dapp_advanced_funds_v1.dart | 6 +- .../lib/pages/v1/dapp_tabs_v1.dart | 8 ++- .../lib/pages/v1/dapp_withdraw_funds_v1.dart | 8 ++- .../lib/pages/tabs/add_stake_panel.dart | 20 +++---- .../lib/pages/tabs/location_panel.dart | 3 +- .../lib/pages/tabs/pull_stake_panel.dart | 3 +- .../lib/pages/tabs/withdraw_stake_panel.dart | 3 +- 18 files changed, 178 insertions(+), 74 deletions(-) diff --git a/gui-orchid/lib/util/format_decimal.dart b/gui-orchid/lib/util/format_decimal.dart index 7243b4e28..fa074deef 100644 --- a/gui-orchid/lib/util/format_decimal.dart +++ b/gui-orchid/lib/util/format_decimal.dart @@ -107,7 +107,6 @@ String formatDecimal( // Implement the min/max manually restricted = trimAndPadDecimal(restricted, minPrecision, maxPrecision); - print("XXX: restricted: $restricted"); var precisionIndicator = ''; if (showPrecisionIndicator) { @@ -122,7 +121,6 @@ String formatDecimal( // Workaround: no internationalization at this stage final unrestricted = value.toString(); - print("XXX: unrestricted: $unrestricted"); precisionIndicator = restricted.length < unrestricted.length ? ellipsis : ''; } diff --git a/web-ethereum/account_dapp/lib/dapp/orchid/dapp_button.dart b/web-ethereum/account_dapp/lib/dapp/orchid/dapp_button.dart index 12001889e..3f0c644b2 100644 --- a/web-ethereum/account_dapp/lib/dapp/orchid/dapp_button.dart +++ b/web-ethereum/account_dapp/lib/dapp/orchid/dapp_button.dart @@ -1,4 +1,6 @@ +import 'package:orchid/orchid/orchid.dart'; import 'package:flutter/material.dart'; +import 'package:orchid/gui-orchid/lib/orchid/orchid_circular_progress.dart'; import 'package:orchid/orchid/orchid_action_button.dart'; class DappButton extends StatelessWidget { @@ -19,7 +21,8 @@ class DappButton extends StatelessWidget { /// use double.infinity for an expandable button, null for the default width this.width, - this.backgroundColor, this.height, + this.backgroundColor, + this.height, }) : super(key: key); @override @@ -36,3 +39,42 @@ class DappButton extends StatelessWidget { ); } } + +// Extend DappButton with a transaction status indicator. +class DappTransactionButton extends DappButton { + final bool txPending; + + const DappTransactionButton({ + Key? key, + required String text, + required VoidCallback? onPressed, + Widget? trailing, + TextStyle? textStyle, + double? width, + double? height, + Color? backgroundColor, + required this.txPending, + }) : super( + key: key, + text: text, + onPressed: onPressed, + trailing: trailing, + textStyle: textStyle, + width: width, + height: height, + backgroundColor: backgroundColor, + ); + + @override + Widget build(BuildContext context) { + return DappButton( + text: txPending ? "Waiting for Transaction" : text, + trailing: txPending ? OrchidCircularProgressIndicator.smallIndeterminate().left(16) : trailing, + onPressed: onPressed, + textStyle: textStyle, + width: width, + height: height, + backgroundColor: backgroundColor, + ); + } +} diff --git a/web-ethereum/account_dapp/lib/dapp/orchid/dapp_transaction_list.dart b/web-ethereum/account_dapp/lib/dapp/orchid/dapp_transaction_list.dart index bad0fd2e4..fd7f97adc 100644 --- a/web-ethereum/account_dapp/lib/dapp/orchid/dapp_transaction_list.dart +++ b/web-ethereum/account_dapp/lib/dapp/orchid/dapp_transaction_list.dart @@ -1,6 +1,4 @@ import 'dart:math'; -import 'package:orchid/api/orchid_eth/chains.dart'; -import 'package:orchid/dapp/preferences/dapp_transaction.dart'; import 'package:orchid/orchid/orchid.dart'; import 'package:orchid/dapp/orchid_web3/orchid_web3_context.dart'; import 'package:orchid/dapp/preferences/user_preferences_dapp.dart'; diff --git a/web-ethereum/account_dapp/lib/dapp/orchid_web3/orchid_erc20.dart b/web-ethereum/account_dapp/lib/dapp/orchid_web3/orchid_erc20.dart index 281a36ef4..cd973b79c 100644 --- a/web-ethereum/account_dapp/lib/dapp/orchid_web3/orchid_erc20.dart +++ b/web-ethereum/account_dapp/lib/dapp/orchid_web3/orchid_erc20.dart @@ -74,11 +74,26 @@ class OrchidERC20 { // For a transaction that uses an ERC20 token, the transaction may require an approval class ERC20PayableTransactionCallbacks { - final Future Function(String approvalHash) onApproval; - final Future Function(String txHash) onTransaction; + final Future Function(String txHash, int seriesIndex, int seriesTotal) onApprovalCallback; + final Future Function(String txHash, int seriesIndex, int seriesTotal) onTransactionCallback; + + bool _hasApproval = false; ERC20PayableTransactionCallbacks({ - required this.onApproval, - required this.onTransaction, + required this.onApprovalCallback, + required this.onTransactionCallback, }); + + Future onApproval(String txHash) async { + _hasApproval = true; + return onApprovalCallback(txHash, 1, 2); // Approval is the first in the series (1 of 2) + } + + Future onTransaction(String txHash) async { + if (_hasApproval) { + return onTransactionCallback(txHash, 2, 2); // If approval has happened, this is the second in the series (2 of 2) + } else { + return onTransactionCallback(txHash, 1, 1); // No approval needed, this is the only transaction (1 of 1) + } + } } \ No newline at end of file diff --git a/web-ethereum/account_dapp/lib/dapp/orchid_web3/v0/orchid_web3_v0.dart b/web-ethereum/account_dapp/lib/dapp/orchid_web3/v0/orchid_web3_v0.dart index 3a01d77e9..e92816d9e 100644 --- a/web-ethereum/account_dapp/lib/dapp/orchid_web3/v0/orchid_web3_v0.dart +++ b/web-ethereum/account_dapp/lib/dapp/orchid_web3/v0/orchid_web3_v0.dart @@ -37,11 +37,12 @@ class OrchidWeb3V0 { /// Transfer the int amount from the user to the specified lottery pot address. /// If the total exceeds walletBalance the amount value is automatically reduced. - Future /*TransactionId*/ > orchidAddFunds({ + Future orchidAddFunds({ required OrchidWallet wallet, required EthereumAddress signer, required Token addBalance, required Token addEscrow, + required ERC20PayableTransactionCallbacks? callbacks, }) async { if (wallet.address == null) { throw Exception("Wallet address is null"); @@ -55,8 +56,6 @@ class OrchidWeb3V0 { var totalOXT = Token.min(addBalance.add(addEscrow), walletBalance); log("Add funds: signer: $signer, amount: ${totalOXT.subtract(addEscrow)}, escrow: $addEscrow"); - List txHashes = []; - // Check allowance and skip approval if sufficient. // function allowance(address owner, address spender) external view returns (uint256) Token oxtAllowance = await _oxt.getERC20Allowance( @@ -69,7 +68,7 @@ class OrchidWeb3V0 { owner: wallet.address!, spender: OrchidContractV0.lotteryContractAddressV0, amount: totalOXT); - txHashes.add(approveTxHash); + callbacks?.onApproval(approveTxHash); } else { log("Add funds: oxtAllowance already sufficient: $oxtAllowance"); } @@ -87,8 +86,7 @@ class OrchidWeb3V0 { TransactionOverride( gasLimit: BigInt.from(OrchidContractV0.gasLimitLotteryPush)), ); - txHashes.add(tx.hash); - return txHashes; + callbacks?.onTransaction(tx.hash); } /// Withdraw from balance and escrow to the wallet address. diff --git a/web-ethereum/account_dapp/lib/dapp/orchid_web3/v1/orchid_web3_v1.dart b/web-ethereum/account_dapp/lib/dapp/orchid_web3/v1/orchid_web3_v1.dart index bca8bf2cb..c47854aee 100644 --- a/web-ethereum/account_dapp/lib/dapp/orchid_web3/v1/orchid_web3_v1.dart +++ b/web-ethereum/account_dapp/lib/dapp/orchid_web3/v1/orchid_web3_v1.dart @@ -30,7 +30,7 @@ class OrchidWeb3V1 { /// Transfer the amount from the user to the specified lottery pot address. /// If the total exceeds walletBalance the balance value is reduced. - Future /*TransactionId*/ > orchidAddFunds({ + Future /*TransactionId*/ orchidAddFunds({ required OrchidWallet wallet, required EthereumAddress signer, required Token addBalance, @@ -64,7 +64,7 @@ class OrchidWeb3V1 { retrieve: retrieve, totalPayable: totalPayable, ); - return [tx.hash]; + return tx.hash; } /// Withdraw funds by moving the specified withdrawEscrow amount from escrow diff --git a/web-ethereum/account_dapp/lib/pages/dapp_add_funds.dart b/web-ethereum/account_dapp/lib/pages/dapp_add_funds.dart index 888320d33..af680ecbb 100644 --- a/web-ethereum/account_dapp/lib/pages/dapp_add_funds.dart +++ b/web-ethereum/account_dapp/lib/pages/dapp_add_funds.dart @@ -1,3 +1,4 @@ +import 'package:orchid/dapp/orchid_web3/orchid_erc20.dart'; import 'package:orchid/orchid/orchid.dart'; import 'package:orchid/api/orchid_crypto.dart'; import 'package:orchid/api/orchid_eth/token_type.dart'; @@ -22,11 +23,12 @@ class AddFundsPane extends StatefulWidget { final tokenType; // Callback to add the funds - final Future> Function({ + final Future Function({ required OrchidWallet? wallet, required EthereumAddress? signer, required Token addBalance, required Token addEscrow, + required ERC20PayableTransactionCallbacks? callbacks, }) addFunds; const AddFundsPane({ @@ -100,9 +102,11 @@ class _AddFundsPaneState extends State with DappTabWalletContext { Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - DappButton( - text: s.addFunds, - onPressed: _addFundsFormEnabled ? _addFunds : null), + DappTransactionButton( + text: s.addFunds, + onPressed: _addFundsFormEnabled ? _addFunds : null, + txPending: txPending, + ), ], ), pady(32), @@ -133,7 +137,8 @@ class _AddFundsPaneState extends State with DappTabWalletContext { bool get _addBalanceFieldValid { var value = _addBalanceField.value; - return value != null && value <= (walletBalanceOf(tokenType) ?? tokenType.zero); + return value != null && + value <= (walletBalanceOf(tokenType) ?? tokenType.zero); } bool get _addBalanceFieldError { @@ -142,7 +147,8 @@ class _AddFundsPaneState extends State with DappTabWalletContext { bool get _addDepositFieldValid { var value = _addDepositField.value; - return value != null && value <= (walletBalanceOf(tokenType) ?? tokenType.zero); + return value != null && + value <= (walletBalanceOf(tokenType) ?? tokenType.zero); } bool get _addDepositFieldError { @@ -176,6 +182,8 @@ class _AddFundsPaneState extends State with DappTabWalletContext { _totalAdd.gtZero(); } + // This generic add funds method can be delegated to either the V0 or V1 impls and so must accommodate + // ERC20 token approvals and transactions. void _addFunds() async { if (!_addBalanceFieldValid) { return; @@ -183,22 +191,42 @@ class _AddFundsPaneState extends State with DappTabWalletContext { setState(() { txPending = true; }); + + final progress = ERC20PayableTransactionCallbacks( + onApprovalCallback: (txHash, seriesIndex, seriesTotal) async { + await UserPreferencesDapp().addTransaction(DappTransaction( + transactionHash: txHash, + chainId: web3Context!.chain.chainId /*always Ethereum*/, + type: DappTransactionType.addFunds, + subtype: "approve", + series_index: seriesIndex, + series_total: seriesTotal, + )); + }, + onTransactionCallback: (txHash, seriesIndex, seriesTotal) async { + await UserPreferencesDapp().addTransaction(DappTransaction( + transactionHash: txHash, + chainId: web3Context!.chain.chainId, + // always Ethereum + type: DappTransactionType.addFunds, + // TODO: Localize + subtype: "push", + series_index: seriesIndex, + series_total: seriesTotal, + )); + }, + ); + try { - final txHashes = await widget.addFunds( + await widget.addFunds( wallet: wallet, signer: widget.signer, // nulls guarded by _addBalanceFieldValid and _addDepositFieldValid addBalance: _addBalanceField.value!, addEscrow: _addDepositField.value!, + callbacks: progress, ); - // Persisting the transaction(s) will update the UI elsewhere. - UserPreferencesDapp().addTransactions(txHashes.map((hash) => DappTransaction( - transactionHash: hash, - chainId: widget.context!.chain.chainId, - type: DappTransactionType.addFunds, - ))); - _addBalanceField.clear(); _addDepositField.clear(); setState(() {}); diff --git a/web-ethereum/account_dapp/lib/pages/v0/dapp_lock_warn_v0.dart b/web-ethereum/account_dapp/lib/pages/v0/dapp_lock_warn_v0.dart index 4809481dd..bffbd486f 100644 --- a/web-ethereum/account_dapp/lib/pages/v0/dapp_lock_warn_v0.dart +++ b/web-ethereum/account_dapp/lib/pages/v0/dapp_lock_warn_v0.dart @@ -56,7 +56,8 @@ class _LockWarnPaneV0State extends State { pot!.isUnlocking ? s.unlocking : s.locked); statusText += pot!.isUnlocking ? '\n' + - s.theFundsWillBeAvailableForWithdrawalInTime(pot!.unlockInString()) + s.theFundsWillBeAvailableForWithdrawalInTime( + pot!.unlockInString()) : ''; isUnlockedOrUnlocking = (pot!.isUnlocked || pot!.isUnlocking); } @@ -75,21 +76,25 @@ class _LockWarnPaneV0State extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ if (isUnlockedOrUnlocking) - DappButton( - text: s.lockDeposit, - onPressed: _formEnabled() - ? () { - _lockOrUnlock(lock: true); - } - : null) + DappTransactionButton( + text: s.lockDeposit, + onPressed: _formEnabled() + ? () { + _lockOrUnlock(lock: true); + } + : null, + txPending: _txPending, + ) else - DappButton( - text: s.unlockDeposit, - onPressed: _formEnabled() - ? () { - _lockOrUnlock(lock: false); - } - : null), + DappTransactionButton( + text: s.unlockDeposit, + onPressed: _formEnabled() + ? () { + _lockOrUnlock(lock: false); + } + : null, + txPending: _txPending, + ), ], ), ], diff --git a/web-ethereum/account_dapp/lib/pages/v0/dapp_move_funds_v0.dart b/web-ethereum/account_dapp/lib/pages/v0/dapp_move_funds_v0.dart index e71c6913a..0c10ff3d5 100644 --- a/web-ethereum/account_dapp/lib/pages/v0/dapp_move_funds_v0.dart +++ b/web-ethereum/account_dapp/lib/pages/v0/dapp_move_funds_v0.dart @@ -19,7 +19,7 @@ class MoveFundsPaneV0 extends StatefulWidget { final bool enabled; const MoveFundsPaneV0({ - Key? key, + Key? key, required this.context, required this.pot, required this.signer, @@ -65,8 +65,11 @@ class _MoveFundsPaneV0State extends State { Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - DappButton( - text: buttonTitle, onPressed: _formEnabled ? _moveFunds : null), + DappTransactionButton( + text: buttonTitle, + onPressed: _formEnabled ? _moveFunds : null, + txPending: _txPending, + ), ], ), ], @@ -94,7 +97,8 @@ class _MoveFundsPaneV0State extends State { _txPending = true; }); try { - var txHash = await OrchidWeb3V0(widget.context!).orchidMoveBalanceToEscrow( + var txHash = + await OrchidWeb3V0(widget.context!).orchidMoveBalanceToEscrow( signer: widget.signer!, pot: pot!, moveAmount: _moveBalanceField.value!, diff --git a/web-ethereum/account_dapp/lib/pages/v0/dapp_tabs_v0.dart b/web-ethereum/account_dapp/lib/pages/v0/dapp_tabs_v0.dart index ceb968204..c11e77e1e 100644 --- a/web-ethereum/account_dapp/lib/pages/v0/dapp_tabs_v0.dart +++ b/web-ethereum/account_dapp/lib/pages/v0/dapp_tabs_v0.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:orchid/api/orchid_crypto.dart'; import 'package:orchid/api/orchid_eth/token_type.dart'; import 'package:orchid/api/orchid_eth/tokens.dart'; +import 'package:orchid/dapp/orchid_web3/orchid_erc20.dart'; import 'package:orchid/dapp/orchid_web3/orchid_web3_context.dart'; import 'package:orchid/dapp/orchid_web3/v0/orchid_web3_v0.dart'; import 'package:orchid/api/orchid_eth/orchid_account_detail.dart'; @@ -119,11 +120,12 @@ class _DappTabsV0State extends State { } // Defers construction of the contract until needed - Future /*TransactionId*/ > _orchidAddFunds({ + Future _orchidAddFunds({ required OrchidWallet? wallet, required EthereumAddress? signer, required Token addBalance, required Token addEscrow, + required ERC20PayableTransactionCallbacks? callbacks, }) async { if (widget.web3Context == null || signer == null || wallet == null) { throw Exception('No web3 context or null signer'); @@ -133,6 +135,7 @@ class _DappTabsV0State extends State { signer: signer, addBalance: addBalance, addEscrow: addEscrow, + callbacks: callbacks, ); } } diff --git a/web-ethereum/account_dapp/lib/pages/v0/dapp_withdraw_funds_v0.dart b/web-ethereum/account_dapp/lib/pages/v0/dapp_withdraw_funds_v0.dart index 1e2efb5fe..96c97a8f3 100644 --- a/web-ethereum/account_dapp/lib/pages/v0/dapp_withdraw_funds_v0.dart +++ b/web-ethereum/account_dapp/lib/pages/v0/dapp_withdraw_funds_v0.dart @@ -73,9 +73,11 @@ class _WithdrawFundsPaneV0State extends State { Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - DappButton( - text: buttonTitle, - onPressed: _withdrawFundsFormEnabled ? _withdrawFunds : null), + DappTransactionButton( + text: buttonTitle, + onPressed: _withdrawFundsFormEnabled ? _withdrawFunds : null, + txPending: _txPending, + ), ], ), ], diff --git a/web-ethereum/account_dapp/lib/pages/v1/dapp_advanced_funds_v1.dart b/web-ethereum/account_dapp/lib/pages/v1/dapp_advanced_funds_v1.dart index ff3fc50e6..1e2f9ecc8 100644 --- a/web-ethereum/account_dapp/lib/pages/v1/dapp_advanced_funds_v1.dart +++ b/web-ethereum/account_dapp/lib/pages/v1/dapp_advanced_funds_v1.dart @@ -116,7 +116,11 @@ class _AdvancedFundsPaneV1State extends State return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - DappButton(text: buttonTitle, onPressed: _formEnabled ? _doTx : null), + DappTransactionButton( + text: buttonTitle, + onPressed: _formEnabled ? _doTx : null, + txPending: txPending, + ), ], ); } diff --git a/web-ethereum/account_dapp/lib/pages/v1/dapp_tabs_v1.dart b/web-ethereum/account_dapp/lib/pages/v1/dapp_tabs_v1.dart index 776d7aa77..390ba6b33 100644 --- a/web-ethereum/account_dapp/lib/pages/v1/dapp_tabs_v1.dart +++ b/web-ethereum/account_dapp/lib/pages/v1/dapp_tabs_v1.dart @@ -1,4 +1,5 @@ import 'package:orchid/api/orchid_eth/tokens.dart'; +import 'package:orchid/dapp/orchid_web3/orchid_erc20.dart'; import 'package:orchid/orchid/orchid.dart'; import 'package:orchid/api/orchid_crypto.dart'; import 'package:orchid/api/orchid_eth/token_type.dart'; @@ -138,20 +139,23 @@ class _DappTabsV1State extends State with TickerProviderStateMixin { } // Defers construction of the contract until needed - Future /*TransactionId*/ > _orchidAddFunds({ + Future _orchidAddFunds({ required OrchidWallet? wallet, required EthereumAddress? signer, required Token addBalance, required Token addEscrow, + required ERC20PayableTransactionCallbacks? callbacks, }) async { if (signer == null || wallet == null) { throw Exception("No signer"); } - return OrchidWeb3V1(widget.web3Context!).orchidAddFunds( + // V1 does not support ERC20 tokens at the moment. + String txHash = await OrchidWeb3V1(widget.web3Context!).orchidAddFunds( wallet: wallet, signer: signer, addBalance: addBalance, addEscrow: addEscrow, ); + callbacks?.onTransaction(txHash); } } diff --git a/web-ethereum/account_dapp/lib/pages/v1/dapp_withdraw_funds_v1.dart b/web-ethereum/account_dapp/lib/pages/v1/dapp_withdraw_funds_v1.dart index 85a089725..36f359f87 100644 --- a/web-ethereum/account_dapp/lib/pages/v1/dapp_withdraw_funds_v1.dart +++ b/web-ethereum/account_dapp/lib/pages/v1/dapp_withdraw_funds_v1.dart @@ -122,9 +122,11 @@ class _WithdrawFundsPaneV1State extends State Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - DappButton( - text: buttonTitle, - onPressed: _formEnabled ? _withdrawFunds : null), + DappTransactionButton( + text: buttonTitle, + onPressed: _formEnabled ? _withdrawFunds : null, + txPending: txPending, + ), ], ), // pady(32), diff --git a/web-ethereum/stake_dapp/lib/pages/tabs/add_stake_panel.dart b/web-ethereum/stake_dapp/lib/pages/tabs/add_stake_panel.dart index 7200ca056..ddd124b83 100644 --- a/web-ethereum/stake_dapp/lib/pages/tabs/add_stake_panel.dart +++ b/web-ethereum/stake_dapp/lib/pages/tabs/add_stake_panel.dart @@ -84,9 +84,10 @@ class _AddStakePanelState extends State .error .top(24), - DappButton( + DappTransactionButton( text: s.addFunds, onPressed: _formEnabled ? _addStake : null, + txPending: txPending, ).top(32), ], ).width(double.infinity); @@ -129,31 +130,28 @@ class _AddStakePanelState extends State txPending = true; }); - bool hasApproval = false; final progress = ERC20PayableTransactionCallbacks( - onApproval: (txHash) async { - hasApproval = true; + onApprovalCallback: (txHash, seriesIndex, seriesTotal) async { await UserPreferencesDapp().addTransaction(DappTransaction( transactionHash: txHash, chainId: web3Context!.chain.chainId, // always Ethereum type: DappTransactionType.addFunds, subtype: "approve", - // TODO: Localize - series_index: 1, - series_total: 2, + series_index: seriesIndex, + series_total: seriesTotal, )); }, - onTransaction: (txHash) async { + onTransactionCallback: (txHash, seriesIndex, seriesTotal) async { await UserPreferencesDapp().addTransaction(DappTransaction( transactionHash: txHash, chainId: web3Context!.chain.chainId, // always Ethereum type: DappTransactionType.addFunds, - subtype: "push", // TODO: Localize - series_index: hasApproval ? 2 : null, - series_total: hasApproval ? 2 : null, + subtype: "push", + series_index: seriesIndex, + series_total: seriesTotal, )); }, ); diff --git a/web-ethereum/stake_dapp/lib/pages/tabs/location_panel.dart b/web-ethereum/stake_dapp/lib/pages/tabs/location_panel.dart index ab4d5bb6a..27f5f651d 100644 --- a/web-ethereum/stake_dapp/lib/pages/tabs/location_panel.dart +++ b/web-ethereum/stake_dapp/lib/pages/tabs/location_panel.dart @@ -72,11 +72,12 @@ class _LocationPanelState extends State .padx(8), ], ), - DappButton( + DappTransactionButton( text: _isPoke ? "POKE" : "UPDATE", onPressed: _isPoke ? (_formEnabled ? _poke : null) : (_formEnabled ? _updateLocation : null), + txPending: txPending, ).top(32), ], ).width(double.infinity); diff --git a/web-ethereum/stake_dapp/lib/pages/tabs/pull_stake_panel.dart b/web-ethereum/stake_dapp/lib/pages/tabs/pull_stake_panel.dart index fd1b6da45..bb3543b1d 100644 --- a/web-ethereum/stake_dapp/lib/pages/tabs/pull_stake_panel.dart +++ b/web-ethereum/stake_dapp/lib/pages/tabs/pull_stake_panel.dart @@ -71,9 +71,10 @@ class _PullStakePanelState extends State showPaste: false, backgroundColor: OrchidColors.dark_background_2, ).top(16).padx(8), - DappButton( + DappTransactionButton( text: "PULL FUNDS", onPressed: _formEnabled ? _pullStake : null, + txPending: txPending, ).top(32), ], ).width(double.infinity); diff --git a/web-ethereum/stake_dapp/lib/pages/tabs/withdraw_stake_panel.dart b/web-ethereum/stake_dapp/lib/pages/tabs/withdraw_stake_panel.dart index 010a8db18..c040b7d62 100644 --- a/web-ethereum/stake_dapp/lib/pages/tabs/withdraw_stake_panel.dart +++ b/web-ethereum/stake_dapp/lib/pages/tabs/withdraw_stake_panel.dart @@ -82,9 +82,10 @@ class _WithdrawStakePanelState extends State contentPadding: EdgeInsets.only(top: 8, bottom: 18, left: 16, right: 16), ).top(16).padx(8), - DappButton( + DappTransactionButton( text: "PULL FUNDS", onPressed: _formEnabled ? _withdrawStake : null, + txPending: txPending, ).top(32), ], ).width(double.infinity); From 563ef9ed740512482ef922c9fb998097b82a9f5d Mon Sep 17 00:00:00 2001 From: Dan Montgomery Date: Fri, 8 Nov 2024 15:36:27 -0500 Subject: [PATCH 4/5] Split inference into separate http endpoint. --- gai-backend/account.py | 141 +++++++++++ gai-backend/billing.py | 139 ++++++++--- gai-backend/config_manager.py | 140 +++++++++++ gai-backend/inference_api.py | 415 +++++++++++++++++++++++++++++++++ gai-backend/jobs.py | 100 -------- gai-backend/lottery.py | 243 ++++++++++++------- gai-backend/lottery0.abi | 1 + gai-backend/lottery1.abi | 1 + gai-backend/payment_handler.py | 74 ++++++ gai-backend/server.py | 363 +++++++++++++++++----------- gai-backend/test.py | 307 ++++++++++++++++++++++++ gai-backend/ticket.py | 227 ++++++++---------- 12 files changed, 1680 insertions(+), 471 deletions(-) create mode 100644 gai-backend/account.py create mode 100644 gai-backend/config_manager.py create mode 100644 gai-backend/inference_api.py delete mode 100644 gai-backend/jobs.py create mode 100644 gai-backend/lottery0.abi create mode 100644 gai-backend/lottery1.abi create mode 100644 gai-backend/payment_handler.py create mode 100644 gai-backend/test.py diff --git a/gai-backend/account.py b/gai-backend/account.py new file mode 100644 index 000000000..3d46876f2 --- /dev/null +++ b/gai-backend/account.py @@ -0,0 +1,141 @@ +from web3 import Web3 +from decimal import Decimal +import secrets +from eth_account.messages import encode_defunct +from lottery import Lottery +from typing import Optional, Dict, Tuple + +class OrchidAccountError(Exception): + """Base class for Orchid account errors""" + pass + +class InvalidAddressError(OrchidAccountError): + """Invalid Ethereum address""" + pass + +class InvalidAmountError(OrchidAccountError): + """Invalid payment amount""" + pass + +class SigningError(OrchidAccountError): + """Error signing transaction or message""" + pass + +class OrchidAccount: + def __init__(self, + lottery: Lottery, + funder_address: str, + private_key: str): + try: + self.lottery = lottery + self.web3 = lottery.web3 + self.funder = self.web3.to_checksum_address(funder_address) + self.key = private_key + self.signer = self.web3.eth.account.from_key(private_key).address + except ValueError as e: + raise InvalidAddressError(f"Invalid address format: {e}") + except Exception as e: + raise OrchidAccountError(f"Failed to initialize account: {e}") + + def create_ticket(self, + amount: int, + recipient: str, + commitment: str, + token_addr: str = "0x0000000000000000000000000000000000000000" + ) -> str: + """ + Create signed nanopayment ticket + + Args: + amount: Payment amount in wei + recipient: Recipient address + commitment: Random commitment hash + token_addr: Token contract address + + Returns: + Serialized ticket string + """ + try: + if amount <= 0: + raise InvalidAmountError("Amount must be positive") + + recipient = self.web3.to_checksum_address(recipient) + token_addr = self.web3.to_checksum_address(token_addr) + + # Random nonce + nonce = secrets.randbits(128) + + # Pack ticket data + packed0 = amount | (nonce << 128) + ratio = 0xffffffffffffffff # Always create winning tickets for testing + packed1 = (ratio << 161) | (0 << 160) # v=0 + + # Sign ticket + message_hash = self._get_ticket_hash( + token_addr, + recipient, + commitment, + packed0, + packed1 + ) + + sig = self.web3.eth.account.sign_message( + encode_defunct(message_hash), + private_key=self.key + ) + + # Adjust v and update packed1 + v = sig.v - 27 + packed1 = packed1 | v + + # Format as hex strings + return ( + hex(packed0)[2:].zfill(64) + + hex(packed1)[2:].zfill(64) + + hex(sig.r)[2:].zfill(64) + + hex(sig.s)[2:].zfill(64) + ) + + except OrchidAccountError: + raise + except Exception as e: + raise SigningError(f"Failed to create ticket: {e}") + + def _get_ticket_hash(self, + token_addr: str, + recipient: str, + commitment: str, + packed0: int, + packed1: int) -> bytes: + try: + return Web3.solidity_keccak( + ['bytes1', 'bytes1', 'address', 'bytes32', 'address', 'address', + 'bytes32', 'uint256', 'uint256', 'bytes32'], + [b'\x19', b'\x00', + self.lottery.contract_addr, + b'\x00' * 31 + b'\x64', # Chain ID + token_addr, + recipient, + Web3.solidity_keccak(['bytes32'], [commitment]), + packed0, + packed1 >> 1, # Remove v + b'\x00' * 32] # Empty data field + ) + except Exception as e: + raise SigningError(f"Failed to create message hash: {e}") + + async def get_balance(self, + token_addr: str = "0x0000000000000000000000000000000000000000" + ) -> Tuple[float, float]: + try: + balance, escrow = await self.lottery.check_balance( + token_addr, + self.funder, + self.signer + ) + return ( + self.lottery.wei_to_token(balance), + self.lottery.wei_to_token(escrow) + ) + except Exception as e: + raise OrchidAccountError(f"Failed to get balance: {e}") diff --git a/gai-backend/billing.py b/gai-backend/billing.py index 5b9421d31..b4d410bd0 100644 --- a/gai-backend/billing.py +++ b/gai-backend/billing.py @@ -1,33 +1,116 @@ import json +from redis.asyncio import Redis +import redis +from decimal import Decimal +from typing import Optional, Dict +import asyncio -disconnect_threshold = -0.002 +class BillingError(Exception): + """Base class for billing errors that should terminate the connection""" + pass -def invoice(amt): - return json.dumps({'type': 'invoice', 'amount': amt}) +class RedisConnectionError(BillingError): + """Redis connection or operation failed""" + pass -class Billing: - def __init__(self, prices): - self.ledger = {} - self.prices = prices - - def credit(self, id, type=None, amount=0): - self.adjust(id, type, amount, 1) +class InconsistentStateError(BillingError): + """Billing state became inconsistent""" + pass - def debit(self, id, type=None, amount=0): - self.adjust(id, type, amount, -1) - - def adjust(self, id, type, amount, sign): - amount_ = self.prices[type] if type is not None else amount - if id in self.ledger: - self.ledger[id] = self.ledger[id] + sign * amount_ - else: - self.ledger[id] = sign * amount_ - - def min_balance(self): - return 2 * (self.prices['invoice'] + self.prices['payment']) - - def balance(self, id): - if id in self.ledger: - return self.ledger[id] - else: - return 0 \ No newline at end of file +class StrictRedisBilling: + def __init__(self, redis: Redis): + self.redis = redis + + async def init(self): + try: + await self.redis.ping() + except Exception as e: + raise RedisConnectionError(f"Failed to connect to Redis: {e}") + + def _get_client_key(self, client_id: str) -> str: + return f"billing:balance:{client_id}" + + def _get_update_channel(self, client_id: str) -> str: + return f"billing:balance:updates:{client_id}" + + async def credit(self, id: str, type: Optional[str] = None, amount: float = 0): + await self.adjust(id, type, amount, 1) + + async def debit(self, id: str, type: Optional[str] = None, amount: float = 0): + await self.adjust(id, type, amount, -1) + + async def adjust(self, id: str, type: Optional[str], amount: float, sign: int): + key = self._get_client_key(id) + channel = self._get_update_channel(id) + + # Get amount from pricing if type is provided + amount_ = amount + if type is not None: + # Get price from config + config_data = await self.redis.get("config:data") + if not config_data: + raise BillingError("No configuration found") + config = json.loads(config_data) + price = config['billing']['prices'].get(type) + if price is None: + raise BillingError(f"Unknown price type: {type}") + amount_ = price + + try: + async with self.redis.pipeline() as pipe: + while True: + try: + await pipe.watch(key) + current = await self.redis.get(key) + try: + current_balance = Decimal(current) if current else Decimal('0') + except (TypeError, ValueError) as e: + raise InconsistentStateError(f"Invalid balance format in Redis: {e}") + + new_balance = current_balance + Decimal(str(sign * amount_)) + + pipe.multi() + await pipe.set(key, str(new_balance)) + await pipe.publish(channel, str(new_balance)) + await pipe.execute() + return + + except redis.WatchError: + continue + + except Exception as e: + raise RedisConnectionError(f"Redis transaction failed: {e}") + + except BillingError: + raise + except Exception as e: + raise RedisConnectionError(f"Unexpected Redis error: {e}") + + async def balance(self, id: str) -> float: + try: + key = self._get_client_key(id) + balance = await self.redis.get(key) + + if balance is None: + return 0 + + try: + return float(Decimal(balance)) + except (TypeError, ValueError) as e: + raise InconsistentStateError(f"Invalid balance format: {e}") + + except BillingError: + raise + except Exception as e: + raise RedisConnectionError(f"Failed to get balance: {e}") + + async def min_balance(self) -> float: + try: + config_data = await self.redis.get("config:data") + if not config_data: + raise BillingError("No configuration found") + config = json.loads(config_data) + prices = config['billing']['prices'] + return 2 * (prices['invoice'] + prices['payment']) + except Exception as e: + raise BillingError(f"Failed to calculate minimum balance: {e}") diff --git a/gai-backend/config_manager.py b/gai-backend/config_manager.py new file mode 100644 index 000000000..b1122bbb8 --- /dev/null +++ b/gai-backend/config_manager.py @@ -0,0 +1,140 @@ +from typing import Dict, Any, Optional +from redis.asyncio import Redis +import json +import time +import os + +class ConfigError(Exception): + """Raised when config operations fail""" + pass + +class ConfigManager: + def __init__(self, redis: Redis): + self.redis = redis + self.last_load_time = 0 + self.current_config = {} + + async def load_from_file(self, config_path: str) -> Dict[str, Any]: + try: + with open(config_path, 'r') as f: + return json.load(f) + except FileNotFoundError: + raise ConfigError(f"Config file not found: {config_path}") + except json.JSONDecodeError as e: + raise ConfigError(f"Invalid JSON in config file: {e}") + except Exception as e: + raise ConfigError(f"Failed to load config file: {e}") + + def process_config(self, config: Dict[str, Any]) -> Dict[str, Any]: + if 'inference' not in config: + raise ConfigError("Missing required 'inference' section") + + if 'endpoints' not in config['inference']: + raise ConfigError("Missing required 'endpoints' in inference config") + + endpoints = config['inference']['endpoints'] + if not endpoints: + raise ConfigError("No inference endpoints configured") + + total_models = 0 + + for endpoint_id, endpoint in endpoints.items(): + required_fields = ['api_type', 'url', 'api_key', 'models'] + missing = [field for field in required_fields if field not in endpoint] + if missing: + raise ConfigError(f"Endpoint {endpoint_id} missing required fields: {', '.join(missing)}") + if not isinstance(endpoint['models'], list): + raise ConfigError(f"Endpoint {endpoint_id} 'models' must be a list") + if not endpoint['models']: + raise ConfigError(f"Endpoint {endpoint_id} has no models configured") + + total_models += len(endpoint['models']) + + for model in endpoint['models']: + required_model_fields = ['id', 'pricing'] + missing = [field for field in required_model_fields if field not in model] + if missing: + raise ConfigError(f"Model in endpoint {endpoint_id} missing required fields: {', '.join(missing)}") + if 'params' not in model: + model['params'] = {} + + pricing = model['pricing'] + if 'type' not in pricing: + raise ConfigError(f"Model {model['id']} missing required pricing type") + + required_pricing_fields = { + 'fixed': ['input_price', 'output_price'], + 'cost_plus': ['backend_input', 'backend_output', 'input_markup', 'output_markup'], + 'multiplier': ['backend_input', 'backend_output', 'input_multiplier', 'output_multiplier'] + } + + if pricing['type'] not in required_pricing_fields: + raise ConfigError(f"Invalid pricing type for model {model['id']}: {pricing['type']}") + + missing = [field for field in required_pricing_fields[pricing['type']] + if field not in pricing] + if missing: + raise ConfigError(f"Model {model['id']} pricing missing required fields: {', '.join(missing)}") + + if total_models == 0: + raise ConfigError("No models configured across all endpoints") + + return config + + async def write_config(self, config: Dict[str, Any], force: bool = False): + try: + config = self.process_config(config) + + async with self.redis.pipeline() as pipe: + if not force: + current_time = await self.redis.get("config:last_update") + if current_time and float(current_time) > self.last_load_time: + raise ValueError("Config was updated more recently by another server") + + timestamp = time.time() + await pipe.set("config:data", json.dumps(config)) + await pipe.set("config:last_update", str(timestamp)) + await pipe.execute() + + self.current_config = config + self.last_load_time = timestamp + + except Exception as e: + raise ConfigError(f"Failed to write config: {e}") + + async def load_config(self, config_path: Optional[str] = None, force_reload: bool = False) -> Dict[str, Any]: + try: + if config_path: + config = await self.load_from_file(config_path) + await self.write_config(config, force=True) + return config + + timestamp = await self.redis.get("config:last_update") + + if not force_reload and timestamp and self.last_load_time >= float(timestamp): + return self.current_config + + config_data = await self.redis.get("config:data") + if not config_data: + raise ConfigError("No configuration found in Redis") + + config = json.loads(config_data) + config = self.process_config(config) + + self.current_config = config + self.last_load_time = float(timestamp) if timestamp else time.time() + return config + + except Exception as e: + raise ConfigError(f"Failed to load config: {e}") + + async def check_for_updates(self) -> bool: + try: + timestamp = await self.redis.get("config:last_update") + if timestamp and float(timestamp) > self.last_load_time: + await self.load_config(force_reload=True) + return True + return False + + except Exception as e: + raise ConfigError(f"Failed to check for updates: {e}") diff --git a/gai-backend/inference_api.py b/gai-backend/inference_api.py new file mode 100644 index 000000000..b5195b3ab --- /dev/null +++ b/gai-backend/inference_api.py @@ -0,0 +1,415 @@ +from fastapi import FastAPI, HTTPException, Depends +from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials +from fastapi.middleware.cors import CORSMiddleware +from redis.asyncio import Redis +from pydantic import BaseModel, Field +from typing import Optional, Dict, Any, Tuple, List, Literal +import json +import os +import requests +from config_manager import ConfigManager +from billing import StrictRedisBilling, BillingError +import logging + +app = FastAPI() +security = HTTPBearer() +logger = logging.getLogger(__name__) + +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + +class Message(BaseModel): + role: Literal["system", "user", "assistant"] + content: str + name: Optional[str] = None + +class InferenceRequest(BaseModel): + messages: List[Message] + model: str + params: Optional[Dict[str, Any]] = None + request_id: Optional[str] = None + +class ModelInfo(BaseModel): + id: str + name: str + api_type: Literal["openai", "anthropic", "openrouter"] + endpoint: str + +class InferenceAPIError(Exception): + def __init__(self, status_code: int, detail: str): + self.status_code = status_code + self.detail = detail + +class PricingError(Exception): + """Raised when pricing calculation fails""" + pass + +class InferenceAPI: + def __init__(self, redis: Redis): + self.redis = redis + self.config_manager = ConfigManager(redis) + self.billing = StrictRedisBilling(redis) + + async def init(self): + await self.billing.init() + await self.config_manager.load_config() + + async def list_models(self) -> Dict[str, ModelInfo]: + config = await self.config_manager.load_config() + models = {} + + for endpoint_id, endpoint in config['inference']['endpoints'].items(): + for model in endpoint['models']: + models[model['id']] = ModelInfo( + id=model['id'], + name=model.get('display_name', model['id']), + api_type=endpoint['api_type'], + endpoint=endpoint_id + ) + + return models + + async def validate_session(self, credentials: HTTPAuthorizationCredentials) -> str: + if not credentials: + logger.error("No credentials provided") + raise InferenceAPIError(401, "Missing session ID") + + session_id = credentials.credentials + logger.info(f"Validating session: {session_id}") + + try: + balance = await self.billing.balance(session_id) + logger.info(f"Session {session_id} balance: {balance}") + + if balance is None: + logger.error(f"No balance found for session {session_id}") + raise InferenceAPIError(401, "Invalid session") + + min_balance = await self.billing.min_balance() + logger.info(f"Minimum balance required: {min_balance}") + + if balance < min_balance: + logger.warning(f"Insufficient balance: {balance} < {min_balance}") + raise InferenceAPIError(402, "Insufficient balance") + + return session_id + + except BillingError as e: + logger.error(f"Billing error during validation: {e}") + raise InferenceAPIError(500, "Internal service error") + + except Exception as e: + logger.error(f"Unexpected error during validation: {e}") + raise InferenceAPIError(500, f"Internal service error: {e}") + + async def get_model_config(self, model_id: str) -> Tuple[Dict[str, Any], Dict[str, Any]]: + config = await self.config_manager.load_config() + endpoints = config['inference']['endpoints'] + + for endpoint_id, endpoint in endpoints.items(): + for model in endpoint['models']: + if model['id'] == model_id: + return endpoint, model + + raise InferenceAPIError(400, f"Unknown model: {model_id}") + + def prepare_request(self, endpoint_config: Dict[str, Any], model_config: Dict[str, Any], request: InferenceRequest) -> Tuple[Dict[str, Any], Dict[str, str]]: + params = { + **(endpoint_config.get('params', {})), + **(model_config.get('params', {})), + **(request.params or {}) + } + + headers = {"Content-Type": "application/json"} + api_type = endpoint_config['api_type'] + + if not (api_key := endpoint_config.get('api_key')): + logger.error("No API key configured for endpoint") + raise InferenceAPIError(500, "Backend authentication not configured") + + data: Dict[str, Any] = {} + + if api_type == 'openai': + headers["Authorization"] = f"Bearer {api_key}" + data = { + 'model': model_config['id'], + 'messages': [msg.dict(exclude_none=True) for msg in request.messages] + } + if 'max_tokens' in (request.params or {}): + data['max_tokens'] = params['max_tokens'] + + elif api_type == 'openrouter': + headers["Authorization"] = f"Bearer {api_key}" + data = { + 'model': model_config['id'], + 'messages': [msg.dict(exclude_none=True) for msg in request.messages] + } + + if 'max_tokens' in (request.params or {}): + user_max_tokens = params['max_tokens'] + config_max_tokens = model_config.get('params', {}).get('max_tokens') + + if config_max_tokens and user_max_tokens > config_max_tokens: + raise InferenceAPIError(400, f"Requested max_tokens {user_max_tokens} exceeds model limit {config_max_tokens}") + + prompt_tokens = self.count_input_tokens(request) + if config_max_tokens and (prompt_tokens + user_max_tokens) > config_max_tokens: + raise InferenceAPIError(400, + f"Combined prompt ({prompt_tokens}) and max_tokens ({user_max_tokens}) " + f"exceeds model context limit {config_max_tokens}") + + data['max_tokens'] = user_max_tokens + + elif api_type == 'anthropic': + headers["x-api-key"] = api_key + headers["anthropic-version"] = "2023-06-01" + system_message = next((msg.content for msg in request.messages if msg.role == "system"), None) + conversation = [msg for msg in request.messages if msg.role != "system"] + + data = { + 'model': model_config['id'], + 'messages': [{'role': msg.role, 'content': msg.content} for msg in conversation], + 'max_tokens': params.get('max_tokens', 4096) + } + if system_message: + data['system'] = system_message + + else: + raise InferenceAPIError(500, f"Unsupported API type: {api_type}") + + for k, v in params.items(): + if k != 'max_tokens' and k not in data: + data[k] = v + + return data, headers + + def parse_response(self, api_type: str, response: Dict[str, Any], request_id: Optional[str] = None) -> Dict[str, Any]: + try: + base_response = { + 'request_id': request_id, + } + + if api_type in ['openai', 'openrouter']: # OpenRouter follows OpenAI response format + return { + **base_response, + 'response': response['choices'][0]['message']['content'], + 'usage': response['usage'] + } + elif api_type == 'anthropic': + return { + **base_response, + 'response': response['content'][0]['text'], + 'usage': { + 'prompt_tokens': response['usage']['input_tokens'], + 'completion_tokens': response['usage']['output_tokens'], + 'total_tokens': response['usage']['input_tokens'] + response['usage']['output_tokens'] + } + } + else: + raise InferenceAPIError(500, f"Unsupported API type: {api_type}") + except KeyError as e: + logger.error(f"Failed to parse {api_type} response: {e}") + logger.error(f"Response: {response}") + logger.debug(f"Raw response: {response}") + raise InferenceAPIError(502, f"Invalid {api_type} response format") + + def query_backend(self, endpoint_config: Dict[str, Any], model_config: Dict[str, Any], request: InferenceRequest) -> Dict[str, Any]: + try: + data, headers = self.prepare_request(endpoint_config, model_config, request) + + logger.info(f"Sending request to backend: {endpoint_config['url']}") + logger.debug(f"Request headers: {headers}") + logger.debug(f"Request data: {data}") + + response = requests.post( + endpoint_config['url'], + headers=headers, + json=data, + timeout=30 + ) + + try: + response.raise_for_status() + except requests.exceptions.HTTPError as e: + if response.status_code == 400: + if endpoint_config['api_type'] == 'openrouter': + error_body = response.json() + if 'error' in error_body and 'message' in error_body['error']: + raise InferenceAPIError(400, error_body['error']['message']) + raise + + result = response.json() + logger.debug(f"Raw backend response: {result}") + + return self.parse_response(endpoint_config['api_type'], result, request.request_id) + + except requests.exceptions.RequestException as e: + logger.error(f"Backend request failed: {e}") + raise InferenceAPIError(502, "Backend service error") + + def get_token_prices(self, pricing_config: Dict[str, Any]) -> Tuple[float, float]: + try: + pricing_type = pricing_config['type'] + + if pricing_type == 'fixed': + return ( + pricing_config['input_price'], + pricing_config['output_price'] + ) + + elif pricing_type == 'cost_plus': + return ( + pricing_config['backend_input'] + pricing_config['input_markup'], + pricing_config['backend_output'] + pricing_config['output_markup'] + ) + + elif pricing_type == 'multiplier': + return ( + pricing_config['backend_input'] * pricing_config['input_multiplier'], + pricing_config['backend_output'] * pricing_config['output_multiplier'] + ) + + else: + raise PricingError(f"Unknown pricing type: {pricing_type}") + + except KeyError as e: + raise PricingError(f"Missing required pricing field: {e}") + + def calculate_cost(self, pricing_config: Dict[str, Any], input_tokens: int, output_tokens: int) -> float: + try: + input_price, output_price = self.get_token_prices(pricing_config) + + total_cost = ( + (input_tokens * input_price) + + (output_tokens * output_price) + ) / 1_000_000 # Convert to millions of tokens + + return total_cost + + except Exception as e: + raise PricingError(f"Failed to calculate cost: {e}") + + def estimate_max_cost(self, pricing_config: Dict[str, Any], input_tokens: int, max_output_tokens: int) -> float: + return self.calculate_cost(pricing_config, input_tokens, max_output_tokens) + + def count_input_tokens(self, request: InferenceRequest) -> int: + # TODO: Implement proper tokenization based on model + return sum(len(msg.content) // 4 for msg in request.messages) # Rough estimate + + async def handle_inference( + self, + request: InferenceRequest, + session_id: str + ) -> Dict[str, Any]: + try: + # Get endpoint and model configs - model is now required + endpoint_config, model_config = await self.get_model_config(request.model) + + # Calculate maximum possible cost + input_tokens = self.count_input_tokens(request) + max_output_tokens = model_config.get('params', {}).get( + 'max_tokens', + endpoint_config.get('params', {}).get('max_tokens', 4096) + ) + + max_cost = self.estimate_max_cost( + model_config['pricing'], + input_tokens, + max_output_tokens + ) + + balance = await self.billing.balance(session_id) + if balance < max_cost: + logger.warning(f"Insufficient balance for max cost: {balance} < {max_cost}") + await self.redis.publish( + f"billing:balance:updates:{session_id}", + str(balance) + ) + raise InferenceAPIError(402, "Insufficient balance") + + await self.billing.debit(session_id, amount=max_cost) + logger.info(f"Reserved {max_cost} tokens from balance") + + try: + result = self.query_backend(endpoint_config, model_config, request) + + actual_cost = self.calculate_cost( + model_config['pricing'], + result['usage']['prompt_tokens'], + result['usage']['completion_tokens'] + ) + + logger.info(f"Actual cost: {actual_cost} (reserved: {max_cost})") + + if actual_cost < max_cost: + refund = max_cost - actual_cost + await self.billing.credit(session_id, amount=refund) + logger.info(f"Refunded excess reservation: {refund}") + + return result + + except Exception as e: + logger.error(f"Error during inference: {e}") + await self.billing.credit(session_id, amount=max_cost) + raise + + except BillingError as e: + logger.error(f"Billing error: {e}") + raise InferenceAPIError(500, "Internal service error") + except PricingError as e: + logger.error(f"Pricing error: {e}") + raise InferenceAPIError(500, f"Pricing configuration error: {e}") + except Exception as e: + logger.error(f"Unexpected error: {e}") + raise InferenceAPIError(500, str(e)) + +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' +) + +redis_url = os.environ.get('REDIS_URL', 'redis://localhost:6379') +redis = Redis.from_url(redis_url, decode_responses=True) +api = InferenceAPI(redis) + +@app.on_event("startup") +async def startup(): + await api.init() + +@app.post("/v1/chat/completions") +async def chat_completion( + request: InferenceRequest, + credentials: HTTPAuthorizationCredentials = Depends(security) +): + logger.info(f"Received chat completion request with auth: {credentials.scheme} {credentials.credentials}") + try: + session_id = await api.validate_session(credentials) + return await api.handle_inference(request, session_id) + except InferenceAPIError as e: + logger.error(f"Inference API error: {e.status_code} - {e.detail}") + raise HTTPException(status_code=e.status_code, detail=e.detail) + except Exception as e: + logger.error(f"Unexpected error: {e}") + raise HTTPException(status_code=500, detail=str(e)) + +@app.post("/v1/inference") +async def inference( + request: InferenceRequest, + credentials: HTTPAuthorizationCredentials = Depends(security) +): + return await chat_completion(request, credentials) + +@app.get("/v1/models") +async def list_models(): + """List available inference models""" + try: + models = await api.list_models() + return models + except Exception as e: + logger.error(f"Failed to list models: {e}") + raise HTTPException(status_code=500, detail=str(e)) diff --git a/gai-backend/jobs.py b/gai-backend/jobs.py deleted file mode 100644 index 6ec93091f..000000000 --- a/gai-backend/jobs.py +++ /dev/null @@ -1,100 +0,0 @@ -import asyncio -import random -import datetime -import json -import requests - -import websockets - - -class jobs: - def __init__(self, model, url, llmkey, llmparams, api='openai'): - self.queue = asyncio.PriorityQueue() - self.sessions = {} - self.model = model - self.url = url - self.llmkey = llmkey - self.llmparams = llmparams - self.api = api - - def get_queues(self, id): - squeue = asyncio.Queue(maxsize=10) - rqueue = asyncio.Queue(maxsize=10) - self.sessions[id] = {'send': squeue, 'recv': rqueue} - return [rqueue, squeue] - - async def add_job(self, id, bid, job): - print(f'add_job({id}, {bid}, {job})') - priority = 0.1 - await self.queue.put((priority, [id, bid, job])) - - async def process_jobs(self): - print(f"Starting process_jobs() with api: {self.api}") - while True: - priority, job_params = await self.queue.get() - id, bid, job = job_params - await self.sessions[id]['send'].put(json.dumps({'type': 'started'})) - response, reason, usage = apis[self.api](job['prompt'], self.llmparams, self.model, self.url, self.llmkey) - if response == None: - continue - await self.sessions[id]['send'].put(json.dumps({'type': 'complete', 'response': response, - 'model': self.model, 'reason': reason, - 'usage': usage})) - - -def query_openai(prompt, params, model, url, llmkey): - result = "" - data = {'model': model, 'messages': [{'role': 'user', 'content': prompt}]} - data = {**data, **params} - headers = {"Content-Type": "application/json"} - if not llmkey is None: - headers["Authorization"] = f"Bearer {llmkey}" - r = requests.post(url, data=json.dumps(data), headers=headers) - result = r.json() - if result['object'] != 'chat.completion': - print('*** process_jobs: Error from llm') - print(f"from job: {job_params}") - print(result) - return None, None, None - response = result['choices'][0]['message']['content'] - model = result['model'] - reason = result['choices'][0]['finish_reason'] - usage = result['usage'] - return response, reason, usage - -def query_gemini(prompt, params, model, url, llmkey): - data = {'contents': [{'parts': [{'text': prompt}]}]} - data = {**data, **params} - print(f"query_gemini(): data: {data}") - headers = {"Content-Type": "application/json"} - url_ = url + f"?key={llmkey}" - r = requests.post(url_, data=json.dumps(data), headers=headers) - print(r.json()) - result = r.json()['candidates'][0] - response = result['content']['parts'][0]['text'] - reason = result['finishReason'] - usage = 0 - return response, reason, usage - -def query_anthropic(prompt, params, model, url, llmkey): - data = {'model': model, 'messages': [{'role': 'user', 'content': prompt}]} - data = {**data, **params} - headers = {"Content-Type": "application/json", - "anthropic-version": "2023-06-01"} - if not llmkey is None: - headers["x-api-key"] = llmkey - r = requests.post(url, data=json.dumps(data), headers=headers) - result = r.json() - if 'content' not in result: - print('*** process_jobs: Error from llm') - print(f"from job: {job_params}") - print(result) - return None, None, None - response = result['content'][0]['text'] - model = result['model'] - reason = result['stop_reason'] - usage = result['usage'] - return response, reason, usage - - -apis = {'openai': query_openai, 'gemini': query_gemini, 'anthropic': query_anthropic} diff --git a/gai-backend/lottery.py b/gai-backend/lottery.py index c7a219777..73f456bae 100644 --- a/gai-backend/lottery.py +++ b/gai-backend/lottery.py @@ -1,82 +1,161 @@ -import json -import web3 - -from ticket import Ticket - -# Gnosis - default -rpc_url_default = 'https://rpc.gnosischain.com/' -chain_id_default = 100 - -# Polygon - default -# rpc_url_default = 'https://polygon-rpc.com' -# chain_id_default = 137 - -# Gas default -gas_amount_default = 100000 - -uint64 = pow(2, 64) - 1 # 18446744073709551615 -uint128 = pow(2, 128) - 1 # 340282366920938463463374607431768211455 - -def to_32byte_hex(val): - return web3.Web3.to_hex(web3.Web3.to_bytes(hexstr=val).rjust(32, b'\0')) - - -class Lottery: - addr_type = pow(2, 20 * 8) - 1 - contract_addr = '0x6dB8381b2B41b74E17F5D4eB82E8d5b04ddA0a82' - token = '0x' + '0' * 40 - contract_abi_str = "[ { \"inputs\": [ { \"internalType\": \"uint64\", \"name\": \"day\", \"type\": \"uint64\" } ], \"stateMutability\": \"nonpayable\", \"type\": \"constructor\" }, { \"anonymous\": false, \"inputs\": [ { \"indexed\": true, \"internalType\": \"contract IERC20\", \"name\": \"token\", \"type\": \"address\" }, { \"indexed\": true, \"internalType\": \"address\", \"name\": \"funder\", \"type\": \"address\" }, { \"indexed\": true, \"internalType\": \"address\", \"name\": \"signer\", \"type\": \"address\" } ], \"name\": \"Create\", \"type\": \"event\" }, { \"anonymous\": false, \"inputs\": [ { \"indexed\": true, \"internalType\": \"bytes32\", \"name\": \"key\", \"type\": \"bytes32\" }, { \"indexed\": false, \"internalType\": \"uint256\", \"name\": \"unlock_warned\", \"type\": \"uint256\" } ], \"name\": \"Delete\", \"type\": \"event\" }, { \"anonymous\": false, \"inputs\": [ { \"indexed\": true, \"internalType\": \"address\", \"name\": \"funder\", \"type\": \"address\" }, { \"indexed\": true, \"internalType\": \"address\", \"name\": \"recipient\", \"type\": \"address\" } ], \"name\": \"Enroll\", \"type\": \"event\" }, { \"anonymous\": false, \"inputs\": [ { \"indexed\": true, \"internalType\": \"bytes32\", \"name\": \"key\", \"type\": \"bytes32\" }, { \"indexed\": false, \"internalType\": \"uint256\", \"name\": \"escrow_amount\", \"type\": \"uint256\" } ], \"name\": \"Update\", \"type\": \"event\" }, { \"inputs\": [ { \"internalType\": \"contract IERC20\", \"name\": \"token\", \"type\": \"address\" }, { \"internalType\": \"address\", \"name\": \"recipient\", \"type\": \"address\" }, { \"components\": [ { \"internalType\": \"bytes32\", \"name\": \"data\", \"type\": \"bytes32\" }, { \"internalType\": \"bytes32\", \"name\": \"reveal\", \"type\": \"bytes32\" }, { \"internalType\": \"uint256\", \"name\": \"packed0\", \"type\": \"uint256\" }, { \"internalType\": \"uint256\", \"name\": \"packed1\", \"type\": \"uint256\" }, { \"internalType\": \"bytes32\", \"name\": \"r\", \"type\": \"bytes32\" }, { \"internalType\": \"bytes32\", \"name\": \"s\", \"type\": \"bytes32\" } ], \"internalType\": \"struct OrchidLottery1.Ticket[]\", \"name\": \"tickets\", \"type\": \"tuple[]\" }, { \"internalType\": \"bytes32[]\", \"name\": \"refunds\", \"type\": \"bytes32[]\" } ], \"name\": \"claim\", \"outputs\": [], \"stateMutability\": \"nonpayable\", \"type\": \"function\" }, { \"inputs\": [ { \"internalType\": \"contract IERC20\", \"name\": \"token\", \"type\": \"address\" }, { \"internalType\": \"uint256\", \"name\": \"amount\", \"type\": \"uint256\" }, { \"internalType\": \"address\", \"name\": \"signer\", \"type\": \"address\" }, { \"internalType\": \"int256\", \"name\": \"adjust\", \"type\": \"int256\" }, { \"internalType\": \"int256\", \"name\": \"warn\", \"type\": \"int256\" }, { \"internalType\": \"uint256\", \"name\": \"retrieve\", \"type\": \"uint256\" } ], \"name\": \"edit\", \"outputs\": [], \"stateMutability\": \"nonpayable\", \"type\": \"function\" }, { \"inputs\": [ { \"internalType\": \"address\", \"name\": \"signer\", \"type\": \"address\" }, { \"internalType\": \"int256\", \"name\": \"adjust\", \"type\": \"int256\" }, { \"internalType\": \"int256\", \"name\": \"warn\", \"type\": \"int256\" }, { \"internalType\": \"uint256\", \"name\": \"retrieve\", \"type\": \"uint256\" } ], \"name\": \"edit\", \"outputs\": [], \"stateMutability\": \"payable\", \"type\": \"function\" }, { \"inputs\": [ { \"internalType\": \"bool\", \"name\": \"cancel\", \"type\": \"bool\" }, { \"internalType\": \"address[]\", \"name\": \"recipients\", \"type\": \"address[]\" } ], \"name\": \"enroll\", \"outputs\": [], \"stateMutability\": \"nonpayable\", \"type\": \"function\" }, { \"inputs\": [ { \"internalType\": \"address\", \"name\": \"funder\", \"type\": \"address\" }, { \"internalType\": \"address\", \"name\": \"recipient\", \"type\": \"address\" } ], \"name\": \"enrolled\", \"outputs\": [ { \"internalType\": \"uint256\", \"name\": \"\", \"type\": \"uint256\" } ], \"stateMutability\": \"view\", \"type\": \"function\" }, { \"inputs\": [ { \"internalType\": \"contract IERC20\", \"name\": \"token\", \"type\": \"address\" }, { \"internalType\": \"address\", \"name\": \"signer\", \"type\": \"address\" }, { \"internalType\": \"uint64\", \"name\": \"marked\", \"type\": \"uint64\" } ], \"name\": \"mark\", \"outputs\": [], \"stateMutability\": \"nonpayable\", \"type\": \"function\" }, { \"inputs\": [ { \"internalType\": \"address\", \"name\": \"sender\", \"type\": \"address\" }, { \"internalType\": \"uint256\", \"name\": \"amount\", \"type\": \"uint256\" }, { \"internalType\": \"bytes\", \"name\": \"data\", \"type\": \"bytes\" } ], \"name\": \"onTokenTransfer\", \"outputs\": [ { \"internalType\": \"bool\", \"name\": \"\", \"type\": \"bool\" } ], \"stateMutability\": \"nonpayable\", \"type\": \"function\" }, { \"inputs\": [ { \"internalType\": \"contract IERC20\", \"name\": \"token\", \"type\": \"address\" }, { \"internalType\": \"address\", \"name\": \"funder\", \"type\": \"address\" }, { \"internalType\": \"address\", \"name\": \"signer\", \"type\": \"address\" } ], \"name\": \"read\", \"outputs\": [ { \"internalType\": \"uint256\", \"name\": \"\", \"type\": \"uint256\" }, { \"internalType\": \"uint256\", \"name\": \"\", \"type\": \"uint256\" } ], \"stateMutability\": \"view\", \"type\": \"function\" }, { \"inputs\": [ { \"internalType\": \"uint256\", \"name\": \"count\", \"type\": \"uint256\" }, { \"internalType\": \"bytes32\", \"name\": \"seed\", \"type\": \"bytes32\" } ], \"name\": \"save\", \"outputs\": [], \"stateMutability\": \"nonpayable\", \"type\": \"function\" }, { \"inputs\": [ { \"internalType\": \"address\", \"name\": \"sender\", \"type\": \"address\" }, { \"internalType\": \"uint256\", \"name\": \"amount\", \"type\": \"uint256\" }, { \"internalType\": \"bytes\", \"name\": \"data\", \"type\": \"bytes\" } ], \"name\": \"tokenFallback\", \"outputs\": [], \"stateMutability\": \"nonpayable\", \"type\": \"function\" }]" - contract_abi= None - - contract= None - rpc_url= None - chain_id = None - gas_amount = None - - def __init__(self, rpc_url=rpc_url_default, chain_id=chain_id_default, gas_amount=gas_amount_default): - self.rpc_url = rpc_url - self.chain_id = chain_id - self.gas_amount = gas_amount - - self.contract_abi = json.loads(self.contract_abi_str) - - def init_contract(self, web3): - self.web3 = web3 - self.contract = self.web3.eth.contract(address=self.contract_addr, abi=self.contract_abi) - - - @staticmethod - def prepareTicket(tk:Ticket, reveal): - return [tk.data, to_32byte_hex(reveal), tk.packed0, tk.packed1, to_32byte_hex(tk.sig_r), to_32byte_hex(tk.sig_s)] - return [tk.data.hex(), reveal, tk.packed0, tk.packed1, tk.sig_r, tk.sig_s] - - # Ticket object, L1 address & key - def claim_ticket(self, ticket, recipient, executor_key, reveal): - tk = Lottery.prepareTicket(ticket, reveal) - executor_address = self.web3.eth.account.from_key(executor_key).address - l1nonce = self.web3.eth.get_transaction_count(executor_address) - func = self.contract.functions.claim(self.token, recipient, [tk], []) - - tx = func.build_transaction({ - 'chainId': self.chain_id, - 'gas': self.gas_amount, - 'maxFeePerGas': self.web3.to_wei('100', 'gwei'), - 'maxPriorityFeePerGas': self.web3.to_wei('40', 'gwei'), - 'nonce': l1nonce - }) - - # Polygon Estimates - # if (self.chain_id == 137): - # gas_estimate = self.web3.eth.estimate_gas(tx) - # print("gas ", gas_estimate) - # tx.update({'gas': gas_estimate}) - - signed = self.web3.eth.account.sign_transaction(tx, private_key=executor_key) - txhash = self.web3.eth.send_raw_transaction(signed.rawTransaction) - return txhash.hex() - - def check_balance(self, addressL1, addressL2): - escrow_amount = self.contract.functions.read(self.token, addressL1, addressL2).call(block_identifier='latest')[0] - balance = float(escrow_amount & uint128) / pow(10,18) - escrow = float(escrow_amount >> 128) / pow(10,18) - return balance, escrow \ No newline at end of file +import web3 +from web3 import Web3 +from typing import Tuple, Optional, List +import json +from eth_abi.packed import encode_packed +from ticket import Ticket +import os + +class LotteryError(Exception): + """Raised when lottery operations fail""" + pass + +class Lottery: + V1_ADDR = "0x6dB8381b2B41b74E17F5D4eB82E8d5b04ddA0a82" # v1 on all chains + V0_CHAIN_ID = 1 + V0_TOKEN = "0x4575f41308EC1483f3d399aa9a2826d74Da13Deb" # OXT + V0_ADDR = "0xb02396f06CC894834b7934ecF8c8E5Ab5C1d12F1" + + WEI = 10**18 + UINT128_MAX = (1 << 128) - 1 + UINT64_MAX = (1 << 64) - 1 + + def __init__(self, + web3_provider: Web3, + chain_id: int = 100, + addr: str = None, + gas_amount: int = 100000): + self.web3 = web3_provider + self.chain_id = chain_id + self.contract_addr = addr or self.V1_ADDR + self.gas_amount = gas_amount + self.version = self._detect_version() + self.contract = None + self.init_contract() + + def _detect_version(self) -> int: + """Determine if this is a v0 or v1 lottery""" + if (self.chain_id == self.V0_CHAIN_ID and + self.contract_addr.lower() == self.V0_ADDR.lower()): + return 0 + if self.contract_addr.lower() != self.V1_ADDR.lower(): + raise LotteryError(f"Unknown lottery contract address: {self.contract_addr}") + return 1 + + def init_contract(self): + try: + if self.version == 0: + abi = self._load_contract_abi("lottery0.abi") + else: + abi = self._load_contract_abi("lottery1.abi") + + self.contract = self.web3.eth.contract( + address=self.contract_addr, + abi=abi + ) + except Exception as e: + raise LotteryError(f"Failed to initialize contract: {e}") + + def _load_contract_abi(self, filename: str) -> dict: + try: + module_dir = os.path.dirname(os.path.abspath(__file__)) + abi_path = os.path.join(module_dir, filename) + + with open(abi_path, 'r') as f: + return json.load(f) + except Exception as e: + raise LotteryError(f"Failed to load contract ABI from {abi_path}: {e}") + + async def check_balance(self, + token_addr: str, + funder: str, + signer: str) -> Tuple[int, int]: + try: + funder = self.web3.to_checksum_address(funder) + signer = self.web3.to_checksum_address(signer) + token_addr = self.web3.to_checksum_address(token_addr) + + if self.version == 0: + if token_addr.lower() != self.V0_TOKEN.lower(): + raise LotteryError("V0 lottery only supports OXT token") + escrow_amount, unlock_warned = await self.contract.functions.look( + funder, + signer + ).call() + else: + escrow_amount, unlock_warned = await self.contract.functions.read( + token_addr, + funder, + signer + ).call() + + balance = escrow_amount & self.UINT128_MAX + escrow = escrow_amount >> 128 + return balance, escrow + + except Exception as e: + raise LotteryError(f"Failed to check balance: {e}") + + def claim_tickets(self, + recipient: str, + tickets: List[Ticket], + executor_key: str, + token_addr: str = "0x0000000000000000000000000000000000000000" + ) -> str: + try: + recipient = self.web3.to_checksum_address(recipient) + token_addr = self.web3.to_checksum_address(token_addr) + + if self.version == 0 and token_addr.lower() != self.V0_TOKEN.lower(): + raise LotteryError("V0 lottery only supports OXT token") + + executor_address = self.web3.eth.account.from_key(executor_key).address + nonce = self.web3.eth.get_transaction_count(executor_address) + + prepared_tickets = [ + self._prepare_ticket(ticket, ticket.reveal) + for ticket in tickets + ] + + func = self.contract.functions.claim( + token_addr, + recipient, + prepared_tickets, + [] # Empty refunds array + ) + + tx = func.build_transaction({ + 'chainId': self.chain_id, + 'gas': self.gas_amount, + 'maxFeePerGas': self.web3.to_wei('100', 'gwei'), + 'maxPriorityFeePerGas': self.web3.to_wei('40', 'gwei'), + 'nonce': nonce + }) + + signed = self.web3.eth.account.sign_transaction( + tx, + private_key=executor_key + ) + tx_hash = self.web3.eth.send_raw_transaction(signed.rawTransaction) + return tx_hash.hex() + + except Exception as e: + raise LotteryError(f"Failed to claim tickets: {e}") + + def _prepare_ticket(self, ticket: Ticket, reveal: str) -> list: + return [ + ticket.data, + Web3.to_bytes(hexstr=reveal), + ticket.packed0, + ticket.packed1, + Web3.to_bytes(hexstr=ticket.sig_r), + Web3.to_bytes(hexstr=ticket.sig_s) + ] + + @staticmethod + def wei_to_token(wei_amount: int) -> float: + return wei_amount / Lottery.WEI + + @staticmethod + def token_to_wei(token_amount: float) -> int: + return int(token_amount * Lottery.WEI) diff --git a/gai-backend/lottery0.abi b/gai-backend/lottery0.abi new file mode 100644 index 000000000..6b8e78799 --- /dev/null +++ b/gai-backend/lottery0.abi @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"funder","type":"address"},{"indexed":true,"internalType":"address","name":"signer","type":"address"}],"name":"Bound","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"funder","type":"address"},{"indexed":true,"internalType":"address","name":"signer","type":"address"}],"name":"Create","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"funder","type":"address"},{"indexed":true,"internalType":"address","name":"signer","type":"address"},{"indexed":false,"internalType":"uint128","name":"amount","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"escrow","type":"uint128"},{"indexed":false,"internalType":"uint256","name":"unlock","type":"uint256"}],"name":"Update","type":"event"},{"constant":false,"inputs":[{"internalType":"address","name":"signer","type":"address"},{"internalType":"contract OrchidVerifier","name":"verify","type":"address"},{"internalType":"bytes","name":"shared","type":"bytes"}],"name":"bind","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"signer","type":"address"},{"internalType":"uint128","name":"escrow","type":"uint128"}],"name":"burn","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"funder","type":"address"},{"internalType":"address payable","name":"recipient","type":"address"},{"internalType":"uint128","name":"amount","type":"uint128"},{"internalType":"bytes","name":"receipt","type":"bytes"}],"name":"give","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"reveal","type":"bytes32"},{"internalType":"bytes32","name":"commit","type":"bytes32"},{"internalType":"uint256","name":"issued","type":"uint256"},{"internalType":"bytes32","name":"nonce","type":"bytes32"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"},{"internalType":"uint128","name":"amount","type":"uint128"},{"internalType":"uint128","name":"ratio","type":"uint128"},{"internalType":"uint256","name":"start","type":"uint256"},{"internalType":"uint128","name":"range","type":"uint128"},{"internalType":"address","name":"funder","type":"address"},{"internalType":"address payable","name":"recipient","type":"address"},{"internalType":"bytes","name":"receipt","type":"bytes"},{"internalType":"bytes32[]","name":"old","type":"bytes32[]"}],"name":"grab","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"funder","type":"address"}],"name":"keys","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"signer","type":"address"}],"name":"kill","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"signer","type":"address"}],"name":"lock","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"funder","type":"address"},{"internalType":"address","name":"signer","type":"address"}],"name":"look","outputs":[{"internalType":"uint128","name":"","type":"uint128"},{"internalType":"uint128","name":"","type":"uint128"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"contract OrchidVerifier","name":"","type":"address"},{"internalType":"bytes32","name":"","type":"bytes32"},{"internalType":"bytes","name":"","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"signer","type":"address"},{"internalType":"uint128","name":"amount","type":"uint128"}],"name":"move","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"funder","type":"address"},{"internalType":"uint256","name":"offset","type":"uint256"},{"internalType":"uint256","name":"count","type":"uint256"}],"name":"page","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"signer","type":"address"},{"internalType":"address payable","name":"target","type":"address"},{"internalType":"bool","name":"autolock","type":"bool"},{"internalType":"uint128","name":"amount","type":"uint128"},{"internalType":"uint128","name":"escrow","type":"uint128"}],"name":"pull","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"signer","type":"address"},{"internalType":"uint128","name":"total","type":"uint128"},{"internalType":"uint128","name":"escrow","type":"uint128"}],"name":"push","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"funder","type":"address"},{"internalType":"uint256","name":"offset","type":"uint256"}],"name":"seek","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"funder","type":"address"}],"name":"size","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"signer","type":"address"}],"name":"warn","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"what","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"signer","type":"address"},{"internalType":"address payable","name":"target","type":"address"},{"internalType":"bool","name":"autolock","type":"bool"}],"name":"yank","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}] diff --git a/gai-backend/lottery1.abi b/gai-backend/lottery1.abi new file mode 100644 index 000000000..3306342f4 --- /dev/null +++ b/gai-backend/lottery1.abi @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"uint64","name":"day","type":"uint64"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"funder","type":"address"},{"indexed":true,"internalType":"address","name":"signer","type":"address"}],"name":"Create","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"key","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"unlock_warned","type":"uint256"}],"name":"Delete","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"funder","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"}],"name":"Enroll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"key","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"escrow_amount","type":"uint256"}],"name":"Update","type":"event"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"components":[{"internalType":"bytes32","name":"data","type":"bytes32"},{"internalType":"bytes32","name":"reveal","type":"bytes32"},{"internalType":"uint256","name":"packed0","type":"uint256"},{"internalType":"uint256","name":"packed1","type":"uint256"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"internalType":"struct OrchidLottery1.Ticket[]","name":"tickets","type":"tuple[]"},{"internalType":"bytes32[]","name":"refunds","type":"bytes32[]"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"signer","type":"address"},{"internalType":"int256","name":"adjust","type":"int256"},{"internalType":"int256","name":"warn","type":"int256"},{"internalType":"uint256","name":"retrieve","type":"uint256"}],"name":"edit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"signer","type":"address"},{"internalType":"int256","name":"adjust","type":"int256"},{"internalType":"int256","name":"warn","type":"int256"},{"internalType":"uint256","name":"retrieve","type":"uint256"}],"name":"edit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bool","name":"cancel","type":"bool"},{"internalType":"address[]","name":"recipients","type":"address[]"}],"name":"enroll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"funder","type":"address"},{"internalType":"address","name":"recipient","type":"address"}],"name":"enrolled","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"address","name":"signer","type":"address"},{"internalType":"uint64","name":"marked","type":"uint64"}],"name":"mark","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onTokenTransfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"address","name":"funder","type":"address"},{"internalType":"address","name":"signer","type":"address"}],"name":"read","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"count","type":"uint256"},{"internalType":"bytes32","name":"seed","type":"bytes32"}],"name":"save","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"tokenFallback","outputs":[],"stateMutability":"nonpayable","type":"function"}] diff --git a/gai-backend/payment_handler.py b/gai-backend/payment_handler.py new file mode 100644 index 000000000..7d8196387 --- /dev/null +++ b/gai-backend/payment_handler.py @@ -0,0 +1,74 @@ +import web3 +from decimal import Decimal +import random +import ethereum +from typing import Tuple, Optional +import json +import sys +import traceback +import logging + +from ticket import Ticket +from lottery import Lottery + +logger = logging.getLogger(__name__) + +wei = pow(10, 18) + +class PaymentError(Exception): + """Base class for payment processing errors""" + pass + +class PaymentHandler: + def __init__(self, lottery_address: str, recipient_key: str, rpc_url: str = 'https://rpc.gnosischain.com/'): + self.lottery_address = lottery_address + self.recipient_key = recipient_key + self.w3 = web3.Web3(web3.Web3.HTTPProvider(rpc_url)) + self.recipient_addr = web3.Account.from_key(recipient_key).address + self.lottery = Lottery(self.w3) + + def new_reveal(self) -> Tuple[str, str]: + num = hex(random.randrange(pow(2,256)))[2:] + reveal = '0x' + num[2:].zfill(64) + try: + commit = ethereum.utils.sha3(bytes.fromhex(reveal[2:])).hex() + return reveal, commit + except Exception as e: + logger.error(f"Failed to generate reveal/commit pair: {e}") + raise PaymentError("Failed to generate payment credentials") + + def create_invoice(self, amount: float, commit: str) -> str: + return json.dumps({ + 'type': 'invoice', + 'amount': int(wei * amount), + 'commit': '0x' + str(commit), + 'recipient': self.recipient_addr + }) + + async def process_ticket(self, ticket_data: str, reveal: str, commit: str) -> Tuple[float, str, str]: + try: + ticket = Ticket.deserialize( + ticket_data, + reveal=reveal, + commitment=commit, + recipient=self.recipient_addr, + lottery_addr=self.lottery_address + ) + + if ticket.is_winner(): + logger.info( + f"Winner found! Face value: {ticket.face_value() / wei}, " + "Adding to claim queue (stubbed)" + ) + + new_reveal, new_commit = self.new_reveal() + return ticket.face_value() / wei, new_reveal, new_commit + + except Exception as e: + logger.error("Failed to process ticket") + logger.error(traceback.format_exc()) + raise PaymentError(f"Ticket processing failed: {e}") + + async def queue_claim(self, ticket: Ticket): + logger.info(f"Queued ticket claim for {ticket.face_value() / wei} tokens (stubbed)") + pass diff --git a/gai-backend/server.py b/gai-backend/server.py index c73633e3e..777a94cff 100644 --- a/gai-backend/server.py +++ b/gai-backend/server.py @@ -4,163 +4,252 @@ import json import hashlib import random - -import web3 -import ethereum - -import billing -import jobs -import ticket -import lottery +from redis.asyncio import Redis +import redis +from decimal import Decimal +import uuid +import time import os -import traceback import sys +import traceback +from typing import Optional, Dict -uint256 = pow(2,256) - 1 -uint64 = pow(2,64) - 1 -wei = pow(10, 18) - -prices = { - 'invoice': 0.0001, - 'payment': 0.0001, - 'connection': 0.0001, - 'error': 0.0001, - 'job': 0.01, - 'complete': 0.001, - 'started': 0.0001 -} - -lottery_address = '0x6dB8381b2B41b74E17F5D4eB82E8d5b04ddA0a82' +import billing +from config_manager import ConfigManager, ConfigError +from payment_handler import PaymentHandler, PaymentError -internal_messages = ['charge'] +# Configuration +LOTTERY_ADDRESS = '0x6dB8381b2B41b74E17F5D4eB82E8d5b04ddA0a82' disconnect_threshold = -25 -def invoice(amt, commit, recipient): - return json.dumps({'type': 'invoice', 'amount': int(pow(10,18) * amt), 'commit': '0x' + str(commit), 'recipient': recipient}) - -def process_tickets(tix, recip, reveal, commit, lotto, key): - try: -# print(f'Got ticket: {tix[0]}') - tk = ticket.Ticket.deserialize_ticket(tix[0], reveal, commit, recip, lotaddr=lottery_address) -# tk.print_ticket() - if tk.is_winner(reveal): - hash = lotto.claim_ticket(tk, recip, key, reveal) - print(f"Claim tx: {hash}") - reveal, commit = new_reveal() - tk.print_ticket() - return tk.value() / wei, reveal, commit - except Exception: - print('process_ticket() failed') - exc_type, exc_value, exc_traceback = sys.exc_info() - traceback.print_exception(exc_type, exc_value, exc_traceback, limit=20, file=sys.stdout) - return 0, reveal, commit - async def send_error(ws, code): await ws.send(json.dumps({'type': 'error', 'code': code})) -def new_reveal(): - num = hex(random.randrange(pow(2,256)))[2:] - reveal = '0x' + num[2:].zfill(64) -# print(f'new_reveal: {reveal}') - try: - commit = ethereum.utils.sha3(bytes.fromhex(reveal[2:])).hex() - except: - exc_type, exc_value, exc_traceback = sys.exc_info() - traceback.print_exception(exc_type, exc_value, exc_traceback, limit=20, file=sys.stdout) - return reveal, commit +class BalanceMonitor: + def __init__(self, redis: Redis, bills: billing.StrictRedisBilling): + self.redis = redis + self.bills = bills + self._monitors = {} + self.pubsub = self.redis.pubsub() + + def _get_channel(self, client_id: str) -> str: + return f"billing:balance:updates:{client_id}" + + async def start_monitoring(self, client_id: str, websocket, payment_handler: PaymentHandler, commit: str): + if client_id in self._monitors: + await self.stop_monitoring(client_id) + + channel = self._get_channel(client_id) + await self.pubsub.subscribe(channel) + + self._monitors[client_id] = asyncio.create_task( + self._monitor_balance(client_id, channel, websocket, payment_handler, commit) + ) + + async def stop_monitoring(self, client_id: str): + if client_id in self._monitors: + channel = self._get_channel(client_id) + await self.pubsub.unsubscribe(channel) + self._monitors[client_id].cancel() + try: + await self._monitors[client_id] + except asyncio.CancelledError: + pass + del self._monitors[client_id] + + async def _monitor_balance(self, client_id: str, channel: str, websocket, payment_handler: PaymentHandler, commit: str): + try: + last_invoice_time = 0 + MIN_INVOICE_INTERVAL = 1.0 # Minimum seconds between invoices + + while True: + message = await self.pubsub.get_message(ignore_subscribe_messages=True) + if message is None: + await asyncio.sleep(0.01) + continue + + try: + # Wait for in-flight payments to process + await asyncio.sleep(0.1) + + current_time = time.time() + if current_time - last_invoice_time < MIN_INVOICE_INTERVAL: + continue + + balance = await self.bills.balance(client_id) + min_balance = await self.bills.min_balance() + + if balance < min_balance: + await self.bills.debit(client_id, type='invoice') + invoice_amount = 2 * min_balance - balance + await websocket.send( + payment_handler.create_invoice(invoice_amount, commit) + ) + last_invoice_time = current_time + + except Exception as e: + print(f"Error processing balance update for {client_id}: {e}") + + except asyncio.CancelledError: + pass + except Exception as e: + print(f"Balance monitor error for {client_id}: {e}") -async def session(websocket, bills=None, job=None, recipient='0x0', key=''): +async def session( + websocket, + bills=None, + payment_handler=None, + config_manager=None +): print("New client connection") - reserve_price = 0.00006 - lotto = lottery.Lottery() - w3 = web3.Web3(web3.Web3.HTTPProvider('https://rpc.gnosischain.com/')) - lotto.init_contract(w3) - id = websocket.id - bills.debit(id, type='invoice') - send_queue, recv_queue = job.get_queues(id) - reveal, commit = new_reveal() - await websocket.send(invoice(2 * bills.min_balance(), commit, recipient)) - sources = [websocket.recv, recv_queue.get] - tasks = [None, None] - while True: - if bills.balance(id) < disconnect_threshold: - await websocket.close(reason='Balance too low') - break + try: + id = websocket.id + balance_monitor = BalanceMonitor(bills.redis, bills) + inference_url = None + + if config_manager: + config = await config_manager.load_config() + inference_url = config.get('inference', {}).get('api_url') + if not inference_url: + print("No inference URL configured") + await websocket.close(reason='Configuration error') + return + + await bills.debit(id, type='invoice') + reveal, commit = payment_handler.new_reveal() + await websocket.send( + payment_handler.create_invoice(2 * await bills.min_balance(), commit) + ) + + await balance_monitor.start_monitoring(id, websocket, payment_handler, commit) + try: - for i in range(2): - if tasks[i] is None: - tasks[i] = asyncio.create_task(sources[i]()) - done, pending = await asyncio.wait(tasks, return_when = asyncio.FIRST_COMPLETED) - for i, task in enumerate(tasks): - if task in done: - tasks[i] = None - for task in done: - message_ = task.result() - message = json.loads(message_) - if message['type'] == 'payment': + while True: + message = await websocket.recv() + try: + msg = json.loads(message) + except json.JSONDecodeError: + print(f"Failed to parse message: {message}") + continue + + if msg['type'] == 'request_token': try: - amt, reveal, commit = process_tickets(message['tickets'], recipient, reveal, commit, lotto, key) - print(f'Got ticket worth {amt}') - bills.credit(id, amount=amt) - except: - print('outer failure in processing payment') - exc_type, exc_value, exc_traceback = sys.exc_info() - traceback.print_tb(exc_traceback, limit=1, file=sys.stdout) - bills.debit(id, type='error') - await send_error(websocket, -6001) - if bills.balance(id) < bills.min_balance(): - bills.debit(id, type='invoice') - await websocket.send(invoice(2 * bills.min_balance() - bills.balance(id), commit, recipient)) - if message['type'] not in internal_messages: - bills.debit(id, type=message['type']) - if message['type'] == 'job': - jid = hashlib.sha256(bytes(message['prompt'], 'utf-8')).hexdigest() - if reserve_price != 0 and float(message['bid']) < reserve_price: - await websocket.send(json.dumps({'type': 'bid_low'})) - continue - await job.add_job(id, message['bid'], - {'id': jid, 'prompt': message['prompt']}) - if message['type'] == 'charge': + await bills.debit(id, type='auth_token') + print(f"Using inference URL: {inference_url}") + await websocket.send(json.dumps({ + 'type': 'auth_token', + 'session_id': str(id), + 'inference_url': inference_url + })) + except billing.BillingError as e: + print(f"Auth token billing failed: {e}") + await send_error(websocket, -6002) + continue + except Exception as e: + print(f"Auth token error: {e}") + await send_error(websocket, -6002) + continue + + elif msg['type'] == 'payment': try: - bills.debit(id, amount=message['amount']) - await send_queue.put(True) - except: - print('exception in charge handler') - if message['type'] == 'complete': - await websocket.send(json.dumps({'type': 'job_complete', "output": message['response'], - 'model': message['model'], 'reason': message['reason'], - 'usage': message['usage']})) - if message['type'] == 'started': - await websocket.send(json.dumps({'type': 'job_started'})) - except (websockets.exceptions.ConnectionClosedOK, websockets.exceptions.ConnectionClosedError): - print('connection closed') - break - + amount, reveal, commit = await payment_handler.process_ticket( + msg['tickets'][0], reveal, commit + ) + print(f'Got ticket worth {amount}') + await bills.credit(id, amount=amount) + except PaymentError as e: + print(f'Payment processing failed: {e}') + await bills.debit(id, type='error') + await send_error(websocket, -6001) + continue + except Exception as e: + print(f'Unexpected payment error: {e}') + await bills.debit(id, type='error') + await send_error(websocket, -6001) + continue + + except websockets.exceptions.ConnectionClosed: + print('Connection closed normally') + except Exception as e: + print(f"Error processing message: {e}") + await websocket.close(reason='Internal server error') + finally: + await balance_monitor.stop_monitoring(id) + + except Exception as e: + print(f"Fatal error in session: {e}") + await websocket.close(reason='Internal server error') + +async def main(bind_addr, bind_port, recipient_key, redis_url, config_path: Optional[str] = None): + redis = Redis.from_url(redis_url, decode_responses=True) + + try: + config_manager = ConfigManager(redis) + config = await config_manager.load_config(config_path) + except ConfigError as e: + print(f"Configuration error: {e}") + return + except Exception as e: + print(f"Unexpected error loading config: {e}") + return + + try: + bills = billing.StrictRedisBilling(redis) + await bills.init() + except billing.BillingError as e: + print(f"Billing initialization error: {e}") + return + except Exception as e: + print(f"Unexpected error initializing billing: {e}") + return + + payment_handler = PaymentHandler(LOTTERY_ADDRESS, recipient_key) -async def main(model, url, bind_addr, bind_port, recipient_key, llmkey, llmparams, api): - recipient_addr = web3.Account.from_key(recipient_key).address - bills = billing.Billing(prices) - job = jobs.jobs(model, url, llmkey, llmparams, api) print("\n*****") print(f"* Server starting up at {bind_addr} {bind_port}") - print(f"* Connecting to back end at {url}") - print(f"* With model {model}") - print(f"* Using wallet at {recipient_addr}") + print(f"* Using wallet at {payment_handler.recipient_addr}") + print(f"* Connected to Redis at {redis_url}") print("******\n\n") - async with websockets.serve(functools.partial(session, bills=bills, job=job, - recipient=recipient_addr, key=recipient_key), - bind_addr, bind_port): - await asyncio.wait([asyncio.create_task(job.process_jobs())]) + + async with websockets.serve( + functools.partial( + session, + bills=bills, + payment_handler=payment_handler, + config_manager=config_manager + ), + bind_addr, + bind_port + ): + await asyncio.Future() # Run forever if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser(description='Start the billing server') + parser.add_argument('--config', type=str, help='Path to config file (optional)') + + args = parser.parse_args() + + required_env = { + 'ORCHID_GENAI_ADDR': "Bind address", + 'ORCHID_GENAI_PORT': "Bind port", + 'ORCHID_GENAI_RECIPIENT_KEY': "Recipient key", + 'ORCHID_GENAI_REDIS_URL': "Redis connection URL", + } + + # Check required environment variables + missing = [name for name in required_env if name not in os.environ] + if missing: + print("Missing required environment variables:") + for name in missing: + print(f" {name}: {required_env[name]}") + sys.exit(1) + bind_addr = os.environ['ORCHID_GENAI_ADDR'] bind_port = os.environ['ORCHID_GENAI_PORT'] recipient_key = os.environ['ORCHID_GENAI_RECIPIENT_KEY'] - url = os.environ['ORCHID_GENAI_LLM_URL'] - model = os.environ['ORCHID_GENAI_LLM_MODEL'] - api = 'openai' if 'ORCHID_GENAI_API_TYPE' not in os.environ else os.environ['ORCHID_GENAI_API_TYPE'] - llmkey = None if 'ORCHID_GENAI_LLM_AUTH_KEY' not in os.environ else os.environ['ORCHID_GENAI_LLM_AUTH_KEY'] - llmparams = {} - if 'ORCHID_GENAI_LLM_PARAMS' in os.environ: - llmparams = json.loads(os.environ['ORCHID_GENAI_LLM_PARAMS']) - asyncio.run(main(model, url, bind_addr, bind_port, recipient_key, llmkey, llmparams, api)) + redis_url = os.environ['ORCHID_GENAI_REDIS_URL'] + + asyncio.run(main(bind_addr, bind_port, recipient_key, redis_url, args.config)) + diff --git a/gai-backend/test.py b/gai-backend/test.py new file mode 100644 index 000000000..469f75084 --- /dev/null +++ b/gai-backend/test.py @@ -0,0 +1,307 @@ +import asyncio +import json +import logging +import os +import secrets +from dataclasses import dataclass +from typing import Dict, Optional, Tuple, List +from web3 import Web3 +import websockets +import aiohttp +import time + +from account import OrchidAccount +from lottery import Lottery +from ticket import Ticket + +@dataclass +class InferenceConfig: + provider: str + funder: str + secret: str + chainid: int + currency: str + rpc: str + +@dataclass +class ProviderLocation: + billing_url: str + +@dataclass +class LocationConfig: + providers: Dict[str, ProviderLocation] + + @classmethod + def from_dict(cls, data: Dict) -> 'LocationConfig': + providers = { + k: ProviderLocation(**v) for k, v in data.items() + } + return cls(providers=providers) + +@dataclass +class Message: + role: str + content: str + name: Optional[str] = None + +@dataclass +class TestConfig: + messages: List[Message] + model: str + params: Dict + retry_delay: float = 1.5 + + @classmethod + def from_dict(cls, data: Dict) -> 'TestConfig': + messages = [] + if 'prompt' in data: + # Handle legacy config with single prompt + messages = [Message(role="user", content=data['prompt'])] + elif 'messages' in data: + messages = [Message(**msg) for msg in data['messages']] + else: + raise ValueError("Config must contain either 'prompt' or 'messages'") + + return cls( + messages=messages, + model=data['model'], + params=data.get('params', {}), + retry_delay=data.get('retry_delay', 1.5) + ) + +@dataclass +class LoggingConfig: + level: str + file: Optional[str] + +@dataclass +class ClientConfig: + inference: InferenceConfig + location: LocationConfig + test: TestConfig + logging: LoggingConfig + + @classmethod + def from_file(cls, config_path: str) -> 'ClientConfig': + with open(config_path) as f: + data = json.load(f) + return cls( + inference=InferenceConfig(**data['inference']), + location=LocationConfig.from_dict(data['location']), + test=TestConfig.from_dict(data['test']), + logging=LoggingConfig(**data['logging']) + ) + +class OrchidLLMTestClient: + def __init__(self, config_path: str, prompt: Optional[str] = None): + self.config = ClientConfig.from_file(config_path) + if prompt: + self.config.test.messages = [Message(role="user", content=prompt)] + + self._setup_logging() + self.logger = logging.getLogger(__name__) + + self.web3 = Web3(Web3.HTTPProvider(self.config.inference.rpc)) + self.lottery = Lottery( + self.web3, + chain_id=self.config.inference.chainid + ) + self.account = OrchidAccount( + self.lottery, + self.config.inference.funder, + self.config.inference.secret + ) + + self.ws = None + self.session_id = None + self.inference_url = None + self.message_queue = asyncio.Queue() + self._handler_task = None + + def _setup_logging(self): + logging.basicConfig( + level=getattr(logging, self.config.logging.level.upper()), + filename=self.config.logging.file, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' + ) + + async def _handle_invoice(self, invoice_data: Dict) -> None: + try: + amount = int(invoice_data['amount']) + recipient = invoice_data['recipient'] + commit = invoice_data['commit'] + + ticket_str = self.account.create_ticket( + amount=amount, + recipient=recipient, + commitment=commit + ) + + await self.ws.send(json.dumps({ + 'type': 'payment', + 'tickets': [ticket_str] + })) + self.logger.info(f"Sent payment ticket for {amount/1e18} tokens") + + except Exception as e: + self.logger.error(f"Failed to handle invoice: {e}") + raise + + async def _billing_handler(self) -> None: + try: + async for message in self.ws: + msg = json.loads(message) + self.logger.debug(f"Received WS message: {msg['type']}") + + if msg['type'] == 'invoice': + await self._handle_invoice(msg) + elif msg['type'] == 'auth_token': + self.session_id = msg['session_id'] + self.inference_url = msg['inference_url'] + await self.message_queue.put(('auth_received', self.session_id)) + elif msg['type'] == 'error': + await self.message_queue.put(('error', msg['code'])) + + except websockets.exceptions.ConnectionClosed: + self.logger.info("Billing WebSocket closed") + except Exception as e: + self.logger.error(f"Billing handler error: {e}") + await self.message_queue.put(('error', str(e))) + + async def connect(self) -> None: + try: + provider = self.config.inference.provider + provider_config = self.config.location.providers.get(provider) + if not provider_config: + raise Exception(f"No configuration found for provider: {provider}") + + self.logger.info(f"Connecting to provider {provider} at {provider_config.billing_url}") + self.ws = await websockets.connect(provider_config.billing_url) + + self._handler_task = asyncio.create_task(self._billing_handler()) + + await self.ws.send(json.dumps({ + 'type': 'request_token', + 'orchid_account': self.config.inference.funder + })) + + msg_type, session_id = await self.message_queue.get() + if msg_type != 'auth_received': + raise Exception(f"Authentication failed: {session_id}") + + self.logger.info("Successfully authenticated") + + except Exception as e: + self.logger.error(f"Connection failed: {e}") + raise + + async def send_inference_request(self, retry_count: int = 0) -> Dict: + if not self.session_id: + raise Exception("Not authenticated") + + if not self.inference_url: + raise Exception("No inference URL received") + + try: + async with aiohttp.ClientSession() as session: + self.logger.debug(f"Using session ID: {self.session_id}") + headers = { + 'Authorization': f'Bearer {self.session_id}', + 'Content-Type': 'application/json' + } + + data = { + 'messages': [ + { + 'role': msg.role, + 'content': msg.content, + **(({'name': msg.name} if msg.name else {})) + } + for msg in self.config.test.messages + ], + 'model': self.config.test.model, + 'params': self.config.test.params + } + + self.logger.info(f"Sending inference request (attempt {retry_count + 1})") + self.logger.debug(f"Request URL: {self.inference_url}") + self.logger.debug(f"Request data: {data}") + + async with session.post( + self.inference_url, + headers=headers, + json=data, + timeout=30 + ) as response: + if response.status == 402: + retry_delay = self.config.test.retry_delay + self.logger.info(f"Insufficient balance, waiting {retry_delay}s for payment processing...") + await asyncio.sleep(retry_delay) + return await self.send_inference_request(retry_count + 1) + elif response.status == 401: + error_text = await response.text() + self.logger.error(f"Authentication failed: {error_text}") + raise Exception(f"Authentication failed: {error_text}") + elif response.status != 200: + error_text = await response.text() + self.logger.error(f"Inference request failed (status {response.status}): {error_text}") + raise Exception(f"Inference request failed: {error_text}") + + result = await response.json() + self.logger.info(f"Inference complete: {result['usage']} tokens used") + return result + + except Exception as e: + self.logger.error(f"Inference request failed: {e}") + raise + + async def close(self) -> None: + if self.ws: + try: + await self.ws.close() + self.logger.info("Connection closed") + except Exception as e: + self.logger.error(f"Error closing connection: {e}") + + if self._handler_task: + self._handler_task.cancel() + try: + await self._handler_task + except asyncio.CancelledError: + pass + + async def __aenter__(self): + """Async context manager support""" + return self + + async def __aexit__(self, exc_type, exc_val, exc_tb): + """Ensure cleanup on context exit""" + await self.close() + +async def main(config_path: str, prompt: Optional[str] = None): + async with OrchidLLMTestClient(config_path, prompt) as client: + try: + await client.connect() + result = await client.send_inference_request() + + print("\nInference Results:") + messages = client.config.test.messages + print(f"Messages:") + for msg in messages: + print(f" {msg.role}: {msg.content}") + print(f"Response: {result['response']}") + print(f"Usage: {json.dumps(result['usage'], indent=2)}") + + except Exception as e: + print(f"Test failed: {e}") + raise + +if __name__ == "__main__": + import argparse + parser = argparse.ArgumentParser() + parser.add_argument("config", help="Path to config file") + parser.add_argument("prompt", nargs="*", help="Optional prompt to override config") + args = parser.parse_args() + + prompt = " ".join(args.prompt) if args.prompt else None + asyncio.run(main(args.config, prompt)) diff --git a/gai-backend/ticket.py b/gai-backend/ticket.py index f63c500ce..33cb98040 100644 --- a/gai-backend/ticket.py +++ b/gai-backend/ticket.py @@ -1,124 +1,103 @@ -import datetime -import web3 -from eth_abi.packed import encode_packed -import eth_account -import ethereum - -uint64 = pow(2, 64) - 1 # 18446744073709551615 -uint128 = pow(2, 128) - 1 # 340282366920938463463374607431768211455 -addrtype = pow(2, 20 * 8) - 1 - - -class Ticket: - def __init__(self, web3_provider, data, lot_addr, token_addr, amount, ratio, funder, recipient, commitment, key): - - self.web3 = web3_provider - self.data = data - self.lot_addr = lot_addr - self.token_addr = token_addr - self.amount = amount - self.ratio = ratio - self.commitment = commitment - self.funder = funder - self.recipient = recipient - self.key = key - - # Check if we have all the variables - if all(v is not None for v in [web3_provider, recipient, commitment, ratio, funder, amount, lot_addr, token_addr, key]): - issued = int(datetime.datetime.now().timestamp()) - l2nonce = int(web3.Web3.keccak(text=(f'{datetime.datetime.now()}')).hex(), base=16) & (pow(2, 64) - 1) - expire = pow(2, 31) - 1 - packed0 = issued << 192 | l2nonce << 128 | amount - packed1 = expire << 224 | ratio << 160 | int(funder, base=16) - - digest = web3.Web3.solidity_keccak( - ['bytes1', 'bytes1', 'address', 'bytes32', 'address', 'address', - 'bytes32', 'uint256', 'uint256', 'bytes32'], - [b'\x19', b'\x00', - self.lot_addr, b'\x00' * 31 + b'\x64', - self.token_addr, recipient, - web3.Web3.solidity_keccak(['bytes32'], [self.commitment]), packed0, - packed1, self.data]) - - sig = self.web3.eth.account.signHash(digest, private_key=key.hex()) - packed1 = packed1 << 1 | ((sig.v - 27) & 1) - - self.packed0 = packed0 - self.packed1 = packed1 - self.sig_r = Ticket.to_32byte_hex(sig.r) - self.sig_s = Ticket.to_32byte_hex(sig.s) - self.sig_v = (sig.v - 27) & 1 - - def digest(self, packed0 = None, packed1 = None): - _packed0 = self.packed0 if packed0 is None else packed0 - _packed1 = self.packed1 if packed1 is None else packed1 - _packed1 = _packed1 >> 1 - types = ['bytes1', 'bytes1', 'address', 'bytes32', 'address', 'address', - 'bytes32', 'uint256', 'uint256', 'bytes32'] - vals = [b'\x19', b'\x00', - self.lot_addr, b'\x00' * 31 + b'\x64', - self.token_addr, self.recipient, - bytes.fromhex(self.commitment[2:]), _packed0, - _packed1, self.data] - packed = encode_packed(types, vals) - return ethereum.utils.sha3(packed) - - - @staticmethod - def to_32byte_hex(val): - return web3.Web3.to_hex(web3.Web3.to_bytes(hexstr=val).rjust(32, b'\0')) - - def serialize_ticket(self): - return Ticket.to_32byte_hex(self.packed0)[2:] + Ticket.to_32byte_hex(self.packed1)[2:] + self.sig_r[2:] + self.sig_s[2:] - - @staticmethod - def deserialize_ticket(tstr, reveal = None, commitment = None, recipient = None, - lotaddr = '0x6dB8381b2B41b74E17F5D4eB82E8d5b04ddA0a82', - tokenaddr = '0x0000000000000000000000000000000000000000'): - tk = [tstr[i:i+64] for i in range(0, len(tstr), 64)] - print(tk) - tk_temp = Ticket(None, None, None, None, None, None, None, None, None, None) - tk_temp.packed0 = int(tk[0], base=16) - tk_temp.packed1 = int(tk[1], base=16) - tk_temp.amount = tk_temp.packed0 & uint128 - tk_temp.ratio = (tk_temp.packed1 >> 161) & uint64 - tk_temp.sig_r = tk[2] - tk_temp.sig_s = tk[3] - tk_temp.sig_v = tk_temp.packed1 & 1 - tk_temp.data = b'\x00' * 32 - tk_temp.reveal = Ticket.to_32byte_hex(reveal) - tk_temp.commitment = Ticket.to_32byte_hex(commitment) - tk_temp.lot_addr = lotaddr - tk_temp.token_addr = tokenaddr - tk_temp.recipient = recipient - digest = tk_temp.digest() - signer = ethereum.utils.checksum_encode(ethereum.utils.sha3(ethereum.utils.ecrecover_to_pub(digest, - tk_temp.sig_v, - bytes.fromhex(tk_temp.sig_r[2:]), - bytes.fromhex(tk_temp.sig_s[2:]) - ))[-20:]) - return tk_temp - - def is_winner(self, reveal): - ratio = uint64 & (self.packed1 >> 161) - issued_nonce = (self.packed0 >> 128) - hash = ethereum.utils.sha3(bytes.fromhex(reveal[2:]) + - issued_nonce.to_bytes(length=16, byteorder='big')) - comp = uint64 & int(hash.hex(), base=16) - if ratio < comp: - return False - return True - - def value(self): - return self.amount * self.ratio / uint64 - - def print_ticket(self): - amount = self.packed0 & uint128 - nonce = (self.packed0 >> 128) & uint64 - funder = addrtype & (self.packed1 >> 1) - ratio = uint64 & (self.packed1 >> 161) - print('Print_ticket():') - print(f'Face Value: {amount}') - print(f'Funder: {funder}') - print(f'Ratio: {ratio}') - +import datetime +from web3 import Web3 +from typing import Optional, Tuple + +class TicketError(Exception): + pass + +class Ticket: + def __init__(self, + packed0: int, + packed1: int, + sig_r: str, + sig_s: str, + reveal: Optional[str] = None, + commitment: Optional[str] = None, + recipient: Optional[str] = None, + lottery_addr: Optional[str] = None, + token_addr: str = "0x0000000000000000000000000000000000000000"): + self.packed0 = packed0 + self.packed1 = packed1 + self.sig_r = sig_r + self.sig_s = sig_s + self.sig_v = packed1 & 1 + self.reveal = reveal + self.commitment = commitment + self.recipient = recipient + self.lottery_addr = lottery_addr + self.token_addr = token_addr + self.data = b'\x00' * 32 # Fixed empty data field + + @classmethod + def deserialize(cls, + ticket_str: str, + reveal: Optional[str] = None, + commitment: Optional[str] = None, + recipient: Optional[str] = None, + lottery_addr: Optional[str] = None, + token_addr: str = "0x0000000000000000000000000000000000000000" + ) -> 'Ticket': + try: + if len(ticket_str) != 256: # 4 x 64 hex chars + raise TicketError("Invalid ticket format") + + parts = [ticket_str[i:i+64] for i in range(0, 256, 64)] + return cls( + packed0=int(parts[0], 16), + packed1=int(parts[1], 16), + sig_r=parts[2], + sig_s=parts[3], + reveal=reveal, + commitment=commitment, + recipient=recipient, + lottery_addr=lottery_addr, + token_addr=token_addr + ) + except Exception as e: + raise TicketError(f"Failed to deserialize ticket: {e}") + + def is_winner(self) -> bool: + if not self.reveal: + raise TicketError("No reveal value available") + + try: + ratio = (self.packed1 >> 161) & ((1 << 64) - 1) + issued_nonce = (self.packed0 >> 128) + hash_val = Web3.keccak( + Web3.to_bytes(hexstr=self.reveal[2:]) + + issued_nonce.to_bytes(length=16, byteorder='big') + ) + comp = ((1 << 64) - 1) & int(hash_val.hex(), 16) + return ratio >= comp + except Exception as e: + raise TicketError(f"Failed to check winning status: {e}") + + def face_value(self) -> int: + return self.packed0 & ((1 << 128) - 1) + + def verify_signature(self, expected_signer: str) -> bool: + if not all([self.commitment, self.recipient, self.lottery_addr]): + raise TicketError("Missing required fields for signature verification") + + try: + digest = Web3.solidity_keccak( + ['bytes1', 'bytes1', 'address', 'bytes32', 'address', 'address', + 'bytes32', 'uint256', 'uint256', 'bytes32'], + [b'\x19', b'\x00', + self.lottery_addr, + b'\x00' * 31 + b'\x64', + self.token_addr, + self.recipient, + Web3.solidity_keccak(['bytes32'], [self.commitment]), + self.packed0, + self.packed1 >> 1, + self.data] + ) + + recovered = Web3.eth.account.recover_message( + eth_message_hash=digest, + vrs=(self.sig_v + 27, int(self.sig_r, 16), int(self.sig_s, 16)) + ) + return recovered.lower() == expected_signer.lower() + except Exception as e: + raise TicketError(f"Failed to verify signature: {e}") From 097fc9a21e71dbc6b93b86f024aac5518fcc5f55 Mon Sep 17 00:00:00 2001 From: Dan Montgomery Date: Fri, 8 Nov 2024 16:40:54 -0500 Subject: [PATCH 5/5] Front end now uses new inference endpoint. Add multi-model mode. --- gai-frontend/lib/chat/chat.dart | 399 +++++++++++++----- gai-frontend/lib/chat/chat_bubble.dart | 171 +++++--- gai-frontend/lib/chat/chat_button.dart | 31 ++ gai-frontend/lib/chat/chat_message.dart | 51 ++- gai-frontend/lib/chat/chat_model_button.dart | 239 +++++------ gai-frontend/lib/chat/chat_prompt.dart | 30 +- .../lib/chat/chat_settings_button.dart | 196 +++++---- gai-frontend/lib/chat/inference_client.dart | 223 ++++++++++ gai-frontend/lib/chat/models.dart | 94 +++++ .../lib/chat/provider_connection.dart | 301 +++++++++++++ gai-frontend/pubspec.lock | 98 +++-- gai-frontend/pubspec.yaml | 6 +- 12 files changed, 1416 insertions(+), 423 deletions(-) create mode 100644 gai-frontend/lib/chat/inference_client.dart create mode 100644 gai-frontend/lib/chat/models.dart create mode 100644 gai-frontend/lib/chat/provider_connection.dart diff --git a/gai-frontend/lib/chat/chat.dart b/gai-frontend/lib/chat/chat.dart index ce37beba3..30217aa34 100644 --- a/gai-frontend/lib/chat/chat.dart +++ b/gai-frontend/lib/chat/chat.dart @@ -21,9 +21,10 @@ import 'package:url_launcher/url_launcher.dart'; import 'chat_bubble.dart'; import 'chat_button.dart'; import 'chat_message.dart'; -import '../provider.dart'; import 'chat_prompt.dart'; import 'chat_model_button.dart'; +import 'models.dart'; +import 'provider_connection.dart'; import '../config/providers_config.dart'; class ChatView extends StatefulWidget { @@ -35,14 +36,13 @@ class ChatView extends StatefulWidget { class _ChatViewState extends State { List _messages = []; -// List _providers = []; -// Map> _providers = {'gpt4': {'url': 'https://nanogenera.danopato.com/ws/', 'name': 'ChatGPT-4'}}; late final Map> _providers; int _providerIndex = 0; bool _debugMode = false; + bool _multiSelectMode = false; bool _connected = false; - double _bid = 0.00007; ProviderConnection? _providerConnection; + int? _maxTokens; // The active account components EthereumAddress? _funder; @@ -52,17 +52,20 @@ class _ChatViewState extends State { final _funderFieldController = AddressValueFieldController(); final ScrollController messageListController = ScrollController(); final _promptTextController = TextEditingController(); - final _bidController = NumericValueFieldController(); + final _maxTokensController = NumericValueFieldController(); bool _showPromptDetails = false; Chain _selectedChain = Chains.Gnosis; + final ModelsState _modelsState = ModelsState(); + List _selectedModelIds = []; @override void initState() { super.initState(); _providers = ProvidersConfig.getProviders(); - _bidController.value = _bid; try { _initFromParams(); + // If we have providers and an account, connect to first provider + _connectToInitialProvider(); } catch (e, stack) { log('Error initializing from params: $e, $stack'); } @@ -75,6 +78,30 @@ class _ChatViewState extends State { return true; } + void _setMaxTokens(int? value) { + setState(() { + _maxTokens = value; + }); + } + + void _connectToInitialProvider() { + if (_providers.isEmpty) { + log('No providers configured'); + return; + } + + // Get first provider from the list + final firstProviderId = _providers.keys.first; + final firstProvider = _providers[firstProviderId]; + if (firstProvider == null) { + log('Invalid provider configuration'); + return; + } + + log('Connecting to initial provider: ${firstProvider['name']}'); + _connectProvider(firstProviderId); + } + Account? get _account { if (_funder == null || _signerKey == null) { return null; @@ -98,7 +125,16 @@ class _ChatViewState extends State { _accountDetail = null; if (_account != null) { _accountDetail = AccountDetailPoller(account: _account!); - _accountDetail?.pollOnce(); + await _accountDetail?.pollOnce(); + + // Disconnect any existing provider connection + if (_connected) { + _providerConnection?.dispose(); + _connected = false; + } + + // Connect to provider with new account + _connectToInitialProvider(); } _accountDetailNotifier.notifyListeners(); setState(() {}); @@ -127,91 +163,165 @@ class _ChatViewState extends State { } void providerConnected([name = '']) { - String nameTag = ''; _connected = true; - if (!name.isEmpty) { - nameTag = ' ${name}'; - } - addMessage(ChatMessageSource.system, 'Connected to provider${nameTag}.'); + // Only show connection message in debug mode + addMessage( + ChatMessageSource.internal, + 'Connected to provider${name.isEmpty ? '' : ' $name'}.', + ); } void providerDisconnected() { _connected = false; - addMessage(ChatMessageSource.system, 'Provider disconnected'); + // Only show disconnection in debug mode + addMessage( + ChatMessageSource.internal, + 'Provider disconnected', + ); } - - void _connectProvider([provider = '']) { + +void _connectProvider([String provider = '']) async { var account = _accountDetail; - String url; - String name; - String providerId = ''; if (account == null) { + log('_connectProvider() -- No account'); return; } - if (_providers.length == 0) { - log('_connectProvider() -- _providers.length == 0'); + if (_providers.isEmpty) { + log('_connectProvider() -- _providers.isEmpty'); return; } + + // Clean up existing connection if any if (_connected) { _providerConnection?.dispose(); - _providerIndex = (_providerIndex + 1) % _providers.length; _connected = false; } + + // Determine which provider to connect to + String providerId; if (provider.isEmpty) { - _providerIndex += 1; - providerId = _providers.keys.elementAt(_providerIndex); + providerId = _providers.keys.first; } else { providerId = provider; } - url = _providers[providerId]?['url'] ?? ''; - name = _providers[providerId]?['name'] ?? ''; - - log('Connecting to provider: ${name}'); - _providerConnection = ProviderConnection( - onMessage: (msg) { - addMessage(ChatMessageSource.internal, msg); - }, - onConnect: () { providerConnected(name); }, - onChat: (msg, metadata) { - addMessage(ChatMessageSource.provider, msg, metadata: metadata, sourceName: name); - }, - onDisconnect: providerDisconnected, - onError: (msg) { - addMessage(ChatMessageSource.system, 'Provider error: $msg'); - }, - onSystemMessage: (msg) { - addMessage(ChatMessageSource.system, msg); - }, - onInternalMessage: (msg) { - addMessage(ChatMessageSource.internal, msg); - }, - accountDetail: account, - contract: - EthereumAddress.from('0x6dB8381b2B41b74E17F5D4eB82E8d5b04ddA0a82'), - url: url, - ); - log('connected...'); - } - void addMessage(ChatMessageSource source, String msg, - {Map? metadata, String sourceName = ''}) { + final providerInfo = _providers[providerId]; + if (providerInfo == null) { + log('Provider not found: $providerId'); + return; + } + + final wsUrl = providerInfo['url'] ?? ''; + final name = providerInfo['name'] ?? ''; + final httpUrl = wsUrl.replaceFirst('ws:', 'http:').replaceFirst('wss:', 'https:'); + + log('Connecting to provider: $name (ws: $wsUrl, http: $httpUrl)'); + + try { + _providerConnection = await ProviderConnection.connect( + billingUrl: wsUrl, + inferenceUrl: httpUrl, + contract: EthereumAddress.from('0x6dB8381b2B41b74E17F5D4eB82E8d5b04ddA0a82'), + accountDetail: account, + onMessage: (msg) { + addMessage(ChatMessageSource.internal, msg); + }, + onConnect: () { + providerConnected(name); + }, + onChat: (msg, metadata) { + print('onChat received metadata: $metadata'); // See what metadata we get + final modelId = metadata['model_id']; + print('Found model_id: $modelId'); // Verify we extract model_id + + String? modelName; + if (modelId != null) { + print('Available models: ${_modelsState.allModels.map((m) => '${m.id}: ${m.name}')}'); // See what models we have + final model = _modelsState.allModels.firstWhere( + (m) => m.id == modelId, + orElse: () => ModelInfo( + id: modelId, + name: modelId, + provider: '', + apiType: '', + ), + ); + modelName = model.name; + print('Looked up model name: $modelName'); // See what name we found + } + + print('Adding message with modelId: $modelId, modelName: $modelName'); // Verify what we're passing + addMessage( + ChatMessageSource.provider, + msg, + metadata: metadata, + modelId: modelId, + modelName: modelName, + ); + }, + onDisconnect: providerDisconnected, + onError: (msg) { + addMessage(ChatMessageSource.system, 'Provider error: $msg'); + }, + onSystemMessage: (msg) { + addMessage(ChatMessageSource.system, msg); + }, + onInternalMessage: (msg) { + addMessage(ChatMessageSource.internal, msg); + }, + onAuthToken: (token, url) async { + // Fetch models after receiving token + log('Fetching models after auth token receipt'); + if (_providerConnection?.inferenceClient != null) { + await _modelsState.fetchModelsForProvider( + providerId, + _providerConnection!.inferenceClient!, + ); + } + }, + ); + + // Request auth token - model fetch will happen in callback + await _providerConnection?.requestAuthToken(); + + } catch (e, stack) { + log('Error connecting to provider: $e\n$stack'); + addMessage(ChatMessageSource.system, 'Failed to connect to provider: $e'); + } + } + + void addMessage( + ChatMessageSource source, + String msg, { + Map? metadata, + String sourceName = '', + String? modelId, + String? modelName, + }) { log('Adding message: ${msg.truncate(64)}'); setState(() { - if (sourceName.isEmpty) { - _messages.add(ChatMessage(source, msg, metadata: metadata)); - } else { - _messages.add(ChatMessage(source, msg, metadata: metadata, sourceName: sourceName)); - } + _messages.add(ChatMessage( + source, + msg, + metadata: metadata, + sourceName: sourceName, + modelId: modelId, + modelName: modelName, + )); }); - // if (source != ChatMessageSource.internal || _debugMode == true) { scrollMessagesDown(); - // } } - void _setBid(double? value) { + void _updateSelectedModels(List modelIds) { setState(() { - _bid = value ?? _bid; + if (_multiSelectMode) { + _selectedModelIds = modelIds; + } else { + // In single-select mode, only keep the most recently selected model + _selectedModelIds = modelIds.isNotEmpty ? [modelIds.last] : []; + } }); + log('Selected models updated to: $_selectedModelIds'); } // TODO: Break out widget @@ -317,18 +427,67 @@ class _ChatViewState extends State { _account != null ? _sendPrompt() : _popAccountDialog(); } - void _sendPrompt() { +void _sendPrompt() async { var msg = _promptTextController.text; if (msg.trim().isEmpty) { return; } - var message = '{"type": "job", "bid": $_bid, "prompt": "$msg"}'; - _providerConnection?.sendProviderMessage(message); + + if (_providerConnection == null) { + addMessage(ChatMessageSource.system, 'Not connected to provider'); + return; + } + + if (_selectedModelIds.isEmpty) { + addMessage(ChatMessageSource.system, + _multiSelectMode ? 'Please select at least one model' : 'Please select a model' + ); + return; + } + + // Add user message immediately to update UI and include in history + addMessage(ChatMessageSource.client, msg); _promptTextController.clear(); FocusManager.instance.primaryFocus?.unfocus(); - addMessage(ChatMessageSource.client, msg); - addMessage(ChatMessageSource.internal, 'Client: $message'); - log('Sending message to provider $message'); + + for (final modelId in _selectedModelIds) { + try { + final modelInfo = _modelsState.allModels + .firstWhere((m) => m.id == modelId, + orElse: () => ModelInfo( + id: modelId, + name: modelId, + provider: '', + apiType: '', + )); + + // Get messages relevant to this model + final relevantMessages = _messages.where((m) => + (m.source == ChatMessageSource.provider && m.modelId == modelId) || + m.source == ChatMessageSource.client + ).toList(); + + Map? params; + if (_maxTokens != null) { + params = {'max_tokens': _maxTokens!}; + } + + addMessage( + ChatMessageSource.internal, + 'Querying ${modelInfo.name}...', + modelId: modelId, + modelName: modelInfo.name, + ); + + await _providerConnection?.requestInference( + modelId, + relevantMessages, + params: params, + ); + } catch (e) { + addMessage(ChatMessageSource.system, 'Error querying model $modelId: $e'); + } + } } void scrollMessagesDown() { @@ -389,9 +548,9 @@ class _ChatViewState extends State { fit: BoxFit.scaleDown, child: SizedBox( width: minWidth, - child: _buildHeaderRow(showIcons: showIcons, providers: _providers))) + child: _buildHeaderRow(showIcons: showIcons))) else - _buildHeaderRow(showIcons: showIcons, providers: _providers), + _buildHeaderRow(showIcons: showIcons), // Messages area _buildChatPane(), // Prompt row @@ -399,16 +558,12 @@ class _ChatViewState extends State { alignment: Alignment.topCenter, duration: millis(150), child: ChatPromptPanel( - promptTextController: _promptTextController, - onSubmit: _send, - setBid: _setBid, - bidController: _bidController) - .top(8), + promptTextController: _promptTextController, + onSubmit: _send, + setMaxTokens: _setMaxTokens, + maxTokensController: _maxTokensController, + ).top(8), ), - if (!_showPromptDetails) - Text('Your bid is $_bid XDAI per token.', - style: OrchidText.normal_14) - .top(12), ], ), ).top(8).bottom(8), @@ -486,34 +641,72 @@ class _ChatViewState extends State { ); } - Widget _buildHeaderRow({required bool showIcons, required Map> providers}) { + Widget _buildHeaderRow({required bool showIcons}) { + final buttonHeight = 40.0; + final settingsIconSize = buttonHeight * 1.5; + return Row( children: [ - SizedBox(height: 40, child: OrchidAsset.image.logo), + // Logo + SizedBox(height: buttonHeight, child: OrchidAsset.image.logo), + + // Model selector with loading state + ListenableBuilder( + listenable: _modelsState, + builder: (context, _) { + if (_modelsState.isAnyLoading) { + return SizedBox( + width: buttonHeight, + height: buttonHeight, + child: Center( + child: CircularProgressIndicator( + strokeWidth: 2, + ), + ), + ); + } + + return ModelSelectionButton( + models: _modelsState.allModels, + selectedModelIds: _selectedModelIds, + updateModels: _updateSelectedModels, + multiSelectMode: _multiSelectMode, + ); + }, + ).left(24), + const Spacer(), - // Connect button - ChatModelButton( - updateModel: (id) { log(id); _connectProvider(id); }, - providers: providers, - ).left(8), -/* - ChatButton( - text: 'Reroll', - onPressed: _connectProvider, - ).left(8), -*/ - // Clear button - ChatButton(text: 'Clear Chat', onPressed: _clearChat).left(8), + // Account button - ChatButton(text: 'Account', onPressed: _popAccountDialog).left(8), + OutlinedChatButton( + text: 'Account', + onPressed: _popAccountDialog, + height: buttonHeight, + ).left(8), + // Settings button - ChatSettingsButton( - debugMode: _debugMode, - onDebugModeChanged: () { - setState(() { - _debugMode = !_debugMode; - }); - }, + SizedBox( + width: settingsIconSize, + height: buttonHeight, + child: Center( + child: ChatSettingsButton( + debugMode: _debugMode, + multiSelectMode: _multiSelectMode, + onDebugModeChanged: () { + setState(() { + _debugMode = !_debugMode; + }); + }, + onMultiSelectModeChanged: () { + setState(() { + _multiSelectMode = !_multiSelectMode; + // Reset selections when toggling modes + _selectedModelIds = []; + }); + }, + onClearChat: _clearChat, + ), + ), ).left(8), ], ); diff --git a/gai-frontend/lib/chat/chat_bubble.dart b/gai-frontend/lib/chat/chat_bubble.dart index 26c6653ef..735588b2a 100644 --- a/gai-frontend/lib/chat/chat_bubble.dart +++ b/gai-frontend/lib/chat/chat_bubble.dart @@ -15,19 +15,10 @@ class ChatBubble extends StatelessWidget { Widget build(BuildContext context) { ChatMessageSource src = message.source; - List msgBubbleColor(ChatMessageSource src) { - if (src == ChatMessageSource.client) { - return [ - const Color(0xff52319c), - const Color(0xff3b146a), - ]; - } else { - return [ - const Color(0xff005965), - OrchidColors.dark_ff3a3149, - ]; - } - } + // Constants for consistent spacing + const double iconSize = 16.0; + const double iconSpacing = 8.0; + const double iconTotalWidth = iconSize + iconSpacing; if (src == ChatMessageSource.system || src == ChatMessageSource.internal) { if (!debugMode && src == ChatMessageSource.internal) { @@ -39,9 +30,13 @@ class ChatBubble extends StatelessWidget { children: [ Text( message.message, - style: src == ChatMessageSource.system - ? OrchidText.normal_14 - : OrchidText.normal_14.grey, + style: const TextStyle( + fontFamily: 'Baloo2', + fontSize: 14, // 16px equivalent + height: 1.0, + fontWeight: FontWeight.normal, + color: Colors.white, + ), ), const SizedBox(height: 2), ], @@ -53,68 +48,112 @@ class ChatBubble extends StatelessWidget { alignment: src == ChatMessageSource.provider ? Alignment.centerLeft : Alignment.centerRight, - child: SizedBox( - width: 0.6 * 800, //MediaQuery.of(context).size.width * 0.6, + child: Container( + constraints: BoxConstraints(maxWidth: 0.6 * 800), child: Column( - crossAxisAlignment: CrossAxisAlignment.end, + crossAxisAlignment: src == ChatMessageSource.provider + ? CrossAxisAlignment.start + : CrossAxisAlignment.end, children: [ - Align( - alignment: Alignment.centerLeft, - child: _chatSourceText(message), -// child: Text(src == ChatMessageSource.provider ? 'Chat' : 'You', -// style: OrchidText.normal_14), - ), - const SizedBox(height: 2), - ClipRRect( - borderRadius: BorderRadius.circular(10), - child: Stack( - children: [ - Positioned.fill( - child: Container( - width: 0.6 * 800, - // MediaQuery.of(context).size.width * 0.6, - decoration: BoxDecoration( - gradient: LinearGradient( - colors: msgBubbleColor(message.source), - ), - ), + // Header row with icon and name for both provider and user + Row( + mainAxisAlignment: src == ChatMessageSource.provider + ? MainAxisAlignment.start + : MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + if (src == ChatMessageSource.provider) ...[ + Icon( + Icons.stars_rounded, + color: OrchidColors.blue_highlight, + size: iconSize, + ), + SizedBox(width: iconSpacing), + Text( + message.displayName ?? 'Chat', + style: TextStyle( + fontFamily: 'Baloo2', + fontSize: 14, // 16px equivalent + height: 1.0, + fontWeight: FontWeight.w500, + color: OrchidColors.blue_highlight, ), ), - Container( - width: 0.6 * 800, - // MediaQuery.of(context).size.width * 0.6, - padding: const EdgeInsets.all(8.0), - child: Text(message.message, - style: OrchidText.medium_20_050), + ] else ...[ + Text( + 'You', + style: TextStyle( + fontFamily: 'Baloo2', + fontSize: 14, // 16px equivalent + height: 1.0, + fontWeight: FontWeight.w500, + color: OrchidColors.blue_highlight, + ), + ), + SizedBox(width: iconSpacing), + Icon( + Icons.account_circle_rounded, + color: OrchidColors.blue_highlight, + size: iconSize, ), ], - ), + ], ), - const SizedBox(height: 2), - if (src == ChatMessageSource.provider) ...[ - Text( - style: OrchidText.normal_14, - 'model: ${message.metadata?["model"]} usage: ${message.metadata?["usage"]}', + const SizedBox(height: 4), + // Message content with padding for provider messages + if (src == ChatMessageSource.provider) + Padding( + padding: EdgeInsets.only(left: iconTotalWidth), + child: Text( + message.message, + style: const TextStyle( + fontFamily: 'Baloo2', + fontSize: 20, // 20px design spec + height: 1.0, + fontWeight: FontWeight.normal, + color: Colors.white, + ), + ), ) + else + Container( + padding: const EdgeInsets.symmetric(horizontal: 25, vertical: 8), + decoration: BoxDecoration( + color: Colors.black, + borderRadius: BorderRadius.circular(10), + ), + child: Text( + message.message, + style: const TextStyle( + fontFamily: 'Baloo2', + fontSize: 20, // 20px design spec + height: 1.0, + fontWeight: FontWeight.normal, + color: Colors.white, + ), + ), + ), + // Usage metadata for provider messages + if (src == ChatMessageSource.provider) ...[ + const SizedBox(height: 4), + Padding( + padding: EdgeInsets.only(left: iconTotalWidth), + child: Text( + message.formatUsage(), + style: TextStyle( + fontFamily: 'Baloo2', + fontSize: 14, // 16px equivalent + height: 1.0, + fontWeight: FontWeight.normal, + color: OrchidColors.purpleCaption, + ), + ), + ), + const SizedBox(height: 6), ], - const SizedBox(height: 6), ], ), ), ); } - - Widget _chatSourceText(ChatMessage msg) { - final String srcText; - if (msg.sourceName.isEmpty) { - srcText = msg.source == ChatMessageSource.provider ? 'Chat' : 'You'; - } else { - srcText = msg.sourceName; - } - return Text( - srcText, - style: OrchidText.normal_14, - ); - } } - diff --git a/gai-frontend/lib/chat/chat_button.dart b/gai-frontend/lib/chat/chat_button.dart index 18f245bd6..f281397fb 100644 --- a/gai-frontend/lib/chat/chat_button.dart +++ b/gai-frontend/lib/chat/chat_button.dart @@ -31,3 +31,34 @@ class ChatButton extends StatelessWidget { } } +class OutlinedChatButton extends StatelessWidget { + const OutlinedChatButton({ + super.key, + required this.text, + required this.onPressed, + this.width, + this.height = 40, + }); + + final String text; + final VoidCallback onPressed; + final double? width, height; + + @override + Widget build(BuildContext context) { + return SizedBox( + height: height, + width: width, + child: OutlinedButton( + style: OutlinedButton.styleFrom( + side: BorderSide(color: Colors.white), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + ), + onPressed: onPressed, + child: Text(text).button.white, + ), + ); + } +} diff --git a/gai-frontend/lib/chat/chat_message.dart b/gai-frontend/lib/chat/chat_message.dart index 8f722153b..a6430249f 100644 --- a/gai-frontend/lib/chat/chat_message.dart +++ b/gai-frontend/lib/chat/chat_message.dart @@ -1,4 +1,3 @@ - enum ChatMessageSource { client, provider, system, internal } class ChatMessage { @@ -6,11 +5,55 @@ class ChatMessage { final String sourceName; final String msg; final Map? metadata; + final String? modelId; + final String? modelName; + + ChatMessage( + this.source, + this.msg, { + this.metadata, + this.sourceName = '', + this.modelId, + this.modelName, + }); + + String get message => msg; + + String? get displayName { + print('Getting displayName. source: $source, modelName: $modelName, sourceName: $sourceName'); // Debug what we have + if (source == ChatMessageSource.provider && modelName != null) { + return modelName; + } + if (sourceName.isNotEmpty) { + return sourceName; + } + print('Returning null displayName'); // See when we hit this case + return null; + } - ChatMessage(this.source, this.msg, {this.metadata, this.sourceName = ''}); + String formatUsage() { + if (metadata == null || !metadata!.containsKey('usage')) { + return ''; + } + + final usage = metadata!['usage']; + if (usage == null) { + return ''; + } + + final prompt = usage['prompt_tokens'] ?? 0; + final completion = usage['completion_tokens'] ?? 0; + + if (prompt == 0 && completion == 0) { + return ''; + } + + return 'tokens: $prompt in, $completion out'; + } - String get message { - return msg; + @override + String toString() { + return 'ChatMessage(source: $source, model: $modelName, msg: ${msg.substring(0, msg.length.clamp(0, 50))}...)'; } } diff --git a/gai-frontend/lib/chat/chat_model_button.dart b/gai-frontend/lib/chat/chat_model_button.dart index d232ff70f..00d2356be 100644 --- a/gai-frontend/lib/chat/chat_model_button.dart +++ b/gai-frontend/lib/chat/chat_model_button.dart @@ -1,43 +1,73 @@ import 'package:orchid/orchid/orchid.dart'; -import 'package:orchid/api/orchid_language.dart'; -import 'package:orchid/api/preferences/user_preferences_ui.dart'; -import 'package:orchid/orchid/menu/expanding_popup_menu_item.dart'; -import 'package:orchid/orchid/menu/orchid_popup_menu_item_utils.dart'; -import 'package:orchid/orchid/menu/submenu_popup_menu_item.dart'; -import 'package:url_launcher/url_launcher_string.dart'; -import '../../../orchid/menu/orchid_popup_menu_button.dart'; -import 'chat_button.dart'; +import 'package:orchid/orchid/menu/orchid_popup_menu_button.dart'; +import 'models.dart'; -class ChatModelButton extends StatefulWidget { -// final bool debugMode; -// final VoidCallback onDebugModeChanged; - final updateModel; - final Map> providers; +class ModelSelectionButton extends StatefulWidget { + final List models; + final List selectedModelIds; + final Function(List) updateModels; + final bool multiSelectMode; - const ChatModelButton({ + const ModelSelectionButton({ Key? key, -// required this.debugMode, -// required this.onDebugModeChanged - required this.providers, - required this.updateModel, + required this.models, + required this.selectedModelIds, + required this.updateModels, + required this.multiSelectMode, }) : super(key: key); @override - State createState() => _ChatModelButtonState(); + State createState() => _ModelSelectionButtonState(); } -class _ChatModelButtonState extends State { - final _width = 273.0; - final _height = 50.0; +class _ModelSelectionButtonState extends State { + final _menuWidth = 273.0; + final _menuHeight = 50.0; final _textStyle = OrchidText.medium_16_025.copyWith(height: 2.0); bool _buttonSelected = false; + String get _buttonText { + if (widget.selectedModelIds.isEmpty) { + return widget.multiSelectMode ? 'Select Models' : 'Select Model'; + } + if (!widget.multiSelectMode || widget.selectedModelIds.length == 1) { + final modelId = widget.selectedModelIds.first; + return widget.models + .firstWhere( + (m) => m.id == modelId, + orElse: () => ModelInfo( + id: modelId, + name: modelId, + provider: '', + apiType: '', + ), + ) + .name; + } + return '${widget.selectedModelIds.length} Models'; + } + + void _handleModelSelection(String modelId) { + if (widget.multiSelectMode) { + final newSelection = List.from(widget.selectedModelIds); + if (newSelection.contains(modelId)) { + newSelection.remove(modelId); + } else { + newSelection.add(modelId); + } + widget.updateModels(newSelection); + } else { + widget.updateModels([modelId]); + } + } + @override Widget build(BuildContext context) { - return OrchidPopupMenuButton( - width: 80, + return OrchidPopupMenuButton( + width: null, height: 40, selected: _buttonSelected, + backgroundColor: Colors.transparent, onSelected: (item) { setState(() { _buttonSelected = false; @@ -52,116 +82,73 @@ class _ChatModelButtonState extends State { setState(() { _buttonSelected = true; }); - - return widget.providers.entries.map((entry) { - final providerId = entry.key; - final providerName = entry.value['name'] ?? providerId; + + if (widget.models.isEmpty) { + return [ + PopupMenuItem( + enabled: false, + height: _menuHeight, + child: SizedBox( + width: _menuWidth, + child: Text('No models available', style: _textStyle), + ), + ), + ]; + } + + return widget.models.map((model) { + final isSelected = widget.selectedModelIds.contains(model.id); return PopupMenuItem( - onTap: () { widget.updateModel(providerId); }, - height: _height, + onTap: () => _handleModelSelection(model.id), + height: _menuHeight, child: SizedBox( - width: _width, - child: Text(providerName, style: _textStyle), + width: _menuWidth, + child: Row( + children: [ + Expanded( + child: Text( + model.name, + style: _textStyle.copyWith( + color: isSelected ? Theme.of(context).primaryColor : null, + ), + ), + ), + if (isSelected) + Icon( + Icons.check, + size: 16, + color: Theme.of(context).primaryColor, + ), + ], + ), ), ); }).toList(); }, -/* - itemBuilder: (itemBuilderContext) { - setState(() { - _buttonSelected = true; - }); - - const div = PopupMenuDivider(height: 1.0); - return [ - PopupMenuItem( - onTap: () { widget.updateModel('gpt4'); }, - height: _height, - child: SizedBox( - width: _width, - child: Text('GPT-4', style: _textStyle), - ), - ), - PopupMenuItem( - onTap: () { widget.updateModel('gpt4o'); }, - height: _height, - child: SizedBox( - width: _width, - child: Text('GPT-4o', style: _textStyle), - ), - ), -// div, - PopupMenuItem( - onTap: () { widget.updateModel('mistral'); }, - height: _height, - child: SizedBox( - width: _width, - child: Text('Mistral 7B', style: _textStyle), - ), - ), - PopupMenuItem( - onTap: () { widget.updateModel('mixtral-8x22b'); }, - height: _height, - child: SizedBox( - width: _width, - child: Text('Mixtral 8x22b', style: _textStyle), - ), + child: SizedBox( + height: 40, + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 16), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Flexible( + child: Text( + _buttonText, + textAlign: TextAlign.left, + overflow: TextOverflow.ellipsis, + ).button.white, + ), + Icon( + Icons.arrow_drop_down, + color: Colors.white, + size: 24, + ), + ], ), - PopupMenuItem( - onTap: () { widget.updateModel('gemini'); }, - height: _height, - child: SizedBox( - width: _width, - child: Text('Gemini 1.5', style: _textStyle), - ), - ), - PopupMenuItem( - onTap: () { widget.updateModel('claude-3'); }, - height: _height, - child: SizedBox( - width: _width, - child: Text('Claude 3 Opus', style: _textStyle), - ), - ), - PopupMenuItem( - onTap: () { widget.updateModel('claude35sonnet'); }, - height: _height, - child: SizedBox( - width: _width, - child: Text('Claude 3.5 Sonnet', style: _textStyle), - ), - ), - ]; - }, -*/ - - /* - child: FittedBox( - fit: BoxFit.scaleDown, - child: SizedBox( - width: 80, height: 20, child: Text('Model'))), -*/ -// child: SizedBox( -// width: 120, height: 20, child: Text('Model', style: _textStyle).white), - child: Align( - alignment: Alignment.center, - child: Text('Model', textAlign: TextAlign.center).button.white, - ), - ); - } - - PopupMenuItem _listMenuItem({ - required bool selected, - required String title, - required VoidCallback onTap, - }) { - return OrchidPopupMenuItemUtils.listMenuItem( - context: context, - selected: selected, - title: title, - onTap: onTap, - textStyle: _textStyle, + ), + ), ); } } diff --git a/gai-frontend/lib/chat/chat_prompt.dart b/gai-frontend/lib/chat/chat_prompt.dart index 06470beba..793d6f00e 100644 --- a/gai-frontend/lib/chat/chat_prompt.dart +++ b/gai-frontend/lib/chat/chat_prompt.dart @@ -2,19 +2,18 @@ import 'package:orchid/orchid/field/orchid_labeled_numeric_field.dart'; import 'package:orchid/orchid/field/orchid_text_field.dart'; import 'package:orchid/orchid/orchid.dart'; -// The prompt row and collapsible bid form footer class ChatPromptPanel extends StatefulWidget { final TextEditingController promptTextController; final VoidCallback onSubmit; - final ValueChanged setBid; - final NumericValueFieldController bidController; + final ValueChanged setMaxTokens; + final NumericValueFieldController maxTokensController; const ChatPromptPanel({ super.key, required this.promptTextController, required this.onSubmit, - required this.setBid, - required this.bidController, + required this.setMaxTokens, + required this.maxTokensController, }); @override @@ -63,26 +62,27 @@ class _ChatPromptPanelState extends State { ], ).padx(8), if (_showPromptDetails) - _buildBidForm(widget.setBid, widget.bidController), + _buildPromptParamsForm(widget.setMaxTokens, widget.maxTokensController), ], ); } - Widget _buildBidForm( - ValueChanged setBid, - NumericValueFieldController bidController, + Widget _buildPromptParamsForm( + ValueChanged setMaxTokens, + NumericValueFieldController maxTokensController, ) { return Container( padding: const EdgeInsets.all(10.0), child: Column( children: [ - Text('Your bid is the price per token in/out you will pay.', - style: OrchidText.medium_20_050) - .top(8), + Text( + 'Set the maximum number of tokens for the response.', + style: OrchidText.medium_20_050, + ).top(8), OrchidLabeledNumericField( - label: 'Bid', - onChange: setBid, - controller: bidController, + label: 'Max Tokens', + onChange: (value) => setMaxTokens(value?.toInt()), + controller: maxTokensController, ).top(12) ], ), diff --git a/gai-frontend/lib/chat/chat_settings_button.dart b/gai-frontend/lib/chat/chat_settings_button.dart index b7d46e46a..7d5b4e958 100644 --- a/gai-frontend/lib/chat/chat_settings_button.dart +++ b/gai-frontend/lib/chat/chat_settings_button.dart @@ -9,12 +9,18 @@ import '../../../orchid/menu/orchid_popup_menu_button.dart'; class ChatSettingsButton extends StatefulWidget { final bool debugMode; + final bool multiSelectMode; final VoidCallback onDebugModeChanged; + final VoidCallback onMultiSelectModeChanged; + final VoidCallback onClearChat; const ChatSettingsButton({ Key? key, required this.debugMode, + required this.multiSelectMode, required this.onDebugModeChanged, + required this.onMultiSelectModeChanged, + required this.onClearChat, }) : super(key: key); @override @@ -33,93 +39,128 @@ class _ChatSettingsButtonState extends State { const String.fromEnvironment('build_commit', defaultValue: '...'); final githubUrl = 'https://github.com/OrchidTechnologies/orchid/tree/$buildCommit/web-ethereum/dapp2'; - return OrchidPopupMenuButton( - width: 40, - height: 40, - selected: _buttonSelected, - onSelected: (item) { - setState(() { - _buttonSelected = false; - }); - }, - onCanceled: () { - setState(() { - _buttonSelected = false; - }); - }, - itemBuilder: (itemBuilderContext) { - setState(() { - _buttonSelected = true; - }); + + return Center( + child: OrchidPopupMenuButton( + width: 30, + height: 30, + selected: _buttonSelected, + backgroundColor: Colors.transparent, + onSelected: (item) { + setState(() { + _buttonSelected = false; + }); + }, + onCanceled: () { + setState(() { + _buttonSelected = false; + }); + }, + itemBuilder: (itemBuilderContext) { + setState(() { + _buttonSelected = true; + }); - const div = PopupMenuDivider(height: 1.0); - return [ - // debug mode - PopupMenuItem( - onTap: widget.onDebugModeChanged, - height: _height, - child: SizedBox( - width: _width, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text("Debug Mode", style: _textStyle), - Icon( - widget.debugMode - ? Icons.check_box_outlined - : Icons.check_box_outline_blank, - color: Colors.white, - ), - ], + const div = PopupMenuDivider(height: 1.0); + return [ + // Clear chat + PopupMenuItem( + onTap: widget.onClearChat, + height: _height, + child: SizedBox( + width: _width, + child: Text("Clear Chat", style: _textStyle), ), ), - ), - div, - SubmenuPopopMenuItemBuilder( - builder: _buildIdenticonsPref, - ), - div, - SubmenuPopopMenuItemBuilder( - builder: _buildLanguagePref, - ), - div, - PopupMenuItem( - onTap: () { - Future.delayed(millis(0), () async { - _openLicensePage(context); - }); - }, - height: _height, - child: SizedBox( - width: _width, - child: Text(s.openSourceLicenses, style: _textStyle), + div, + // debug mode + PopupMenuItem( + onTap: widget.onDebugModeChanged, + height: _height, + child: SizedBox( + width: _width, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text("Debug Mode", style: _textStyle), + Icon( + widget.debugMode + ? Icons.check_box_outlined + : Icons.check_box_outline_blank, + color: Colors.white, + ), + ], + ), + ), ), + div, + // multi-select mode + PopupMenuItem( + onTap: widget.onMultiSelectModeChanged, + height: _height, + child: SizedBox( + width: _width, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text("Multi-Model Mode", style: _textStyle), + Icon( + widget.multiSelectMode + ? Icons.check_box_outlined + : Icons.check_box_outline_blank, + color: Colors.white, + ), + ], + ), + ), + ), + div, + SubmenuPopopMenuItemBuilder( + builder: _buildIdenticonsPref, + ), + div, + SubmenuPopopMenuItemBuilder( + builder: _buildLanguagePref, + ), + div, + PopupMenuItem( + onTap: () { + Future.delayed(millis(0), () async { + _openLicensePage(context); + }); + }, + height: _height, + child: SizedBox( + width: _width, + child: Text(s.openSourceLicenses, style: _textStyle), + ), + ), + div, + // dapp version item + _listMenuItem( + selected: false, + title: 'Version: ' + buildCommit, + onTap: () async { + launchUrlString(githubUrl); + }, + ), + ]; + }, + child: SizedBox( + width: 30, + height: 30, + child: FittedBox( + fit: BoxFit.contain, + child: OrchidAsset.svg.settings_gear, ), - div, - // dapp version item - _listMenuItem( - selected: false, - title: 'Version: ' + buildCommit, - onTap: () async { - launchUrlString(githubUrl); - }, - ), - ]; - }, - child: FittedBox( - fit: BoxFit.scaleDown, - child: SizedBox( - width: 20, height: 20, child: OrchidAsset.svg.settings_gear)), + ), + ), ); } Future _openLicensePage(BuildContext context) { // TODO: return Future.delayed(millis(100), () async {}); - // return Navigator.push(context, - // MaterialPageRoute(builder: (BuildContext context) { - // return OpenSourcePage(); - // })); } Widget _buildLanguagePref(bool expanded) { @@ -147,12 +188,11 @@ class _ChatSettingsButtonState extends State { ), ) .toList() - .cast() // so that we can add the items below + .cast() .separatedWith( PopupMenuDivider(height: 1.0), ); - // Default system language option items.insert( 0, _listMenuItem( diff --git a/gai-frontend/lib/chat/inference_client.dart b/gai-frontend/lib/chat/inference_client.dart new file mode 100644 index 000000000..4ded9a1d7 --- /dev/null +++ b/gai-frontend/lib/chat/inference_client.dart @@ -0,0 +1,223 @@ +import 'dart:convert'; +import 'package:http/http.dart' as http; +import 'chat_message.dart'; + +class InferenceError implements Exception { + final int statusCode; + final String message; + + InferenceError(this.statusCode, this.message); + + @override + String toString() => 'InferenceError($statusCode): $message'; +} + +class TokenUsage { + final int promptTokens; + final int completionTokens; + final int totalTokens; + + TokenUsage({ + required this.promptTokens, + required this.completionTokens, + required this.totalTokens, + }); + + factory TokenUsage.fromJson(Map json) { + return TokenUsage( + promptTokens: json['prompt_tokens'], + completionTokens: json['completion_tokens'], + totalTokens: json['total_tokens'], + ); + } + + Map toJson() => { + 'prompt_tokens': promptTokens, + 'completion_tokens': completionTokens, + 'total_tokens': totalTokens, + }; +} + +class ModelInfo { + final String id; + final String name; + final String apiType; + + ModelInfo({ + required this.id, + required this.name, + required this.apiType, + }); + + factory ModelInfo.fromJson(Map json) { + return ModelInfo( + id: json['id'], + name: json['name'], + apiType: json['api_type'], + ); + } +} + +class InferenceResponse { + final String response; + final TokenUsage usage; + + InferenceResponse({ + required this.response, + required this.usage, + }); + + factory InferenceResponse.fromJson(Map json) { + return InferenceResponse( + response: json['response'], + usage: TokenUsage.fromJson(json['usage']), + ); + } + + Map toMetadata() => { + 'usage': usage.toJson(), + }; +} + +class InferenceClient { + final String baseUrl; + String? _authToken; + + InferenceClient({required String baseUrl}) + : baseUrl = _normalizeBaseUrl(baseUrl); + + static String _normalizeBaseUrl(String url) { + if (url.endsWith('/')) { + url = url.substring(0, url.length - 1); + } + + if (url.endsWith('/v1/inference')) { + url = url.substring(0, url.length - '/v1/inference'.length); + } + + return url; + } + + void setAuthToken(String token) { + _authToken = token; + } + + Future> listModels() async { + if (_authToken == null) { + throw InferenceError(401, 'No auth token'); + } + + final response = await http.get( + Uri.parse('$baseUrl/v1/models'), + headers: {'Authorization': 'Bearer $_authToken'}, + ); + + if (response.statusCode != 200) { + throw InferenceError(response.statusCode, response.body); + } + + final data = json.decode(response.body) as Map; + return data.map((key, value) => MapEntry( + key, + ModelInfo.fromJson(value as Map), + )); + } + + Map _chatMessageToJson(ChatMessage msg) { + String role; + switch (msg.source) { + case ChatMessageSource.client: + role = 'user'; + break; + case ChatMessageSource.provider: + role = 'assistant'; + break; + default: + role = 'system'; + } + + final map = { + 'role': role, + 'content': msg.message, + }; + + if (msg.modelName != null) { + map['name'] = msg.modelName; + } + + return map; + } + + // Simple token estimation + int _estimateTokenCount(String text) { + // Average English word is ~4 characters + space + // Average token is ~4 characters + return (text.length / 4).ceil(); + } + + Future> inference({ + required List messages, + String? model, + Map? params, + }) async { + if (_authToken == null) { + throw InferenceError(401, 'No auth token'); + } + + if (messages.isEmpty) { + throw InferenceError(400, 'No messages provided'); + } + + final estimatedTokens = messages.fold( + 0, (sum, msg) => sum + _estimateTokenCount(msg.message) + ); + + final formattedMessages = messages.map(_chatMessageToJson).toList(); + + final Map payload = { + 'messages': formattedMessages, + 'estimated_prompt_tokens': estimatedTokens, + }; + + if (model != null) { + payload['model'] = model; + } + + if (params != null) { + payload.addAll(params); + } + + print('InferenceClient: Preparing request to $baseUrl/v1/inference'); + print('Payload: ${const JsonEncoder.withIndent(' ').convert(payload)}'); + + final response = await http.post( + Uri.parse('$baseUrl/v1/inference'), + headers: { + 'Authorization': 'Bearer $_authToken', + 'Content-Type': 'application/json', + }, + body: json.encode(payload), + ); + + print('InferenceClient: Received response status ${response.statusCode}'); + print('Response body: ${response.body}'); + + if (response.statusCode == 402) { + throw InferenceError(402, 'Insufficient balance'); + } + + if (response.statusCode != 200) { + throw InferenceError(response.statusCode, response.body); + } + + final inferenceResponse = InferenceResponse.fromJson( + json.decode(response.body) + ); + + return { + 'response': inferenceResponse.response, + 'usage': inferenceResponse.usage.toJson(), + 'estimated_prompt_tokens': estimatedTokens, + }; + } +} diff --git a/gai-frontend/lib/chat/models.dart b/gai-frontend/lib/chat/models.dart new file mode 100644 index 000000000..79cfd8cb2 --- /dev/null +++ b/gai-frontend/lib/chat/models.dart @@ -0,0 +1,94 @@ +import 'package:flutter/foundation.dart'; + +class ModelInfo { + final String id; + final String name; + final String provider; + final String apiType; + + ModelInfo({ + required this.id, + required this.name, + required this.provider, + required this.apiType, + }); + + factory ModelInfo.fromJson(Map json, String providerId) { + return ModelInfo( + id: json['id'], + name: json['name'], + provider: providerId, + apiType: json['api_type'], + ); + } + + @override + String toString() => 'ModelInfo(id: $id, name: $name, provider: $provider)'; +} + +class ModelsState extends ChangeNotifier { + final _modelsByProvider = >{}; + final _loadingProviders = {}; + final _errors = {}; + + bool isLoading(String providerId) => _loadingProviders.contains(providerId); + String? getError(String providerId) => _errors[providerId]; + + List getModelsForProvider(String providerId) { + return _modelsByProvider[providerId] ?? []; + } + + List get allModels { + final models = _modelsByProvider.values.expand((models) => models).toList(); + print('ModelsState.allModels returning ${models.length} models: $models'); + return models; + } + + Future fetchModelsForProvider( + String providerId, + dynamic client, + ) async { + print('ModelsState: Fetching models for provider $providerId'); + _loadingProviders.add(providerId); + _errors.remove(providerId); + notifyListeners(); + + try { + final response = await client.listModels(); + print('ModelsState: Received model data from client: $response'); + + // Convert the response map entries directly to ModelInfo objects + final modelsList = response.entries.map((entry) => ModelInfo( + id: entry.value.id, + name: entry.value.name, + provider: providerId, + apiType: entry.value.apiType, + )).toList(); + + print('ModelsState: Created models list: $modelsList'); + + _modelsByProvider[providerId] = modelsList; + print('ModelsState: Updated models for provider $providerId: $modelsList'); + } catch (e, stack) { + print('ModelsState: Error fetching models: $e\n$stack'); + _errors[providerId] = e.toString(); + } finally { + _loadingProviders.remove(providerId); + notifyListeners(); + print('ModelsState: Notified listeners, current state: \n' + 'Models: ${_modelsByProvider}\n' + 'Loading: $_loadingProviders\n' + 'Errors: $_errors'); + } + } + + void clearProviderModels(String providerId) { + _modelsByProvider.remove(providerId); + _errors.remove(providerId); + _loadingProviders.remove(providerId); + notifyListeners(); + } + + bool get isAnyLoading => _loadingProviders.isNotEmpty; + Set get activeProviders => _modelsByProvider.keys.toSet(); +} diff --git a/gai-frontend/lib/chat/provider_connection.dart b/gai-frontend/lib/chat/provider_connection.dart new file mode 100644 index 000000000..d9f1aa39b --- /dev/null +++ b/gai-frontend/lib/chat/provider_connection.dart @@ -0,0 +1,301 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:math'; +import 'package:web_socket_channel/web_socket_channel.dart'; +import 'package:orchid/api/orchid_crypto.dart'; +import 'package:orchid/api/orchid_eth/orchid_ticket.dart'; +import 'package:orchid/api/orchid_eth/orchid_account.dart'; +import 'package:orchid/api/orchid_eth/orchid_account_detail.dart'; +import 'inference_client.dart'; +import 'chat_message.dart'; + +typedef MessageCallback = void Function(String message); +typedef ChatCallback = void Function(String message, Map metadata); +typedef VoidCallback = void Function(); +typedef ErrorCallback = void Function(String error); +typedef AuthTokenCallback = void Function(String token, String inferenceUrl); + +class _PendingRequest { + final String requestId; + final String modelId; + final List messages; + final Map? params; + final DateTime timestamp; + + _PendingRequest({ + required this.requestId, + required this.modelId, + required this.messages, + required this.params, + }) : timestamp = DateTime.now(); +} + +class ProviderConnection { + final maxuint256 = BigInt.two.pow(256) - BigInt.one; + final maxuint64 = BigInt.two.pow(64) - BigInt.one; + final wei = BigInt.from(10).pow(18); + WebSocketChannel? _providerChannel; + InferenceClient? get inferenceClient => _inferenceClient; + InferenceClient? _inferenceClient; + final MessageCallback onMessage; + final ChatCallback onChat; + final VoidCallback onConnect; + final ErrorCallback onError; + final VoidCallback onDisconnect; + final MessageCallback onSystemMessage; + final MessageCallback onInternalMessage; + final EthereumAddress contract; + final String url; + final AccountDetail accountDetail; + final AuthTokenCallback? onAuthToken; + final Map _requestModels = {}; + final Map _pendingRequests = {}; + + String _generateRequestId() { + return '${DateTime.now().millisecondsSinceEpoch}-${Random().nextInt(10000)}'; + } + + ProviderConnection({ + required this.onMessage, + required this.onConnect, + required this.onChat, + required this.onDisconnect, + required this.onError, + required this.onSystemMessage, + required this.onInternalMessage, + required this.contract, + required this.url, + required this.accountDetail, + this.onAuthToken, + }) { + try { + _providerChannel = WebSocketChannel.connect(Uri.parse(url)); + _providerChannel?.ready; + } catch (e) { + onError('Failed on provider connection: $e'); + return; + } + _providerChannel?.stream.listen( + receiveProviderMessage, + onDone: () => onDisconnect(), + onError: (error) => onError('ws error: $error'), + ); + onConnect(); + } + + static Future connect({ + required String billingUrl, + required String inferenceUrl, // This won't be used initially + required EthereumAddress contract, + required AccountDetail accountDetail, + required MessageCallback onMessage, + required ChatCallback onChat, + required VoidCallback onConnect, + required ErrorCallback onError, + required VoidCallback onDisconnect, + required MessageCallback onSystemMessage, + required MessageCallback onInternalMessage, + AuthTokenCallback? onAuthToken, + }) async { + final connection = ProviderConnection( + onMessage: onMessage, + onConnect: onConnect, + onChat: onChat, + onDisconnect: onDisconnect, + onError: onError, + onSystemMessage: onSystemMessage, + onInternalMessage: onInternalMessage, + contract: contract, + url: billingUrl, + accountDetail: accountDetail, + onAuthToken: onAuthToken, + ); + + return connection; + } + + void _handleAuthToken(Map data) { + final token = data['session_id']; + final inferenceUrl = data['inference_url']; + if (token == null || inferenceUrl == null) { + onError('Invalid auth token response'); + return; + } + + // Create new inference client with the URL from the auth token + _inferenceClient = InferenceClient(baseUrl: inferenceUrl); + _inferenceClient!.setAuthToken(token); + onInternalMessage('Auth token received and inference client initialized'); + + onAuthToken?.call(token, inferenceUrl); + } + + bool validInvoice(invoice) { + return invoice.containsKey('amount') && invoice.containsKey('commit') && + invoice.containsKey('recipient'); + } + + void payInvoice(Map invoice) { + var payment; + if (!validInvoice(invoice)) { + onError('Invalid invoice ${invoice}'); + return; + } + + assert(accountDetail.funder != null); + final balance = accountDetail.lotteryPot?.balance.intValue ?? BigInt.zero; + final deposit = accountDetail.lotteryPot?.deposit.intValue ?? BigInt.zero; + + if (balance <= BigInt.zero || deposit <= BigInt.zero) { + onError('Insufficient funds: balance=$balance, deposit=$deposit'); + return; + } + + final faceval = _bigIntMin(balance, (wei * deposit) ~/ (wei * BigInt.two)); + if (faceval <= BigInt.zero) { + onError('Invalid face value: $faceval'); + return; + } + + final data = BigInt.zero; + final due = BigInt.parse(invoice['amount']); + final lotaddr = contract; + final token = EthereumAddress.zero; + + BigInt ratio; + try { + ratio = maxuint64 & (maxuint64 * due ~/ faceval); + } catch (e) { + onError('Failed to calculate ratio: $e (due=$due, faceval=$faceval)'); + return; + } + + final commit = BigInt.parse(invoice['commit'] ?? '0x0'); + final recipient = invoice['recipient']; + + final ticket = OrchidTicket( + data: data, + lotaddr: lotaddr, + token: token, + amount: faceval, + ratio: ratio, + funder: accountDetail.account.funder, + recipient: EthereumAddress.from(recipient), + commitment: commit, + privateKey: accountDetail.account.signerKey.private, + millisecondsSinceEpoch: DateTime.now().millisecondsSinceEpoch, + ); + + payment = '{"type": "payment", "tickets": ["${ticket.serializeTicket()}"]}'; + onInternalMessage('Client: $payment'); + _sendProviderMessage(payment); + } + + void receiveProviderMessage(dynamic message) { + final data = jsonDecode(message) as Map; + print(message); + onMessage('Provider: $message'); + + switch (data['type']) { + case 'job_complete': + final requestId = data['request_id']; + final pendingRequest = requestId != null ? _pendingRequests.remove(requestId) : null; + + onChat(data['output'], { + ...data, + 'model_id': pendingRequest?.modelId, + }); + break; + case 'invoice': + payInvoice(data); + break; + case 'bid_low': + onSystemMessage("Bid below provider's reserve price."); + break; + case 'auth_token': + _handleAuthToken(data); + break; + } + } + + Future requestAuthToken() async { + final message = '{"type": "request_token"}'; + onInternalMessage('Requesting auth token'); + _sendProviderMessage(message); + } + + Future requestInference( + String modelId, + List messages, { + Map? params, + }) async { + if (_inferenceClient == null) { + await requestAuthToken(); + await Future.delayed(Duration(milliseconds: 100)); + + if (_inferenceClient == null) { + onError('No inference connection available'); + return; + } + } + + try { + final requestId = _generateRequestId(); + + _pendingRequests[requestId] = _PendingRequest( + requestId: requestId, + modelId: modelId, + messages: messages, + params: params, + ); + + final allParams = { + ...?params, + 'request_id': requestId, + }; + + onInternalMessage('Sending inference request:\n' + 'Model: $modelId\n' + 'Messages: ${messages.map((m) => "${m.source}: ${m.message}").join("\n")}\n' + 'Params: $allParams' + ); + + final result = await _inferenceClient!.inference( + messages: messages, + model: modelId, + params: allParams, + ); + + final pendingRequest = _pendingRequests.remove(requestId); + + onChat(result['response'], { + 'type': 'job_complete', + 'output': result['response'], + 'usage': result['usage'], + 'model_id': pendingRequest?.modelId, + 'request_id': requestId, + 'estimated_prompt_tokens': result['estimated_prompt_tokens'], + }); + } catch (e, stack) { + onError('Failed to send inference request: $e\n$stack'); + } + } + + void _sendProviderMessage(String message) { + print('Sending message to provider $message'); + _providerChannel?.sink.add(message); + } + + void dispose() { + _providerChannel?.sink.close(); + _pendingRequests.clear(); + onDisconnect(); + } + + BigInt _bigIntMin(BigInt a, BigInt b) { + if (a > b) { + return b; + } + return a; + } +} diff --git a/gai-frontend/pubspec.lock b/gai-frontend/pubspec.lock index 3d744db6b..dc26a272f 100644 --- a/gai-frontend/pubspec.lock +++ b/gai-frontend/pubspec.lock @@ -125,10 +125,10 @@ packages: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" convert: dependency: transitive description: @@ -161,6 +161,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.4" + decimal: + dependency: "direct main" + description: + name: decimal + sha256: "4140a688f9e443e2f4de3a1162387bf25e1ac6d51e24c9da263f245210f41440" + url: "https://pub.dev" + source: hosted + version: "3.0.2" fake_async: dependency: transitive description: @@ -254,7 +262,7 @@ packages: source: hosted version: "2.1.2" http: - dependency: transitive + dependency: "direct main" description: name: http sha256: "5895291c13fa8a3bd82e76d5627f69e0d85ca6a30dcac95c4ea19a5d555879c2" @@ -273,10 +281,10 @@ packages: dependency: "direct main" description: name: intl - sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf url: "https://pub.dev" source: hosted - version: "0.18.1" + version: "0.19.0" jdenticon_dart: dependency: "direct main" description: @@ -309,6 +317,30 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.2" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" + url: "https://pub.dev" + source: hosted + version: "10.0.5" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" + url: "https://pub.dev" + source: hosted + version: "3.0.5" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" lints: dependency: transitive description: @@ -329,26 +361,26 @@ packages: dependency: transitive description: name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.16" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.15.0" package_config: dependency: transitive description: @@ -361,10 +393,10 @@ packages: dependency: transitive description: name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.8.3" + version: "1.9.0" path_drawing: dependency: transitive description: @@ -453,6 +485,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.3" + rational: + dependency: transitive + description: + name: rational + sha256: cb808fb6f1a839e6fc5f7d8cb3b0a10e1db48b3be102de73938c627f0b636336 + url: "https://pub.dev" + source: hosted + version: "2.2.3" rxdart: dependency: "direct main" description: @@ -534,18 +574,18 @@ packages: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: "direct main" description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" stream_transform: dependency: transitive description: @@ -590,10 +630,10 @@ packages: dependency: transitive description: name: test_api - sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "0.7.2" typed_data: dependency: transitive description: @@ -682,22 +722,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" - watcher: + vm_service: dependency: transitive description: - name: watcher - sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" + name: vm_service + sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc url: "https://pub.dev" source: hosted - version: "1.1.0" - web: + version: "14.2.4" + watcher: dependency: transitive description: - name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + name: watcher + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "1.1.0" web3dart: dependency: "direct main" description: @@ -755,5 +795,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.1.0 <4.0.0" - flutter: ">=3.13.0" + dart: ">=3.3.0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/gai-frontend/pubspec.yaml b/gai-frontend/pubspec.yaml index 2d554748a..1a299ff65 100644 --- a/gai-frontend/pubspec.yaml +++ b/gai-frontend/pubspec.yaml @@ -21,10 +21,10 @@ dependencies: # flutter_lints: ^1.0.0 flutter_svg: 1.0.3 flutter_web3: 2.1.6 - intl: 0.18.1 + intl: 0.19.0 pointycastle: 3.5.0 rxdart: 0.27.7 - shared_preferences: ^2.2.2 + shared_preferences: ^2.0.5 styled_text: 4.0.0 uuid: 3.0.5 url_launcher: 6.1.3 @@ -34,6 +34,8 @@ dependencies: browser_detector: ^2.0.0 badges: 3.1.1 jdenticon_dart: 2.0.0 + decimal: ^3.0.2 + http: ^0.13.4 dev_dependencies: flutter_test: