mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2025-01-03 09:29:48 +00:00
Merge branch 'main' of https://github.com/cake-tech/cake_wallet into CW-565-sign-messages [skip ci]
This commit is contained in:
commit
11455a8ba2
62 changed files with 678 additions and 268 deletions
1
.github/workflows/pr_test_build.yml
vendored
1
.github/workflows/pr_test_build.yml
vendored
|
@ -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
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -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
|
||||
|
|
|
@ -91,6 +91,13 @@
|
|||
<data android:scheme="tron-wallet" />
|
||||
<data android:scheme="tron_wallet" />
|
||||
</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>
|
||||
<meta-data
|
||||
android:name="flutterEmbedding"
|
||||
|
|
5
assets/banano_node_list.yml
Normal file
5
assets/banano_node_list.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
-
|
||||
uri: kaliumapi.appditto.com
|
||||
path: /api
|
||||
useSSL: true
|
||||
is_default: true
|
|
@ -2,3 +2,7 @@
|
|||
uri: api.trongrid.io
|
||||
is_default: true
|
||||
useSSL: true
|
||||
-
|
||||
uri: tron-rpc.publicnode.com:443
|
||||
is_default: false
|
||||
useSSL: true
|
|
@ -49,6 +49,9 @@ dev_dependencies:
|
|||
mobx_codegen: ^2.0.7
|
||||
hive_generator: ^1.1.3
|
||||
|
||||
dependency_overrides:
|
||||
watcher: ^1.1.0
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
||||
|
|
|
@ -259,10 +259,16 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> 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()]!;
|
||||
}
|
||||
|
||||
|
|
22
cw_core/lib/window_size.dart
Normal file
22
cw_core/lib/window_size.dart
Normal file
|
@ -0,0 +1,22 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
const MethodChannel _channel = MethodChannel('com.cake_wallet/native_utils');
|
||||
|
||||
Future<void> setDefaultMinimumWindowSize() async {
|
||||
if (!Platform.isMacOS) return;
|
||||
|
||||
try {
|
||||
final result = await _channel.invokeMethod(
|
||||
'setMinWindowSize',
|
||||
{'width': 500, 'height': 700},
|
||||
) as bool;
|
||||
|
||||
if (!result) {
|
||||
print("Failed to set minimum window size.");
|
||||
}
|
||||
} on PlatformException catch (e) {
|
||||
print("Failed to set minimum window size: '${e.message}'.");
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -10,6 +10,7 @@ import 'package:http/http.dart' as http;
|
|||
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<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 {
|
||||
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<String, dynamic>);
|
||||
} catch (e) {
|
||||
print("error while getting account info");
|
||||
print("error while getting account info $e");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -170,7 +180,7 @@ class NanoClient {
|
|||
Future<String> requestWork(String hash) async {
|
||||
final response = await http.post(
|
||||
_powNode!.uri,
|
||||
headers: CAKE_HEADERS,
|
||||
headers: getHeaders(),
|
||||
body: json.encode(
|
||||
{
|
||||
"action": "work_generate",
|
||||
|
@ -213,7 +223,7 @@ class NanoClient {
|
|||
|
||||
final processResponse = await http.post(
|
||||
_node!.uri,
|
||||
headers: CAKE_HEADERS,
|
||||
headers: getHeaders(),
|
||||
body: processBody,
|
||||
);
|
||||
|
||||
|
@ -412,7 +422,7 @@ class NanoClient {
|
|||
});
|
||||
final processResponse = await http.post(
|
||||
_node!.uri,
|
||||
headers: CAKE_HEADERS,
|
||||
headers: getHeaders(),
|
||||
body: processBody,
|
||||
);
|
||||
|
||||
|
@ -428,7 +438,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,
|
||||
|
@ -476,7 +486,7 @@ class NanoClient {
|
|||
Future<List<NanoTransactionModel>> fetchTransactions(String address) async {
|
||||
try {
|
||||
final response = await http.post(_node!.uri,
|
||||
headers: CAKE_HEADERS,
|
||||
headers: getHeaders(),
|
||||
body: jsonEncode({
|
||||
"action": "account_history",
|
||||
"account": address,
|
||||
|
|
|
@ -367,7 +367,7 @@ class TronClient {
|
|||
) async {
|
||||
// This is introduce to server as a limit in cases where feeLimit is 0
|
||||
// The transaction signing will fail if the feeLimit is explicitly 0.
|
||||
int defaultFeeLimit = 100000;
|
||||
int defaultFeeLimit = 269000;
|
||||
|
||||
final block = await _provider!.request(TronRequestGetNowBlock());
|
||||
// Create the transfer contract
|
||||
|
@ -401,8 +401,9 @@ class TronClient {
|
|||
final tronBalanceInt = tronBalance.toInt();
|
||||
|
||||
if (feeLimit > tronBalanceInt) {
|
||||
final feeInTrx = TronHelper.fromSun(BigInt.parse(feeLimit.toString()));
|
||||
throw Exception(
|
||||
'You don\'t have enough TRX to cover the transaction fee for this transaction. Kindly top up.',
|
||||
'You don\'t have enough TRX to cover the transaction fee for this transaction. Kindly top up.\nTransaction fee: $feeInTrx TRX',
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -442,6 +443,9 @@ class TronClient {
|
|||
|
||||
if (!request.isSuccess) {
|
||||
log("Tron TRC20 error: ${request.error} \n ${request.respose}");
|
||||
throw Exception(
|
||||
'An error occured while creating the transfer request. Please try again.',
|
||||
);
|
||||
}
|
||||
|
||||
final feeLimit = await getFeeLimit(
|
||||
|
@ -454,8 +458,9 @@ class TronClient {
|
|||
final tronBalanceInt = tronBalance.toInt();
|
||||
|
||||
if (feeLimit > tronBalanceInt) {
|
||||
final feeInTrx = TronHelper.fromSun(BigInt.parse(feeLimit.toString()));
|
||||
throw Exception(
|
||||
'You don\'t have enough TRX to cover the transaction fee for this transaction. Kindly top up.',
|
||||
'You don\'t have enough TRX to cover the transaction fee for this transaction. Kindly top up. Transaction fee: $feeInTrx TRX',
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -140,6 +140,16 @@
|
|||
<string>nano-wallet</string>
|
||||
</array>
|
||||
</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>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
|
|
45
lib/di.dart
45
lib/di.dart
|
@ -28,6 +28,7 @@ import 'package:cake_wallet/entities/exchange_api_mode.dart';
|
|||
import 'package:cake_wallet/entities/parse_address_from_domain.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/sign_page.dart';
|
||||
import 'package:cake_wallet/src/screens/receive/address_list_page.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:cake_wallet/view_model/dashboard/sign_view_model.dart';
|
||||
|
@ -271,6 +272,7 @@ Future<void> setup({
|
|||
required Box<UnspentCoinsInfo> unspentCoinsInfoSource,
|
||||
required Box<AnonpayInvoiceInfo> anonpayInvoiceInfoSource,
|
||||
required FlutterSecureStorage secureStorage,
|
||||
required GlobalKey<NavigatorState> navigatorKey,
|
||||
}) async {
|
||||
_walletInfoSource = walletInfoSource;
|
||||
_nodeSource = nodeSource;
|
||||
|
@ -432,12 +434,21 @@ Future<void> setup({
|
|||
),
|
||||
);
|
||||
|
||||
getIt.registerFactory<AuthPage>(() {
|
||||
return AuthPage(getIt.get<AuthViewModel>(),
|
||||
getIt.registerLazySingleton<LinkViewModel>(() {
|
||||
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) {
|
||||
if (!isAuthenticated) {
|
||||
return;
|
||||
} else {
|
||||
}
|
||||
final authStore = getIt.get<AuthenticationStore>();
|
||||
final appStore = getIt.get<AppStore>();
|
||||
final useTotp = appStore.settingsStore.useTOTP2FA;
|
||||
|
@ -449,8 +460,8 @@ Future<void> setup({
|
|||
arguments: TotpAuthArgumentsModel(
|
||||
isForSetup: false,
|
||||
isClosable: false,
|
||||
onTotpAuthenticationFinished: (bool isAuthenticatedSuccessfully,
|
||||
TotpAuthCodePageState totpAuthPageState) async {
|
||||
onTotpAuthenticationFinished:
|
||||
(bool isAuthenticatedSuccessfully, TotpAuthCodePageState totpAuthPageState) async {
|
||||
if (!isAuthenticatedSuccessfully) {
|
||||
return;
|
||||
}
|
||||
|
@ -474,11 +485,20 @@ Future<void> setup({
|
|||
),
|
||||
);
|
||||
} else {
|
||||
// wallet is already loaded:
|
||||
if (appStore.wallet != null) {
|
||||
// goes to the dashboard:
|
||||
authStore.allowed();
|
||||
// trigger any deep links:
|
||||
final linkViewModel = getIt.get<LinkViewModel>();
|
||||
if (linkViewModel.currentLink != null) {
|
||||
linkViewModel.handleLink();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// load the wallet:
|
||||
|
||||
authPageState.changeProcessText('Loading the wallet');
|
||||
|
||||
if (loginError != null) {
|
||||
|
@ -489,11 +509,14 @@ Future<void> setup({
|
|||
_reaction = reaction((_) => appStore.wallet, (Object? _) {
|
||||
_reaction?.reaction.dispose();
|
||||
authStore.allowed();
|
||||
final linkViewModel = getIt.get<LinkViewModel>();
|
||||
if (linkViewModel.currentLink != null) {
|
||||
linkViewModel.handleLink();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}, closable: false);
|
||||
}, instanceName: 'login');
|
||||
});
|
||||
});
|
||||
|
||||
getIt.registerSingleton<BottomSheetService>(BottomSheetServiceImpl());
|
||||
|
||||
|
@ -854,8 +877,10 @@ Future<void> setup({
|
|||
tradesStore: getIt.get<TradesStore>(),
|
||||
sendViewModel: getIt.get<SendViewModel>()));
|
||||
|
||||
getIt.registerFactory(
|
||||
() => ExchangePage(getIt.get<ExchangeViewModel>(), getIt.get<AuthService>()));
|
||||
getIt.registerFactoryParam<ExchangePage, PaymentRequest?, void>(
|
||||
(PaymentRequest? paymentRequest, __) {
|
||||
return ExchangePage(getIt.get<ExchangeViewModel>(), getIt.get<AuthService>(), paymentRequest);
|
||||
});
|
||||
|
||||
getIt.registerFactory(() => ExchangeConfirmPage(tradesStore: getIt.get<TradesStore>()));
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'package:cake_wallet/entities/openalias_record.dart';
|
|||
import 'package:cake_wallet/entities/parsed_address.dart';
|
||||
import 'package:cake_wallet/entities/unstoppable_domain_address.dart';
|
||||
import 'package:cake_wallet/entities/emoji_string_extension.dart';
|
||||
import 'package:cake_wallet/exchange/provider/thorchain_exchange.provider.dart';
|
||||
import 'package:cake_wallet/mastodon/mastodon_api.dart';
|
||||
import 'package:cake_wallet/nostr/nostr_api.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
|
@ -71,8 +72,8 @@ class AddressResolver {
|
|||
return emailRegex.hasMatch(address);
|
||||
}
|
||||
|
||||
// TODO: refactor this to take Crypto currency instead of ticker, or at least pass in the tag as well
|
||||
Future<ParsedAddress> resolve(BuildContext context, String text, String ticker) async {
|
||||
Future<ParsedAddress> resolve(BuildContext context, String text, CryptoCurrency currency) async {
|
||||
final ticker = currency.title;
|
||||
try {
|
||||
if (text.startsWith('@') && !text.substring(1).contains('@')) {
|
||||
if (settingsStore.lookupsTwitter) {
|
||||
|
@ -116,8 +117,7 @@ class AddressResolver {
|
|||
await MastodonAPI.lookupUserByUserName(userName: userName, apiHost: hostName);
|
||||
|
||||
if (mastodonUser != null) {
|
||||
String? addressFromBio = extractAddressByType(
|
||||
raw: mastodonUser.note, type: CryptoCurrency.fromString(ticker));
|
||||
String? addressFromBio = extractAddressByType(raw: mastodonUser.note, type: currency);
|
||||
|
||||
if (addressFromBio != null) {
|
||||
return ParsedAddress.fetchMastodonAddress(
|
||||
|
@ -131,8 +131,8 @@ class AddressResolver {
|
|||
|
||||
if (pinnedPosts.isNotEmpty) {
|
||||
final userPinnedPostsText = pinnedPosts.map((item) => item.content).join('\n');
|
||||
String? addressFromPinnedPost = extractAddressByType(
|
||||
raw: userPinnedPostsText, type: CryptoCurrency.fromString(ticker));
|
||||
String? addressFromPinnedPost =
|
||||
extractAddressByType(raw: userPinnedPostsText, type: currency);
|
||||
|
||||
if (addressFromPinnedPost != null) {
|
||||
return ParsedAddress.fetchMastodonAddress(
|
||||
|
@ -162,6 +162,16 @@ class AddressResolver {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
final thorChainAddress = await ThorChainExchangeProvider.lookupAddressByName(text);
|
||||
if (thorChainAddress != null) {
|
||||
String? address =
|
||||
thorChainAddress[ticker] ?? (ticker == 'RUNE' ? thorChainAddress['THOR'] : null);
|
||||
if (address != null) {
|
||||
return ParsedAddress.thorChainAddress(address: address, name: text);
|
||||
}
|
||||
}
|
||||
|
||||
final formattedName = OpenaliasRecord.formatDomainName(text);
|
||||
final domainParts = formattedName.split('.');
|
||||
final name = domainParts.last;
|
||||
|
@ -204,7 +214,7 @@ class AddressResolver {
|
|||
|
||||
if (nostrUserData != null) {
|
||||
String? addressFromBio = extractAddressByType(
|
||||
raw: nostrUserData.about, type: CryptoCurrency.fromString(ticker));
|
||||
raw: nostrUserData.about, type: currency);
|
||||
if (addressFromBio != null) {
|
||||
return ParsedAddress.nostrAddress(
|
||||
address: addressFromBio,
|
||||
|
|
|
@ -11,7 +11,8 @@ enum ParseFrom {
|
|||
ens,
|
||||
contact,
|
||||
mastodon,
|
||||
nostr
|
||||
nostr,
|
||||
thorChain
|
||||
}
|
||||
|
||||
class ParsedAddress {
|
||||
|
@ -133,6 +134,14 @@ class ParsedAddress {
|
|||
);
|
||||
}
|
||||
|
||||
factory ParsedAddress.thorChainAddress({required String address, required String name}) {
|
||||
return ParsedAddress(
|
||||
addresses: [address],
|
||||
name: name,
|
||||
parseFrom: ParseFrom.thorChain,
|
||||
);
|
||||
}
|
||||
|
||||
final List<String> addresses;
|
||||
final String name;
|
||||
final String description;
|
||||
|
|
|
@ -34,11 +34,13 @@ class ThorChainExchangeProvider extends ExchangeProvider {
|
|||
|
||||
static final isRefundAddressSupported = [CryptoCurrency.eth];
|
||||
|
||||
static const _baseURL = 'thornode.ninerealms.com';
|
||||
static const _baseNodeURL = 'thornode.ninerealms.com';
|
||||
static const _baseURL = 'midgard.ninerealms.com';
|
||||
static const _quotePath = '/thorchain/quote/swap';
|
||||
static const _txInfoPath = '/thorchain/tx/status/';
|
||||
static const _affiliateName = 'cakewallet';
|
||||
static const _affiliateBps = '175';
|
||||
static const _nameLookUpPath= 'v2/thorname/lookup/';
|
||||
|
||||
final Box<Trade> tradesStore;
|
||||
|
||||
|
@ -154,7 +156,7 @@ class ThorChainExchangeProvider extends ExchangeProvider {
|
|||
Future<Trade> findTradeById({required String id}) async {
|
||||
if (id.isEmpty) throw Exception('Trade id is empty');
|
||||
final formattedId = id.startsWith('0x') ? id.substring(2) : id;
|
||||
final uri = Uri.https(_baseURL, '$_txInfoPath$formattedId');
|
||||
final uri = Uri.https(_baseNodeURL, '$_txInfoPath$formattedId');
|
||||
final response = await http.get(uri);
|
||||
|
||||
if (response.statusCode == 404) {
|
||||
|
@ -206,8 +208,35 @@ class ThorChainExchangeProvider extends ExchangeProvider {
|
|||
);
|
||||
}
|
||||
|
||||
static Future<Map<String, String>?>? lookupAddressByName(String name) async {
|
||||
final uri = Uri.https(_baseURL, '$_nameLookUpPath$name');
|
||||
final response = await http.get(uri);
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final body = json.decode(response.body) as Map<String, dynamic>;
|
||||
final entries = body['entries'] as List<dynamic>?;
|
||||
|
||||
if (entries == null || entries.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Map<String, String> chainToAddressMap = {};
|
||||
|
||||
for (final entry in entries) {
|
||||
final chain = entry['chain'] as String;
|
||||
final address = entry['address'] as String;
|
||||
chainToAddressMap[chain] = address;
|
||||
}
|
||||
|
||||
return chainToAddressMap;
|
||||
}
|
||||
|
||||
|
||||
Future<Map<String, dynamic>> _getSwapQuote(Map<String, String> params) async {
|
||||
Uri uri = Uri.https(_baseURL, _quotePath, params);
|
||||
Uri uri = Uri.https(_baseNodeURL, _quotePath, params);
|
||||
|
||||
final response = await http.get(uri);
|
||||
|
||||
|
|
|
@ -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';
|
||||
|
@ -41,6 +42,7 @@ import 'package:uni_links/uni_links.dart';
|
|||
import 'package:cw_core/unspent_coins_info.dart';
|
||||
import 'package:cake_wallet/monero/monero.dart';
|
||||
import 'package:cw_core/cake_hive.dart';
|
||||
import 'package:cw_core/window_size.dart';
|
||||
|
||||
final navigatorKey = GlobalKey<NavigatorState>();
|
||||
final rootKey = GlobalKey<RootState>();
|
||||
|
@ -60,6 +62,8 @@ Future<void> main() async {
|
|||
return true;
|
||||
};
|
||||
|
||||
await setDefaultMinimumWindowSize();
|
||||
|
||||
await CakeHive.close();
|
||||
|
||||
await initializeAppConfigs();
|
||||
|
@ -213,7 +217,9 @@ Future<void> initialSetup(
|
|||
ordersSource: ordersSource,
|
||||
anonpayInvoiceInfoSource: anonpayInvoiceInfo,
|
||||
unspentCoinsInfoSource: unspentCoinsInfoSource,
|
||||
secureStorage: secureStorage);
|
||||
secureStorage: secureStorage,
|
||||
navigatorKey: navigatorKey,
|
||||
);
|
||||
await bootstrap(navigatorKey);
|
||||
monero?.onStartup();
|
||||
}
|
||||
|
@ -284,6 +290,7 @@ class AppState extends State<App> with SingleTickerProviderStateMixin {
|
|||
return Observer(builder: (BuildContext context) {
|
||||
final appStore = getIt.get<AppStore>();
|
||||
final authService = getIt.get<AuthService>();
|
||||
final linkViewModel = getIt.get<LinkViewModel>();
|
||||
final settingsStore = appStore.settingsStore;
|
||||
final statusBarColor = Colors.transparent;
|
||||
final authenticationStore = getIt.get<AuthenticationStore>();
|
||||
|
@ -306,6 +313,7 @@ class AppState extends State<App> with SingleTickerProviderStateMixin {
|
|||
authenticationStore: authenticationStore,
|
||||
navigatorKey: navigatorKey,
|
||||
authService: authService,
|
||||
linkViewModel: linkViewModel,
|
||||
child: MaterialApp(
|
||||
navigatorObservers: [routeObserver],
|
||||
navigatorKey: navigatorKey,
|
||||
|
|
|
@ -225,7 +225,8 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
|||
return CupertinoPageRoute<void>(
|
||||
builder: (_) => getIt.get<SetupPinCodePage>(
|
||||
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,
|
||||
);
|
||||
|
@ -235,8 +236,8 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
|||
builder: (_) => ConnectDevicePage(
|
||||
ConnectDevicePageParams(
|
||||
walletType: availableWalletTypes.first,
|
||||
onConnectDevice: (BuildContext context, _) =>
|
||||
Navigator.of(context).pushNamed(Routes.chooseHardwareWalletAccount,
|
||||
onConnectDevice: (BuildContext context, _) => Navigator.of(context).pushNamed(
|
||||
Routes.chooseHardwareWalletAccount,
|
||||
arguments: [availableWalletTypes.first]),
|
||||
),
|
||||
getIt.get<LedgerViewModel>(),
|
||||
|
@ -247,9 +248,8 @@ Route<dynamic> 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);
|
||||
|
@ -467,7 +467,9 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
|||
|
||||
case Routes.exchange:
|
||||
return CupertinoPageRoute<void>(
|
||||
fullscreenDialog: true, builder: (_) => getIt.get<ExchangePage>());
|
||||
fullscreenDialog: true,
|
||||
builder: (_) => getIt.get<ExchangePage>(param1: settings.arguments as PaymentRequest?),
|
||||
);
|
||||
|
||||
case Routes.exchangeTemplate:
|
||||
return CupertinoPageRoute<void>(builder: (_) => getIt.get<ExchangeTemplatePage>());
|
||||
|
|
|
@ -22,7 +22,7 @@ class DropDownItemWidget extends StatelessWidget {
|
|||
child: Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 22,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
|
||||
),
|
||||
|
|
|
@ -60,12 +60,15 @@ class MarketPlacePage extends StatelessWidget {
|
|||
// ),
|
||||
SizedBox(height: 20),
|
||||
DashBoardRoundedCardWidget(
|
||||
onTap: () => launchUrl(
|
||||
Uri.https("buy.cakepay.com"),
|
||||
mode: LaunchMode.externalApplication,
|
||||
),
|
||||
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"),
|
||||
),
|
||||
|
||||
SizedBox(height: 20),
|
||||
|
@ -92,6 +95,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
|
||||
void _navigatorToGiftCardsPage(BuildContext context) {
|
||||
final walletType = dashboardViewModel.type;
|
||||
|
|
|
@ -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<ExchangeCardState>();
|
||||
final receiveKey = GlobalKey<ExchangeCardState>();
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
|
@ -330,10 +332,12 @@ class ExchangePage extends BasePage {
|
|||
|
||||
void applyTemplate(
|
||||
BuildContext context, ExchangeViewModel exchangeViewModel, ExchangeTemplate template) async {
|
||||
exchangeViewModel.changeDepositCurrency(
|
||||
currency: CryptoCurrency.fromString(template.depositCurrency));
|
||||
exchangeViewModel.changeReceiveCurrency(
|
||||
currency: CryptoCurrency.fromString(template.receiveCurrency));
|
||||
|
||||
final depositCryptoCurrency = CryptoCurrency.fromString(template.depositCurrency);
|
||||
final receiveCryptoCurrency = CryptoCurrency.fromString(template.receiveCurrency);
|
||||
|
||||
exchangeViewModel.changeDepositCurrency(currency: depositCryptoCurrency);
|
||||
exchangeViewModel.changeReceiveCurrency(currency: receiveCryptoCurrency);
|
||||
|
||||
exchangeViewModel.changeDepositAmount(amount: template.amount);
|
||||
exchangeViewModel.depositAddress = template.depositAddress;
|
||||
|
@ -342,12 +346,10 @@ class ExchangePage extends BasePage {
|
|||
exchangeViewModel.isFixedRateMode = false;
|
||||
|
||||
var domain = template.depositAddress;
|
||||
var ticker = template.depositCurrency.toLowerCase();
|
||||
exchangeViewModel.depositAddress = await fetchParsedAddress(context, domain, ticker);
|
||||
exchangeViewModel.depositAddress = await fetchParsedAddress(context, domain, depositCryptoCurrency);
|
||||
|
||||
domain = template.receiveAddress;
|
||||
ticker = template.receiveCurrency.toLowerCase();
|
||||
exchangeViewModel.receiveAddress = await fetchParsedAddress(context, domain, ticker);
|
||||
exchangeViewModel.receiveAddress = await fetchParsedAddress(context, domain, receiveCryptoCurrency);
|
||||
}
|
||||
|
||||
void _setReactions(BuildContext context, ExchangeViewModel exchangeViewModel) {
|
||||
|
@ -519,16 +521,14 @@ class ExchangePage extends BasePage {
|
|||
_depositAddressFocus.addListener(() async {
|
||||
if (!_depositAddressFocus.hasFocus && depositAddressController.text.isNotEmpty) {
|
||||
final domain = depositAddressController.text;
|
||||
final ticker = exchangeViewModel.depositCurrency.title.toLowerCase();
|
||||
exchangeViewModel.depositAddress = await fetchParsedAddress(context, domain, ticker);
|
||||
exchangeViewModel.depositAddress = await fetchParsedAddress(context, domain, exchangeViewModel.depositCurrency);
|
||||
}
|
||||
});
|
||||
|
||||
_receiveAddressFocus.addListener(() async {
|
||||
if (!_receiveAddressFocus.hasFocus && receiveAddressController.text.isNotEmpty) {
|
||||
final domain = receiveAddressController.text;
|
||||
final ticker = exchangeViewModel.receiveCurrency.title.toLowerCase();
|
||||
exchangeViewModel.receiveAddress = await fetchParsedAddress(context, domain, ticker);
|
||||
exchangeViewModel.receiveAddress = await fetchParsedAddress(context, domain, exchangeViewModel.receiveCurrency);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -545,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;
|
||||
}
|
||||
|
||||
|
@ -575,8 +581,8 @@ class ExchangePage extends BasePage {
|
|||
}
|
||||
}
|
||||
|
||||
Future<String> fetchParsedAddress(BuildContext context, String domain, String ticker) async {
|
||||
final parsedAddress = await getIt.get<AddressResolver>().resolve(context, domain, ticker);
|
||||
Future<String> fetchParsedAddress(BuildContext context, String domain, CryptoCurrency currency) async {
|
||||
final parsedAddress = await getIt.get<AddressResolver>().resolve(context, domain, currency);
|
||||
final address = await extractAddressFromParsed(context, parsedAddress);
|
||||
return address;
|
||||
}
|
||||
|
@ -663,15 +669,13 @@ class ExchangePage extends BasePage {
|
|||
addressTextFieldValidator: AddressValidator(type: exchangeViewModel.depositCurrency),
|
||||
onPushPasteButton: (context) async {
|
||||
final domain = exchangeViewModel.depositAddress;
|
||||
final ticker = exchangeViewModel.depositCurrency.title.toLowerCase();
|
||||
exchangeViewModel.depositAddress =
|
||||
await fetchParsedAddress(context, domain, ticker);
|
||||
await fetchParsedAddress(context, domain, exchangeViewModel.depositCurrency);
|
||||
},
|
||||
onPushAddressBookButton: (context) async {
|
||||
final domain = exchangeViewModel.depositAddress;
|
||||
final ticker = exchangeViewModel.depositCurrency.title.toLowerCase();
|
||||
exchangeViewModel.depositAddress =
|
||||
await fetchParsedAddress(context, domain, ticker);
|
||||
await fetchParsedAddress(context, domain, exchangeViewModel.depositCurrency);
|
||||
},
|
||||
));
|
||||
|
||||
|
@ -712,15 +716,13 @@ class ExchangePage extends BasePage {
|
|||
addressTextFieldValidator: AddressValidator(type: exchangeViewModel.receiveCurrency),
|
||||
onPushPasteButton: (context) async {
|
||||
final domain = exchangeViewModel.receiveAddress;
|
||||
final ticker = exchangeViewModel.receiveCurrency.title.toLowerCase();
|
||||
exchangeViewModel.receiveAddress =
|
||||
await fetchParsedAddress(context, domain, ticker);
|
||||
await fetchParsedAddress(context, domain, exchangeViewModel.receiveCurrency);
|
||||
},
|
||||
onPushAddressBookButton: (context) async {
|
||||
final domain = exchangeViewModel.receiveAddress;
|
||||
final ticker = exchangeViewModel.receiveCurrency.title.toLowerCase();
|
||||
exchangeViewModel.receiveAddress =
|
||||
await fetchParsedAddress(context, domain, ticker);
|
||||
await fetchParsedAddress(context, domain, exchangeViewModel.receiveCurrency);
|
||||
},
|
||||
));
|
||||
|
||||
|
|
|
@ -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<NavigatorState> navigatorKey;
|
||||
final AuthService authService;
|
||||
final Widget child;
|
||||
final LinkViewModel linkViewModel;
|
||||
|
||||
@override
|
||||
RootState createState() => RootState();
|
||||
|
@ -53,7 +56,6 @@ class RootState extends State<Root> with WidgetsBindingObserver {
|
|||
StreamSubscription<Uri?>? stream;
|
||||
ReactionDisposer? _walletReactionDisposer;
|
||||
ReactionDisposer? _deepLinksReactionDisposer;
|
||||
Uri? launchUri;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
@ -98,7 +100,7 @@ class RootState extends State<Root> 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<Root> 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<Root> 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,7 +162,7 @@ class RootState extends State<Root> with WidgetsBindingObserver {
|
|||
arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) {
|
||||
if (!isAuthenticatedSuccessfully) {
|
||||
return;
|
||||
} else {
|
||||
}
|
||||
final useTotp = widget.appStore.settingsStore.useTOTP2FA;
|
||||
final shouldUseTotp2FAToAccessWallets =
|
||||
widget.appStore.settingsStore.shouldRequireTOTP2FAForAccessingWallet;
|
||||
|
@ -174,11 +178,10 @@ class RootState extends State<Root> with WidgetsBindingObserver {
|
|||
}
|
||||
_reset();
|
||||
totpAuth.close(
|
||||
route: _getRouteToGo(),
|
||||
arguments:
|
||||
isWalletConnectLink ? launchUri : PaymentRequest.fromUri(launchUri),
|
||||
route: widget.linkViewModel.getRouteToGo(),
|
||||
arguments: widget.linkViewModel.getRouteArgs(),
|
||||
);
|
||||
launchUri = null;
|
||||
widget.linkViewModel.currentLink = null;
|
||||
},
|
||||
isForSetup: false,
|
||||
isClosable: false,
|
||||
|
@ -187,11 +190,10 @@ class RootState extends State<Root> with WidgetsBindingObserver {
|
|||
} else {
|
||||
_reset();
|
||||
auth.close(
|
||||
route: _getRouteToGo(),
|
||||
arguments: isWalletConnectLink ? launchUri : PaymentRequest.fromUri(launchUri),
|
||||
route: widget.linkViewModel.getRouteToGo(),
|
||||
arguments: widget.linkViewModel.getRouteArgs(),
|
||||
);
|
||||
launchUri = null;
|
||||
}
|
||||
widget.linkViewModel.currentLink = null;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
@ -216,36 +218,7 @@ class RootState extends State<Root> 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<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) {
|
||||
void waitForWalletInstance(BuildContext context) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (context.mounted) {
|
||||
_walletReactionDisposer = reaction(
|
||||
|
@ -263,14 +236,6 @@ class RootState extends State<Root> 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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,6 +35,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({
|
||||
|
@ -420,12 +422,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<void>(
|
||||
|
@ -460,10 +460,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<void>(
|
||||
await showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext _dialogContext) {
|
||||
return Observer(builder: (_) {
|
||||
|
@ -481,12 +481,14 @@ class SendPage extends BasePage {
|
|||
sendViewModel.selectedCryptoCurrency.toString());
|
||||
|
||||
final waitMessage = sendViewModel.walletType == WalletType.solana
|
||||
? '. ${S.of(_dialogContext).waitFewSecondForTxUpdate}' : '';
|
||||
? '. ${S.of(_dialogContext).waitFewSecondForTxUpdate}'
|
||||
: '';
|
||||
|
||||
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";
|
||||
|
||||
if (newContactAddress != null) {
|
||||
|
@ -509,6 +511,10 @@ class SendPage extends BasePage {
|
|||
newContactAddress = null;
|
||||
});
|
||||
} else {
|
||||
if (initialPaymentRequest?.callbackMessage?.isNotEmpty ??
|
||||
false) {
|
||||
alertContent = initialPaymentRequest!.callbackMessage!;
|
||||
}
|
||||
return AlertWithOneAction(
|
||||
alertTitle: '',
|
||||
alertContent: alertContent,
|
||||
|
@ -523,6 +529,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());
|
||||
});
|
||||
|
|
|
@ -56,6 +56,11 @@ Future<String> extractAddressFromParsed(
|
|||
profileImageUrl = parsedAddress.profileImageUrl;
|
||||
profileName = parsedAddress.profileName;
|
||||
break;
|
||||
case ParseFrom.thorChain:
|
||||
title = S.of(context).address_detected;
|
||||
content = S.of(context).extracted_address_content('${parsedAddress.name} (ThorChain)');
|
||||
address = parsedAddress.addresses.first;
|
||||
break;
|
||||
case ParseFrom.yatRecord:
|
||||
if (parsedAddress.name.isEmpty) {
|
||||
title = S.of(context).yat_error;
|
||||
|
|
|
@ -12,7 +12,6 @@ final _settingsNavigatorKey = GlobalKey<NavigatorState>();
|
|||
class DesktopSettingsPage extends StatefulWidget {
|
||||
const DesktopSettingsPage({super.key});
|
||||
|
||||
|
||||
@override
|
||||
State<DesktopSettingsPage> createState() => _DesktopSettingsPageState();
|
||||
}
|
||||
|
@ -33,22 +32,21 @@ class _DesktopSettingsPageState extends State<DesktopSettingsPage> {
|
|||
return Scaffold(
|
||||
body: Container(
|
||||
height: MediaQuery.of(context).size.height,
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(24),
|
||||
padding: const EdgeInsets.fromLTRB(24, 24, 24, 4),
|
||||
child: Text(
|
||||
S.current.settings,
|
||||
style: textXLarge(),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: ListView.separated(
|
||||
padding: EdgeInsets.only(top: 0),
|
||||
itemBuilder: (_, index) {
|
||||
|
@ -78,6 +76,9 @@ class _DesktopSettingsPageState extends State<DesktopSettingsPage> {
|
|||
itemCount: itemCount,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
flex: 2,
|
||||
child: ConstrainedBox(
|
||||
|
@ -99,9 +100,6 @@ class _DesktopSettingsPageState extends State<DesktopSettingsPage> {
|
|||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -171,7 +171,7 @@ class WalletListBodyState extends State<WalletListBody> {
|
|||
maxLines: null,
|
||||
softWrap: true,
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontSize: DeviceInfo.instance.isDesktop ? 18 : 20,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context)
|
||||
.extension<CakeTextTheme>()!
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
118
lib/view_model/link_view_model.dart
Normal file
118
lib/view_model/link_view_model.dart
Normal 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -296,8 +296,8 @@ abstract class OutputBase with Store {
|
|||
|
||||
Future<void> fetchParsedAddress(BuildContext context) async {
|
||||
final domain = address;
|
||||
final ticker = cryptoCurrencyHandler().title.toLowerCase();
|
||||
parsedAddress = await getIt.get<AddressResolver>().resolve(context, domain, ticker);
|
||||
final currency = cryptoCurrencyHandler();
|
||||
parsedAddress = await getIt.get<AddressResolver>().resolve(context, domain, currency);
|
||||
extractedAddress = await extractAddressFromParsed(context, parsedAddress);
|
||||
note = parsedAddress.description;
|
||||
}
|
||||
|
|
|
@ -363,7 +363,8 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
|||
} catch (e) {
|
||||
if (e is LedgerException) {
|
||||
final errorCode = e.errorCode.toRadixString(16);
|
||||
final fallbackMsg = e.message.isNotEmpty ? e.message : "Unexpected Ledger Error Code: $errorCode";
|
||||
final fallbackMsg =
|
||||
e.message.isNotEmpty ? e.message : "Unexpected Ledger Error Code: $errorCode";
|
||||
final errorMsg = ledgerViewModel.interpretErrorCode(errorCode) ?? fallbackMsg;
|
||||
|
||||
state = FailureState(errorMsg);
|
||||
|
@ -444,7 +445,10 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
|||
Object _credentials() {
|
||||
final priority = _settingsStore.priority[wallet.type];
|
||||
|
||||
if (priority == null && wallet.type != WalletType.nano && wallet.type != WalletType.banano && wallet.type != WalletType.solana &&
|
||||
if (priority == null &&
|
||||
wallet.type != WalletType.nano &&
|
||||
wallet.type != WalletType.banano &&
|
||||
wallet.type != WalletType.solana &&
|
||||
wallet.type != WalletType.tron) {
|
||||
throw Exception('Priority is null for wallet type: ${wallet.type}');
|
||||
}
|
||||
|
@ -570,6 +574,16 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
|||
return errorMessage;
|
||||
}
|
||||
|
||||
if (walletType == WalletType.tron) {
|
||||
if (errorMessage.contains('balance is not sufficient')) {
|
||||
return S.current.do_not_have_enough_gas_asset(currency.toString());
|
||||
}
|
||||
|
||||
if (errorMessage.contains('Transaction expired')) {
|
||||
return 'An error occurred while processing the transaction. Kindly retry the transaction';
|
||||
}
|
||||
}
|
||||
|
||||
if (walletType == WalletType.bitcoin ||
|
||||
walletType == WalletType.litecoin ||
|
||||
walletType == WalletType.bitcoinCash) {
|
||||
|
|
|
@ -10,6 +10,7 @@ import cw_monero
|
|||
import device_info_plus
|
||||
import devicelocale
|
||||
import flutter_inappwebview_macos
|
||||
import flutter_local_authentication
|
||||
import flutter_secure_storage_macos
|
||||
import in_app_review
|
||||
import package_info
|
||||
|
@ -26,6 +27,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
|||
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
||||
DevicelocalePlugin.register(with: registry.registrar(forPlugin: "DevicelocalePlugin"))
|
||||
InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin"))
|
||||
FlutterLocalAuthenticationPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalAuthenticationPlugin"))
|
||||
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
|
||||
InAppReviewPlugin.register(with: registry.registrar(forPlugin: "InAppReviewPlugin"))
|
||||
FLTPackageInfoPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlugin"))
|
||||
|
|
|
@ -26,6 +26,8 @@ PODS:
|
|||
- flutter_inappwebview_macos (0.0.1):
|
||||
- FlutterMacOS
|
||||
- OrderedSet (~> 5.0)
|
||||
- flutter_local_authentication (1.2.0):
|
||||
- FlutterMacOS
|
||||
- flutter_secure_storage_macos (6.1.1):
|
||||
- FlutterMacOS
|
||||
- FlutterMacOS (1.0.0)
|
||||
|
@ -56,6 +58,7 @@ DEPENDENCIES:
|
|||
- device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`)
|
||||
- devicelocale (from `Flutter/ephemeral/.symlinks/plugins/devicelocale/macos`)
|
||||
- flutter_inappwebview_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_inappwebview_macos/macos`)
|
||||
- flutter_local_authentication (from `Flutter/ephemeral/.symlinks/plugins/flutter_local_authentication/macos`)
|
||||
- flutter_secure_storage_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos`)
|
||||
- FlutterMacOS (from `Flutter/ephemeral`)
|
||||
- in_app_review (from `Flutter/ephemeral/.symlinks/plugins/in_app_review/macos`)
|
||||
|
@ -83,6 +86,8 @@ EXTERNAL SOURCES:
|
|||
:path: Flutter/ephemeral/.symlinks/plugins/devicelocale/macos
|
||||
flutter_inappwebview_macos:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/flutter_inappwebview_macos/macos
|
||||
flutter_local_authentication:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/flutter_local_authentication/macos
|
||||
flutter_secure_storage_macos:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos
|
||||
FlutterMacOS:
|
||||
|
@ -110,6 +115,7 @@ SPEC CHECKSUMS:
|
|||
device_info_plus: 5401765fde0b8d062a2f8eb65510fb17e77cf07f
|
||||
devicelocale: 9f0f36ac651cabae2c33f32dcff4f32b61c38225
|
||||
flutter_inappwebview_macos: 9600c9df9fdb346aaa8933812009f8d94304203d
|
||||
flutter_local_authentication: 85674893931e1c9cfa7c9e4f5973cb8c56b018b0
|
||||
flutter_secure_storage_macos: d56e2d218c1130b262bef8b4a7d64f88d7f9c9ea
|
||||
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
|
||||
in_app_review: a850789fad746e89bce03d4aeee8078b45a53fd0
|
||||
|
|
|
@ -24,7 +24,21 @@ class AppDelegate: FlutterAppDelegate {
|
|||
}
|
||||
|
||||
result(secRandom(count: count))
|
||||
|
||||
case "setMinWindowSize":
|
||||
guard let self = self else {
|
||||
result(false)
|
||||
return
|
||||
}
|
||||
if let arguments = call.arguments as? [String: Any],
|
||||
let width = arguments["width"] as? Double,
|
||||
let height = arguments["height"] as? Double {
|
||||
DispatchQueue.main.async {
|
||||
self.mainFlutterWindow?.minSize = CGSize(width: width, height: height)
|
||||
}
|
||||
result(true)
|
||||
} else {
|
||||
result(false)
|
||||
}
|
||||
default:
|
||||
result(FlutterMethodNotImplemented)
|
||||
}
|
||||
|
|
|
@ -367,6 +367,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": "ضيق",
|
||||
|
|
|
@ -367,6 +367,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": "Тесен",
|
||||
|
|
|
@ -367,6 +367,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ý",
|
||||
|
|
|
@ -367,6 +367,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",
|
||||
|
|
|
@ -367,6 +367,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",
|
||||
|
|
|
@ -367,6 +367,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",
|
||||
|
|
|
@ -367,6 +367,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",
|
||||
|
|
|
@ -367,6 +367,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",
|
||||
|
|
|
@ -367,6 +367,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": "सँकरा",
|
||||
|
|
|
@ -367,6 +367,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",
|
||||
|
|
|
@ -367,6 +367,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",
|
||||
|
|
|
@ -368,6 +368,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",
|
||||
|
|
|
@ -368,6 +368,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": "狭い",
|
||||
|
|
|
@ -367,6 +367,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": "좁은",
|
||||
|
|
|
@ -367,6 +367,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": "ကျဉ်းသော",
|
||||
|
|
|
@ -367,6 +367,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",
|
||||
|
|
|
@ -367,6 +367,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",
|
||||
|
|
|
@ -368,6 +368,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",
|
||||
|
|
|
@ -367,6 +367,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": "Узкий",
|
||||
|
|
|
@ -367,6 +367,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": "แคบ",
|
||||
|
|
|
@ -367,6 +367,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",
|
||||
|
|
|
@ -367,6 +367,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",
|
||||
|
|
|
@ -367,6 +367,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": "вузькі",
|
||||
|
|
|
@ -367,6 +367,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": "تنگ",
|
||||
|
|
|
@ -368,6 +368,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",
|
||||
|
|
|
@ -367,6 +367,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": "狭窄的",
|
||||
|
|
|
@ -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<void> main(List<String> args) async => generateSecretsConfig(args);
|
||||
|
@ -21,6 +22,7 @@ Future<void> generateSecretsConfig(List<String> args) async {
|
|||
final configFile = File(configPath);
|
||||
final evmChainsConfigFile = File(evmChainsConfigPath);
|
||||
final solanaConfigFile = File(solanaConfigPath);
|
||||
final nanoConfigFile = File(nanoConfigPath);
|
||||
final tronConfigFile = File(tronConfigPath);
|
||||
|
||||
final secrets = <String, dynamic>{};
|
||||
|
@ -42,45 +44,48 @@ Future<void> generateSecretsConfig(List<String> 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<void> generateSecretsConfig(List<String> args) async {
|
|||
|
||||
secrets[sec.name] = sec.generate();
|
||||
});
|
||||
|
||||
secretsJson = JsonEncoder.withIndent(' ').convert(secrets);
|
||||
|
||||
await tronConfigFile.writeAsString(secretsJson);
|
||||
secrets.clear();
|
||||
}
|
||||
|
|
|
@ -50,6 +50,10 @@ class SecretKey {
|
|||
SecretKey('ankrApiKey', () => ''),
|
||||
];
|
||||
|
||||
static final nanoSecrets = [
|
||||
SecretKey('nano2ApiKey', () => ''),
|
||||
];
|
||||
|
||||
static final tronSecrets = [
|
||||
SecretKey('tronGridApiKey', () => ''),
|
||||
];
|
||||
|
|
Loading…
Reference in a new issue