diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml
index 7b2b611d3..8fef4fdb6 100644
--- a/.github/workflows/pr_test_build.yml
+++ b/.github/workflows/pr_test_build.yml
@@ -97,6 +97,7 @@ jobs:
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_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
- name: Add secrets
@@ -131,6 +132,7 @@ jobs:
echo "const fiatApiKey = '${{ secrets.FIAT_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 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 exolixApiKey = '${{ secrets.EXOLIX_API_KEY }}';" >> lib/.secrets.g.dart
echo "const robinhoodApplicationId = '${{ secrets.ROBINHOOD_APPLICATION_ID }}';" >> lib/.secrets.g.dart
diff --git a/.gitignore b/.gitignore
index c735d4058..0a883dd18 100644
--- a/.gitignore
+++ b/.gitignore
@@ -126,6 +126,7 @@ lib/haven/haven.dart
lib/ethereum/ethereum.dart
lib/bitcoin_cash/bitcoin_cash.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_120.png
diff --git a/android/app/src/main/AndroidManifestBase.xml b/android/app/src/main/AndroidManifestBase.xml
index 910149f60..f32482e22 100644
--- a/android/app/src/main/AndroidManifestBase.xml
+++ b/android/app/src/main/AndroidManifestBase.xml
@@ -62,6 +62,9 @@
+
+
+
(other is Erc20Token && other.contractAddress == contractAddress) ||
diff --git a/cw_core/lib/get_height_by_date.dart b/cw_core/lib/get_height_by_date.dart
index a680e6c25..6f3ccaf68 100644
--- a/cw_core/lib/get_height_by_date.dart
+++ b/cw_core/lib/get_height_by_date.dart
@@ -118,7 +118,10 @@ final dates = {
"2023-6": 2898234,
"2023-7": 2919771,
"2023-8": 2942045,
- "2023-9": 2964280
+ "2023-9": 2964280,
+ "2023-10": 2985937,
+ "2023-11": 3008178,
+ "2023-12": 3029759
};
int getMoneroHeigthByDate({required DateTime date}) {
diff --git a/cw_core/lib/node.dart b/cw_core/lib/node.dart
index 484325f91..bd96e1395 100644
--- a/cw_core/lib/node.dart
+++ b/cw_core/lib/node.dart
@@ -6,7 +6,7 @@ import 'package:hive/hive.dart';
import 'package:cw_core/hive_type_ids.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:http/io_client.dart' as ioc;
-import 'package:tor/tor.dart';
+// import 'package:tor/tor.dart';
part 'node.g.dart';
@@ -88,6 +88,8 @@ class Node extends HiveObject with Keyable {
} else {
return Uri.http(uriRaw, '');
}
+ case WalletType.polygon:
+ return Uri.https(uriRaw, '');
default:
throw Exception('Unexpected type ${type.toString()} for Node uri');
}
@@ -146,6 +148,8 @@ class Node extends HiveObject with Keyable {
case WalletType.nano:
case WalletType.banano:
return requestNanoNode();
+ case WalletType.polygon:
+ return requestElectrumServer();
default:
return false;
}
@@ -210,14 +214,17 @@ class Node extends HiveObject with Keyable {
}
Future requestNodeWithProxy() async {
- if (!isValidProxyAddress && !Tor.instance.enabled) {
+ if (!isValidProxyAddress/* && !Tor.instance.enabled*/) {
return false;
}
String? proxy = socksProxyAddress;
- if ((proxy?.isEmpty ?? true) && Tor.instance.enabled) {
- proxy = "${InternetAddress.loopbackIPv4.address}:${Tor.instance.port}";
+ // if ((proxy?.isEmpty ?? true) && Tor.instance.enabled) {
+ // proxy = "${InternetAddress.loopbackIPv4.address}:${Tor.instance.port}";
+ // }
+ if (proxy == null) {
+ return false;
}
final proxyAddress = proxy!.split(':')[0];
final proxyPort = int.parse(proxy.split(':')[1]);
diff --git a/cw_bitcoin/lib/file.dart b/cw_core/lib/utils/file.dart
similarity index 66%
rename from cw_bitcoin/lib/file.dart
rename to cw_core/lib/utils/file.dart
index 8fd236ec3..0b1c5cffd 100644
--- a/cw_bitcoin/lib/file.dart
+++ b/cw_core/lib/utils/file.dart
@@ -2,17 +2,8 @@ import 'dart:io';
import 'package:cw_core/key.dart';
import 'package:encrypt/encrypt.dart' as encrypt;
-Future write(
- {required String path,
- required String password,
- required String data}) async {
- final keys = extractKeys(password);
- final key = encrypt.Key.fromBase64(keys.first);
- final iv = encrypt.IV.fromBase64(keys.last);
- final encrypted = await encode(key: key, iv: iv, data: data);
- final f = File(path);
- f.writeAsStringSync(encrypted);
-}
+Future write({required String path, required String password, required String data}) async =>
+ writeData(path: path, password: password, data: data);
Future writeData(
{required String path,
diff --git a/cw_core/lib/wallet_type.dart b/cw_core/lib/wallet_type.dart
index debf92e11..20f0bdb19 100644
--- a/cw_core/lib/wallet_type.dart
+++ b/cw_core/lib/wallet_type.dart
@@ -13,6 +13,7 @@ const walletTypes = [
WalletType.bitcoinCash,
WalletType.nano,
WalletType.banano,
+ WalletType.polygon,
];
@HiveType(typeId: WALLET_TYPE_TYPE_ID)
@@ -44,6 +45,8 @@ enum WalletType {
@HiveField(8)
bitcoinCash,
+ @HiveField(9)
+ polygon
}
int serializeToInt(WalletType type) {
@@ -64,6 +67,8 @@ int serializeToInt(WalletType type) {
return 6;
case WalletType.bitcoinCash:
return 7;
+ case WalletType.polygon:
+ return 8;
default:
return -1;
}
@@ -87,6 +92,8 @@ WalletType deserializeFromInt(int raw) {
return WalletType.banano;
case 7:
return WalletType.bitcoinCash;
+ case 8:
+ return WalletType.polygon;
default:
throw Exception('Unexpected token: $raw for WalletType deserializeFromInt');
}
@@ -110,6 +117,8 @@ String walletTypeToString(WalletType type) {
return 'Nano';
case WalletType.banano:
return 'Banano';
+ case WalletType.polygon:
+ return 'Polygon';
default:
return '';
}
@@ -133,6 +142,8 @@ String walletTypeToDisplayName(WalletType type) {
return 'Nano (XNO)';
case WalletType.banano:
return 'Banano (BAN)';
+ case WalletType.polygon:
+ return 'Polygon (MATIC)';
default:
return '';
}
@@ -156,7 +167,10 @@ CryptoCurrency walletTypeToCryptoCurrency(WalletType type) {
return CryptoCurrency.nano;
case WalletType.banano:
return CryptoCurrency.banano;
+ case WalletType.polygon:
+ return CryptoCurrency.maticpoly;
default:
- throw Exception('Unexpected wallet type: ${type.toString()} for CryptoCurrency walletTypeToCryptoCurrency');
+ throw Exception(
+ 'Unexpected wallet type: ${type.toString()} for CryptoCurrency walletTypeToCryptoCurrency');
}
}
diff --git a/cw_core/pubspec.yaml b/cw_core/pubspec.yaml
index 533b578ad..04a840d4e 100644
--- a/cw_core/pubspec.yaml
+++ b/cw_core/pubspec.yaml
@@ -20,10 +20,10 @@ dependencies:
intl: ^0.18.0
encrypt: ^5.0.1
socks5_proxy: ^1.0.4
- tor:
- git:
- url: https://github.com/cake-tech/tor.git
- ref: main
+# tor:
+# git:
+# url: https://github.com/cake-tech/tor.git
+# ref: main
dev_dependencies:
flutter_test:
diff --git a/cw_ethereum/lib/ethereum_client.dart b/cw_ethereum/lib/ethereum_client.dart
index f0c7381e8..5e408bb9e 100644
--- a/cw_ethereum/lib/ethereum_client.dart
+++ b/cw_ethereum/lib/ethereum_client.dart
@@ -15,12 +15,12 @@ import 'package:cw_ethereum/ethereum_transaction_priority.dart';
import 'package:cw_ethereum/.secrets.g.dart' as secrets;
class EthereumClient {
- final _httpClient = Client();
+ final httpClient = Client();
Web3Client? _client;
bool connect(Node node) {
try {
- _client = Web3Client(node.uri.toString(), _httpClient);
+ _client = Web3Client(node.uri.toString(), httpClient);
return true;
} catch (e) {
@@ -74,9 +74,11 @@ class EthereumClient {
required int exponent,
String? contractAddress,
}) 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();
@@ -84,19 +86,23 @@ class EthereumClient {
from: privateKey.address,
to: EthereumAddress.fromHex(toAddress),
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;
- if (_isEthereum) {
+ if (_isEVMCompatibleChain) {
_sendTransaction = () async => await sendTransaction(signedTransaction);
} else {
final erc20 = ERC20(
client: _client!,
address: EthereumAddress.fromHex(contractAddress!),
+ chainId: chainId,
);
_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 sendTransaction(Uint8List signedTransaction) async =>
await _client!.sendRawTransaction(prependTransactionType(0x02, signedTransaction));
@@ -198,7 +214,7 @@ I/flutter ( 4474): Gas Used: 53000
Future> fetchTransactions(String address,
{String? contractAddress}) async {
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",
"action": contractAddress != null ? "tokentx" : "txlist",
if (contractAddress != null) "contractaddress": contractAddress,
diff --git a/cw_ethereum/lib/ethereum_transaction_info.dart b/cw_ethereum/lib/ethereum_transaction_info.dart
index a0649ba25..f0deae931 100644
--- a/cw_ethereum/lib/ethereum_transaction_info.dart
+++ b/cw_ethereum/lib/ethereum_transaction_info.dart
@@ -1,3 +1,5 @@
+import 'dart:math';
+
import 'package:cw_core/format_amount.dart';
import 'package:cw_core/transaction_direction.dart';
import 'package:cw_core/transaction_info.dart';
@@ -34,8 +36,10 @@ class EthereumTransactionInfo extends TransactionInfo {
final String? to;
@override
- String amountFormatted() =>
- '${formatAmount((ethAmount / BigInt.from(10).pow(exponent)).toString())} $tokenSymbol';
+ String amountFormatted() {
+ final amount = formatAmount((ethAmount / BigInt.from(10).pow(exponent)).toString());
+ return '${amount.substring(0, min(10, amount.length))} $tokenSymbol';
+ }
@override
String fiatAmount() => _fiatAmount ?? '';
@@ -44,7 +48,10 @@ class EthereumTransactionInfo extends TransactionInfo {
void changeFiatAmount(String amount) => _fiatAmount = formatAmount(amount);
@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 data) {
return EthereumTransactionInfo(
diff --git a/cw_ethereum/lib/ethereum_transaction_model.dart b/cw_ethereum/lib/ethereum_transaction_model.dart
index c1260795a..3b5f724fc 100644
--- a/cw_ethereum/lib/ethereum_transaction_model.dart
+++ b/cw_ethereum/lib/ethereum_transaction_model.dart
@@ -1,3 +1,4 @@
+//! Model used for in parsing transactions fetched using etherscan
class EthereumTransactionModel {
final DateTime date;
final String hash;
diff --git a/cw_ethereum/lib/pending_ethereum_transaction.dart b/cw_ethereum/lib/pending_ethereum_transaction.dart
index 35b0123cc..d47630fd6 100644
--- a/cw_ethereum/lib/pending_ethereum_transaction.dart
+++ b/cw_ethereum/lib/pending_ethereum_transaction.dart
@@ -21,8 +21,8 @@ class PendingEthereumTransaction with PendingTransaction {
@override
String get amountFormatted {
- final _amount = BigInt.parse(amount) / BigInt.from(pow(10, exponent));
- return _amount.toStringAsFixed(min(15, _amount.toString().length));
+ final _amount = (BigInt.parse(amount) / BigInt.from(pow(10, exponent))).toString();
+ return _amount.substring(0, min(10, _amount.length));
}
@override
@@ -30,8 +30,8 @@ class PendingEthereumTransaction with PendingTransaction {
@override
String get feeFormatted {
- final _fee = fee / BigInt.from(pow(10, 18));
- return _fee.toStringAsFixed(min(15, _fee.toString().length));
+ final _fee = (fee / BigInt.from(pow(10, 18))).toString();
+ return _fee.substring(0, min(10, _fee.length));
}
@override
diff --git a/cw_monero/ios/Classes/monero_api.cpp b/cw_monero/ios/Classes/monero_api.cpp
index 7ad873647..6162375b2 100644
--- a/cw_monero/ios/Classes/monero_api.cpp
+++ b/cw_monero/ios/Classes/monero_api.cpp
@@ -385,6 +385,9 @@ extern "C"
(uint64_t)restoreHeight,
std::string(spendKey));
+ // Cache Raw to support Polyseed
+ wallet->setCacheAttribute("cakewallet.seed", std::string(seed));
+
int status;
std::string errorString;
@@ -396,9 +399,6 @@ extern "C"
return false;
}
- // Cache Raw to support Polyseed
- wallet->setCacheAttribute("cakewallet.seed", std::string(seed));
-
change_current_wallet(wallet);
return true;
}
diff --git a/cw_monero/lib/api/wallet_manager.dart b/cw_monero/lib/api/wallet_manager.dart
index 260b420c5..0aa694e9a 100644
--- a/cw_monero/lib/api/wallet_manager.dart
+++ b/cw_monero/lib/api/wallet_manager.dart
@@ -1,14 +1,16 @@
import 'dart:ffi';
-import 'package:ffi/ffi.dart';
-import 'package:flutter/foundation.dart';
+
import 'package:cw_monero/api/convert_utf8_to_string.dart';
-import 'package:cw_monero/api/signatures.dart';
-import 'package:cw_monero/api/types.dart';
-import 'package:cw_monero/api/monero_api.dart';
-import 'package:cw_monero/api/exceptions/wallet_opening_exception.dart';
import 'package:cw_monero/api/exceptions/wallet_creation_exception.dart';
+import 'package:cw_monero/api/exceptions/wallet_opening_exception.dart';
import 'package:cw_monero/api/exceptions/wallet_restore_from_keys_exception.dart';
import 'package:cw_monero/api/exceptions/wallet_restore_from_seed_exception.dart';
+import 'package:cw_monero/api/monero_api.dart';
+import 'package:cw_monero/api/signatures.dart';
+import 'package:cw_monero/api/types.dart';
+import 'package:cw_monero/api/wallet.dart';
+import 'package:ffi/ffi.dart';
+import 'package:flutter/foundation.dart';
final createWalletNative = moneroApi
.lookup>('create_wallet')
@@ -175,6 +177,8 @@ void restoreWalletFromSpendKeySync(
calloc.free(languagePointer);
calloc.free(spendKeyPointer);
+ storeSync();
+
if (!isWalletRestored) {
throw WalletRestoreFromKeysException(
message: convertUTF8ToString(pointer: errorMessagePointer));
diff --git a/cw_monero/lib/monero_wallet_service.dart b/cw_monero/lib/monero_wallet_service.dart
index 077ab4e54..a6b3227b2 100644
--- a/cw_monero/lib/monero_wallet_service.dart
+++ b/cw_monero/lib/monero_wallet_service.dart
@@ -77,8 +77,12 @@ class MoneroWalletService extends WalletService<
final polyseed = Polyseed.create();
final lang = PolyseedLang.getByEnglishName(credentials.language);
+ final heightOverride =
+ getMoneroHeigthByDate(date: DateTime.now().subtract(Duration(days: 2)));
+
return _restoreFromPolyseed(
- path, credentials.password!, polyseed, credentials.walletInfo!, lang);
+ path, credentials.password!, polyseed, credentials.walletInfo!, lang,
+ overrideHeight: heightOverride);
}
await monero_wallet_manager.createWallet(
@@ -268,10 +272,11 @@ class MoneroWalletService extends WalletService<
Future _restoreFromPolyseed(String path, String password, Polyseed polyseed,
WalletInfo walletInfo, PolyseedLang lang,
- {PolyseedCoin coin = PolyseedCoin.POLYSEED_MONERO}) async {
- final height = getMoneroHeigthByDate(
+ {PolyseedCoin coin = PolyseedCoin.POLYSEED_MONERO, int? overrideHeight}) async {
+ final height = overrideHeight ?? getMoneroHeigthByDate(
date: DateTime.fromMillisecondsSinceEpoch(polyseed.birthday * 1000));
final spendKey = keyToHexString(polyseed.generateKey(coin, 32));
+ final seed = polyseed.encode(lang, coin);
walletInfo.isRecovery = true;
walletInfo.restoreHeight = height;
@@ -279,10 +284,11 @@ class MoneroWalletService extends WalletService<
await monero_wallet_manager.restoreFromSpendKey(
path: path,
password: password,
- seed: polyseed.encode(lang, coin),
+ seed: seed,
language: lang.nameEnglish,
restoreHeight: height,
spendKey: spendKey);
+
final wallet = MoneroWallet(
walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfoSource);
await wallet.init();
diff --git a/cw_monero/macos/Classes/monero_api.cpp b/cw_monero/macos/Classes/monero_api.cpp
index a5ca13822..6fabc29fd 100644
--- a/cw_monero/macos/Classes/monero_api.cpp
+++ b/cw_monero/macos/Classes/monero_api.cpp
@@ -385,6 +385,9 @@ extern "C"
(uint64_t)restoreHeight,
std::string(spendKey));
+ // Cache Raw to support Polyseed
+ wallet->setCacheAttribute("cakewallet.seed", std::string(seed));
+
int status;
std::string errorString;
@@ -396,9 +399,6 @@ extern "C"
return false;
}
- // Cache Raw to support Polyseed
- wallet->setCacheAttribute("cakewallet.seed", std::string(seed));
-
change_current_wallet(wallet);
return true;
}
diff --git a/cw_polygon/.gitignore b/cw_polygon/.gitignore
new file mode 100644
index 000000000..96486fd93
--- /dev/null
+++ b/cw_polygon/.gitignore
@@ -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/
diff --git a/cw_polygon/.metadata b/cw_polygon/.metadata
new file mode 100644
index 000000000..fa347fc6a
--- /dev/null
+++ b/cw_polygon/.metadata
@@ -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
diff --git a/cw_polygon/CHANGELOG.md b/cw_polygon/CHANGELOG.md
new file mode 100644
index 000000000..41cc7d819
--- /dev/null
+++ b/cw_polygon/CHANGELOG.md
@@ -0,0 +1,3 @@
+## 0.0.1
+
+* TODO: Describe initial release.
diff --git a/cw_polygon/LICENSE b/cw_polygon/LICENSE
new file mode 100644
index 000000000..ba75c69f7
--- /dev/null
+++ b/cw_polygon/LICENSE
@@ -0,0 +1 @@
+TODO: Add your license here.
diff --git a/cw_polygon/README.md b/cw_polygon/README.md
new file mode 100644
index 000000000..02fe8ecab
--- /dev/null
+++ b/cw_polygon/README.md
@@ -0,0 +1,39 @@
+
+
+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.
diff --git a/cw_polygon/analysis_options.yaml b/cw_polygon/analysis_options.yaml
new file mode 100644
index 000000000..a5744c1cf
--- /dev/null
+++ b/cw_polygon/analysis_options.yaml
@@ -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
diff --git a/cw_polygon/lib/cw_polygon.dart b/cw_polygon/lib/cw_polygon.dart
new file mode 100644
index 000000000..5d4e447d1
--- /dev/null
+++ b/cw_polygon/lib/cw_polygon.dart
@@ -0,0 +1,7 @@
+library cw_polygon;
+
+/// A Calculator.
+class Calculator {
+ /// Returns [value] plus 1.
+ int addOne(int value) => value + 1;
+}
diff --git a/cw_polygon/lib/default_erc20_tokens.dart b/cw_polygon/lib/default_erc20_tokens.dart
new file mode 100644
index 000000000..132c52e4c
--- /dev/null
+++ b/cw_polygon/lib/default_erc20_tokens.dart
@@ -0,0 +1,86 @@
+import 'package:cw_core/crypto_currency.dart';
+import 'package:cw_core/erc20_token.dart';
+
+class DefaultPolygonErc20Tokens {
+ final List _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 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();
+}
diff --git a/cw_polygon/lib/pending_polygon_transaction.dart b/cw_polygon/lib/pending_polygon_transaction.dart
new file mode 100644
index 000000000..50f1f0638
--- /dev/null
+++ b/cw_polygon/lib/pending_polygon_transaction.dart
@@ -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,
+ );
+}
diff --git a/cw_polygon/lib/polygon_client.dart b/cw_polygon/lib/polygon_client.dart
new file mode 100644
index 000000000..86c2253af
--- /dev/null
+++ b/cw_polygon/lib/polygon_client.dart
@@ -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> 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;
+
+ if (response.statusCode >= 200 && response.statusCode < 300 && jsonResponse['status'] != 0) {
+ return (jsonResponse['result'] as List)
+ .map((e) => PolygonTransactionModel.fromJson(e as Map))
+ .toList();
+ }
+
+ return [];
+ } catch (e) {
+ print(e);
+ return [];
+ }
+ }
+}
diff --git a/cw_polygon/lib/polygon_exceptions.dart b/cw_polygon/lib/polygon_exceptions.dart
new file mode 100644
index 000000000..2d08106b6
--- /dev/null
+++ b/cw_polygon/lib/polygon_exceptions.dart
@@ -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);
+}
diff --git a/cw_polygon/lib/polygon_formatter.dart b/cw_polygon/lib/polygon_formatter.dart
new file mode 100644
index 000000000..f016db7ab
--- /dev/null
+++ b/cw_polygon/lib/polygon_formatter.dart
@@ -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;
+ }
+ }
+}
diff --git a/cw_polygon/lib/polygon_mnemonics_exception.dart b/cw_polygon/lib/polygon_mnemonics_exception.dart
new file mode 100644
index 000000000..c1a2fcc84
--- /dev/null
+++ b/cw_polygon/lib/polygon_mnemonics_exception.dart
@@ -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.';
+}
diff --git a/cw_polygon/lib/polygon_transaction_credentials.dart b/cw_polygon/lib/polygon_transaction_credentials.dart
new file mode 100644
index 000000000..6611e15da
--- /dev/null
+++ b/cw_polygon/lib/polygon_transaction_credentials.dart
@@ -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 outputs, {
+ required PolygonTransactionPriority? priority,
+ required CryptoCurrency currency,
+ final int? feeRate,
+ }) : super(
+ outputs,
+ currency: currency,
+ priority: priority,
+ feeRate: feeRate,
+ );
+}
diff --git a/cw_polygon/lib/polygon_transaction_history.dart b/cw_polygon/lib/polygon_transaction_history.dart
new file mode 100644
index 000000000..a06b8be4a
--- /dev/null
+++ b/cw_polygon/lib/polygon_transaction_history.dart
@@ -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
+ with Store {
+ PolygonTransactionHistoryBase({required this.walletInfo, required String password})
+ : _password = password {
+ transactions = ObservableMap();
+ }
+
+ final WalletInfo walletInfo;
+ String _password;
+
+ Future init() async => await _load();
+
+ @override
+ Future 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 transactions) =>
+ this.transactions.addAll(transactions);
+
+ Future