diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml
index 23902f110..69c632967 100644
--- a/.github/workflows/pr_test_build.yml
+++ b/.github/workflows/pr_test_build.yml
@@ -151,6 +151,7 @@ jobs:
echo "const moralisApiKey = '${{ secrets.MORALIS_API_KEY }}';" >> lib/.secrets.g.dart
echo "const polygonScanApiKey = '${{ secrets.POLYGON_SCAN_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart
echo "const ankrApiKey = '${{ secrets.ANKR_API_KEY }}';" >> cw_solana/lib/.secrets.g.dart
+ echo "const nano2ApiKey = '${{ secrets.NANO2_API_KEY }}';" >> cw_nano/lib/.secrets.g.dart
echo "const tronGridApiKey = '${{ secrets.TRON_GRID_API_KEY }}';" >> cw_tron/lib/.secrets.g.dart
- name: Rename app
diff --git a/.gitignore b/.gitignore
index f1e5b6da3..24b7291f8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -94,6 +94,7 @@ android/app/key.jks
**/tool/.evm-secrets-config.json
**/tool/.ethereum-secrets-config.json
**/tool/.solana-secrets-config.json
+**/tool/.nano-secrets-config.json
**/tool/.tron-secrets-config.json
**/lib/.secrets.g.dart
**/cw_evm/lib/.secrets.g.dart
diff --git a/android/app/src/main/AndroidManifestBase.xml b/android/app/src/main/AndroidManifestBase.xml
index 23207d629..57462099c 100644
--- a/android/app/src/main/AndroidManifestBase.xml
+++ b/android/app/src/main/AndroidManifestBase.xml
@@ -91,6 +91,13 @@
+
+
+
+
+
+
+
with Serializable implemen
element.tag == walletCurrency?.tag));
} catch (_) {}
+ // search by fullName if not found by title:
+ try {
+ return CryptoCurrency.all.firstWhere((element) => element.fullName?.toLowerCase() == name);
+ } catch (_) {}
+
if (CryptoCurrency._nameCurrencyMap[name.toLowerCase()] == null) {
final s = 'Unexpected token: $name for CryptoCurrency fromString';
throw ArgumentError.value(name, 'name', s);
}
+
return CryptoCurrency._nameCurrencyMap[name.toLowerCase()]!;
}
diff --git a/cw_nano/lib/banano_balance.dart b/cw_nano/lib/banano_balance.dart
index b904a35cb..d766077fc 100644
--- a/cw_nano/lib/banano_balance.dart
+++ b/cw_nano/lib/banano_balance.dart
@@ -1,12 +1,28 @@
import 'package:cw_core/balance.dart';
import 'package:nanoutil/nanoutil.dart';
+BigInt stringAmountToBigIntBanano(String amount) {
+ return BigInt.parse(NanoAmounts.getAmountAsRaw(amount, NanoAmounts.rawPerBanano));
+}
+
class BananoBalance extends Balance {
final BigInt currentBalance;
final BigInt receivableBalance;
BananoBalance({required this.currentBalance, required this.receivableBalance}) : super(0, 0);
+ BananoBalance.fromFormattedString(
+ {required String formattedCurrentBalance, required String formattedReceivableBalance})
+ : currentBalance = stringAmountToBigIntBanano(formattedCurrentBalance),
+ receivableBalance = stringAmountToBigIntBanano(formattedReceivableBalance),
+ super(0, 0);
+
+ BananoBalance.fromRawString(
+ {required String currentBalance, required String receivableBalance})
+ : currentBalance = BigInt.parse(currentBalance),
+ receivableBalance = BigInt.parse(receivableBalance),
+ super(0, 0);
+
@override
String get formattedAvailableBalance {
return NanoAmounts.getRawAsUsableString(currentBalance.toString(), NanoAmounts.rawPerBanano);
diff --git a/cw_nano/lib/nano_balance.dart b/cw_nano/lib/nano_balance.dart
index 8b8c93b33..691b3a32d 100644
--- a/cw_nano/lib/nano_balance.dart
+++ b/cw_nano/lib/nano_balance.dart
@@ -1,7 +1,7 @@
import 'package:cw_core/balance.dart';
import 'package:nanoutil/nanoutil.dart';
-BigInt stringAmountToBigInt(String amount) {
+BigInt stringAmountToBigIntNano(String amount) {
return BigInt.parse(NanoAmounts.getAmountAsRaw(amount, NanoAmounts.rawPerNano));
}
@@ -13,8 +13,8 @@ class NanoBalance extends Balance {
NanoBalance.fromFormattedString(
{required String formattedCurrentBalance, required String formattedReceivableBalance})
- : currentBalance = stringAmountToBigInt(formattedCurrentBalance),
- receivableBalance = stringAmountToBigInt(formattedReceivableBalance),
+ : currentBalance = stringAmountToBigIntNano(formattedCurrentBalance),
+ receivableBalance = stringAmountToBigIntNano(formattedReceivableBalance),
super(0, 0);
NanoBalance.fromRawString(
diff --git a/cw_nano/lib/nano_client.dart b/cw_nano/lib/nano_client.dart
index 064a0bdee..3b388e5e8 100644
--- a/cw_nano/lib/nano_client.dart
+++ b/cw_nano/lib/nano_client.dart
@@ -10,6 +10,7 @@ import 'package:nanodart/nanodart.dart';
import 'package:cw_core/node.dart';
import 'package:nanoutil/nanoutil.dart';
import 'package:shared_preferences/shared_preferences.dart';
+import 'package:cw_nano/.secrets.g.dart' as secrets;
class NanoClient {
static const Map CAKE_HEADERS = {
@@ -52,10 +53,19 @@ class NanoClient {
}
}
+ Map getHeaders() {
+ if (_node!.uri == "https://rpc.nano.to") {
+ return CAKE_HEADERS..addAll({
+ "key": secrets.nano2ApiKey,
+ });
+ }
+ return CAKE_HEADERS;
+ }
+
Future getBalance(String address) async {
final response = await http.post(
_node!.uri,
- headers: CAKE_HEADERS,
+ headers: getHeaders(),
body: jsonEncode(
{
"action": "account_balance",
@@ -82,7 +92,7 @@ class NanoClient {
try {
final response = await http.post(
_node!.uri,
- headers: CAKE_HEADERS,
+ headers: getHeaders(),
body: jsonEncode(
{
"action": "account_info",
@@ -94,7 +104,7 @@ class NanoClient {
final data = await jsonDecode(response.body);
return AccountInfoResponse.fromJson(data as Map);
} catch (e) {
- print("error while getting account info");
+ print("error while getting account info $e");
return null;
}
}
@@ -149,7 +159,7 @@ class NanoClient {
Future requestWork(String hash) async {
final response = await http.post(
_powNode!.uri,
- headers: CAKE_HEADERS,
+ headers: getHeaders(),
body: json.encode(
{
"action": "work_generate",
@@ -192,7 +202,7 @@ class NanoClient {
final processResponse = await http.post(
_node!.uri,
- headers: CAKE_HEADERS,
+ headers: getHeaders(),
body: processBody,
);
@@ -351,7 +361,7 @@ class NanoClient {
});
final processResponse = await http.post(
_node!.uri,
- headers: CAKE_HEADERS,
+ headers: getHeaders(),
body: processBody,
);
@@ -367,7 +377,7 @@ class NanoClient {
required String privateKey,
}) async {
final receivableResponse = await http.post(_node!.uri,
- headers: CAKE_HEADERS,
+ headers: getHeaders(),
body: jsonEncode({
"action": "receivable",
"account": destinationAddress,
@@ -417,7 +427,7 @@ class NanoClient {
Future> fetchTransactions(String address) async {
try {
final response = await http.post(_node!.uri,
- headers: CAKE_HEADERS,
+ headers: getHeaders(),
body: jsonEncode({
"action": "account_history",
"account": address,
diff --git a/ios/Runner/InfoBase.plist b/ios/Runner/InfoBase.plist
index 02365bda7..83e60b542 100644
--- a/ios/Runner/InfoBase.plist
+++ b/ios/Runner/InfoBase.plist
@@ -140,6 +140,16 @@
nano-wallet
+
+ CFBundleTypeRole
+ Viewer
+ CFBundleURLName
+ nano-gpt
+ CFBundleURLSchemes
+
+ nano-gpt
+
+
CFBundleTypeRole
Editor
diff --git a/lib/di.dart b/lib/di.dart
index 35da7c751..15a72e80c 100644
--- a/lib/di.dart
+++ b/lib/di.dart
@@ -26,6 +26,7 @@ import 'package:cake_wallet/entities/contact.dart';
import 'package:cake_wallet/entities/contact_record.dart';
import 'package:cake_wallet/entities/exchange_api_mode.dart';
import 'package:cake_wallet/entities/parse_address_from_domain.dart';
+import 'package:cake_wallet/view_model/link_view_model.dart';
import 'package:cake_wallet/tron/tron.dart';
import 'package:cake_wallet/src/screens/transaction_details/rbf_details_page.dart';
import 'package:cw_core/receive_page_option.dart';
@@ -266,6 +267,7 @@ Future setup({
required Box unspentCoinsInfoSource,
required Box anonpayInvoiceInfoSource,
required FlutterSecureStorage secureStorage,
+ required GlobalKey navigatorKey,
}) async {
_walletInfoSource = walletInfoSource;
_nodeSource = nodeSource;
@@ -427,68 +429,89 @@ Future setup({
),
);
- getIt.registerFactory(() {
- return AuthPage(getIt.get(),
+ getIt.registerLazySingleton(() {
+ return LinkViewModel(
+ appStore: getIt.get(),
+ settingsStore: getIt.get(),
+ authenticationStore: getIt.get(),
+ navigatorKey: navigatorKey,
+ );
+ });
+
+ getIt.registerFactory(instanceName: 'login', () {
+ return AuthPage(getIt.get(), closable: false,
onAuthenticationFinished: (isAuthenticated, AuthPageState authPageState) {
if (!isAuthenticated) {
return;
- } else {
- final authStore = getIt.get();
- final appStore = getIt.get();
- final useTotp = appStore.settingsStore.useTOTP2FA;
- final shouldUseTotp2FAToAccessWallets =
- appStore.settingsStore.shouldRequireTOTP2FAForAccessingWallet;
- if (useTotp && shouldUseTotp2FAToAccessWallets) {
- authPageState.close(
- route: Routes.totpAuthCodePage,
- arguments: TotpAuthArgumentsModel(
- isForSetup: false,
- isClosable: false,
- onTotpAuthenticationFinished: (bool isAuthenticatedSuccessfully,
- TotpAuthCodePageState totpAuthPageState) async {
- if (!isAuthenticatedSuccessfully) {
- return;
- }
- if (appStore.wallet != null) {
- authStore.allowed();
- return;
- }
-
- totpAuthPageState.changeProcessText('Loading the wallet');
-
- if (loginError != null) {
- totpAuthPageState.changeProcessText('ERROR: ${loginError.toString()}');
- }
-
- ReactionDisposer? _reaction;
- _reaction = reaction((_) => appStore.wallet, (Object? _) {
- _reaction?.reaction.dispose();
- authStore.allowed();
- });
- },
- ),
- );
- } else {
- if (appStore.wallet != null) {
- authStore.allowed();
- return;
- }
-
- authPageState.changeProcessText('Loading the wallet');
-
- if (loginError != null) {
- authPageState.changeProcessText('ERROR: ${loginError.toString()}');
- }
-
- ReactionDisposer? _reaction;
- _reaction = reaction((_) => appStore.wallet, (Object? _) {
- _reaction?.reaction.dispose();
- authStore.allowed();
- });
- }
}
- }, closable: false);
- }, instanceName: 'login');
+ final authStore = getIt.get();
+ final appStore = getIt.get();
+ final useTotp = appStore.settingsStore.useTOTP2FA;
+ final shouldUseTotp2FAToAccessWallets =
+ appStore.settingsStore.shouldRequireTOTP2FAForAccessingWallet;
+ if (useTotp && shouldUseTotp2FAToAccessWallets) {
+ authPageState.close(
+ route: Routes.totpAuthCodePage,
+ arguments: TotpAuthArgumentsModel(
+ isForSetup: false,
+ isClosable: false,
+ onTotpAuthenticationFinished:
+ (bool isAuthenticatedSuccessfully, TotpAuthCodePageState totpAuthPageState) async {
+ if (!isAuthenticatedSuccessfully) {
+ return;
+ }
+ if (appStore.wallet != null) {
+ authStore.allowed();
+ return;
+ }
+
+ totpAuthPageState.changeProcessText('Loading the wallet');
+
+ if (loginError != null) {
+ totpAuthPageState.changeProcessText('ERROR: ${loginError.toString()}');
+ }
+
+ ReactionDisposer? _reaction;
+ _reaction = reaction((_) => appStore.wallet, (Object? _) {
+ _reaction?.reaction.dispose();
+ authStore.allowed();
+ });
+ },
+ ),
+ );
+ } else {
+ // wallet is already loaded:
+ if (appStore.wallet != null) {
+ // goes to the dashboard:
+ authStore.allowed();
+ // trigger any deep links:
+ final linkViewModel = getIt.get();
+ if (linkViewModel.currentLink != null) {
+ linkViewModel.handleLink();
+ }
+ return;
+ }
+
+ // load the wallet:
+
+ authPageState.changeProcessText('Loading the wallet');
+
+ if (loginError != null) {
+ authPageState.changeProcessText('ERROR: ${loginError.toString()}');
+ }
+
+ ReactionDisposer? _reaction;
+ _reaction = reaction((_) => appStore.wallet, (Object? _) {
+ _reaction?.reaction.dispose();
+ authStore.allowed();
+ final linkViewModel = getIt.get();
+ if (linkViewModel.currentLink != null) {
+ linkViewModel.handleLink();
+ }
+ });
+ }
+ });
+ });
getIt.registerSingleton(BottomSheetServiceImpl());
@@ -851,8 +874,10 @@ Future setup({
tradesStore: getIt.get(),
sendViewModel: getIt.get()));
- getIt.registerFactory(
- () => ExchangePage(getIt.get(), getIt.get()));
+ getIt.registerFactoryParam(
+ (PaymentRequest? paymentRequest, __) {
+ return ExchangePage(getIt.get(), getIt.get(), paymentRequest);
+ });
getIt.registerFactory(() => ExchangeConfirmPage(tradesStore: getIt.get()));
diff --git a/lib/main.dart b/lib/main.dart
index b2e32d7a9..2a4e12236 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -7,6 +7,7 @@ import 'package:cake_wallet/locales/locale.dart';
import 'package:cake_wallet/store/yat/yat_store.dart';
import 'package:cake_wallet/utils/device_info.dart';
import 'package:cake_wallet/utils/exception_handler.dart';
+import 'package:cake_wallet/view_model/link_view_model.dart';
import 'package:cw_core/address_info.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cw_core/hive_type_ids.dart';
@@ -205,18 +206,20 @@ Future initialSetup(
nodes: nodes,
powNodes: powNodes);
await setup(
- walletInfoSource: walletInfoSource,
- nodeSource: nodes,
- powNodeSource: powNodes,
- contactSource: contactSource,
- tradesSource: tradesSource,
- templates: templates,
- exchangeTemplates: exchangeTemplates,
- transactionDescriptionBox: transactionDescriptions,
- ordersSource: ordersSource,
- anonpayInvoiceInfoSource: anonpayInvoiceInfo,
- unspentCoinsInfoSource: unspentCoinsInfoSource,
- secureStorage: secureStorage);
+ walletInfoSource: walletInfoSource,
+ nodeSource: nodes,
+ powNodeSource: powNodes,
+ contactSource: contactSource,
+ tradesSource: tradesSource,
+ templates: templates,
+ exchangeTemplates: exchangeTemplates,
+ transactionDescriptionBox: transactionDescriptions,
+ ordersSource: ordersSource,
+ anonpayInvoiceInfoSource: anonpayInvoiceInfo,
+ unspentCoinsInfoSource: unspentCoinsInfoSource,
+ secureStorage: secureStorage,
+ navigatorKey: navigatorKey,
+ );
await bootstrap(navigatorKey);
monero?.onStartup();
}
@@ -287,6 +290,7 @@ class AppState extends State with SingleTickerProviderStateMixin {
return Observer(builder: (BuildContext context) {
final appStore = getIt.get();
final authService = getIt.get();
+ final linkViewModel = getIt.get();
final settingsStore = appStore.settingsStore;
final statusBarColor = Colors.transparent;
final authenticationStore = getIt.get();
@@ -309,6 +313,7 @@ class AppState extends State with SingleTickerProviderStateMixin {
authenticationStore: authenticationStore,
navigatorKey: navigatorKey,
authService: authService,
+ linkViewModel: linkViewModel,
child: MaterialApp(
navigatorObservers: [routeObserver],
navigatorKey: navigatorKey,
diff --git a/lib/router.dart b/lib/router.dart
index 3032ceb6a..741597731 100644
--- a/lib/router.dart
+++ b/lib/router.dart
@@ -221,7 +221,8 @@ Route createRoute(RouteSettings settings) {
return CupertinoPageRoute(
builder: (_) => getIt.get(
param1: (PinCodeState context, dynamic _) =>
- Navigator.of(context.context).pushNamed(Routes.restoreWalletFromHardwareWallet, arguments: false),
+ Navigator.of(context.context)
+ .pushNamed(Routes.restoreWalletFromHardwareWallet, arguments: false),
),
fullscreenDialog: true,
);
@@ -231,9 +232,9 @@ Route createRoute(RouteSettings settings) {
builder: (_) => ConnectDevicePage(
ConnectDevicePageParams(
walletType: availableWalletTypes.first,
- onConnectDevice: (BuildContext context, _) =>
- Navigator.of(context).pushNamed(Routes.chooseHardwareWalletAccount,
- arguments: [availableWalletTypes.first]),
+ onConnectDevice: (BuildContext context, _) => Navigator.of(context).pushNamed(
+ Routes.chooseHardwareWalletAccount,
+ arguments: [availableWalletTypes.first]),
),
getIt.get(),
));
@@ -243,9 +244,8 @@ Route createRoute(RouteSettings settings) {
param1: (BuildContext context, WalletType type) {
final arguments = ConnectDevicePageParams(
walletType: type,
- onConnectDevice: (BuildContext context, _) =>
- Navigator.of(context).pushNamed(Routes.chooseHardwareWalletAccount,
- arguments: [type]),
+ onConnectDevice: (BuildContext context, _) => Navigator.of(context)
+ .pushNamed(Routes.chooseHardwareWalletAccount, arguments: [type]),
);
Navigator.of(context).pushNamed(Routes.connectDevices, arguments: arguments);
@@ -308,8 +308,7 @@ Route createRoute(RouteSettings settings) {
case Routes.bumpFeePage:
return CupertinoPageRoute(
fullscreenDialog: true,
- builder: (_) =>
- getIt.get(param1: settings.arguments as TransactionInfo));
+ builder: (_) => getIt.get(param1: settings.arguments as TransactionInfo));
case Routes.newSubaddress:
return CupertinoPageRoute(
@@ -461,7 +460,9 @@ Route createRoute(RouteSettings settings) {
case Routes.exchange:
return CupertinoPageRoute(
- fullscreenDialog: true, builder: (_) => getIt.get());
+ fullscreenDialog: true,
+ builder: (_) => getIt.get(param1: settings.arguments as PaymentRequest?),
+ );
case Routes.exchangeTemplate:
return CupertinoPageRoute(builder: (_) => getIt.get());
diff --git a/lib/src/screens/dashboard/pages/cake_features_page.dart b/lib/src/screens/dashboard/pages/cake_features_page.dart
index aa587a5f4..89c0435e1 100644
--- a/lib/src/screens/dashboard/pages/cake_features_page.dart
+++ b/lib/src/screens/dashboard/pages/cake_features_page.dart
@@ -78,6 +78,15 @@ class CakeFeaturesPage extends StatelessWidget {
fit: BoxFit.cover,
),
),
+ const SizedBox(height: 20),
+ DashBoardRoundedCardWidget(
+ title: "NanoGPT",
+ subTitle: S.of(context).nanogpt_subtitle,
+ onTap: () => launchUrl(
+ Uri.https("cake.nano-gpt.com"),
+ mode: LaunchMode.externalApplication,
+ ),
+ ),
if (dashboardViewModel.hasSilentPayments) ...[
SizedBox(height: 10),
DashBoardRoundedCardWidget(
diff --git a/lib/src/screens/dashboard/pages/market_place_page.dart b/lib/src/screens/dashboard/pages/market_place_page.dart
new file mode 100644
index 000000000..d28048844
--- /dev/null
+++ b/lib/src/screens/dashboard/pages/market_place_page.dart
@@ -0,0 +1,119 @@
+import 'package:cake_wallet/routes.dart';
+import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
+import 'package:cake_wallet/src/widgets/dashboard_card_widget.dart';
+import 'package:cake_wallet/utils/show_pop_up.dart';
+import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
+import 'package:cake_wallet/view_model/dashboard/market_place_view_model.dart';
+import 'package:cw_core/wallet_type.dart';
+import 'package:flutter/material.dart';
+import 'package:cake_wallet/generated/i18n.dart';
+import 'package:url_launcher/url_launcher.dart';
+import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart';
+
+class MarketPlacePage extends StatelessWidget {
+ MarketPlacePage({
+ required this.dashboardViewModel,
+ required this.marketPlaceViewModel,
+ });
+
+ final DashboardViewModel dashboardViewModel;
+ final MarketPlaceViewModel marketPlaceViewModel;
+ final _scrollController = ScrollController();
+
+ @override
+ Widget build(BuildContext context) {
+ return Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 10.0),
+ child: RawScrollbar(
+ thumbColor: Colors.white.withOpacity(0.15),
+ radius: Radius.circular(20),
+ thumbVisibility: true,
+ thickness: 2,
+ controller: _scrollController,
+ child: Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 10.0),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ SizedBox(height: 50),
+ Text(
+ S.of(context).market_place,
+ style: TextStyle(
+ fontSize: 24,
+ fontWeight: FontWeight.w500,
+ color: Theme.of(context).extension()!.pageTitleTextColor,
+ ),
+ ),
+ Expanded(
+ child: ListView(
+ controller: _scrollController,
+ children: [
+ // SizedBox(height: 20),
+ // DashBoardRoundedCardWidget(
+ // onTap: () => launchUrl(
+ // Uri.parse("https://cakelabs.com/news/cake-pay-mobile-to-shut-down/"),
+ // mode: LaunchMode.externalApplication,
+ // ),
+ // title: S.of(context).cake_pay_title,
+ // subTitle: S.of(context).cake_pay_subtitle,
+ // ),
+ SizedBox(height: 20),
+ DashBoardRoundedCardWidget(
+ title: S.of(context).cake_pay_web_cards_title,
+ subTitle: S.of(context).cake_pay_web_cards_subtitle,
+ onTap: () => _launchMarketPlaceUrl("buy.cakepay.com"),
+ ),
+ const SizedBox(height: 20),
+ DashBoardRoundedCardWidget(
+ title: "NanoGPT",
+ subTitle: S.of(context).nanogpt_subtitle,
+ onTap: () => _launchMarketPlaceUrl("cake.nano-gpt.com"),
+ ),
+ ],
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+
+ void _launchMarketPlaceUrl(String url) async {
+ try {
+ launchUrl(
+ Uri.https(url),
+ mode: LaunchMode.externalApplication,
+ );
+ } catch (e) {
+ print(e);
+ }
+ }
+
+ // TODO: Remove ionia flow/files if we will discard it
+ void _navigatorToGiftCardsPage(BuildContext context) {
+ final walletType = dashboardViewModel.type;
+
+ switch (walletType) {
+ case WalletType.haven:
+ showPopUp(
+ context: context,
+ builder: (BuildContext context) {
+ return AlertWithOneAction(
+ alertTitle: S.of(context).error,
+ alertContent: S.of(context).gift_cards_unavailable,
+ buttonText: S.of(context).ok,
+ buttonAction: () => Navigator.of(context).pop());
+ });
+ break;
+ default:
+ marketPlaceViewModel.isIoniaUserAuthenticated().then((value) {
+ if (value) {
+ Navigator.pushNamed(context, Routes.ioniaManageCardsPage);
+ return;
+ }
+ Navigator.of(context).pushNamed(Routes.ioniaWelcomePage);
+ });
+ }
+ }
+}
diff --git a/lib/src/screens/exchange/exchange_page.dart b/lib/src/screens/exchange/exchange_page.dart
index c4e4aa199..e2d424fa0 100644
--- a/lib/src/screens/exchange/exchange_page.dart
+++ b/lib/src/screens/exchange/exchange_page.dart
@@ -10,6 +10,7 @@ import 'package:cake_wallet/src/widgets/add_template_button.dart';
import 'package:cake_wallet/themes/extensions/send_page_theme.dart';
import 'package:cake_wallet/themes/theme_base.dart';
import 'package:cake_wallet/utils/debounce.dart';
+import 'package:cake_wallet/utils/payment_request.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cw_core/sync_status.dart';
import 'package:cw_core/wallet_type.dart';
@@ -43,7 +44,7 @@ import 'package:cake_wallet/src/screens/exchange/widgets/present_provider_picker
import 'package:cake_wallet/src/screens/dashboard/widgets/sync_indicator_icon.dart';
class ExchangePage extends BasePage {
- ExchangePage(this.exchangeViewModel, this.authService) {
+ ExchangePage(this.exchangeViewModel, this.authService, this.initialPaymentRequest) {
depositWalletName = exchangeViewModel.depositCurrency == CryptoCurrency.xmr
? exchangeViewModel.wallet.name
: null;
@@ -54,6 +55,7 @@ class ExchangePage extends BasePage {
final ExchangeViewModel exchangeViewModel;
final AuthService authService;
+ final PaymentRequest? initialPaymentRequest;
final depositKey = GlobalKey();
final receiveKey = GlobalKey();
final _formKey = GlobalKey();
@@ -543,6 +545,12 @@ class ExchangePage extends BasePage {
// amount: depositAmountController.text);
});
+ if (initialPaymentRequest != null) {
+ exchangeViewModel.receiveCurrency = CryptoCurrency.fromString(initialPaymentRequest!.scheme);
+ exchangeViewModel.depositAmount = initialPaymentRequest!.amount;
+ exchangeViewModel.receiveAddress = initialPaymentRequest!.address;
+ }
+
_isReactionsSet = true;
}
diff --git a/lib/src/screens/root/root.dart b/lib/src/screens/root/root.dart
index e3472f510..afdd14865 100644
--- a/lib/src/screens/root/root.dart
+++ b/lib/src/screens/root/root.dart
@@ -5,6 +5,7 @@ import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/reactions/wallet_connect.dart';
import 'package:cake_wallet/utils/device_info.dart';
import 'package:cake_wallet/utils/payment_request.dart';
+import 'package:cake_wallet/view_model/link_view_model.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/routes.dart';
@@ -25,6 +26,7 @@ class Root extends StatefulWidget {
required this.child,
required this.navigatorKey,
required this.authService,
+ required this.linkViewModel,
}) : super(key: key);
final AuthenticationStore authenticationStore;
@@ -32,6 +34,7 @@ class Root extends StatefulWidget {
final GlobalKey navigatorKey;
final AuthService authService;
final Widget child;
+ final LinkViewModel linkViewModel;
@override
RootState createState() => RootState();
@@ -53,7 +56,6 @@ class RootState extends State with WidgetsBindingObserver {
StreamSubscription? stream;
ReactionDisposer? _walletReactionDisposer;
ReactionDisposer? _deepLinksReactionDisposer;
- Uri? launchUri;
@override
void initState() {
@@ -98,7 +100,7 @@ class RootState extends State with WidgetsBindingObserver {
void handleDeepLinking(Uri? uri) async {
if (uri == null || !mounted) return;
- launchUri = uri;
+ widget.linkViewModel.currentLink = uri;
bool requireAuth = await widget.authService.requireAuth();
@@ -112,7 +114,7 @@ class RootState extends State with WidgetsBindingObserver {
(AuthenticationState state) {
if (state == AuthenticationState.allowed) {
if (widget.appStore.wallet == null) {
- waitForWalletInstance(context, launchUri!);
+ waitForWalletInstance(context);
} else {
_navigateToDeepLinkScreen();
}
@@ -150,6 +152,8 @@ class RootState extends State with WidgetsBindingObserver {
@override
Widget build(BuildContext context) {
+ // this only happens when the app has been in the background for some time
+ // this does NOT trigger when the app is started from the "closed" state!
if (_isInactive && !_postFrameCallback && _requestAuth) {
_postFrameCallback = true;
WidgetsBinding.instance.addPostFrameCallback((_) {
@@ -158,40 +162,38 @@ class RootState extends State with WidgetsBindingObserver {
arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) {
if (!isAuthenticatedSuccessfully) {
return;
+ }
+ final useTotp = widget.appStore.settingsStore.useTOTP2FA;
+ final shouldUseTotp2FAToAccessWallets =
+ widget.appStore.settingsStore.shouldRequireTOTP2FAForAccessingWallet;
+ if (useTotp && shouldUseTotp2FAToAccessWallets) {
+ _reset();
+ auth.close(
+ route: Routes.totpAuthCodePage,
+ arguments: TotpAuthArgumentsModel(
+ onTotpAuthenticationFinished:
+ (bool isAuthenticatedSuccessfully, TotpAuthCodePageState totpAuth) {
+ if (!isAuthenticatedSuccessfully) {
+ return;
+ }
+ _reset();
+ totpAuth.close(
+ route: widget.linkViewModel.getRouteToGo(),
+ arguments: widget.linkViewModel.getRouteArgs(),
+ );
+ widget.linkViewModel.currentLink = null;
+ },
+ isForSetup: false,
+ isClosable: false,
+ ),
+ );
} else {
- final useTotp = widget.appStore.settingsStore.useTOTP2FA;
- final shouldUseTotp2FAToAccessWallets =
- widget.appStore.settingsStore.shouldRequireTOTP2FAForAccessingWallet;
- if (useTotp && shouldUseTotp2FAToAccessWallets) {
- _reset();
- auth.close(
- route: Routes.totpAuthCodePage,
- arguments: TotpAuthArgumentsModel(
- onTotpAuthenticationFinished:
- (bool isAuthenticatedSuccessfully, TotpAuthCodePageState totpAuth) {
- if (!isAuthenticatedSuccessfully) {
- return;
- }
- _reset();
- totpAuth.close(
- route: _getRouteToGo(),
- arguments:
- isWalletConnectLink ? launchUri : PaymentRequest.fromUri(launchUri),
- );
- launchUri = null;
- },
- isForSetup: false,
- isClosable: false,
- ),
- );
- } else {
- _reset();
- auth.close(
- route: _getRouteToGo(),
- arguments: isWalletConnectLink ? launchUri : PaymentRequest.fromUri(launchUri),
- );
- launchUri = null;
- }
+ _reset();
+ auth.close(
+ route: widget.linkViewModel.getRouteToGo(),
+ arguments: widget.linkViewModel.getRouteArgs(),
+ );
+ widget.linkViewModel.currentLink = null;
}
},
);
@@ -216,36 +218,7 @@ class RootState extends State with WidgetsBindingObserver {
_isInactiveController.add(value);
}
- bool _isValidPaymentUri() => launchUri?.path.isNotEmpty ?? false;
-
- bool get isWalletConnectLink => launchUri?.authority == 'wc';
-
- String? _getRouteToGo() {
- if (isWalletConnectLink) {
- if (isEVMCompatibleChain(widget.appStore.wallet!.type)) {
- _nonETHWalletErrorToast(S.current.switchToEVMCompatibleWallet);
- return null;
- }
- return Routes.walletConnectConnectionsListing;
- } else if (_isValidPaymentUri()) {
- return Routes.send;
- } else {
- return null;
- }
- }
-
- Future _nonETHWalletErrorToast(String message) async {
- Fluttertoast.showToast(
- msg: message,
- toastLength: Toast.LENGTH_LONG,
- gravity: ToastGravity.SNACKBAR,
- backgroundColor: Colors.black,
- textColor: Colors.white,
- fontSize: 16.0,
- );
- }
-
- void waitForWalletInstance(BuildContext context, Uri tempLaunchUri) {
+ void waitForWalletInstance(BuildContext context) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (context.mounted) {
_walletReactionDisposer = reaction(
@@ -263,14 +236,6 @@ class RootState extends State with WidgetsBindingObserver {
}
void _navigateToDeepLinkScreen() {
- if (_getRouteToGo() != null) {
- WidgetsBinding.instance.addPostFrameCallback((_) {
- widget.navigatorKey.currentState?.pushNamed(
- _getRouteToGo()!,
- arguments: isWalletConnectLink ? launchUri : PaymentRequest.fromUri(launchUri),
- );
- launchUri = null;
- });
- }
+ widget.linkViewModel.handleLink();
}
}
diff --git a/lib/src/screens/send/send_page.dart b/lib/src/screens/send/send_page.dart
index c8f3aa320..bdfc0656f 100644
--- a/lib/src/screens/send/send_page.dart
+++ b/lib/src/screens/send/send_page.dart
@@ -36,6 +36,8 @@ import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:mobx/mobx.dart';
import 'package:smooth_page_indicator/smooth_page_indicator.dart';
+import 'package:cw_core/crypto_currency.dart';
+import 'package:url_launcher/url_launcher.dart';
class SendPage extends BasePage {
SendPage({
@@ -425,12 +427,10 @@ class SendPage extends BasePage {
}
reaction((_) => sendViewModel.state, (ExecutionState state) {
-
if (dialogContext != null && dialogContext?.mounted == true) {
Navigator.of(dialogContext!).pop();
}
-
if (state is FailureState) {
WidgetsBinding.instance.addPostFrameCallback((_) {
showPopUp(
@@ -465,10 +465,10 @@ class SendPage extends BasePage {
outputs: sendViewModel.outputs,
rightButtonText: S.of(_dialogContext).send,
leftButtonText: S.of(_dialogContext).cancel,
- actionRightButton: () {
+ actionRightButton: () async {
Navigator.of(_dialogContext).pop();
sendViewModel.commitTransaction();
- showPopUp(
+ await showPopUp(
context: context,
builder: (BuildContext _dialogContext) {
return Observer(builder: (_) {
@@ -493,7 +493,7 @@ class SendPage extends BasePage {
? '\n${S.of(_dialogContext).add_contact_to_address_book}'
: '';
- final alertContent =
+ String alertContent =
"$successMessage$waitMessage$newContactMessage";
if (newContactAddress != null) {
@@ -516,6 +516,10 @@ class SendPage extends BasePage {
newContactAddress = null;
});
} else {
+ if (initialPaymentRequest?.callbackMessage?.isNotEmpty ??
+ false) {
+ alertContent = initialPaymentRequest!.callbackMessage!;
+ }
return AlertWithOneAction(
alertTitle: '',
alertContent: alertContent,
@@ -530,6 +534,20 @@ class SendPage extends BasePage {
return Offstage();
});
});
+ if (state is TransactionCommitted) {
+ if (initialPaymentRequest?.callbackUrl?.isNotEmpty ?? false) {
+ // wait a second so it's not as jarring:
+ await Future.delayed(Duration(seconds: 1));
+ try {
+ launchUrl(
+ Uri.parse(initialPaymentRequest!.callbackUrl!),
+ mode: LaunchMode.externalApplication,
+ );
+ } catch (e) {
+ print(e);
+ }
+ }
+ }
},
actionLeftButton: () => Navigator.of(_dialogContext).pop());
});
diff --git a/lib/src/screens/subaddress/address_edit_or_create_page.dart b/lib/src/screens/subaddress/address_edit_or_create_page.dart
index 750af846e..e067c78d0 100644
--- a/lib/src/screens/subaddress/address_edit_or_create_page.dart
+++ b/lib/src/screens/subaddress/address_edit_or_create_page.dart
@@ -80,4 +80,4 @@ class AddressEditOrCreatePage extends BasePage {
_isEffectsInstalled = true;
}
-}
+}
\ No newline at end of file
diff --git a/lib/utils/payment_request.dart b/lib/utils/payment_request.dart
index 00093b413..fe0ecf605 100644
--- a/lib/utils/payment_request.dart
+++ b/lib/utils/payment_request.dart
@@ -1,19 +1,29 @@
+import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/nano/nano.dart';
class PaymentRequest {
- PaymentRequest(this.address, this.amount, this.note, this.scheme);
+ PaymentRequest(this.address, this.amount, this.note, this.scheme, {this.callbackUrl, this.callbackMessage});
factory PaymentRequest.fromUri(Uri? uri) {
var address = "";
var amount = "";
var note = "";
var scheme = "";
+ String? callbackUrl;
+ String? callbackMessage;
if (uri != null) {
- address = uri.path;
+ address = uri.queryParameters['address'] ?? uri.path;
amount = uri.queryParameters['tx_amount'] ?? uri.queryParameters['amount'] ?? "";
note = uri.queryParameters['tx_description'] ?? uri.queryParameters['message'] ?? "";
scheme = uri.scheme;
+ callbackUrl = uri.queryParameters['callback'];
+ callbackMessage = uri.queryParameters['callbackMessage'];
+ }
+
+ if (scheme == "nano-gpt") {
+ // treat as nano so filling out the address works:
+ scheme = "nano";
}
if (nano != null) {
@@ -26,11 +36,20 @@ class PaymentRequest {
}
}
- return PaymentRequest(address, amount, note, scheme);
+ return PaymentRequest(
+ address,
+ amount,
+ note,
+ scheme,
+ callbackUrl: callbackUrl,
+ callbackMessage: callbackMessage,
+ );
}
final String address;
final String amount;
final String note;
final String scheme;
+ final String? callbackUrl;
+ final String? callbackMessage;
}
diff --git a/lib/view_model/link_view_model.dart b/lib/view_model/link_view_model.dart
new file mode 100644
index 000000000..714b57e53
--- /dev/null
+++ b/lib/view_model/link_view_model.dart
@@ -0,0 +1,118 @@
+import 'package:cake_wallet/generated/i18n.dart';
+import 'package:cake_wallet/reactions/wallet_connect.dart';
+import 'package:cake_wallet/routes.dart';
+import 'package:cake_wallet/store/app_store.dart';
+import 'package:cake_wallet/store/authentication_store.dart';
+import 'package:cake_wallet/store/settings_store.dart';
+import 'package:cake_wallet/utils/payment_request.dart';
+import 'package:flutter/material.dart';
+import 'package:fluttertoast/fluttertoast.dart';
+import 'package:mobx/mobx.dart';
+
+part 'link_view_model.g.dart';
+
+class LinkViewModel = LinkViewModelBase with _$LinkViewModel;
+
+abstract class LinkViewModelBase with Store {
+ LinkViewModelBase({
+ required this.settingsStore,
+ required this.appStore,
+ required this.authenticationStore,
+ required this.navigatorKey,
+ }) {}
+
+ final SettingsStore settingsStore;
+ final AppStore appStore;
+ final AuthenticationStore authenticationStore;
+ final GlobalKey navigatorKey;
+ Uri? currentLink;
+
+ bool get _isValidPaymentUri => currentLink?.path.isNotEmpty ?? false;
+ bool get isWalletConnectLink => currentLink?.authority == 'wc';
+ bool get isNanoGptLink => currentLink?.scheme == 'nano-gpt';
+
+ String? getRouteToGo() {
+ if (isWalletConnectLink) {
+ if (!isEVMCompatibleChain(appStore.wallet!.type)) {
+ _errorToast(S.current.switchToEVMCompatibleWallet);
+ return null;
+ }
+ return Routes.walletConnectConnectionsListing;
+ }
+
+ if (authenticationStore.state == AuthenticationState.uninitialized) {
+ return null;
+ }
+
+ if (isNanoGptLink) {
+ switch (currentLink?.authority ?? '') {
+ case "exchange":
+ return Routes.exchange;
+ case "send":
+ return Routes.send;
+ case "buy":
+ return Routes.buySellPage;
+ }
+ }
+
+ if (_isValidPaymentUri) {
+ return Routes.send;
+ }
+
+ return null;
+ }
+
+ dynamic getRouteArgs() {
+ if (isWalletConnectLink) {
+ return currentLink;
+ }
+
+ if (isNanoGptLink) {
+ switch (currentLink?.authority ?? '') {
+ case "exchange":
+ case "send":
+ return PaymentRequest.fromUri(currentLink);
+ case "buy":
+ return true;
+ }
+ }
+
+ if (_isValidPaymentUri) {
+ return PaymentRequest.fromUri(currentLink);
+ }
+
+ return null;
+ }
+
+ Future _errorToast(String message, {double fontSize = 16}) async {
+ Fluttertoast.showToast(
+ msg: message,
+ toastLength: Toast.LENGTH_LONG,
+ gravity: ToastGravity.SNACKBAR,
+ backgroundColor: Colors.black,
+ textColor: Colors.white,
+ fontSize: fontSize,
+ );
+ }
+
+ Future handleLink() async {
+ String? route = getRouteToGo();
+ dynamic args = getRouteArgs();
+ if (route != null) {
+ if (appStore.wallet == null) {
+ return;
+ }
+
+ if (isNanoGptLink) {
+ if (route == Routes.buySellPage || route == Routes.exchange) {
+ await _errorToast(S.current.nano_gpt_thanks_message, fontSize: 14);
+ }
+ }
+ currentLink = null;
+ navigatorKey.currentState?.pushNamed(
+ route,
+ arguments: args,
+ );
+ }
+ }
+}
diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb
index 551e61706..8f6ae476a 100644
--- a/res/values/strings_ar.arb
+++ b/res/values/strings_ar.arb
@@ -371,6 +371,8 @@
"moonpay_alert_text": "يجب أن تكون قيمة المبلغ أكبر من أو تساوي ${minAmount} ${fiatCurrency}",
"more_options": "المزيد من الخيارات",
"name": "ﻢﺳﺍ",
+ "nano_gpt_thanks_message": "شكرا لاستخدام nanogpt! تذكر أن تعود إلى المتصفح بعد اكتمال معاملتك!",
+ "nanogpt_subtitle": "جميع النماذج الأحدث (GPT-4 ، Claude). \\ nno اشتراك ، ادفع مع Crypto.",
"nano_current_rep": "الممثل الحالي",
"nano_pick_new_rep": "اختر ممثلًا جديدًا",
"narrow": "ضيق",
diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb
index 01daa382e..c8cf62be2 100644
--- a/res/values/strings_bg.arb
+++ b/res/values/strings_bg.arb
@@ -371,6 +371,8 @@
"moonpay_alert_text": "Сумата трябва да бъде най-малко ${minAmount} ${fiatCurrency}",
"more_options": "Още настройки",
"name": "Име",
+ "nano_gpt_thanks_message": "Благодаря, че използвахте Nanogpt! Не забравяйте да се върнете обратно към браузъра, след като транзакцията ви приключи!",
+ "nanogpt_subtitle": "Всички най-нови модели (GPT-4, Claude). \\ Nno абонамент, платете с Crypto.",
"nano_current_rep": "Настоящ представител",
"nano_pick_new_rep": "Изберете нов представител",
"narrow": "Тесен",
diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb
index 4aed0eaea..524bc5913 100644
--- a/res/values/strings_cs.arb
+++ b/res/values/strings_cs.arb
@@ -371,6 +371,8 @@
"moonpay_alert_text": "Částka musí být větší nebo rovna ${minAmount} ${fiatCurrency}",
"more_options": "Více možností",
"name": "název",
+ "nano_gpt_thanks_message": "Děkujeme za používání Nanogpt! Nezapomeňte se po dokončení transakce vydat zpět do prohlížeče!",
+ "nanogpt_subtitle": "Všechny nejnovější modely (GPT-4, Claude). \\ Nno předplatné, plaťte krypto.",
"nano_current_rep": "Současný zástupce",
"nano_pick_new_rep": "Vyberte nového zástupce",
"narrow": "Úzký",
diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb
index 5ae922ca2..a9987f3a8 100644
--- a/res/values/strings_de.arb
+++ b/res/values/strings_de.arb
@@ -371,6 +371,8 @@
"moonpay_alert_text": "Der Wert des Betrags muss größer oder gleich ${minAmount} ${fiatCurrency} sein",
"more_options": "Weitere Optionen",
"name": "Name",
+ "nano_gpt_thanks_message": "Danke, dass du Nanogpt benutzt hast! Denken Sie daran, nach Abschluss Ihrer Transaktion zurück zum Browser zu gehen!",
+ "nanogpt_subtitle": "Alle neuesten Modelle (GPT-4, Claude).",
"nano_current_rep": "Aktueller Vertreter",
"nano_pick_new_rep": "Wählen Sie einen neuen Vertreter aus",
"narrow": "Eng",
diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb
index 8073310b0..290130a62 100644
--- a/res/values/strings_en.arb
+++ b/res/values/strings_en.arb
@@ -371,6 +371,8 @@
"moonpay_alert_text": "Value of the amount must be more or equal to ${minAmount} ${fiatCurrency}",
"more_options": "More Options",
"name": "Name",
+ "nano_gpt_thanks_message": "Thanks for using NanoGPT! Remember to head back to the browser after your transaction completes!",
+ "nanogpt_subtitle": "All the newest models (GPT-4, Claude).\\nNo subscription, pay with crypto.",
"nano_current_rep": "Current Representative",
"nano_pick_new_rep": "Pick a new representative",
"narrow": "Narrow",
diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb
index abe596b69..56e496efb 100644
--- a/res/values/strings_es.arb
+++ b/res/values/strings_es.arb
@@ -371,6 +371,8 @@
"moonpay_alert_text": "El valor de la cantidad debe ser mayor o igual a ${minAmount} ${fiatCurrency}",
"more_options": "Más Opciones",
"name": "Nombre",
+ "nano_gpt_thanks_message": "¡Gracias por usar nanogpt! ¡Recuerde regresar al navegador después de que se complete su transacción!",
+ "nanogpt_subtitle": "Todos los modelos más nuevos (GPT-4, Claude). \\ Nno suscripción, pague con cripto.",
"nano_current_rep": "Representante actual",
"nano_pick_new_rep": "Elija un nuevo representante",
"narrow": "Angosto",
diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb
index 0650cd36a..2d538bcdb 100644
--- a/res/values/strings_fr.arb
+++ b/res/values/strings_fr.arb
@@ -371,6 +371,8 @@
"moonpay_alert_text": "Le montant doit être au moins égal à ${minAmount} ${fiatCurrency}",
"more_options": "Plus d'options",
"name": "Nom",
+ "nano_gpt_thanks_message": "Merci d'avoir utilisé Nanogpt! N'oubliez pas de retourner au navigateur une fois votre transaction terminée!",
+ "nanogpt_subtitle": "Tous les modèles les plus récents (GPT-4, Claude). \\ NNO abonnement, payez avec crypto.",
"nano_current_rep": "Représentant actuel",
"nano_pick_new_rep": "Choisissez un nouveau représentant",
"narrow": "Étroit",
diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb
index ab67d8eb2..72d70d796 100644
--- a/res/values/strings_ha.arb
+++ b/res/values/strings_ha.arb
@@ -371,6 +371,8 @@
"moonpay_alert_text": "Darajar adadin dole ne ya zama fiye ko daidai da ${minAmount} ${fiatCurrency}",
"more_options": "Ƙarin Zaɓuɓɓuka",
"name": "Suna",
+ "nano_gpt_thanks_message": "Na gode da amfani da Nanogpt! Ka tuna da komawa zuwa mai bincike bayan ma'amalar ka ta cika!",
+ "nanogpt_subtitle": "Duk sabbin samfuran (GPT-4, CLODE). \\ NNO biyan kuɗi, biya tare da crypto.",
"nano_current_rep": "Wakilin Yanzu",
"nano_pick_new_rep": "Dauki sabon wakili",
"narrow": "kunkuntar",
diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb
index 13df4ab39..3bb8cd5b8 100644
--- a/res/values/strings_hi.arb
+++ b/res/values/strings_hi.arb
@@ -371,6 +371,8 @@
"moonpay_alert_text": "राशि का मूल्य अधिक है या करने के लिए बराबर होना चाहिए ${minAmount} ${fiatCurrency}",
"more_options": "और विकल्प",
"name": "नाम",
+ "nano_gpt_thanks_message": "Nanogpt का उपयोग करने के लिए धन्यवाद! अपने लेन -देन के पूरा होने के बाद ब्राउज़र पर वापस जाना याद रखें!",
+ "nanogpt_subtitle": "सभी नवीनतम मॉडल (GPT-4, क्लाउड)। \\ nno सदस्यता, क्रिप्टो के साथ भुगतान करें।",
"nano_current_rep": "वर्तमान प्रतिनिधि",
"nano_pick_new_rep": "एक नया प्रतिनिधि चुनें",
"narrow": "सँकरा",
diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb
index 3cc08e87d..7ee91495c 100644
--- a/res/values/strings_hr.arb
+++ b/res/values/strings_hr.arb
@@ -371,6 +371,8 @@
"moonpay_alert_text": "Vrijednost iznosa mora biti veća ili jednaka ${minAmount} ${fiatCurrency}",
"more_options": "Više opcija",
"name": "Ime",
+ "nano_gpt_thanks_message": "Hvala što ste koristili nanogpt! Ne zaboravite da se vratite u preglednik nakon što vam se transakcija završi!",
+ "nanogpt_subtitle": "Svi najnoviji modeli (GPT-4, Claude). \\ NNO pretplata, plaćajte kripto.",
"nano_current_rep": "Trenutni predstavnik",
"nano_pick_new_rep": "Odaberite novog predstavnika",
"narrow": "Usko",
diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb
index 778f0c0ec..d7a1ffece 100644
--- a/res/values/strings_id.arb
+++ b/res/values/strings_id.arb
@@ -371,6 +371,8 @@
"moonpay_alert_text": "Nilai jumlah harus lebih atau sama dengan ${minAmount} ${fiatCurrency}",
"more_options": "Opsi Lainnya",
"name": "Nama",
+ "nano_gpt_thanks_message": "Terima kasih telah menggunakan Nanogpt! Ingatlah untuk kembali ke browser setelah transaksi Anda selesai!",
+ "nanogpt_subtitle": "Semua model terbaru (GPT-4, Claude). \\ Nno langganan, bayar dengan crypto.",
"nano_current_rep": "Perwakilan saat ini",
"nano_pick_new_rep": "Pilih perwakilan baru",
"narrow": "Sempit",
diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb
index d0fdfe4e1..190ae23dd 100644
--- a/res/values/strings_it.arb
+++ b/res/values/strings_it.arb
@@ -372,6 +372,8 @@
"moonpay_alert_text": "Il valore dell'importo deve essere maggiore o uguale a ${minAmount} ${fiatCurrency}",
"more_options": "Altre opzioni",
"name": "Nome",
+ "nano_gpt_thanks_message": "Grazie per aver usato il nanogpt! Ricorda di tornare al browser dopo il completamento della transazione!",
+ "nanogpt_subtitle": "Tutti i modelli più recenti (GPT-4, Claude). Abbonamento nno, paga con cripto.",
"nano_current_rep": "Rappresentante attuale",
"nano_pick_new_rep": "Scegli un nuovo rappresentante",
"narrow": "Stretto",
diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb
index 211e32cb5..2cfa71e31 100644
--- a/res/values/strings_ja.arb
+++ b/res/values/strings_ja.arb
@@ -372,6 +372,8 @@
"moonpay_alert_text": "金額の値は以上でなければなりません ${minAmount} ${fiatCurrency}",
"more_options": "その他のオプション",
"name": "名前",
+ "nano_gpt_thanks_message": "NanoGptを使用してくれてありがとう!トランザクションが完了したら、ブラウザに戻ることを忘れないでください!",
+ "nanogpt_subtitle": "すべての最新モデル(GPT-4、Claude)。\\ nnoサブスクリプション、暗号で支払います。",
"nano_current_rep": "現在の代表",
"nano_pick_new_rep": "新しい代表者を選びます",
"narrow": "狭い",
diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb
index c305acd75..42b99189b 100644
--- a/res/values/strings_ko.arb
+++ b/res/values/strings_ko.arb
@@ -371,6 +371,8 @@
"moonpay_alert_text": "금액은 다음보다 크거나 같아야합니다 ${minAmount} ${fiatCurrency}",
"more_options": "추가 옵션",
"name": "이름",
+ "nano_gpt_thanks_message": "Nanogpt를 사용해 주셔서 감사합니다! 거래가 완료된 후 브라우저로 돌아가는 것을 잊지 마십시오!",
+ "nanogpt_subtitle": "모든 최신 모델 (GPT-4, Claude). \\ nno 구독, Crypto로 지불하십시오.",
"nano_current_rep": "현재 대표",
"nano_pick_new_rep": "새로운 담당자를 선택하십시오",
"narrow": "좁은",
diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb
index 9dd8b170f..f9163ce05 100644
--- a/res/values/strings_my.arb
+++ b/res/values/strings_my.arb
@@ -371,6 +371,8 @@
"moonpay_alert_text": "ပမာဏ၏တန်ဖိုးသည် ${minAmount} ${fiatCurrency} နှင့် ပိုနေရမည်",
"more_options": "နောက်ထပ် ရွေးချယ်စရာများ",
"name": "နာမည်",
+ "nano_gpt_thanks_message": "nanogpt ကိုသုံးပြီးကျေးဇူးတင်ပါတယ် သင်၏ငွေပေးငွေယူပြီးနောက် browser သို့ပြန်သွားရန်သတိရပါ။",
+ "nanogpt_subtitle": "အားလုံးနောက်ဆုံးပေါ်မော်ဒယ်များ (GPT-4, Claude) ။ \\ nno subscription, crypto နှင့်အတူပေးဆောင်။",
"nano_current_rep": "လက်ရှိကိုယ်စားလှယ်",
"nano_pick_new_rep": "အသစ်တစ်ခုကိုရွေးပါ",
"narrow": "ကျဉ်းသော",
diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb
index 1b7ab4217..6dd3b3573 100644
--- a/res/values/strings_nl.arb
+++ b/res/values/strings_nl.arb
@@ -371,6 +371,8 @@
"moonpay_alert_text": "Waarde van het bedrag moet meer of gelijk zijn aan ${minAmount} ${fiatCurrency}",
"more_options": "Meer opties",
"name": "Naam",
+ "nano_gpt_thanks_message": "Bedankt voor het gebruik van Nanogpt! Vergeet niet om terug te gaan naar de browser nadat uw transactie is voltooid!",
+ "nanogpt_subtitle": "Alle nieuwste modellen (GPT-4, Claude). \\ Nno-abonnement, betalen met crypto.",
"nano_current_rep": "Huidige vertegenwoordiger",
"nano_pick_new_rep": "Kies een nieuwe vertegenwoordiger",
"narrow": "Smal",
diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb
index 8c00c7209..7f50a204d 100644
--- a/res/values/strings_pl.arb
+++ b/res/values/strings_pl.arb
@@ -371,6 +371,8 @@
"moonpay_alert_text": "Wartość kwoty musi być większa lub równa ${minAmount} ${fiatCurrency}",
"more_options": "Więcej opcji",
"name": "Nazwa",
+ "nano_gpt_thanks_message": "Dzięki za użycie Nanogpt! Pamiętaj, aby wrócić do przeglądarki po zakończeniu transakcji!",
+ "nanogpt_subtitle": "Wszystkie najnowsze modele (GPT-4, Claude). \\ Nno subskrypcja, płacą za pomocą kryptografii.",
"nano_current_rep": "Obecny przedstawiciel",
"nano_pick_new_rep": "Wybierz nowego przedstawiciela",
"narrow": "Wąski",
diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb
index 70a9b706a..f71854b5d 100644
--- a/res/values/strings_pt.arb
+++ b/res/values/strings_pt.arb
@@ -372,6 +372,8 @@
"moonpay_alert_text": "O valor do montante deve ser maior ou igual a ${minAmount} ${fiatCurrency}",
"more_options": "Mais opções",
"name": "Nome",
+ "nano_gpt_thanks_message": "Obrigado por usar o Nanogpt! Lembre -se de voltar para o navegador após a conclusão da transação!",
+ "nanogpt_subtitle": "Todos os modelos mais recentes (GPT-4, Claude). \\ Nno assinatura, pagam com criptografia.",
"nano_current_rep": "Representante atual",
"nano_pick_new_rep": "Escolha um novo representante",
"narrow": "Estreito",
diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb
index 2d8a5da8c..990543476 100644
--- a/res/values/strings_ru.arb
+++ b/res/values/strings_ru.arb
@@ -371,6 +371,8 @@
"moonpay_alert_text": "Сумма должна быть больше или равна ${minAmount} ${fiatCurrency}",
"more_options": "Дополнительные параметры",
"name": "Имя",
+ "nano_gpt_thanks_message": "Спасибо за использование Nanogpt! Не забудьте вернуться в браузер после завершения транзакции!",
+ "nanogpt_subtitle": "Все новейшие модели (GPT-4, Claude). \\ Nno Подписка, платите с крипто.",
"nano_current_rep": "Нынешний представитель",
"nano_pick_new_rep": "Выберите нового представителя",
"narrow": "Узкий",
diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb
index d8f20c357..06e2fae14 100644
--- a/res/values/strings_th.arb
+++ b/res/values/strings_th.arb
@@ -371,6 +371,8 @@
"moonpay_alert_text": "มูลค่าของจำนวนต้องมากกว่าหรือเท่ากับ ${minAmount} ${fiatCurrency}",
"more_options": "ตัวเลือกเพิ่มเติม",
"name": "ชื่อ",
+ "nano_gpt_thanks_message": "ขอบคุณที่ใช้ Nanogpt! อย่าลืมกลับไปที่เบราว์เซอร์หลังจากการทำธุรกรรมของคุณเสร็จสิ้น!",
+ "nanogpt_subtitle": "รุ่นใหม่ล่าสุดทั้งหมด (GPT-4, Claude). การสมัครสมาชิก \\ nno, จ่ายด้วย crypto",
"nano_current_rep": "ตัวแทนปัจจุบัน",
"nano_pick_new_rep": "เลือกตัวแทนใหม่",
"narrow": "แคบ",
diff --git a/res/values/strings_tl.arb b/res/values/strings_tl.arb
index 068faa0f9..6fa6ed59c 100644
--- a/res/values/strings_tl.arb
+++ b/res/values/strings_tl.arb
@@ -371,6 +371,8 @@
"moonpay_alert_text": "Ang halaga ng halaga ay dapat na higit pa o katumbas ng ${minAmount} ${fiatCurrency}",
"more_options": "Higit pang mga pagpipilian",
"name": "Pangalan",
+ "nano_gpt_thanks_message": "Salamat sa paggamit ng nanogpt! Tandaan na bumalik sa browser matapos makumpleto ang iyong transaksyon!",
+ "nanogpt_subtitle": "Ang lahat ng mga pinakabagong modelo (GPT-4, Claude). \\ Nno subscription, magbayad gamit ang crypto.",
"nano_current_rep": "Kasalukuyang kinatawan",
"nano_pick_new_rep": "Pumili ng isang bagong kinatawan",
"narrow": "Makitid",
diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb
index fc7941f4d..4a69a6aee 100644
--- a/res/values/strings_tr.arb
+++ b/res/values/strings_tr.arb
@@ -371,6 +371,8 @@
"moonpay_alert_text": "Tutar ${minAmount} ${fiatCurrency} miktarına eşit veya daha fazla olmalıdır",
"more_options": "Daha Fazla Seçenek",
"name": "İsim",
+ "nano_gpt_thanks_message": "Nanogpt kullandığınız için teşekkürler! İşleminiz tamamlandıktan sonra tarayıcıya geri dönmeyi unutmayın!",
+ "nanogpt_subtitle": "En yeni modeller (GPT-4, Claude). \\ Nno aboneliği, kripto ile ödeme yapın.",
"nano_current_rep": "Mevcut temsilci",
"nano_pick_new_rep": "Yeni bir temsilci seçin",
"narrow": "Dar",
diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb
index 13dbc2f30..73d6dd2f9 100644
--- a/res/values/strings_uk.arb
+++ b/res/values/strings_uk.arb
@@ -371,6 +371,8 @@
"moonpay_alert_text": "Значення суми має бути більшим або дорівнювати ${minAmount} ${fiatCurrency}",
"more_options": "Більше параметрів",
"name": "Ім'я",
+ "nano_gpt_thanks_message": "Дякуємо за використання наногпта! Не забудьте повернутися до браузера після завершення транзакції!",
+ "nanogpt_subtitle": "Усі найновіші моделі (GPT-4, Claude). \\ Nno підписка, оплата криптовалютою.",
"nano_current_rep": "Поточний представник",
"nano_pick_new_rep": "Виберіть нового представника",
"narrow": "вузькі",
diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb
index 6434fc25d..6839bff7c 100644
--- a/res/values/strings_ur.arb
+++ b/res/values/strings_ur.arb
@@ -371,6 +371,8 @@
"moonpay_alert_text": "رقم کی قدر ${minAmount} ${fiatCurrency} کے برابر یا زیادہ ہونی چاہیے۔",
"more_options": "مزید زرائے",
"name": "ﻡﺎﻧ",
+ "nano_gpt_thanks_message": "نانوگپٹ استعمال کرنے کا شکریہ! اپنے لین دین کی تکمیل کے بعد براؤزر کی طرف واپس جانا یاد رکھیں!",
+ "nanogpt_subtitle": "تمام تازہ ترین ماڈل (GPT-4 ، کلاڈ)۔ n n no سبسکرپشن ، کریپٹو کے ساتھ ادائیگی کریں۔",
"nano_current_rep": "موجودہ نمائندہ",
"nano_pick_new_rep": "ایک نیا نمائندہ منتخب کریں",
"narrow": "تنگ",
diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb
index df7a5532f..e6e24a1f2 100644
--- a/res/values/strings_yo.arb
+++ b/res/values/strings_yo.arb
@@ -372,6 +372,8 @@
"moonpay_alert_text": "Iye owó kò gbọ́dọ̀ kéré ju ${minAmount} ${fiatCurrency}",
"more_options": "Ìyàn àfikún",
"name": "Oruko",
+ "nano_gpt_thanks_message": "O ṣeun fun lilo Nonnogt! Ranti lati tẹle pada si ẹrọ lilọ kiri ayelujara lẹhin iṣowo rẹ pari!",
+ "nanogpt_subtitle": "Gbogbo awọn awoṣe tuntun (GPT-4, Claude). \\ Nno alabapin kan, sanwo pẹlu Crypto.",
"nano_current_rep": "Aṣoju lọwọlọwọ",
"nano_pick_new_rep": "Mu aṣoju tuntun kan",
"narrow": "Taara",
diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb
index 590b4cadb..ecd0ae738 100644
--- a/res/values/strings_zh.arb
+++ b/res/values/strings_zh.arb
@@ -371,6 +371,8 @@
"moonpay_alert_text": "金额的价值必须大于或等于 ${minAmount} ${fiatCurrency}",
"more_options": "更多选项",
"name": "姓名",
+ "nano_gpt_thanks_message": "感谢您使用Nanogpt!事务完成后,请记住回到浏览器!",
+ "nanogpt_subtitle": "所有最新型号(GPT-4,Claude)。\\ nno订阅,用加密货币付款。",
"nano_current_rep": "当前代表",
"nano_pick_new_rep": "选择新代表",
"narrow": "狭窄的",
diff --git a/tool/generate_secrets_config.dart b/tool/generate_secrets_config.dart
index 6aaa39b7c..cab41ca69 100644
--- a/tool/generate_secrets_config.dart
+++ b/tool/generate_secrets_config.dart
@@ -6,6 +6,7 @@ import 'utils/utils.dart';
const configPath = 'tool/.secrets-config.json';
const evmChainsConfigPath = 'tool/.evm-secrets-config.json';
const solanaConfigPath = 'tool/.solana-secrets-config.json';
+const nanoConfigPath = 'tool/.nano-secrets-config.json';
const tronConfigPath = 'tool/.tron-secrets-config.json';
Future main(List args) async => generateSecretsConfig(args);
@@ -21,6 +22,7 @@ Future generateSecretsConfig(List args) async {
final configFile = File(configPath);
final evmChainsConfigFile = File(evmChainsConfigPath);
final solanaConfigFile = File(solanaConfigPath);
+ final nanoConfigFile = File(nanoConfigPath);
final tronConfigFile = File(tronConfigPath);
final secrets = {};
@@ -42,45 +44,48 @@ Future generateSecretsConfig(List args) async {
}
}
+ // base:
SecretKey.base.forEach((sec) {
if (secrets[sec.name] != null) {
return;
}
-
secrets[sec.name] = sec.generate();
});
-
var secretsJson = JsonEncoder.withIndent(' ').convert(secrets);
await configFile.writeAsString(secretsJson);
-
secrets.clear();
+ // evm chains:
SecretKey.evmChainsSecrets.forEach((sec) {
if (secrets[sec.name] != null) {
return;
}
-
secrets[sec.name] = sec.generate();
});
-
secretsJson = JsonEncoder.withIndent(' ').convert(secrets);
-
await evmChainsConfigFile.writeAsString(secretsJson);
-
secrets.clear();
+ // solana:
SecretKey.solanaSecrets.forEach((sec) {
if (secrets[sec.name] != null) {
return;
}
-
secrets[sec.name] = sec.generate();
});
-
secretsJson = JsonEncoder.withIndent(' ').convert(secrets);
-
await solanaConfigFile.writeAsString(secretsJson);
+ secrets.clear();
+ // nano:
+ SecretKey.nanoSecrets.forEach((sec) {
+ if (secrets[sec.name] != null) {
+ return;
+ }
+ secrets[sec.name] = sec.generate();
+ });
+ secretsJson = JsonEncoder.withIndent(' ').convert(secrets);
+ await nanoConfigFile.writeAsString(secretsJson);
secrets.clear();
SecretKey.tronSecrets.forEach((sec) {
@@ -90,8 +95,7 @@ Future generateSecretsConfig(List args) async {
secrets[sec.name] = sec.generate();
});
-
secretsJson = JsonEncoder.withIndent(' ').convert(secrets);
-
await tronConfigFile.writeAsString(secretsJson);
+ secrets.clear();
}
diff --git a/tool/utils/secret_key.dart b/tool/utils/secret_key.dart
index 542e91b38..89e4de12d 100644
--- a/tool/utils/secret_key.dart
+++ b/tool/utils/secret_key.dart
@@ -50,6 +50,10 @@ class SecretKey {
SecretKey('ankrApiKey', () => ''),
];
+ static final nanoSecrets = [
+ SecretKey('nano2ApiKey', () => ''),
+ ];
+
static final tronSecrets = [
SecretKey('tronGridApiKey', () => ''),
];