CW-527-Add-Polygon-MATIC-Wallet (#1179)

* chore: Initial setup for polygon package

* feat: Add polygon node urls

* feat: Add Polygon(MATIC) wallet WIP

* feat: Add Polygon(MATIC) wallet WIP

* feat: Add Polygon MATIC wallet [skip ci]

* fix: Issue with create/restore wallet for polygon

* feat: Add erc20 tokens for polygon

* feat: Adding Polygon MATIC Wallet

* fix: Add build command for polygon to workflow file to fix failing action

* fix: Switch evm to not display additional balance

* chore: Sync with remote

* fix: Revert change to inject app script

* feat: Add polygon erc20 tokens

* feat: Increase migration version

* fix: Restore from QR address validator fix

* fix: Adjust wallet connect connection flow to adapt to wallet type

* fix: Make wallet fetch nfts based on the current wallet type

* fix: Make wallet fetch nfts based on the current wallet type

* fix: Try fetching transactions with moralis

* fix: Requested review changes

* fix: Error creating new wallet

* fix: Revert script

* fix: Exclude spam NFTs from nft listing API response

* Update default_erc20_tokens.dart

* replace matic with matic poly

* Add polygon wallet scheme to app links

* style: reformat default_settings_migration.dart 

* minor enhancement 

* fix using different wallet function for setting the transaction priorities 

* fix: Add chain to calls

* Add USDC.e to initial coins

* Add other default polygon node 

* Use Polygon scan
some UI fixes

* Add polygon scan api key to secrets generation code

---------

Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
This commit is contained in:
Adegoke David 2023-12-02 03:26:43 +01:00 committed by GitHub
parent 3b7f9a297c
commit b3d579c24a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
116 changed files with 2351 additions and 206 deletions

View file

@ -97,6 +97,7 @@ jobs:
cd cw_ethereum && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd .. cd cw_ethereum && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
cd cw_bitcoin_cash && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd .. cd cw_bitcoin_cash && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
cd cw_nano && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd .. cd cw_nano && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
cd cw_polygon && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
flutter packages pub run build_runner build --delete-conflicting-outputs flutter packages pub run build_runner build --delete-conflicting-outputs
- name: Add secrets - name: Add secrets
@ -131,6 +132,7 @@ jobs:
echo "const fiatApiKey = '${{ secrets.FIAT_API_KEY }}';" >> lib/.secrets.g.dart echo "const fiatApiKey = '${{ secrets.FIAT_API_KEY }}';" >> lib/.secrets.g.dart
echo "const payfuraApiKey = '${{ secrets.PAYFURA_API_KEY }}';" >> lib/.secrets.g.dart echo "const payfuraApiKey = '${{ secrets.PAYFURA_API_KEY }}';" >> lib/.secrets.g.dart
echo "const etherScanApiKey = '${{ secrets.ETHER_SCAN_API_KEY }}';" >> cw_ethereum/lib/.secrets.g.dart echo "const etherScanApiKey = '${{ secrets.ETHER_SCAN_API_KEY }}';" >> cw_ethereum/lib/.secrets.g.dart
echo "const polygonScanApiKey = '${{ secrets.POLYGON_SCAN_API_KEY }}';" >> cw_ethereum/lib/.secrets.g.dart
echo "const chatwootWebsiteToken = '${{ secrets.CHATWOOT_WEBSITE_TOKEN }}';" >> lib/.secrets.g.dart echo "const chatwootWebsiteToken = '${{ secrets.CHATWOOT_WEBSITE_TOKEN }}';" >> lib/.secrets.g.dart
echo "const exolixApiKey = '${{ secrets.EXOLIX_API_KEY }}';" >> lib/.secrets.g.dart echo "const exolixApiKey = '${{ secrets.EXOLIX_API_KEY }}';" >> lib/.secrets.g.dart
echo "const robinhoodApplicationId = '${{ secrets.ROBINHOOD_APPLICATION_ID }}';" >> lib/.secrets.g.dart echo "const robinhoodApplicationId = '${{ secrets.ROBINHOOD_APPLICATION_ID }}';" >> lib/.secrets.g.dart

1
.gitignore vendored
View file

@ -126,6 +126,7 @@ lib/haven/haven.dart
lib/ethereum/ethereum.dart lib/ethereum/ethereum.dart
lib/bitcoin_cash/bitcoin_cash.dart lib/bitcoin_cash/bitcoin_cash.dart
lib/nano/nano.dart lib/nano/nano.dart
lib/polygon/polygon.dart
ios/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_180.png ios/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_180.png
ios/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_120.png ios/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_120.png

View file

@ -62,6 +62,9 @@
<data android:scheme="bitcoincash" /> <data android:scheme="bitcoincash" />
<data android:scheme="bitcoincash-wallet" /> <data android:scheme="bitcoincash-wallet" />
<data android:scheme="bitcoincash_wallet" /> <data android:scheme="bitcoincash_wallet" />
<data android:scheme="polygon" />
<data android:scheme="polygon-wallet" />
<data android:scheme="polygon_wallet" />
</intent-filter> </intent-filter>
</activity> </activity>
<meta-data <meta-data

View file

@ -0,0 +1,6 @@
-
uri: polygon-bor.publicnode.com
-
uri: polygon-rpc.com
-
uri: polygon.llamarpc.com

View file

@ -19,6 +19,8 @@ CryptoCurrency currencyForWalletType(WalletType type) {
return CryptoCurrency.nano; return CryptoCurrency.nano;
case WalletType.banano: case WalletType.banano:
return CryptoCurrency.banano; return CryptoCurrency.banano;
case WalletType.polygon:
return CryptoCurrency.maticpoly;
default: default:
throw Exception('Unexpected wallet type: ${type.toString()} for CryptoCurrency currencyForWalletType'); throw Exception('Unexpected wallet type: ${type.toString()} for CryptoCurrency currencyForWalletType');
} }

View file

@ -58,6 +58,7 @@ class Erc20Token extends CryptoCurrency with HiveObjectMixin {
static const typeId = ERC20_TOKEN_TYPE_ID; static const typeId = ERC20_TOKEN_TYPE_ID;
static const boxName = 'Erc20Tokens'; static const boxName = 'Erc20Tokens';
static const polygonBoxName = ' PolygonErc20Tokens';
@override @override
bool operator ==(other) => (other is Erc20Token && other.contractAddress == contractAddress) || bool operator ==(other) => (other is Erc20Token && other.contractAddress == contractAddress) ||

View file

@ -88,6 +88,8 @@ class Node extends HiveObject with Keyable {
} else { } else {
return Uri.http(uriRaw, ''); return Uri.http(uriRaw, '');
} }
case WalletType.polygon:
return Uri.https(uriRaw, '');
default: default:
throw Exception('Unexpected type ${type.toString()} for Node uri'); throw Exception('Unexpected type ${type.toString()} for Node uri');
} }
@ -146,6 +148,8 @@ class Node extends HiveObject with Keyable {
case WalletType.nano: case WalletType.nano:
case WalletType.banano: case WalletType.banano:
return requestNanoNode(); return requestNanoNode();
case WalletType.polygon:
return requestElectrumServer();
default: default:
return false; return false;
} }

View file

@ -13,6 +13,7 @@ const walletTypes = [
WalletType.bitcoinCash, WalletType.bitcoinCash,
WalletType.nano, WalletType.nano,
WalletType.banano, WalletType.banano,
WalletType.polygon,
]; ];
@HiveType(typeId: WALLET_TYPE_TYPE_ID) @HiveType(typeId: WALLET_TYPE_TYPE_ID)
@ -44,6 +45,8 @@ enum WalletType {
@HiveField(8) @HiveField(8)
bitcoinCash, bitcoinCash,
@HiveField(9)
polygon
} }
int serializeToInt(WalletType type) { int serializeToInt(WalletType type) {
@ -64,6 +67,8 @@ int serializeToInt(WalletType type) {
return 6; return 6;
case WalletType.bitcoinCash: case WalletType.bitcoinCash:
return 7; return 7;
case WalletType.polygon:
return 8;
default: default:
return -1; return -1;
} }
@ -87,6 +92,8 @@ WalletType deserializeFromInt(int raw) {
return WalletType.banano; return WalletType.banano;
case 7: case 7:
return WalletType.bitcoinCash; return WalletType.bitcoinCash;
case 8:
return WalletType.polygon;
default: default:
throw Exception('Unexpected token: $raw for WalletType deserializeFromInt'); throw Exception('Unexpected token: $raw for WalletType deserializeFromInt');
} }
@ -110,6 +117,8 @@ String walletTypeToString(WalletType type) {
return 'Nano'; return 'Nano';
case WalletType.banano: case WalletType.banano:
return 'Banano'; return 'Banano';
case WalletType.polygon:
return 'Polygon';
default: default:
return ''; return '';
} }
@ -133,6 +142,8 @@ String walletTypeToDisplayName(WalletType type) {
return 'Nano (XNO)'; return 'Nano (XNO)';
case WalletType.banano: case WalletType.banano:
return 'Banano (BAN)'; return 'Banano (BAN)';
case WalletType.polygon:
return 'Polygon (MATIC)';
default: default:
return ''; return '';
} }
@ -156,7 +167,10 @@ CryptoCurrency walletTypeToCryptoCurrency(WalletType type) {
return CryptoCurrency.nano; return CryptoCurrency.nano;
case WalletType.banano: case WalletType.banano:
return CryptoCurrency.banano; return CryptoCurrency.banano;
case WalletType.polygon:
return CryptoCurrency.maticpoly;
default: default:
throw Exception('Unexpected wallet type: ${type.toString()} for CryptoCurrency walletTypeToCryptoCurrency'); throw Exception(
'Unexpected wallet type: ${type.toString()} for CryptoCurrency walletTypeToCryptoCurrency');
} }
} }

View file

@ -15,12 +15,12 @@ import 'package:cw_ethereum/ethereum_transaction_priority.dart';
import 'package:cw_ethereum/.secrets.g.dart' as secrets; import 'package:cw_ethereum/.secrets.g.dart' as secrets;
class EthereumClient { class EthereumClient {
final _httpClient = Client(); final httpClient = Client();
Web3Client? _client; Web3Client? _client;
bool connect(Node node) { bool connect(Node node) {
try { try {
_client = Web3Client(node.uri.toString(), _httpClient); _client = Web3Client(node.uri.toString(), httpClient);
return true; return true;
} catch (e) { } catch (e) {
@ -74,9 +74,11 @@ class EthereumClient {
required int exponent, required int exponent,
String? contractAddress, String? contractAddress,
}) async { }) async {
assert(currency == CryptoCurrency.eth || contractAddress != null); assert(currency == CryptoCurrency.eth ||
currency == CryptoCurrency.maticpoly ||
contractAddress != null);
bool _isEthereum = currency == CryptoCurrency.eth; bool _isEVMCompatibleChain = currency == CryptoCurrency.eth || currency == CryptoCurrency.maticpoly;
final price = _client!.getGasPrice(); final price = _client!.getGasPrice();
@ -84,19 +86,23 @@ class EthereumClient {
from: privateKey.address, from: privateKey.address,
to: EthereumAddress.fromHex(toAddress), to: EthereumAddress.fromHex(toAddress),
maxPriorityFeePerGas: EtherAmount.fromInt(EtherUnit.gwei, priority.tip), maxPriorityFeePerGas: EtherAmount.fromInt(EtherUnit.gwei, priority.tip),
value: _isEthereum ? EtherAmount.inWei(BigInt.parse(amount)) : EtherAmount.zero(), value: _isEVMCompatibleChain ? EtherAmount.inWei(BigInt.parse(amount)) : EtherAmount.zero(),
); );
final signedTransaction = await _client!.signTransaction(privateKey, transaction); final chainId = _getChainIdForCurrency(currency);
final signedTransaction =
await _client!.signTransaction(privateKey, transaction, chainId: chainId);
final Function _sendTransaction; final Function _sendTransaction;
if (_isEthereum) { if (_isEVMCompatibleChain) {
_sendTransaction = () async => await sendTransaction(signedTransaction); _sendTransaction = () async => await sendTransaction(signedTransaction);
} else { } else {
final erc20 = ERC20( final erc20 = ERC20(
client: _client!, client: _client!,
address: EthereumAddress.fromHex(contractAddress!), address: EthereumAddress.fromHex(contractAddress!),
chainId: chainId,
); );
_sendTransaction = () async { _sendTransaction = () async {
@ -118,6 +124,16 @@ class EthereumClient {
); );
} }
int _getChainIdForCurrency(CryptoCurrency currency) {
switch (currency) {
case CryptoCurrency.maticpoly:
return 137;
case CryptoCurrency.eth:
default:
return 1;
}
}
Future<String> sendTransaction(Uint8List signedTransaction) async => Future<String> sendTransaction(Uint8List signedTransaction) async =>
await _client!.sendRawTransaction(prependTransactionType(0x02, signedTransaction)); await _client!.sendRawTransaction(prependTransactionType(0x02, signedTransaction));
@ -198,7 +214,7 @@ I/flutter ( 4474): Gas Used: 53000
Future<List<EthereumTransactionModel>> fetchTransactions(String address, Future<List<EthereumTransactionModel>> fetchTransactions(String address,
{String? contractAddress}) async { {String? contractAddress}) async {
try { try {
final response = await _httpClient.get(Uri.https("api.etherscan.io", "/api", { final response = await httpClient.get(Uri.https("api.etherscan.io", "/api", {
"module": "account", "module": "account",
"action": contractAddress != null ? "tokentx" : "txlist", "action": contractAddress != null ? "tokentx" : "txlist",
if (contractAddress != null) "contractaddress": contractAddress, if (contractAddress != null) "contractaddress": contractAddress,

View file

@ -1,3 +1,5 @@
import 'dart:math';
import 'package:cw_core/format_amount.dart'; import 'package:cw_core/format_amount.dart';
import 'package:cw_core/transaction_direction.dart'; import 'package:cw_core/transaction_direction.dart';
import 'package:cw_core/transaction_info.dart'; import 'package:cw_core/transaction_info.dart';
@ -34,8 +36,10 @@ class EthereumTransactionInfo extends TransactionInfo {
final String? to; final String? to;
@override @override
String amountFormatted() => String amountFormatted() {
'${formatAmount((ethAmount / BigInt.from(10).pow(exponent)).toString())} $tokenSymbol'; final amount = formatAmount((ethAmount / BigInt.from(10).pow(exponent)).toString());
return '${amount.substring(0, min(10, amount.length))} $tokenSymbol';
}
@override @override
String fiatAmount() => _fiatAmount ?? ''; String fiatAmount() => _fiatAmount ?? '';
@ -44,7 +48,10 @@ class EthereumTransactionInfo extends TransactionInfo {
void changeFiatAmount(String amount) => _fiatAmount = formatAmount(amount); void changeFiatAmount(String amount) => _fiatAmount = formatAmount(amount);
@override @override
String feeFormatted() => '${(ethFee / BigInt.from(10).pow(18)).toString()} ETH'; String feeFormatted() {
final amount = (ethFee / BigInt.from(10).pow(18)).toString();
return '${amount.substring(0, min(10, amount.length))} ETH';
}
factory EthereumTransactionInfo.fromJson(Map<String, dynamic> data) { factory EthereumTransactionInfo.fromJson(Map<String, dynamic> data) {
return EthereumTransactionInfo( return EthereumTransactionInfo(

View file

@ -1,3 +1,4 @@
//! Model used for in parsing transactions fetched using etherscan
class EthereumTransactionModel { class EthereumTransactionModel {
final DateTime date; final DateTime date;
final String hash; final String hash;

View file

@ -21,8 +21,8 @@ class PendingEthereumTransaction with PendingTransaction {
@override @override
String get amountFormatted { String get amountFormatted {
final _amount = BigInt.parse(amount) / BigInt.from(pow(10, exponent)); final _amount = (BigInt.parse(amount) / BigInt.from(pow(10, exponent))).toString();
return _amount.toStringAsFixed(min(15, _amount.toString().length)); return _amount.substring(0, min(10, _amount.length));
} }
@override @override
@ -30,8 +30,8 @@ class PendingEthereumTransaction with PendingTransaction {
@override @override
String get feeFormatted { String get feeFormatted {
final _fee = fee / BigInt.from(pow(10, 18)); final _fee = (fee / BigInt.from(pow(10, 18))).toString();
return _fee.toStringAsFixed(min(15, _fee.toString().length)); return _fee.substring(0, min(10, _fee.length));
} }
@override @override

30
cw_polygon/.gitignore vendored Normal file
View file

@ -0,0 +1,30 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
/pubspec.lock
**/doc/api/
.dart_tool/
.packages
build/

10
cw_polygon/.metadata Normal file
View file

@ -0,0 +1,10 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
channel: stable
project_type: package

3
cw_polygon/CHANGELOG.md Normal file
View file

@ -0,0 +1,3 @@
## 0.0.1
* TODO: Describe initial release.

1
cw_polygon/LICENSE Normal file
View file

@ -0,0 +1 @@
TODO: Add your license here.

39
cw_polygon/README.md Normal file
View file

@ -0,0 +1,39 @@
<!--
This README describes the package. If you publish this package to pub.dev,
this README's contents appear on the landing page for your package.
For information about how to write a good package README, see the guide for
[writing package pages](https://dart.dev/guides/libraries/writing-package-pages).
For general information about developing packages, see the Dart guide for
[creating packages](https://dart.dev/guides/libraries/create-library-packages)
and the Flutter guide for
[developing packages and plugins](https://flutter.dev/developing-packages).
-->
TODO: Put a short description of the package here that helps potential users
know whether this package might be useful for them.
## Features
TODO: List what your package can do. Maybe include images, gifs, or videos.
## Getting started
TODO: List prerequisites and provide or point to information on how to
start using the package.
## Usage
TODO: Include short and useful examples for package users. Add longer examples
to `/example` folder.
```dart
const like = 'sample';
```
## Additional information
TODO: Tell users more about the package: where to find more information, how to
contribute to the package, how to file issues, what response they can expect
from the package authors, and more.

View file

@ -0,0 +1,4 @@
include: package:flutter_lints/flutter.yaml
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options

View file

@ -0,0 +1,7 @@
library cw_polygon;
/// A Calculator.
class Calculator {
/// Returns [value] plus 1.
int addOne(int value) => value + 1;
}

View file

@ -0,0 +1,86 @@
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/erc20_token.dart';
class DefaultPolygonErc20Tokens {
final List<Erc20Token> _defaultTokens = [
Erc20Token(
name: "Wrapped Ether",
symbol: "WETH",
contractAddress: "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619",
decimal: 18,
enabled: false,
),
Erc20Token(
name: "Tether USD (PoS)",
symbol: "USDT",
contractAddress: "0xc2132D05D31c914a87C6611C10748AEb04B58e8F",
decimal: 6,
enabled: true,
),
Erc20Token(
name: "USD Coin",
symbol: "USDC",
contractAddress: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
decimal: 6,
enabled: true,
),
Erc20Token(
name: "USD Coin (POS)",
symbol: "USDC.e",
contractAddress: "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
decimal: 6,
enabled: false,
),
Erc20Token(
name: "Avalanche Token",
symbol: "AVAX",
contractAddress: "0x2C89bbc92BD86F8075d1DEcc58C7F4E0107f286b",
decimal: 18,
enabled: false,
),
Erc20Token(
name: "Wrapped BTC (PoS)",
symbol: "WBTC",
contractAddress: "0x1BFD67037B42Cf73acF2047067bd4F2C47D9BfD6",
decimal: 8,
enabled: false,
),
Erc20Token(
name: "Dai (PoS)",
symbol: "DAI",
contractAddress: "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063",
decimal: 18,
enabled: true,
),
Erc20Token(
name: "SHIBA INU (PoS)",
symbol: "SHIB",
contractAddress: "0x6f8a06447Ff6FcF75d803135a7de15CE88C1d4ec",
decimal: 18,
enabled: false,
),
Erc20Token(
name: "Uniswap (PoS)",
symbol: "UNI",
contractAddress: "0xb33EaAd8d922B1083446DC23f610c2567fB5180f",
decimal: 18,
enabled: false,
),
];
List<Erc20Token> get initialPolygonErc20Tokens => _defaultTokens.map((token) {
String? iconPath;
try {
iconPath = CryptoCurrency.all
.firstWhere((element) =>
element.title.toUpperCase() == token.symbol.toUpperCase())
.iconPath;
} catch (_) {}
if (iconPath != null) {
return Erc20Token.copyWith(token, iconPath);
}
return token;
}).toList();
}

View file

@ -0,0 +1,19 @@
import 'dart:typed_data';
import 'package:cw_ethereum/pending_ethereum_transaction.dart';
class PendingPolygonTransaction extends PendingEthereumTransaction {
PendingPolygonTransaction({
required Function sendTransaction,
required Uint8List signedTransaction,
required BigInt fee,
required String amount,
required int exponent,
}) : super(
amount: amount,
sendTransaction: sendTransaction,
signedTransaction: signedTransaction,
fee: fee,
exponent: exponent,
);
}

View file

@ -0,0 +1,34 @@
import 'dart:convert';
import 'package:cw_ethereum/ethereum_client.dart';
import 'package:cw_polygon/polygon_transaction_model.dart';
import 'package:cw_ethereum/.secrets.g.dart' as secrets;
class PolygonClient extends EthereumClient {
@override
Future<List<PolygonTransactionModel>> fetchTransactions(String address,
{String? contractAddress}) async {
try {
final response = await httpClient.get(Uri.https("api.polygonscan.com", "/api", {
"module": "account",
"action": contractAddress != null ? "tokentx" : "txlist",
if (contractAddress != null) "contractaddress": contractAddress,
"address": address,
"apikey": secrets.polygonScanApiKey,
}));
final jsonResponse = json.decode(response.body) as Map<String, dynamic>;
if (response.statusCode >= 200 && response.statusCode < 300 && jsonResponse['status'] != 0) {
return (jsonResponse['result'] as List)
.map((e) => PolygonTransactionModel.fromJson(e as Map<String, dynamic>))
.toList();
}
return [];
} catch (e) {
print(e);
return [];
}
}
}

View file

@ -0,0 +1,6 @@
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_ethereum/ethereum_exceptions.dart';
class PolygonTransactionCreationException extends EthereumTransactionCreationException {
PolygonTransactionCreationException(CryptoCurrency currency) : super(currency);
}

View file

@ -0,0 +1,25 @@
import 'package:intl/intl.dart';
const polygonAmountLength = 12;
const polygonAmountDivider = 1000000000000;
final polygonAmountFormat = NumberFormat()
..maximumFractionDigits = polygonAmountLength
..minimumFractionDigits = 1;
class PolygonFormatter {
static int parsePolygonAmount(String amount) {
try {
return (double.parse(amount) * polygonAmountDivider).round();
} catch (_) {
return 0;
}
}
static double parsePolygonAmountToDouble(int amount) {
try {
return amount / polygonAmountDivider;
} catch (_) {
return 0;
}
}
}

View file

@ -0,0 +1,5 @@
class PolygonMnemonicIsIncorrectException implements Exception {
@override
String toString() =>
'Polygon mnemonic has incorrect format. Mnemonic should contain 12 or 24 words separated by space.';
}

View file

@ -0,0 +1,18 @@
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/output_info.dart';
import 'package:cw_ethereum/ethereum_transaction_credentials.dart';
import 'package:cw_polygon/polygon_transaction_priority.dart';
class PolygonTransactionCredentials extends EthereumTransactionCredentials {
PolygonTransactionCredentials(
List<OutputInfo> outputs, {
required PolygonTransactionPriority? priority,
required CryptoCurrency currency,
final int? feeRate,
}) : super(
outputs,
currency: currency,
priority: priority,
feeRate: feeRate,
);
}

View file

@ -0,0 +1,77 @@
import 'dart:convert';
import 'dart:core';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_ethereum/file.dart';
import 'package:cw_polygon/polygon_transaction_info.dart';
import 'package:mobx/mobx.dart';
import 'package:cw_core/transaction_history.dart';
part 'polygon_transaction_history.g.dart';
const transactionsHistoryFileName = 'polygon_transactions.json';
class PolygonTransactionHistory = PolygonTransactionHistoryBase with _$PolygonTransactionHistory;
abstract class PolygonTransactionHistoryBase extends TransactionHistoryBase<PolygonTransactionInfo>
with Store {
PolygonTransactionHistoryBase({required this.walletInfo, required String password})
: _password = password {
transactions = ObservableMap<String, PolygonTransactionInfo>();
}
final WalletInfo walletInfo;
String _password;
Future<void> init() async => await _load();
@override
Future<void> save() async {
try {
final dirPath = await pathForWalletDir(name: walletInfo.name, type: walletInfo.type);
final path = '$dirPath/$transactionsHistoryFileName';
final data = json.encode({'transactions': transactions});
await writeData(path: path, password: _password, data: data);
} catch (e, s) {
print('Error while saving polygon transaction history: ${e.toString()}');
print(s);
}
}
@override
void addOne(PolygonTransactionInfo transaction) => transactions[transaction.id] = transaction;
@override
void addMany(Map<String, PolygonTransactionInfo> transactions) =>
this.transactions.addAll(transactions);
Future<Map<String, dynamic>> _read() async {
final dirPath = await pathForWalletDir(name: walletInfo.name, type: walletInfo.type);
final path = '$dirPath/$transactionsHistoryFileName';
final content = await read(path: path, password: _password);
if (content.isEmpty) {
return {};
}
return json.decode(content) as Map<String, dynamic>;
}
Future<void> _load() async {
try {
final content = await _read();
final txs = content['transactions'] as Map<String, dynamic>? ?? {};
txs.entries.forEach((entry) {
final val = entry.value;
if (val is Map<String, dynamic>) {
final tx = PolygonTransactionInfo.fromJson(val);
_update(tx);
}
});
} catch (e) {
print(e);
}
}
void _update(PolygonTransactionInfo transaction) => transactions[transaction.id] = transaction;
}

View file

@ -0,0 +1,49 @@
import 'package:cw_core/transaction_direction.dart';
import 'package:cw_ethereum/ethereum_transaction_info.dart';
class PolygonTransactionInfo extends EthereumTransactionInfo {
PolygonTransactionInfo({
required String id,
required int height,
required BigInt ethAmount,
int exponent = 18,
required TransactionDirection direction,
required DateTime date,
required bool isPending,
required BigInt ethFee,
required int confirmations,
String tokenSymbol = "MATIC",
required String? to,
}) : super(
confirmations: confirmations,
id: id,
height: height,
ethAmount: ethAmount,
exponent: exponent,
direction: direction,
date: date,
isPending: isPending,
ethFee: ethFee,
to: to,
tokenSymbol: tokenSymbol,
);
factory PolygonTransactionInfo.fromJson(Map<String, dynamic> data) {
return PolygonTransactionInfo(
id: data['id'] as String,
height: data['height'] as int,
ethAmount: BigInt.parse(data['amount']),
exponent: data['exponent'] as int,
ethFee: BigInt.parse(data['fee']),
direction: parseTransactionDirectionFromInt(data['direction'] as int),
date: DateTime.fromMillisecondsSinceEpoch(data['date'] as int),
isPending: data['isPending'] as bool,
confirmations: data['confirmations'] as int,
tokenSymbol: data['tokenSymbol'] as String,
to: data['to'],
);
}
@override
String feeFormatted() => '${(ethFee / BigInt.from(10).pow(18)).toString()} MATIC';
}

View file

@ -0,0 +1,49 @@
import 'package:cw_ethereum/ethereum_transaction_model.dart';
class PolygonTransactionModel extends EthereumTransactionModel {
PolygonTransactionModel({
required DateTime date,
required String hash,
required String from,
required String to,
required BigInt amount,
required int gasUsed,
required BigInt gasPrice,
required String contractAddress,
required int confirmations,
required int blockNumber,
required String? tokenSymbol,
required int? tokenDecimal,
required bool isError,
}) : super(
amount: amount,
date: date,
hash: hash,
from: from,
to: to,
gasPrice: gasPrice,
gasUsed: gasUsed,
confirmations: confirmations,
contractAddress: contractAddress,
blockNumber: blockNumber,
tokenDecimal: tokenDecimal,
tokenSymbol: tokenSymbol,
isError: isError,
);
factory PolygonTransactionModel.fromJson(Map<String, dynamic> json) => PolygonTransactionModel(
date: DateTime.fromMillisecondsSinceEpoch(int.parse(json["timeStamp"]) * 1000),
hash: json["hash"],
from: json["from"],
to: json["to"],
amount: BigInt.parse(json["value"]),
gasUsed: int.parse(json["gasUsed"]),
gasPrice: BigInt.parse(json["gasPrice"]),
contractAddress: json["contractAddress"],
confirmations: int.parse(json["confirmations"]),
blockNumber: int.parse(json["blockNumber"]),
tokenSymbol: json["tokenSymbol"] ?? "MATIC",
tokenDecimal: int.tryParse(json["tokenDecimal"] ?? ""),
isError: json["isError"] == "1",
);
}

View file

@ -0,0 +1,51 @@
import 'package:cw_ethereum/ethereum_transaction_priority.dart';
class PolygonTransactionPriority extends EthereumTransactionPriority {
const PolygonTransactionPriority({required String title, required int raw, required int tip})
: super(title: title, raw: raw, tip: tip);
static const List<PolygonTransactionPriority> all = [fast, medium, slow];
static const PolygonTransactionPriority slow =
PolygonTransactionPriority(title: 'slow', raw: 0, tip: 1);
static const PolygonTransactionPriority medium =
PolygonTransactionPriority(title: 'Medium', raw: 1, tip: 2);
static const PolygonTransactionPriority fast =
PolygonTransactionPriority(title: 'Fast', raw: 2, tip: 4);
static PolygonTransactionPriority deserialize({required int raw}) {
switch (raw) {
case 0:
return slow;
case 1:
return medium;
case 2:
return fast;
default:
throw Exception('Unexpected token: $raw for PolygonTransactionPriority deserialize');
}
}
@override
String get units => 'gas';
@override
String toString() {
var label = '';
switch (this) {
case PolygonTransactionPriority.slow:
label = 'Slow';
break;
case PolygonTransactionPriority.medium:
label = 'Medium';
break;
case PolygonTransactionPriority.fast:
label = 'Fast';
break;
default:
break;
}
return label;
}
}

View file

@ -0,0 +1,540 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:math';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/cake_hive.dart';
import 'package:cw_core/node.dart';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/pending_transaction.dart';
import 'package:cw_core/sync_status.dart';
import 'package:cw_core/transaction_direction.dart';
import 'package:cw_core/transaction_priority.dart';
import 'package:cw_core/wallet_addresses.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_ethereum/erc20_balance.dart';
import 'package:cw_ethereum/ethereum_formatter.dart';
import 'package:cw_ethereum/ethereum_transaction_model.dart';
import 'package:cw_ethereum/file.dart';
import 'package:cw_core/erc20_token.dart';
import 'package:cw_polygon/default_erc20_tokens.dart';
import 'package:cw_polygon/polygon_client.dart';
import 'package:cw_polygon/polygon_exceptions.dart';
import 'package:cw_polygon/polygon_formatter.dart';
import 'package:cw_polygon/polygon_transaction_credentials.dart';
import 'package:cw_polygon/polygon_transaction_history.dart';
import 'package:cw_polygon/polygon_transaction_info.dart';
import 'package:cw_polygon/polygon_transaction_model.dart';
import 'package:cw_polygon/polygon_transaction_priority.dart';
import 'package:cw_polygon/polygon_wallet_addresses.dart';
import 'package:hive/hive.dart';
import 'package:hex/hex.dart';
import 'package:mobx/mobx.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:web3dart/crypto.dart';
import 'package:web3dart/web3dart.dart';
import 'package:bip39/bip39.dart' as bip39;
import 'package:bip32/bip32.dart' as bip32;
part 'polygon_wallet.g.dart';
class PolygonWallet = PolygonWalletBase with _$PolygonWallet;
abstract class PolygonWalletBase extends WalletBase<ERC20Balance,
PolygonTransactionHistory, PolygonTransactionInfo> with Store {
PolygonWalletBase({
required WalletInfo walletInfo,
String? mnemonic,
String? privateKey,
required String password,
ERC20Balance? initialBalance,
}) : syncStatus = NotConnectedSyncStatus(),
_password = password,
_mnemonic = mnemonic,
_hexPrivateKey = privateKey,
_isTransactionUpdating = false,
_client = PolygonClient(),
walletAddresses = PolygonWalletAddresses(walletInfo),
balance = ObservableMap<CryptoCurrency, ERC20Balance>.of({
CryptoCurrency.maticpoly: initialBalance ?? ERC20Balance(BigInt.zero)
}),
super(walletInfo) {
this.walletInfo = walletInfo;
transactionHistory =
PolygonTransactionHistory(walletInfo: walletInfo, password: password);
if (!CakeHive.isAdapterRegistered(Erc20Token.typeId)) {
CakeHive.registerAdapter(Erc20TokenAdapter());
}
_sharedPrefs.complete(SharedPreferences.getInstance());
}
final String? _mnemonic;
final String? _hexPrivateKey;
final String _password;
late final Box<Erc20Token> polygonErc20TokensBox;
late final EthPrivateKey _polygonPrivateKey;
EthPrivateKey get polygonPrivateKey => _polygonPrivateKey;
late PolygonClient _client;
int? _gasPrice;
int? _estimatedGas;
bool _isTransactionUpdating;
// TODO: remove after integrating our own node and having eth_newPendingTransactionFilter
Timer? _transactionsUpdateTimer;
@override
WalletAddresses walletAddresses;
@override
@observable
SyncStatus syncStatus;
@override
@observable
late ObservableMap<CryptoCurrency, ERC20Balance> balance;
Completer<SharedPreferences> _sharedPrefs = Completer();
Future<void> init() async {
polygonErc20TokensBox =
await CakeHive.openBox<Erc20Token>(Erc20Token.polygonBoxName);
await walletAddresses.init();
await transactionHistory.init();
_polygonPrivateKey = await getPrivateKey(
mnemonic: _mnemonic,
privateKey: _hexPrivateKey,
password: _password,
);
walletAddresses.address = _polygonPrivateKey.address.toString();
await save();
}
@override
int calculateEstimatedFee(TransactionPriority priority, int? amount) {
try {
if (priority is PolygonTransactionPriority) {
final priorityFee =
EtherAmount.fromInt(EtherUnit.gwei, priority.tip).getInWei.toInt();
return (_gasPrice! + priorityFee) * (_estimatedGas ?? 0);
}
return 0;
} catch (e) {
return 0;
}
}
@override
Future<void> changePassword(String password) {
throw UnimplementedError("changePassword");
}
@override
void close() {
_client.stop();
_transactionsUpdateTimer?.cancel();
}
@action
@override
Future<void> connectToNode({required Node node}) async {
try {
syncStatus = ConnectingSyncStatus();
final isConnected = _client.connect(node);
if (!isConnected) {
throw Exception("Polygon Node connection failed");
}
_client.setListeners(_polygonPrivateKey.address, _onNewTransaction);
_setTransactionUpdateTimer();
syncStatus = ConnectedSyncStatus();
} catch (e) {
syncStatus = FailedSyncStatus();
}
}
@override
Future<PendingTransaction> createTransaction(Object credentials) async {
final _credentials = credentials as PolygonTransactionCredentials;
final outputs = _credentials.outputs;
final hasMultiDestination = outputs.length > 1;
final CryptoCurrency transactionCurrency = balance.keys
.firstWhere((element) => element.title == _credentials.currency.title);
final _erc20Balance = balance[transactionCurrency]!;
BigInt totalAmount = BigInt.zero;
int exponent =
transactionCurrency is Erc20Token ? transactionCurrency.decimal : 18;
num amountToPolygonMultiplier = pow(10, exponent);
// so far this can not be made with Polygon as Polygon does not support multiple recipients
if (hasMultiDestination) {
if (outputs.any(
(item) => item.sendAll || (item.formattedCryptoAmount ?? 0) <= 0)) {
throw PolygonTransactionCreationException(transactionCurrency);
}
final totalOriginalAmount = PolygonFormatter.parsePolygonAmountToDouble(
outputs.fold(
0, (acc, value) => acc + (value.formattedCryptoAmount ?? 0)));
totalAmount =
BigInt.from(totalOriginalAmount * amountToPolygonMultiplier);
if (_erc20Balance.balance < totalAmount) {
throw PolygonTransactionCreationException(transactionCurrency);
}
} else {
final output = outputs.first;
// since the fees are taken from Ethereum
// then no need to subtract the fees from the amount if send all
final BigInt allAmount;
if (transactionCurrency is Erc20Token) {
allAmount = _erc20Balance.balance;
} else {
allAmount = _erc20Balance.balance -
BigInt.from(calculateEstimatedFee(_credentials.priority!, null));
}
final totalOriginalAmount = EthereumFormatter.parseEthereumAmountToDouble(
output.formattedCryptoAmount ?? 0);
totalAmount = output.sendAll
? allAmount
: BigInt.from(totalOriginalAmount * amountToPolygonMultiplier);
if (_erc20Balance.balance < totalAmount) {
throw PolygonTransactionCreationException(transactionCurrency);
}
}
final pendingPolygonTransaction = await _client.signTransaction(
privateKey: _polygonPrivateKey,
toAddress: _credentials.outputs.first.isParsedAddress
? _credentials.outputs.first.extractedAddress!
: _credentials.outputs.first.address,
amount: totalAmount.toString(),
gas: _estimatedGas!,
priority: _credentials.priority!,
currency: transactionCurrency,
exponent: exponent,
contractAddress: transactionCurrency is Erc20Token
? transactionCurrency.contractAddress
: null,
);
return pendingPolygonTransaction;
}
Future<void> _updateTransactions() async {
try {
if (_isTransactionUpdating) {
return;
}
bool isPolygonScanEnabled = (await _sharedPrefs.future).getBool("use_polygonscan") ?? true;
if (!isPolygonScanEnabled) {
return;
}
_isTransactionUpdating = true;
final transactions = await fetchTransactions();
transactionHistory.addMany(transactions);
await transactionHistory.save();
_isTransactionUpdating = false;
} catch (_) {
_isTransactionUpdating = false;
}
}
@override
Future<Map<String, PolygonTransactionInfo>> fetchTransactions() async {
final address = _polygonPrivateKey.address.hex;
final transactions = await _client.fetchTransactions(address);
final List<Future<List<PolygonTransactionModel>>> polygonErc20TokensTransactions =
[];
for (var token in balance.keys) {
if (token is Erc20Token) {
polygonErc20TokensTransactions.add(_client.fetchTransactions(
address,
contractAddress: token.contractAddress,
) as Future<List<PolygonTransactionModel>>);
}
}
final tokensTransaction = await Future.wait(polygonErc20TokensTransactions);
transactions.addAll(tokensTransaction.expand((element) => element));
final Map<String, PolygonTransactionInfo> result = {};
for (var transactionModel in transactions) {
if (transactionModel.isError) {
continue;
}
result[transactionModel.hash] = PolygonTransactionInfo(
id: transactionModel.hash,
height: transactionModel.blockNumber,
ethAmount: transactionModel.amount,
direction: transactionModel.from == address
? TransactionDirection.outgoing
: TransactionDirection.incoming,
isPending: false,
date: transactionModel.date,
confirmations: transactionModel.confirmations,
ethFee:
BigInt.from(transactionModel.gasUsed) * transactionModel.gasPrice,
exponent: transactionModel.tokenDecimal ?? 18,
tokenSymbol: transactionModel.tokenSymbol ?? "MATIC",
to: transactionModel.to,
);
}
return result;
}
@override
Object get keys => throw UnimplementedError("keys");
@override
Future<void> rescan({required int height}) {
throw UnimplementedError("rescan");
}
@override
Future<void> save() async {
await walletAddresses.updateAddressesInBox();
final path = await makePath();
await write(path: path, password: _password, data: toJSON());
await transactionHistory.save();
}
@override
String? get seed => _mnemonic;
@override
String get privateKey => HEX.encode(_polygonPrivateKey.privateKey);
@action
@override
Future<void> startSync() async {
try {
syncStatus = AttemptingSyncStatus();
await _updateBalance();
await _updateTransactions();
_gasPrice = await _client.getGasUnitPrice();
_estimatedGas = await _client.getEstimatedGas();
Timer.periodic(const Duration(minutes: 1),
(timer) async => _gasPrice = await _client.getGasUnitPrice());
Timer.periodic(const Duration(seconds: 10),
(timer) async => _estimatedGas = await _client.getEstimatedGas());
syncStatus = SyncedSyncStatus();
} catch (e) {
syncStatus = FailedSyncStatus();
}
}
Future<String> makePath() async =>
pathForWallet(name: walletInfo.name, type: walletInfo.type);
String toJSON() => json.encode({
'mnemonic': _mnemonic,
'private_key': privateKey,
'balance': balance[currency]!.toJSON(),
});
static Future<PolygonWallet> open({
required String name,
required String password,
required WalletInfo walletInfo,
}) async {
final path = await pathForWallet(name: name, type: walletInfo.type);
final jsonSource = await read(path: path, password: password);
final data = json.decode(jsonSource) as Map;
final mnemonic = data['mnemonic'] as String?;
final privateKey = data['private_key'] as String?;
final balance = ERC20Balance.fromJSON(data['balance'] as String) ??
ERC20Balance(BigInt.zero);
return PolygonWallet(
walletInfo: walletInfo,
password: password,
mnemonic: mnemonic,
privateKey: privateKey,
initialBalance: balance,
);
}
Future<void> _updateBalance() async {
balance[currency] = await _fetchMaticBalance();
await _fetchErc20Balances();
await save();
}
Future<ERC20Balance> _fetchMaticBalance() async {
final balance = await _client.getBalance(_polygonPrivateKey.address);
return ERC20Balance(balance.getInWei);
}
Future<void> _fetchErc20Balances() async {
for (var token in polygonErc20TokensBox.values) {
try {
if (token.enabled) {
balance[token] = await _client.fetchERC20Balances(
_polygonPrivateKey.address,
token.contractAddress,
);
} else {
balance.remove(token);
}
} catch (_) {}
}
}
Future<EthPrivateKey> getPrivateKey(
{String? mnemonic, String? privateKey, required String password}) async {
assert(mnemonic != null || privateKey != null);
if (privateKey != null) {
return EthPrivateKey.fromHex(privateKey);
}
final seed = bip39.mnemonicToSeed(mnemonic!);
final root = bip32.BIP32.fromSeed(seed);
const _hdPathPolygon = "m/44'/60'/0'/0";
const index = 0;
final addressAtIndex = root.derivePath("$_hdPathPolygon/$index");
return EthPrivateKey.fromHex(
HEX.encode(addressAtIndex.privateKey as List<int>));
}
Future<void>? updateBalance() async => await _updateBalance();
List<Erc20Token> get erc20Currencies => polygonErc20TokensBox.values.toList();
Future<void> addErc20Token(Erc20Token token) async {
String? iconPath;
try {
iconPath = CryptoCurrency.all
.firstWhere((element) =>
element.title.toUpperCase() == token.symbol.toUpperCase())
.iconPath;
} catch (_) {}
final _token = Erc20Token(
name: token.name,
symbol: token.symbol,
contractAddress: token.contractAddress,
decimal: token.decimal,
enabled: token.enabled,
iconPath: iconPath,
);
await polygonErc20TokensBox.put(_token.contractAddress, _token);
if (_token.enabled) {
balance[_token] = await _client.fetchERC20Balances(
_polygonPrivateKey.address,
_token.contractAddress,
);
} else {
balance.remove(_token);
}
}
Future<void> deleteErc20Token(Erc20Token token) async {
await token.delete();
balance.remove(token);
_updateBalance();
}
Future<Erc20Token?> getErc20Token(String contractAddress) async =>
await _client.getErc20Token(contractAddress);
void _onNewTransaction() {
_updateBalance();
_updateTransactions();
}
void addInitialTokens() {
final initialErc20Tokens =
DefaultPolygonErc20Tokens().initialPolygonErc20Tokens;
for (var token in initialErc20Tokens) {
polygonErc20TokensBox.put(token.contractAddress, token);
}
}
@override
Future<void> renameWalletFiles(String newWalletName) async {
final currentWalletPath =
await pathForWallet(name: walletInfo.name, type: type);
final currentWalletFile = File(currentWalletPath);
final currentDirPath =
await pathForWalletDir(name: walletInfo.name, type: type);
final currentTransactionsFile =
File('$currentDirPath/$transactionsHistoryFileName');
// Copies current wallet files into new wallet name's dir and files
if (currentWalletFile.existsSync()) {
final newWalletPath =
await pathForWallet(name: newWalletName, type: type);
await currentWalletFile.copy(newWalletPath);
}
if (currentTransactionsFile.existsSync()) {
final newDirPath =
await pathForWalletDir(name: newWalletName, type: type);
await currentTransactionsFile
.copy('$newDirPath/$transactionsHistoryFileName');
}
// Delete old name's dir and files
await Directory(currentDirPath).delete(recursive: true);
}
void _setTransactionUpdateTimer() {
if (_transactionsUpdateTimer?.isActive ?? false) {
_transactionsUpdateTimer!.cancel();
}
_transactionsUpdateTimer = Timer.periodic(Duration(seconds: 10), (_) {
_updateTransactions();
_updateBalance();
});
}
void updatePolygonScanUsageState(bool isEnabled) {
if (isEnabled) {
_updateTransactions();
_setTransactionUpdateTimer();
} else {
_transactionsUpdateTimer?.cancel();
}
}
@override
String signMessage(String message, {String? address = null}) => bytesToHex(
_polygonPrivateKey.signPersonalMessageToUint8List(ascii.encode(message)));
Web3Client? getWeb3Client() => _client.getWeb3Client();
}

View file

@ -0,0 +1,5 @@
import 'package:cw_ethereum/ethereum_wallet_addresses.dart';
class PolygonWalletAddresses extends EthereumWalletAddresses {
PolygonWalletAddresses(super.walletInfo);
}

View file

@ -0,0 +1,28 @@
import 'package:cw_core/wallet_credentials.dart';
import 'package:cw_core/wallet_info.dart';
class PolygonNewWalletCredentials extends WalletCredentials {
PolygonNewWalletCredentials({required String name, WalletInfo? walletInfo})
: super(name: name, walletInfo: walletInfo);
}
class PolygonRestoreWalletFromSeedCredentials extends WalletCredentials {
PolygonRestoreWalletFromSeedCredentials(
{required String name,
required String password,
required this.mnemonic,
WalletInfo? walletInfo})
: super(name: name, password: password, walletInfo: walletInfo);
final String mnemonic;
}
class PolygonRestoreWalletFromPrivateKey extends WalletCredentials {
PolygonRestoreWalletFromPrivateKey(
{required String name,
required String password,
required this.privateKey,
WalletInfo? walletInfo})
: super(name: name, password: password, walletInfo: walletInfo);
final String privateKey;
}

View file

@ -0,0 +1,123 @@
import 'dart:io';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_service.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cw_ethereum/ethereum_mnemonics.dart';
import 'package:cw_polygon/polygon_wallet.dart';
import 'package:bip39/bip39.dart' as bip39;
import 'package:hive/hive.dart';
import 'polygon_wallet_creation_credentials.dart';
import 'package:collection/collection.dart';
class PolygonWalletService extends WalletService<PolygonNewWalletCredentials,
PolygonRestoreWalletFromSeedCredentials, PolygonRestoreWalletFromPrivateKey> {
PolygonWalletService(this.walletInfoSource);
final Box<WalletInfo> walletInfoSource;
@override
Future<PolygonWallet> create(PolygonNewWalletCredentials credentials) async {
final strength = (credentials.seedPhraseLength == 12)
? 128
: (credentials.seedPhraseLength == 24)
? 256
: 128;
final mnemonic = bip39.generateMnemonic(strength: strength);
final wallet = PolygonWallet(
walletInfo: credentials.walletInfo!,
mnemonic: mnemonic,
password: credentials.password!,
);
await wallet.init();
wallet.addInitialTokens();
await wallet.save();
return wallet;
}
@override
WalletType getType() => WalletType.polygon;
@override
Future<bool> isWalletExit(String name) async =>
File(await pathForWallet(name: name, type: getType())).existsSync();
@override
Future<PolygonWallet> openWallet(String name, String password) async {
final walletInfo =
walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
final wallet = await PolygonWalletBase.open(
name: name,
password: password,
walletInfo: walletInfo,
);
await wallet.init();
await wallet.save();
return wallet;
}
@override
Future<void> remove(String wallet) async {
File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true);
final walletInfo = walletInfoSource.values
.firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!;
await walletInfoSource.delete(walletInfo.key);
}
@override
Future<PolygonWallet> restoreFromKeys(PolygonRestoreWalletFromPrivateKey credentials) async {
final wallet = PolygonWallet(
password: credentials.password!,
privateKey: credentials.privateKey,
walletInfo: credentials.walletInfo!,
);
await wallet.init();
wallet.addInitialTokens();
await wallet.save();
return wallet;
}
@override
Future<PolygonWallet> restoreFromSeed(PolygonRestoreWalletFromSeedCredentials credentials) async {
if (!bip39.validateMnemonic(credentials.mnemonic)) {
throw EthereumMnemonicIsIncorrectException();
}
final wallet = PolygonWallet(
password: credentials.password!,
mnemonic: credentials.mnemonic,
walletInfo: credentials.walletInfo!,
);
await wallet.init();
wallet.addInitialTokens();
await wallet.save();
return wallet;
}
@override
Future<void> rename(String currentName, String password, String newName) async {
final currentWalletInfo = walletInfoSource.values
.firstWhere((info) => info.id == WalletBase.idFor(currentName, getType()));
final currentWallet = await PolygonWalletBase.open(
password: password, name: currentName, walletInfo: currentWalletInfo);
await currentWallet.renameWalletFiles(newName);
final newWalletInfo = currentWalletInfo;
newWalletInfo.id = WalletBase.idFor(newName, getType());
newWalletInfo.name = newName;
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
}
}

73
cw_polygon/pubspec.yaml Normal file
View file

@ -0,0 +1,73 @@
name: cw_polygon
description: A new Flutter package project.
version: 0.0.1
publish_to: none
author: Cake Wallet
homepage: https://cakewallet.com
environment:
sdk: '>=3.0.6 <4.0.0'
flutter: ">=1.17.0"
dependencies:
flutter:
sdk: flutter
cw_core:
path: ../cw_core
cw_ethereum:
path: ../cw_ethereum
mobx: ^2.0.7+4
intl: ^0.18.0
bip39: ^1.0.6
hive: ^2.2.3
collection: ^1.17.1
web3dart: ^2.7.1
bip32: ^2.0.0
hex: ^0.2.0
shared_preferences: ^2.0.15
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^2.0.0
build_runner: ^2.1.11
mobx_codegen: ^2.0.7
hive_generator: ^1.1.3
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter packages.
flutter:
# To add assets to your package, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
#
# For details regarding assets in packages, see
# https://flutter.dev/assets-and-images/#from-packages
#
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware
# To add custom fonts to your package, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts in packages, see
# https://flutter.dev/custom-fonts/#from-packages

View file

@ -0,0 +1,12 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:cw_polygon/cw_polygon.dart';
void main() {
test('adds one to input values', () {
final calculator = Calculator();
expect(calculator.addOne(2), 3);
expect(calculator.addOne(-7), -6);
expect(calculator.addOne(0), 1);
});
}

View file

@ -160,6 +160,26 @@
<string>bitcoincash-wallet</string> <string>bitcoincash-wallet</string>
</array> </array>
</dict> </dict>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>polygon</string>
<key>CFBundleURLSchemes</key>
<array>
<string>polygon</string>
</array>
</dict>
<dict>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>CFBundleURLName</key>
<string>polygon-wallet</string>
<key>CFBundleURLSchemes</key>
<array>
<string>polygon-wallet</string>
</array>
</dict>
</array> </array>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string> <string>$(CURRENT_PROJECT_VERSION)</string>

View file

@ -271,6 +271,8 @@ class AddressValidator extends TextValidator {
'|([^0-9a-zA-Z]|^)ltc[a-zA-Z0-9]{26,45}([^0-9a-zA-Z]|\$)'; '|([^0-9a-zA-Z]|^)ltc[a-zA-Z0-9]{26,45}([^0-9a-zA-Z]|\$)';
case CryptoCurrency.eth: case CryptoCurrency.eth:
return '0x[0-9a-zA-Z]{42}'; return '0x[0-9a-zA-Z]{42}';
case CryptoCurrency.maticpoly:
return '0x[0-9a-zA-Z]{42}';
case CryptoCurrency.nano: case CryptoCurrency.nano:
return 'nano_[0-9a-zA-Z]{60}'; return 'nano_[0-9a-zA-Z]{60}';
case CryptoCurrency.banano: case CryptoCurrency.banano:

View file

@ -16,7 +16,7 @@ Future<double> _fetchPrice(Map<String, dynamic> args) async {
final Map<String, String> queryParams = { final Map<String, String> queryParams = {
'interval_count': '1', 'interval_count': '1',
'base': crypto, 'base': crypto.split(".").first,
'quote': fiat, 'quote': fiat,
'key': secrets.fiatApiKey, 'key': secrets.fiatApiKey,
}; };

View file

@ -3,6 +3,7 @@ import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:cake_wallet/haven/haven.dart'; import 'package:cake_wallet/haven/haven.dart';
import 'package:cake_wallet/core/validator.dart'; import 'package:cake_wallet/core/validator.dart';
import 'package:cake_wallet/entities/mnemonic_item.dart'; import 'package:cake_wallet/entities/mnemonic_item.dart';
import 'package:cake_wallet/polygon/polygon.dart';
import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_type.dart';
import 'package:cake_wallet/monero/monero.dart'; import 'package:cake_wallet/monero/monero.dart';
import 'package:cake_wallet/nano/nano.dart'; import 'package:cake_wallet/nano/nano.dart';
@ -34,6 +35,8 @@ class SeedValidator extends Validator<MnemonicItem> {
case WalletType.nano: case WalletType.nano:
case WalletType.banano: case WalletType.banano:
return nano!.getNanoWordList(language); return nano!.getNanoWordList(language);
case WalletType.polygon:
return polygon!.getPolygonWordList(language);
default: default:
return []; return [];
} }

View file

@ -6,6 +6,7 @@ import 'package:cake_wallet/core/wallet_connect/eth_transaction_model.dart';
import 'package:cake_wallet/core/wallet_connect/evm_chain_id.dart'; import 'package:cake_wallet/core/wallet_connect/evm_chain_id.dart';
import 'package:cake_wallet/core/wallet_connect/wc_bottom_sheet_service.dart'; import 'package:cake_wallet/core/wallet_connect/wc_bottom_sheet_service.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/reactions/wallet_connect.dart';
import 'package:cake_wallet/src/screens/wallet_connect/widgets/message_display_widget.dart'; import 'package:cake_wallet/src/screens/wallet_connect/widgets/message_display_widget.dart';
import 'package:cake_wallet/store/app_store.dart'; import 'package:cake_wallet/store/app_store.dart';
import 'package:cake_wallet/core/wallet_connect/models/chain_key_model.dart'; import 'package:cake_wallet/core/wallet_connect/models/chain_key_model.dart';
@ -14,7 +15,6 @@ import 'package:cake_wallet/src/screens/wallet_connect/widgets/connection_widget
import 'package:cake_wallet/src/screens/wallet_connect/widgets/modals/web3_request_modal.dart'; import 'package:cake_wallet/src/screens/wallet_connect/widgets/modals/web3_request_modal.dart';
import 'package:cake_wallet/src/screens/wallet_connect/utils/string_parsing.dart'; import 'package:cake_wallet/src/screens/wallet_connect/utils/string_parsing.dart';
import 'package:convert/convert.dart'; import 'package:convert/convert.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:eth_sig_util/eth_sig_util.dart'; import 'package:eth_sig_util/eth_sig_util.dart';
import 'package:eth_sig_util/util/utils.dart'; import 'package:eth_sig_util/util/utils.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
@ -46,13 +46,12 @@ class EvmChainServiceImpl implements ChainService {
required this.wcKeyService, required this.wcKeyService,
required this.bottomSheetService, required this.bottomSheetService,
required this.wallet, required this.wallet,
Web3Client? ethClient, Web3Client? web3Client,
}) : ethClient = ethClient ?? }) : ethClient = web3Client ??
Web3Client( Web3Client(
appStore.settingsStore.getCurrentNode(WalletType.ethereum).uri.toString(), appStore.settingsStore.getCurrentNode(appStore.wallet!.type).uri.toString(),
http.Client(), http.Client(),
) { ) {
for (final String event in getEvents()) { for (final String event in getEvents()) {
wallet.registerEventEmitter(chainId: getChainId(), event: event); wallet.registerEventEmitter(chainId: getChainId(), event: event);
} }
@ -138,7 +137,8 @@ class EvmChainServiceImpl implements ChainService {
try { try {
// Load the private key // Load the private key
final List<ChainKeyModel> keys = wcKeyService.getKeysForChain(getChainId()); final List<ChainKeyModel> keys = wcKeyService
.getKeysForChain(getChainNameSpaceAndIdBasedOnWalletType(appStore.wallet!.type));
final Credentials credentials = EthPrivateKey.fromHex(keys[0].privateKey); final Credentials credentials = EthPrivateKey.fromHex(keys[0].privateKey);
@ -176,13 +176,15 @@ class EvmChainServiceImpl implements ChainService {
try { try {
// Load the private key // Load the private key
final List<ChainKeyModel> keys = wcKeyService.getKeysForChain(getChainId()); final List<ChainKeyModel> keys = wcKeyService
.getKeysForChain(getChainNameSpaceAndIdBasedOnWalletType(appStore.wallet!.type));
final EthPrivateKey credentials = EthPrivateKey.fromHex(keys[0].privateKey); final EthPrivateKey credentials = EthPrivateKey.fromHex(keys[0].privateKey);
final String signature = hex.encode( final String signature = hex.encode(
credentials.signPersonalMessageToUint8List( credentials.signPersonalMessageToUint8List(
Uint8List.fromList(utf8.encode(message)), Uint8List.fromList(utf8.encode(message)),
chainId: getChainIdBasedOnWalletType(appStore.wallet!.type),
), ),
); );
log(signature); log(signature);
@ -212,7 +214,8 @@ class EvmChainServiceImpl implements ChainService {
} }
// Load the private key // Load the private key
final List<ChainKeyModel> keys = wcKeyService.getKeysForChain(getChainId()); final List<ChainKeyModel> keys = wcKeyService
.getKeysForChain(getChainNameSpaceAndIdBasedOnWalletType(appStore.wallet!.type));
final Credentials credentials = EthPrivateKey.fromHex(keys[0].privateKey); final Credentials credentials = EthPrivateKey.fromHex(keys[0].privateKey);
@ -232,7 +235,11 @@ class EvmChainServiceImpl implements ChainService {
); );
try { try {
final result = await ethClient.sendTransaction(credentials, transaction); final result = await ethClient.sendTransaction(
credentials,
transaction,
chainId: getChainIdBasedOnWalletType(appStore.wallet!.type),
);
log('Result: $result'); log('Result: $result');
@ -267,7 +274,8 @@ class EvmChainServiceImpl implements ChainService {
return authError; return authError;
} }
final List<ChainKeyModel> keys = wcKeyService.getKeysForChain(getChainId()); final List<ChainKeyModel> keys = wcKeyService
.getKeysForChain(getChainNameSpaceAndIdBasedOnWalletType(appStore.wallet!.type));
return EthSigUtil.signTypedData( return EthSigUtil.signTypedData(
privateKey: keys[0].privateKey, privateKey: keys[0].privateKey,

View file

@ -1,9 +1,11 @@
import 'package:cake_wallet/ethereum/ethereum.dart'; import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:cake_wallet/core/wallet_connect/models/chain_key_model.dart'; import 'package:cake_wallet/core/wallet_connect/models/chain_key_model.dart';
import 'package:cake_wallet/polygon/polygon.dart';
import 'package:cw_core/balance.dart'; import 'package:cw_core/balance.dart';
import 'package:cw_core/transaction_history.dart'; import 'package:cw_core/transaction_history.dart';
import 'package:cw_core/transaction_info.dart'; import 'package:cw_core/transaction_info.dart';
import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_type.dart';
abstract class WalletConnectKeyService { abstract class WalletConnectKeyService {
/// Returns a list of all the keys. /// Returns a list of all the keys.
@ -32,16 +34,36 @@ class KeyServiceImpl implements WalletConnectKeyService {
'eip155:42161', 'eip155:42161',
'eip155:80001', 'eip155:80001',
], ],
privateKey: ethereum!.getPrivateKey(wallet), privateKey: _getPrivateKeyForWallet(wallet),
publicKey: ethereum!.getPublicKey(wallet), publicKey: _getPublicKeyForWallet(wallet),
), ),
]; ];
late final WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo> wallet; late final WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo> wallet;
late final List<ChainKeyModel> _keys; late final List<ChainKeyModel> _keys;
static String _getPrivateKeyForWallet(WalletBase wallet) {
switch (wallet.type) {
case WalletType.ethereum:
return ethereum!.getPrivateKey(wallet);
case WalletType.polygon:
return polygon!.getPrivateKey(wallet);
default:
return '';
}
}
static String _getPublicKeyForWallet(WalletBase wallet) {
switch (wallet.type) {
case WalletType.ethereum:
return ethereum!.getPublicKey(wallet);
case WalletType.polygon:
return polygon!.getPublicKey(wallet);
default:
return '';
}
}
@override @override
List<String> getChains() { List<String> getChains() {
final List<String> chainIds = []; final List<String> chainIds = [];

View file

@ -9,6 +9,7 @@ import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/core/wallet_connect/models/auth_request_model.dart'; import 'package:cake_wallet/core/wallet_connect/models/auth_request_model.dart';
import 'package:cake_wallet/core/wallet_connect/models/chain_key_model.dart'; import 'package:cake_wallet/core/wallet_connect/models/chain_key_model.dart';
import 'package:cake_wallet/core/wallet_connect/models/session_request_model.dart'; import 'package:cake_wallet/core/wallet_connect/models/session_request_model.dart';
import 'package:cake_wallet/reactions/wallet_connect.dart';
import 'package:cake_wallet/src/screens/wallet_connect/widgets/connection_request_widget.dart'; import 'package:cake_wallet/src/screens/wallet_connect/widgets/connection_request_widget.dart';
import 'package:cake_wallet/src/screens/wallet_connect/widgets/message_display_widget.dart'; import 'package:cake_wallet/src/screens/wallet_connect/widgets/message_display_widget.dart';
import 'package:cake_wallet/src/screens/wallet_connect/widgets/modals/web3_request_modal.dart'; import 'package:cake_wallet/src/screens/wallet_connect/widgets/modals/web3_request_modal.dart';
@ -164,8 +165,10 @@ abstract class Web3WalletServiceBase with Store {
void _onSessionProposal(SessionProposalEvent? args) async { void _onSessionProposal(SessionProposalEvent? args) async {
if (args != null) { if (args != null) {
final chaindIdNamespace = getChainNameSpaceAndIdBasedOnWalletType(appStore.wallet!.type);
final Widget modalWidget = Web3RequestModal( final Widget modalWidget = Web3RequestModal(
child: ConnectionRequestWidget( child: ConnectionRequestWidget(
chaindIdNamespace: chaindIdNamespace,
wallet: _web3Wallet, wallet: _web3Wallet,
sessionProposal: SessionRequestModel(request: args.params), sessionProposal: SessionRequestModel(request: args.params),
), ),
@ -232,12 +235,13 @@ abstract class Web3WalletServiceBase with Store {
@action @action
Future<void> _onAuthRequest(AuthRequest? args) async { Future<void> _onAuthRequest(AuthRequest? args) async {
if (args != null) { if (args != null) {
List<ChainKeyModel> chainKeys = walletKeyService.getKeysForChain('eip155:1'); final chaindIdNamespace = getChainNameSpaceAndIdBasedOnWalletType(appStore.wallet!.type);
List<ChainKeyModel> chainKeys = walletKeyService.getKeysForChain(chaindIdNamespace);
// Create the message to be signed // Create the message to be signed
final String iss = 'did:pkh:eip155:1:${chainKeys.first.publicKey}'; final String iss = 'did:pkh:$chaindIdNamespace:${chainKeys.first.publicKey}';
final Widget modalWidget = Web3RequestModal( final Widget modalWidget = Web3RequestModal(
child: ConnectionRequestWidget( child: ConnectionRequestWidget(
chaindIdNamespace: chaindIdNamespace,
wallet: _web3Wallet, wallet: _web3Wallet,
authRequest: AuthRequestModel(iss: iss, request: args), authRequest: AuthRequestModel(iss: iss, request: args),
), ),

View file

@ -18,6 +18,8 @@ import 'package:cake_wallet/nano/nano.dart';
import 'package:cake_wallet/ionia/ionia_anypay.dart'; import 'package:cake_wallet/ionia/ionia_anypay.dart';
import 'package:cake_wallet/ionia/ionia_gift_card.dart'; import 'package:cake_wallet/ionia/ionia_gift_card.dart';
import 'package:cake_wallet/ionia/ionia_tip.dart'; import 'package:cake_wallet/ionia/ionia_tip.dart';
import 'package:cake_wallet/polygon/polygon.dart';
import 'package:cake_wallet/reactions/wallet_connect.dart';
import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/anonpay_details/anonpay_details_page.dart'; import 'package:cake_wallet/src/screens/anonpay_details/anonpay_details_page.dart';
import 'package:cake_wallet/src/screens/buy/buy_options_page.dart'; import 'package:cake_wallet/src/screens/buy/buy_options_page.dart';
@ -750,7 +752,7 @@ Future<void> setup({
final wallet = getIt.get<AppStore>().wallet; final wallet = getIt.get<AppStore>().wallet;
return ConnectionSyncPage( return ConnectionSyncPage(
getIt.get<DashboardViewModel>(), getIt.get<DashboardViewModel>(),
wallet?.type == WalletType.ethereum ? getIt.get<Web3WalletService>() : null, isEVMCompatibleChain(wallet!.type) ? getIt.get<Web3WalletService>() : null,
); );
}); });
@ -847,6 +849,8 @@ Future<void> setup({
.createBitcoinCashWalletService(_walletInfoSource, _unspentCoinsInfoSource!); .createBitcoinCashWalletService(_walletInfoSource, _unspentCoinsInfoSource!);
case WalletType.nano: case WalletType.nano:
return nano!.createNanoWalletService(_walletInfoSource); return nano!.createNanoWalletService(_walletInfoSource);
case WalletType.polygon:
return polygon!.createPolygonWalletService(_walletInfoSource);
default: default:
throw Exception('Unexpected token: ${param1.toString()} for generating of WalletService'); throw Exception('Unexpected token: ${param1.toString()} for generating of WalletService');
} }

View file

@ -27,6 +27,7 @@ const cakeWalletBitcoinElectrumUri = 'electrum.cakewallet.com:50002';
const cakeWalletLitecoinElectrumUri = 'ltc-electrum.cakewallet.com:50002'; const cakeWalletLitecoinElectrumUri = 'ltc-electrum.cakewallet.com:50002';
const havenDefaultNodeUri = 'nodes.havenprotocol.org:443'; const havenDefaultNodeUri = 'nodes.havenprotocol.org:443';
const ethereumDefaultNodeUri = 'ethereum.publicnode.com'; const ethereumDefaultNodeUri = 'ethereum.publicnode.com';
const polygonDefaultNodeUri = 'polygon-bor.publicnode.com';
const cakeWalletBitcoinCashDefaultNodeUri = 'bitcoincash.stackwallet.com:50002'; const cakeWalletBitcoinCashDefaultNodeUri = 'bitcoincash.stackwallet.com:50002';
const nanoDefaultNodeUri = 'rpc.nano.to'; const nanoDefaultNodeUri = 'rpc.nano.to';
const nanoDefaultPowNodeUri = 'rpc.nano.to'; const nanoDefaultPowNodeUri = 'rpc.nano.to';
@ -65,6 +66,8 @@ Future<void> defaultSettingsMigration(
final migrationVersions = final migrationVersions =
List<int>.generate(migrationVersionsLength, (i) => currentVersion + (i + 1)); List<int>.generate(migrationVersionsLength, (i) => currentVersion + (i + 1));
/// When you add a new case, increase the initialMigrationVersion parameter in the main.dart file.
/// This ensures that this switch case runs the newly added case.
await Future.forEach(migrationVersions, (int version) async { await Future.forEach(migrationVersions, (int version) async {
try { try {
switch (version) { switch (version) {
@ -175,6 +178,11 @@ Future<void> defaultSettingsMigration(
await changeBitcoinCurrentElectrumServerToDefault( await changeBitcoinCurrentElectrumServerToDefault(
sharedPreferences: sharedPreferences, nodes: nodes); sharedPreferences: sharedPreferences, nodes: nodes);
break; break;
case 24:
await addPolygonNodeList(nodes: nodes);
await changePolygonCurrentNodeToDefault(
sharedPreferences: sharedPreferences, nodes: nodes);
break;
case 25: case 25:
await rewriteSecureStoragePin(secureStorage: secureStorage); await rewriteSecureStoragePin(secureStorage: secureStorage);
break; break;
@ -332,6 +340,11 @@ Node? getEthereumDefaultNode({required Box<Node> nodes}) {
nodes.values.firstWhereOrNull((node) => node.type == WalletType.ethereum); nodes.values.firstWhereOrNull((node) => node.type == WalletType.ethereum);
} }
Node? getPolygonDefaultNode({required Box<Node> nodes}) {
return nodes.values.firstWhereOrNull((Node node) => node.uriRaw == polygonDefaultNodeUri) ??
nodes.values.firstWhereOrNull((node) => node.type == WalletType.polygon);
}
Node? getNanoDefaultNode({required Box<Node> nodes}) { Node? getNanoDefaultNode({required Box<Node> nodes}) {
return nodes.values.firstWhereOrNull((Node node) => node.uriRaw == nanoDefaultNodeUri) ?? return nodes.values.firstWhereOrNull((Node node) => node.uriRaw == nanoDefaultNodeUri) ??
nodes.values.firstWhereOrNull((node) => node.type == WalletType.nano); nodes.values.firstWhereOrNull((node) => node.type == WalletType.nano);
@ -575,6 +588,7 @@ Future<void> checkCurrentNodes(
sharedPreferences.getInt(PreferencesKey.currentLitecoinElectrumSererIdKey); sharedPreferences.getInt(PreferencesKey.currentLitecoinElectrumSererIdKey);
final currentHavenNodeId = sharedPreferences.getInt(PreferencesKey.currentHavenNodeIdKey); final currentHavenNodeId = sharedPreferences.getInt(PreferencesKey.currentHavenNodeIdKey);
final currentEthereumNodeId = sharedPreferences.getInt(PreferencesKey.currentEthereumNodeIdKey); final currentEthereumNodeId = sharedPreferences.getInt(PreferencesKey.currentEthereumNodeIdKey);
final currentPolygonNodeId = sharedPreferences.getInt(PreferencesKey.currentPolygonNodeIdKey);
final currentNanoNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoNodeIdKey); final currentNanoNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoNodeIdKey);
final currentNanoPowNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoPowNodeIdKey); final currentNanoPowNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoPowNodeIdKey);
final currentBitcoinCashNodeId = final currentBitcoinCashNodeId =
@ -589,6 +603,8 @@ Future<void> checkCurrentNodes(
nodeSource.values.firstWhereOrNull((node) => node.key == currentHavenNodeId); nodeSource.values.firstWhereOrNull((node) => node.key == currentHavenNodeId);
final currentEthereumNodeServer = final currentEthereumNodeServer =
nodeSource.values.firstWhereOrNull((node) => node.key == currentEthereumNodeId); nodeSource.values.firstWhereOrNull((node) => node.key == currentEthereumNodeId);
final currentPolygonNodeServer =
nodeSource.values.firstWhereOrNull((node) => node.key == currentPolygonNodeId);
final currentNanoNodeServer = final currentNanoNodeServer =
nodeSource.values.firstWhereOrNull((node) => node.key == currentNanoNodeId); nodeSource.values.firstWhereOrNull((node) => node.key == currentNanoNodeId);
final currentNanoPowNodeServer = final currentNanoPowNodeServer =
@ -648,6 +664,12 @@ Future<void> checkCurrentNodes(
await nodeSource.add(node); await nodeSource.add(node);
await sharedPreferences.setInt(PreferencesKey.currentBitcoinCashNodeIdKey, node.key as int); await sharedPreferences.setInt(PreferencesKey.currentBitcoinCashNodeIdKey, node.key as int);
} }
if (currentPolygonNodeServer == null) {
final node = Node(uri: polygonDefaultNodeUri, type: WalletType.polygon);
await nodeSource.add(node);
await sharedPreferences.setInt(PreferencesKey.currentPolygonNodeIdKey, node.key as int);
}
} }
Future<void> resetBitcoinElectrumServer( Future<void> resetBitcoinElectrumServer(
@ -742,3 +764,20 @@ Future<void> changeNanoCurrentPowNodeToDefault(
final nodeId = node?.key as int? ?? 0; final nodeId = node?.key as int? ?? 0;
await sharedPreferences.setInt(PreferencesKey.currentNanoPowNodeIdKey, nodeId); await sharedPreferences.setInt(PreferencesKey.currentNanoPowNodeIdKey, nodeId);
} }
Future<void> addPolygonNodeList({required Box<Node> nodes}) async {
final nodeList = await loadDefaultPolygonNodes();
for (var node in nodeList) {
if (nodes.values.firstWhereOrNull((element) => element.uriRaw == node.uriRaw) == null) {
await nodes.add(node);
}
}
}
Future<void> changePolygonCurrentNodeToDefault(
{required SharedPreferences sharedPreferences, required Box<Node> nodes}) async {
final node = getPolygonDefaultNode(nodes: nodes);
final nodeId = node?.key as int? ?? 0;
await sharedPreferences.setInt(PreferencesKey.currentPolygonNodeIdKey, nodeId);
}

View file

@ -1,4 +1,5 @@
import 'package:cake_wallet/ethereum/ethereum.dart'; import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:cake_wallet/polygon/polygon.dart';
import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_type.dart';
import 'package:ens_dart/ens_dart.dart'; import 'package:ens_dart/ens_dart.dart';
@ -12,6 +13,10 @@ class EnsRecord {
if (wallet != null && wallet.type == WalletType.ethereum) { if (wallet != null && wallet.type == WalletType.ethereum) {
_client = ethereum!.getWeb3Client(wallet); _client = ethereum!.getWeb3Client(wallet);
} }
if (wallet != null && wallet.type == WalletType.polygon) {
_client = polygon!.getWeb3Client(wallet);
}
if (_client == null) { if (_client == null) {
_client = Web3Client("https://ethereum.publicnode.com", Client()); _client = Web3Client("https://ethereum.publicnode.com", Client());
@ -31,6 +36,7 @@ class EnsRecord {
case WalletType.haven: case WalletType.haven:
return await ens.withName(name).getCoinAddress(CoinType.XHV); return await ens.withName(name).getCoinAddress(CoinType.XHV);
case WalletType.ethereum: case WalletType.ethereum:
case WalletType.polygon:
default: default:
return (await ens.withName(name).getAddress()).hex; return (await ens.withName(name).getAddress()).hex;
} }

View file

@ -19,7 +19,8 @@ class MainActions {
final bool Function(DashboardViewModel viewModel)? isEnabled; final bool Function(DashboardViewModel viewModel)? isEnabled;
final bool Function(DashboardViewModel viewModel)? canShow; final bool Function(DashboardViewModel viewModel)? canShow;
final Future<void> Function(BuildContext context, DashboardViewModel viewModel) onTap; final Future<void> Function(
BuildContext context, DashboardViewModel viewModel) onTap;
MainActions._({ MainActions._({
required this.name, required this.name,
@ -52,6 +53,7 @@ class MainActions {
case WalletType.bitcoin: case WalletType.bitcoin:
case WalletType.litecoin: case WalletType.litecoin:
case WalletType.ethereum: case WalletType.ethereum:
case WalletType.polygon:
case WalletType.bitcoinCash: case WalletType.bitcoinCash:
switch (defaultBuyProvider) { switch (defaultBuyProvider) {
case BuyProviderType.AskEachTime: case BuyProviderType.AskEachTime:
@ -124,6 +126,7 @@ class MainActions {
case WalletType.bitcoin: case WalletType.bitcoin:
case WalletType.litecoin: case WalletType.litecoin:
case WalletType.ethereum: case WalletType.ethereum:
case WalletType.polygon:
case WalletType.bitcoinCash: case WalletType.bitcoinCash:
if (viewModel.isEnabledSellAction) { if (viewModel.isEnabledSellAction) {
final moonPaySellProvider = MoonPaySellProvider(); final moonPaySellProvider = MoonPaySellProvider();

View file

@ -133,6 +133,22 @@ Future<List<Node>> loadDefaultNanoPowNodes() async {
return nodes; return nodes;
} }
Future<List<Node>> loadDefaultPolygonNodes() async {
final nodesRaw = await rootBundle.loadString('assets/polygon_node_list.yml');
final loadedNodes = loadYaml(nodesRaw) as YamlList;
final nodes = <Node>[];
for (final raw in loadedNodes) {
if (raw is Map) {
final node = Node.fromMap(Map<String, Object>.from(raw));
node.type = WalletType.polygon;
nodes.add(node);
}
}
return nodes;
}
Future<void> resetToDefault(Box<Node> nodeSource) async { Future<void> resetToDefault(Box<Node> nodeSource) async {
final moneroNodes = await loadDefaultNodes(); final moneroNodes = await loadDefaultNodes();
final bitcoinElectrumServerList = await loadBitcoinElectrumServerList(); final bitcoinElectrumServerList = await loadBitcoinElectrumServerList();
@ -141,6 +157,8 @@ Future<void> resetToDefault(Box<Node> nodeSource) async {
final havenNodes = await loadDefaultHavenNodes(); final havenNodes = await loadDefaultHavenNodes();
final ethereumNodes = await loadDefaultEthereumNodes(); final ethereumNodes = await loadDefaultEthereumNodes();
final nanoNodes = await loadDefaultNanoNodes(); final nanoNodes = await loadDefaultNanoNodes();
final polygonNodes = await loadDefaultPolygonNodes();
final nodes = moneroNodes + final nodes = moneroNodes +
bitcoinElectrumServerList + bitcoinElectrumServerList +
@ -148,7 +166,8 @@ Future<void> resetToDefault(Box<Node> nodeSource) async {
havenNodes + havenNodes +
ethereumNodes + ethereumNodes +
bitcoinCashElectrumServerList + bitcoinCashElectrumServerList +
nanoNodes; nanoNodes +
polygonNodes;
await nodeSource.clear(); await nodeSource.clear();
await nodeSource.addAll(nodes); await nodeSource.addAll(nodes);

View file

@ -6,6 +6,7 @@ class PreferencesKey {
static const currentLitecoinElectrumSererIdKey = 'current_node_id_ltc'; static const currentLitecoinElectrumSererIdKey = 'current_node_id_ltc';
static const currentHavenNodeIdKey = 'current_node_id_xhv'; static const currentHavenNodeIdKey = 'current_node_id_xhv';
static const currentEthereumNodeIdKey = 'current_node_id_eth'; static const currentEthereumNodeIdKey = 'current_node_id_eth';
static const currentPolygonNodeIdKey = 'current_node_id_matic';
static const currentNanoNodeIdKey = 'current_node_id_nano'; static const currentNanoNodeIdKey = 'current_node_id_nano';
static const currentNanoPowNodeIdKey = 'current_node_id_nano_pow'; static const currentNanoPowNodeIdKey = 'current_node_id_nano_pow';
static const currentBananoNodeIdKey = 'current_node_id_banano'; static const currentBananoNodeIdKey = 'current_node_id_banano';
@ -37,6 +38,7 @@ class PreferencesKey {
static const havenTransactionPriority = 'current_fee_priority_haven'; static const havenTransactionPriority = 'current_fee_priority_haven';
static const litecoinTransactionPriority = 'current_fee_priority_litecoin'; static const litecoinTransactionPriority = 'current_fee_priority_litecoin';
static const ethereumTransactionPriority = 'current_fee_priority_ethereum'; static const ethereumTransactionPriority = 'current_fee_priority_ethereum';
static const polygonTransactionPriority = 'current_fee_priority_polygon';
static const bitcoinCashTransactionPriority = 'current_fee_priority_bitcoin_cash'; static const bitcoinCashTransactionPriority = 'current_fee_priority_bitcoin_cash';
static const shouldShowReceiveWarning = 'should_show_receive_warning'; static const shouldShowReceiveWarning = 'should_show_receive_warning';
static const shouldShowYatPopup = 'should_show_yat_popup'; static const shouldShowYatPopup = 'should_show_yat_popup';
@ -50,6 +52,7 @@ class PreferencesKey {
static const sortBalanceBy = 'sort_balance_by'; static const sortBalanceBy = 'sort_balance_by';
static const pinNativeTokenAtTop = 'pin_native_token_at_top'; static const pinNativeTokenAtTop = 'pin_native_token_at_top';
static const useEtherscan = 'use_etherscan'; static const useEtherscan = 'use_etherscan';
static const usePolygonScan = 'use_polygonscan';
static const defaultNanoRep = 'default_nano_representative'; static const defaultNanoRep = 'default_nano_representative';
static const defaultBananoRep = 'default_banano_representative'; static const defaultBananoRep = 'default_banano_representative';
static const lookupsTwitter = 'looks_up_twitter'; static const lookupsTwitter = 'looks_up_twitter';

View file

@ -3,6 +3,7 @@ import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
import 'package:cake_wallet/ethereum/ethereum.dart'; import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:cake_wallet/haven/haven.dart'; import 'package:cake_wallet/haven/haven.dart';
import 'package:cake_wallet/monero/monero.dart'; import 'package:cake_wallet/monero/monero.dart';
import 'package:cake_wallet/polygon/polygon.dart';
import 'package:cw_core/transaction_priority.dart'; import 'package:cw_core/transaction_priority.dart';
import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_type.dart';
@ -24,6 +25,8 @@ List<TransactionPriority> priorityForWalletType(WalletType type) {
case WalletType.nano: case WalletType.nano:
case WalletType.banano: case WalletType.banano:
return []; return [];
case WalletType.polygon:
return polygon!.getTransactionPriorities();
default: default:
return []; return [];
} }

156
lib/polygon/cw_polygon.dart Normal file
View file

@ -0,0 +1,156 @@
part of 'polygon.dart';
class CWPolygon extends Polygon {
@override
List<String> getPolygonWordList(String language) => EthereumMnemonics.englishWordlist;
WalletService createPolygonWalletService(Box<WalletInfo> walletInfoSource) =>
PolygonWalletService(walletInfoSource);
@override
WalletCredentials createPolygonNewWalletCredentials({
required String name,
WalletInfo? walletInfo,
}) =>
PolygonNewWalletCredentials(name: name, walletInfo: walletInfo);
@override
WalletCredentials createPolygonRestoreWalletFromSeedCredentials({
required String name,
required String mnemonic,
required String password,
}) =>
PolygonRestoreWalletFromSeedCredentials(name: name, password: password, mnemonic: mnemonic);
@override
WalletCredentials createPolygonRestoreWalletFromPrivateKey({
required String name,
required String privateKey,
required String password,
}) =>
PolygonRestoreWalletFromPrivateKey(name: name, password: password, privateKey: privateKey);
@override
String getAddress(WalletBase wallet) => (wallet as PolygonWallet).walletAddresses.address;
@override
String getPrivateKey(WalletBase wallet) {
final privateKeyHolder = (wallet as PolygonWallet).polygonPrivateKey;
String stringKey = bytesToHex(privateKeyHolder.privateKey);
return stringKey;
}
@override
String getPublicKey(WalletBase wallet) {
final privateKeyInUnitInt = (wallet as PolygonWallet).polygonPrivateKey;
final publicKey = privateKeyInUnitInt.address.hex;
return publicKey;
}
@override
TransactionPriority getDefaultTransactionPriority() => PolygonTransactionPriority.medium;
@override
TransactionPriority getPolygonTransactionPrioritySlow() => PolygonTransactionPriority.slow;
@override
List<TransactionPriority> getTransactionPriorities() => PolygonTransactionPriority.all;
@override
TransactionPriority deserializePolygonTransactionPriority(int raw) =>
PolygonTransactionPriority.deserialize(raw: raw);
Object createPolygonTransactionCredentials(
List<Output> outputs, {
required TransactionPriority priority,
required CryptoCurrency currency,
int? feeRate,
}) =>
PolygonTransactionCredentials(
outputs
.map((out) => OutputInfo(
fiatAmount: out.fiatAmount,
cryptoAmount: out.cryptoAmount,
address: out.address,
note: out.note,
sendAll: out.sendAll,
extractedAddress: out.extractedAddress,
isParsedAddress: out.isParsedAddress,
formattedCryptoAmount: out.formattedCryptoAmount))
.toList(),
priority: priority as PolygonTransactionPriority,
currency: currency,
feeRate: feeRate,
);
Object createPolygonTransactionCredentialsRaw(
List<OutputInfo> outputs, {
TransactionPriority? priority,
required CryptoCurrency currency,
required int feeRate,
}) =>
PolygonTransactionCredentials(
outputs,
priority: priority as PolygonTransactionPriority?,
currency: currency,
feeRate: feeRate,
);
@override
int formatterPolygonParseAmount(String amount) => PolygonFormatter.parsePolygonAmount(amount);
@override
double formatterPolygonAmountToDouble(
{TransactionInfo? transaction, BigInt? amount, int exponent = 18}) {
assert(transaction != null || amount != null);
if (transaction != null) {
transaction as PolygonTransactionInfo;
return transaction.ethAmount / BigInt.from(10).pow(transaction.exponent);
} else {
return (amount!) / BigInt.from(10).pow(exponent);
}
}
@override
List<Erc20Token> getERC20Currencies(WalletBase wallet) {
final polygonWallet = wallet as PolygonWallet;
return polygonWallet.erc20Currencies;
}
@override
Future<void> addErc20Token(WalletBase wallet, Erc20Token token) async =>
await (wallet as PolygonWallet).addErc20Token(token);
@override
Future<void> deleteErc20Token(WalletBase wallet, Erc20Token token) async =>
await (wallet as PolygonWallet).deleteErc20Token(token);
@override
Future<Erc20Token?> getErc20Token(WalletBase wallet, String contractAddress) async {
final polygonWallet = wallet as PolygonWallet;
return await polygonWallet.getErc20Token(contractAddress);
}
@override
CryptoCurrency assetOfTransaction(WalletBase wallet, TransactionInfo transaction) {
transaction as PolygonTransactionInfo;
if (transaction.tokenSymbol == CryptoCurrency.maticpoly.title) {
return CryptoCurrency.maticpoly;
}
wallet as PolygonWallet;
return wallet.erc20Currencies.firstWhere(
(element) => transaction.tokenSymbol.toLowerCase() == element.symbol.toLowerCase());
}
@override
void updatePolygonScanUsageState(WalletBase wallet, bool isEnabled) {
(wallet as PolygonWallet).updatePolygonScanUsageState(isEnabled);
}
@override
Web3Client? getWeb3Client(WalletBase wallet) {
return (wallet as PolygonWallet).getWeb3Client();
}
}

View file

@ -3,9 +3,11 @@ import 'package:cake_wallet/core/fiat_conversion_service.dart';
import 'package:cake_wallet/entities/fiat_api_mode.dart'; import 'package:cake_wallet/entities/fiat_api_mode.dart';
import 'package:cake_wallet/entities/update_haven_rate.dart'; import 'package:cake_wallet/entities/update_haven_rate.dart';
import 'package:cake_wallet/ethereum/ethereum.dart'; import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:cake_wallet/polygon/polygon.dart';
import 'package:cake_wallet/store/app_store.dart'; import 'package:cake_wallet/store/app_store.dart';
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart'; import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/store/settings_store.dart';
import 'package:cw_core/erc20_token.dart';
import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_type.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
@ -33,10 +35,18 @@ Future<void> startFiatRateUpdate(
torOnly: settingsStore.fiatApiMode == FiatApiMode.torOnly); torOnly: settingsStore.fiatApiMode == FiatApiMode.torOnly);
} }
Iterable<Erc20Token>? currencies;
if (appStore.wallet!.type == WalletType.ethereum) { if (appStore.wallet!.type == WalletType.ethereum) {
final currencies = currencies =
ethereum!.getERC20Currencies(appStore.wallet!).where((element) => element.enabled); ethereum!.getERC20Currencies(appStore.wallet!).where((element) => element.enabled);
}
if (appStore.wallet!.type == WalletType.polygon) {
currencies =
polygon!.getERC20Currencies(appStore.wallet!).where((element) => element.enabled);
}
if (currencies != null) {
for (final currency in currencies) { for (final currency in currencies) {
() async { () async {
fiatConversionStore.prices[currency] = await FiatConversionService.fetchPrice( fiatConversionStore.prices[currency] = await FiatConversionService.fetchPrice(

View file

@ -2,7 +2,8 @@ import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
import 'package:cake_wallet/entities/fiat_api_mode.dart'; import 'package:cake_wallet/entities/fiat_api_mode.dart';
import 'package:cake_wallet/entities/update_haven_rate.dart'; import 'package:cake_wallet/entities/update_haven_rate.dart';
import 'package:cake_wallet/ethereum/ethereum.dart'; import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:cake_wallet/nano/nano.dart'; import 'package:cake_wallet/polygon/polygon.dart';
import 'package:cw_core/erc20_token.dart';
import 'package:cw_core/transaction_history.dart'; import 'package:cw_core/transaction_history.dart';
import 'package:cw_core/balance.dart'; import 'package:cw_core/balance.dart';
import 'package:cw_core/transaction_info.dart'; import 'package:cw_core/transaction_info.dart';
@ -107,10 +108,17 @@ void startCurrentWalletChangeReaction(
fiat: settingsStore.fiatCurrency, fiat: settingsStore.fiatCurrency,
torOnly: settingsStore.fiatApiMode == FiatApiMode.torOnly); torOnly: settingsStore.fiatApiMode == FiatApiMode.torOnly);
Iterable<Erc20Token>? currencies;
if (wallet.type == WalletType.ethereum) { if (wallet.type == WalletType.ethereum) {
final currencies = currencies =
ethereum!.getERC20Currencies(appStore.wallet!).where((element) => element.enabled); ethereum!.getERC20Currencies(appStore.wallet!).where((element) => element.enabled);
}
if (wallet.type == WalletType.polygon) {
currencies =
polygon!.getERC20Currencies(appStore.wallet!).where((element) => element.enabled);
}
if (currencies != null) {
for (final currency in currencies) { for (final currency in currencies) {
() async { () async {
fiatConversionStore.prices[currency] = await FiatConversionService.fetchPrice( fiatConversionStore.prices[currency] = await FiatConversionService.fetchPrice(

View file

@ -0,0 +1,46 @@
import 'package:cake_wallet/core/wallet_connect/evm_chain_id.dart';
import 'package:cw_core/wallet_type.dart';
bool isEVMCompatibleChain(WalletType walletType) {
switch (walletType) {
case WalletType.polygon:
case WalletType.ethereum:
return true;
default:
return false;
}
}
String getChainNameSpaceAndIdBasedOnWalletType(WalletType walletType) {
switch (walletType) {
case WalletType.ethereum:
return EVMChainId.ethereum.chain();
case WalletType.polygon:
return EVMChainId.polygon.chain();
default:
return '';
}
}
int getChainIdBasedOnWalletType(WalletType walletType) {
switch (walletType) {
case WalletType.polygon:
return 137;
// For now, we return eth chain Id as the default, we'll modify as we add more wallets
case WalletType.ethereum:
default:
return 1;
}
}
String getChainNameBasedOnWalletType(WalletType walletType) {
switch (walletType) {
case WalletType.ethereum:
return 'eth';
case WalletType.polygon:
return 'polygon';
default:
return '';
}
}

View file

@ -33,6 +33,7 @@ class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionD
final litecoinIcon = Image.asset('assets/images/litecoin_icon.png', height: 24, width: 24); final litecoinIcon = Image.asset('assets/images/litecoin_icon.png', height: 24, width: 24);
final havenIcon = Image.asset('assets/images/haven_logo.png', height: 24, width: 24); final havenIcon = Image.asset('assets/images/haven_logo.png', height: 24, width: 24);
final ethereumIcon = Image.asset('assets/images/eth_icon.png', height: 24, width: 24); final ethereumIcon = Image.asset('assets/images/eth_icon.png', height: 24, width: 24);
final polygonIcon = Image.asset('assets/images/matic_icon.png', height: 24, width: 24);
final bitcoinCashIcon = Image.asset('assets/images/bch_icon.png', height: 24, width: 24); final bitcoinCashIcon = Image.asset('assets/images/bch_icon.png', height: 24, width: 24);
final nanoIcon = Image.asset('assets/images/nano_icon.png', height: 24, width: 24); final nanoIcon = Image.asset('assets/images/nano_icon.png', height: 24, width: 24);
final bananoIcon = Image.asset('assets/images/nano_icon.png', height: 24, width: 24); final bananoIcon = Image.asset('assets/images/nano_icon.png', height: 24, width: 24);
@ -150,6 +151,8 @@ class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionD
return nanoIcon; return nanoIcon;
case WalletType.banano: case WalletType.banano:
return bananoIcon; return bananoIcon;
case WalletType.polygon:
return polygonIcon;
default: default:
return nonWalletTypeIcon; return nonWalletTypeIcon;
} }

View file

@ -1,5 +1,6 @@
import 'package:auto_size_text/auto_size_text.dart'; import 'package:auto_size_text/auto_size_text.dart';
import 'package:cake_wallet/generated/i18n.dart'; 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/routes.dart';
import 'package:cake_wallet/src/screens/dashboard/pages/nft_listing_page.dart'; import 'package:cake_wallet/src/screens/dashboard/pages/nft_listing_page.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/home_screen_account_widget.dart'; import 'package:cake_wallet/src/screens/dashboard/widgets/home_screen_account_widget.dart';
@ -13,7 +14,6 @@ import 'package:cake_wallet/utils/feature_flag.dart';
import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/nft_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/nft_view_model.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:flutter_mobx/flutter_mobx.dart';
@ -32,12 +32,12 @@ class BalancePage extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Observer( return Observer(
builder: (context) { builder: (context) {
final isEthereumWallet = dashboardViewModel.type == WalletType.ethereum; final isEVMCompatible = isEVMCompatibleChain(dashboardViewModel.type);
return DefaultTabController( return DefaultTabController(
length: isEthereumWallet ? 2 : 1, length: isEVMCompatible ? 2 : 1,
child: Column( child: Column(
children: [ children: [
if (isEthereumWallet) if (isEVMCompatible)
Align( Align(
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Padding( child: Padding(
@ -66,7 +66,7 @@ class BalancePage extends StatelessWidget {
physics: NeverScrollableScrollPhysics(), physics: NeverScrollableScrollPhysics(),
children: [ children: [
CryptoBalanceWidget(dashboardViewModel: dashboardViewModel), CryptoBalanceWidget(dashboardViewModel: dashboardViewModel),
if (isEthereumWallet) NFTListingPage(nftViewModel: nftViewModel) if (isEVMCompatible) NFTListingPage(nftViewModel: nftViewModel)
], ],
), ),
), ),

View file

@ -32,7 +32,8 @@ class MenuWidgetState extends State<MenuWidget> {
this.ethereumIcon = Image.asset('assets/images/eth_icon.png'), this.ethereumIcon = Image.asset('assets/images/eth_icon.png'),
this.nanoIcon = Image.asset('assets/images/nano_icon.png'), this.nanoIcon = Image.asset('assets/images/nano_icon.png'),
this.bananoIcon = Image.asset('assets/images/nano_icon.png'), this.bananoIcon = Image.asset('assets/images/nano_icon.png'),
this.bitcoinCashIcon = Image.asset('assets/images/bch_icon.png'); this.bitcoinCashIcon = Image.asset('assets/images/bch_icon.png'),
this.polygonIcon = Image.asset('assets/images/matic_icon.png');
final largeScreen = 731; final largeScreen = 731;
@ -54,6 +55,8 @@ class MenuWidgetState extends State<MenuWidget> {
Image bitcoinCashIcon; Image bitcoinCashIcon;
Image nanoIcon; Image nanoIcon;
Image bananoIcon; Image bananoIcon;
Image polygonIcon;
@override @override
void initState() { void initState() {
@ -219,6 +222,8 @@ class MenuWidgetState extends State<MenuWidget> {
return nanoIcon; return nanoIcon;
case WalletType.banano: case WalletType.banano:
return bananoIcon; return bananoIcon;
case WalletType.polygon:
return polygonIcon;
default: default:
throw Exception('No icon for ${type.toString()}'); throw Exception('No icon for ${type.toString()}');
} }

View file

@ -1,5 +1,6 @@
import 'package:cake_wallet/src/widgets/base_text_form_field.dart'; import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/currency.dart'; import 'package:cw_core/currency.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
@ -23,6 +24,13 @@ class CurrencyInputField extends StatelessWidget {
final TextEditingController controller; final TextEditingController controller;
final bool isLight; final bool isLight;
String get _currencyName {
if (selectedCurrency is CryptoCurrency) {
return (selectedCurrency as CryptoCurrency).title.toUpperCase();
}
return selectedCurrency.name.toUpperCase();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final arrowBottomPurple = Image.asset( final arrowBottomPurple = Image.asset(
@ -74,7 +82,7 @@ class CurrencyInputField extends StatelessWidget {
child: arrowBottomPurple, child: arrowBottomPurple,
), ),
Text( Text(
selectedCurrency.name.toUpperCase(), _currencyName,
style: TextStyle( style: TextStyle(
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
fontSize: 16, fontSize: 16,
@ -83,7 +91,7 @@ class CurrencyInputField extends StatelessWidget {
), ),
if (selectedCurrency.tag != null) if (selectedCurrency.tag != null)
Padding( Padding(
padding: const EdgeInsets.only(right: 3.0), padding: const EdgeInsets.symmetric(horizontal: 3.0),
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: Theme.of(context).extension<SendPageTheme>()!.textFieldButtonColor, color: Theme.of(context).extension<SendPageTheme>()!.textFieldButtonColor,

View file

@ -2,9 +2,9 @@ import 'dart:async';
import 'package:cake_wallet/core/auth_service.dart'; import 'package:cake_wallet/core/auth_service.dart';
import 'package:cake_wallet/core/totp_request_details.dart'; import 'package:cake_wallet/core/totp_request_details.dart';
import 'package:cake_wallet/generated/i18n.dart'; 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/device_info.dart';
import 'package:cake_wallet/utils/payment_request.dart'; import 'package:cake_wallet/utils/payment_request.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/auth/auth_page.dart'; import 'package:cake_wallet/src/screens/auth/auth_page.dart';
@ -169,7 +169,7 @@ class RootState extends State<Root> with WidgetsBindingObserver {
); );
launchUri = null; launchUri = null;
} else if (isWalletConnectLink) { } else if (isWalletConnectLink) {
if (widget.appStore.wallet!.type == WalletType.ethereum) { if (isEVMCompatibleChain(widget.appStore.wallet!.type)) {
widget.navigatorKey.currentState?.pushNamed( widget.navigatorKey.currentState?.pushNamed(
Routes.walletConnectConnectionsListing, Routes.walletConnectConnectionsListing,
arguments: launchUri, arguments: launchUri,
@ -179,7 +179,7 @@ class RootState extends State<Root> with WidgetsBindingObserver {
_nonETHWalletErrorToast(S.current.switchToETHWallet); _nonETHWalletErrorToast(S.current.switchToETHWallet);
} }
} }
launchUri = null; launchUri = null;
return WillPopScope( return WillPopScope(
onWillPop: () async => false, onWillPop: () async => false,
@ -205,7 +205,7 @@ class RootState extends State<Root> with WidgetsBindingObserver {
String? _getRouteToGo() { String? _getRouteToGo() {
if (isWalletConnectLink) { if (isWalletConnectLink) {
if (widget.appStore.wallet!.type != WalletType.ethereum) { if (isEVMCompatibleChain(widget.appStore.wallet!.type)) {
_nonETHWalletErrorToast(S.current.switchToETHWallet); _nonETHWalletErrorToast(S.current.switchToETHWallet);
return null; return null;
} }

View file

@ -13,7 +13,8 @@ class PreSeedPage extends BasePage {
PreSeedPage(this.type, this.advancedPrivacySettingsViewModel) PreSeedPage(this.type, this.advancedPrivacySettingsViewModel)
: imageLight = Image.asset('assets/images/pre_seed_light.png'), : imageLight = Image.asset('assets/images/pre_seed_light.png'),
imageDark = Image.asset('assets/images/pre_seed_dark.png'), imageDark = Image.asset('assets/images/pre_seed_dark.png'),
seedPhraseLength = advancedPrivacySettingsViewModel.seedPhraseLength.value { seedPhraseLength =
advancedPrivacySettingsViewModel.seedPhraseLength.value {
wordsCount = _wordsCount(type, seedPhraseLength); wordsCount = _wordsCount(type, seedPhraseLength);
} }
@ -40,14 +41,14 @@ class PreSeedPage extends BasePage {
alignment: Alignment.center, alignment: Alignment.center,
padding: EdgeInsets.all(24), padding: EdgeInsets.all(24),
child: ConstrainedBox( child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: ResponsiveLayoutUtilBase.kDesktopMaxWidthConstraint), constraints: BoxConstraints(
maxWidth: ResponsiveLayoutUtilBase.kDesktopMaxWidthConstraint),
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[ children: <Widget>[
ConstrainedBox( ConstrainedBox(
constraints: BoxConstraints( constraints: BoxConstraints(
maxHeight: MediaQuery.of(context).size.height * 0.3 maxHeight: MediaQuery.of(context).size.height * 0.3),
),
child: AspectRatio(aspectRatio: 1, child: image), child: AspectRatio(aspectRatio: 1, child: image),
), ),
Padding( Padding(
@ -58,12 +59,14 @@ class PreSeedPage extends BasePage {
style: TextStyle( style: TextStyle(
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.normal, fontWeight: FontWeight.normal,
color: Theme.of(context).extension<CakeTextTheme>()!.secondaryTextColor), color: Theme.of(context)
.extension<CakeTextTheme>()!
.secondaryTextColor),
), ),
), ),
PrimaryButton( PrimaryButton(
onPressed: () => onPressed: () => Navigator.of(context)
Navigator.of(context).popAndPushNamed(Routes.seed, arguments: true), .popAndPushNamed(Routes.seed, arguments: true),
text: S.of(context).pre_seed_button_text, text: S.of(context).pre_seed_button_text,
color: Theme.of(context).primaryColor, color: Theme.of(context).primaryColor,
textColor: Colors.white) textColor: Colors.white)
@ -79,6 +82,7 @@ class PreSeedPage extends BasePage {
return 25; return 25;
case WalletType.ethereum: case WalletType.ethereum:
case WalletType.bitcoinCash: case WalletType.bitcoinCash:
case WalletType.polygon:
return seedPhraseLength; return seedPhraseLength;
default: default:
return 24; return 24;

View file

@ -1,4 +1,5 @@
import 'package:cake_wallet/core/wallet_connect/web3wallet_service.dart'; import 'package:cake_wallet/core/wallet_connect/web3wallet_service.dart';
import 'package:cake_wallet/reactions/wallet_connect.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arrow.dart'; import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arrow.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.dart'; import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart'; import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart';
@ -8,7 +9,6 @@ import 'package:cake_wallet/utils/feature_flag.dart';
import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:cake_wallet/view_model/settings/sync_mode.dart'; import 'package:cake_wallet/view_model/settings/sync_mode.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
@ -85,7 +85,7 @@ class ConnectionSyncPage extends BasePage {
); );
}, },
), ),
if (dashboardViewModel.wallet.type == WalletType.ethereum) ...[ if (isEVMCompatibleChain(dashboardViewModel.wallet.type)) ...[
WalletConnectTile( WalletConnectTile(
onTap: () => Navigator.of(context).pushNamed(Routes.walletConnectConnectionsListing), onTap: () => Navigator.of(context).pushNamed(Routes.walletConnectConnectionsListing),
), ),
@ -101,6 +101,7 @@ class ConnectionSyncPage extends BasePage {
); );
} }
Future<void> _presentReconnectAlert(BuildContext context) async { Future<void> _presentReconnectAlert(BuildContext context) async {
await showPopUp<void>( await showPopUp<void>(
context: context, context: context,

View file

@ -87,6 +87,14 @@ class PrivacyPage extends BasePage {
onValueChange: (BuildContext _, bool value) { onValueChange: (BuildContext _, bool value) {
_privacySettingsViewModel.setUseEtherscan(value); _privacySettingsViewModel.setUseEtherscan(value);
}), }),
if (_privacySettingsViewModel.canUsePolygonScan)
SettingsSwitcherCell(
title: S.current.polygonscan_history,
value: _privacySettingsViewModel.usePolygonScan,
onValueChange: (BuildContext _, bool value) {
_privacySettingsViewModel.setUsePolygonScan(value);
},
),
SettingsCellWithArrow( SettingsCellWithArrow(
title: S.current.domain_looks_up, title: S.current.domain_looks_up,
handler: (context) => Navigator.of(context).pushNamed(Routes.domainLookupsPage), handler: (context) => Navigator.of(context).pushNamed(Routes.domainLookupsPage),

View file

@ -14,12 +14,14 @@ import 'connection_widget.dart';
class ConnectionRequestWidget extends StatefulWidget { class ConnectionRequestWidget extends StatefulWidget {
const ConnectionRequestWidget({ const ConnectionRequestWidget({
required this.wallet, required this.wallet,
required this.chaindIdNamespace,
this.authRequest, this.authRequest,
this.sessionProposal, this.sessionProposal,
Key? key, Key? key,
}) : super(key: key); }) : super(key: key);
final Web3Wallet wallet; final Web3Wallet wallet;
final String chaindIdNamespace;
final AuthRequestModel? authRequest; final AuthRequestModel? authRequest;
final SessionRequestModel? sessionProposal; final SessionRequestModel? sessionProposal;
@ -52,23 +54,26 @@ class _ConnectionRequestWidgetState extends State<ConnectionRequestWidget> {
return _ConnectionMetadataDisplayWidget( return _ConnectionMetadataDisplayWidget(
metadata: metadata, metadata: metadata,
wallet: widget.wallet,
authRequest: widget.authRequest, authRequest: widget.authRequest,
sessionProposal: widget.sessionProposal, sessionProposal: widget.sessionProposal,
wallet: widget.wallet, chaindIdNamespace: widget.chaindIdNamespace,
); );
} }
} }
class _ConnectionMetadataDisplayWidget extends StatelessWidget { class _ConnectionMetadataDisplayWidget extends StatelessWidget {
const _ConnectionMetadataDisplayWidget({ const _ConnectionMetadataDisplayWidget({
required this.metadata,
required this.wallet, required this.wallet,
this.authRequest, required this.metadata,
required this.sessionProposal, required this.sessionProposal,
required this.chaindIdNamespace,
this.authRequest,
}); });
final ConnectionMetadata? metadata; final ConnectionMetadata? metadata;
final Web3Wallet wallet; final Web3Wallet wallet;
final String chaindIdNamespace;
final AuthRequestModel? authRequest; final AuthRequestModel? authRequest;
final SessionRequestModel? sessionProposal; final SessionRequestModel? sessionProposal;
@ -114,7 +119,11 @@ class _ConnectionMetadataDisplayWidget extends StatelessWidget {
const SizedBox(height: 8), const SizedBox(height: 8),
Visibility( Visibility(
visible: authRequest != null, visible: authRequest != null,
child: _AuthRequestWidget(wallet: wallet, authRequest: authRequest), child: _AuthRequestWidget(
wallet: wallet,
authRequest: authRequest,
chaindIdNamespace: chaindIdNamespace,
),
//If authRequest is null, sessionProposal is not null. //If authRequest is null, sessionProposal is not null.
replacement: _SessionProposalWidget(sessionProposal: sessionProposal!), replacement: _SessionProposalWidget(sessionProposal: sessionProposal!),
@ -126,16 +135,21 @@ class _ConnectionMetadataDisplayWidget extends StatelessWidget {
} }
class _AuthRequestWidget extends StatelessWidget { class _AuthRequestWidget extends StatelessWidget {
const _AuthRequestWidget({required this.wallet, this.authRequest}); const _AuthRequestWidget({
required this.wallet,
required this.chaindIdNamespace,
this.authRequest,
});
final Web3Wallet wallet; final Web3Wallet wallet;
final String chaindIdNamespace;
final AuthRequestModel? authRequest; final AuthRequestModel? authRequest;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final model = ConnectionModel( final model = ConnectionModel(
text: wallet.formatAuthMessage( text: wallet.formatAuthMessage(
iss: 'did:pkh:eip155:1:${authRequest!.iss}', iss: 'did:pkh:$chaindIdNamespace:${authRequest!.iss}',
cacaoPayload: CacaoRequestPayload.fromPayloadParams( cacaoPayload: CacaoRequestPayload.fromPayloadParams(
authRequest!.request.payloadParams, authRequest!.request.payloadParams,
), ),

View file

@ -49,6 +49,7 @@ class WalletListBodyState extends State<WalletListBody> {
final ethereumIcon = Image.asset('assets/images/eth_icon.png', height: 24, width: 24); final ethereumIcon = Image.asset('assets/images/eth_icon.png', height: 24, width: 24);
final bitcoinCashIcon = Image.asset('assets/images/bch_icon.png', height: 24, width: 24); final bitcoinCashIcon = Image.asset('assets/images/bch_icon.png', height: 24, width: 24);
final nanoIcon = Image.asset('assets/images/nano_icon.png', height: 24, width: 24); final nanoIcon = Image.asset('assets/images/nano_icon.png', height: 24, width: 24);
final polygonIcon = Image.asset('assets/images/matic_icon.png', height: 24, width: 24);
final scrollController = ScrollController(); final scrollController = ScrollController();
final double tileHeight = 60; final double tileHeight = 60;
Flushbar<void>? _progressBar; Flushbar<void>? _progressBar;
@ -256,6 +257,8 @@ class WalletListBodyState extends State<WalletListBody> {
return bitcoinCashIcon; return bitcoinCashIcon;
case WalletType.nano: case WalletType.nano:
return nanoIcon; return nanoIcon;
case WalletType.polygon:
return polygonIcon;
default: default:
return nonWalletTypeIcon; return nonWalletTypeIcon;
} }

View file

@ -1,8 +1,8 @@
import 'package:cake_wallet/core/wallet_connect/web3wallet_service.dart'; import 'package:cake_wallet/core/wallet_connect/web3wallet_service.dart';
import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/reactions/wallet_connect.dart';
import 'package:cake_wallet/utils/exception_handler.dart'; import 'package:cake_wallet/utils/exception_handler.dart';
import 'package:cw_core/transaction_info.dart'; import 'package:cw_core/transaction_info.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
import 'package:cw_core/balance.dart'; import 'package:cw_core/balance.dart';
import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_base.dart';
@ -44,7 +44,7 @@ abstract class AppStoreBase with Store {
this.wallet = wallet; this.wallet = wallet;
this.wallet!.setExceptionHandler(ExceptionHandler.onError); this.wallet!.setExceptionHandler(ExceptionHandler.onError);
if (wallet.type == WalletType.ethereum) { if (isEVMCompatibleChain(wallet.type)) {
getIt.get<Web3WalletService>().init(); getIt.get<Web3WalletService>().init();
} }
} }

View file

@ -12,6 +12,7 @@ import 'package:cake_wallet/entities/preferences_key.dart';
import 'package:cake_wallet/entities/seed_phrase_length.dart'; import 'package:cake_wallet/entities/seed_phrase_length.dart';
import 'package:cake_wallet/entities/seed_type.dart'; import 'package:cake_wallet/entities/seed_type.dart';
import 'package:cake_wallet/entities/sort_balance_types.dart'; import 'package:cake_wallet/entities/sort_balance_types.dart';
import 'package:cake_wallet/polygon/polygon.dart';
import 'package:cake_wallet/exchange/provider/trocador_exchange_provider.dart'; import 'package:cake_wallet/exchange/provider/trocador_exchange_provider.dart';
import 'package:cake_wallet/view_model/settings/sync_mode.dart'; import 'package:cake_wallet/view_model/settings/sync_mode.dart';
import 'package:cake_wallet/utils/device_info.dart'; import 'package:cake_wallet/utils/device_info.dart';
@ -88,6 +89,7 @@ abstract class SettingsStoreBase with Store {
required this.sortBalanceBy, required this.sortBalanceBy,
required this.pinNativeTokenAtTop, required this.pinNativeTokenAtTop,
required this.useEtherscan, required this.useEtherscan,
required this.usePolygonScan,
required this.defaultNanoRep, required this.defaultNanoRep,
required this.defaultBananoRep, required this.defaultBananoRep,
required this.lookupsTwitter, required this.lookupsTwitter,
@ -101,6 +103,7 @@ abstract class SettingsStoreBase with Store {
TransactionPriority? initialHavenTransactionPriority, TransactionPriority? initialHavenTransactionPriority,
TransactionPriority? initialLitecoinTransactionPriority, TransactionPriority? initialLitecoinTransactionPriority,
TransactionPriority? initialEthereumTransactionPriority, TransactionPriority? initialEthereumTransactionPriority,
TransactionPriority? initialPolygonTransactionPriority,
TransactionPriority? initialBitcoinCashTransactionPriority}) TransactionPriority? initialBitcoinCashTransactionPriority})
: nodes = ObservableMap<WalletType, Node>.of(nodes), : nodes = ObservableMap<WalletType, Node>.of(nodes),
powNodes = ObservableMap<WalletType, Node>.of(powNodes), powNodes = ObservableMap<WalletType, Node>.of(powNodes),
@ -165,6 +168,10 @@ abstract class SettingsStoreBase with Store {
priority[WalletType.ethereum] = initialEthereumTransactionPriority; priority[WalletType.ethereum] = initialEthereumTransactionPriority;
} }
if (initialPolygonTransactionPriority != null) {
priority[WalletType.polygon] = initialPolygonTransactionPriority;
}
if (initialBitcoinCashTransactionPriority != null) { if (initialBitcoinCashTransactionPriority != null) {
priority[WalletType.bitcoinCash] = initialBitcoinCashTransactionPriority; priority[WalletType.bitcoinCash] = initialBitcoinCashTransactionPriority;
} }
@ -202,6 +209,9 @@ abstract class SettingsStoreBase with Store {
case WalletType.bitcoinCash: case WalletType.bitcoinCash:
key = PreferencesKey.bitcoinCashTransactionPriority; key = PreferencesKey.bitcoinCashTransactionPriority;
break; break;
case WalletType.polygon:
key = PreferencesKey.polygonTransactionPriority;
break;
default: default:
key = null; key = null;
} }
@ -245,8 +255,8 @@ abstract class SettingsStoreBase with Store {
reaction( reaction(
(_) => moneroSeedType, (_) => moneroSeedType,
(SeedType moneroSeedType) => sharedPreferences.setInt( (SeedType moneroSeedType) =>
PreferencesKey.moneroSeedType, moneroSeedType.raw)); sharedPreferences.setInt(PreferencesKey.moneroSeedType, moneroSeedType.raw));
reaction( reaction(
(_) => fiatApiMode, (_) => fiatApiMode,
@ -342,9 +352,9 @@ abstract class SettingsStoreBase with Store {
sharedPreferences.setString(PreferencesKey.currentLanguageCode, languageCode)); sharedPreferences.setString(PreferencesKey.currentLanguageCode, languageCode));
reaction( reaction(
(_) => seedPhraseLength, (_) => seedPhraseLength,
(SeedPhraseLength seedPhraseWordCount) => (SeedPhraseLength seedPhraseWordCount) => sharedPreferences.setInt(
sharedPreferences.setInt(PreferencesKey.currentSeedPhraseLength, seedPhraseWordCount.value)); PreferencesKey.currentSeedPhraseLength, seedPhraseWordCount.value));
reaction( reaction(
(_) => pinTimeOutDuration, (_) => pinTimeOutDuration,
@ -388,6 +398,11 @@ abstract class SettingsStoreBase with Store {
(bool useEtherscan) => (bool useEtherscan) =>
_sharedPreferences.setBool(PreferencesKey.useEtherscan, useEtherscan)); _sharedPreferences.setBool(PreferencesKey.useEtherscan, useEtherscan));
reaction(
(_) => usePolygonScan,
(bool usePolygonScan) =>
_sharedPreferences.setBool(PreferencesKey.usePolygonScan, usePolygonScan));
reaction((_) => defaultNanoRep, reaction((_) => defaultNanoRep,
(String nanoRep) => _sharedPreferences.setString(PreferencesKey.defaultNanoRep, nanoRep)); (String nanoRep) => _sharedPreferences.setString(PreferencesKey.defaultNanoRep, nanoRep));
@ -396,34 +411,32 @@ abstract class SettingsStoreBase with Store {
(String bananoRep) => (String bananoRep) =>
_sharedPreferences.setString(PreferencesKey.defaultBananoRep, bananoRep)); _sharedPreferences.setString(PreferencesKey.defaultBananoRep, bananoRep));
reaction( reaction(
(_) => lookupsTwitter, (_) => lookupsTwitter,
(bool looksUpTwitter) => (bool looksUpTwitter) =>
_sharedPreferences.setBool(PreferencesKey.lookupsTwitter, looksUpTwitter)); _sharedPreferences.setBool(PreferencesKey.lookupsTwitter, looksUpTwitter));
reaction( reaction(
(_) => lookupsMastodon, (_) => lookupsMastodon,
(bool looksUpMastodon) => (bool looksUpMastodon) =>
_sharedPreferences.setBool(PreferencesKey.lookupsMastodon, looksUpMastodon)); _sharedPreferences.setBool(PreferencesKey.lookupsMastodon, looksUpMastodon));
reaction( reaction(
(_) => lookupsYatService, (_) => lookupsYatService,
(bool looksUpYatService) => (bool looksUpYatService) =>
_sharedPreferences.setBool(PreferencesKey.lookupsYatService, looksUpYatService)); _sharedPreferences.setBool(PreferencesKey.lookupsYatService, looksUpYatService));
reaction( reaction(
(_) => lookupsUnstoppableDomains, (_) => lookupsUnstoppableDomains,
(bool looksUpUnstoppableDomains) => (bool looksUpUnstoppableDomains) => _sharedPreferences.setBool(
_sharedPreferences.setBool(PreferencesKey.lookupsUnstoppableDomains, looksUpUnstoppableDomains)); PreferencesKey.lookupsUnstoppableDomains, looksUpUnstoppableDomains));
reaction( reaction(
(_) => lookupsOpenAlias, (_) => lookupsOpenAlias,
(bool looksUpOpenAlias) => (bool looksUpOpenAlias) =>
_sharedPreferences.setBool(PreferencesKey.lookupsOpenAlias, looksUpOpenAlias)); _sharedPreferences.setBool(PreferencesKey.lookupsOpenAlias, looksUpOpenAlias));
reaction( reaction((_) => lookupsENS,
(_) => lookupsENS, (bool looksUpENS) => _sharedPreferences.setBool(PreferencesKey.lookupsENS, looksUpENS));
(bool looksUpENS) =>
_sharedPreferences.setBool(PreferencesKey.lookupsENS, looksUpENS));
this.nodes.observe((change) { this.nodes.observe((change) {
if (change.newValue != null && change.key != null) { if (change.newValue != null && change.key != null) {
@ -562,6 +575,9 @@ abstract class SettingsStoreBase with Store {
@observable @observable
bool useEtherscan; bool useEtherscan;
@observable
bool usePolygonScan;
@observable @observable
String defaultNanoRep; String defaultNanoRep;
@ -651,6 +667,7 @@ abstract class SettingsStoreBase with Store {
TransactionPriority? havenTransactionPriority; TransactionPriority? havenTransactionPriority;
TransactionPriority? litecoinTransactionPriority; TransactionPriority? litecoinTransactionPriority;
TransactionPriority? ethereumTransactionPriority; TransactionPriority? ethereumTransactionPriority;
TransactionPriority? polygonTransactionPriority;
TransactionPriority? bitcoinCashTransactionPriority; TransactionPriority? bitcoinCashTransactionPriority;
if (sharedPreferences.getInt(PreferencesKey.havenTransactionPriority) != null) { if (sharedPreferences.getInt(PreferencesKey.havenTransactionPriority) != null) {
@ -662,9 +679,13 @@ abstract class SettingsStoreBase with Store {
sharedPreferences.getInt(PreferencesKey.litecoinTransactionPriority)!); sharedPreferences.getInt(PreferencesKey.litecoinTransactionPriority)!);
} }
if (sharedPreferences.getInt(PreferencesKey.ethereumTransactionPriority) != null) { if (sharedPreferences.getInt(PreferencesKey.ethereumTransactionPriority) != null) {
ethereumTransactionPriority = bitcoin?.deserializeLitecoinTransactionPriority( ethereumTransactionPriority = ethereum?.deserializeEthereumTransactionPriority(
sharedPreferences.getInt(PreferencesKey.ethereumTransactionPriority)!); sharedPreferences.getInt(PreferencesKey.ethereumTransactionPriority)!);
} }
if (sharedPreferences.getInt(PreferencesKey.polygonTransactionPriority) != null) {
polygonTransactionPriority = polygon?.deserializePolygonTransactionPriority(
sharedPreferences.getInt(PreferencesKey.polygonTransactionPriority)!);
}
if (sharedPreferences.getInt(PreferencesKey.bitcoinCashTransactionPriority) != null) { if (sharedPreferences.getInt(PreferencesKey.bitcoinCashTransactionPriority) != null) {
bitcoinCashTransactionPriority = bitcoinCash?.deserializeBitcoinCashTransactionPriority( bitcoinCashTransactionPriority = bitcoinCash?.deserializeBitcoinCashTransactionPriority(
sharedPreferences.getInt(PreferencesKey.bitcoinCashTransactionPriority)!); sharedPreferences.getInt(PreferencesKey.bitcoinCashTransactionPriority)!);
@ -676,6 +697,7 @@ abstract class SettingsStoreBase with Store {
litecoinTransactionPriority ??= bitcoin?.getLitecoinTransactionPriorityMedium(); litecoinTransactionPriority ??= bitcoin?.getLitecoinTransactionPriorityMedium();
ethereumTransactionPriority ??= ethereum?.getDefaultTransactionPriority(); ethereumTransactionPriority ??= ethereum?.getDefaultTransactionPriority();
bitcoinCashTransactionPriority ??= bitcoinCash?.getDefaultTransactionPriority(); bitcoinCashTransactionPriority ??= bitcoinCash?.getDefaultTransactionPriority();
polygonTransactionPriority ??= polygon?.getDefaultTransactionPriority();
final currentBalanceDisplayMode = BalanceDisplayMode.deserialize( final currentBalanceDisplayMode = BalanceDisplayMode.deserialize(
raw: sharedPreferences.getInt(PreferencesKey.currentBalanceDisplayModeKey)!); raw: sharedPreferences.getInt(PreferencesKey.currentBalanceDisplayModeKey)!);
@ -749,12 +771,14 @@ abstract class SettingsStoreBase with Store {
final pinNativeTokenAtTop = final pinNativeTokenAtTop =
sharedPreferences.getBool(PreferencesKey.pinNativeTokenAtTop) ?? true; sharedPreferences.getBool(PreferencesKey.pinNativeTokenAtTop) ?? true;
final useEtherscan = sharedPreferences.getBool(PreferencesKey.useEtherscan) ?? true; final useEtherscan = sharedPreferences.getBool(PreferencesKey.useEtherscan) ?? true;
final usePolygonScan = sharedPreferences.getBool(PreferencesKey.usePolygonScan) ?? true;
final defaultNanoRep = sharedPreferences.getString(PreferencesKey.defaultNanoRep) ?? ""; final defaultNanoRep = sharedPreferences.getString(PreferencesKey.defaultNanoRep) ?? "";
final defaultBananoRep = sharedPreferences.getString(PreferencesKey.defaultBananoRep) ?? ""; final defaultBananoRep = sharedPreferences.getString(PreferencesKey.defaultBananoRep) ?? "";
final lookupsTwitter = sharedPreferences.getBool(PreferencesKey.lookupsTwitter) ?? true; final lookupsTwitter = sharedPreferences.getBool(PreferencesKey.lookupsTwitter) ?? true;
final lookupsMastodon = sharedPreferences.getBool(PreferencesKey.lookupsMastodon) ?? true; final lookupsMastodon = sharedPreferences.getBool(PreferencesKey.lookupsMastodon) ?? true;
final lookupsYatService = sharedPreferences.getBool(PreferencesKey.lookupsYatService) ?? true; final lookupsYatService = sharedPreferences.getBool(PreferencesKey.lookupsYatService) ?? true;
final lookupsUnstoppableDomains = sharedPreferences.getBool(PreferencesKey.lookupsUnstoppableDomains) ?? true; final lookupsUnstoppableDomains =
sharedPreferences.getBool(PreferencesKey.lookupsUnstoppableDomains) ?? true;
final lookupsOpenAlias = sharedPreferences.getBool(PreferencesKey.lookupsOpenAlias) ?? true; final lookupsOpenAlias = sharedPreferences.getBool(PreferencesKey.lookupsOpenAlias) ?? true;
final lookupsENS = sharedPreferences.getBool(PreferencesKey.lookupsENS) ?? true; final lookupsENS = sharedPreferences.getBool(PreferencesKey.lookupsENS) ?? true;
@ -774,6 +798,7 @@ abstract class SettingsStoreBase with Store {
sharedPreferences.getInt(PreferencesKey.currentBitcoinCashNodeIdKey); sharedPreferences.getInt(PreferencesKey.currentBitcoinCashNodeIdKey);
final havenNodeId = sharedPreferences.getInt(PreferencesKey.currentHavenNodeIdKey); final havenNodeId = sharedPreferences.getInt(PreferencesKey.currentHavenNodeIdKey);
final ethereumNodeId = sharedPreferences.getInt(PreferencesKey.currentEthereumNodeIdKey); final ethereumNodeId = sharedPreferences.getInt(PreferencesKey.currentEthereumNodeIdKey);
final polygonNodeId = sharedPreferences.getInt(PreferencesKey.currentPolygonNodeIdKey);
final nanoNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoNodeIdKey); final nanoNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoNodeIdKey);
final nanoPowNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoPowNodeIdKey); final nanoPowNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoPowNodeIdKey);
final moneroNode = nodeSource.get(nodeId); final moneroNode = nodeSource.get(nodeId);
@ -781,6 +806,7 @@ abstract class SettingsStoreBase with Store {
final litecoinElectrumServer = nodeSource.get(litecoinElectrumServerId); final litecoinElectrumServer = nodeSource.get(litecoinElectrumServerId);
final havenNode = nodeSource.get(havenNodeId); final havenNode = nodeSource.get(havenNodeId);
final ethereumNode = nodeSource.get(ethereumNodeId); final ethereumNode = nodeSource.get(ethereumNodeId);
final polygonNode = nodeSource.get(polygonNodeId);
final bitcoinCashElectrumServer = nodeSource.get(bitcoinCashElectrumServerId); final bitcoinCashElectrumServer = nodeSource.get(bitcoinCashElectrumServerId);
final nanoNode = nodeSource.get(nanoNodeId); final nanoNode = nodeSource.get(nanoNodeId);
final nanoPowNode = powNodeSource.get(nanoPowNodeId); final nanoPowNode = powNodeSource.get(nanoPowNodeId);
@ -824,6 +850,10 @@ abstract class SettingsStoreBase with Store {
nodes[WalletType.ethereum] = ethereumNode; nodes[WalletType.ethereum] = ethereumNode;
} }
if (polygonNode != null) {
nodes[WalletType.polygon] = polygonNode;
}
if (bitcoinCashElectrumServer != null) { if (bitcoinCashElectrumServer != null) {
nodes[WalletType.bitcoinCash] = bitcoinCashElectrumServer; nodes[WalletType.bitcoinCash] = bitcoinCashElectrumServer;
} }
@ -841,19 +871,19 @@ abstract class SettingsStoreBase with Store {
}); });
final savedSyncAll = sharedPreferences.getBool(PreferencesKey.syncAllKey) ?? true; final savedSyncAll = sharedPreferences.getBool(PreferencesKey.syncAllKey) ?? true;
return SettingsStore( return SettingsStore(
sharedPreferences: sharedPreferences, sharedPreferences: sharedPreferences,
initialShouldShowMarketPlaceInDashboard: shouldShowMarketPlaceInDashboard, initialShouldShowMarketPlaceInDashboard: shouldShowMarketPlaceInDashboard,
nodes: nodes, nodes: nodes,
powNodes: powNodes, powNodes: powNodes,
appVersion: packageInfo.version, appVersion: packageInfo.version,
deviceName: deviceName, deviceName: deviceName,
isBitcoinBuyEnabled: isBitcoinBuyEnabled, isBitcoinBuyEnabled: isBitcoinBuyEnabled,
initialFiatCurrency: currentFiatCurrency, initialFiatCurrency: currentFiatCurrency,
initialBalanceDisplayMode: currentBalanceDisplayMode, initialBalanceDisplayMode: currentBalanceDisplayMode,
initialSaveRecipientAddress: shouldSaveRecipientAddress, initialSaveRecipientAddress: shouldSaveRecipientAddress,
initialAutoGenerateSubaddressStatus: autoGenerateSubaddressStatus, initialAutoGenerateSubaddressStatus: autoGenerateSubaddressStatus,
initialMoneroSeedType: moneroSeedType, initialMoneroSeedType: moneroSeedType,
initialAppSecure: isAppSecure, initialAppSecure: isAppSecure,
initialDisableBuy: disableBuy, initialDisableBuy: disableBuy,
initialDisableSell: disableSell, initialDisableSell: disableSell,
@ -869,42 +899,45 @@ abstract class SettingsStoreBase with Store {
actionlistDisplayMode: actionListDisplayMode, actionlistDisplayMode: actionListDisplayMode,
initialPinLength: pinLength, initialPinLength: pinLength,
pinTimeOutDuration: pinCodeTimeOutDuration, pinTimeOutDuration: pinCodeTimeOutDuration,
seedPhraseLength: seedPhraseWordCount,initialLanguageCode: savedLanguageCode, seedPhraseLength: seedPhraseWordCount,
initialLanguageCode: savedLanguageCode,
sortBalanceBy: sortBalanceBy, sortBalanceBy: sortBalanceBy,
pinNativeTokenAtTop: pinNativeTokenAtTop, pinNativeTokenAtTop: pinNativeTokenAtTop,
useEtherscan: useEtherscan, useEtherscan: useEtherscan,
usePolygonScan: usePolygonScan,
defaultNanoRep: defaultNanoRep, defaultNanoRep: defaultNanoRep,
defaultBananoRep: defaultBananoRep, defaultBananoRep: defaultBananoRep,
lookupsTwitter: lookupsTwitter, lookupsTwitter: lookupsTwitter,
lookupsMastodon: lookupsMastodon, lookupsMastodon: lookupsMastodon,
lookupsYatService: lookupsYatService, lookupsYatService: lookupsYatService,
lookupsUnstoppableDomains: lookupsUnstoppableDomains, lookupsUnstoppableDomains: lookupsUnstoppableDomains,
lookupsOpenAlias: lookupsOpenAlias, lookupsOpenAlias: lookupsOpenAlias,
lookupsENS: lookupsENS, lookupsENS: lookupsENS,
initialMoneroTransactionPriority: moneroTransactionPriority, initialMoneroTransactionPriority: moneroTransactionPriority,
initialBitcoinTransactionPriority: bitcoinTransactionPriority, initialBitcoinTransactionPriority: bitcoinTransactionPriority,
initialHavenTransactionPriority: havenTransactionPriority, initialHavenTransactionPriority: havenTransactionPriority,
initialLitecoinTransactionPriority: litecoinTransactionPriority, initialLitecoinTransactionPriority: litecoinTransactionPriority,
initialBitcoinCashTransactionPriority: bitcoinCashTransactionPriority, initialBitcoinCashTransactionPriority: bitcoinCashTransactionPriority,
initialShouldRequireTOTP2FAForAccessingWallet: shouldRequireTOTP2FAForAccessingWallet, initialShouldRequireTOTP2FAForAccessingWallet: shouldRequireTOTP2FAForAccessingWallet,
initialShouldRequireTOTP2FAForSendsToContact: shouldRequireTOTP2FAForSendsToContact, initialShouldRequireTOTP2FAForSendsToContact: shouldRequireTOTP2FAForSendsToContact,
initialShouldRequireTOTP2FAForSendsToNonContact: shouldRequireTOTP2FAForSendsToNonContact, initialShouldRequireTOTP2FAForSendsToNonContact: shouldRequireTOTP2FAForSendsToNonContact,
initialShouldRequireTOTP2FAForSendsToInternalWallets: initialShouldRequireTOTP2FAForSendsToInternalWallets:
shouldRequireTOTP2FAForSendsToInternalWallets, shouldRequireTOTP2FAForSendsToInternalWallets,
initialShouldRequireTOTP2FAForExchangesToInternalWallets: initialShouldRequireTOTP2FAForExchangesToInternalWallets:
shouldRequireTOTP2FAForExchangesToInternalWallets, shouldRequireTOTP2FAForExchangesToInternalWallets,
initialShouldRequireTOTP2FAForExchangesToExternalWallets: initialShouldRequireTOTP2FAForExchangesToExternalWallets:
shouldRequireTOTP2FAForExchangesToExternalWallets, shouldRequireTOTP2FAForExchangesToExternalWallets,
initialShouldRequireTOTP2FAForAddingContacts: shouldRequireTOTP2FAForAddingContacts, initialShouldRequireTOTP2FAForAddingContacts: shouldRequireTOTP2FAForAddingContacts,
initialShouldRequireTOTP2FAForCreatingNewWallets: shouldRequireTOTP2FAForCreatingNewWallets, initialShouldRequireTOTP2FAForCreatingNewWallets: shouldRequireTOTP2FAForCreatingNewWallets,
initialShouldRequireTOTP2FAForAllSecurityAndBackupSettings: initialShouldRequireTOTP2FAForAllSecurityAndBackupSettings:
shouldRequireTOTP2FAForAllSecurityAndBackupSettings, shouldRequireTOTP2FAForAllSecurityAndBackupSettings,
initialEthereumTransactionPriority: ethereumTransactionPriority, initialEthereumTransactionPriority: ethereumTransactionPriority,
backgroundTasks: backgroundTasks, initialPolygonTransactionPriority: polygonTransactionPriority,
initialSyncMode: savedSyncMode, backgroundTasks: backgroundTasks,
initialSyncAll: savedSyncAll, initialSyncMode: savedSyncMode,
shouldShowYatPopup: shouldShowYatPopup); initialSyncAll: savedSyncAll,
} shouldShowYatPopup: shouldShowYatPopup);
}
Future<void> reload({required Box<Node> nodeSource}) async { Future<void> reload({required Box<Node> nodeSource}) async {
final sharedPreferences = await getIt.getAsync<SharedPreferences>(); final sharedPreferences = await getIt.getAsync<SharedPreferences>();
@ -934,6 +967,11 @@ abstract class SettingsStoreBase with Store {
sharedPreferences.getInt(PreferencesKey.ethereumTransactionPriority)!) ?? sharedPreferences.getInt(PreferencesKey.ethereumTransactionPriority)!) ??
priority[WalletType.ethereum]!; priority[WalletType.ethereum]!;
} }
if (sharedPreferences.getInt(PreferencesKey.polygonTransactionPriority) != null) {
priority[WalletType.polygon] = polygon?.deserializePolygonTransactionPriority(
sharedPreferences.getInt(PreferencesKey.polygonTransactionPriority)!) ??
priority[WalletType.polygon]!;
}
if (sharedPreferences.getInt(PreferencesKey.bitcoinCashTransactionPriority) != null) { if (sharedPreferences.getInt(PreferencesKey.bitcoinCashTransactionPriority) != null) {
priority[WalletType.bitcoinCash] = bitcoinCash?.deserializeBitcoinCashTransactionPriority( priority[WalletType.bitcoinCash] = bitcoinCash?.deserializeBitcoinCashTransactionPriority(
sharedPreferences.getInt(PreferencesKey.bitcoinCashTransactionPriority)!) ?? sharedPreferences.getInt(PreferencesKey.bitcoinCashTransactionPriority)!) ??
@ -1027,12 +1065,14 @@ abstract class SettingsStoreBase with Store {
.values[sharedPreferences.getInt(PreferencesKey.sortBalanceBy) ?? sortBalanceBy.index]; .values[sharedPreferences.getInt(PreferencesKey.sortBalanceBy) ?? sortBalanceBy.index];
pinNativeTokenAtTop = sharedPreferences.getBool(PreferencesKey.pinNativeTokenAtTop) ?? true; pinNativeTokenAtTop = sharedPreferences.getBool(PreferencesKey.pinNativeTokenAtTop) ?? true;
useEtherscan = sharedPreferences.getBool(PreferencesKey.useEtherscan) ?? true; useEtherscan = sharedPreferences.getBool(PreferencesKey.useEtherscan) ?? true;
usePolygonScan = sharedPreferences.getBool(PreferencesKey.usePolygonScan) ?? true;
defaultNanoRep = sharedPreferences.getString(PreferencesKey.defaultNanoRep) ?? ""; defaultNanoRep = sharedPreferences.getString(PreferencesKey.defaultNanoRep) ?? "";
defaultBananoRep = sharedPreferences.getString(PreferencesKey.defaultBananoRep) ?? ""; defaultBananoRep = sharedPreferences.getString(PreferencesKey.defaultBananoRep) ?? "";
lookupsTwitter = sharedPreferences.getBool(PreferencesKey.lookupsTwitter) ?? true; lookupsTwitter = sharedPreferences.getBool(PreferencesKey.lookupsTwitter) ?? true;
lookupsMastodon = sharedPreferences.getBool(PreferencesKey.lookupsMastodon) ?? true; lookupsMastodon = sharedPreferences.getBool(PreferencesKey.lookupsMastodon) ?? true;
lookupsYatService = sharedPreferences.getBool(PreferencesKey.lookupsYatService) ?? true; lookupsYatService = sharedPreferences.getBool(PreferencesKey.lookupsYatService) ?? true;
lookupsUnstoppableDomains = sharedPreferences.getBool(PreferencesKey.lookupsUnstoppableDomains) ?? true; lookupsUnstoppableDomains =
sharedPreferences.getBool(PreferencesKey.lookupsUnstoppableDomains) ?? true;
lookupsOpenAlias = sharedPreferences.getBool(PreferencesKey.lookupsOpenAlias) ?? true; lookupsOpenAlias = sharedPreferences.getBool(PreferencesKey.lookupsOpenAlias) ?? true;
lookupsENS = sharedPreferences.getBool(PreferencesKey.lookupsENS) ?? true; lookupsENS = sharedPreferences.getBool(PreferencesKey.lookupsENS) ?? true;
@ -1045,6 +1085,7 @@ abstract class SettingsStoreBase with Store {
sharedPreferences.getInt(PreferencesKey.currentBitcoinCashNodeIdKey); sharedPreferences.getInt(PreferencesKey.currentBitcoinCashNodeIdKey);
final havenNodeId = sharedPreferences.getInt(PreferencesKey.currentHavenNodeIdKey); final havenNodeId = sharedPreferences.getInt(PreferencesKey.currentHavenNodeIdKey);
final ethereumNodeId = sharedPreferences.getInt(PreferencesKey.currentEthereumNodeIdKey); final ethereumNodeId = sharedPreferences.getInt(PreferencesKey.currentEthereumNodeIdKey);
final polygonNodeId = sharedPreferences.getInt(PreferencesKey.currentPolygonNodeIdKey);
final nanoNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoNodeIdKey); final nanoNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoNodeIdKey);
final nanoPowNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoNodeIdKey); final nanoPowNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoNodeIdKey);
final moneroNode = nodeSource.get(nodeId); final moneroNode = nodeSource.get(nodeId);
@ -1052,6 +1093,7 @@ abstract class SettingsStoreBase with Store {
final litecoinElectrumServer = nodeSource.get(litecoinElectrumServerId); final litecoinElectrumServer = nodeSource.get(litecoinElectrumServerId);
final havenNode = nodeSource.get(havenNodeId); final havenNode = nodeSource.get(havenNodeId);
final ethereumNode = nodeSource.get(ethereumNodeId); final ethereumNode = nodeSource.get(ethereumNodeId);
final polygonNode = nodeSource.get(polygonNodeId);
final bitcoinCashNode = nodeSource.get(bitcoinCashElectrumServerId); final bitcoinCashNode = nodeSource.get(bitcoinCashElectrumServerId);
final nanoNode = nodeSource.get(nanoNodeId); final nanoNode = nodeSource.get(nanoNodeId);
@ -1075,6 +1117,10 @@ abstract class SettingsStoreBase with Store {
nodes[WalletType.ethereum] = ethereumNode; nodes[WalletType.ethereum] = ethereumNode;
} }
if (polygonNode != null) {
nodes[WalletType.polygon] = polygonNode;
}
if (bitcoinCashNode != null) { if (bitcoinCashNode != null) {
nodes[WalletType.bitcoinCash] = bitcoinCashNode; nodes[WalletType.bitcoinCash] = bitcoinCashNode;
} }
@ -1110,6 +1156,9 @@ abstract class SettingsStoreBase with Store {
case WalletType.nano: case WalletType.nano:
await _sharedPreferences.setInt(PreferencesKey.currentNanoNodeIdKey, node.key as int); await _sharedPreferences.setInt(PreferencesKey.currentNanoNodeIdKey, node.key as int);
break; break;
case WalletType.polygon:
await _sharedPreferences.setInt(PreferencesKey.currentPolygonNodeIdKey, node.key as int);
break;
default: default:
break; break;
} }
@ -1141,7 +1190,6 @@ abstract class SettingsStoreBase with Store {
trocadorProviderStates[providerName] = state; trocadorProviderStates[providerName] = state;
} }
static Future<String?> _getDeviceName() async { static Future<String?> _getDeviceName() async {
String? deviceName = ''; String? deviceName = '';
final deviceInfoPlugin = DeviceInfoPlugin(); final deviceInfoPlugin = DeviceInfoPlugin();

View file

@ -1,5 +1,6 @@
import 'package:cake_wallet/entities/fiat_api_mode.dart'; import 'package:cake_wallet/entities/fiat_api_mode.dart';
import 'package:cake_wallet/entities/sort_balance_types.dart'; import 'package:cake_wallet/entities/sort_balance_types.dart';
import 'package:cake_wallet/reactions/wallet_connect.dart';
import 'package:cw_core/transaction_history.dart'; import 'package:cw_core/transaction_history.dart';
import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/balance.dart'; import 'package:cw_core/balance.dart';
@ -81,7 +82,7 @@ abstract class BalanceViewModelBase with Store {
bool get isFiatDisabled => settingsStore.fiatApiMode == FiatApiMode.disabled; bool get isFiatDisabled => settingsStore.fiatApiMode == FiatApiMode.disabled;
@computed @computed
bool get isHomeScreenSettingsEnabled => wallet.type == WalletType.ethereum; bool get isHomeScreenSettingsEnabled => isEVMCompatibleChain(wallet.type);
@computed @computed
bool get hasAccounts => wallet.type == WalletType.monero; bool get hasAccounts => wallet.type == WalletType.monero;
@ -123,6 +124,7 @@ abstract class BalanceViewModelBase with Store {
case WalletType.monero: case WalletType.monero:
case WalletType.haven: case WalletType.haven:
case WalletType.ethereum: case WalletType.ethereum:
case WalletType.polygon:
return S.current.xmr_available_balance; return S.current.xmr_available_balance;
default: default:
return S.current.confirmed; return S.current.confirmed;
@ -135,6 +137,7 @@ abstract class BalanceViewModelBase with Store {
case WalletType.monero: case WalletType.monero:
case WalletType.haven: case WalletType.haven:
case WalletType.ethereum: case WalletType.ethereum:
case WalletType.polygon:
return S.current.xmr_full_balance; return S.current.xmr_full_balance;
default: default:
return S.current.unconfirmed; return S.current.unconfirmed;
@ -272,7 +275,8 @@ abstract class BalanceViewModelBase with Store {
} }
@computed @computed
bool get hasAdditionalBalance => wallet.type != WalletType.ethereum; bool get hasAdditionalBalance => !isEVMCompatibleChain(wallet.type);
@computed @computed
List<BalanceRecord> get formattedBalances { List<BalanceRecord> get formattedBalances {

View file

@ -2,10 +2,12 @@ import 'package:cake_wallet/core/fiat_conversion_service.dart';
import 'package:cake_wallet/entities/fiat_api_mode.dart'; import 'package:cake_wallet/entities/fiat_api_mode.dart';
import 'package:cake_wallet/entities/sort_balance_types.dart'; import 'package:cake_wallet/entities/sort_balance_types.dart';
import 'package:cake_wallet/ethereum/ethereum.dart'; import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:cake_wallet/polygon/polygon.dart';
import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart';
import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/erc20_token.dart'; import 'package:cw_core/erc20_token.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
part 'home_settings_view_model.g.dart'; part 'home_settings_view_model.g.dart';
@ -42,18 +44,41 @@ abstract class HomeSettingsViewModelBase with Store {
void setPinNativeToken(bool value) => _settingsStore.pinNativeTokenAtTop = value; void setPinNativeToken(bool value) => _settingsStore.pinNativeTokenAtTop = value;
Future<void> addErc20Token(Erc20Token token) async { Future<void> addErc20Token(Erc20Token token) async {
await ethereum!.addErc20Token(_balanceViewModel.wallet, token); if (_balanceViewModel.wallet.type == WalletType.ethereum) {
await ethereum!.addErc20Token(_balanceViewModel.wallet, token);
}
if (_balanceViewModel.wallet.type == WalletType.polygon) {
await polygon!.addErc20Token(_balanceViewModel.wallet, token);
}
_updateTokensList(); _updateTokensList();
_updateFiatPrices(token); _updateFiatPrices(token);
} }
Future<void> deleteErc20Token(Erc20Token token) async { Future<void> deleteErc20Token(Erc20Token token) async {
await ethereum!.deleteErc20Token(_balanceViewModel.wallet, token); if (_balanceViewModel.wallet.type == WalletType.ethereum) {
await ethereum!.deleteErc20Token(_balanceViewModel.wallet, token);
}
if (_balanceViewModel.wallet.type == WalletType.polygon) {
await polygon!.deleteErc20Token(_balanceViewModel.wallet, token);
}
_updateTokensList(); _updateTokensList();
} }
Future<Erc20Token?> getErc20Token(String contractAddress) async => Future<Erc20Token?> getErc20Token(String contractAddress) async {
await ethereum!.getErc20Token(_balanceViewModel.wallet, contractAddress); if (_balanceViewModel.wallet.type == WalletType.ethereum) {
return await ethereum!.getErc20Token(_balanceViewModel.wallet, contractAddress);
}
if (_balanceViewModel.wallet.type == WalletType.polygon) {
return await polygon!.getErc20Token(_balanceViewModel.wallet, contractAddress);
}
return null;
}
CryptoCurrency get nativeToken => _balanceViewModel.wallet.currency; CryptoCurrency get nativeToken => _balanceViewModel.wallet.currency;
@ -69,7 +94,12 @@ abstract class HomeSettingsViewModelBase with Store {
void changeTokenAvailability(Erc20Token token, bool value) async { void changeTokenAvailability(Erc20Token token, bool value) async {
token.enabled = value; token.enabled = value;
ethereum!.addErc20Token(_balanceViewModel.wallet, token); if (_balanceViewModel.wallet.type == WalletType.ethereum) {
ethereum!.addErc20Token(_balanceViewModel.wallet, token);
}
if (_balanceViewModel.wallet.type == WalletType.polygon) {
polygon!.addErc20Token(_balanceViewModel.wallet, token);
}
_refreshTokensList(); _refreshTokensList();
} }
@ -83,7 +113,8 @@ abstract class HomeSettingsViewModelBase with Store {
return -1; return -1;
} else if (e2.enabled && !e1.enabled) { } else if (e2.enabled && !e1.enabled) {
return 1; return 1;
} else if (!e1.enabled && !e2.enabled) { // if both are disabled then sort alphabetically } else if (!e1.enabled && !e2.enabled) {
// if both are disabled then sort alphabetically
return e1.name.compareTo(e2.name); return e1.name.compareTo(e2.name);
} }
@ -92,11 +123,21 @@ abstract class HomeSettingsViewModelBase with Store {
tokens.clear(); tokens.clear();
tokens.addAll(ethereum! if (_balanceViewModel.wallet.type == WalletType.ethereum) {
.getERC20Currencies(_balanceViewModel.wallet) tokens.addAll(ethereum!
.where((element) => _matchesSearchText(element)) .getERC20Currencies(_balanceViewModel.wallet)
.toList() .where((element) => _matchesSearchText(element))
..sort(_sortFunc)); .toList()
..sort(_sortFunc));
}
if (_balanceViewModel.wallet.type == WalletType.polygon) {
tokens.addAll(polygon!
.getERC20Currencies(_balanceViewModel.wallet)
.where((element) => _matchesSearchText(element))
.toList()
..sort(_sortFunc));
}
} }
@action @action

View file

@ -1,10 +1,9 @@
// ignore_for_file: public_member_api_docs, sort_constructors_first
import 'dart:convert'; import 'dart:convert';
import 'dart:developer'; import 'dart:developer';
import 'package:cake_wallet/core/wallet_connect/wc_bottom_sheet_service.dart'; import 'package:cake_wallet/core/wallet_connect/wc_bottom_sheet_service.dart';
import 'package:cake_wallet/reactions/wallet_connect.dart';
import 'package:cake_wallet/src/screens/wallet_connect/widgets/message_display_widget.dart'; import 'package:cake_wallet/src/screens/wallet_connect/widgets/message_display_widget.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
import 'package:cake_wallet/.secrets.g.dart' as secrets; import 'package:cake_wallet/.secrets.g.dart' as secrets;
@ -39,23 +38,26 @@ abstract class NFTViewModelBase with Store {
@action @action
Future<void> getNFTAssetByWallet() async { Future<void> getNFTAssetByWallet() async {
if (appStore.wallet!.type != WalletType.ethereum) return; if (!isEVMCompatibleChain(appStore.wallet!.type)) return;
final walletAddress = appStore.wallet!.walletInfo.address; final walletAddress = appStore.wallet!.walletInfo.address;
log('Fetching wallet NFTs for $walletAddress'); log('Fetching wallet NFTs for $walletAddress');
final chainName = getChainNameBasedOnWalletType(appStore.wallet!.type);
// the [chain] refers to the chain network that the nft is on // the [chain] refers to the chain network that the nft is on
// the [format] refers to the number format type of the responses // the [format] refers to the number format type of the responses
// the [normalizedMetadata] field is a boolean that determines if // the [normalizedMetadata] field is a boolean that determines if
// the response would include a json string of the NFT Metadata that can be decoded // the response would include a json string of the NFT Metadata that can be decoded
// and used within the wallet // and used within the wallet
// the [excludeSpam] field is a boolean that determines if spam nfts be excluded from the response.
final uri = Uri.https( final uri = Uri.https(
'deep-index.moralis.io', 'deep-index.moralis.io',
'/api/v2.2/$walletAddress/nft', '/api/v2.2/$walletAddress/nft',
{ {
"chain": "eth", "chain": chainName,
"format": "decimal", "format": "decimal",
"media_items": "false", "media_items": "false",
"exclude_spam": "true",
"normalizeMetadata": "true", "normalizeMetadata": "true",
}, },
); );
@ -94,7 +96,7 @@ abstract class NFTViewModelBase with Store {
@action @action
Future<void> importNFT(String tokenAddress, String tokenId) async { Future<void> importNFT(String tokenAddress, String tokenId) async {
final chainName = getChainNameBasedOnWalletType(appStore.wallet!.type);
// the [chain] refers to the chain network that the nft is on // the [chain] refers to the chain network that the nft is on
// the [format] refers to the number format type of the responses // the [format] refers to the number format type of the responses
// the [normalizedMetadata] field is a boolean that determines if // the [normalizedMetadata] field is a boolean that determines if
@ -104,7 +106,7 @@ abstract class NFTViewModelBase with Store {
'deep-index.moralis.io', 'deep-index.moralis.io',
'/api/v2.2/nft/$tokenAddress/$tokenId', '/api/v2.2/nft/$tokenAddress/$tokenId',
{ {
"chain": "eth", "chain": chainName,
"format": "decimal", "format": "decimal",
"media_items": "false", "media_items": "false",
"normalizeMetadata": "true", "normalizeMetadata": "true",

View file

@ -3,6 +3,7 @@ import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/ethereum/ethereum.dart'; import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/nano/nano.dart'; import 'package:cake_wallet/nano/nano.dart';
import 'package:cake_wallet/polygon/polygon.dart';
import 'package:cw_core/transaction_direction.dart'; import 'package:cw_core/transaction_direction.dart';
import 'package:cw_core/transaction_info.dart'; import 'package:cw_core/transaction_info.dart';
import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/store/settings_store.dart';
@ -91,6 +92,13 @@ class TransactionListItem extends ActionListItem with Keyable {
cryptoAmount: ethereum!.formatterEthereumAmountToDouble(transaction: transaction), cryptoAmount: ethereum!.formatterEthereumAmountToDouble(transaction: transaction),
price: price); price: price);
break; break;
case WalletType.polygon:
final asset = polygon!.assetOfTransaction(balanceViewModel.wallet, transaction);
final price = balanceViewModel.fiatConvertationStore.prices[asset];
amount = calculateFiatAmountRaw(
cryptoAmount: polygon!.formatterPolygonAmountToDouble(transaction: transaction),
price: price);
break;
case WalletType.nano: case WalletType.nano:
amount = calculateFiatAmountRaw( amount = calculateFiatAmountRaw(
cryptoAmount: double.parse(nanoUtil!.getRawAsDecimalString( cryptoAmount: double.parse(nanoUtil!.getRawAsDecimalString(

View file

@ -28,10 +28,7 @@ abstract class ExchangeTradeViewModelBase with Store {
required this.tradesStore, required this.tradesStore,
required this.sendViewModel}) required this.sendViewModel})
: trade = tradesStore.trade!, : trade = tradesStore.trade!,
isSendable = tradesStore.trade!.from == wallet.currency || isSendable = _checkIfCanSend(tradesStore, wallet),
tradesStore.trade!.provider == ExchangeProviderDescription.xmrto ||
(wallet.currency == CryptoCurrency.eth &&
tradesStore.trade!.from.tag == CryptoCurrency.eth.title),
items = ObservableList<ExchangeTradeItem>() { items = ObservableList<ExchangeTradeItem>() {
switch (trade.provider) { switch (trade.provider) {
case ExchangeProviderDescription.changeNow: case ExchangeProviderDescription.changeNow:
@ -155,4 +152,19 @@ abstract class ExchangeTradeViewModelBase with Store {
isCopied: true), isCopied: true),
]); ]);
} }
static bool _checkIfCanSend(TradesStore tradesStore, WalletBase wallet) {
bool _isEthToken() =>
wallet.currency == CryptoCurrency.eth &&
tradesStore.trade!.from.tag == CryptoCurrency.eth.title;
bool _isPolygonToken() =>
wallet.currency == CryptoCurrency.maticpoly &&
tradesStore.trade!.from.tag == CryptoCurrency.maticpoly.tag;
return tradesStore.trade!.from == wallet.currency ||
tradesStore.trade!.provider == ExchangeProviderDescription.xmrto ||
_isEthToken() ||
_isPolygonToken();
}
} }

View file

@ -23,6 +23,7 @@ import 'package:cake_wallet/exchange/trade.dart';
import 'package:cake_wallet/exchange/trade_request.dart'; import 'package:cake_wallet/exchange/trade_request.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/monero/monero.dart'; import 'package:cake_wallet/monero/monero.dart';
import 'package:cake_wallet/polygon/polygon.dart';
import 'package:cake_wallet/store/app_store.dart'; import 'package:cake_wallet/store/app_store.dart';
import 'package:cake_wallet/store/dashboard/trades_store.dart'; import 'package:cake_wallet/store/dashboard/trades_store.dart';
import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/store/settings_store.dart';
@ -287,6 +288,8 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
return transactionPriority == ethereum!.getEthereumTransactionPrioritySlow(); return transactionPriority == ethereum!.getEthereumTransactionPrioritySlow();
case WalletType.bitcoinCash: case WalletType.bitcoinCash:
return transactionPriority == bitcoinCash!.getBitcoinCashTransactionPrioritySlow(); return transactionPriority == bitcoinCash!.getBitcoinCashTransactionPrioritySlow();
case WalletType.polygon:
return transactionPriority == polygon!.getPolygonTransactionPrioritySlow();
default: default:
return false; return false;
} }
@ -626,6 +629,10 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
depositCurrency = CryptoCurrency.nano; depositCurrency = CryptoCurrency.nano;
receiveCurrency = CryptoCurrency.xmr; receiveCurrency = CryptoCurrency.xmr;
break; break;
case WalletType.polygon:
depositCurrency = CryptoCurrency.maticpoly;
receiveCurrency = CryptoCurrency.xmr;
break;
default: default:
break; break;
} }
@ -713,6 +720,9 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
case WalletType.bitcoinCash: case WalletType.bitcoinCash:
_settingsStore.priority[wallet.type] = bitcoinCash!.getDefaultTransactionPriority(); _settingsStore.priority[wallet.type] = bitcoinCash!.getDefaultTransactionPriority();
break; break;
case WalletType.polygon:
_settingsStore.priority[wallet.type] = polygon!.getDefaultTransactionPriority();
break;
default: default:
break; break;
} }

View file

@ -72,6 +72,9 @@ abstract class NodeListViewModelBase with Store {
case WalletType.nano: case WalletType.nano:
node = getNanoDefaultNode(nodes: _nodeSource)!; node = getNanoDefaultNode(nodes: _nodeSource)!;
break; break;
case WalletType.polygon:
node = getPolygonDefaultNode(nodes: _nodeSource)!;
break;
default: default:
throw Exception('Unexpected wallet type: ${_appStore.wallet!.type}'); throw Exception('Unexpected wallet type: ${_appStore.wallet!.type}');
} }

View file

@ -2,6 +2,7 @@ import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart'; import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
import 'package:cake_wallet/ethereum/ethereum.dart'; import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:cake_wallet/nano/nano.dart'; import 'package:cake_wallet/nano/nano.dart';
import 'package:cake_wallet/polygon/polygon.dart';
import 'package:cake_wallet/view_model/restore/restore_mode.dart'; import 'package:cake_wallet/view_model/restore/restore_mode.dart';
import 'package:cake_wallet/view_model/restore/restore_wallet.dart'; import 'package:cake_wallet/view_model/restore/restore_wallet.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
@ -71,6 +72,9 @@ abstract class WalletRestorationFromQRVMBase extends WalletCreationVM with Store
case WalletType.ethereum: case WalletType.ethereum:
return ethereum!.createEthereumRestoreWalletFromPrivateKey( return ethereum!.createEthereumRestoreWalletFromPrivateKey(
name: name, password: password, privateKey: restoreWallet.privateKey!); name: name, password: password, privateKey: restoreWallet.privateKey!);
case WalletType.polygon:
return polygon!.createPolygonRestoreWalletFromPrivateKey(
name: name, password: password, privateKey: restoreWallet.privateKey!);
default: default:
throw Exception('Unexpected type: ${restoreWallet.type.toString()}'); throw Exception('Unexpected type: ${restoreWallet.type.toString()}');
} }
@ -95,6 +99,9 @@ abstract class WalletRestorationFromQRVMBase extends WalletCreationVM with Store
case WalletType.nano: case WalletType.nano:
return nano!.createNanoRestoreWalletFromSeedCredentials( return nano!.createNanoRestoreWalletFromSeedCredentials(
name: name, mnemonic: restoreWallet.mnemonicSeed ?? '', password: password); name: name, mnemonic: restoreWallet.mnemonicSeed ?? '', password: password);
case WalletType.polygon:
return polygon!.createPolygonRestoreWalletFromSeedCredentials(
name: name, mnemonic: restoreWallet.mnemonicSeed ?? '', password: password);
default: default:
throw Exception('Unexpected type: ${type.toString()}'); throw Exception('Unexpected type: ${type.toString()}');
} }

View file

@ -26,6 +26,7 @@ class WalletRestoreFromQRCode {
'litecoin-wallet': WalletType.litecoin, 'litecoin-wallet': WalletType.litecoin,
'litecoin_wallet': WalletType.litecoin, 'litecoin_wallet': WalletType.litecoin,
'ethereum-wallet': WalletType.ethereum, 'ethereum-wallet': WalletType.ethereum,
'polygon-wallet': WalletType.polygon,
'nano-wallet': WalletType.nano, 'nano-wallet': WalletType.nano,
'nano_wallet': WalletType.nano, 'nano_wallet': WalletType.nano,
'bitcoincash': WalletType.bitcoinCash, 'bitcoincash': WalletType.bitcoinCash,
@ -157,7 +158,16 @@ class WalletRestoreFromQRCode {
return WalletRestoreMode.keys; return WalletRestoreMode.keys;
} }
if ((type == WalletType.nano || type == WalletType.banano) && credentials.containsKey('hexSeed')) { if (type == WalletType.polygon && credentials.containsKey('private_key')) {
final privateKey = credentials['private_key'] as String;
if (privateKey.isEmpty) {
throw Exception('Unexpected restore mode: private_key');
}
return WalletRestoreMode.keys;
}
if ((type == WalletType.nano || type == WalletType.banano) &&
credentials.containsKey('hexSeed')) {
final hexSeed = credentials['hexSeed'] as String; final hexSeed = credentials['hexSeed'] as String;
if (hexSeed.isEmpty) { if (hexSeed.isEmpty) {
throw Exception('Unexpected restore mode: hexSeed'); throw Exception('Unexpected restore mode: hexSeed');

View file

@ -4,6 +4,8 @@ import 'package:cake_wallet/entities/parse_address_from_domain.dart';
import 'package:cake_wallet/entities/parsed_address.dart'; import 'package:cake_wallet/entities/parsed_address.dart';
import 'package:cake_wallet/ethereum/ethereum.dart'; import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:cake_wallet/haven/haven.dart'; import 'package:cake_wallet/haven/haven.dart';
import 'package:cake_wallet/polygon/polygon.dart';
import 'package:cake_wallet/reactions/wallet_connect.dart';
import 'package:cake_wallet/src/screens/send/widgets/extract_address_from_parsed.dart'; import 'package:cake_wallet/src/screens/send/widgets/extract_address_from_parsed.dart';
import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/crypto_currency.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -27,7 +29,8 @@ const String cryptoNumberPattern = '0.0';
class Output = OutputBase with _$Output; class Output = OutputBase with _$Output;
abstract class OutputBase with Store { abstract class OutputBase with Store {
OutputBase(this._wallet, this._settingsStore, this._fiatConversationStore, this.cryptoCurrencyHandler) OutputBase(
this._wallet, this._settingsStore, this._fiatConversationStore, this.cryptoCurrencyHandler)
: _cryptoNumberFormat = NumberFormat(cryptoNumberPattern), : _cryptoNumberFormat = NumberFormat(cryptoNumberPattern),
key = UniqueKey(), key = UniqueKey(),
sendAll = false, sendAll = false,
@ -65,8 +68,7 @@ abstract class OutputBase with Store {
@computed @computed
bool get isParsedAddress => bool get isParsedAddress =>
parsedAddress.parseFrom != ParseFrom.notParsed && parsedAddress.parseFrom != ParseFrom.notParsed && parsedAddress.name.isNotEmpty;
parsedAddress.name.isNotEmpty;
@computed @computed
int get formattedCryptoAmount { int get formattedCryptoAmount {
@ -83,8 +85,7 @@ abstract class OutputBase with Store {
case WalletType.bitcoin: case WalletType.bitcoin:
case WalletType.litecoin: case WalletType.litecoin:
case WalletType.bitcoinCash: case WalletType.bitcoinCash:
_amount = _amount = bitcoin!.formatterStringDoubleToBitcoinAmount(_cryptoAmount);
bitcoin!.formatterStringDoubleToBitcoinAmount(_cryptoAmount);
break; break;
case WalletType.haven: case WalletType.haven:
_amount = haven!.formatterMoneroParseAmount(amount: _cryptoAmount); _amount = haven!.formatterMoneroParseAmount(amount: _cryptoAmount);
@ -92,6 +93,9 @@ abstract class OutputBase with Store {
case WalletType.ethereum: case WalletType.ethereum:
_amount = ethereum!.formatterEthereumParseAmount(_cryptoAmount); _amount = ethereum!.formatterEthereumParseAmount(_cryptoAmount);
break; break;
case WalletType.polygon:
_amount = polygon!.formatterPolygonParseAmount(_cryptoAmount);
break;
default: default:
break; break;
} }
@ -130,6 +134,10 @@ abstract class OutputBase with Store {
if (_wallet.type == WalletType.ethereum) { if (_wallet.type == WalletType.ethereum) {
return ethereum!.formatterEthereumAmountToDouble(amount: BigInt.from(fee)); return ethereum!.formatterEthereumAmountToDouble(amount: BigInt.from(fee));
} }
if (_wallet.type == WalletType.polygon) {
return polygon!.formatterPolygonAmountToDouble(amount: BigInt.from(fee));
}
} catch (e) { } catch (e) {
print(e.toString()); print(e.toString());
} }
@ -140,10 +148,11 @@ abstract class OutputBase with Store {
@computed @computed
String get estimatedFeeFiatAmount { String get estimatedFeeFiatAmount {
try { try {
final currency = _wallet.type == WalletType.ethereum ? _wallet.currency : cryptoCurrencyHandler(); final currency = isEVMCompatibleChain(_wallet.type)
? _wallet.currency
: cryptoCurrencyHandler();
final fiat = calculateFiatAmountRaw( final fiat = calculateFiatAmountRaw(
price: _fiatConversationStore.prices[currency]!, price: _fiatConversationStore.prices[currency]!, cryptoAmount: estimatedFee);
cryptoAmount: estimatedFee);
return fiat; return fiat;
} catch (_) { } catch (_) {
return '0.00'; return '0.00';
@ -240,6 +249,7 @@ abstract class OutputBase with Store {
maximumFractionDigits = 12; maximumFractionDigits = 12;
break; break;
case WalletType.ethereum: case WalletType.ethereum:
case WalletType.polygon:
maximumFractionDigits = 12; maximumFractionDigits = 12;
break; break;
default: default:

View file

@ -50,7 +50,9 @@ abstract class SendTemplateViewModelBase with Store {
TemplateValidator get templateValidator => TemplateValidator(); TemplateValidator get templateValidator => TemplateValidator();
bool get hasMultiRecipient => bool get hasMultiRecipient =>
_wallet.type != WalletType.haven && _wallet.type != WalletType.ethereum; _wallet.type != WalletType.haven &&
_wallet.type != WalletType.ethereum &&
_wallet.type != WalletType.polygon;
@computed @computed
CryptoCurrency get cryptoCurrency => _wallet.currency; CryptoCurrency get cryptoCurrency => _wallet.currency;

View file

@ -5,6 +5,8 @@ import 'package:cake_wallet/nano/nano.dart';
import 'package:cake_wallet/core/wallet_change_listener_view_model.dart'; import 'package:cake_wallet/core/wallet_change_listener_view_model.dart';
import 'package:cake_wallet/entities/contact_record.dart'; import 'package:cake_wallet/entities/contact_record.dart';
import 'package:cake_wallet/entities/wallet_contact.dart'; import 'package:cake_wallet/entities/wallet_contact.dart';
import 'package:cake_wallet/polygon/polygon.dart';
import 'package:cake_wallet/reactions/wallet_connect.dart';
import 'package:cake_wallet/store/app_store.dart'; import 'package:cake_wallet/store/app_store.dart';
import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart'; import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart';
@ -42,7 +44,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
void onWalletChange(wallet) { void onWalletChange(wallet) {
currencies = wallet.balance.keys.toList(); currencies = wallet.balance.keys.toList();
selectedCryptoCurrency = wallet.currency; selectedCryptoCurrency = wallet.currency;
hasMultipleTokens = wallet.type == WalletType.ethereum; hasMultipleTokens = isEVMCompatibleChain(wallet.type);
} }
SendViewModelBase( SendViewModelBase(
@ -55,7 +57,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
) : state = InitialExecutionState(), ) : state = InitialExecutionState(),
currencies = appStore.wallet!.balance.keys.toList(), currencies = appStore.wallet!.balance.keys.toList(),
selectedCryptoCurrency = appStore.wallet!.currency, selectedCryptoCurrency = appStore.wallet!.currency,
hasMultipleTokens = appStore.wallet!.type == WalletType.ethereum, hasMultipleTokens = isEVMCompatibleChain(appStore.wallet!.type),
outputs = ObservableList<Output>(), outputs = ObservableList<Output>(),
_settingsStore = appStore.settingsStore, _settingsStore = appStore.settingsStore,
fiatFromSettings = appStore.settingsStore.fiatCurrency, fiatFromSettings = appStore.settingsStore.fiatCurrency,
@ -119,7 +121,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
try { try {
if (pendingTransaction != null) { if (pendingTransaction != null) {
final currency = final currency =
walletType == WalletType.ethereum ? wallet.currency : selectedCryptoCurrency; isEVMCompatibleChain(walletType) ? wallet.currency : selectedCryptoCurrency;
final fiat = calculateFiatAmount( final fiat = calculateFiatAmount(
price: _fiatConversationStore.prices[currency]!, price: _fiatConversationStore.prices[currency]!,
cryptoAmount: pendingTransaction!.feeFormatted); cryptoAmount: pendingTransaction!.feeFormatted);
@ -372,6 +374,9 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
priority: priority!, currency: selectedCryptoCurrency); priority: priority!, currency: selectedCryptoCurrency);
case WalletType.nano: case WalletType.nano:
return nano!.createNanoTransactionCredentials(outputs); return nano!.createNanoTransactionCredentials(outputs);
case WalletType.polygon:
return polygon!.createPolygonTransactionCredentials(outputs,
priority: priority!, currency: selectedCryptoCurrency);
default: default:
throw Exception('Unexpected wallet type: ${wallet.type}'); throw Exception('Unexpected wallet type: ${wallet.type}');
} }
@ -412,11 +417,15 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
WalletType walletType, WalletType walletType,
CryptoCurrency currency, CryptoCurrency currency,
) { ) {
if (walletType == WalletType.ethereum || walletType == WalletType.haven) { if (walletType == WalletType.ethereum ||
walletType == WalletType.polygon ||
walletType == WalletType.haven) {
if (error.contains('gas required exceeds allowance') || if (error.contains('gas required exceeds allowance') ||
error.contains('insufficient funds for')) { error.contains('insufficient funds for')) {
return S.current.do_not_have_enough_gas_asset(currency.toString()); return S.current.do_not_have_enough_gas_asset(currency.toString());
} }
return error;
} }
return error; return error;

View file

@ -1,6 +1,7 @@
import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart'; import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
import 'package:cake_wallet/entities/exchange_api_mode.dart'; import 'package:cake_wallet/entities/exchange_api_mode.dart';
import 'package:cake_wallet/ethereum/ethereum.dart'; import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:cake_wallet/polygon/polygon.dart';
import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/store/settings_store.dart';
import 'package:cw_core/balance.dart'; import 'package:cw_core/balance.dart';
import 'package:cw_core/transaction_history.dart'; import 'package:cw_core/transaction_history.dart';
@ -12,8 +13,7 @@ import 'package:cake_wallet/entities/fiat_api_mode.dart';
part 'privacy_settings_view_model.g.dart'; part 'privacy_settings_view_model.g.dart';
class class PrivacySettingsViewModel = PrivacySettingsViewModelBase with _$PrivacySettingsViewModel;
PrivacySettingsViewModel = PrivacySettingsViewModelBase with _$PrivacySettingsViewModel;
abstract class PrivacySettingsViewModelBase with Store { abstract class PrivacySettingsViewModelBase with Store {
PrivacySettingsViewModelBase(this._settingsStore, this._wallet); PrivacySettingsViewModelBase(this._settingsStore, this._wallet);
@ -58,6 +58,9 @@ abstract class PrivacySettingsViewModelBase with Store {
@computed @computed
bool get useEtherscan => _settingsStore.useEtherscan; bool get useEtherscan => _settingsStore.useEtherscan;
@computed
bool get usePolygonScan => _settingsStore.usePolygonScan;
@computed @computed
bool get lookupTwitter => _settingsStore.lookupsTwitter; bool get lookupTwitter => _settingsStore.lookupsTwitter;
@ -78,6 +81,8 @@ abstract class PrivacySettingsViewModelBase with Store {
bool get canUseEtherscan => _wallet.type == WalletType.ethereum; bool get canUseEtherscan => _wallet.type == WalletType.ethereum;
bool get canUsePolygonScan => _wallet.type == WalletType.polygon;
@action @action
void setShouldSaveRecipientAddress(bool value) => void setShouldSaveRecipientAddress(bool value) =>
_settingsStore.shouldSaveRecipientAddress = value; _settingsStore.shouldSaveRecipientAddress = value;
@ -120,4 +125,10 @@ abstract class PrivacySettingsViewModelBase with Store {
_settingsStore.useEtherscan = value; _settingsStore.useEtherscan = value;
ethereum!.updateEtherscanUsageState(_wallet, value); ethereum!.updateEtherscanUsageState(_wallet, value);
} }
@action
void setUsePolygonScan(bool value) {
_settingsStore.usePolygonScan = value;
polygon!.updatePolygonScanUsageState(_wallet, value);
}
} }

View file

@ -51,6 +51,9 @@ abstract class TransactionDetailsViewModelBase with Store {
case WalletType.nano: case WalletType.nano:
_addNanoListItems(tx, dateFormat); _addNanoListItems(tx, dateFormat);
break; break;
case WalletType.polygon:
_addPolygonListItems(tx, dateFormat);
break;
default: default:
break; break;
} }
@ -125,7 +128,9 @@ abstract class TransactionDetailsViewModelBase with Store {
case WalletType.nano: case WalletType.nano:
return 'https://nanolooker.com/block/${txId}'; return 'https://nanolooker.com/block/${txId}';
case WalletType.banano: case WalletType.banano:
return 'https://bananolooker.com/block/${txId}'; return 'https://bananolooker.com/block/${txId}';
case WalletType.polygon:
return 'https://polygonscan.com/tx/${txId}';
default: default:
return ''; return '';
} }
@ -148,6 +153,8 @@ abstract class TransactionDetailsViewModelBase with Store {
return S.current.view_transaction_on + 'nanolooker.com'; return S.current.view_transaction_on + 'nanolooker.com';
case WalletType.banano: case WalletType.banano:
return S.current.view_transaction_on + 'bananolooker.com'; return S.current.view_transaction_on + 'bananolooker.com';
case WalletType.polygon:
return S.current.view_transaction_on + 'polygonscan.com';
default: default:
return ''; return '';
} }
@ -237,7 +244,6 @@ abstract class TransactionDetailsViewModelBase with Store {
items.addAll(_items); items.addAll(_items);
} }
void _addNanoListItems(TransactionInfo tx, DateFormat dateFormat) { void _addNanoListItems(TransactionInfo tx, DateFormat dateFormat) {
final _items = [ final _items = [
StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.id), StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.id),
@ -250,4 +256,21 @@ abstract class TransactionDetailsViewModelBase with Store {
items.addAll(_items); items.addAll(_items);
} }
void _addPolygonListItems(TransactionInfo tx, DateFormat dateFormat) {
final _items = [
StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.id),
StandartListItem(
title: S.current.transaction_details_date, value: dateFormat.format(tx.date)),
StandartListItem(title: S.current.confirmations, value: tx.confirmations.toString()),
StandartListItem(title: S.current.transaction_details_height, value: '${tx.height}'),
StandartListItem(title: S.current.transaction_details_amount, value: tx.amountFormatted()),
if (tx.feeFormatted()?.isNotEmpty ?? false)
StandartListItem(title: S.current.transaction_details_fee, value: tx.feeFormatted()!),
if (showRecipientAddress && tx.to != null)
StandartListItem(title: S.current.transaction_details_recipient_address, value: tx.to!),
];
items.addAll(_items);
}
} }

View file

@ -1,6 +1,7 @@
import 'package:cake_wallet/core/wallet_change_listener_view_model.dart'; import 'package:cake_wallet/core/wallet_change_listener_view_model.dart';
import 'package:cake_wallet/ethereum/ethereum.dart'; import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:cake_wallet/entities/fiat_currency.dart'; import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/polygon/polygon.dart';
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart'; import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
import 'package:cake_wallet/store/yat/yat_store.dart'; import 'package:cake_wallet/store/yat/yat_store.dart';
import 'package:cw_core/currency.dart'; import 'package:cw_core/currency.dart';
@ -139,6 +140,22 @@ class NanoURI extends PaymentURI {
} }
} }
class PolygonURI extends PaymentURI {
PolygonURI({required String amount, required String address})
: super(amount: amount, address: address);
@override
String toString() {
var base = 'polygon:' + address;
if (amount.isNotEmpty) {
base += '?amount=${amount.replaceAll(',', '.')}';
}
return base;
}
}
abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewModel with Store { abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewModel with Store {
WalletAddressListViewModelBase({ WalletAddressListViewModelBase({
required AppStore appStore, required AppStore appStore,
@ -216,6 +233,10 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
return NanoURI(amount: amount, address: address.address); return NanoURI(amount: amount, address: address.address);
} }
if (wallet.type == WalletType.polygon) {
return PolygonURI(amount: amount, address: address.address);
}
throw Exception('Unexpected type: ${type.toString()}'); throw Exception('Unexpected type: ${type.toString()}');
} }
@ -272,6 +293,12 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
addressList.add(WalletAddressListItem(isPrimary: true, name: null, address: primaryAddress)); addressList.add(WalletAddressListItem(isPrimary: true, name: null, address: primaryAddress));
} }
if (wallet.type == WalletType.polygon) {
final primaryAddress = polygon!.getAddress(wallet);
addressList.add(WalletAddressListItem(isPrimary: true, name: null, address: primaryAddress));
}
return addressList; return addressList;
} }

View file

@ -1,3 +1,4 @@
import 'package:cake_wallet/reactions/wallet_connect.dart';
import 'package:cake_wallet/store/app_store.dart'; import 'package:cake_wallet/store/app_store.dart';
import 'package:cw_core/transaction_direction.dart'; import 'package:cw_core/transaction_direction.dart';
import 'package:cw_core/transaction_info.dart'; import 'package:cw_core/transaction_info.dart';
@ -19,7 +20,8 @@ abstract class WalletKeysViewModelBase with Store {
: title = _appStore.wallet!.type == WalletType.bitcoin || : title = _appStore.wallet!.type == WalletType.bitcoin ||
_appStore.wallet!.type == WalletType.litecoin || _appStore.wallet!.type == WalletType.litecoin ||
_appStore.wallet!.type == WalletType.bitcoinCash || _appStore.wallet!.type == WalletType.bitcoinCash ||
_appStore.wallet!.type == WalletType.ethereum _appStore.wallet!.type == WalletType.ethereum ||
_appStore.wallet!.type == WalletType.polygon
? S.current.wallet_seed ? S.current.wallet_seed
: S.current.wallet_keys, : S.current.wallet_keys,
_restoreHeight = _appStore.wallet!.walletInfo.restoreHeight, _restoreHeight = _appStore.wallet!.walletInfo.restoreHeight,
@ -98,7 +100,7 @@ abstract class WalletKeysViewModelBase with Store {
]); ]);
} }
if (_appStore.wallet!.type == WalletType.ethereum) { if (isEVMCompatibleChain(_appStore.wallet!.type)) {
items.addAll([ items.addAll([
if (_appStore.wallet!.privateKey != null) if (_appStore.wallet!.privateKey != null)
StandartListItem(title: S.current.private_key, value: _appStore.wallet!.privateKey!), StandartListItem(title: S.current.private_key, value: _appStore.wallet!.privateKey!),
@ -151,6 +153,8 @@ abstract class WalletKeysViewModelBase with Store {
return 'nano-wallet'; return 'nano-wallet';
case WalletType.banano: case WalletType.banano:
return 'banano-wallet'; return 'banano-wallet';
case WalletType.polygon:
return 'polygon-wallet';
default: default:
throw Exception('Unexpected wallet type: ${_appStore.wallet!.toString()}'); throw Exception('Unexpected wallet type: ${_appStore.wallet!.toString()}');
} }

View file

@ -14,6 +14,8 @@ import 'package:cake_wallet/view_model/wallet_creation_vm.dart';
import 'package:cake_wallet/bitcoin/bitcoin.dart'; import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/haven/haven.dart'; import 'package:cake_wallet/haven/haven.dart';
import '../polygon/polygon.dart';
part 'wallet_new_vm.g.dart'; part 'wallet_new_vm.g.dart';
class WalletNewVM = WalletNewVMBase with _$WalletNewVM; class WalletNewVM = WalletNewVMBase with _$WalletNewVM;
@ -52,6 +54,8 @@ abstract class WalletNewVMBase extends WalletCreationVM with Store {
return bitcoinCash!.createBitcoinCashNewWalletCredentials(name: name); return bitcoinCash!.createBitcoinCashNewWalletCredentials(name: name);
case WalletType.nano: case WalletType.nano:
return nano!.createNanoNewWalletCredentials(name: name); return nano!.createNanoNewWalletCredentials(name: name);
case WalletType.polygon:
return polygon!.createPolygonNewWalletCredentials(name: name);
default: default:
throw Exception('Unexpected type: ${type.toString()}'); throw Exception('Unexpected type: ${type.toString()}');
} }

View file

@ -3,6 +3,7 @@ import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/nano/nano.dart'; import 'package:cake_wallet/nano/nano.dart';
import 'package:cake_wallet/ethereum/ethereum.dart'; import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart'; import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
import 'package:cake_wallet/polygon/polygon.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
import 'package:cake_wallet/store/app_store.dart'; import 'package:cake_wallet/store/app_store.dart';
@ -28,7 +29,10 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
: hasSeedLanguageSelector = type == WalletType.monero || type == WalletType.haven, : hasSeedLanguageSelector = type == WalletType.monero || type == WalletType.haven,
hasBlockchainHeightLanguageSelector = type == WalletType.monero || type == WalletType.haven, hasBlockchainHeightLanguageSelector = type == WalletType.monero || type == WalletType.haven,
hasRestoreFromPrivateKey = hasRestoreFromPrivateKey =
type == WalletType.ethereum || type == WalletType.nano || type == WalletType.banano, type == WalletType.ethereum ||
type == WalletType.polygon ||
type == WalletType.nano ||
type == WalletType.banano,
isButtonEnabled = false, isButtonEnabled = false,
mode = WalletRestoreMode.seed, mode = WalletRestoreMode.seed,
super(appStore, walletInfoSource, walletCreationService, type: type, isRecovery: true) { super(appStore, walletInfoSource, walletCreationService, type: type, isRecovery: true) {
@ -36,6 +40,7 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
case WalletType.monero: case WalletType.monero:
case WalletType.haven: case WalletType.haven:
case WalletType.ethereum: case WalletType.ethereum:
case WalletType.polygon:
availableModes = WalletRestoreMode.values; availableModes = WalletRestoreMode.values;
break; break;
case WalletType.nano: case WalletType.nano:
@ -107,6 +112,12 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
mnemonic: seed, mnemonic: seed,
password: password, password: password,
derivationType: derivationType); derivationType: derivationType);
case WalletType.polygon:
return polygon!.createPolygonRestoreWalletFromSeedCredentials(
name: name,
mnemonic: seed,
password: password,
);
default: default:
break; break;
} }
@ -153,6 +164,12 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
password: password, password: password,
seedKey: options['private_key'] as String, seedKey: options['private_key'] as String,
derivationType: options["derivationType"] as DerivationType); derivationType: options["derivationType"] as DerivationType);
case WalletType.polygon:
return polygon!.createPolygonRestoreWalletFromPrivateKey(
name: name,
password: password,
privateKey: options['private_key'] as String,
);
default: default:
break; break;
} }

View file

@ -41,6 +41,7 @@ PODS:
- shared_preferences_foundation (0.0.1): - shared_preferences_foundation (0.0.1):
- Flutter - Flutter
- FlutterMacOS - FlutterMacOS
- tor (0.0.1)
- url_launcher_macos (0.0.1): - url_launcher_macos (0.0.1):
- FlutterMacOS - FlutterMacOS
- wakelock_plus (0.0.1): - wakelock_plus (0.0.1):
@ -59,6 +60,7 @@ DEPENDENCIES:
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
- share_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/share_plus_macos/macos`) - share_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/share_plus_macos/macos`)
- shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`) - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`)
- tor (from `Flutter/ephemeral/.symlinks/plugins/tor/macos`)
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`) - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
- wakelock_plus (from `Flutter/ephemeral/.symlinks/plugins/wakelock_plus/macos`) - wakelock_plus (from `Flutter/ephemeral/.symlinks/plugins/wakelock_plus/macos`)
@ -91,6 +93,8 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral/.symlinks/plugins/share_plus_macos/macos :path: Flutter/ephemeral/.symlinks/plugins/share_plus_macos/macos
shared_preferences_foundation: shared_preferences_foundation:
:path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin
tor:
:path: Flutter/ephemeral/.symlinks/plugins/tor/macos
url_launcher_macos: url_launcher_macos:
:path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos :path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
wakelock_plus: wakelock_plus:
@ -98,7 +102,7 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS: SPEC CHECKSUMS:
connectivity_plus_macos: f6e86fd000e971d361e54b5afcadc8c8fa773308 connectivity_plus_macos: f6e86fd000e971d361e54b5afcadc8c8fa773308
cw_monero: ec03de55a19c4a2b174ea687e0f4202edc716fa4 cw_monero: f8b7f104508efba2591548e76b5c058d05cba3f0
device_info_plus: 5401765fde0b8d062a2f8eb65510fb17e77cf07f device_info_plus: 5401765fde0b8d062a2f8eb65510fb17e77cf07f
devicelocale: 9f0f36ac651cabae2c33f32dcff4f32b61c38225 devicelocale: 9f0f36ac651cabae2c33f32dcff4f32b61c38225
flutter_secure_storage_macos: d56e2d218c1130b262bef8b4a7d64f88d7f9c9ea flutter_secure_storage_macos: d56e2d218c1130b262bef8b4a7d64f88d7f9c9ea
@ -110,6 +114,7 @@ SPEC CHECKSUMS:
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
share_plus_macos: 853ee48e7dce06b633998ca0735d482dd671ade4 share_plus_macos: 853ee48e7dce06b633998ca0735d482dd671ade4
shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126 shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126
tor: 2138c48428e696b83eacdda404de6d5574932e26
url_launcher_macos: d2691c7dd33ed713bf3544850a623080ec693d95 url_launcher_macos: d2691c7dd33ed713bf3544850a623080ec693d95
wakelock_plus: 4783562c9a43d209c458cb9b30692134af456269 wakelock_plus: 4783562c9a43d209c458cb9b30692134af456269

View file

@ -5,4 +5,5 @@ cd cw_haven && flutter pub get && flutter packages pub run build_runner build --
cd cw_ethereum && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd .. cd cw_ethereum && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
cd cw_nano && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd .. cd cw_nano && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
cd cw_bitcoin_cash && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd .. cd cw_bitcoin_cash && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
cd cw_polygon && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
flutter packages pub run build_runner build --delete-conflicting-outputs flutter packages pub run build_runner build --delete-conflicting-outputs

View file

@ -147,6 +147,7 @@ flutter:
- assets/bitcoin_cash_electrum_server_list.yml - assets/bitcoin_cash_electrum_server_list.yml
- assets/nano_node_list.yml - assets/nano_node_list.yml
- assets/nano_pow_node_list.yml - assets/nano_pow_node_list.yml
- assets/polygon_node_list.yml
- assets/text/ - assets/text/
- assets/faq/ - assets/faq/
- assets/animation/ - assets/animation/

View file

@ -745,5 +745,6 @@
"seedtype_polyseed": "بوليسيد (16 كلمة)", "seedtype_polyseed": "بوليسيد (16 كلمة)",
"seed_language_czech": "التشيكية", "seed_language_czech": "التشيكية",
"seed_language_korean": "الكورية", "seed_language_korean": "الكورية",
"seed_language_chinese_traditional": "تقاليد صينية)" "seed_language_chinese_traditional": "تقاليد صينية)",
"polygonscan_history": "ﻥﺎﻜﺴﻧﻮﺠﻴﻟﻮﺑ ﺦﻳﺭﺎﺗ"
} }

View file

@ -741,5 +741,6 @@
"seedtype_polyseed": "Поли семе (16 думи)", "seedtype_polyseed": "Поли семе (16 думи)",
"seed_language_czech": "Чех", "seed_language_czech": "Чех",
"seed_language_korean": "Корейски", "seed_language_korean": "Корейски",
"seed_language_chinese_traditional": "Традиционен китайски)" "seed_language_chinese_traditional": "Традиционен китайски)",
"polygonscan_history": "История на PolygonScan"
} }

View file

@ -741,5 +741,6 @@
"seedtype_polyseed": "Polyseed (16 slov)", "seedtype_polyseed": "Polyseed (16 slov)",
"seed_language_czech": "čeština", "seed_language_czech": "čeština",
"seed_language_korean": "korejština", "seed_language_korean": "korejština",
"seed_language_chinese_traditional": "Číňan (tradiční)" "seed_language_chinese_traditional": "Číňan (tradiční)",
"polygonscan_history": "Historie PolygonScan"
} }

View file

@ -749,5 +749,6 @@
"seedtype_polyseed": "Polyseed (16 Wörter)", "seedtype_polyseed": "Polyseed (16 Wörter)",
"seed_language_czech": "Tschechisch", "seed_language_czech": "Tschechisch",
"seed_language_korean": "Koreanisch", "seed_language_korean": "Koreanisch",
"seed_language_chinese_traditional": "Chinesisch (Traditionell)" "seed_language_chinese_traditional": "Chinesisch (Traditionell)",
"polygonscan_history": "PolygonScan-Verlauf"
} }

View file

@ -750,5 +750,6 @@
"seedtype_polyseed": "Polyseed (16 words)", "seedtype_polyseed": "Polyseed (16 words)",
"seed_language_czech": "Czech", "seed_language_czech": "Czech",
"seed_language_korean": "Korean", "seed_language_korean": "Korean",
"seed_language_chinese_traditional": "Chinese (Traditional)" "seed_language_chinese_traditional": "Chinese (Traditional)",
"polygonscan_history": "PolygonScan history"
} }

View file

@ -749,5 +749,6 @@
"seedtype_polyseed": "Polieta (16 palabras)", "seedtype_polyseed": "Polieta (16 palabras)",
"seed_language_czech": "checo", "seed_language_czech": "checo",
"seed_language_korean": "coreano", "seed_language_korean": "coreano",
"seed_language_chinese_traditional": "Chino (tradicional)" "seed_language_chinese_traditional": "Chino (tradicional)",
"polygonscan_history": "Historial de PolygonScan"
} }

View file

@ -749,5 +749,6 @@
"seedtype_polyseed": "Polyseed (16 mots)", "seedtype_polyseed": "Polyseed (16 mots)",
"seed_language_czech": "tchèque", "seed_language_czech": "tchèque",
"seed_language_korean": "coréen", "seed_language_korean": "coréen",
"seed_language_chinese_traditional": "Chinois (Traditionnel)" "seed_language_chinese_traditional": "Chinois (Traditionnel)",
"polygonscan_history": "Historique de PolygonScan"
} }

View file

@ -727,5 +727,6 @@
"seedtype_polyseed": "Polyseed (16 kalmomi)", "seedtype_polyseed": "Polyseed (16 kalmomi)",
"seed_language_czech": "Czech", "seed_language_czech": "Czech",
"seed_language_korean": "Yaren Koriya", "seed_language_korean": "Yaren Koriya",
"seed_language_chinese_traditional": "Sinanci (na gargajiya)" "seed_language_chinese_traditional": "Sinanci (na gargajiya)",
"polygonscan_history": "PolygonScan tarihin kowane zamani"
} }

View file

@ -749,5 +749,6 @@
"seedtype_polyseed": "पॉलीसीड (16 शब्द)", "seedtype_polyseed": "पॉलीसीड (16 शब्द)",
"seed_language_czech": "चेक", "seed_language_czech": "चेक",
"seed_language_korean": "कोरियाई", "seed_language_korean": "कोरियाई",
"seed_language_chinese_traditional": "चीनी पारंपरिक)" "seed_language_chinese_traditional": "चीनी पारंपरिक)",
"polygonscan_history": "पॉलीगॉनस्कैन इतिहास"
} }

View file

@ -747,5 +747,6 @@
"seedtype_polyseed": "Poliseed (16 riječi)", "seedtype_polyseed": "Poliseed (16 riječi)",
"seed_language_czech": "češki", "seed_language_czech": "češki",
"seed_language_korean": "korejski", "seed_language_korean": "korejski",
"seed_language_chinese_traditional": "Kinesko (tradicionalno)" "seed_language_chinese_traditional": "Kinesko (tradicionalno)",
"polygonscan_history": "Povijest PolygonScan"
} }

View file

@ -737,5 +737,6 @@
"seedtype_polyseed": "Polyseed (16 kata)", "seedtype_polyseed": "Polyseed (16 kata)",
"seed_language_czech": "Ceko", "seed_language_czech": "Ceko",
"seed_language_korean": "Korea", "seed_language_korean": "Korea",
"seed_language_chinese_traditional": "Cina (tradisional)" "seed_language_chinese_traditional": "Cina (tradisional)",
"polygonscan_history": "Sejarah PolygonScan"
} }

View file

@ -749,5 +749,6 @@
"seedtype_polyseed": "Polyseed (16 parole)", "seedtype_polyseed": "Polyseed (16 parole)",
"seed_language_czech": "ceco", "seed_language_czech": "ceco",
"seed_language_korean": "coreano", "seed_language_korean": "coreano",
"seed_language_chinese_traditional": "Cinese tradizionale)" "seed_language_chinese_traditional": "Cinese tradizionale)",
"polygonscan_history": "Cronologia PolygonScan"
} }

View file

@ -749,5 +749,6 @@
"seedtype_polyseed": "ポリシード16語", "seedtype_polyseed": "ポリシード16語",
"seed_language_czech": "チェコ", "seed_language_czech": "チェコ",
"seed_language_korean": "韓国語", "seed_language_korean": "韓国語",
"seed_language_chinese_traditional": "中国の伝統的な)" "seed_language_chinese_traditional": "中国の伝統的な)",
"polygonscan_history": "ポリゴンスキャン履歴"
} }

Some files were not shown because too many files have changed in this diff Show more