* init

* updates

* nano updates

* updates

* updates

* [skipci] wip deep link changes

* fix deep links

* minor fix

* add reminder message on buy and exchange routes

* [skip ci] font fixes

* review updates

* [skip ci] minor fix

* save

* fixes

* minor code cleanup

* minor potential fix
This commit is contained in:
Matthew Fosse 2024-05-07 17:00:01 -07:00 committed by GitHub
parent e5be737236
commit baad7f7469
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
46 changed files with 486 additions and 195 deletions

View file

@ -151,6 +151,7 @@ jobs:
echo "const moralisApiKey = '${{ secrets.MORALIS_API_KEY }}';" >> lib/.secrets.g.dart 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 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 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 echo "const tronGridApiKey = '${{ secrets.TRON_GRID_API_KEY }}';" >> cw_tron/lib/.secrets.g.dart
- name: Rename app - name: Rename app

1
.gitignore vendored
View file

@ -94,6 +94,7 @@ android/app/key.jks
**/tool/.evm-secrets-config.json **/tool/.evm-secrets-config.json
**/tool/.ethereum-secrets-config.json **/tool/.ethereum-secrets-config.json
**/tool/.solana-secrets-config.json **/tool/.solana-secrets-config.json
**/tool/.nano-secrets-config.json
**/tool/.tron-secrets-config.json **/tool/.tron-secrets-config.json
**/lib/.secrets.g.dart **/lib/.secrets.g.dart
**/cw_evm/lib/.secrets.g.dart **/cw_evm/lib/.secrets.g.dart

View file

@ -91,6 +91,13 @@
<data android:scheme="tron-wallet" /> <data android:scheme="tron-wallet" />
<data android:scheme="tron_wallet" /> <data android:scheme="tron_wallet" />
</intent-filter> </intent-filter>
<!-- nano-gpt link scheme -->
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="nano-gpt" />
</intent-filter>
</activity> </activity>
<meta-data <meta-data
android:name="flutterEmbedding" android:name="flutterEmbedding"

View file

@ -0,0 +1,5 @@
-
uri: kaliumapi.appditto.com
path: /api
useSSL: true
is_default: true

View file

@ -259,10 +259,16 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
element.tag == walletCurrency?.tag)); element.tag == walletCurrency?.tag));
} catch (_) {} } 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) { if (CryptoCurrency._nameCurrencyMap[name.toLowerCase()] == null) {
final s = 'Unexpected token: $name for CryptoCurrency fromString'; final s = 'Unexpected token: $name for CryptoCurrency fromString';
throw ArgumentError.value(name, 'name', s); throw ArgumentError.value(name, 'name', s);
} }
return CryptoCurrency._nameCurrencyMap[name.toLowerCase()]!; return CryptoCurrency._nameCurrencyMap[name.toLowerCase()]!;
} }

View file

@ -1,12 +1,28 @@
import 'package:cw_core/balance.dart'; import 'package:cw_core/balance.dart';
import 'package:nanoutil/nanoutil.dart'; import 'package:nanoutil/nanoutil.dart';
BigInt stringAmountToBigIntBanano(String amount) {
return BigInt.parse(NanoAmounts.getAmountAsRaw(amount, NanoAmounts.rawPerBanano));
}
class BananoBalance extends Balance { class BananoBalance extends Balance {
final BigInt currentBalance; final BigInt currentBalance;
final BigInt receivableBalance; final BigInt receivableBalance;
BananoBalance({required this.currentBalance, required this.receivableBalance}) : super(0, 0); 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 @override
String get formattedAvailableBalance { String get formattedAvailableBalance {
return NanoAmounts.getRawAsUsableString(currentBalance.toString(), NanoAmounts.rawPerBanano); return NanoAmounts.getRawAsUsableString(currentBalance.toString(), NanoAmounts.rawPerBanano);

View file

@ -1,7 +1,7 @@
import 'package:cw_core/balance.dart'; import 'package:cw_core/balance.dart';
import 'package:nanoutil/nanoutil.dart'; import 'package:nanoutil/nanoutil.dart';
BigInt stringAmountToBigInt(String amount) { BigInt stringAmountToBigIntNano(String amount) {
return BigInt.parse(NanoAmounts.getAmountAsRaw(amount, NanoAmounts.rawPerNano)); return BigInt.parse(NanoAmounts.getAmountAsRaw(amount, NanoAmounts.rawPerNano));
} }
@ -13,8 +13,8 @@ class NanoBalance extends Balance {
NanoBalance.fromFormattedString( NanoBalance.fromFormattedString(
{required String formattedCurrentBalance, required String formattedReceivableBalance}) {required String formattedCurrentBalance, required String formattedReceivableBalance})
: currentBalance = stringAmountToBigInt(formattedCurrentBalance), : currentBalance = stringAmountToBigIntNano(formattedCurrentBalance),
receivableBalance = stringAmountToBigInt(formattedReceivableBalance), receivableBalance = stringAmountToBigIntNano(formattedReceivableBalance),
super(0, 0); super(0, 0);
NanoBalance.fromRawString( NanoBalance.fromRawString(

View file

@ -10,6 +10,7 @@ import 'package:nanodart/nanodart.dart';
import 'package:cw_core/node.dart'; import 'package:cw_core/node.dart';
import 'package:nanoutil/nanoutil.dart'; import 'package:nanoutil/nanoutil.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:cw_nano/.secrets.g.dart' as secrets;
class NanoClient { class NanoClient {
static const Map<String, String> CAKE_HEADERS = { static const Map<String, String> CAKE_HEADERS = {
@ -52,10 +53,19 @@ class NanoClient {
} }
} }
Map<String, String> getHeaders() {
if (_node!.uri == "https://rpc.nano.to") {
return CAKE_HEADERS..addAll({
"key": secrets.nano2ApiKey,
});
}
return CAKE_HEADERS;
}
Future<NanoBalance> getBalance(String address) async { Future<NanoBalance> getBalance(String address) async {
final response = await http.post( final response = await http.post(
_node!.uri, _node!.uri,
headers: CAKE_HEADERS, headers: getHeaders(),
body: jsonEncode( body: jsonEncode(
{ {
"action": "account_balance", "action": "account_balance",
@ -82,7 +92,7 @@ class NanoClient {
try { try {
final response = await http.post( final response = await http.post(
_node!.uri, _node!.uri,
headers: CAKE_HEADERS, headers: getHeaders(),
body: jsonEncode( body: jsonEncode(
{ {
"action": "account_info", "action": "account_info",
@ -94,7 +104,7 @@ class NanoClient {
final data = await jsonDecode(response.body); final data = await jsonDecode(response.body);
return AccountInfoResponse.fromJson(data as Map<String, dynamic>); return AccountInfoResponse.fromJson(data as Map<String, dynamic>);
} catch (e) { } catch (e) {
print("error while getting account info"); print("error while getting account info $e");
return null; return null;
} }
} }
@ -149,7 +159,7 @@ class NanoClient {
Future<String> requestWork(String hash) async { Future<String> requestWork(String hash) async {
final response = await http.post( final response = await http.post(
_powNode!.uri, _powNode!.uri,
headers: CAKE_HEADERS, headers: getHeaders(),
body: json.encode( body: json.encode(
{ {
"action": "work_generate", "action": "work_generate",
@ -192,7 +202,7 @@ class NanoClient {
final processResponse = await http.post( final processResponse = await http.post(
_node!.uri, _node!.uri,
headers: CAKE_HEADERS, headers: getHeaders(),
body: processBody, body: processBody,
); );
@ -351,7 +361,7 @@ class NanoClient {
}); });
final processResponse = await http.post( final processResponse = await http.post(
_node!.uri, _node!.uri,
headers: CAKE_HEADERS, headers: getHeaders(),
body: processBody, body: processBody,
); );
@ -367,7 +377,7 @@ class NanoClient {
required String privateKey, required String privateKey,
}) async { }) async {
final receivableResponse = await http.post(_node!.uri, final receivableResponse = await http.post(_node!.uri,
headers: CAKE_HEADERS, headers: getHeaders(),
body: jsonEncode({ body: jsonEncode({
"action": "receivable", "action": "receivable",
"account": destinationAddress, "account": destinationAddress,
@ -417,7 +427,7 @@ class NanoClient {
Future<List<NanoTransactionModel>> fetchTransactions(String address) async { Future<List<NanoTransactionModel>> fetchTransactions(String address) async {
try { try {
final response = await http.post(_node!.uri, final response = await http.post(_node!.uri,
headers: CAKE_HEADERS, headers: getHeaders(),
body: jsonEncode({ body: jsonEncode({
"action": "account_history", "action": "account_history",
"account": address, "account": address,

View file

@ -140,6 +140,16 @@
<string>nano-wallet</string> <string>nano-wallet</string>
</array> </array>
</dict> </dict>
<dict>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>CFBundleURLName</key>
<string>nano-gpt</string>
<key>CFBundleURLSchemes</key>
<array>
<string>nano-gpt</string>
</array>
</dict>
<dict> <dict>
<key>CFBundleTypeRole</key> <key>CFBundleTypeRole</key>
<string>Editor</string> <string>Editor</string>

View file

@ -26,6 +26,7 @@ import 'package:cake_wallet/entities/contact.dart';
import 'package:cake_wallet/entities/contact_record.dart'; import 'package:cake_wallet/entities/contact_record.dart';
import 'package:cake_wallet/entities/exchange_api_mode.dart'; import 'package:cake_wallet/entities/exchange_api_mode.dart';
import 'package:cake_wallet/entities/parse_address_from_domain.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/tron/tron.dart';
import 'package:cake_wallet/src/screens/transaction_details/rbf_details_page.dart'; import 'package:cake_wallet/src/screens/transaction_details/rbf_details_page.dart';
import 'package:cw_core/receive_page_option.dart'; import 'package:cw_core/receive_page_option.dart';
@ -268,6 +269,7 @@ Future<void> setup({
required Box<UnspentCoinsInfo> unspentCoinsInfoSource, required Box<UnspentCoinsInfo> unspentCoinsInfoSource,
required Box<AnonpayInvoiceInfo> anonpayInvoiceInfoSource, required Box<AnonpayInvoiceInfo> anonpayInvoiceInfoSource,
required FlutterSecureStorage secureStorage, required FlutterSecureStorage secureStorage,
required GlobalKey<NavigatorState> navigatorKey,
}) async { }) async {
_walletInfoSource = walletInfoSource; _walletInfoSource = walletInfoSource;
_nodeSource = nodeSource; _nodeSource = nodeSource;
@ -429,12 +431,21 @@ Future<void> setup({
), ),
); );
getIt.registerFactory<AuthPage>(() { getIt.registerLazySingleton<LinkViewModel>(() {
return AuthPage(getIt.get<AuthViewModel>(), return LinkViewModel(
appStore: getIt.get<AppStore>(),
settingsStore: getIt.get<SettingsStore>(),
authenticationStore: getIt.get<AuthenticationStore>(),
navigatorKey: navigatorKey,
);
});
getIt.registerFactory<AuthPage>(instanceName: 'login', () {
return AuthPage(getIt.get<AuthViewModel>(), closable: false,
onAuthenticationFinished: (isAuthenticated, AuthPageState authPageState) { onAuthenticationFinished: (isAuthenticated, AuthPageState authPageState) {
if (!isAuthenticated) { if (!isAuthenticated) {
return; return;
} else { }
final authStore = getIt.get<AuthenticationStore>(); final authStore = getIt.get<AuthenticationStore>();
final appStore = getIt.get<AppStore>(); final appStore = getIt.get<AppStore>();
final useTotp = appStore.settingsStore.useTOTP2FA; final useTotp = appStore.settingsStore.useTOTP2FA;
@ -446,8 +457,8 @@ Future<void> setup({
arguments: TotpAuthArgumentsModel( arguments: TotpAuthArgumentsModel(
isForSetup: false, isForSetup: false,
isClosable: false, isClosable: false,
onTotpAuthenticationFinished: (bool isAuthenticatedSuccessfully, onTotpAuthenticationFinished:
TotpAuthCodePageState totpAuthPageState) async { (bool isAuthenticatedSuccessfully, TotpAuthCodePageState totpAuthPageState) async {
if (!isAuthenticatedSuccessfully) { if (!isAuthenticatedSuccessfully) {
return; return;
} }
@ -471,11 +482,20 @@ Future<void> setup({
), ),
); );
} else { } else {
// wallet is already loaded:
if (appStore.wallet != null) { if (appStore.wallet != null) {
// goes to the dashboard:
authStore.allowed(); authStore.allowed();
// trigger any deep links:
final linkViewModel = getIt.get<LinkViewModel>();
if (linkViewModel.currentLink != null) {
linkViewModel.handleLink();
}
return; return;
} }
// load the wallet:
authPageState.changeProcessText('Loading the wallet'); authPageState.changeProcessText('Loading the wallet');
if (loginError != null) { if (loginError != null) {
@ -486,11 +506,14 @@ Future<void> setup({
_reaction = reaction((_) => appStore.wallet, (Object? _) { _reaction = reaction((_) => appStore.wallet, (Object? _) {
_reaction?.reaction.dispose(); _reaction?.reaction.dispose();
authStore.allowed(); authStore.allowed();
final linkViewModel = getIt.get<LinkViewModel>();
if (linkViewModel.currentLink != null) {
linkViewModel.handleLink();
}
}); });
} }
} });
}, closable: false); });
}, instanceName: 'login');
getIt.registerSingleton<BottomSheetService>(BottomSheetServiceImpl()); getIt.registerSingleton<BottomSheetService>(BottomSheetServiceImpl());
@ -849,8 +872,10 @@ Future<void> setup({
tradesStore: getIt.get<TradesStore>(), tradesStore: getIt.get<TradesStore>(),
sendViewModel: getIt.get<SendViewModel>())); sendViewModel: getIt.get<SendViewModel>()));
getIt.registerFactory( getIt.registerFactoryParam<ExchangePage, PaymentRequest?, void>(
() => ExchangePage(getIt.get<ExchangeViewModel>(), getIt.get<AuthService>())); (PaymentRequest? paymentRequest, __) {
return ExchangePage(getIt.get<ExchangeViewModel>(), getIt.get<AuthService>(), paymentRequest);
});
getIt.registerFactory(() => ExchangeConfirmPage(tradesStore: getIt.get<TradesStore>())); getIt.registerFactory(() => ExchangeConfirmPage(tradesStore: getIt.get<TradesStore>()));

View file

@ -7,6 +7,7 @@ import 'package:cake_wallet/locales/locale.dart';
import 'package:cake_wallet/store/yat/yat_store.dart'; import 'package:cake_wallet/store/yat/yat_store.dart';
import 'package:cake_wallet/utils/device_info.dart'; import 'package:cake_wallet/utils/device_info.dart';
import 'package:cake_wallet/utils/exception_handler.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:cw_core/address_info.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cw_core/hive_type_ids.dart'; import 'package:cw_core/hive_type_ids.dart';
@ -216,7 +217,9 @@ Future<void> initialSetup(
ordersSource: ordersSource, ordersSource: ordersSource,
anonpayInvoiceInfoSource: anonpayInvoiceInfo, anonpayInvoiceInfoSource: anonpayInvoiceInfo,
unspentCoinsInfoSource: unspentCoinsInfoSource, unspentCoinsInfoSource: unspentCoinsInfoSource,
secureStorage: secureStorage); secureStorage: secureStorage,
navigatorKey: navigatorKey,
);
await bootstrap(navigatorKey); await bootstrap(navigatorKey);
monero?.onStartup(); monero?.onStartup();
} }
@ -287,6 +290,7 @@ class AppState extends State<App> with SingleTickerProviderStateMixin {
return Observer(builder: (BuildContext context) { return Observer(builder: (BuildContext context) {
final appStore = getIt.get<AppStore>(); final appStore = getIt.get<AppStore>();
final authService = getIt.get<AuthService>(); final authService = getIt.get<AuthService>();
final linkViewModel = getIt.get<LinkViewModel>();
final settingsStore = appStore.settingsStore; final settingsStore = appStore.settingsStore;
final statusBarColor = Colors.transparent; final statusBarColor = Colors.transparent;
final authenticationStore = getIt.get<AuthenticationStore>(); final authenticationStore = getIt.get<AuthenticationStore>();
@ -309,6 +313,7 @@ class AppState extends State<App> with SingleTickerProviderStateMixin {
authenticationStore: authenticationStore, authenticationStore: authenticationStore,
navigatorKey: navigatorKey, navigatorKey: navigatorKey,
authService: authService, authService: authService,
linkViewModel: linkViewModel,
child: MaterialApp( child: MaterialApp(
navigatorObservers: [routeObserver], navigatorObservers: [routeObserver],
navigatorKey: navigatorKey, navigatorKey: navigatorKey,

View file

@ -221,7 +221,8 @@ Route<dynamic> createRoute(RouteSettings settings) {
return CupertinoPageRoute<void>( return CupertinoPageRoute<void>(
builder: (_) => getIt.get<SetupPinCodePage>( builder: (_) => getIt.get<SetupPinCodePage>(
param1: (PinCodeState<PinCodeWidget> context, dynamic _) => param1: (PinCodeState<PinCodeWidget> context, dynamic _) =>
Navigator.of(context.context).pushNamed(Routes.restoreWalletFromHardwareWallet, arguments: false), Navigator.of(context.context)
.pushNamed(Routes.restoreWalletFromHardwareWallet, arguments: false),
), ),
fullscreenDialog: true, fullscreenDialog: true,
); );
@ -231,8 +232,8 @@ Route<dynamic> createRoute(RouteSettings settings) {
builder: (_) => ConnectDevicePage( builder: (_) => ConnectDevicePage(
ConnectDevicePageParams( ConnectDevicePageParams(
walletType: availableWalletTypes.first, walletType: availableWalletTypes.first,
onConnectDevice: (BuildContext context, _) => onConnectDevice: (BuildContext context, _) => Navigator.of(context).pushNamed(
Navigator.of(context).pushNamed(Routes.chooseHardwareWalletAccount, Routes.chooseHardwareWalletAccount,
arguments: [availableWalletTypes.first]), arguments: [availableWalletTypes.first]),
), ),
getIt.get<LedgerViewModel>(), getIt.get<LedgerViewModel>(),
@ -243,9 +244,8 @@ Route<dynamic> createRoute(RouteSettings settings) {
param1: (BuildContext context, WalletType type) { param1: (BuildContext context, WalletType type) {
final arguments = ConnectDevicePageParams( final arguments = ConnectDevicePageParams(
walletType: type, walletType: type,
onConnectDevice: (BuildContext context, _) => onConnectDevice: (BuildContext context, _) => Navigator.of(context)
Navigator.of(context).pushNamed(Routes.chooseHardwareWalletAccount, .pushNamed(Routes.chooseHardwareWalletAccount, arguments: [type]),
arguments: [type]),
); );
Navigator.of(context).pushNamed(Routes.connectDevices, arguments: arguments); Navigator.of(context).pushNamed(Routes.connectDevices, arguments: arguments);
@ -308,8 +308,7 @@ Route<dynamic> createRoute(RouteSettings settings) {
case Routes.bumpFeePage: case Routes.bumpFeePage:
return CupertinoPageRoute<void>( return CupertinoPageRoute<void>(
fullscreenDialog: true, fullscreenDialog: true,
builder: (_) => builder: (_) => getIt.get<RBFDetailsPage>(param1: settings.arguments as TransactionInfo));
getIt.get<RBFDetailsPage>(param1: settings.arguments as TransactionInfo));
case Routes.newSubaddress: case Routes.newSubaddress:
return CupertinoPageRoute<void>( return CupertinoPageRoute<void>(
@ -461,7 +460,9 @@ Route<dynamic> createRoute(RouteSettings settings) {
case Routes.exchange: case Routes.exchange:
return CupertinoPageRoute<void>( return CupertinoPageRoute<void>(
fullscreenDialog: true, builder: (_) => getIt.get<ExchangePage>()); fullscreenDialog: true,
builder: (_) => getIt.get<ExchangePage>(param1: settings.arguments as PaymentRequest?),
);
case Routes.exchangeTemplate: case Routes.exchangeTemplate:
return CupertinoPageRoute<void>(builder: (_) => getIt.get<ExchangeTemplatePage>()); return CupertinoPageRoute<void>(builder: (_) => getIt.get<ExchangeTemplatePage>());

View file

@ -59,12 +59,15 @@ class MarketPlacePage extends StatelessWidget {
// ), // ),
SizedBox(height: 20), SizedBox(height: 20),
DashBoardRoundedCardWidget( DashBoardRoundedCardWidget(
onTap: () => launchUrl(
Uri.https("buy.cakepay.com"),
mode: LaunchMode.externalApplication,
),
title: S.of(context).cake_pay_web_cards_title, title: S.of(context).cake_pay_web_cards_title,
subTitle: S.of(context).cake_pay_web_cards_subtitle, 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"),
), ),
], ],
), ),
@ -76,6 +79,17 @@ class MarketPlacePage extends StatelessWidget {
); );
} }
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 // TODO: Remove ionia flow/files if we will discard it
void _navigatorToGiftCardsPage(BuildContext context) { void _navigatorToGiftCardsPage(BuildContext context) {
final walletType = dashboardViewModel.type; final walletType = dashboardViewModel.type;

View file

@ -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/extensions/send_page_theme.dart';
import 'package:cake_wallet/themes/theme_base.dart'; import 'package:cake_wallet/themes/theme_base.dart';
import 'package:cake_wallet/utils/debounce.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:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cw_core/sync_status.dart'; import 'package:cw_core/sync_status.dart';
import 'package:cw_core/wallet_type.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'; import 'package:cake_wallet/src/screens/dashboard/widgets/sync_indicator_icon.dart';
class ExchangePage extends BasePage { class ExchangePage extends BasePage {
ExchangePage(this.exchangeViewModel, this.authService) { ExchangePage(this.exchangeViewModel, this.authService, this.initialPaymentRequest) {
depositWalletName = exchangeViewModel.depositCurrency == CryptoCurrency.xmr depositWalletName = exchangeViewModel.depositCurrency == CryptoCurrency.xmr
? exchangeViewModel.wallet.name ? exchangeViewModel.wallet.name
: null; : null;
@ -54,6 +55,7 @@ class ExchangePage extends BasePage {
final ExchangeViewModel exchangeViewModel; final ExchangeViewModel exchangeViewModel;
final AuthService authService; final AuthService authService;
final PaymentRequest? initialPaymentRequest;
final depositKey = GlobalKey<ExchangeCardState>(); final depositKey = GlobalKey<ExchangeCardState>();
final receiveKey = GlobalKey<ExchangeCardState>(); final receiveKey = GlobalKey<ExchangeCardState>();
final _formKey = GlobalKey<FormState>(); final _formKey = GlobalKey<FormState>();
@ -543,6 +545,12 @@ class ExchangePage extends BasePage {
// amount: depositAmountController.text); // amount: depositAmountController.text);
}); });
if (initialPaymentRequest != null) {
exchangeViewModel.receiveCurrency = CryptoCurrency.fromString(initialPaymentRequest!.scheme);
exchangeViewModel.depositAmount = initialPaymentRequest!.amount;
exchangeViewModel.receiveAddress = initialPaymentRequest!.address;
}
_isReactionsSet = true; _isReactionsSet = true;
} }

View file

@ -5,6 +5,7 @@ import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/reactions/wallet_connect.dart'; import 'package:cake_wallet/reactions/wallet_connect.dart';
import 'package:cake_wallet/utils/device_info.dart'; import 'package:cake_wallet/utils/device_info.dart';
import 'package:cake_wallet/utils/payment_request.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:cw_core/wallet_base.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/routes.dart';
@ -25,6 +26,7 @@ class Root extends StatefulWidget {
required this.child, required this.child,
required this.navigatorKey, required this.navigatorKey,
required this.authService, required this.authService,
required this.linkViewModel,
}) : super(key: key); }) : super(key: key);
final AuthenticationStore authenticationStore; final AuthenticationStore authenticationStore;
@ -32,6 +34,7 @@ class Root extends StatefulWidget {
final GlobalKey<NavigatorState> navigatorKey; final GlobalKey<NavigatorState> navigatorKey;
final AuthService authService; final AuthService authService;
final Widget child; final Widget child;
final LinkViewModel linkViewModel;
@override @override
RootState createState() => RootState(); RootState createState() => RootState();
@ -53,7 +56,6 @@ class RootState extends State<Root> with WidgetsBindingObserver {
StreamSubscription<Uri?>? stream; StreamSubscription<Uri?>? stream;
ReactionDisposer? _walletReactionDisposer; ReactionDisposer? _walletReactionDisposer;
ReactionDisposer? _deepLinksReactionDisposer; ReactionDisposer? _deepLinksReactionDisposer;
Uri? launchUri;
@override @override
void initState() { void initState() {
@ -98,7 +100,7 @@ class RootState extends State<Root> with WidgetsBindingObserver {
void handleDeepLinking(Uri? uri) async { void handleDeepLinking(Uri? uri) async {
if (uri == null || !mounted) return; if (uri == null || !mounted) return;
launchUri = uri; widget.linkViewModel.currentLink = uri;
bool requireAuth = await widget.authService.requireAuth(); bool requireAuth = await widget.authService.requireAuth();
@ -112,7 +114,7 @@ class RootState extends State<Root> with WidgetsBindingObserver {
(AuthenticationState state) { (AuthenticationState state) {
if (state == AuthenticationState.allowed) { if (state == AuthenticationState.allowed) {
if (widget.appStore.wallet == null) { if (widget.appStore.wallet == null) {
waitForWalletInstance(context, launchUri!); waitForWalletInstance(context);
} else { } else {
_navigateToDeepLinkScreen(); _navigateToDeepLinkScreen();
} }
@ -150,6 +152,8 @@ class RootState extends State<Root> with WidgetsBindingObserver {
@override @override
Widget build(BuildContext context) { 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) { if (_isInactive && !_postFrameCallback && _requestAuth) {
_postFrameCallback = true; _postFrameCallback = true;
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
@ -158,7 +162,7 @@ class RootState extends State<Root> with WidgetsBindingObserver {
arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) { arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) {
if (!isAuthenticatedSuccessfully) { if (!isAuthenticatedSuccessfully) {
return; return;
} else { }
final useTotp = widget.appStore.settingsStore.useTOTP2FA; final useTotp = widget.appStore.settingsStore.useTOTP2FA;
final shouldUseTotp2FAToAccessWallets = final shouldUseTotp2FAToAccessWallets =
widget.appStore.settingsStore.shouldRequireTOTP2FAForAccessingWallet; widget.appStore.settingsStore.shouldRequireTOTP2FAForAccessingWallet;
@ -174,11 +178,10 @@ class RootState extends State<Root> with WidgetsBindingObserver {
} }
_reset(); _reset();
totpAuth.close( totpAuth.close(
route: _getRouteToGo(), route: widget.linkViewModel.getRouteToGo(),
arguments: arguments: widget.linkViewModel.getRouteArgs(),
isWalletConnectLink ? launchUri : PaymentRequest.fromUri(launchUri),
); );
launchUri = null; widget.linkViewModel.currentLink = null;
}, },
isForSetup: false, isForSetup: false,
isClosable: false, isClosable: false,
@ -187,11 +190,10 @@ class RootState extends State<Root> with WidgetsBindingObserver {
} else { } else {
_reset(); _reset();
auth.close( auth.close(
route: _getRouteToGo(), route: widget.linkViewModel.getRouteToGo(),
arguments: isWalletConnectLink ? launchUri : PaymentRequest.fromUri(launchUri), arguments: widget.linkViewModel.getRouteArgs(),
); );
launchUri = null; widget.linkViewModel.currentLink = null;
}
} }
}, },
); );
@ -216,36 +218,7 @@ class RootState extends State<Root> with WidgetsBindingObserver {
_isInactiveController.add(value); _isInactiveController.add(value);
} }
bool _isValidPaymentUri() => launchUri?.path.isNotEmpty ?? false; void waitForWalletInstance(BuildContext context) {
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<void> _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) {
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
if (context.mounted) { if (context.mounted) {
_walletReactionDisposer = reaction( _walletReactionDisposer = reaction(
@ -263,14 +236,6 @@ class RootState extends State<Root> with WidgetsBindingObserver {
} }
void _navigateToDeepLinkScreen() { void _navigateToDeepLinkScreen() {
if (_getRouteToGo() != null) { widget.linkViewModel.handleLink();
WidgetsBinding.instance.addPostFrameCallback((_) {
widget.navigatorKey.currentState?.pushNamed(
_getRouteToGo()!,
arguments: isWalletConnectLink ? launchUri : PaymentRequest.fromUri(launchUri),
);
launchUri = null;
});
}
} }
} }

View file

@ -35,6 +35,8 @@ import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
import 'package:smooth_page_indicator/smooth_page_indicator.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 { class SendPage extends BasePage {
SendPage({ SendPage({
@ -420,12 +422,10 @@ class SendPage extends BasePage {
} }
reaction((_) => sendViewModel.state, (ExecutionState state) { reaction((_) => sendViewModel.state, (ExecutionState state) {
if (dialogContext != null && dialogContext?.mounted == true) { if (dialogContext != null && dialogContext?.mounted == true) {
Navigator.of(dialogContext!).pop(); Navigator.of(dialogContext!).pop();
} }
if (state is FailureState) { if (state is FailureState) {
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
showPopUp<void>( showPopUp<void>(
@ -460,10 +460,10 @@ class SendPage extends BasePage {
outputs: sendViewModel.outputs, outputs: sendViewModel.outputs,
rightButtonText: S.of(_dialogContext).send, rightButtonText: S.of(_dialogContext).send,
leftButtonText: S.of(_dialogContext).cancel, leftButtonText: S.of(_dialogContext).cancel,
actionRightButton: () { actionRightButton: () async {
Navigator.of(_dialogContext).pop(); Navigator.of(_dialogContext).pop();
sendViewModel.commitTransaction(); sendViewModel.commitTransaction();
showPopUp<void>( await showPopUp<void>(
context: context, context: context,
builder: (BuildContext _dialogContext) { builder: (BuildContext _dialogContext) {
return Observer(builder: (_) { return Observer(builder: (_) {
@ -481,12 +481,14 @@ class SendPage extends BasePage {
sendViewModel.selectedCryptoCurrency.toString()); sendViewModel.selectedCryptoCurrency.toString());
final waitMessage = sendViewModel.walletType == WalletType.solana final waitMessage = sendViewModel.walletType == WalletType.solana
? '. ${S.of(_dialogContext).waitFewSecondForTxUpdate}' : ''; ? '. ${S.of(_dialogContext).waitFewSecondForTxUpdate}'
: '';
final newContactMessage = newContactAddress != null final newContactMessage = newContactAddress != null
? '\n${S.of(_dialogContext).add_contact_to_address_book}' : ''; ? '\n${S.of(_dialogContext).add_contact_to_address_book}'
: '';
final alertContent = String alertContent =
"$successMessage$waitMessage$newContactMessage"; "$successMessage$waitMessage$newContactMessage";
if (newContactAddress != null) { if (newContactAddress != null) {
@ -509,6 +511,10 @@ class SendPage extends BasePage {
newContactAddress = null; newContactAddress = null;
}); });
} else { } else {
if (initialPaymentRequest?.callbackMessage?.isNotEmpty ??
false) {
alertContent = initialPaymentRequest!.callbackMessage!;
}
return AlertWithOneAction( return AlertWithOneAction(
alertTitle: '', alertTitle: '',
alertContent: alertContent, alertContent: alertContent,
@ -523,6 +529,20 @@ class SendPage extends BasePage {
return Offstage(); 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()); actionLeftButton: () => Navigator.of(_dialogContext).pop());
}); });

View file

@ -1,19 +1,29 @@
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/nano/nano.dart'; import 'package:cake_wallet/nano/nano.dart';
class PaymentRequest { 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) { factory PaymentRequest.fromUri(Uri? uri) {
var address = ""; var address = "";
var amount = ""; var amount = "";
var note = ""; var note = "";
var scheme = ""; var scheme = "";
String? callbackUrl;
String? callbackMessage;
if (uri != null) { if (uri != null) {
address = uri.path; address = uri.queryParameters['address'] ?? uri.path;
amount = uri.queryParameters['tx_amount'] ?? uri.queryParameters['amount'] ?? ""; amount = uri.queryParameters['tx_amount'] ?? uri.queryParameters['amount'] ?? "";
note = uri.queryParameters['tx_description'] ?? uri.queryParameters['message'] ?? ""; note = uri.queryParameters['tx_description'] ?? uri.queryParameters['message'] ?? "";
scheme = uri.scheme; 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) { 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 address;
final String amount; final String amount;
final String note; final String note;
final String scheme; final String scheme;
final String? callbackUrl;
final String? callbackMessage;
} }

View file

@ -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<NavigatorState> 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<void> _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<void> 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,
);
}
}
}

View file

@ -366,6 +366,8 @@
"moonpay_alert_text": "يجب أن تكون قيمة المبلغ أكبر من أو تساوي ${minAmount} ${fiatCurrency}", "moonpay_alert_text": "يجب أن تكون قيمة المبلغ أكبر من أو تساوي ${minAmount} ${fiatCurrency}",
"more_options": "المزيد من الخيارات", "more_options": "المزيد من الخيارات",
"name": "ﻢﺳﺍ", "name": "ﻢﺳﺍ",
"nano_gpt_thanks_message": "شكرا لاستخدام nanogpt! تذكر أن تعود إلى المتصفح بعد اكتمال معاملتك!",
"nanogpt_subtitle": "جميع النماذج الأحدث (GPT-4 ، Claude). \\ nno اشتراك ، ادفع مع Crypto.",
"nano_current_rep": "الممثل الحالي", "nano_current_rep": "الممثل الحالي",
"nano_pick_new_rep": "اختر ممثلًا جديدًا", "nano_pick_new_rep": "اختر ممثلًا جديدًا",
"narrow": "ضيق", "narrow": "ضيق",

View file

@ -366,6 +366,8 @@
"moonpay_alert_text": "Сумата трябва да бъде най-малко ${minAmount} ${fiatCurrency}", "moonpay_alert_text": "Сумата трябва да бъде най-малко ${minAmount} ${fiatCurrency}",
"more_options": "Още настройки", "more_options": "Още настройки",
"name": "Име", "name": "Име",
"nano_gpt_thanks_message": "Благодаря, че използвахте Nanogpt! Не забравяйте да се върнете обратно към браузъра, след като транзакцията ви приключи!",
"nanogpt_subtitle": "Всички най-нови модели (GPT-4, Claude). \\ Nno абонамент, платете с Crypto.",
"nano_current_rep": "Настоящ представител", "nano_current_rep": "Настоящ представител",
"nano_pick_new_rep": "Изберете нов представител", "nano_pick_new_rep": "Изберете нов представител",
"narrow": "Тесен", "narrow": "Тесен",

View file

@ -366,6 +366,8 @@
"moonpay_alert_text": "Částka musí být větší nebo rovna ${minAmount} ${fiatCurrency}", "moonpay_alert_text": "Částka musí být větší nebo rovna ${minAmount} ${fiatCurrency}",
"more_options": "Více možností", "more_options": "Více možností",
"name": "název", "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_current_rep": "Současný zástupce",
"nano_pick_new_rep": "Vyberte nového zástupce", "nano_pick_new_rep": "Vyberte nového zástupce",
"narrow": "Úzký", "narrow": "Úzký",

View file

@ -366,6 +366,8 @@
"moonpay_alert_text": "Der Wert des Betrags muss größer oder gleich ${minAmount} ${fiatCurrency} sein", "moonpay_alert_text": "Der Wert des Betrags muss größer oder gleich ${minAmount} ${fiatCurrency} sein",
"more_options": "Weitere Optionen", "more_options": "Weitere Optionen",
"name": "Name", "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_current_rep": "Aktueller Vertreter",
"nano_pick_new_rep": "Wählen Sie einen neuen Vertreter aus", "nano_pick_new_rep": "Wählen Sie einen neuen Vertreter aus",
"narrow": "Eng", "narrow": "Eng",

View file

@ -366,6 +366,8 @@
"moonpay_alert_text": "Value of the amount must be more or equal to ${minAmount} ${fiatCurrency}", "moonpay_alert_text": "Value of the amount must be more or equal to ${minAmount} ${fiatCurrency}",
"more_options": "More Options", "more_options": "More Options",
"name": "Name", "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_current_rep": "Current Representative",
"nano_pick_new_rep": "Pick a new representative", "nano_pick_new_rep": "Pick a new representative",
"narrow": "Narrow", "narrow": "Narrow",

View file

@ -366,6 +366,8 @@
"moonpay_alert_text": "El valor de la cantidad debe ser mayor o igual a ${minAmount} ${fiatCurrency}", "moonpay_alert_text": "El valor de la cantidad debe ser mayor o igual a ${minAmount} ${fiatCurrency}",
"more_options": "Más Opciones", "more_options": "Más Opciones",
"name": "Nombre", "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_current_rep": "Representante actual",
"nano_pick_new_rep": "Elija un nuevo representante", "nano_pick_new_rep": "Elija un nuevo representante",
"narrow": "Angosto", "narrow": "Angosto",

View file

@ -366,6 +366,8 @@
"moonpay_alert_text": "Le montant doit être au moins égal à ${minAmount} ${fiatCurrency}", "moonpay_alert_text": "Le montant doit être au moins égal à ${minAmount} ${fiatCurrency}",
"more_options": "Plus d'options", "more_options": "Plus d'options",
"name": "Nom", "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_current_rep": "Représentant actuel",
"nano_pick_new_rep": "Choisissez un nouveau représentant", "nano_pick_new_rep": "Choisissez un nouveau représentant",
"narrow": "Étroit", "narrow": "Étroit",

View file

@ -366,6 +366,8 @@
"moonpay_alert_text": "Darajar adadin dole ne ya zama fiye ko daidai da ${minAmount} ${fiatCurrency}", "moonpay_alert_text": "Darajar adadin dole ne ya zama fiye ko daidai da ${minAmount} ${fiatCurrency}",
"more_options": "Ƙarin Zaɓuɓɓuka", "more_options": "Ƙarin Zaɓuɓɓuka",
"name": "Suna", "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_current_rep": "Wakilin Yanzu",
"nano_pick_new_rep": "Dauki sabon wakili", "nano_pick_new_rep": "Dauki sabon wakili",
"narrow": "kunkuntar", "narrow": "kunkuntar",

View file

@ -366,6 +366,8 @@
"moonpay_alert_text": "राशि का मूल्य अधिक है या करने के लिए बराबर होना चाहिए ${minAmount} ${fiatCurrency}", "moonpay_alert_text": "राशि का मूल्य अधिक है या करने के लिए बराबर होना चाहिए ${minAmount} ${fiatCurrency}",
"more_options": "और विकल्प", "more_options": "और विकल्प",
"name": "नाम", "name": "नाम",
"nano_gpt_thanks_message": "Nanogpt का उपयोग करने के लिए धन्यवाद! अपने लेन -देन के पूरा होने के बाद ब्राउज़र पर वापस जाना याद रखें!",
"nanogpt_subtitle": "सभी नवीनतम मॉडल (GPT-4, क्लाउड)। \\ nno सदस्यता, क्रिप्टो के साथ भुगतान करें।",
"nano_current_rep": "वर्तमान प्रतिनिधि", "nano_current_rep": "वर्तमान प्रतिनिधि",
"nano_pick_new_rep": "एक नया प्रतिनिधि चुनें", "nano_pick_new_rep": "एक नया प्रतिनिधि चुनें",
"narrow": "सँकरा", "narrow": "सँकरा",

View file

@ -366,6 +366,8 @@
"moonpay_alert_text": "Vrijednost iznosa mora biti veća ili jednaka ${minAmount} ${fiatCurrency}", "moonpay_alert_text": "Vrijednost iznosa mora biti veća ili jednaka ${minAmount} ${fiatCurrency}",
"more_options": "Više opcija", "more_options": "Više opcija",
"name": "Ime", "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_current_rep": "Trenutni predstavnik",
"nano_pick_new_rep": "Odaberite novog predstavnika", "nano_pick_new_rep": "Odaberite novog predstavnika",
"narrow": "Usko", "narrow": "Usko",

View file

@ -366,6 +366,8 @@
"moonpay_alert_text": "Nilai jumlah harus lebih atau sama dengan ${minAmount} ${fiatCurrency}", "moonpay_alert_text": "Nilai jumlah harus lebih atau sama dengan ${minAmount} ${fiatCurrency}",
"more_options": "Opsi Lainnya", "more_options": "Opsi Lainnya",
"name": "Nama", "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_current_rep": "Perwakilan saat ini",
"nano_pick_new_rep": "Pilih perwakilan baru", "nano_pick_new_rep": "Pilih perwakilan baru",
"narrow": "Sempit", "narrow": "Sempit",

View file

@ -367,6 +367,8 @@
"moonpay_alert_text": "Il valore dell'importo deve essere maggiore o uguale a ${minAmount} ${fiatCurrency}", "moonpay_alert_text": "Il valore dell'importo deve essere maggiore o uguale a ${minAmount} ${fiatCurrency}",
"more_options": "Altre opzioni", "more_options": "Altre opzioni",
"name": "Nome", "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_current_rep": "Rappresentante attuale",
"nano_pick_new_rep": "Scegli un nuovo rappresentante", "nano_pick_new_rep": "Scegli un nuovo rappresentante",
"narrow": "Stretto", "narrow": "Stretto",

View file

@ -367,6 +367,8 @@
"moonpay_alert_text": "金額の値は以上でなければなりません ${minAmount} ${fiatCurrency}", "moonpay_alert_text": "金額の値は以上でなければなりません ${minAmount} ${fiatCurrency}",
"more_options": "その他のオプション", "more_options": "その他のオプション",
"name": "名前", "name": "名前",
"nano_gpt_thanks_message": "NanoGptを使用してくれてありがとうトランザクションが完了したら、ブラウザに戻ることを忘れないでください",
"nanogpt_subtitle": "すべての最新モデルGPT-4、Claude。\\ nnoサブスクリプション、暗号で支払います。",
"nano_current_rep": "現在の代表", "nano_current_rep": "現在の代表",
"nano_pick_new_rep": "新しい代表者を選びます", "nano_pick_new_rep": "新しい代表者を選びます",
"narrow": "狭い", "narrow": "狭い",

View file

@ -366,6 +366,8 @@
"moonpay_alert_text": "금액은 다음보다 크거나 같아야합니다 ${minAmount} ${fiatCurrency}", "moonpay_alert_text": "금액은 다음보다 크거나 같아야합니다 ${minAmount} ${fiatCurrency}",
"more_options": "추가 옵션", "more_options": "추가 옵션",
"name": "이름", "name": "이름",
"nano_gpt_thanks_message": "Nanogpt를 사용해 주셔서 감사합니다! 거래가 완료된 후 브라우저로 돌아가는 것을 잊지 마십시오!",
"nanogpt_subtitle": "모든 최신 모델 (GPT-4, Claude). \\ nno 구독, Crypto로 지불하십시오.",
"nano_current_rep": "현재 대표", "nano_current_rep": "현재 대표",
"nano_pick_new_rep": "새로운 담당자를 선택하십시오", "nano_pick_new_rep": "새로운 담당자를 선택하십시오",
"narrow": "좁은", "narrow": "좁은",

View file

@ -366,6 +366,8 @@
"moonpay_alert_text": "ပမာဏ၏တန်ဖိုးသည် ${minAmount} ${fiatCurrency} နှင့် ပိုနေရမည်", "moonpay_alert_text": "ပမာဏ၏တန်ဖိုးသည် ${minAmount} ${fiatCurrency} နှင့် ပိုနေရမည်",
"more_options": "နောက်ထပ် ရွေးချယ်စရာများ", "more_options": "နောက်ထပ် ရွေးချယ်စရာများ",
"name": "နာမည်", "name": "နာမည်",
"nano_gpt_thanks_message": "nanogpt ကိုသုံးပြီးကျေးဇူးတင်ပါတယ် သင်၏ငွေပေးငွေယူပြီးနောက် browser သို့ပြန်သွားရန်သတိရပါ။",
"nanogpt_subtitle": "အားလုံးနောက်ဆုံးပေါ်မော်ဒယ်များ (GPT-4, Claude) ။ \\ nno subscription, crypto နှင့်အတူပေးဆောင်။",
"nano_current_rep": "လက်ရှိကိုယ်စားလှယ်", "nano_current_rep": "လက်ရှိကိုယ်စားလှယ်",
"nano_pick_new_rep": "အသစ်တစ်ခုကိုရွေးပါ", "nano_pick_new_rep": "အသစ်တစ်ခုကိုရွေးပါ",
"narrow": "ကျဉ်းသော", "narrow": "ကျဉ်းသော",

View file

@ -366,6 +366,8 @@
"moonpay_alert_text": "Waarde van het bedrag moet meer of gelijk zijn aan ${minAmount} ${fiatCurrency}", "moonpay_alert_text": "Waarde van het bedrag moet meer of gelijk zijn aan ${minAmount} ${fiatCurrency}",
"more_options": "Meer opties", "more_options": "Meer opties",
"name": "Naam", "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_current_rep": "Huidige vertegenwoordiger",
"nano_pick_new_rep": "Kies een nieuwe vertegenwoordiger", "nano_pick_new_rep": "Kies een nieuwe vertegenwoordiger",
"narrow": "Smal", "narrow": "Smal",

View file

@ -366,6 +366,8 @@
"moonpay_alert_text": "Wartość kwoty musi być większa lub równa ${minAmount} ${fiatCurrency}", "moonpay_alert_text": "Wartość kwoty musi być większa lub równa ${minAmount} ${fiatCurrency}",
"more_options": "Więcej opcji", "more_options": "Więcej opcji",
"name": "Nazwa", "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_current_rep": "Obecny przedstawiciel",
"nano_pick_new_rep": "Wybierz nowego przedstawiciela", "nano_pick_new_rep": "Wybierz nowego przedstawiciela",
"narrow": "Wąski", "narrow": "Wąski",

View file

@ -367,6 +367,8 @@
"moonpay_alert_text": "O valor do montante deve ser maior ou igual a ${minAmount} ${fiatCurrency}", "moonpay_alert_text": "O valor do montante deve ser maior ou igual a ${minAmount} ${fiatCurrency}",
"more_options": "Mais opções", "more_options": "Mais opções",
"name": "Nome", "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_current_rep": "Representante atual",
"nano_pick_new_rep": "Escolha um novo representante", "nano_pick_new_rep": "Escolha um novo representante",
"narrow": "Estreito", "narrow": "Estreito",

View file

@ -366,6 +366,8 @@
"moonpay_alert_text": "Сумма должна быть больше или равна ${minAmount} ${fiatCurrency}", "moonpay_alert_text": "Сумма должна быть больше или равна ${minAmount} ${fiatCurrency}",
"more_options": "Дополнительные параметры", "more_options": "Дополнительные параметры",
"name": "Имя", "name": "Имя",
"nano_gpt_thanks_message": "Спасибо за использование Nanogpt! Не забудьте вернуться в браузер после завершения транзакции!",
"nanogpt_subtitle": "Все новейшие модели (GPT-4, Claude). \\ Nno Подписка, платите с крипто.",
"nano_current_rep": "Нынешний представитель", "nano_current_rep": "Нынешний представитель",
"nano_pick_new_rep": "Выберите нового представителя", "nano_pick_new_rep": "Выберите нового представителя",
"narrow": "Узкий", "narrow": "Узкий",

View file

@ -366,6 +366,8 @@
"moonpay_alert_text": "มูลค่าของจำนวนต้องมากกว่าหรือเท่ากับ ${minAmount} ${fiatCurrency}", "moonpay_alert_text": "มูลค่าของจำนวนต้องมากกว่าหรือเท่ากับ ${minAmount} ${fiatCurrency}",
"more_options": "ตัวเลือกเพิ่มเติม", "more_options": "ตัวเลือกเพิ่มเติม",
"name": "ชื่อ", "name": "ชื่อ",
"nano_gpt_thanks_message": "ขอบคุณที่ใช้ Nanogpt! อย่าลืมกลับไปที่เบราว์เซอร์หลังจากการทำธุรกรรมของคุณเสร็จสิ้น!",
"nanogpt_subtitle": "รุ่นใหม่ล่าสุดทั้งหมด (GPT-4, Claude). การสมัครสมาชิก \\ nno, จ่ายด้วย crypto",
"nano_current_rep": "ตัวแทนปัจจุบัน", "nano_current_rep": "ตัวแทนปัจจุบัน",
"nano_pick_new_rep": "เลือกตัวแทนใหม่", "nano_pick_new_rep": "เลือกตัวแทนใหม่",
"narrow": "แคบ", "narrow": "แคบ",

View file

@ -366,6 +366,8 @@
"moonpay_alert_text": "Ang halaga ng halaga ay dapat na higit pa o katumbas ng ${minAmount} ${fiatCurrency}", "moonpay_alert_text": "Ang halaga ng halaga ay dapat na higit pa o katumbas ng ${minAmount} ${fiatCurrency}",
"more_options": "Higit pang mga pagpipilian", "more_options": "Higit pang mga pagpipilian",
"name": "Pangalan", "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_current_rep": "Kasalukuyang kinatawan",
"nano_pick_new_rep": "Pumili ng isang bagong kinatawan", "nano_pick_new_rep": "Pumili ng isang bagong kinatawan",
"narrow": "Makitid", "narrow": "Makitid",

View file

@ -366,6 +366,8 @@
"moonpay_alert_text": "Tutar ${minAmount} ${fiatCurrency} miktarına eşit veya daha fazla olmalıdır", "moonpay_alert_text": "Tutar ${minAmount} ${fiatCurrency} miktarına eşit veya daha fazla olmalıdır",
"more_options": "Daha Fazla Seçenek", "more_options": "Daha Fazla Seçenek",
"name": "İsim", "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_current_rep": "Mevcut temsilci",
"nano_pick_new_rep": "Yeni bir temsilci seçin", "nano_pick_new_rep": "Yeni bir temsilci seçin",
"narrow": "Dar", "narrow": "Dar",

View file

@ -366,6 +366,8 @@
"moonpay_alert_text": "Значення суми має бути більшим або дорівнювати ${minAmount} ${fiatCurrency}", "moonpay_alert_text": "Значення суми має бути більшим або дорівнювати ${minAmount} ${fiatCurrency}",
"more_options": "Більше параметрів", "more_options": "Більше параметрів",
"name": "Ім'я", "name": "Ім'я",
"nano_gpt_thanks_message": "Дякуємо за використання наногпта! Не забудьте повернутися до браузера після завершення транзакції!",
"nanogpt_subtitle": "Усі найновіші моделі (GPT-4, Claude). \\ Nno підписка, оплата криптовалютою.",
"nano_current_rep": "Поточний представник", "nano_current_rep": "Поточний представник",
"nano_pick_new_rep": "Виберіть нового представника", "nano_pick_new_rep": "Виберіть нового представника",
"narrow": "вузькі", "narrow": "вузькі",

View file

@ -366,6 +366,8 @@
"moonpay_alert_text": "رقم کی قدر ${minAmount} ${fiatCurrency} کے برابر یا زیادہ ہونی چاہیے۔", "moonpay_alert_text": "رقم کی قدر ${minAmount} ${fiatCurrency} کے برابر یا زیادہ ہونی چاہیے۔",
"more_options": "مزید زرائے", "more_options": "مزید زرائے",
"name": "ﻡﺎﻧ", "name": "ﻡﺎﻧ",
"nano_gpt_thanks_message": "نانوگپٹ استعمال کرنے کا شکریہ! اپنے لین دین کی تکمیل کے بعد براؤزر کی طرف واپس جانا یاد رکھیں!",
"nanogpt_subtitle": "تمام تازہ ترین ماڈل (GPT-4 ، کلاڈ)۔ n n no سبسکرپشن ، کریپٹو کے ساتھ ادائیگی کریں۔",
"nano_current_rep": "موجودہ نمائندہ", "nano_current_rep": "موجودہ نمائندہ",
"nano_pick_new_rep": "ایک نیا نمائندہ منتخب کریں", "nano_pick_new_rep": "ایک نیا نمائندہ منتخب کریں",
"narrow": "تنگ", "narrow": "تنگ",

View file

@ -367,6 +367,8 @@
"moonpay_alert_text": "Iye owó kò gbọ́dọ̀ kéré ju ${minAmount} ${fiatCurrency}", "moonpay_alert_text": "Iye owó kò gbọ́dọ̀ kéré ju ${minAmount} ${fiatCurrency}",
"more_options": "Ìyàn àfikún", "more_options": "Ìyàn àfikún",
"name": "Oruko", "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_current_rep": "Aṣoju lọwọlọwọ",
"nano_pick_new_rep": "Mu aṣoju tuntun kan", "nano_pick_new_rep": "Mu aṣoju tuntun kan",
"narrow": "Taara", "narrow": "Taara",

View file

@ -366,6 +366,8 @@
"moonpay_alert_text": "金额的价值必须大于或等于 ${minAmount} ${fiatCurrency}", "moonpay_alert_text": "金额的价值必须大于或等于 ${minAmount} ${fiatCurrency}",
"more_options": "更多选项", "more_options": "更多选项",
"name": "姓名", "name": "姓名",
"nano_gpt_thanks_message": "感谢您使用Nanogpt事务完成后请记住回到浏览器",
"nanogpt_subtitle": "所有最新型号GPT-4Claude。\\ nno订阅用加密货币付款。",
"nano_current_rep": "当前代表", "nano_current_rep": "当前代表",
"nano_pick_new_rep": "选择新代表", "nano_pick_new_rep": "选择新代表",
"narrow": "狭窄的", "narrow": "狭窄的",

View file

@ -6,6 +6,7 @@ import 'utils/utils.dart';
const configPath = 'tool/.secrets-config.json'; const configPath = 'tool/.secrets-config.json';
const evmChainsConfigPath = 'tool/.evm-secrets-config.json'; const evmChainsConfigPath = 'tool/.evm-secrets-config.json';
const solanaConfigPath = 'tool/.solana-secrets-config.json'; const solanaConfigPath = 'tool/.solana-secrets-config.json';
const nanoConfigPath = 'tool/.nano-secrets-config.json';
const tronConfigPath = 'tool/.tron-secrets-config.json'; const tronConfigPath = 'tool/.tron-secrets-config.json';
Future<void> main(List<String> args) async => generateSecretsConfig(args); Future<void> main(List<String> args) async => generateSecretsConfig(args);
@ -21,6 +22,7 @@ Future<void> generateSecretsConfig(List<String> args) async {
final configFile = File(configPath); final configFile = File(configPath);
final evmChainsConfigFile = File(evmChainsConfigPath); final evmChainsConfigFile = File(evmChainsConfigPath);
final solanaConfigFile = File(solanaConfigPath); final solanaConfigFile = File(solanaConfigPath);
final nanoConfigFile = File(nanoConfigPath);
final tronConfigFile = File(tronConfigPath); final tronConfigFile = File(tronConfigPath);
final secrets = <String, dynamic>{}; final secrets = <String, dynamic>{};
@ -42,45 +44,48 @@ Future<void> generateSecretsConfig(List<String> args) async {
} }
} }
// base:
SecretKey.base.forEach((sec) { SecretKey.base.forEach((sec) {
if (secrets[sec.name] != null) { if (secrets[sec.name] != null) {
return; return;
} }
secrets[sec.name] = sec.generate(); secrets[sec.name] = sec.generate();
}); });
var secretsJson = JsonEncoder.withIndent(' ').convert(secrets); var secretsJson = JsonEncoder.withIndent(' ').convert(secrets);
await configFile.writeAsString(secretsJson); await configFile.writeAsString(secretsJson);
secrets.clear(); secrets.clear();
// evm chains:
SecretKey.evmChainsSecrets.forEach((sec) { SecretKey.evmChainsSecrets.forEach((sec) {
if (secrets[sec.name] != null) { if (secrets[sec.name] != null) {
return; return;
} }
secrets[sec.name] = sec.generate(); secrets[sec.name] = sec.generate();
}); });
secretsJson = JsonEncoder.withIndent(' ').convert(secrets); secretsJson = JsonEncoder.withIndent(' ').convert(secrets);
await evmChainsConfigFile.writeAsString(secretsJson); await evmChainsConfigFile.writeAsString(secretsJson);
secrets.clear(); secrets.clear();
// solana:
SecretKey.solanaSecrets.forEach((sec) { SecretKey.solanaSecrets.forEach((sec) {
if (secrets[sec.name] != null) { if (secrets[sec.name] != null) {
return; return;
} }
secrets[sec.name] = sec.generate(); secrets[sec.name] = sec.generate();
}); });
secretsJson = JsonEncoder.withIndent(' ').convert(secrets); secretsJson = JsonEncoder.withIndent(' ').convert(secrets);
await solanaConfigFile.writeAsString(secretsJson); 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(); secrets.clear();
SecretKey.tronSecrets.forEach((sec) { SecretKey.tronSecrets.forEach((sec) {
@ -90,8 +95,7 @@ Future<void> generateSecretsConfig(List<String> args) async {
secrets[sec.name] = sec.generate(); secrets[sec.name] = sec.generate();
}); });
secretsJson = JsonEncoder.withIndent(' ').convert(secrets); secretsJson = JsonEncoder.withIndent(' ').convert(secrets);
await tronConfigFile.writeAsString(secretsJson); await tronConfigFile.writeAsString(secretsJson);
secrets.clear();
} }

View file

@ -50,6 +50,10 @@ class SecretKey {
SecretKey('ankrApiKey', () => ''), SecretKey('ankrApiKey', () => ''),
]; ];
static final nanoSecrets = [
SecretKey('nano2ApiKey', () => ''),
];
static final tronSecrets = [ static final tronSecrets = [
SecretKey('tronGridApiKey', () => ''), SecretKey('tronGridApiKey', () => ''),
]; ];