mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2024-11-17 01:37:40 +00:00
Merge branch 'main' into CW-607-Update-list-of-Trocador-providers-to-be-fetched-from-the-API
This commit is contained in:
commit
b54c755dd5
292 changed files with 10946 additions and 2386 deletions
6
.github/workflows/pr_test_build.yml
vendored
6
.github/workflows/pr_test_build.yml
vendored
|
@ -42,7 +42,7 @@ jobs:
|
|||
- name: Flutter action
|
||||
uses: subosito/flutter-action@v1
|
||||
with:
|
||||
flutter-version: "3.10.x"
|
||||
flutter-version: "3.19.5"
|
||||
channel: stable
|
||||
|
||||
- name: Install package dependencies
|
||||
|
@ -113,6 +113,7 @@ jobs:
|
|||
touch lib/.secrets.g.dart
|
||||
touch cw_evm/lib/.secrets.g.dart
|
||||
touch cw_solana/lib/.secrets.g.dart
|
||||
touch cw_tron/lib/.secrets.g.dart
|
||||
echo "const salt = '${{ secrets.SALT }}';" > lib/.secrets.g.dart
|
||||
echo "const keychainSalt = '${{ secrets.KEY_CHAIN_SALT }}';" >> lib/.secrets.g.dart
|
||||
echo "const key = '${{ secrets.KEY }}';" >> lib/.secrets.g.dart
|
||||
|
@ -150,6 +151,9 @@ jobs:
|
|||
echo "const moralisApiKey = '${{ secrets.MORALIS_API_KEY }}';" >> lib/.secrets.g.dart
|
||||
echo "const polygonScanApiKey = '${{ secrets.POLYGON_SCAN_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart
|
||||
echo "const ankrApiKey = '${{ secrets.ANKR_API_KEY }}';" >> cw_solana/lib/.secrets.g.dart
|
||||
echo "const quantexExchangeMarkup = '${{ secrets.QUANTEX_EXCHANGE_MARKUP }}';" >> lib/.secrets.g.dart
|
||||
echo "const nano2ApiKey = '${{ secrets.NANO2_API_KEY }}';" >> cw_nano/lib/.secrets.g.dart
|
||||
echo "const tronGridApiKey = '${{ secrets.TRON_GRID_API_KEY }}';" >> cw_tron/lib/.secrets.g.dart
|
||||
|
||||
- name: Rename app
|
||||
run: |
|
||||
|
|
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -94,9 +94,12 @@ android/app/key.jks
|
|||
**/tool/.evm-secrets-config.json
|
||||
**/tool/.ethereum-secrets-config.json
|
||||
**/tool/.solana-secrets-config.json
|
||||
**/tool/.nano-secrets-config.json
|
||||
**/tool/.tron-secrets-config.json
|
||||
**/lib/.secrets.g.dart
|
||||
**/cw_evm/lib/.secrets.g.dart
|
||||
**/cw_solana/lib/.secrets.g.dart
|
||||
**/cw_tron/lib/.secrets.g.dart
|
||||
|
||||
vendor/
|
||||
|
||||
|
@ -132,6 +135,7 @@ lib/bitcoin_cash/bitcoin_cash.dart
|
|||
lib/nano/nano.dart
|
||||
lib/polygon/polygon.dart
|
||||
lib/solana/solana.dart
|
||||
lib/tron/tron.dart
|
||||
|
||||
ios/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_180.png
|
||||
ios/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_120.png
|
||||
|
@ -152,6 +156,7 @@ assets/images/app_logo.png
|
|||
macos/Runner/Info.plist
|
||||
macos/Runner/DebugProfile.entitlements
|
||||
macos/Runner/Release.entitlements
|
||||
lib/core/secure_storage.dart
|
||||
|
||||
macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png
|
||||
macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png
|
||||
|
|
|
@ -9,6 +9,26 @@
|
|||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
||||
|
||||
<!--bibo01 : hardware option-->
|
||||
<uses-feature android:name="android.hardware.bluetooth" android:required="false"/>
|
||||
<uses-feature android:name="android.hardware.bluetooth_le" android:required="false"/>
|
||||
|
||||
<!-- required for API 18 - 30 -->
|
||||
<uses-permission
|
||||
android:name="android.permission.BLUETOOTH"
|
||||
android:maxSdkVersion="30" />
|
||||
<uses-permission
|
||||
android:name="android.permission.BLUETOOTH_ADMIN"
|
||||
android:maxSdkVersion="30" />
|
||||
|
||||
<!-- API 31+ -->
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
||||
<uses-permission
|
||||
android:name="android.permission.BLUETOOTH_SCAN"
|
||||
android:usesPermissionFlags="neverForLocation" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
|
||||
|
||||
<application
|
||||
android:name=".Application"
|
||||
|
@ -67,6 +87,16 @@
|
|||
<data android:scheme="polygon-wallet" />
|
||||
<data android:scheme="polygon_wallet" />
|
||||
<data android:scheme="solana-wallet" />
|
||||
<data android:scheme="tron" />
|
||||
<data android:scheme="tron-wallet" />
|
||||
<data android:scheme="tron_wallet" />
|
||||
</intent-filter>
|
||||
<!-- nano-gpt link scheme -->
|
||||
<intent-filter android:autoVerify="true">
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="nano-gpt" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<meta-data
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
buildscript {
|
||||
ext.kotlin_version = '1.7.10'
|
||||
ext.kotlin_version = '1.8.21'
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
|
|
5
assets/banano_node_list.yml
Normal file
5
assets/banano_node_list.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
-
|
||||
uri: kaliumapi.appditto.com
|
||||
path: /api
|
||||
useSSL: true
|
||||
is_default: true
|
BIN
assets/images/bluetooth.png
Normal file
BIN
assets/images/bluetooth.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.8 KiB |
BIN
assets/images/ledger_nano.png
Normal file
BIN
assets/images/ledger_nano.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/images/quantex.png
Normal file
BIN
assets/images/quantex.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
BIN
assets/images/usb.png
Normal file
BIN
assets/images/usb.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.4 KiB |
|
@ -6,4 +6,4 @@
|
|||
uri: workers.perish.co
|
||||
-
|
||||
uri: worker.nanoriver.cc
|
||||
useSSL: true
|
||||
useSSL: true
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
UI enhancements
|
||||
Bug fixes
|
||||
Bug fixes and generic enhancements
|
|
@ -1,7 +1,3 @@
|
|||
Add Replace-By-Fee to boost pending Bitcoin transactions
|
||||
Enable WalletConnect for Solana
|
||||
WalletConnect Enhancements
|
||||
Enhancements for ERC-20 tokens and Solana tokens
|
||||
Enhancements for Nano wallet
|
||||
UI enhancements
|
||||
Add Tron wallet
|
||||
Hardware wallets enhancements
|
||||
Bug fixes
|
8
assets/tron_node_list.yml
Normal file
8
assets/tron_node_list.yml
Normal file
|
@ -0,0 +1,8 @@
|
|||
-
|
||||
uri: tron-rpc.publicnode.com:443
|
||||
is_default: true
|
||||
useSSL: true
|
||||
-
|
||||
uri: api.trongrid.io
|
||||
is_default: false
|
||||
useSSL: true
|
43
cw_bitcoin/lib/bitcoin_hardware_wallet_service.dart
Normal file
43
cw_bitcoin/lib/bitcoin_hardware_wallet_service.dart
Normal file
|
@ -0,0 +1,43 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart';
|
||||
import 'package:cw_bitcoin/utils.dart';
|
||||
import 'package:cw_core/hardware/hardware_account_data.dart';
|
||||
import 'package:ledger_bitcoin/ledger_bitcoin.dart';
|
||||
import 'package:ledger_flutter/ledger_flutter.dart';
|
||||
|
||||
class BitcoinHardwareWalletService {
|
||||
BitcoinHardwareWalletService(this.ledger, this.device);
|
||||
|
||||
final Ledger ledger;
|
||||
final LedgerDevice device;
|
||||
|
||||
Future<List<HardwareAccountData>> getAvailableAccounts({int index = 0, int limit = 5}) async {
|
||||
final bitcoinLedgerApp = BitcoinLedgerApp(ledger);
|
||||
|
||||
final masterFp = await bitcoinLedgerApp.getMasterFingerprint(device);
|
||||
print(masterFp);
|
||||
|
||||
final accounts = <HardwareAccountData>[];
|
||||
final indexRange = List.generate(limit, (i) => i + index);
|
||||
|
||||
for (final i in indexRange) {
|
||||
final derivationPath = "m/84'/0'/$i'";
|
||||
final xpub = await bitcoinLedgerApp.getXPubKey(device, derivationPath: derivationPath);
|
||||
HDWallet hd = HDWallet.fromBase58(xpub).derive(0);
|
||||
|
||||
final address = generateP2WPKHAddress(hd: hd, index: 0, network: BitcoinNetwork.mainnet);
|
||||
|
||||
accounts.add(HardwareAccountData(
|
||||
address: address,
|
||||
accountIndex: i,
|
||||
derivationPath: derivationPath,
|
||||
masterFingerprint: masterFp,
|
||||
xpub: xpub,
|
||||
));
|
||||
}
|
||||
|
||||
return accounts;
|
||||
}
|
||||
}
|
|
@ -90,8 +90,7 @@ List<bool> prefixMatches(String source, List<String> prefixes) {
|
|||
return prefixes.map((prefix) => hx.startsWith(prefix.toLowerCase())).toList();
|
||||
}
|
||||
|
||||
Future<String> generateMnemonic(
|
||||
{int strength = 264, String prefix = segwit}) async {
|
||||
Future<String> generateElectrumMnemonic({int strength = 264, String prefix = segwit}) async {
|
||||
final wordBitlen = logBase(wordlist.length, 2).ceil();
|
||||
final wordCount = strength / wordBitlen;
|
||||
final byteCount = ((wordCount * wordBitlen).ceil() / 8).ceil();
|
||||
|
@ -106,22 +105,29 @@ Future<String> generateMnemonic(
|
|||
return result;
|
||||
}
|
||||
|
||||
Future<bool> checkIfMnemonicIsElectrum2(String mnemonic) async {
|
||||
return prefixMatches(mnemonic, [segwit]).first;
|
||||
}
|
||||
|
||||
Future<String> getMnemonicHash(String mnemonic) async {
|
||||
final hmacSha512 = Hmac(sha512, utf8.encode('Seed version'));
|
||||
final digest = hmacSha512.convert(utf8.encode(normalizeText(mnemonic)));
|
||||
final hx = digest.toString();
|
||||
return hx;
|
||||
}
|
||||
|
||||
Future<Uint8List> mnemonicToSeedBytes(String mnemonic, {String prefix = segwit}) async {
|
||||
final pbkdf2 = cryptography.Pbkdf2(
|
||||
macAlgorithm: cryptography.Hmac.sha512(),
|
||||
iterations: 2048,
|
||||
bits: 512);
|
||||
final pbkdf2 =
|
||||
cryptography.Pbkdf2(macAlgorithm: cryptography.Hmac.sha512(), iterations: 2048, bits: 512);
|
||||
final text = normalizeText(mnemonic);
|
||||
// pbkdf2.deriveKey(secretKey: secretKey, nonce: nonce)
|
||||
final key = await pbkdf2.deriveKey(
|
||||
secretKey: cryptography.SecretKey(text.codeUnits),
|
||||
nonce: 'electrum'.codeUnits);
|
||||
secretKey: cryptography.SecretKey(text.codeUnits), nonce: 'electrum'.codeUnits);
|
||||
final bytes = await key.extractBytes();
|
||||
return Uint8List.fromList(bytes);
|
||||
}
|
||||
|
||||
bool matchesAnyPrefix(String mnemonic) =>
|
||||
prefixMatches(mnemonic, [segwit]).any((el) => el);
|
||||
bool matchesAnyPrefix(String mnemonic) => prefixMatches(mnemonic, [segwit]).any((el) => el);
|
||||
|
||||
bool validateMnemonic(String mnemonic, {String prefix = segwit}) {
|
||||
try {
|
||||
|
@ -208,10 +214,8 @@ String removeCJKSpaces(String source) {
|
|||
}
|
||||
|
||||
String normalizeText(String source) {
|
||||
final res = removeCombiningCharacters(unorm.nfkd(source).toLowerCase())
|
||||
.trim()
|
||||
.split('/\s+/')
|
||||
.join(' ');
|
||||
final res =
|
||||
removeCombiningCharacters(unorm.nfkd(source).toLowerCase()).trim().split('/\s+/').join(' ');
|
||||
|
||||
return removeCJKSpaces(res);
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ class BitcoinTransactionPriority extends TransactionPriority {
|
|||
|
||||
switch (this) {
|
||||
case BitcoinTransactionPriority.slow:
|
||||
label = 'Slow ~24hrs'; // '${S.current.transaction_priority_slow} ~24hrs';
|
||||
label = 'Slow ~24hrs+'; // '${S.current.transaction_priority_slow} ~24hrs';
|
||||
break;
|
||||
case BitcoinTransactionPriority.medium:
|
||||
label = 'Medium'; // S.current.transaction_priority_medium;
|
||||
|
|
|
@ -1,17 +1,24 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:bip39/bip39.dart' as bip39;
|
||||
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||
import 'package:convert/convert.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_wallet_addresses.dart';
|
||||
import 'package:cw_bitcoin/electrum_balance.dart';
|
||||
import 'package:cw_bitcoin/electrum_wallet.dart';
|
||||
import 'package:cw_bitcoin/electrum_wallet_snapshot.dart';
|
||||
import 'package:cw_bitcoin/psbt_transaction_builder.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/unspent_coins_info.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||
import 'package:cw_bitcoin/electrum_wallet_snapshot.dart';
|
||||
import 'package:cw_bitcoin/electrum_wallet.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
||||
import 'package:cw_bitcoin/electrum_balance.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_wallet_addresses.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:ledger_bitcoin/ledger_bitcoin.dart';
|
||||
import 'package:ledger_flutter/ledger_flutter.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
part 'bitcoin_wallet.g.dart';
|
||||
|
||||
|
@ -19,31 +26,40 @@ class BitcoinWallet = BitcoinWalletBase with _$BitcoinWallet;
|
|||
|
||||
abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||
BitcoinWalletBase({
|
||||
required String mnemonic,
|
||||
required String password,
|
||||
required WalletInfo walletInfo,
|
||||
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
||||
required Uint8List seedBytes,
|
||||
Uint8List? seedBytes,
|
||||
String? mnemonic,
|
||||
String? xpub,
|
||||
String? addressPageType,
|
||||
BasedUtxoNetwork? networkParam,
|
||||
List<BitcoinAddressRecord>? initialAddresses,
|
||||
ElectrumBalance? initialBalance,
|
||||
Map<String, int>? initialRegularAddressIndex,
|
||||
Map<String, int>? initialChangeAddressIndex,
|
||||
String? passphrase,
|
||||
}) : super(
|
||||
mnemonic: mnemonic,
|
||||
password: password,
|
||||
walletInfo: walletInfo,
|
||||
unspentCoinsInfo: unspentCoinsInfo,
|
||||
networkType: networkParam == null
|
||||
? bitcoin.bitcoin
|
||||
: networkParam == BitcoinNetwork.mainnet
|
||||
? bitcoin.bitcoin
|
||||
: bitcoin.testnet,
|
||||
initialAddresses: initialAddresses,
|
||||
initialBalance: initialBalance,
|
||||
seedBytes: seedBytes,
|
||||
currency: CryptoCurrency.btc) {
|
||||
mnemonic: mnemonic,
|
||||
passphrase: passphrase,
|
||||
xpub: xpub,
|
||||
password: password,
|
||||
walletInfo: walletInfo,
|
||||
unspentCoinsInfo: unspentCoinsInfo,
|
||||
networkType: networkParam == null
|
||||
? bitcoin.bitcoin
|
||||
: networkParam == BitcoinNetwork.mainnet
|
||||
? bitcoin.bitcoin
|
||||
: bitcoin.testnet,
|
||||
initialAddresses: initialAddresses,
|
||||
initialBalance: initialBalance,
|
||||
seedBytes: seedBytes,
|
||||
currency: CryptoCurrency.btc) {
|
||||
// in a standard BIP44 wallet, mainHd derivation path = m/84'/0'/0'/0 (account 0, index unspecified here)
|
||||
// the sideHd derivation path = m/84'/0'/0'/1 (account 1, index unspecified here)
|
||||
// String derivationPath = walletInfo.derivationInfo!.derivationPath!;
|
||||
// String sideDerivationPath = derivationPath.substring(0, derivationPath.length - 1) + "1";
|
||||
// final hd = bitcoin.HDWallet.fromSeed(seedBytes, network: networkType);
|
||||
walletAddresses = BitcoinWalletAddresses(
|
||||
walletInfo,
|
||||
electrumClient: electrumClient,
|
||||
|
@ -51,7 +67,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
|||
initialRegularAddressIndex: initialRegularAddressIndex,
|
||||
initialChangeAddressIndex: initialChangeAddressIndex,
|
||||
mainHd: hd,
|
||||
sideHd: bitcoin.HDWallet.fromSeed(seedBytes, network: networkType).derivePath("m/0'/1"),
|
||||
sideHd: accountHD.derive(1),
|
||||
network: networkParam ?? network,
|
||||
);
|
||||
autorun((_) {
|
||||
|
@ -64,6 +80,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
|||
required String password,
|
||||
required WalletInfo walletInfo,
|
||||
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
||||
String? passphrase,
|
||||
String? addressPageType,
|
||||
BasedUtxoNetwork? network,
|
||||
List<BitcoinAddressRecord>? initialAddresses,
|
||||
|
@ -71,14 +88,29 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
|||
Map<String, int>? initialRegularAddressIndex,
|
||||
Map<String, int>? initialChangeAddressIndex,
|
||||
}) async {
|
||||
late Uint8List seedBytes;
|
||||
|
||||
switch (walletInfo.derivationInfo?.derivationType) {
|
||||
case DerivationType.bip39:
|
||||
seedBytes = await bip39.mnemonicToSeed(
|
||||
mnemonic,
|
||||
passphrase: passphrase ?? "",
|
||||
);
|
||||
break;
|
||||
case DerivationType.electrum:
|
||||
default:
|
||||
seedBytes = await mnemonicToSeedBytes(mnemonic);
|
||||
break;
|
||||
}
|
||||
return BitcoinWallet(
|
||||
mnemonic: mnemonic,
|
||||
passphrase: passphrase ?? "",
|
||||
password: password,
|
||||
walletInfo: walletInfo,
|
||||
unspentCoinsInfo: unspentCoinsInfo,
|
||||
initialAddresses: initialAddresses,
|
||||
initialBalance: initialBalance,
|
||||
seedBytes: await mnemonicToSeedBytes(mnemonic),
|
||||
seedBytes: seedBytes,
|
||||
initialRegularAddressIndex: initialRegularAddressIndex,
|
||||
initialChangeAddressIndex: initialChangeAddressIndex,
|
||||
addressPageType: addressPageType,
|
||||
|
@ -97,18 +129,110 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
|||
: BitcoinNetwork.mainnet;
|
||||
final snp = await ElectrumWalletSnapshot.load(name, walletInfo.type, password, network);
|
||||
|
||||
walletInfo.derivationInfo ??= DerivationInfo(
|
||||
derivationType: snp.derivationType ?? DerivationType.electrum,
|
||||
derivationPath: snp.derivationPath,
|
||||
);
|
||||
|
||||
// set the default if not present:
|
||||
walletInfo.derivationInfo!.derivationPath = snp.derivationPath ?? "m/0'/0";
|
||||
walletInfo.derivationInfo!.derivationType = snp.derivationType ?? DerivationType.electrum;
|
||||
|
||||
Uint8List? seedBytes = null;
|
||||
|
||||
if (snp.mnemonic != null) {
|
||||
switch (walletInfo.derivationInfo!.derivationType) {
|
||||
case DerivationType.electrum:
|
||||
seedBytes = await mnemonicToSeedBytes(snp.mnemonic!);
|
||||
break;
|
||||
case DerivationType.bip39:
|
||||
default:
|
||||
seedBytes = await bip39.mnemonicToSeed(
|
||||
snp.mnemonic!,
|
||||
passphrase: snp.passphrase ?? '',
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return BitcoinWallet(
|
||||
mnemonic: snp.mnemonic,
|
||||
xpub: snp.xpub,
|
||||
password: password,
|
||||
passphrase: snp.passphrase,
|
||||
walletInfo: walletInfo,
|
||||
unspentCoinsInfo: unspentCoinsInfo,
|
||||
initialAddresses: snp.addresses,
|
||||
initialBalance: snp.balance,
|
||||
seedBytes: await mnemonicToSeedBytes(snp.mnemonic),
|
||||
seedBytes: seedBytes,
|
||||
initialRegularAddressIndex: snp.regularAddressIndex,
|
||||
initialChangeAddressIndex: snp.changeAddressIndex,
|
||||
addressPageType: snp.addressPageType,
|
||||
networkParam: network,
|
||||
);
|
||||
}
|
||||
|
||||
Ledger? _ledger;
|
||||
LedgerDevice? _ledgerDevice;
|
||||
BitcoinLedgerApp? _bitcoinLedgerApp;
|
||||
|
||||
void setLedger(Ledger setLedger, LedgerDevice setLedgerDevice) {
|
||||
_ledger = setLedger;
|
||||
_ledgerDevice = setLedgerDevice;
|
||||
_bitcoinLedgerApp = BitcoinLedgerApp(_ledger!, derivationPath: walletInfo.derivationInfo!.derivationPath!);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<BtcTransaction> buildHardwareWalletTransaction({
|
||||
required List<BitcoinBaseOutput> outputs,
|
||||
required BigInt fee,
|
||||
required BasedUtxoNetwork network,
|
||||
required List<UtxoWithAddress> utxos,
|
||||
required Map<String, PublicKeyWithDerivationPath> publicKeys,
|
||||
String? memo,
|
||||
bool enableRBF = false,
|
||||
BitcoinOrdering inputOrdering = BitcoinOrdering.bip69,
|
||||
BitcoinOrdering outputOrdering = BitcoinOrdering.bip69,
|
||||
}) async {
|
||||
final masterFingerprint = await _bitcoinLedgerApp!.getMasterFingerprint(_ledgerDevice!);
|
||||
|
||||
final psbtReadyInputs = <PSBTReadyUtxoWithAddress>[];
|
||||
for (final utxo in utxos) {
|
||||
final rawTx = await electrumClient.getTransactionHex(hash: utxo.utxo.txHash);
|
||||
final publicKeyAndDerivationPath = publicKeys[utxo.ownerDetails.address.pubKeyHash()]!;
|
||||
|
||||
psbtReadyInputs.add(PSBTReadyUtxoWithAddress(
|
||||
utxo: utxo.utxo,
|
||||
rawTx: rawTx,
|
||||
ownerDetails: utxo.ownerDetails,
|
||||
ownerDerivationPath: publicKeyAndDerivationPath.derivationPath,
|
||||
ownerMasterFingerprint: masterFingerprint,
|
||||
ownerPublicKey: publicKeyAndDerivationPath.publicKey,
|
||||
));
|
||||
}
|
||||
|
||||
final psbt = PSBTTransactionBuild(inputs: psbtReadyInputs, outputs: outputs, enableRBF: enableRBF);
|
||||
|
||||
final rawHex = await _bitcoinLedgerApp!.signPsbt(_ledgerDevice!, psbt: psbt.psbt);
|
||||
return BtcTransaction.fromRaw(hex.encode(rawHex));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> signMessage(String message, {String? address = null}) async {
|
||||
if (walletInfo.isHardwareWallet) {
|
||||
final addressEntry = address != null
|
||||
? walletAddresses.allAddresses.firstWhere((element) => element.address == address)
|
||||
: null;
|
||||
final index = addressEntry?.index ?? 0;
|
||||
final isChange = addressEntry?.isHidden == true ? 1 : 0;
|
||||
final accountPath = walletInfo.derivationInfo?.derivationPath;
|
||||
final derivationPath = accountPath != null ? "$accountPath/$isChange/$index" : null;
|
||||
|
||||
final signature = await _bitcoinLedgerApp!
|
||||
.signMessage(_ledgerDevice!, message: ascii.encode(message), signDerivationPath: derivationPath);
|
||||
return base64Encode(signature);
|
||||
}
|
||||
|
||||
return super.signMessage(message, address: address);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,23 +1,58 @@
|
|||
import 'package:cw_core/hardware/hardware_account_data.dart';
|
||||
import 'package:cw_core/wallet_credentials.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
|
||||
class BitcoinNewWalletCredentials extends WalletCredentials {
|
||||
BitcoinNewWalletCredentials({required String name, WalletInfo? walletInfo})
|
||||
: super(name: name, walletInfo: walletInfo);
|
||||
BitcoinNewWalletCredentials(
|
||||
{required String name,
|
||||
WalletInfo? walletInfo,
|
||||
DerivationType? derivationType,
|
||||
String? derivationPath})
|
||||
: super(
|
||||
name: name,
|
||||
walletInfo: walletInfo,
|
||||
);
|
||||
}
|
||||
|
||||
class BitcoinRestoreWalletFromSeedCredentials extends WalletCredentials {
|
||||
BitcoinRestoreWalletFromSeedCredentials(
|
||||
{required String name, required String password, required this.mnemonic, WalletInfo? walletInfo})
|
||||
: super(name: name, password: password, walletInfo: walletInfo);
|
||||
BitcoinRestoreWalletFromSeedCredentials({
|
||||
required String name,
|
||||
required String password,
|
||||
required this.mnemonic,
|
||||
WalletInfo? walletInfo,
|
||||
required DerivationType derivationType,
|
||||
required String derivationPath,
|
||||
String? passphrase,
|
||||
}) : super(
|
||||
name: name,
|
||||
password: password,
|
||||
passphrase: passphrase,
|
||||
walletInfo: walletInfo,
|
||||
derivationInfo: DerivationInfo(
|
||||
derivationType: derivationType,
|
||||
derivationPath: derivationPath,
|
||||
));
|
||||
|
||||
final String mnemonic;
|
||||
}
|
||||
|
||||
class BitcoinRestoreWalletFromWIFCredentials extends WalletCredentials {
|
||||
BitcoinRestoreWalletFromWIFCredentials(
|
||||
{required String name, required String password, required this.wif, WalletInfo? walletInfo})
|
||||
: super(name: name, password: password, walletInfo: walletInfo);
|
||||
BitcoinRestoreWalletFromWIFCredentials({
|
||||
required String name,
|
||||
required String password,
|
||||
required this.wif,
|
||||
WalletInfo? walletInfo,
|
||||
}) : super(name: name, password: password, walletInfo: walletInfo);
|
||||
|
||||
final String wif;
|
||||
}
|
||||
}
|
||||
|
||||
class BitcoinRestoreWalletFromHardware extends WalletCredentials {
|
||||
BitcoinRestoreWalletFromHardware({
|
||||
required String name,
|
||||
required this.hwAccountData,
|
||||
WalletInfo? walletInfo,
|
||||
}) : super(name: name, walletInfo: walletInfo);
|
||||
|
||||
final HardwareAccountData hwAccountData;
|
||||
}
|
||||
|
|
|
@ -12,9 +12,13 @@ import 'package:cw_core/wallet_info.dart';
|
|||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:bip39/bip39.dart' as bip39;
|
||||
|
||||
class BitcoinWalletService extends WalletService<BitcoinNewWalletCredentials,
|
||||
BitcoinRestoreWalletFromSeedCredentials, BitcoinRestoreWalletFromWIFCredentials> {
|
||||
class BitcoinWalletService extends WalletService<
|
||||
BitcoinNewWalletCredentials,
|
||||
BitcoinRestoreWalletFromSeedCredentials,
|
||||
BitcoinRestoreWalletFromWIFCredentials,
|
||||
BitcoinRestoreWalletFromHardware> {
|
||||
BitcoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
|
||||
|
||||
final Box<WalletInfo> walletInfoSource;
|
||||
|
@ -29,8 +33,9 @@ class BitcoinWalletService extends WalletService<BitcoinNewWalletCredentials,
|
|||
credentials.walletInfo?.network = network.value;
|
||||
|
||||
final wallet = await BitcoinWalletBase.create(
|
||||
mnemonic: await generateMnemonic(),
|
||||
mnemonic: await generateElectrumMnemonic(),
|
||||
password: credentials.password!,
|
||||
passphrase: credentials.passphrase,
|
||||
walletInfo: credentials.walletInfo!,
|
||||
unspentCoinsInfo: unspentCoinsInfoSource,
|
||||
network: network,
|
||||
|
@ -97,15 +102,34 @@ class BitcoinWalletService extends WalletService<BitcoinNewWalletCredentials,
|
|||
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<BitcoinWallet> restoreFromHardwareWallet(BitcoinRestoreWalletFromHardware credentials,
|
||||
{bool? isTestnet}) async {
|
||||
|
||||
final network = isTestnet == true ? BitcoinNetwork.testnet : BitcoinNetwork.mainnet;
|
||||
credentials.walletInfo?.network = network.value;
|
||||
credentials.walletInfo?.derivationInfo?.derivationPath = credentials.hwAccountData.derivationPath;
|
||||
|
||||
final wallet = await BitcoinWallet(password: credentials.password!,
|
||||
xpub: credentials.hwAccountData.xpub,
|
||||
walletInfo: credentials.walletInfo!,
|
||||
unspentCoinsInfo: unspentCoinsInfoSource,
|
||||
networkParam: network,
|
||||
);
|
||||
await wallet.save();
|
||||
await wallet.init();
|
||||
return wallet;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<BitcoinWallet> restoreFromKeys(BitcoinRestoreWalletFromWIFCredentials credentials,
|
||||
{bool? isTestnet}) async =>
|
||||
{bool? isTestnet}) async =>
|
||||
throw UnimplementedError();
|
||||
|
||||
@override
|
||||
Future<BitcoinWallet> restoreFromSeed(BitcoinRestoreWalletFromSeedCredentials credentials,
|
||||
{bool? isTestnet}) async {
|
||||
if (!validateMnemonic(credentials.mnemonic)) {
|
||||
if (!validateMnemonic(credentials.mnemonic) && !bip39.validateMnemonic(credentials.mnemonic)) {
|
||||
throw BitcoinMnemonicIsIncorrectException();
|
||||
}
|
||||
|
||||
|
@ -114,6 +138,7 @@ class BitcoinWalletService extends WalletService<BitcoinNewWalletCredentials,
|
|||
|
||||
final wallet = await BitcoinWalletBase.create(
|
||||
password: credentials.password!,
|
||||
passphrase: credentials.passphrase,
|
||||
mnemonic: credentials.mnemonic,
|
||||
walletInfo: credentials.walletInfo!,
|
||||
unspentCoinsInfo: unspentCoinsInfoSource,
|
||||
|
|
110
cw_bitcoin/lib/electrum_derivations.dart
Normal file
110
cw_bitcoin/lib/electrum_derivations.dart
Normal file
|
@ -0,0 +1,110 @@
|
|||
import 'package:cw_core/wallet_info.dart';
|
||||
|
||||
Map<DerivationType, List<DerivationInfo>> electrum_derivations = {
|
||||
DerivationType.electrum: [
|
||||
DerivationInfo(
|
||||
derivationType: DerivationType.electrum,
|
||||
derivationPath: "m/0'",
|
||||
description: "Electrum",
|
||||
scriptType: "p2wpkh",
|
||||
),
|
||||
],
|
||||
DerivationType.bip39: [
|
||||
DerivationInfo(
|
||||
derivationType: DerivationType.bip39,
|
||||
derivationPath: "m/44'/0'/0'",
|
||||
description: "Standard BIP44",
|
||||
scriptType: "p2pkh",
|
||||
),
|
||||
DerivationInfo(
|
||||
derivationType: DerivationType.bip39,
|
||||
derivationPath: "m/49'/0'/0'",
|
||||
description: "Standard BIP49 compatibility segwit",
|
||||
scriptType: "p2wpkh-p2sh",
|
||||
),
|
||||
DerivationInfo(
|
||||
derivationType: DerivationType.bip39,
|
||||
derivationPath: "m/84'/0'/0'",
|
||||
description: "Standard BIP84 native segwit",
|
||||
scriptType: "p2wpkh",
|
||||
),
|
||||
DerivationInfo(
|
||||
derivationType: DerivationType.bip39,
|
||||
derivationPath: "m/86'/0'/0'",
|
||||
description: "Standard BIP86 Taproot",
|
||||
scriptType: "p2tr",
|
||||
),
|
||||
DerivationInfo(
|
||||
derivationType: DerivationType.bip39,
|
||||
derivationPath: "m/0'",
|
||||
description: "Non-standard legacy",
|
||||
scriptType: "p2pkh",
|
||||
),
|
||||
DerivationInfo(
|
||||
derivationType: DerivationType.bip39,
|
||||
derivationPath: "m/0'",
|
||||
description: "Non-standard compatibility segwit",
|
||||
scriptType: "p2wpkh-p2sh",
|
||||
),
|
||||
DerivationInfo(
|
||||
derivationType: DerivationType.bip39,
|
||||
derivationPath: "m/0'",
|
||||
description: "Non-standard native segwit",
|
||||
scriptType: "p2wpkh",
|
||||
),
|
||||
DerivationInfo(
|
||||
derivationType: DerivationType.bip39,
|
||||
derivationPath: "m/44'/0'/0'",
|
||||
description: "Samourai Deposit",
|
||||
scriptType: "p2wpkh",
|
||||
),
|
||||
DerivationInfo(
|
||||
derivationType: DerivationType.bip39,
|
||||
derivationPath: "m/49'/0'/0'",
|
||||
description: "Samourai Deposit",
|
||||
scriptType: "p2wpkh",
|
||||
),
|
||||
DerivationInfo(
|
||||
derivationType: DerivationType.bip39,
|
||||
derivationPath: "m/84'/0'/2147483644'",
|
||||
description: "Samourai Bad Bank (toxic change)",
|
||||
scriptType: "p2wpkh",
|
||||
),
|
||||
DerivationInfo(
|
||||
derivationType: DerivationType.bip39,
|
||||
derivationPath: "m/84'/0'/2147483645'",
|
||||
description: "Samourai Whirlpool Pre Mix",
|
||||
scriptType: "p2wpkh",
|
||||
),
|
||||
DerivationInfo(
|
||||
derivationType: DerivationType.bip39,
|
||||
derivationPath: "m/84'/0'/2147483646'",
|
||||
description: "Samourai Whirlpool Post Mix",
|
||||
scriptType: "p2wpkh",
|
||||
),
|
||||
DerivationInfo(
|
||||
derivationType: DerivationType.bip39,
|
||||
derivationPath: "m/44'/0'/2147483647'",
|
||||
description: "Samourai Ricochet legacy",
|
||||
scriptType: "p2pkh",
|
||||
),
|
||||
DerivationInfo(
|
||||
derivationType: DerivationType.bip39,
|
||||
derivationPath: "m/49'/0'/2147483647'",
|
||||
description: "Samourai Ricochet compatibility segwit",
|
||||
scriptType: "p2wpkh-p2sh",
|
||||
),
|
||||
DerivationInfo(
|
||||
derivationType: DerivationType.bip39,
|
||||
derivationPath: "m/84'/0'/2147483647'",
|
||||
description: "Samourai Ricochet native segwit",
|
||||
scriptType: "p2wpkh",
|
||||
),
|
||||
DerivationInfo(
|
||||
derivationType: DerivationType.bip39,
|
||||
derivationPath: "m/84'/2'/0'",
|
||||
description: "Default Litecoin",
|
||||
scriptType: "p2wpkh",
|
||||
),
|
||||
],
|
||||
};
|
|
@ -4,8 +4,8 @@ import 'dart:io';
|
|||
import 'dart:math';
|
||||
|
||||
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||
import 'package:bitcoin_base/bitcoin_base.dart' as bitcoin_base;
|
||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:cw_bitcoin/address_from_output.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
||||
|
@ -37,9 +37,9 @@ import 'package:cw_core/wallet_base.dart';
|
|||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:rxdart/subjects.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
part 'electrum_wallet.g.dart';
|
||||
|
||||
|
@ -53,15 +53,16 @@ abstract class ElectrumWalletBase
|
|||
required WalletInfo walletInfo,
|
||||
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
||||
required this.networkType,
|
||||
required this.mnemonic,
|
||||
required Uint8List seedBytes,
|
||||
String? xpub,
|
||||
String? mnemonic,
|
||||
Uint8List? seedBytes,
|
||||
this.passphrase,
|
||||
List<BitcoinAddressRecord>? initialAddresses,
|
||||
ElectrumClient? electrumClient,
|
||||
ElectrumBalance? initialBalance,
|
||||
CryptoCurrency? currency})
|
||||
: hd = currency == CryptoCurrency.bch
|
||||
? bitcoinCashHDWallet(seedBytes)
|
||||
: bitcoin.HDWallet.fromSeed(seedBytes, network: networkType).derivePath("m/0'/0"),
|
||||
: accountHD =
|
||||
getAccountHDWallet(currency, networkType, seedBytes, xpub, walletInfo.derivationInfo),
|
||||
syncStatus = NotConnectedSyncStatus(),
|
||||
_password = password,
|
||||
_feeRates = <int>[],
|
||||
|
@ -78,20 +79,45 @@ abstract class ElectrumWalletBase
|
|||
this.unspentCoinsInfo = unspentCoinsInfo,
|
||||
this.network = _getNetwork(networkType, currency),
|
||||
this.isTestnet = networkType == bitcoin.testnet,
|
||||
this._mnemonic = mnemonic,
|
||||
super(walletInfo) {
|
||||
this.electrumClient = electrumClient ?? ElectrumClient();
|
||||
this.walletInfo = walletInfo;
|
||||
transactionHistory = ElectrumTransactionHistory(walletInfo: walletInfo, password: password);
|
||||
}
|
||||
|
||||
static bitcoin.HDWallet getAccountHDWallet(
|
||||
CryptoCurrency? currency,
|
||||
bitcoin.NetworkType networkType,
|
||||
Uint8List? seedBytes,
|
||||
String? xpub,
|
||||
DerivationInfo? derivationInfo) {
|
||||
if (seedBytes == null && xpub == null) {
|
||||
throw Exception(
|
||||
"To create a Wallet you need either a seed or an xpub. This should not happen");
|
||||
}
|
||||
|
||||
if (seedBytes != null) {
|
||||
return currency == CryptoCurrency.bch
|
||||
? bitcoinCashHDWallet(seedBytes)
|
||||
: bitcoin.HDWallet.fromSeed(seedBytes, network: networkType)
|
||||
.derivePath(_hardenedDerivationPath(derivationInfo?.derivationPath ?? "m/0'"));
|
||||
}
|
||||
|
||||
return bitcoin.HDWallet.fromBase58(xpub!);
|
||||
}
|
||||
|
||||
static bitcoin.HDWallet bitcoinCashHDWallet(Uint8List seedBytes) =>
|
||||
bitcoin.HDWallet.fromSeed(seedBytes).derivePath("m/44'/145'/0'/0");
|
||||
bitcoin.HDWallet.fromSeed(seedBytes).derivePath("m/44'/145'/0'");
|
||||
|
||||
static int estimatedTransactionSize(int inputsCount, int outputsCounts) =>
|
||||
inputsCount * 68 + outputsCounts * 34 + 10;
|
||||
|
||||
final bitcoin.HDWallet hd;
|
||||
final String mnemonic;
|
||||
final bitcoin.HDWallet accountHD;
|
||||
final String? _mnemonic;
|
||||
|
||||
bitcoin.HDWallet get hd => accountHD.derive(0);
|
||||
final String? passphrase;
|
||||
|
||||
@override
|
||||
@observable
|
||||
|
@ -120,10 +146,10 @@ abstract class ElectrumWalletBase
|
|||
.map((addr) => scriptHash(addr.address, network: network))
|
||||
.toList();
|
||||
|
||||
String get xpub => hd.base58!;
|
||||
String get xpub => accountHD.base58!;
|
||||
|
||||
@override
|
||||
String get seed => mnemonic;
|
||||
String? get seed => _mnemonic;
|
||||
|
||||
bitcoin.NetworkType networkType;
|
||||
BasedUtxoNetwork network;
|
||||
|
@ -200,22 +226,38 @@ abstract class ElectrumWalletBase
|
|||
int credentialsAmount = 0,
|
||||
}) async {
|
||||
final utxos = <UtxoWithAddress>[];
|
||||
List<ECPrivate> privateKeys = [];
|
||||
final privateKeys = <ECPrivate>[];
|
||||
final publicKeys = <String, PublicKeyWithDerivationPath>{};
|
||||
|
||||
int allInputsAmount = 0;
|
||||
|
||||
bool spendsUnconfirmedTX = false;
|
||||
|
||||
for (int i = 0; i < unspentCoins.length; i++) {
|
||||
final utx = unspentCoins[i];
|
||||
|
||||
if (utx.isSending) {
|
||||
if (utx.isSending && !utx.isFrozen) {
|
||||
if (!spendsUnconfirmedTX) spendsUnconfirmedTX = utx.confirmations == 0;
|
||||
|
||||
allInputsAmount += utx.value;
|
||||
|
||||
final address = addressTypeFromStr(utx.address, network);
|
||||
final privkey = generateECPrivate(
|
||||
hd: utx.bitcoinAddressRecord.isHidden ? walletAddresses.sideHd : walletAddresses.mainHd,
|
||||
index: utx.bitcoinAddressRecord.index,
|
||||
network: network);
|
||||
final hd =
|
||||
utx.bitcoinAddressRecord.isHidden ? walletAddresses.sideHd : walletAddresses.mainHd;
|
||||
final derivationPath =
|
||||
"${_hardenedDerivationPath(walletInfo.derivationInfo?.derivationPath ?? "m/0'")}"
|
||||
"/${utx.bitcoinAddressRecord.isHidden ? "1" : "0"}"
|
||||
"/${utx.bitcoinAddressRecord.index}";
|
||||
final pubKeyHex = hd.derive(utx.bitcoinAddressRecord.index).pubKey!;
|
||||
|
||||
privateKeys.add(privkey);
|
||||
publicKeys[address.pubKeyHash()] = PublicKeyWithDerivationPath(pubKeyHex, derivationPath);
|
||||
|
||||
if (!walletInfo.isHardwareWallet) {
|
||||
final privkey =
|
||||
generateECPrivate(hd: hd, index: utx.bitcoinAddressRecord.index, network: network);
|
||||
|
||||
privateKeys.add(privkey);
|
||||
}
|
||||
|
||||
utxos.add(
|
||||
UtxoWithAddress(
|
||||
|
@ -226,7 +268,7 @@ abstract class ElectrumWalletBase
|
|||
scriptType: _getScriptType(address),
|
||||
),
|
||||
ownerDetails: UtxoAddressDetails(
|
||||
publicKey: privkey.getPublic().toHex(),
|
||||
publicKey: pubKeyHex,
|
||||
address: address,
|
||||
),
|
||||
),
|
||||
|
@ -264,6 +306,10 @@ abstract class ElectrumWalletBase
|
|||
// Here, when sending all, the output amount equals to the input value - fee to fully spend every input on the transaction and have no amount left for change
|
||||
int amount = allInputsAmount - fee;
|
||||
|
||||
if (amount <= 0) {
|
||||
throw BitcoinTransactionWrongBalanceException();
|
||||
}
|
||||
|
||||
// Attempting to send less than the dust limit
|
||||
if (_isBelowDust(amount)) {
|
||||
throw BitcoinTransactionNoDustException();
|
||||
|
@ -283,11 +329,13 @@ abstract class ElectrumWalletBase
|
|||
return EstimatedTxResult(
|
||||
utxos: utxos,
|
||||
privateKeys: privateKeys,
|
||||
publicKeys: publicKeys,
|
||||
fee: fee,
|
||||
amount: amount,
|
||||
isSendAll: true,
|
||||
hasChange: false,
|
||||
memo: memo,
|
||||
spendsUnconfirmedTX: spendsUnconfirmedTX,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -297,27 +345,48 @@ abstract class ElectrumWalletBase
|
|||
int feeRate, {
|
||||
int? inputsCount,
|
||||
String? memo,
|
||||
bool? useUnconfirmed,
|
||||
}) async {
|
||||
final utxos = <UtxoWithAddress>[];
|
||||
List<ECPrivate> privateKeys = [];
|
||||
final privateKeys = <ECPrivate>[];
|
||||
final publicKeys = <String, PublicKeyWithDerivationPath>{};
|
||||
|
||||
int allInputsAmount = 0;
|
||||
bool spendsUnconfirmedTX = false;
|
||||
|
||||
int leftAmount = credentialsAmount;
|
||||
final sendingCoins = unspentCoins.where((utx) => utx.isSending).toList();
|
||||
final sendingCoins = unspentCoins.where((utx) => utx.isSending && !utx.isFrozen).toList();
|
||||
final unconfirmedCoins = sendingCoins.where((utx) => utx.confirmations == 0).toList();
|
||||
|
||||
for (int i = 0; i < sendingCoins.length; i++) {
|
||||
final utx = sendingCoins[i];
|
||||
|
||||
final isUncormirmed = utx.confirmations == 0;
|
||||
if (useUnconfirmed != true && isUncormirmed) continue;
|
||||
|
||||
if (!spendsUnconfirmedTX) spendsUnconfirmedTX = isUncormirmed;
|
||||
|
||||
allInputsAmount += utx.value;
|
||||
leftAmount = leftAmount - utx.value;
|
||||
|
||||
final address = addressTypeFromStr(utx.address, network);
|
||||
final privkey = generateECPrivate(
|
||||
hd: utx.bitcoinAddressRecord.isHidden ? walletAddresses.sideHd : walletAddresses.mainHd,
|
||||
index: utx.bitcoinAddressRecord.index,
|
||||
network: network);
|
||||
|
||||
privateKeys.add(privkey);
|
||||
final hd =
|
||||
utx.bitcoinAddressRecord.isHidden ? walletAddresses.sideHd : walletAddresses.mainHd;
|
||||
final derivationPath =
|
||||
"${_hardenedDerivationPath(walletInfo.derivationInfo?.derivationPath ?? "m/0'")}"
|
||||
"/${utx.bitcoinAddressRecord.isHidden ? "1" : "0"}"
|
||||
"/${utx.bitcoinAddressRecord.index}";
|
||||
final pubKeyHex = hd.derive(utx.bitcoinAddressRecord.index).pubKey!;
|
||||
|
||||
publicKeys[address.pubKeyHash()] = PublicKeyWithDerivationPath(pubKeyHex, derivationPath);
|
||||
|
||||
if (!walletInfo.isHardwareWallet) {
|
||||
final privkey =
|
||||
generateECPrivate(hd: hd, index: utx.bitcoinAddressRecord.index, network: network);
|
||||
|
||||
privateKeys.add(privkey);
|
||||
}
|
||||
|
||||
utxos.add(
|
||||
UtxoWithAddress(
|
||||
|
@ -328,7 +397,7 @@ abstract class ElectrumWalletBase
|
|||
scriptType: _getScriptType(address),
|
||||
),
|
||||
ownerDetails: UtxoAddressDetails(
|
||||
publicKey: privkey.getPublic().toHex(),
|
||||
publicKey: pubKeyHex,
|
||||
address: address,
|
||||
),
|
||||
),
|
||||
|
@ -345,11 +414,23 @@ abstract class ElectrumWalletBase
|
|||
}
|
||||
|
||||
final spendingAllCoins = sendingCoins.length == utxos.length;
|
||||
final spendingAllConfirmedCoins =
|
||||
!spendsUnconfirmedTX && utxos.length == sendingCoins.length - unconfirmedCoins.length;
|
||||
|
||||
// How much is being spent - how much is being sent
|
||||
int amountLeftForChangeAndFee = allInputsAmount - credentialsAmount;
|
||||
|
||||
if (amountLeftForChangeAndFee <= 0) {
|
||||
if (!spendingAllCoins) {
|
||||
return estimateTxForAmount(
|
||||
credentialsAmount,
|
||||
outputs,
|
||||
feeRate,
|
||||
inputsCount: utxos.length + 1,
|
||||
memo: memo,
|
||||
useUnconfirmed: useUnconfirmed ?? spendingAllConfirmedCoins,
|
||||
);
|
||||
}
|
||||
throw BitcoinTransactionWrongBalanceException();
|
||||
}
|
||||
|
||||
|
@ -403,6 +484,7 @@ abstract class ElectrumWalletBase
|
|||
feeRate,
|
||||
inputsCount: utxos.length + 1,
|
||||
memo: memo,
|
||||
useUnconfirmed: useUnconfirmed ?? spendingAllConfirmedCoins,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -449,6 +531,7 @@ abstract class ElectrumWalletBase
|
|||
feeRate,
|
||||
inputsCount: utxos.length + 1,
|
||||
memo: memo,
|
||||
useUnconfirmed: useUnconfirmed ?? spendingAllConfirmedCoins,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -456,11 +539,13 @@ abstract class ElectrumWalletBase
|
|||
return EstimatedTxResult(
|
||||
utxos: utxos,
|
||||
privateKeys: privateKeys,
|
||||
publicKeys: publicKeys,
|
||||
fee: fee,
|
||||
amount: amount,
|
||||
hasChange: true,
|
||||
isSendAll: false,
|
||||
memo: memo,
|
||||
spendsUnconfirmedTX: spendsUnconfirmedTX,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -522,6 +607,35 @@ abstract class ElectrumWalletBase
|
|||
);
|
||||
}
|
||||
|
||||
if (walletInfo.isHardwareWallet) {
|
||||
final transaction = await buildHardwareWalletTransaction(
|
||||
utxos: estimatedTx.utxos,
|
||||
outputs: outputs,
|
||||
publicKeys: estimatedTx.publicKeys,
|
||||
fee: BigInt.from(estimatedTx.fee),
|
||||
network: network,
|
||||
memo: estimatedTx.memo,
|
||||
outputOrdering: BitcoinOrdering.none,
|
||||
enableRBF: true,
|
||||
);
|
||||
|
||||
return PendingBitcoinTransaction(
|
||||
transaction,
|
||||
type,
|
||||
electrumClient: electrumClient,
|
||||
amount: estimatedTx.amount,
|
||||
fee: estimatedTx.fee,
|
||||
feeRate: feeRateInt.toString(),
|
||||
network: network,
|
||||
hasChange: estimatedTx.hasChange,
|
||||
isSendAll: estimatedTx.isSendAll,
|
||||
hasTaprootInputs: false, // ToDo: (Konsti) Support Taproot
|
||||
)..addListener((transaction) async {
|
||||
transactionHistory.addOne(transaction);
|
||||
await updateBalance();
|
||||
});
|
||||
}
|
||||
|
||||
BasedBitcoinTransacationBuilder txb;
|
||||
if (network is BitcoinCashNetwork) {
|
||||
txb = ForkedTransactionBuilder(
|
||||
|
@ -531,7 +645,7 @@ abstract class ElectrumWalletBase
|
|||
network: network,
|
||||
memo: estimatedTx.memo,
|
||||
outputOrdering: BitcoinOrdering.none,
|
||||
enableRBF: true,
|
||||
enableRBF: !estimatedTx.spendsUnconfirmedTX,
|
||||
);
|
||||
} else {
|
||||
txb = BitcoinTransactionBuilder(
|
||||
|
@ -541,7 +655,7 @@ abstract class ElectrumWalletBase
|
|||
network: network,
|
||||
memo: estimatedTx.memo,
|
||||
outputOrdering: BitcoinOrdering.none,
|
||||
enableRBF: true,
|
||||
enableRBF: !estimatedTx.spendsUnconfirmedTX,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -583,8 +697,23 @@ abstract class ElectrumWalletBase
|
|||
}
|
||||
}
|
||||
|
||||
Future<BtcTransaction> buildHardwareWalletTransaction({
|
||||
required List<BitcoinBaseOutput> outputs,
|
||||
required BigInt fee,
|
||||
required BasedUtxoNetwork network,
|
||||
required List<UtxoWithAddress> utxos,
|
||||
required Map<String, PublicKeyWithDerivationPath> publicKeys,
|
||||
String? memo,
|
||||
bool enableRBF = false,
|
||||
BitcoinOrdering inputOrdering = BitcoinOrdering.bip69,
|
||||
BitcoinOrdering outputOrdering = BitcoinOrdering.bip69,
|
||||
}) async =>
|
||||
throw UnimplementedError();
|
||||
|
||||
String toJSON() => json.encode({
|
||||
'mnemonic': mnemonic,
|
||||
'mnemonic': _mnemonic,
|
||||
'xpub': xpub,
|
||||
'passphrase': passphrase ?? '',
|
||||
'account_index': walletAddresses.currentReceiveAddressIndexByType,
|
||||
'change_address_index': walletAddresses.currentChangeAddressIndexByType,
|
||||
'addresses': walletAddresses.allAddresses.map((addr) => addr.toJSON()).toList(),
|
||||
|
@ -592,6 +721,8 @@ abstract class ElectrumWalletBase
|
|||
? SegwitAddresType.p2wpkh.toString()
|
||||
: walletInfo.addressPageType.toString(),
|
||||
'balance': balance[currency]?.toJSON(),
|
||||
'derivationTypeIndex': walletInfo.derivationInfo?.derivationType?.index,
|
||||
'derivationPath': walletInfo.derivationInfo?.derivationPath,
|
||||
});
|
||||
|
||||
int feeRate(TransactionPriority priority) {
|
||||
|
@ -721,6 +852,7 @@ abstract class ElectrumWalletBase
|
|||
final tx = await fetchTransactionInfo(
|
||||
hash: coin.hash, height: 0, myAddresses: addressesSet);
|
||||
coin.isChange = tx?.direction == TransactionDirection.outgoing;
|
||||
coin.confirmations = tx?.confirmations;
|
||||
updatedUnspentCoins.add(coin);
|
||||
} catch (_) {}
|
||||
}))));
|
||||
|
@ -1037,9 +1169,11 @@ abstract class ElectrumWalletBase
|
|||
|
||||
return Future.wait(addressesByType.map((addressRecord) async {
|
||||
final history = await _fetchAddressHistory(addressRecord, addressesSet, currentHeight);
|
||||
final balance = await electrumClient.getBalance(addressRecord.scriptHash!);
|
||||
|
||||
if (history.isNotEmpty) {
|
||||
addressRecord.txCount = history.length;
|
||||
addressRecord.balance = balance['confirmed'] as int? ?? 0;
|
||||
historiesWithDetails.addAll(history);
|
||||
|
||||
final matchedAddresses =
|
||||
|
@ -1221,7 +1355,7 @@ abstract class ElectrumWalletBase
|
|||
void setExceptionHandler(void Function(FlutterErrorDetails) onError) => _onError = onError;
|
||||
|
||||
@override
|
||||
String signMessage(String message, {String? address = null}) {
|
||||
Future<String> signMessage(String message, {String? address = null}) async {
|
||||
final index = address != null
|
||||
? walletAddresses.allAddresses.firstWhere((element) => element.address == address).index
|
||||
: null;
|
||||
|
@ -1244,6 +1378,9 @@ abstract class ElectrumWalletBase
|
|||
|
||||
return BitcoinNetwork.mainnet;
|
||||
}
|
||||
|
||||
static String _hardenedDerivationPath(String derivationPath) =>
|
||||
derivationPath.substring(0, derivationPath.lastIndexOf("'") + 1);
|
||||
}
|
||||
|
||||
class EstimateTxParams {
|
||||
|
@ -1265,20 +1402,31 @@ class EstimatedTxResult {
|
|||
EstimatedTxResult({
|
||||
required this.utxos,
|
||||
required this.privateKeys,
|
||||
required this.publicKeys,
|
||||
required this.fee,
|
||||
required this.amount,
|
||||
required this.hasChange,
|
||||
required this.isSendAll,
|
||||
this.memo,
|
||||
required this.spendsUnconfirmedTX,
|
||||
});
|
||||
|
||||
final List<UtxoWithAddress> utxos;
|
||||
final List<ECPrivate> privateKeys;
|
||||
final Map<String, PublicKeyWithDerivationPath> publicKeys; // PubKey to derivationPath
|
||||
final int fee;
|
||||
final int amount;
|
||||
final bool hasChange;
|
||||
final bool isSendAll;
|
||||
final String? memo;
|
||||
final bool spendsUnconfirmedTX;
|
||||
}
|
||||
|
||||
class PublicKeyWithDerivationPath {
|
||||
const PublicKeyWithDerivationPath(this.publicKey, this.derivationPath);
|
||||
|
||||
final String derivationPath;
|
||||
final String publicKey;
|
||||
}
|
||||
|
||||
BitcoinBaseAddress addressTypeFromStr(String address, BasedUtxoNetwork network) {
|
||||
|
|
|
@ -3,6 +3,7 @@ import 'package:bitcoin_base/bitcoin_base.dart';
|
|||
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
||||
import 'package:cw_bitcoin/electrum_balance.dart';
|
||||
import 'package:cw_core/pathForWallet.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/utils/file.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
|
||||
|
@ -12,11 +13,15 @@ class ElectrumWalletSnapshot {
|
|||
required this.type,
|
||||
required this.password,
|
||||
required this.mnemonic,
|
||||
required this.xpub,
|
||||
required this.addresses,
|
||||
required this.balance,
|
||||
required this.regularAddressIndex,
|
||||
required this.changeAddressIndex,
|
||||
required this.addressPageType,
|
||||
this.passphrase,
|
||||
this.derivationType,
|
||||
this.derivationPath,
|
||||
});
|
||||
|
||||
final String name;
|
||||
|
@ -24,11 +29,15 @@ class ElectrumWalletSnapshot {
|
|||
final WalletType type;
|
||||
final String? addressPageType;
|
||||
|
||||
String mnemonic;
|
||||
String? mnemonic;
|
||||
String? xpub;
|
||||
List<BitcoinAddressRecord> addresses;
|
||||
ElectrumBalance balance;
|
||||
Map<String, int> regularAddressIndex;
|
||||
Map<String, int> changeAddressIndex;
|
||||
String? passphrase;
|
||||
DerivationType? derivationType;
|
||||
String? derivationPath;
|
||||
|
||||
static Future<ElectrumWalletSnapshot> load(
|
||||
String name, WalletType type, String password, BasedUtxoNetwork network) async {
|
||||
|
@ -36,7 +45,9 @@ class ElectrumWalletSnapshot {
|
|||
final jsonSource = await read(path: path, password: password);
|
||||
final data = json.decode(jsonSource) as Map;
|
||||
final addressesTmp = data['addresses'] as List? ?? <Object>[];
|
||||
final mnemonic = data['mnemonic'] as String;
|
||||
final mnemonic = data['mnemonic'] as String?;
|
||||
final xpub = data['xpub'] as String?;
|
||||
final passphrase = data['passphrase'] as String? ?? '';
|
||||
final addresses = addressesTmp
|
||||
.whereType<String>()
|
||||
.map((addr) => BitcoinAddressRecord.fromJSON(addr, network))
|
||||
|
@ -46,6 +57,10 @@ class ElectrumWalletSnapshot {
|
|||
var regularAddressIndexByType = {SegwitAddresType.p2wpkh.toString(): 0};
|
||||
var changeAddressIndexByType = {SegwitAddresType.p2wpkh.toString(): 0};
|
||||
|
||||
final derivationType =
|
||||
DerivationType.values[(data['derivationTypeIndex'] as int?) ?? DerivationType.electrum.index];
|
||||
final derivationPath = data['derivationPath'] as String? ?? "m/0'/0";
|
||||
|
||||
try {
|
||||
regularAddressIndexByType = {
|
||||
SegwitAddresType.p2wpkh.toString(): int.parse(data['account_index'] as String? ?? '0')
|
||||
|
@ -65,12 +80,16 @@ class ElectrumWalletSnapshot {
|
|||
name: name,
|
||||
type: type,
|
||||
password: password,
|
||||
passphrase: passphrase,
|
||||
mnemonic: mnemonic,
|
||||
xpub: xpub,
|
||||
addresses: addresses,
|
||||
balance: balance,
|
||||
regularAddressIndex: regularAddressIndexByType,
|
||||
changeAddressIndex: changeAddressIndexByType,
|
||||
addressPageType: data['address_page_type'] as String?,
|
||||
derivationType: derivationType,
|
||||
derivationPath: derivationPath,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,9 @@ class BitcoinTransactionNoDustOnChangeException extends TransactionNoDustOnChang
|
|||
BitcoinTransactionNoDustOnChangeException(super.max, super.min);
|
||||
}
|
||||
|
||||
class BitcoinTransactionCommitFailed extends TransactionCommitFailed {}
|
||||
class BitcoinTransactionCommitFailed extends TransactionCommitFailed {
|
||||
BitcoinTransactionCommitFailed({super.errorMessage});
|
||||
}
|
||||
|
||||
class BitcoinTransactionCommitFailedDustChange extends TransactionCommitFailedDustChange {}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ import 'package:cw_bitcoin/electrum_wallet.dart';
|
|||
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
||||
import 'package:cw_bitcoin/electrum_balance.dart';
|
||||
import 'package:cw_bitcoin/litecoin_network.dart';
|
||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||
import 'package:bip39/bip39.dart' as bip39;
|
||||
|
||||
part 'litecoin_wallet.g.dart';
|
||||
|
||||
|
@ -49,7 +49,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
|||
initialRegularAddressIndex: initialRegularAddressIndex,
|
||||
initialChangeAddressIndex: initialChangeAddressIndex,
|
||||
mainHd: hd,
|
||||
sideHd: bitcoin.HDWallet.fromSeed(seedBytes, network: networkType).derivePath("m/0'/1"),
|
||||
sideHd: accountHD.derive(1),
|
||||
network: network,
|
||||
);
|
||||
autorun((_) {
|
||||
|
@ -62,11 +62,26 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
|||
required String password,
|
||||
required WalletInfo walletInfo,
|
||||
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
||||
String? passphrase,
|
||||
String? addressPageType,
|
||||
List<BitcoinAddressRecord>? initialAddresses,
|
||||
ElectrumBalance? initialBalance,
|
||||
Map<String, int>? initialRegularAddressIndex,
|
||||
Map<String, int>? initialChangeAddressIndex}) async {
|
||||
late Uint8List seedBytes;
|
||||
|
||||
switch (walletInfo.derivationInfo?.derivationType) {
|
||||
case DerivationType.bip39:
|
||||
seedBytes = await bip39.mnemonicToSeed(
|
||||
mnemonic,
|
||||
passphrase: passphrase ?? "",
|
||||
);
|
||||
break;
|
||||
case DerivationType.electrum:
|
||||
default:
|
||||
seedBytes = await mnemonicToSeedBytes(mnemonic);
|
||||
break;
|
||||
}
|
||||
return LitecoinWallet(
|
||||
mnemonic: mnemonic,
|
||||
password: password,
|
||||
|
@ -74,7 +89,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
|||
unspentCoinsInfo: unspentCoinsInfo,
|
||||
initialAddresses: initialAddresses,
|
||||
initialBalance: initialBalance,
|
||||
seedBytes: await mnemonicToSeedBytes(mnemonic),
|
||||
seedBytes: seedBytes,
|
||||
initialRegularAddressIndex: initialRegularAddressIndex,
|
||||
initialChangeAddressIndex: initialChangeAddressIndex,
|
||||
addressPageType: addressPageType,
|
||||
|
@ -90,13 +105,13 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
|||
final snp =
|
||||
await ElectrumWalletSnapshot.load(name, walletInfo.type, password, LitecoinNetwork.mainnet);
|
||||
return LitecoinWallet(
|
||||
mnemonic: snp.mnemonic,
|
||||
mnemonic: snp.mnemonic!,
|
||||
password: password,
|
||||
walletInfo: walletInfo,
|
||||
unspentCoinsInfo: unspentCoinsInfo,
|
||||
initialAddresses: snp.addresses,
|
||||
initialBalance: snp.balance,
|
||||
seedBytes: await mnemonicToSeedBytes(snp.mnemonic),
|
||||
seedBytes: await mnemonicToSeedBytes(snp.mnemonic!),
|
||||
initialRegularAddressIndex: snp.regularAddressIndex,
|
||||
initialChangeAddressIndex: snp.changeAddressIndex,
|
||||
addressPageType: snp.addressPageType,
|
||||
|
|
|
@ -11,11 +11,12 @@ import 'package:cw_core/wallet_type.dart';
|
|||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:bip39/bip39.dart' as bip39;
|
||||
|
||||
class LitecoinWalletService extends WalletService<
|
||||
BitcoinNewWalletCredentials,
|
||||
BitcoinRestoreWalletFromSeedCredentials,
|
||||
BitcoinRestoreWalletFromWIFCredentials> {
|
||||
BitcoinRestoreWalletFromWIFCredentials,BitcoinNewWalletCredentials> {
|
||||
LitecoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
|
||||
|
||||
final Box<WalletInfo> walletInfoSource;
|
||||
|
@ -27,8 +28,9 @@ class LitecoinWalletService extends WalletService<
|
|||
@override
|
||||
Future<LitecoinWallet> create(BitcoinNewWalletCredentials credentials, {bool? isTestnet}) async {
|
||||
final wallet = await LitecoinWalletBase.create(
|
||||
mnemonic: await generateMnemonic(),
|
||||
mnemonic: await generateElectrumMnemonic(),
|
||||
password: credentials.password!,
|
||||
passphrase: credentials.passphrase,
|
||||
walletInfo: credentials.walletInfo!,
|
||||
unspentCoinsInfo: unspentCoinsInfoSource);
|
||||
await wallet.save();
|
||||
|
@ -92,6 +94,11 @@ class LitecoinWalletService extends WalletService<
|
|||
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LitecoinWallet> restoreFromHardwareWallet(BitcoinNewWalletCredentials credentials) {
|
||||
throw UnimplementedError("Restoring a Litecoin wallet from a hardware wallet is not yet supported!");
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LitecoinWallet> restoreFromKeys(
|
||||
BitcoinRestoreWalletFromWIFCredentials credentials, {bool? isTestnet}) async =>
|
||||
|
@ -100,12 +107,13 @@ class LitecoinWalletService extends WalletService<
|
|||
@override
|
||||
Future<LitecoinWallet> restoreFromSeed(
|
||||
BitcoinRestoreWalletFromSeedCredentials credentials, {bool? isTestnet}) async {
|
||||
if (!validateMnemonic(credentials.mnemonic)) {
|
||||
if (!validateMnemonic(credentials.mnemonic) && !bip39.validateMnemonic(credentials.mnemonic)) {
|
||||
throw LitecoinMnemonicIsIncorrectException();
|
||||
}
|
||||
|
||||
final wallet = await LitecoinWalletBase.create(
|
||||
password: credentials.password!,
|
||||
passphrase: credentials.passphrase,
|
||||
mnemonic: credentials.mnemonic,
|
||||
walletInfo: credentials.walletInfo!,
|
||||
unspentCoinsInfo: unspentCoinsInfoSource);
|
||||
|
|
|
@ -73,7 +73,9 @@ class PendingBitcoinTransaction with PendingTransaction {
|
|||
if (error.contains("bad-txns-vout-negative")) {
|
||||
throw BitcoinTransactionCommitFailedVoutNegative();
|
||||
}
|
||||
throw BitcoinTransactionCommitFailed(errorMessage: error);
|
||||
}
|
||||
|
||||
throw BitcoinTransactionCommitFailed();
|
||||
}
|
||||
|
||||
|
|
96
cw_bitcoin/lib/psbt_transaction_builder.dart
Normal file
96
cw_bitcoin/lib/psbt_transaction_builder.dart
Normal file
|
@ -0,0 +1,96 @@
|
|||
import 'dart:typed_data';
|
||||
|
||||
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||
import 'package:convert/convert.dart';
|
||||
import 'package:ledger_bitcoin/psbt.dart';
|
||||
|
||||
class PSBTTransactionBuild {
|
||||
final PsbtV2 psbt = PsbtV2();
|
||||
|
||||
PSBTTransactionBuild(
|
||||
{required List<PSBTReadyUtxoWithAddress> inputs, required List<BitcoinBaseOutput> outputs, bool enableRBF = true}) {
|
||||
psbt.setGlobalTxVersion(2);
|
||||
psbt.setGlobalInputCount(inputs.length);
|
||||
psbt.setGlobalOutputCount(outputs.length);
|
||||
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
final input = inputs[i];
|
||||
|
||||
print(input.utxo.isP2tr());
|
||||
print(input.utxo.isSegwit());
|
||||
print(input.utxo.isP2shSegwit());
|
||||
|
||||
psbt.setInputPreviousTxId(i, Uint8List.fromList(hex.decode(input.utxo.txHash).reversed.toList()));
|
||||
psbt.setInputOutputIndex(i, input.utxo.vout);
|
||||
psbt.setInputSequence(i, enableRBF ? 0x1 : 0xffffffff);
|
||||
|
||||
|
||||
if (input.utxo.isSegwit()) {
|
||||
setInputSegwit(i, input);
|
||||
} else if (input.utxo.isP2shSegwit()) {
|
||||
setInputP2shSegwit(i, input);
|
||||
} else if (input.utxo.isP2tr()) {
|
||||
// ToDo: (Konsti) Handle Taproot Inputs
|
||||
} else {
|
||||
setInputP2pkh(i, input);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < outputs.length; i++) {
|
||||
final output = outputs[i];
|
||||
|
||||
if (output is BitcoinOutput) {
|
||||
psbt.setOutputScript(i, Uint8List.fromList(output.address.toScriptPubKey().toBytes()));
|
||||
psbt.setOutputAmount(i, output.value.toInt());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setInputP2pkh(int i, PSBTReadyUtxoWithAddress input) {
|
||||
psbt.setInputNonWitnessUtxo(i, Uint8List.fromList(hex.decode(input.rawTx)));
|
||||
psbt.setInputBip32Derivation(
|
||||
i,
|
||||
Uint8List.fromList(hex.decode(input.ownerPublicKey)),
|
||||
input.ownerMasterFingerprint,
|
||||
BIPPath.fromString(input.ownerDerivationPath).toPathArray());
|
||||
}
|
||||
|
||||
void setInputSegwit(int i, PSBTReadyUtxoWithAddress input) {
|
||||
psbt.setInputNonWitnessUtxo(i, Uint8List.fromList(hex.decode(input.rawTx)));
|
||||
psbt.setInputBip32Derivation(
|
||||
i,
|
||||
Uint8List.fromList(hex.decode(input.ownerPublicKey)),
|
||||
input.ownerMasterFingerprint,
|
||||
BIPPath.fromString(input.ownerDerivationPath).toPathArray());
|
||||
|
||||
psbt.setInputWitnessUtxo(i, Uint8List.fromList(bigIntToUint64LE(input.utxo.value)),
|
||||
Uint8List.fromList(input.ownerDetails.address.toScriptPubKey().toBytes()));
|
||||
}
|
||||
|
||||
void setInputP2shSegwit(int i, PSBTReadyUtxoWithAddress input) {
|
||||
psbt.setInputNonWitnessUtxo(i, Uint8List.fromList(hex.decode(input.rawTx)));
|
||||
psbt.setInputBip32Derivation(i, Uint8List.fromList(hex.decode(input.ownerPublicKey)),
|
||||
input.ownerMasterFingerprint, BIPPath.fromString(input.ownerDerivationPath).toPathArray());
|
||||
|
||||
psbt.setInputRedeemScript(
|
||||
i, Uint8List.fromList(input.ownerDetails.address.toScriptPubKey().toBytes()));
|
||||
psbt.setInputWitnessUtxo(i, Uint8List.fromList(bigIntToUint64LE(input.utxo.value)),
|
||||
Uint8List.fromList(input.ownerDetails.address.toScriptPubKey().toBytes()));
|
||||
}
|
||||
}
|
||||
|
||||
class PSBTReadyUtxoWithAddress extends UtxoWithAddress {
|
||||
final String rawTx;
|
||||
final String ownerDerivationPath;
|
||||
final Uint8List ownerMasterFingerprint;
|
||||
final String ownerPublicKey;
|
||||
|
||||
PSBTReadyUtxoWithAddress({
|
||||
required super.utxo,
|
||||
required this.rawTx,
|
||||
required super.ownerDetails,
|
||||
required this.ownerDerivationPath,
|
||||
required this.ownerMasterFingerprint,
|
||||
required this.ownerPublicKey,
|
||||
});
|
||||
}
|
|
@ -5,29 +5,58 @@ import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
|||
import 'package:bitcoin_flutter/src/payments/index.dart' show PaymentData;
|
||||
import 'package:hex/hex.dart';
|
||||
|
||||
bitcoin.PaymentData generatePaymentData({required bitcoin.HDWallet hd, required int index}) =>
|
||||
PaymentData(pubkey: Uint8List.fromList(HEX.decode(hd.derive(index).pubKey!)));
|
||||
bitcoin.PaymentData generatePaymentData({required bitcoin.HDWallet hd, int? index}) {
|
||||
final pubKey = index != null ? hd.derive(index).pubKey! : hd.pubKey!;
|
||||
return PaymentData(pubkey: Uint8List.fromList(HEX.decode(pubKey)));
|
||||
}
|
||||
|
||||
ECPrivate generateECPrivate(
|
||||
{required bitcoin.HDWallet hd, required int index, required BasedUtxoNetwork network}) =>
|
||||
ECPrivate.fromWif(hd.derive(index).wif!, netVersion: network.wifNetVer);
|
||||
{required bitcoin.HDWallet hd, required BasedUtxoNetwork network, int? index}) {
|
||||
final wif = index != null ? hd.derive(index).wif! : hd.wif!;
|
||||
return ECPrivate.fromWif(wif, netVersion: network.wifNetVer);
|
||||
}
|
||||
|
||||
String generateP2WPKHAddress(
|
||||
{required bitcoin.HDWallet hd, required int index, required BasedUtxoNetwork network}) =>
|
||||
ECPublic.fromHex(hd.derive(index).pubKey!).toP2wpkhAddress().toAddress(network);
|
||||
String generateP2WPKHAddress({
|
||||
required bitcoin.HDWallet hd,
|
||||
required BasedUtxoNetwork network,
|
||||
int? index,
|
||||
}) {
|
||||
final pubKey = index != null ? hd.derive(index).pubKey! : hd.pubKey!;
|
||||
return ECPublic.fromHex(pubKey).toP2wpkhAddress().toAddress(network);
|
||||
}
|
||||
|
||||
String generateP2SHAddress(
|
||||
{required bitcoin.HDWallet hd, required int index, required BasedUtxoNetwork network}) =>
|
||||
ECPublic.fromHex(hd.derive(index).pubKey!).toP2wpkhInP2sh().toAddress(network);
|
||||
String generateP2SHAddress({
|
||||
required bitcoin.HDWallet hd,
|
||||
required BasedUtxoNetwork network,
|
||||
int? index,
|
||||
}) {
|
||||
final pubKey = index != null ? hd.derive(index).pubKey! : hd.pubKey!;
|
||||
return ECPublic.fromHex(pubKey).toP2wpkhInP2sh().toAddress(network);
|
||||
}
|
||||
|
||||
String generateP2WSHAddress(
|
||||
{required bitcoin.HDWallet hd, required int index, required BasedUtxoNetwork network}) =>
|
||||
ECPublic.fromHex(hd.derive(index).pubKey!).toP2wshAddress().toAddress(network);
|
||||
String generateP2WSHAddress({
|
||||
required bitcoin.HDWallet hd,
|
||||
required BasedUtxoNetwork network,
|
||||
int? index,
|
||||
}) {
|
||||
final pubKey = index != null ? hd.derive(index).pubKey! : hd.pubKey!;
|
||||
return ECPublic.fromHex(pubKey).toP2wshAddress().toAddress(network);
|
||||
}
|
||||
|
||||
String generateP2PKHAddress(
|
||||
{required bitcoin.HDWallet hd, required int index, required BasedUtxoNetwork network}) =>
|
||||
ECPublic.fromHex(hd.derive(index).pubKey!).toP2pkhAddress().toAddress(network);
|
||||
String generateP2PKHAddress({
|
||||
required bitcoin.HDWallet hd,
|
||||
required BasedUtxoNetwork network,
|
||||
int? index,
|
||||
}) {
|
||||
final pubKey = index != null ? hd.derive(index).pubKey! : hd.pubKey!;
|
||||
return ECPublic.fromHex(pubKey).toP2pkhAddress().toAddress(network);
|
||||
}
|
||||
|
||||
String generateP2TRAddress(
|
||||
{required bitcoin.HDWallet hd, required int index, required BasedUtxoNetwork network}) =>
|
||||
ECPublic.fromHex(hd.derive(index).pubKey!).toTaprootAddress().toAddress(network);
|
||||
String generateP2TRAddress({
|
||||
required bitcoin.HDWallet hd,
|
||||
required BasedUtxoNetwork network,
|
||||
int? index,
|
||||
}) {
|
||||
final pubKey = index != null ? hd.derive(index).pubKey! : hd.pubKey!;
|
||||
return ECPublic.fromHex(pubKey).toTaprootAddress().toAddress(network);
|
||||
}
|
||||
|
|
|
@ -21,10 +21,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: args
|
||||
sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596
|
||||
sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.2"
|
||||
version: "2.5.0"
|
||||
asn1lib:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -80,7 +80,7 @@ packages:
|
|||
description:
|
||||
path: "."
|
||||
ref: cake-update-v2
|
||||
resolved-ref: "3fd81d238b990bb767fc7a4fdd5053a22a142e2e"
|
||||
resolved-ref: "01d844a5f5a520a31df5254e34169af4664aa769"
|
||||
url: "https://github.com/cake-tech/bitcoin_base.git"
|
||||
source: git
|
||||
version: "4.2.0"
|
||||
|
@ -153,10 +153,10 @@ packages:
|
|||
dependency: "direct dev"
|
||||
description:
|
||||
name: build_runner
|
||||
sha256: "581bacf68f89ec8792f5e5a0b2c4decd1c948e97ce659dc783688c8a88fbec21"
|
||||
sha256: "3ac61a79bfb6f6cc11f693591063a7f19a7af628dc52f141743edac5c16e8c22"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.8"
|
||||
version: "2.4.9"
|
||||
build_runner_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -177,10 +177,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: built_value
|
||||
sha256: a3ec2e0f967bc47f69f95009bb93db936288d61d5343b9436e378b28a2f830c6
|
||||
sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.9.0"
|
||||
version: "8.9.2"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -217,10 +217,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
|
||||
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.17.1"
|
||||
version: "1.18.0"
|
||||
convert:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -260,6 +260,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.4"
|
||||
dart_varuint_bitcoin:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: dart_varuint_bitcoin
|
||||
sha256: "4f0ccc9733fb54148b9d3688eea822b7aaabf5cc00025998f8c09a1d45b31b4b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.3"
|
||||
encrypt:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -309,10 +317,18 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_mobx
|
||||
sha256: "4a5d062ff85ed3759f4aac6410ff0ffae32e324b2e71ca722ae1b37b32e865f4"
|
||||
sha256: "859fbf452fa9c2519d2700b125dd7fb14c508bbdd7fb65e26ca8ff6c92280e2e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0+2"
|
||||
version: "2.2.1+1"
|
||||
flutter_reactive_ble:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_reactive_ble
|
||||
sha256: "247e2efa76de203d1ba11335c13754b5b9d0504b5423e5b0c93a600f016b24e0"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.3.1"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
|
@ -322,10 +338,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: frontend_server_client
|
||||
sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612"
|
||||
sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.0"
|
||||
version: "4.0.0"
|
||||
functional_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: functional_data
|
||||
sha256: aefdec4365452283b2a7cf420a3169654d51d3e9553069a22d76680d7a9d7c3d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
glob:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -422,6 +446,55 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.8.1"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker
|
||||
sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.0.0"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_flutter_testing
|
||||
sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
leak_tracker_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_testing
|
||||
sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
ledger_bitcoin:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "."
|
||||
ref: HEAD
|
||||
resolved-ref: f819d37e235e239c315e93856abbf5e5d3b71dab
|
||||
url: "https://github.com/cake-tech/ledger-bitcoin"
|
||||
source: git
|
||||
version: "0.0.2"
|
||||
ledger_flutter:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: ledger_flutter
|
||||
sha256: f1680060ed6ff78f275837e0024ccaf667715a59ba7aa29fa7354bc7752e71c8
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
ledger_usb:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ledger_usb
|
||||
sha256: "52c92d03a4cffe06c82921c8e2f79f3cdad6e1cf78e1e9ca35444196ff8f14c2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -434,26 +507,26 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
|
||||
sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.15"
|
||||
version: "0.12.16+1"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
|
||||
sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.0"
|
||||
version: "0.8.0"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
|
||||
sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
version: "1.11.0"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -466,10 +539,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: mobx
|
||||
sha256: "74ee54012dc7c1b3276eaa960a600a7418ef5f9997565deb8fca1fd88fb36b78"
|
||||
sha256: "63920b27b32ad1910adfe767ab1750e4c212e8923232a1f891597b362074ea5e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.0+1"
|
||||
version: "2.3.3+2"
|
||||
mobx_codegen:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
|
@ -498,10 +571,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: path
|
||||
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
|
||||
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.8.3"
|
||||
version: "1.9.0"
|
||||
path_provider:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -570,10 +643,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: pointycastle
|
||||
sha256: "43ac87de6e10afabc85c445745a7b799e04de84cebaa4fd7bf55a5e1e9604d29"
|
||||
sha256: "70fe966348fe08c34bf929582f1d8247d9d9408130723206472b4687227e4333"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.7.4"
|
||||
version: "3.8.0"
|
||||
pool:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -582,14 +655,22 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.5.1"
|
||||
protobuf:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: protobuf
|
||||
sha256: "01dd9bd0fa02548bf2ceee13545d4a0ec6046459d847b6b061d8a27237108a08"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
provider:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: provider
|
||||
sha256: "9a96a0a19b594dbc5bf0f1f27d2bc67d5f95957359b461cd9feb44ed6ae75096"
|
||||
sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.1"
|
||||
version: "6.1.2"
|
||||
pub_semver:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -606,6 +687,22 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.3"
|
||||
reactive_ble_mobile:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: reactive_ble_mobile
|
||||
sha256: "9ec2b4c9c725e439950838d551579750060258fbccd5536d0543b4d07d225798"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.3.1"
|
||||
reactive_ble_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: reactive_ble_platform_interface
|
||||
sha256: "632c92401a2d69c9b94bd48f8fd47488a7013f3d1f9b291884350291a4a81813"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.3.1"
|
||||
rxdart:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -663,26 +760,26 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
|
||||
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
version: "1.10.0"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
|
||||
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.11.0"
|
||||
version: "1.11.1"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_channel
|
||||
sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
|
||||
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
version: "2.1.2"
|
||||
stream_transform:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -711,10 +808,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
|
||||
sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.1"
|
||||
version: "0.6.1"
|
||||
timing:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -747,8 +844,16 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
watcher:
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "13.0.0"
|
||||
watcher:
|
||||
dependency: "direct overridden"
|
||||
description:
|
||||
name: watcher
|
||||
sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8"
|
||||
|
@ -788,5 +893,5 @@ packages:
|
|||
source: hosted
|
||||
version: "3.1.2"
|
||||
sdks:
|
||||
dart: ">=3.0.0 <4.0.0"
|
||||
dart: ">=3.2.0-0 <4.0.0"
|
||||
flutter: ">=3.10.0"
|
||||
|
|
|
@ -35,15 +35,22 @@ dependencies:
|
|||
url: https://github.com/cake-tech/bitcoin_base.git
|
||||
ref: cake-update-v2
|
||||
blockchain_utils: ^2.1.1
|
||||
ledger_flutter: ^1.0.1
|
||||
ledger_bitcoin:
|
||||
git:
|
||||
url: https://github.com/cake-tech/ledger-bitcoin
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
build_runner: ^2.1.11
|
||||
build_runner: ^2.4.7
|
||||
build_resolvers: ^2.0.9
|
||||
mobx_codegen: ^2.0.7
|
||||
hive_generator: ^1.1.3
|
||||
|
||||
dependency_overrides:
|
||||
watcher: ^1.1.0
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
|
|||
initialRegularAddressIndex: initialRegularAddressIndex,
|
||||
initialChangeAddressIndex: initialChangeAddressIndex,
|
||||
mainHd: hd,
|
||||
sideHd: bitcoin.HDWallet.fromSeed(seedBytes).derivePath("m/44'/145'/0'/1"),
|
||||
sideHd: accountHD.derive(1),
|
||||
network: network,
|
||||
initialAddressPageType: addressPageType,
|
||||
);
|
||||
|
@ -93,7 +93,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
|
|||
final snp = await ElectrumWalletSnapshot.load(
|
||||
name, walletInfo.type, password, BitcoinCashNetwork.mainnet);
|
||||
return BitcoinCashWallet(
|
||||
mnemonic: snp.mnemonic,
|
||||
mnemonic: snp.mnemonic!,
|
||||
password: password,
|
||||
walletInfo: walletInfo,
|
||||
unspentCoinsInfo: unspentCoinsInfo,
|
||||
|
@ -118,7 +118,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
|
|||
}
|
||||
}).toList(),
|
||||
initialBalance: snp.balance,
|
||||
seedBytes: await Mnemonic.toSeed(snp.mnemonic),
|
||||
seedBytes: await Mnemonic.toSeed(snp.mnemonic!),
|
||||
initialRegularAddressIndex: snp.regularAddressIndex,
|
||||
initialChangeAddressIndex: snp.changeAddressIndex,
|
||||
addressPageType: P2pkhAddressType.p2pkh,
|
||||
|
@ -166,7 +166,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
|
|||
}
|
||||
|
||||
@override
|
||||
String signMessage(String message, {String? address = null}) {
|
||||
Future<String> signMessage(String message, {String? address = null}) async {
|
||||
final index = address != null
|
||||
? walletAddresses.allAddresses
|
||||
.firstWhere((element) => element.address == AddressUtils.toLegacyAddress(address))
|
||||
|
|
|
@ -12,7 +12,7 @@ import 'package:collection/collection.dart';
|
|||
import 'package:hive/hive.dart';
|
||||
|
||||
class BitcoinCashWalletService extends WalletService<BitcoinCashNewWalletCredentials,
|
||||
BitcoinCashRestoreWalletFromSeedCredentials, BitcoinCashRestoreWalletFromWIFCredentials> {
|
||||
BitcoinCashRestoreWalletFromSeedCredentials, BitcoinCashRestoreWalletFromWIFCredentials, BitcoinCashNewWalletCredentials> {
|
||||
BitcoinCashWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
|
||||
|
||||
final Box<WalletInfo> walletInfoSource;
|
||||
|
@ -93,6 +93,11 @@ class BitcoinCashWalletService extends WalletService<BitcoinCashNewWalletCredent
|
|||
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<BitcoinCashWallet> restoreFromHardwareWallet(BitcoinCashNewWalletCredentials credentials) {
|
||||
throw UnimplementedError("Restoring a Bitcoin Cash wallet from a hardware wallet is not yet supported!");
|
||||
}
|
||||
|
||||
@override
|
||||
Future<BitcoinCashWallet> restoreFromKeys(credentials, {bool? isTestnet}) {
|
||||
// TODO: implement restoreFromKeys
|
||||
|
|
|
@ -62,7 +62,9 @@ class PendingBitcoinCashTransaction with PendingTransaction {
|
|||
if (error.contains("bad-txns-vout-negative")) {
|
||||
throw BitcoinTransactionCommitFailedVoutNegative();
|
||||
}
|
||||
throw BitcoinTransactionCommitFailed(errorMessage: error);
|
||||
}
|
||||
|
||||
throw BitcoinTransactionCommitFailed();
|
||||
}
|
||||
|
||||
|
|
|
@ -39,10 +39,13 @@ dependencies:
|
|||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
build_runner: ^2.1.11
|
||||
build_runner: ^2.4.7
|
||||
mobx_codegen: ^2.0.7
|
||||
hive_generator: ^1.1.3
|
||||
|
||||
dependency_overrides:
|
||||
watcher: ^1.1.0
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
||||
|
|
|
@ -103,6 +103,7 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
|
|||
CryptoCurrency.kaspa,
|
||||
CryptoCurrency.digibyte,
|
||||
CryptoCurrency.usdtSol,
|
||||
CryptoCurrency.usdcTrc20,
|
||||
];
|
||||
|
||||
static const havenCurrencies = [
|
||||
|
@ -217,6 +218,7 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
|
|||
static const kaspa = CryptoCurrency(title: 'KAS', fullName: 'Kaspa', raw: 89, name: 'kas', iconPath: 'assets/images/kaspa_icon.png', decimals: 8);
|
||||
static const digibyte = CryptoCurrency(title: 'DGB', fullName: 'DigiByte', raw: 90, name: 'dgb', iconPath: 'assets/images/digibyte.png', decimals: 8);
|
||||
static const usdtSol = CryptoCurrency(title: 'USDT', tag: 'SOL', fullName: 'USDT Tether', raw: 91, name: 'usdtsol', iconPath: 'assets/images/usdt_icon.png', decimals: 6);
|
||||
static const usdcTrc20 = CryptoCurrency(title: 'USDC', tag: 'TRX', fullName: 'USDC Coin', raw: 92, name: 'usdctrc20', iconPath: 'assets/images/usdc_icon.png', decimals: 6);
|
||||
|
||||
|
||||
static final Map<int, CryptoCurrency> _rawCurrencyMap =
|
||||
|
@ -257,10 +259,16 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
|
|||
element.tag == walletCurrency?.tag));
|
||||
} catch (_) {}
|
||||
|
||||
// search by fullName if not found by title:
|
||||
try {
|
||||
return CryptoCurrency.all.firstWhere((element) => element.fullName?.toLowerCase() == name);
|
||||
} catch (_) {}
|
||||
|
||||
if (CryptoCurrency._nameCurrencyMap[name.toLowerCase()] == null) {
|
||||
final s = 'Unexpected token: $name for CryptoCurrency fromString';
|
||||
throw ArgumentError.value(name, 'name', s);
|
||||
}
|
||||
|
||||
return CryptoCurrency._nameCurrencyMap[name.toLowerCase()]!;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,10 @@ CryptoCurrency currencyForWalletType(WalletType type) {
|
|||
return CryptoCurrency.maticpoly;
|
||||
case WalletType.solana:
|
||||
return CryptoCurrency.sol;
|
||||
case WalletType.tron:
|
||||
return CryptoCurrency.trx;
|
||||
default:
|
||||
throw Exception('Unexpected wallet type: ${type.toString()} for CryptoCurrency currencyForWalletType');
|
||||
throw Exception(
|
||||
'Unexpected wallet type: ${type.toString()} for CryptoCurrency currencyForWalletType');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,11 @@ class TransactionNoDustOnChangeException implements Exception {
|
|||
final String min;
|
||||
}
|
||||
|
||||
class TransactionCommitFailed implements Exception {}
|
||||
class TransactionCommitFailed implements Exception {
|
||||
final String? errorMessage;
|
||||
|
||||
TransactionCommitFailed({this.errorMessage});
|
||||
}
|
||||
|
||||
class TransactionCommitFailedDustChange implements Exception {}
|
||||
|
||||
|
|
28
cw_core/lib/hardware/device_connection_type.dart
Normal file
28
cw_core/lib/hardware/device_connection_type.dart
Normal file
|
@ -0,0 +1,28 @@
|
|||
import 'package:cw_core/wallet_type.dart';
|
||||
|
||||
enum DeviceConnectionType {
|
||||
usb,
|
||||
ble;
|
||||
|
||||
static List<DeviceConnectionType> supportedConnectionTypes(WalletType walletType,
|
||||
[bool isIOS = false]) {
|
||||
switch (walletType) {
|
||||
case WalletType.bitcoin:
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
if (isIOS) return [DeviceConnectionType.ble];
|
||||
return [DeviceConnectionType.ble, DeviceConnectionType.usb];
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
String get iconString {
|
||||
switch (this) {
|
||||
case ble:
|
||||
return 'assets/images/bluetooth.png';
|
||||
case usb:
|
||||
return 'assets/images/usb.png';
|
||||
}
|
||||
}
|
||||
}
|
7
cw_core/lib/hardware/device_not_connected_exception.dart
Normal file
7
cw_core/lib/hardware/device_not_connected_exception.dart
Normal file
|
@ -0,0 +1,7 @@
|
|||
class DeviceNotConnectedException implements Exception {
|
||||
final String message;
|
||||
|
||||
DeviceNotConnectedException({
|
||||
this.message = '',
|
||||
});
|
||||
}
|
19
cw_core/lib/hardware/hardware_account_data.dart
Normal file
19
cw_core/lib/hardware/hardware_account_data.dart
Normal file
|
@ -0,0 +1,19 @@
|
|||
import 'dart:typed_data';
|
||||
|
||||
class HardwareAccountData {
|
||||
HardwareAccountData({
|
||||
required this.address,
|
||||
required this.accountIndex,
|
||||
required this.derivationPath,
|
||||
this.xpub,
|
||||
this.masterFingerprint,
|
||||
});
|
||||
|
||||
final String address;
|
||||
final int accountIndex;
|
||||
final String derivationPath;
|
||||
|
||||
// Bitcoin Specific
|
||||
final Uint8List? masterFingerprint;
|
||||
final String? xpub;
|
||||
}
|
|
@ -14,4 +14,7 @@ const ERC20_TOKEN_TYPE_ID = 12;
|
|||
const NANO_ACCOUNT_TYPE_ID = 13;
|
||||
const POW_NODE_TYPE_ID = 14;
|
||||
const DERIVATION_TYPE_TYPE_ID = 15;
|
||||
const SPL_TOKEN_TYPE_ID = 16;
|
||||
const SPL_TOKEN_TYPE_ID = 16;
|
||||
const DERIVATION_INFO_TYPE_ID = 17;
|
||||
const TRON_TOKEN_TYPE_ID = 18;
|
||||
const HARDWARE_WALLET_TYPE_TYPE_ID = 19;
|
||||
|
|
|
@ -10,7 +10,7 @@ import 'package:http/io_client.dart' as ioc;
|
|||
|
||||
part 'node.g.dart';
|
||||
|
||||
Uri createUriFromElectrumAddress(String address) => Uri.tryParse('tcp://$address')!;
|
||||
Uri createUriFromElectrumAddress(String address, String path) => Uri.tryParse('tcp://$address$path')!;
|
||||
|
||||
@HiveType(typeId: Node.typeId)
|
||||
class Node extends HiveObject with Keyable {
|
||||
|
@ -83,7 +83,7 @@ class Node extends HiveObject with Keyable {
|
|||
case WalletType.bitcoin:
|
||||
case WalletType.litecoin:
|
||||
case WalletType.bitcoinCash:
|
||||
return createUriFromElectrumAddress(uriRaw);
|
||||
return createUriFromElectrumAddress(uriRaw, path ?? '');
|
||||
case WalletType.nano:
|
||||
case WalletType.banano:
|
||||
if (isSSL) {
|
||||
|
@ -94,7 +94,8 @@ class Node extends HiveObject with Keyable {
|
|||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
case WalletType.solana:
|
||||
return Uri.https(uriRaw, '');
|
||||
case WalletType.tron:
|
||||
return Uri.https(uriRaw, path ?? '');
|
||||
default:
|
||||
throw Exception('Unexpected type ${type.toString()} for Node uri');
|
||||
}
|
||||
|
@ -152,6 +153,7 @@ class Node extends HiveObject with Keyable {
|
|||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
case WalletType.solana:
|
||||
case WalletType.tron:
|
||||
return requestElectrumServer();
|
||||
default:
|
||||
return false;
|
||||
|
|
|
@ -14,6 +14,7 @@ class Unspent {
|
|||
bool isChange;
|
||||
bool isSending;
|
||||
bool isFrozen;
|
||||
int? confirmations;
|
||||
String note;
|
||||
|
||||
bool get isP2wpkh => address.startsWith('bc') || address.startsWith('ltc');
|
||||
|
|
|
@ -56,6 +56,8 @@ abstract class WalletBase<BalanceType extends Balance, HistoryType extends Trans
|
|||
|
||||
bool get isEnabledAutoGenerateSubaddress => false;
|
||||
|
||||
bool get isHardwareWallet => walletInfo.isHardwareWallet;
|
||||
|
||||
Future<void> connectToNode({required Node node});
|
||||
|
||||
// there is a default definition here because only coins with a pow node (nano based) need to override this
|
||||
|
@ -88,7 +90,7 @@ abstract class WalletBase<BalanceType extends Balance, HistoryType extends Trans
|
|||
|
||||
Future<void> renameWalletFiles(String newWalletName);
|
||||
|
||||
String signMessage(String message, {String? address = null}) => throw UnimplementedError();
|
||||
Future<String> signMessage(String message, {String? address = null}) => throw UnimplementedError();
|
||||
|
||||
bool? isTestnet;
|
||||
}
|
||||
|
|
|
@ -7,15 +7,21 @@ abstract class WalletCredentials {
|
|||
this.seedPhraseLength,
|
||||
this.walletInfo,
|
||||
this.password,
|
||||
this.derivationType,
|
||||
this.derivationPath,
|
||||
});
|
||||
this.passphrase,
|
||||
this.derivationInfo,
|
||||
this.hardwareWalletType,
|
||||
}) {
|
||||
if (this.walletInfo != null && derivationInfo != null) {
|
||||
this.walletInfo!.derivationInfo = derivationInfo;
|
||||
}
|
||||
}
|
||||
|
||||
final String name;
|
||||
final int? height;
|
||||
int? seedPhraseLength;
|
||||
String? password;
|
||||
DerivationType? derivationType;
|
||||
String? derivationPath;
|
||||
String? passphrase;
|
||||
WalletInfo? walletInfo;
|
||||
DerivationInfo? derivationInfo;
|
||||
HardwareWalletType? hardwareWalletType;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:cw_core/address_info.dart';
|
||||
import 'package:cw_core/hive_type_ids.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
|
@ -17,28 +18,48 @@ enum DerivationType {
|
|||
@HiveField(3)
|
||||
bip39,
|
||||
@HiveField(4)
|
||||
electrum1,
|
||||
@HiveField(5)
|
||||
electrum2,
|
||||
electrum,
|
||||
}
|
||||
|
||||
class DerivationInfo {
|
||||
@HiveType(typeId: HARDWARE_WALLET_TYPE_TYPE_ID)
|
||||
enum HardwareWalletType {
|
||||
@HiveField(0)
|
||||
ledger,
|
||||
}
|
||||
|
||||
@HiveType(typeId: DerivationInfo.typeId)
|
||||
class DerivationInfo extends HiveObject {
|
||||
DerivationInfo({
|
||||
required this.derivationType,
|
||||
this.derivationType,
|
||||
this.derivationPath,
|
||||
this.balance = "",
|
||||
this.address = "",
|
||||
this.height = 0,
|
||||
this.script_type,
|
||||
this.transactionsCount = 0,
|
||||
this.scriptType,
|
||||
this.description,
|
||||
});
|
||||
|
||||
String balance;
|
||||
static const typeId = DERIVATION_INFO_TYPE_ID;
|
||||
|
||||
@HiveField(0, defaultValue: '')
|
||||
String address;
|
||||
int height;
|
||||
final DerivationType derivationType;
|
||||
final String? derivationPath;
|
||||
final String? script_type;
|
||||
|
||||
@HiveField(1, defaultValue: '')
|
||||
String balance;
|
||||
|
||||
@HiveField(2, defaultValue: 0)
|
||||
int transactionsCount;
|
||||
|
||||
@HiveField(3)
|
||||
DerivationType? derivationType;
|
||||
|
||||
@HiveField(4)
|
||||
String? derivationPath;
|
||||
|
||||
@HiveField(5)
|
||||
final String? scriptType;
|
||||
|
||||
@HiveField(6)
|
||||
final String? description;
|
||||
}
|
||||
|
||||
|
@ -57,9 +78,9 @@ class WalletInfo extends HiveObject {
|
|||
this.yatEid,
|
||||
this.yatLastUsedAddressRaw,
|
||||
this.showIntroCakePayCard,
|
||||
this.derivationType,
|
||||
this.derivationPath)
|
||||
: _yatLastUsedAddressController = StreamController<String>.broadcast();
|
||||
this.derivationInfo,
|
||||
this.hardwareWalletType,
|
||||
): _yatLastUsedAddressController = StreamController<String>.broadcast();
|
||||
|
||||
factory WalletInfo.external({
|
||||
required String id,
|
||||
|
@ -74,24 +95,25 @@ class WalletInfo extends HiveObject {
|
|||
bool? showIntroCakePayCard,
|
||||
String yatEid = '',
|
||||
String yatLastUsedAddressRaw = '',
|
||||
DerivationType? derivationType,
|
||||
String? derivationPath,
|
||||
DerivationInfo? derivationInfo,
|
||||
HardwareWalletType? hardwareWalletType,
|
||||
}) {
|
||||
return WalletInfo(
|
||||
id,
|
||||
name,
|
||||
type,
|
||||
isRecovery,
|
||||
restoreHeight,
|
||||
date.millisecondsSinceEpoch,
|
||||
dirPath,
|
||||
path,
|
||||
address,
|
||||
yatEid,
|
||||
yatLastUsedAddressRaw,
|
||||
showIntroCakePayCard,
|
||||
derivationType,
|
||||
derivationPath);
|
||||
id,
|
||||
name,
|
||||
type,
|
||||
isRecovery,
|
||||
restoreHeight,
|
||||
date.millisecondsSinceEpoch,
|
||||
dirPath,
|
||||
path,
|
||||
address,
|
||||
yatEid,
|
||||
yatLastUsedAddressRaw,
|
||||
showIntroCakePayCard,
|
||||
derivationInfo,
|
||||
hardwareWalletType,
|
||||
);
|
||||
}
|
||||
|
||||
static const typeId = WALLET_INFO_TYPE_ID;
|
||||
|
@ -142,11 +164,13 @@ class WalletInfo extends HiveObject {
|
|||
@HiveField(15)
|
||||
List<String>? usedAddresses;
|
||||
|
||||
@deprecated
|
||||
@HiveField(16)
|
||||
DerivationType? derivationType;
|
||||
DerivationType? derivationType; // no longer used
|
||||
|
||||
@deprecated
|
||||
@HiveField(17)
|
||||
String? derivationPath;
|
||||
String? derivationPath; // no longer used
|
||||
|
||||
@HiveField(18)
|
||||
String? addressPageType;
|
||||
|
@ -154,6 +178,12 @@ class WalletInfo extends HiveObject {
|
|||
@HiveField(19)
|
||||
String? network;
|
||||
|
||||
@HiveField(20)
|
||||
DerivationInfo? derivationInfo;
|
||||
|
||||
@HiveField(21)
|
||||
HardwareWalletType? hardwareWalletType;
|
||||
|
||||
String get yatLastUsedAddress => yatLastUsedAddressRaw ?? '';
|
||||
|
||||
set yatLastUsedAddress(String address) {
|
||||
|
@ -170,6 +200,8 @@ class WalletInfo extends HiveObject {
|
|||
return showIntroCakePayCard!;
|
||||
}
|
||||
|
||||
bool get isHardwareWallet => hardwareWalletType != null;
|
||||
|
||||
DateTime get date => DateTime.fromMillisecondsSinceEpoch(timestamp);
|
||||
|
||||
Stream<String> get yatLastUsedAddressStream => _yatLastUsedAddressController.stream;
|
||||
|
|
|
@ -6,11 +6,13 @@ import 'package:cw_core/wallet_credentials.dart';
|
|||
import 'package:cw_core/wallet_type.dart';
|
||||
|
||||
abstract class WalletService<N extends WalletCredentials, RFS extends WalletCredentials,
|
||||
RFK extends WalletCredentials> {
|
||||
RFK extends WalletCredentials, RFH extends WalletCredentials> {
|
||||
WalletType getType();
|
||||
|
||||
Future<WalletBase> create(N credentials, {bool? isTestnet});
|
||||
|
||||
Future<WalletBase> restoreFromHardwareWallet(RFH credentials);
|
||||
|
||||
Future<WalletBase> restoreFromSeed(RFS credentials, {bool? isTestnet});
|
||||
|
||||
Future<WalletBase> restoreFromKeys(RFK credentials, {bool? isTestnet});
|
||||
|
|
|
@ -15,6 +15,7 @@ const walletTypes = [
|
|||
WalletType.banano,
|
||||
WalletType.polygon,
|
||||
WalletType.solana,
|
||||
WalletType.tron,
|
||||
];
|
||||
|
||||
@HiveType(typeId: WALLET_TYPE_TYPE_ID)
|
||||
|
@ -50,7 +51,10 @@ enum WalletType {
|
|||
polygon,
|
||||
|
||||
@HiveField(10)
|
||||
solana
|
||||
solana,
|
||||
|
||||
@HiveField(11)
|
||||
tron
|
||||
}
|
||||
|
||||
int serializeToInt(WalletType type) {
|
||||
|
@ -75,6 +79,8 @@ int serializeToInt(WalletType type) {
|
|||
return 8;
|
||||
case WalletType.solana:
|
||||
return 9;
|
||||
case WalletType.tron:
|
||||
return 10;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
@ -102,6 +108,8 @@ WalletType deserializeFromInt(int raw) {
|
|||
return WalletType.polygon;
|
||||
case 9:
|
||||
return WalletType.solana;
|
||||
case 10:
|
||||
return WalletType.tron;
|
||||
default:
|
||||
throw Exception('Unexpected token: $raw for WalletType deserializeFromInt');
|
||||
}
|
||||
|
@ -129,6 +137,8 @@ String walletTypeToString(WalletType type) {
|
|||
return 'Polygon';
|
||||
case WalletType.solana:
|
||||
return 'Solana';
|
||||
case WalletType.tron:
|
||||
return 'Tron';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
|
@ -156,6 +166,8 @@ String walletTypeToDisplayName(WalletType type) {
|
|||
return 'Polygon (MATIC)';
|
||||
case WalletType.solana:
|
||||
return 'Solana (SOL)';
|
||||
case WalletType.tron:
|
||||
return 'Tron (TRX)';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
|
@ -183,6 +195,8 @@ CryptoCurrency walletTypeToCryptoCurrency(WalletType type) {
|
|||
return CryptoCurrency.maticpoly;
|
||||
case WalletType.solana:
|
||||
return CryptoCurrency.sol;
|
||||
case WalletType.tron:
|
||||
return CryptoCurrency.trx;
|
||||
default:
|
||||
throw Exception(
|
||||
'Unexpected wallet type: ${type.toString()} for CryptoCurrency walletTypeToCryptoCurrency');
|
||||
|
|
22
cw_core/lib/window_size.dart
Normal file
22
cw_core/lib/window_size.dart
Normal file
|
@ -0,0 +1,22 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
const MethodChannel _channel = MethodChannel('com.cake_wallet/native_utils');
|
||||
|
||||
Future<void> setDefaultMinimumWindowSize() async {
|
||||
if (!Platform.isMacOS) return;
|
||||
|
||||
try {
|
||||
final result = await _channel.invokeMethod(
|
||||
'setMinWindowSize',
|
||||
{'width': 500, 'height': 700},
|
||||
) as bool;
|
||||
|
||||
if (!result) {
|
||||
print("Failed to set minimum window size.");
|
||||
}
|
||||
} on PlatformException catch (e) {
|
||||
print("Failed to set minimum window size: '${e.message}'.");
|
||||
}
|
||||
}
|
|
@ -149,10 +149,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
|
||||
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.17.1"
|
||||
version: "1.18.0"
|
||||
convert:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -331,6 +331,30 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.8.1"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker
|
||||
sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.0.0"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_flutter_testing
|
||||
sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
leak_tracker_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_testing
|
||||
sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -343,26 +367,26 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
|
||||
sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.15"
|
||||
version: "0.12.16+1"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
|
||||
sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.0"
|
||||
version: "0.8.0"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
|
||||
sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
version: "1.11.0"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -407,10 +431,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: path
|
||||
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
|
||||
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.8.3"
|
||||
version: "1.9.0"
|
||||
path_provider:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -564,26 +588,26 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
|
||||
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
version: "1.10.0"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
|
||||
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.11.0"
|
||||
version: "1.11.1"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_channel
|
||||
sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
|
||||
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
version: "2.1.2"
|
||||
stream_transform:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -612,10 +636,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
|
||||
sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.1"
|
||||
version: "0.6.1"
|
||||
timing:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -640,8 +664,16 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
watcher:
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "13.0.0"
|
||||
watcher:
|
||||
dependency: "direct overridden"
|
||||
description:
|
||||
name: watcher
|
||||
sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8"
|
||||
|
@ -681,5 +713,5 @@ packages:
|
|||
source: hosted
|
||||
version: "3.1.2"
|
||||
sdks:
|
||||
dart: ">=3.0.0 <4.0.0"
|
||||
dart: ">=3.2.0-0 <4.0.0"
|
||||
flutter: ">=3.10.0"
|
||||
|
|
|
@ -28,11 +28,14 @@ dependencies:
|
|||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
build_runner: ^2.1.11
|
||||
build_runner: ^2.4.7
|
||||
build_resolvers: ^2.0.9
|
||||
mobx_codegen: ^2.0.7
|
||||
hive_generator: ^2.0.1
|
||||
|
||||
dependency_overrides:
|
||||
watcher: ^1.1.0
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import 'package:bip39/bip39.dart' as bip39;
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cw_ethereum/ethereum_client.dart';
|
||||
import 'package:cw_ethereum/ethereum_mnemonics_exception.dart';
|
||||
import 'package:cw_ethereum/ethereum_wallet.dart';
|
||||
import 'package:cw_evm/evm_chain_wallet_creation_credentials.dart';
|
||||
import 'package:cw_evm/evm_chain_wallet_service.dart';
|
||||
import 'package:bip39/bip39.dart' as bip39;
|
||||
|
||||
class EthereumWalletService extends EVMChainWalletService<EthereumWallet> {
|
||||
EthereumWalletService(super.walletInfoSource, {required this.client});
|
||||
|
@ -82,6 +83,29 @@ class EthereumWalletService extends EVMChainWalletService<EthereumWallet> {
|
|||
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<EthereumWallet> restoreFromHardwareWallet(
|
||||
EVMChainRestoreWalletFromHardware credentials) async {
|
||||
credentials.walletInfo!.derivationInfo = DerivationInfo(
|
||||
derivationType: DerivationType.bip39,
|
||||
derivationPath: "m/44'/60'/${credentials.hwAccountData.accountIndex}'/0/0"
|
||||
);
|
||||
credentials.walletInfo!.hardwareWalletType = credentials.hardwareWalletType;
|
||||
credentials.walletInfo!.address = credentials.hwAccountData.address;
|
||||
|
||||
final wallet = EthereumWallet(
|
||||
walletInfo: credentials.walletInfo!,
|
||||
password: credentials.password!,
|
||||
client: client,
|
||||
);
|
||||
|
||||
await wallet.init();
|
||||
wallet.addInitialTokens();
|
||||
await wallet.save();
|
||||
|
||||
return wallet;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<EthereumWallet> restoreFromKeys(EVMChainRestoreWalletFromPrivateKey credentials,
|
||||
{bool? isTestnet}) async {
|
||||
|
|
|
@ -19,10 +19,18 @@ dependencies:
|
|||
path: ../cw_evm
|
||||
hive: ^2.2.3
|
||||
|
||||
dependency_overrides:
|
||||
web3dart:
|
||||
git:
|
||||
url: https://github.com/cake-tech/web3dart.git
|
||||
ref: cake
|
||||
watcher: ^1.1.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
build_runner: ^2.1.11
|
||||
build_runner: ^2.4.7
|
||||
|
||||
flutter:
|
||||
# assets:
|
||||
# - images/a_dot_burr.jpeg
|
||||
|
|
209
cw_evm/lib/contract/erc20.dart
Normal file
209
cw_evm/lib/contract/erc20.dart
Normal file
|
@ -0,0 +1,209 @@
|
|||
import 'dart:typed_data';
|
||||
|
||||
import 'package:web3dart/web3dart.dart' as web3;
|
||||
|
||||
final _contractAbi = web3.ContractAbi.fromJson(
|
||||
'[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]',
|
||||
'Erc20');
|
||||
|
||||
/// Interface of the ERC20 standard as defined in the EIP.
|
||||
class ERC20 extends web3.GeneratedContract {
|
||||
/// Constructor.
|
||||
ERC20({
|
||||
required web3.EthereumAddress address,
|
||||
required web3.Web3Client client,
|
||||
int? chainId,
|
||||
}) : super(web3.DeployedContract(_contractAbi, address), client, chainId);
|
||||
|
||||
/// Returns the remaining number of tokens that [spender] will be allowed to spend on behalf of [owner] through [transferFrom]. This is zero by default. This value changes when [approve] or [transferFrom] are called.
|
||||
///
|
||||
/// The optional [atBlock] parameter can be used to view historical data. When
|
||||
/// set, the function will be evaluated in the specified block. By default, the
|
||||
/// latest on-chain block will be used.
|
||||
Future<BigInt> allowance(
|
||||
web3.EthereumAddress owner,
|
||||
web3.EthereumAddress spender, {
|
||||
web3.BlockNum? atBlock,
|
||||
}) async {
|
||||
final function = self.abi.functions[0];
|
||||
assert(checkSignature(function, 'dd62ed3e'));
|
||||
final params = [owner, spender];
|
||||
final response = await read(function, params, atBlock);
|
||||
return (response[0] as BigInt);
|
||||
}
|
||||
|
||||
/// Sets [amount] as the allowance of [spender] over the caller's tokens. Returns a boolean value indicating whether the operation succeeded. IMPORTANT: Beware that changing an allowance with this method brings the risk that someone may use both the old and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 Emits an [Approval] event.
|
||||
///
|
||||
/// The optional [transaction] parameter can be used to override parameters
|
||||
/// like the gas price, nonce and max gas. The `data` and `to` fields will be
|
||||
/// set by the contract.
|
||||
Future<Uint8List> approve(
|
||||
web3.EthereumAddress spender,
|
||||
BigInt amount, {
|
||||
required web3.Credentials credentials,
|
||||
web3.Transaction? transaction,
|
||||
}) async {
|
||||
final function = self.abi.functions[1];
|
||||
assert(checkSignature(function, '095ea7b3'));
|
||||
final params = [spender, amount];
|
||||
return writeRaw(credentials, transaction, function, params);
|
||||
}
|
||||
|
||||
/// Returns the amount of tokens owned by [account].
|
||||
///
|
||||
/// The optional [atBlock] parameter can be used to view historical data. When
|
||||
/// set, the function will be evaluated in the specified block. By default, the
|
||||
/// latest on-chain block will be used.
|
||||
Future<BigInt> balanceOf(
|
||||
web3.EthereumAddress account, {
|
||||
web3.BlockNum? atBlock,
|
||||
}) async {
|
||||
final function = self.abi.functions[2];
|
||||
assert(checkSignature(function, '70a08231'));
|
||||
final params = [account];
|
||||
final response = await read(function, params, atBlock);
|
||||
return (response[0] as BigInt);
|
||||
}
|
||||
|
||||
/// Returns the decimal precision of the token.
|
||||
///
|
||||
/// The optional [atBlock] parameter can be used to view historical data. When
|
||||
/// set, the function will be evaluated in the specified block. By default, the
|
||||
/// latest on-chain block will be used.
|
||||
Future<BigInt> decimals({web3.BlockNum? atBlock}) async {
|
||||
final function = self.abi.functions[3];
|
||||
assert(checkSignature(function, '313ce567'));
|
||||
final params = [];
|
||||
final response = await read(function, params, atBlock);
|
||||
return (response[0] as BigInt);
|
||||
}
|
||||
|
||||
/// Returns the name of the token.
|
||||
///
|
||||
/// The optional [atBlock] parameter can be used to view historical data. When
|
||||
/// set, the function will be evaluated in the specified block. By default, the
|
||||
/// latest on-chain block will be used.
|
||||
Future<String> name({web3.BlockNum? atBlock}) async {
|
||||
final function = self.abi.functions[4];
|
||||
assert(checkSignature(function, '06fdde03'));
|
||||
final params = [];
|
||||
final response = await read(function, params, atBlock);
|
||||
return (response[0] as String);
|
||||
}
|
||||
|
||||
/// Returns the symbol of the token.
|
||||
///
|
||||
/// The optional [atBlock] parameter can be used to view historical data. When
|
||||
/// set, the function will be evaluated in the specified block. By default, the
|
||||
/// latest on-chain block will be used.
|
||||
Future<String> symbol({web3.BlockNum? atBlock}) async {
|
||||
final function = self.abi.functions[5];
|
||||
assert(checkSignature(function, '95d89b41'));
|
||||
final params = [];
|
||||
final response = await read(function, params, atBlock);
|
||||
return (response[0] as String);
|
||||
}
|
||||
|
||||
/// Returns the amount of tokens in existence.
|
||||
///
|
||||
/// The optional [atBlock] parameter can be used to view historical data. When
|
||||
/// set, the function will be evaluated in the specified block. By default, the
|
||||
/// latest on-chain block will be used.
|
||||
Future<BigInt> totalSupply({web3.BlockNum? atBlock}) async {
|
||||
final function = self.abi.functions[6];
|
||||
assert(checkSignature(function, '18160ddd'));
|
||||
final params = [];
|
||||
final response = await read(function, params, atBlock);
|
||||
return (response[0] as BigInt);
|
||||
}
|
||||
|
||||
/// Moves [amount] tokens from the caller's account to [recipient]. Returns a boolean value indicating whether the operation succeeded. Emits a [Transfer] event.
|
||||
///
|
||||
/// The optional [transaction] parameter can be used to override parameters
|
||||
/// like the gas price, nonce and max gas. The `data` and `to` fields will be
|
||||
/// set by the contract.
|
||||
Future<Uint8List> transfer(
|
||||
web3.EthereumAddress recipient,
|
||||
BigInt amount, {
|
||||
required web3.Credentials credentials,
|
||||
web3.Transaction? transaction,
|
||||
}) async {
|
||||
final function = self.abi.functions[7];
|
||||
assert(checkSignature(function, 'a9059cbb'));
|
||||
final params = [recipient, amount];
|
||||
return writeRaw(credentials, transaction, function, params);
|
||||
}
|
||||
|
||||
/// Moves [amount] tokens from [sender] to [recipient] using the allowance mechanism. [amount] is then deducted from the caller's allowance. Returns a boolean value indicating whether the operation succeeded. Emits a [Transfer] event.
|
||||
///
|
||||
/// The optional [transaction] parameter can be used to override parameters
|
||||
/// like the gas price, nonce and max gas. The `data` and `to` fields will be
|
||||
/// set by the contract.
|
||||
Future<Uint8List> transferFrom(web3.EthereumAddress sender,
|
||||
web3.EthereumAddress recipient, BigInt amount,
|
||||
{required web3.Credentials credentials,
|
||||
web3.Transaction? transaction}) async {
|
||||
final function = self.abi.functions[8];
|
||||
assert(checkSignature(function, '23b872dd'));
|
||||
final params = [sender, recipient, amount];
|
||||
return writeRaw(credentials, transaction, function, params);
|
||||
}
|
||||
|
||||
/// Returns a live stream of all Approval events emitted by this contract.
|
||||
Stream<Approval> approvalEvents(
|
||||
{web3.BlockNum? fromBlock, web3.BlockNum? toBlock}) {
|
||||
final event = self.event('Approval');
|
||||
final filter = web3.FilterOptions.events(
|
||||
contract: self, event: event, fromBlock: fromBlock, toBlock: toBlock);
|
||||
return client.events(filter).map((web3.FilterEvent result) {
|
||||
final decoded = event.decodeResults(result.topics!, result.data!);
|
||||
return Approval._(decoded);
|
||||
});
|
||||
}
|
||||
|
||||
/// Returns a live stream of all Transfer events emitted by this contract.
|
||||
Stream<Transfer> transferEvents(
|
||||
{web3.BlockNum? fromBlock, web3.BlockNum? toBlock}) {
|
||||
final event = self.event('Transfer');
|
||||
final filter = web3.FilterOptions.events(
|
||||
contract: self, event: event, fromBlock: fromBlock, toBlock: toBlock);
|
||||
return client.events(filter).map((web3.FilterEvent result) {
|
||||
final decoded = event.decodeResults(result.topics!, result.data!);
|
||||
return Transfer._(decoded);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Emitted when the allowance of a [spender] for an [owner] is set by a call to [ERC20.approve]. [value] is the new allowance.
|
||||
class Approval {
|
||||
Approval._(List<dynamic> response)
|
||||
: owner = (response[0] as web3.EthereumAddress),
|
||||
spender = (response[1] as web3.EthereumAddress),
|
||||
value = (response[2] as BigInt);
|
||||
|
||||
/// The owner address.
|
||||
final web3.EthereumAddress owner;
|
||||
|
||||
/// The spender address.
|
||||
final web3.EthereumAddress spender;
|
||||
|
||||
/// Value.
|
||||
final BigInt value;
|
||||
}
|
||||
|
||||
/// Emitted when [value] tokens are moved from one account ([from]) to another ([to]). Note that [value] may be zero.
|
||||
class Transfer {
|
||||
Transfer._(List<dynamic> response)
|
||||
: from = (response[0] as web3.EthereumAddress),
|
||||
to = (response[1] as web3.EthereumAddress),
|
||||
value = (response[2] as BigInt);
|
||||
|
||||
/// From address.
|
||||
final web3.EthereumAddress from;
|
||||
|
||||
/// To address.
|
||||
final web3.EthereumAddress to;
|
||||
|
||||
/// Value.
|
||||
final BigInt value;
|
||||
}
|
|
@ -2,21 +2,20 @@ import 'dart:async';
|
|||
import 'dart:convert';
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:cw_core/node.dart';
|
||||
import 'package:cw_core/erc20_token.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
|
||||
import 'package:cw_evm/evm_erc20_balance.dart';
|
||||
import 'package:cw_core/erc20_token.dart';
|
||||
import 'package:cw_core/node.dart';
|
||||
import 'package:cw_evm/evm_chain_transaction_model.dart';
|
||||
import 'package:cw_evm/pending_evm_chain_transaction.dart';
|
||||
import 'package:cw_evm/evm_chain_transaction_priority.dart';
|
||||
import 'package:cw_evm/evm_erc20_balance.dart';
|
||||
import 'package:cw_evm/pending_evm_chain_transaction.dart';
|
||||
import 'package:cw_evm/.secrets.g.dart' as secrets;
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'package:http/http.dart';
|
||||
import 'package:erc20/erc20.dart';
|
||||
import 'package:web3dart/web3dart.dart';
|
||||
import 'package:hex/hex.dart' as hex;
|
||||
import 'package:http/http.dart';
|
||||
import 'package:web3dart/web3dart.dart';
|
||||
|
||||
import 'contract/erc20.dart';
|
||||
|
||||
abstract class EVMChainClient {
|
||||
final httpClient = Client();
|
||||
|
@ -82,7 +81,7 @@ abstract class EVMChainClient {
|
|||
}
|
||||
|
||||
Future<PendingEVMChainTransaction> signTransaction({
|
||||
required EthPrivateKey privateKey,
|
||||
required Credentials privateKey,
|
||||
required String toAddress,
|
||||
required BigInt amount,
|
||||
required int gas,
|
||||
|
@ -96,8 +95,7 @@ abstract class EVMChainClient {
|
|||
currency == CryptoCurrency.maticpoly ||
|
||||
contractAddress != null);
|
||||
|
||||
bool isEVMCompatibleChain =
|
||||
currency == CryptoCurrency.eth || currency == CryptoCurrency.maticpoly;
|
||||
bool isNativeToken = currency == CryptoCurrency.eth || currency == CryptoCurrency.maticpoly;
|
||||
|
||||
final price = _client!.getGasPrice();
|
||||
|
||||
|
@ -105,17 +103,16 @@ abstract class EVMChainClient {
|
|||
from: privateKey.address,
|
||||
to: EthereumAddress.fromHex(toAddress),
|
||||
maxPriorityFeePerGas: EtherAmount.fromInt(EtherUnit.gwei, priority.tip),
|
||||
amount: isEVMCompatibleChain ? EtherAmount.inWei(amount) : EtherAmount.zero(),
|
||||
amount: isNativeToken ? EtherAmount.inWei(amount) : EtherAmount.zero(),
|
||||
data: data != null ? hexToBytes(data) : null,
|
||||
);
|
||||
|
||||
final signedTransaction =
|
||||
await _client!.signTransaction(privateKey, transaction, chainId: chainId);
|
||||
Uint8List signedTransaction;
|
||||
|
||||
final Function _sendTransaction;
|
||||
|
||||
if (isEVMCompatibleChain) {
|
||||
_sendTransaction = () async => await sendTransaction(signedTransaction);
|
||||
if (isNativeToken) {
|
||||
signedTransaction = await _client!.signTransaction(privateKey, transaction, chainId: chainId);
|
||||
} else {
|
||||
final erc20 = ERC20(
|
||||
client: _client!,
|
||||
|
@ -123,16 +120,17 @@ abstract class EVMChainClient {
|
|||
chainId: chainId,
|
||||
);
|
||||
|
||||
_sendTransaction = () async {
|
||||
await erc20.transfer(
|
||||
EthereumAddress.fromHex(toAddress),
|
||||
amount,
|
||||
credentials: privateKey,
|
||||
transaction: transaction,
|
||||
);
|
||||
};
|
||||
signedTransaction = await erc20.transfer(
|
||||
EthereumAddress.fromHex(toAddress),
|
||||
amount,
|
||||
credentials: privateKey,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
_sendTransaction = () async => await sendTransaction(signedTransaction);
|
||||
|
||||
|
||||
return PendingEVMChainTransaction(
|
||||
signedTransaction: signedTransaction,
|
||||
amount: amount.toString(),
|
||||
|
@ -158,8 +156,9 @@ abstract class EVMChainClient {
|
|||
);
|
||||
}
|
||||
|
||||
Future<String> sendTransaction(Uint8List signedTransaction) async =>
|
||||
await _client!.sendRawTransaction(prepareSignedTransactionForSending(signedTransaction));
|
||||
Future<String> sendTransaction(Uint8List signedTransaction) async {
|
||||
return await _client!.sendRawTransaction(prepareSignedTransactionForSending(signedTransaction));
|
||||
}
|
||||
|
||||
Future getTransactionDetails(String transactionHash) async {
|
||||
// Wait for the transaction receipt to become available
|
||||
|
@ -234,14 +233,17 @@ abstract class EVMChainClient {
|
|||
|
||||
final decodedResponse = jsonDecode(response.body)[0] as Map<String, dynamic>;
|
||||
|
||||
|
||||
final symbol = (decodedResponse['symbol'] ?? '') as String;
|
||||
String filteredSymbol = symbol.replaceFirst(RegExp('^\\\$'), '');
|
||||
|
||||
final name = decodedResponse['name'] ?? '';
|
||||
final symbol = decodedResponse['symbol'] ?? '';
|
||||
final decimal = decodedResponse['decimals'] ?? '0';
|
||||
final iconPath = decodedResponse['logo'] ?? '';
|
||||
|
||||
return Erc20Token(
|
||||
name: name,
|
||||
symbol: symbol,
|
||||
symbol: filteredSymbol,
|
||||
contractAddress: contractAddress,
|
||||
decimal: int.tryParse(decimal) ?? 0,
|
||||
iconPath: iconPath,
|
||||
|
|
|
@ -6,6 +6,8 @@ class EVMChainTransactionCreationException implements Exception {
|
|||
EVMChainTransactionCreationException(CryptoCurrency currency)
|
||||
: exceptionMessage = 'Wrong balance. Not enough ${currency.title} on your balance.';
|
||||
|
||||
EVMChainTransactionCreationException.fromMessage(this.exceptionMessage);
|
||||
|
||||
@override
|
||||
String toString() => exceptionMessage;
|
||||
}
|
||||
|
|
35
cw_evm/lib/evm_chain_hardware_wallet_service.dart
Normal file
35
cw_evm/lib/evm_chain_hardware_wallet_service.dart
Normal file
|
@ -0,0 +1,35 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:cw_core/hardware/hardware_account_data.dart';
|
||||
import 'package:ledger_ethereum/ledger_ethereum.dart';
|
||||
import 'package:ledger_flutter/ledger_flutter.dart';
|
||||
|
||||
class EVMChainHardwareWalletService {
|
||||
EVMChainHardwareWalletService(this.ledger, this.device);
|
||||
|
||||
final Ledger ledger;
|
||||
final LedgerDevice device;
|
||||
|
||||
Future<List<HardwareAccountData>> getAvailableAccounts({int index = 0, int limit = 5}) async {
|
||||
final ethereumLedgerApp = EthereumLedgerApp(ledger);
|
||||
|
||||
final version = await ethereumLedgerApp.getVersion(device);
|
||||
|
||||
final accounts = <HardwareAccountData>[];
|
||||
final indexRange = List.generate(limit, (i) => i + index);
|
||||
|
||||
for (final i in indexRange) {
|
||||
final derivationPath = "m/44'/60'/$i'/0/0";
|
||||
final address =
|
||||
await ethereumLedgerApp.getAccounts(device, accountsDerivationPath: derivationPath);
|
||||
|
||||
accounts.add(HardwareAccountData(
|
||||
address: address.first,
|
||||
accountIndex: i,
|
||||
derivationPath: derivationPath,
|
||||
));
|
||||
}
|
||||
|
||||
return accounts;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/output_info.dart';
|
||||
import 'package:cw_evm/evm_chain_transaction_priority.dart';
|
||||
import 'package:ledger_flutter/ledger_flutter.dart';
|
||||
|
||||
class EVMChainTransactionCredentials {
|
||||
EVMChainTransactionCredentials(
|
||||
|
|
|
@ -25,6 +25,7 @@ import 'package:cw_evm/evm_chain_transaction_history.dart';
|
|||
import 'package:cw_evm/evm_chain_transaction_model.dart';
|
||||
import 'package:cw_evm/evm_chain_transaction_priority.dart';
|
||||
import 'package:cw_evm/evm_chain_wallet_addresses.dart';
|
||||
import 'package:cw_evm/evm_ledger_credentials.dart';
|
||||
import 'package:cw_evm/file.dart';
|
||||
import 'package:hex/hex.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
@ -83,9 +84,9 @@ abstract class EVMChainWalletBase
|
|||
|
||||
late final Box<Erc20Token> evmChainErc20TokensBox;
|
||||
|
||||
late final EthPrivateKey _evmChainPrivateKey;
|
||||
late final Credentials _evmChainPrivateKey;
|
||||
|
||||
EthPrivateKey get evmChainPrivateKey => _evmChainPrivateKey;
|
||||
Credentials get evmChainPrivateKey => _evmChainPrivateKey;
|
||||
|
||||
late EVMChainClient _client;
|
||||
|
||||
|
@ -141,12 +142,18 @@ abstract class EVMChainWalletBase
|
|||
|
||||
await walletAddresses.init();
|
||||
await transactionHistory.init();
|
||||
_evmChainPrivateKey = await getPrivateKey(
|
||||
mnemonic: _mnemonic,
|
||||
privateKey: _hexPrivateKey,
|
||||
password: _password,
|
||||
);
|
||||
walletAddresses.address = _evmChainPrivateKey.address.hexEip55;
|
||||
|
||||
if (walletInfo.isHardwareWallet) {
|
||||
_evmChainPrivateKey = EvmLedgerCredentials(walletInfo.address);
|
||||
walletAddresses.address = walletInfo.address;
|
||||
} else {
|
||||
_evmChainPrivateKey = await getPrivateKey(
|
||||
mnemonic: _mnemonic,
|
||||
privateKey: _hexPrivateKey,
|
||||
password: _password,
|
||||
);
|
||||
walletAddresses.address = _evmChainPrivateKey.address.hexEip55;
|
||||
}
|
||||
await save();
|
||||
}
|
||||
|
||||
|
@ -283,6 +290,11 @@ abstract class EVMChainWalletBase
|
|||
}
|
||||
}
|
||||
|
||||
if (transactionCurrency is Erc20Token && isHardwareWallet) {
|
||||
await (_evmChainPrivateKey as EvmLedgerCredentials)
|
||||
.provideERC20Info(transactionCurrency.contractAddress, _client.chainId);
|
||||
}
|
||||
|
||||
final pendingEVMChainTransaction = await _client.signTransaction(
|
||||
privateKey: _evmChainPrivateKey,
|
||||
toAddress: _credentials.outputs.first.isParsedAddress
|
||||
|
@ -377,7 +389,9 @@ abstract class EVMChainWalletBase
|
|||
String? get seed => _mnemonic;
|
||||
|
||||
@override
|
||||
String get privateKey => HEX.encode(_evmChainPrivateKey.privateKey);
|
||||
String? get privateKey => evmChainPrivateKey is EthPrivateKey
|
||||
? HEX.encode((evmChainPrivateKey as EthPrivateKey).privateKey)
|
||||
: null;
|
||||
|
||||
Future<String> makePath() async => pathForWallet(name: walletInfo.name, type: walletInfo.type);
|
||||
|
||||
|
@ -468,9 +482,15 @@ abstract class EVMChainWalletBase
|
|||
await token.delete();
|
||||
|
||||
balance.remove(token);
|
||||
await _removeTokenTransactionsInHistory(token);
|
||||
_updateBalance();
|
||||
}
|
||||
|
||||
Future<void> _removeTokenTransactionsInHistory(Erc20Token token) async {
|
||||
transactionHistory.transactions.removeWhere((key, value) => value.tokenSymbol == token.title);
|
||||
await transactionHistory.save();
|
||||
}
|
||||
|
||||
Future<Erc20Token?> getErc20Token(String contractAddress, String chainName) async =>
|
||||
await _client.getErc20Token(contractAddress, chainName);
|
||||
|
||||
|
@ -529,8 +549,8 @@ abstract class EVMChainWalletBase
|
|||
}
|
||||
|
||||
@override
|
||||
String signMessage(String message, {String? address}) =>
|
||||
bytesToHex(_evmChainPrivateKey.signPersonalMessageToUint8List(ascii.encode(message)));
|
||||
Future<String> signMessage(String message, {String? address}) async =>
|
||||
bytesToHex(await _evmChainPrivateKey.signPersonalMessage(ascii.encode(message)));
|
||||
|
||||
Web3Client? getWeb3Client() => _client.getWeb3Client();
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:cw_core/hardware/hardware_account_data.dart';
|
||||
import 'package:cw_core/wallet_credentials.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
|
||||
|
@ -27,3 +28,13 @@ class EVMChainRestoreWalletFromPrivateKey extends WalletCredentials {
|
|||
|
||||
final String privateKey;
|
||||
}
|
||||
|
||||
class EVMChainRestoreWalletFromHardware extends WalletCredentials {
|
||||
EVMChainRestoreWalletFromHardware({
|
||||
required String name,
|
||||
required this.hwAccountData,
|
||||
WalletInfo? walletInfo,
|
||||
}) : super(name: name, walletInfo: walletInfo);
|
||||
|
||||
final HardwareAccountData hwAccountData;
|
||||
}
|
||||
|
|
|
@ -13,7 +13,8 @@ import 'package:hive/hive.dart';
|
|||
abstract class EVMChainWalletService<T extends EVMChainWallet> extends WalletService<
|
||||
EVMChainNewWalletCredentials,
|
||||
EVMChainRestoreWalletFromSeedCredentials,
|
||||
EVMChainRestoreWalletFromPrivateKey> {
|
||||
EVMChainRestoreWalletFromPrivateKey,
|
||||
EVMChainRestoreWalletFromHardware> {
|
||||
EVMChainWalletService(this.walletInfoSource);
|
||||
|
||||
final Box<WalletInfo> walletInfoSource;
|
||||
|
@ -24,6 +25,9 @@ abstract class EVMChainWalletService<T extends EVMChainWallet> extends WalletSer
|
|||
@override
|
||||
Future<T> create(EVMChainNewWalletCredentials credentials, {bool? isTestnet});
|
||||
|
||||
@override
|
||||
Future<T> restoreFromHardwareWallet(EVMChainRestoreWalletFromHardware credentials);
|
||||
|
||||
@override
|
||||
Future<T> openWallet(String name, String password);
|
||||
|
||||
|
|
103
cw_evm/lib/evm_ledger_credentials.dart
Normal file
103
cw_evm/lib/evm_ledger_credentials.dart
Normal file
|
@ -0,0 +1,103 @@
|
|||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:cw_core/hardware/device_not_connected_exception.dart';
|
||||
import 'package:ledger_ethereum/ledger_ethereum.dart';
|
||||
import 'package:ledger_flutter/ledger_flutter.dart';
|
||||
import 'package:web3dart/crypto.dart';
|
||||
import 'package:web3dart/web3dart.dart';
|
||||
|
||||
class EvmLedgerCredentials extends CredentialsWithKnownAddress {
|
||||
final String _address;
|
||||
|
||||
Ledger? ledger;
|
||||
LedgerDevice? ledgerDevice;
|
||||
EthereumLedgerApp? ethereumLedgerApp;
|
||||
|
||||
EvmLedgerCredentials(this._address);
|
||||
|
||||
@override
|
||||
EthereumAddress get address => EthereumAddress.fromHex(_address);
|
||||
|
||||
void setLedger(Ledger setLedger, [LedgerDevice? setLedgerDevice, String? derivationPath]) {
|
||||
ledger = setLedger;
|
||||
ledgerDevice = setLedgerDevice;
|
||||
ethereumLedgerApp =
|
||||
EthereumLedgerApp(ledger!, derivationPath: derivationPath ?? "m/44'/60'/0'/0/0");
|
||||
}
|
||||
|
||||
@override
|
||||
MsgSignature signToEcSignature(Uint8List payload, {int? chainId, bool isEIP1559 = false}) =>
|
||||
throw UnimplementedError("EvmLedgerCredentials.signToEcSignature");
|
||||
|
||||
@override
|
||||
Future<MsgSignature> signToSignature(Uint8List payload,
|
||||
{int? chainId, bool isEIP1559 = false}) async {
|
||||
if (ledgerDevice == null && ledger?.devices.isNotEmpty != true) {
|
||||
throw DeviceNotConnectedException();
|
||||
}
|
||||
|
||||
final sig = await ethereumLedgerApp!.signTransaction(device, payload);
|
||||
|
||||
final v = sig[0].toInt();
|
||||
final r = bytesToHex(sig.sublist(1, 1 + 32));
|
||||
final s = bytesToHex(sig.sublist(1 + 32, 1 + 32 + 32));
|
||||
|
||||
var truncChainId = chainId ?? 1;
|
||||
while (truncChainId.bitLength > 32) {
|
||||
truncChainId >>= 8;
|
||||
}
|
||||
|
||||
final truncTarget = truncChainId * 2 + 35;
|
||||
|
||||
int parity = v;
|
||||
if (truncTarget & 0xff == v) {
|
||||
parity = 0;
|
||||
} else if ((truncTarget + 1) & 0xff == v) {
|
||||
parity = 1;
|
||||
}
|
||||
|
||||
// https://github.com/ethereumjs/ethereumjs-util/blob/8ffe697fafb33cefc7b7ec01c11e3a7da787fe0e/src/signature.ts#L26
|
||||
int chainIdV;
|
||||
if (isEIP1559) {
|
||||
chainIdV = v;
|
||||
} else {
|
||||
chainIdV = chainId != null ? (parity + (chainId * 2 + 35)) : parity;
|
||||
}
|
||||
|
||||
return MsgSignature(BigInt.parse(r, radix: 16), BigInt.parse(s, radix: 16), chainIdV);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Uint8List> signPersonalMessage(Uint8List payload, {int? chainId}) async {
|
||||
if (isNotConnected) throw DeviceNotConnectedException();
|
||||
|
||||
final sig = await ethereumLedgerApp!.signMessage(device, payload);
|
||||
|
||||
final r = sig.sublist(1, 1 + 32);
|
||||
final s = sig.sublist(1 + 32, 1 + 32 + 32);
|
||||
final v = [sig[0]];
|
||||
|
||||
// https://github.com/ethereumjs/ethereumjs-util/blob/8ffe697fafb33cefc7b7ec01c11e3a7da787fe0e/src/signature.ts#L63
|
||||
return Uint8List.fromList(r + s + v);
|
||||
}
|
||||
|
||||
@override
|
||||
Uint8List signPersonalMessageToUint8List(Uint8List payload, {int? chainId}) =>
|
||||
throw UnimplementedError("EvmLedgerCredentials.signPersonalMessageToUint8List");
|
||||
|
||||
Future<void> provideERC20Info(String erc20ContractAddress, int chainId) async {
|
||||
if (isNotConnected) throw DeviceNotConnectedException();
|
||||
|
||||
try {
|
||||
await ethereumLedgerApp!.getAndProvideERC20TokenInformation(device,
|
||||
erc20ContractAddress: erc20ContractAddress, chainId: chainId);
|
||||
} on LedgerException catch (e) {
|
||||
if (e.errorCode != -28672) rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
bool get isNotConnected => (ledgerDevice ?? ledger?.devices.firstOrNull) == null;
|
||||
|
||||
LedgerDevice get device => ledgerDevice ?? ledger!.devices.first;
|
||||
}
|
|
@ -13,7 +13,6 @@ dependencies:
|
|||
flutter:
|
||||
sdk: flutter
|
||||
web3dart: ^2.7.1
|
||||
erc20: ^1.0.1
|
||||
bip39: ^1.0.6
|
||||
bip32: ^2.0.0
|
||||
hex: ^0.2.0
|
||||
|
@ -23,11 +22,26 @@ dependencies:
|
|||
shared_preferences: ^2.0.15
|
||||
cw_core:
|
||||
path: ../cw_core
|
||||
ledger_flutter: ^1.0.1
|
||||
ledger_ethereum:
|
||||
git:
|
||||
url: https://github.com/cake-tech/ledger-ethereum.git
|
||||
|
||||
dependency_overrides:
|
||||
web3dart:
|
||||
git:
|
||||
url: https://github.com/cake-tech/web3dart.git
|
||||
ref: cake
|
||||
ledger_flutter:
|
||||
git:
|
||||
url: https://github.com/cake-tech/ledger-flutter.git
|
||||
ref: cake
|
||||
watcher: ^1.1.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
build_runner: ^2.1.11
|
||||
build_runner: ^2.4.7
|
||||
mobx_codegen: ^2.0.7
|
||||
hive_generator: ^1.1.3
|
||||
flutter_lints: ^2.0.0
|
||||
|
|
|
@ -56,7 +56,8 @@ class HavenRestoreWalletFromKeysCredentials extends WalletCredentials {
|
|||
class HavenWalletService extends WalletService<
|
||||
HavenNewWalletCredentials,
|
||||
HavenRestoreWalletFromSeedCredentials,
|
||||
HavenRestoreWalletFromKeysCredentials> {
|
||||
HavenRestoreWalletFromKeysCredentials,
|
||||
HavenNewWalletCredentials> {
|
||||
HavenWalletService(this.walletInfoSource);
|
||||
|
||||
final Box<WalletInfo> walletInfoSource;
|
||||
|
@ -172,6 +173,11 @@ class HavenWalletService extends WalletService<
|
|||
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<HavenWallet> restoreFromHardwareWallet(HavenNewWalletCredentials credentials) {
|
||||
throw UnimplementedError("Restoring a Haven wallet from a hardware wallet is not yet supported!");
|
||||
}
|
||||
|
||||
@override
|
||||
Future<HavenWallet> restoreFromKeys(
|
||||
HavenRestoreWalletFromKeysCredentials credentials, {bool? isTestnet}) async {
|
||||
|
|
|
@ -69,10 +69,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: build_daemon
|
||||
sha256: "6bc5544ea6ce4428266e7ea680e945c68806c4aae2da0eb5e9ccf38df8d6acbf"
|
||||
sha256: "5f02d73eb2ba16483e693f80bee4f088563a820e47d1027d4cdfe62b5bb43e65"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.0"
|
||||
version: "4.0.0"
|
||||
build_resolvers:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
|
@ -85,10 +85,10 @@ packages:
|
|||
dependency: "direct dev"
|
||||
description:
|
||||
name: build_runner
|
||||
sha256: b0a8a7b8a76c493e85f1b84bffa0588859a06197863dba8c9036b15581fd9727
|
||||
sha256: "3ac61a79bfb6f6cc11f693591063a7f19a7af628dc52f141743edac5c16e8c22"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.3"
|
||||
version: "2.4.9"
|
||||
build_runner_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -149,10 +149,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
|
||||
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.17.1"
|
||||
version: "1.18.0"
|
||||
convert:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -338,6 +338,30 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.8.0"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker
|
||||
sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.0.0"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_flutter_testing
|
||||
sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
leak_tracker_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_testing
|
||||
sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -350,26 +374,26 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
|
||||
sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.15"
|
||||
version: "0.12.16+1"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
|
||||
sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.0"
|
||||
version: "0.8.0"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
|
||||
sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
version: "1.11.0"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -406,10 +430,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: path
|
||||
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
|
||||
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.8.3"
|
||||
version: "1.9.0"
|
||||
path_provider:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -563,26 +587,26 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
|
||||
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
version: "1.10.0"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
|
||||
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.11.0"
|
||||
version: "1.11.1"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_channel
|
||||
sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
|
||||
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
version: "2.1.2"
|
||||
stream_transform:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -611,10 +635,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
|
||||
sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.1"
|
||||
version: "0.6.1"
|
||||
timing:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -639,14 +663,22 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
watcher:
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: watcher
|
||||
sha256: "6a7f46926b01ce81bfc339da6a7f20afbe7733eff9846f6d6a5466aa4c6667c0"
|
||||
name: vm_service
|
||||
sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
version: "13.0.0"
|
||||
watcher:
|
||||
dependency: "direct overridden"
|
||||
description:
|
||||
name: watcher
|
||||
sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
web_socket_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -680,5 +712,5 @@ packages:
|
|||
source: hosted
|
||||
version: "3.1.1"
|
||||
sdks:
|
||||
dart: ">=3.0.0 <4.0.0"
|
||||
dart: ">=3.2.0-0 <4.0.0"
|
||||
flutter: ">=3.7.0"
|
||||
|
|
|
@ -24,11 +24,14 @@ dependencies:
|
|||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
build_runner: ^2.1.11
|
||||
build_runner: ^2.4.7
|
||||
mobx_codegen: ^2.0.7
|
||||
build_resolvers: ^2.0.9
|
||||
hive_generator: ^1.1.3
|
||||
|
||||
dependency_overrides:
|
||||
watcher: ^1.1.0
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
||||
|
|
|
@ -53,10 +53,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
|
||||
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.17.1"
|
||||
version: "1.18.0"
|
||||
convert:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -201,6 +201,30 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.7"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker
|
||||
sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.0.0"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_flutter_testing
|
||||
sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
leak_tracker_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_testing
|
||||
sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
lints:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -213,26 +237,26 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
|
||||
sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.15"
|
||||
version: "0.12.16+1"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
|
||||
sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.0"
|
||||
version: "0.8.0"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
|
||||
sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
version: "1.11.0"
|
||||
mobx:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -245,10 +269,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: path
|
||||
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
|
||||
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.8.3"
|
||||
version: "1.9.0"
|
||||
path_provider:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -354,26 +378,26 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
|
||||
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
version: "1.10.0"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
|
||||
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.11.0"
|
||||
version: "1.11.1"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_channel
|
||||
sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
|
||||
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
version: "2.1.2"
|
||||
string_scanner:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -394,10 +418,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
|
||||
sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.1"
|
||||
version: "0.6.1"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -414,6 +438,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "13.0.0"
|
||||
win32:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -431,5 +463,5 @@ packages:
|
|||
source: hosted
|
||||
version: "0.2.0+3"
|
||||
sdks:
|
||||
dart: ">=3.0.6 <4.0.0"
|
||||
dart: ">=3.2.0-0 <4.0.0"
|
||||
flutter: ">=3.7.0"
|
||||
|
|
|
@ -17,6 +17,9 @@ typedef restore_wallet_from_keys = Int8 Function(Pointer<Utf8>, Pointer<Utf8>, P
|
|||
typedef restore_wallet_from_spend_key = Int8 Function(Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>,
|
||||
Pointer<Utf8>, Pointer<Utf8>, Int32, Int64, Pointer<Utf8>);
|
||||
|
||||
// typedef restore_wallet_from_device = Int8 Function(Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>,
|
||||
// Int32, Int64, Pointer<Utf8>);
|
||||
|
||||
typedef is_wallet_exist = Int8 Function(Pointer<Utf8>);
|
||||
|
||||
typedef load_wallet = Int8 Function(Pointer<Utf8>, Pointer<Utf8>, Int8);
|
||||
|
|
|
@ -17,6 +17,9 @@ typedef RestoreWalletFromKeys = int Function(Pointer<Utf8>, Pointer<Utf8>,
|
|||
typedef RestoreWalletFromSpendKey = int Function(Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>,
|
||||
Pointer<Utf8>, Pointer<Utf8>, int, int, Pointer<Utf8>);
|
||||
|
||||
typedef RestoreWalletFromDevice = int Function(Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>,
|
||||
int, int, Pointer<Utf8>);
|
||||
|
||||
typedef IsWalletExist = int Function(Pointer<Utf8>);
|
||||
|
||||
typedef LoadWallet = int Function(Pointer<Utf8>, Pointer<Utf8>, int);
|
||||
|
|
|
@ -31,6 +31,11 @@ final restoreWalletFromSpendKeyNative = moneroApi
|
|||
'restore_wallet_from_spend_key')
|
||||
.asFunction<RestoreWalletFromSpendKey>();
|
||||
|
||||
// final restoreWalletFromDeviceNative = moneroApi
|
||||
// .lookup<NativeFunction<restore_wallet_from_device>>(
|
||||
// 'restore_wallet_from_device')
|
||||
// .asFunction<RestoreWalletFromDevice>();
|
||||
|
||||
final isWalletExistNative = moneroApi
|
||||
.lookup<NativeFunction<is_wallet_exist>>('is_wallet_exist')
|
||||
.asFunction<IsWalletExist>();
|
||||
|
@ -185,6 +190,38 @@ void restoreWalletFromSpendKeySync(
|
|||
}
|
||||
}
|
||||
|
||||
// void restoreMoneroWalletFromDevice(
|
||||
// {required String path,
|
||||
// required String password,
|
||||
// required String deviceName,
|
||||
// int nettype = 0,
|
||||
// int restoreHeight = 0}) {
|
||||
//
|
||||
// final pathPointer = path.toNativeUtf8();
|
||||
// final passwordPointer = password.toNativeUtf8();
|
||||
// final deviceNamePointer = deviceName.toNativeUtf8();
|
||||
// final errorMessagePointer = ''.toNativeUtf8();
|
||||
//
|
||||
// final isWalletRestored = restoreWalletFromDeviceNative(
|
||||
// pathPointer,
|
||||
// passwordPointer,
|
||||
// deviceNamePointer,
|
||||
// nettype,
|
||||
// restoreHeight,
|
||||
// errorMessagePointer) != 0;
|
||||
//
|
||||
// calloc.free(pathPointer);
|
||||
// calloc.free(passwordPointer);
|
||||
//
|
||||
// storeSync();
|
||||
//
|
||||
// if (!isWalletRestored) {
|
||||
// throw WalletRestoreFromKeysException(
|
||||
// message: convertUTF8ToString(pointer: errorMessagePointer));
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
void loadWallet({
|
||||
required String path,
|
||||
required String password,
|
||||
|
|
|
@ -686,7 +686,7 @@ abstract class MoneroWalletBase
|
|||
void setExceptionHandler(void Function(FlutterErrorDetails) e) => onError = e;
|
||||
|
||||
@override
|
||||
String signMessage(String message, {String? address}) {
|
||||
Future<String> signMessage(String message, {String? address}) async {
|
||||
final useAddress = address ?? "";
|
||||
return monero_wallet.signMessage(message, address: useAddress);
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ class MoneroRestoreWalletFromKeysCredentials extends WalletCredentials {
|
|||
}
|
||||
|
||||
class MoneroWalletService extends WalletService<MoneroNewWalletCredentials,
|
||||
MoneroRestoreWalletFromSeedCredentials, MoneroRestoreWalletFromKeysCredentials> {
|
||||
MoneroRestoreWalletFromSeedCredentials, MoneroRestoreWalletFromKeysCredentials, MoneroNewWalletCredentials> {
|
||||
MoneroWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
|
||||
|
||||
final Box<WalletInfo> walletInfoSource;
|
||||
|
@ -227,6 +227,11 @@ class MoneroWalletService extends WalletService<MoneroNewWalletCredentials,
|
|||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MoneroWallet> restoreFromHardwareWallet(MoneroNewWalletCredentials credentials) {
|
||||
throw UnimplementedError("Restoring a Monero wallet from a hardware wallet is not yet supported!");
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MoneroWallet> restoreFromSeed(MoneroRestoreWalletFromSeedCredentials credentials,
|
||||
{bool? isTestnet}) async {
|
||||
|
|
|
@ -69,10 +69,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: build_daemon
|
||||
sha256: "6bc5544ea6ce4428266e7ea680e945c68806c4aae2da0eb5e9ccf38df8d6acbf"
|
||||
sha256: "5f02d73eb2ba16483e693f80bee4f088563a820e47d1027d4cdfe62b5bb43e65"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.0"
|
||||
version: "4.0.0"
|
||||
build_resolvers:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
|
@ -85,10 +85,10 @@ packages:
|
|||
dependency: "direct dev"
|
||||
description:
|
||||
name: build_runner
|
||||
sha256: b0a8a7b8a76c493e85f1b84bffa0588859a06197863dba8c9036b15581fd9727
|
||||
sha256: "3ac61a79bfb6f6cc11f693591063a7f19a7af628dc52f141743edac5c16e8c22"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.3"
|
||||
version: "2.4.9"
|
||||
build_runner_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -149,10 +149,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
|
||||
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.17.1"
|
||||
version: "1.18.0"
|
||||
convert:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -354,6 +354,30 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.8.0"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker
|
||||
sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.0.0"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_flutter_testing
|
||||
sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
leak_tracker_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_testing
|
||||
sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -366,26 +390,26 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
|
||||
sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.15"
|
||||
version: "0.12.16+1"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
|
||||
sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.0"
|
||||
version: "0.8.0"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
|
||||
sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
version: "1.11.0"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -422,10 +446,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: path
|
||||
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
|
||||
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.8.3"
|
||||
version: "1.9.0"
|
||||
path_provider:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -587,26 +611,26 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
|
||||
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
version: "1.10.0"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
|
||||
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.11.0"
|
||||
version: "1.11.1"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_channel
|
||||
sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
|
||||
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
version: "2.1.2"
|
||||
stream_transform:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -635,10 +659,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
|
||||
sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.1"
|
||||
version: "0.6.1"
|
||||
timing:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -663,14 +687,22 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
watcher:
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: watcher
|
||||
sha256: "6a7f46926b01ce81bfc339da6a7f20afbe7733eff9846f6d6a5466aa4c6667c0"
|
||||
name: vm_service
|
||||
sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
version: "13.0.0"
|
||||
watcher:
|
||||
dependency: "direct overridden"
|
||||
description:
|
||||
name: watcher
|
||||
sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
web_socket_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -704,5 +736,5 @@ packages:
|
|||
source: hosted
|
||||
version: "3.1.1"
|
||||
sdks:
|
||||
dart: ">=3.0.6 <4.0.0"
|
||||
dart: ">=3.2.0-0 <4.0.0"
|
||||
flutter: ">=3.7.0"
|
||||
|
|
|
@ -26,11 +26,14 @@ dependencies:
|
|||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
build_runner: ^2.1.11
|
||||
build_runner: ^2.4.7
|
||||
build_resolvers: ^2.0.9
|
||||
mobx_codegen: ^2.0.7
|
||||
hive_generator: ^1.1.3
|
||||
|
||||
dependency_overrides:
|
||||
watcher: ^1.1.0
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
||||
|
|
|
@ -1,12 +1,28 @@
|
|||
import 'package:cw_core/balance.dart';
|
||||
import 'package:nanoutil/nanoutil.dart';
|
||||
|
||||
BigInt stringAmountToBigIntBanano(String amount) {
|
||||
return BigInt.parse(NanoAmounts.getAmountAsRaw(amount, NanoAmounts.rawPerBanano));
|
||||
}
|
||||
|
||||
class BananoBalance extends Balance {
|
||||
final BigInt currentBalance;
|
||||
final BigInt receivableBalance;
|
||||
|
||||
BananoBalance({required this.currentBalance, required this.receivableBalance}) : super(0, 0);
|
||||
|
||||
BananoBalance.fromFormattedString(
|
||||
{required String formattedCurrentBalance, required String formattedReceivableBalance})
|
||||
: currentBalance = stringAmountToBigIntBanano(formattedCurrentBalance),
|
||||
receivableBalance = stringAmountToBigIntBanano(formattedReceivableBalance),
|
||||
super(0, 0);
|
||||
|
||||
BananoBalance.fromRawString(
|
||||
{required String currentBalance, required String receivableBalance})
|
||||
: currentBalance = BigInt.parse(currentBalance),
|
||||
receivableBalance = BigInt.parse(receivableBalance),
|
||||
super(0, 0);
|
||||
|
||||
@override
|
||||
String get formattedAvailableBalance {
|
||||
return NanoAmounts.getRawAsUsableString(currentBalance.toString(), NanoAmounts.rawPerBanano);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import 'package:cw_core/balance.dart';
|
||||
import 'package:nanoutil/nanoutil.dart';
|
||||
|
||||
BigInt stringAmountToBigInt(String amount) {
|
||||
BigInt stringAmountToBigIntNano(String amount) {
|
||||
return BigInt.parse(NanoAmounts.getAmountAsRaw(amount, NanoAmounts.rawPerNano));
|
||||
}
|
||||
|
||||
|
@ -13,8 +13,8 @@ class NanoBalance extends Balance {
|
|||
|
||||
NanoBalance.fromFormattedString(
|
||||
{required String formattedCurrentBalance, required String formattedReceivableBalance})
|
||||
: currentBalance = stringAmountToBigInt(formattedCurrentBalance),
|
||||
receivableBalance = stringAmountToBigInt(formattedReceivableBalance),
|
||||
: currentBalance = stringAmountToBigIntNano(formattedCurrentBalance),
|
||||
receivableBalance = stringAmountToBigIntNano(formattedReceivableBalance),
|
||||
super(0, 0);
|
||||
|
||||
NanoBalance.fromRawString(
|
||||
|
|
|
@ -10,6 +10,7 @@ import 'package:nanodart/nanodart.dart';
|
|||
import 'package:cw_core/node.dart';
|
||||
import 'package:nanoutil/nanoutil.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:cw_nano/.secrets.g.dart' as secrets;
|
||||
|
||||
class NanoClient {
|
||||
static const Map<String, String> CAKE_HEADERS = {
|
||||
|
@ -52,10 +53,19 @@ class NanoClient {
|
|||
}
|
||||
}
|
||||
|
||||
Map<String, String> getHeaders() {
|
||||
if (_node!.uri == "https://rpc.nano.to") {
|
||||
return CAKE_HEADERS..addAll({
|
||||
"key": secrets.nano2ApiKey,
|
||||
});
|
||||
}
|
||||
return CAKE_HEADERS;
|
||||
}
|
||||
|
||||
Future<NanoBalance> getBalance(String address) async {
|
||||
final response = await http.post(
|
||||
_node!.uri,
|
||||
headers: CAKE_HEADERS,
|
||||
headers: getHeaders(),
|
||||
body: jsonEncode(
|
||||
{
|
||||
"action": "account_balance",
|
||||
|
@ -82,7 +92,7 @@ class NanoClient {
|
|||
try {
|
||||
final response = await http.post(
|
||||
_node!.uri,
|
||||
headers: CAKE_HEADERS,
|
||||
headers: getHeaders(),
|
||||
body: jsonEncode(
|
||||
{
|
||||
"action": "account_info",
|
||||
|
@ -94,7 +104,7 @@ class NanoClient {
|
|||
final data = await jsonDecode(response.body);
|
||||
return AccountInfoResponse.fromJson(data as Map<String, dynamic>);
|
||||
} catch (e) {
|
||||
print("error while getting account info");
|
||||
print("error while getting account info $e");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -149,7 +159,7 @@ class NanoClient {
|
|||
Future<String> requestWork(String hash) async {
|
||||
final response = await http.post(
|
||||
_powNode!.uri,
|
||||
headers: CAKE_HEADERS,
|
||||
headers: getHeaders(),
|
||||
body: json.encode(
|
||||
{
|
||||
"action": "work_generate",
|
||||
|
@ -192,7 +202,7 @@ class NanoClient {
|
|||
|
||||
final processResponse = await http.post(
|
||||
_node!.uri,
|
||||
headers: CAKE_HEADERS,
|
||||
headers: getHeaders(),
|
||||
body: processBody,
|
||||
);
|
||||
|
||||
|
@ -351,7 +361,7 @@ class NanoClient {
|
|||
});
|
||||
final processResponse = await http.post(
|
||||
_node!.uri,
|
||||
headers: CAKE_HEADERS,
|
||||
headers: getHeaders(),
|
||||
body: processBody,
|
||||
);
|
||||
|
||||
|
@ -367,7 +377,7 @@ class NanoClient {
|
|||
required String privateKey,
|
||||
}) async {
|
||||
final receivableResponse = await http.post(_node!.uri,
|
||||
headers: CAKE_HEADERS,
|
||||
headers: getHeaders(),
|
||||
body: jsonEncode({
|
||||
"action": "receivable",
|
||||
"account": destinationAddress,
|
||||
|
@ -417,7 +427,7 @@ class NanoClient {
|
|||
Future<List<NanoTransactionModel>> fetchTransactions(String address) async {
|
||||
try {
|
||||
final response = await http.post(_node!.uri,
|
||||
headers: CAKE_HEADERS,
|
||||
headers: getHeaders(),
|
||||
body: jsonEncode({
|
||||
"action": "account_history",
|
||||
"account": address,
|
||||
|
|
|
@ -43,7 +43,7 @@ abstract class NanoWalletBase
|
|||
}) : syncStatus = NotConnectedSyncStatus(),
|
||||
_password = password,
|
||||
_mnemonic = mnemonic,
|
||||
_derivationType = walletInfo.derivationType!,
|
||||
_derivationType = walletInfo.derivationInfo!.derivationType!,
|
||||
_isTransactionUpdating = false,
|
||||
_client = NanoClient(),
|
||||
walletAddresses = NanoWalletAddresses(walletInfo),
|
||||
|
@ -389,7 +389,10 @@ abstract class NanoWalletBase
|
|||
derivationType = DerivationType.bip39;
|
||||
}
|
||||
|
||||
walletInfo.derivationType = derivationType;
|
||||
walletInfo.derivationInfo ??= DerivationInfo(derivationType: derivationType);
|
||||
if (walletInfo.derivationInfo!.derivationType == null) {
|
||||
walletInfo.derivationInfo!.derivationType = derivationType;
|
||||
}
|
||||
|
||||
return NanoWallet(
|
||||
walletInfo: walletInfo,
|
||||
|
|
|
@ -2,8 +2,15 @@ import 'package:cw_core/wallet_credentials.dart';
|
|||
import 'package:cw_core/wallet_info.dart';
|
||||
|
||||
class NanoNewWalletCredentials extends WalletCredentials {
|
||||
NanoNewWalletCredentials({required String name, String? password})
|
||||
: super(name: name, password: password);
|
||||
NanoNewWalletCredentials({
|
||||
required String name,
|
||||
String? password,
|
||||
DerivationType? derivationType,
|
||||
}) : super(
|
||||
name: name,
|
||||
password: password,
|
||||
derivationInfo: DerivationInfo(derivationType: derivationType),
|
||||
);
|
||||
}
|
||||
|
||||
class NanoRestoreWalletFromSeedCredentials extends WalletCredentials {
|
||||
|
@ -11,11 +18,11 @@ class NanoRestoreWalletFromSeedCredentials extends WalletCredentials {
|
|||
required String name,
|
||||
required this.mnemonic,
|
||||
String? password,
|
||||
DerivationType? derivationType,
|
||||
required DerivationType derivationType,
|
||||
}) : super(
|
||||
name: name,
|
||||
password: password,
|
||||
derivationType: derivationType,
|
||||
derivationInfo: DerivationInfo(derivationType: derivationType),
|
||||
);
|
||||
|
||||
final String mnemonic;
|
||||
|
@ -30,12 +37,12 @@ class NanoRestoreWalletFromKeysCredentials extends WalletCredentials {
|
|||
NanoRestoreWalletFromKeysCredentials({
|
||||
required String name,
|
||||
required String password,
|
||||
required DerivationType derivationType,
|
||||
required this.seedKey,
|
||||
DerivationType? derivationType,
|
||||
}) : super(
|
||||
name: name,
|
||||
password: password,
|
||||
derivationType: derivationType,
|
||||
derivationInfo: DerivationInfo(derivationType: derivationType),
|
||||
);
|
||||
|
||||
final String seedKey;
|
||||
|
|
|
@ -14,7 +14,7 @@ import 'package:nanodart/nanodart.dart';
|
|||
import 'package:nanoutil/nanoutil.dart';
|
||||
|
||||
class NanoWalletService extends WalletService<NanoNewWalletCredentials,
|
||||
NanoRestoreWalletFromSeedCredentials, NanoRestoreWalletFromKeysCredentials> {
|
||||
NanoRestoreWalletFromSeedCredentials, NanoRestoreWalletFromKeysCredentials, NanoNewWalletCredentials> {
|
||||
NanoWalletService(this.walletInfoSource);
|
||||
|
||||
final Box<WalletInfo> walletInfoSource;
|
||||
|
@ -28,11 +28,11 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
|
|||
@override
|
||||
Future<WalletBase> create(NanoNewWalletCredentials credentials, {bool? isTestnet}) async {
|
||||
// nano standard:
|
||||
DerivationType derivationType = DerivationType.nano;
|
||||
String seedKey = NanoSeeds.generateSeed();
|
||||
String mnemonic = NanoDerivations.standardSeedToMnemonic(seedKey);
|
||||
|
||||
credentials.walletInfo!.derivationType = derivationType;
|
||||
// ensure default if not present:
|
||||
credentials.walletInfo!.derivationInfo ??= DerivationInfo(derivationType: DerivationType.nano);
|
||||
|
||||
final wallet = NanoWallet(
|
||||
walletInfo: credentials.walletInfo!,
|
||||
|
@ -88,9 +88,6 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
|
|||
}
|
||||
}
|
||||
|
||||
DerivationType derivationType = credentials.derivationType ?? DerivationType.nano;
|
||||
credentials.walletInfo!.derivationType = derivationType;
|
||||
|
||||
String? mnemonic;
|
||||
|
||||
// we can't derive the mnemonic from the key in all cases, only if it's a "nano" seed
|
||||
|
@ -112,6 +109,11 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
|
|||
return wallet;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<NanoWallet> restoreFromHardwareWallet(NanoNewWalletCredentials credentials) {
|
||||
throw UnimplementedError("Restoring a Nano wallet from a hardware wallet is not yet supported!");
|
||||
}
|
||||
|
||||
@override
|
||||
Future<NanoWallet> restoreFromSeed(NanoRestoreWalletFromSeedCredentials credentials, {bool? isTestnet}) async {
|
||||
if (credentials.mnemonic.contains(' ')) {
|
||||
|
@ -128,9 +130,10 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
|
|||
}
|
||||
}
|
||||
|
||||
DerivationType derivationType = credentials.derivationType ?? DerivationType.nano;
|
||||
DerivationType derivationType =
|
||||
credentials.walletInfo?.derivationInfo?.derivationType ?? DerivationType.nano;
|
||||
|
||||
credentials.walletInfo!.derivationType = derivationType;
|
||||
credentials.walletInfo!.derivationInfo ??= DerivationInfo(derivationType: derivationType);
|
||||
|
||||
final wallet = await NanoWallet(
|
||||
password: credentials.password!,
|
||||
|
|
|
@ -93,10 +93,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: build_daemon
|
||||
sha256: "757153e5d9cd88253cb13f28c2fb55a537dc31fefd98137549895b5beb7c6169"
|
||||
sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.1"
|
||||
version: "4.0.1"
|
||||
build_resolvers:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -109,10 +109,10 @@ packages:
|
|||
dependency: "direct dev"
|
||||
description:
|
||||
name: build_runner
|
||||
sha256: b0a8a7b8a76c493e85f1b84bffa0588859a06197863dba8c9036b15581fd9727
|
||||
sha256: "3ac61a79bfb6f6cc11f693591063a7f19a7af628dc52f141743edac5c16e8c22"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.3"
|
||||
version: "2.4.9"
|
||||
build_runner_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -173,10 +173,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
|
||||
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.17.1"
|
||||
version: "1.18.0"
|
||||
convert:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -399,6 +399,30 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.8.1"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker
|
||||
sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.0.0"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_flutter_testing
|
||||
sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
leak_tracker_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_testing
|
||||
sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
libcrypto:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -419,26 +443,26 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
|
||||
sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.15"
|
||||
version: "0.12.16+1"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
|
||||
sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.0"
|
||||
version: "0.8.0"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
|
||||
sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
version: "1.11.0"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -492,10 +516,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: path
|
||||
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
|
||||
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.8.3"
|
||||
version: "1.9.0"
|
||||
path_provider:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -713,26 +737,26 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
|
||||
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
version: "1.10.0"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
|
||||
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.11.0"
|
||||
version: "1.11.1"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_channel
|
||||
sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
|
||||
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
version: "2.1.2"
|
||||
stream_transform:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -761,10 +785,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
|
||||
sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.1"
|
||||
version: "0.6.1"
|
||||
timing:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -789,14 +813,22 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
watcher:
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: watcher
|
||||
sha256: "6a7f46926b01ce81bfc339da6a7f20afbe7733eff9846f6d6a5466aa4c6667c0"
|
||||
name: vm_service
|
||||
sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
version: "13.0.0"
|
||||
watcher:
|
||||
dependency: "direct overridden"
|
||||
description:
|
||||
name: watcher
|
||||
sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
web_socket_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -830,5 +862,5 @@ packages:
|
|||
source: hosted
|
||||
version: "3.1.2"
|
||||
sdks:
|
||||
dart: ">=3.0.0 <4.0.0"
|
||||
dart: ">=3.2.0-0 <4.0.0"
|
||||
flutter: ">=3.7.0"
|
||||
|
|
|
@ -32,10 +32,13 @@ dependencies:
|
|||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
build_runner: ^2.1.11
|
||||
build_runner: ^2.4.7
|
||||
mobx_codegen: ^2.0.7
|
||||
hive_generator: ^1.1.3
|
||||
|
||||
dependency_overrides:
|
||||
watcher: ^1.1.0
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:bip39/bip39.dart' as bip39;
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cw_evm/evm_chain_wallet_creation_credentials.dart';
|
||||
import 'package:cw_evm/evm_chain_wallet_service.dart';
|
||||
|
@ -86,6 +87,29 @@ class PolygonWalletService extends EVMChainWalletService<PolygonWallet> {
|
|||
return wallet;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<PolygonWallet> restoreFromHardwareWallet(
|
||||
EVMChainRestoreWalletFromHardware credentials) async {
|
||||
credentials.walletInfo!.derivationInfo = DerivationInfo(
|
||||
derivationType: DerivationType.bip39,
|
||||
derivationPath: "m/44'/60'/${credentials.hwAccountData.accountIndex}'/0/0"
|
||||
);
|
||||
credentials.walletInfo!.hardwareWalletType = credentials.hardwareWalletType;
|
||||
credentials.walletInfo!.address = credentials.hwAccountData.address;
|
||||
|
||||
final wallet = PolygonWallet(
|
||||
walletInfo: credentials.walletInfo!,
|
||||
password: credentials.password!,
|
||||
client: client,
|
||||
);
|
||||
|
||||
await wallet.init();
|
||||
wallet.addInitialTokens();
|
||||
await wallet.save();
|
||||
|
||||
return wallet;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<PolygonWallet> restoreFromSeed(EVMChainRestoreWalletFromSeedCredentials credentials,
|
||||
{bool? isTestnet}) async {
|
||||
|
|
|
@ -23,12 +23,19 @@ dependencies:
|
|||
bip39: ^1.0.6
|
||||
collection: ^1.17.1
|
||||
|
||||
dependency_overrides:
|
||||
web3dart:
|
||||
git:
|
||||
url: https://github.com/cake-tech/web3dart.git
|
||||
ref: cake
|
||||
watcher: ^1.1.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
flutter_lints: ^2.0.0
|
||||
build_runner: ^2.1.11
|
||||
build_runner: ^2.4.7
|
||||
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
|
|
@ -34,10 +34,7 @@ class SolanaTransactionInfo extends TransactionInfo {
|
|||
@override
|
||||
String amountFormatted() {
|
||||
String stringBalance = solAmount.toString();
|
||||
|
||||
if (stringBalance.toString().length >= 6) {
|
||||
stringBalance = stringBalance.substring(0, 6);
|
||||
}
|
||||
|
||||
return '$stringBalance $tokenSymbol';
|
||||
}
|
||||
|
||||
|
|
|
@ -145,12 +145,17 @@ abstract class SolanaWalletBase
|
|||
Future<Wallet> getWalletPair({String? mnemonic, String? privateKey}) async {
|
||||
assert(mnemonic != null || privateKey != null);
|
||||
|
||||
if (privateKey != null) {
|
||||
final privateKeyBytes = base58decode(privateKey);
|
||||
return await Wallet.fromPrivateKeyBytes(privateKey: privateKeyBytes.take(32).toList());
|
||||
if (mnemonic != null) {
|
||||
return Wallet.fromMnemonic(mnemonic, account: 0, change: 0);
|
||||
}
|
||||
|
||||
return Wallet.fromMnemonic(mnemonic!, account: 0, change: 0);
|
||||
try {
|
||||
final privateKeyBytes = base58decode(privateKey!);
|
||||
return await Wallet.fromPrivateKeyBytes(privateKey: privateKeyBytes.take(32).toList());
|
||||
} catch (_) {
|
||||
final privateKeyBytes = HEX.decode(privateKey!);
|
||||
return await Wallet.fromPrivateKeyBytes(privateKey: privateKeyBytes);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -273,32 +278,12 @@ abstract class SolanaWalletBase
|
|||
|
||||
final transactions = await _client.fetchTransactions(address);
|
||||
|
||||
final Map<String, SolanaTransactionInfo> result = {};
|
||||
|
||||
for (var transactionModel in transactions) {
|
||||
result[transactionModel.id] = SolanaTransactionInfo(
|
||||
id: transactionModel.id,
|
||||
to: transactionModel.to,
|
||||
from: transactionModel.from,
|
||||
blockTime: transactionModel.blockTime,
|
||||
direction: transactionModel.isOutgoingTx
|
||||
? TransactionDirection.outgoing
|
||||
: TransactionDirection.incoming,
|
||||
solAmount: transactionModel.amount,
|
||||
isPending: false,
|
||||
txFee: transactionModel.fee,
|
||||
tokenSymbol: transactionModel.tokenSymbol,
|
||||
);
|
||||
}
|
||||
|
||||
transactionHistory.addMany(result);
|
||||
|
||||
await transactionHistory.save();
|
||||
await _addTransactionsToTransactionHistory(transactions);
|
||||
}
|
||||
|
||||
/// Fetches the SPL Tokens transactions linked to the token account Public Key
|
||||
Future<void> _updateSPLTokenTransactions() async {
|
||||
List<SolanaTransactionModel> splTokenTransactions = [];
|
||||
// List<SolanaTransactionModel> splTokenTransactions = [];
|
||||
|
||||
// Make a copy of keys to avoid concurrent modification
|
||||
var tokenKeys = List<CryptoCurrency>.from(balance.keys);
|
||||
|
@ -312,13 +297,20 @@ abstract class SolanaWalletBase
|
|||
_walletKeyPair!,
|
||||
);
|
||||
|
||||
splTokenTransactions.addAll(tokenTxs);
|
||||
// splTokenTransactions.addAll(tokenTxs);
|
||||
await _addTransactionsToTransactionHistory(tokenTxs);
|
||||
}
|
||||
}
|
||||
|
||||
// await _addTransactionsToTransactionHistory(splTokenTransactions);
|
||||
}
|
||||
|
||||
Future<void> _addTransactionsToTransactionHistory(
|
||||
List<SolanaTransactionModel> transactions,
|
||||
) async {
|
||||
final Map<String, SolanaTransactionInfo> result = {};
|
||||
|
||||
for (var transactionModel in splTokenTransactions) {
|
||||
for (var transactionModel in transactions) {
|
||||
result[transactionModel.id] = SolanaTransactionInfo(
|
||||
id: transactionModel.id,
|
||||
to: transactionModel.to,
|
||||
|
@ -373,7 +365,7 @@ abstract class SolanaWalletBase
|
|||
|
||||
String toJSON() => json.encode({
|
||||
'mnemonic': _mnemonic,
|
||||
'private_key': privateKey,
|
||||
'private_key': _hexPrivateKey,
|
||||
'balance': balance[currency]!.toJSON(),
|
||||
});
|
||||
|
||||
|
@ -460,12 +452,23 @@ abstract class SolanaWalletBase
|
|||
await token.delete();
|
||||
|
||||
balance.remove(token);
|
||||
await _removeTokenTransactionsInHistory(token);
|
||||
_updateBalance();
|
||||
}
|
||||
|
||||
Future<void> _removeTokenTransactionsInHistory(SPLToken token) async {
|
||||
transactionHistory.transactions.removeWhere((key, value) => value.tokenSymbol == token.title);
|
||||
await transactionHistory.save();
|
||||
}
|
||||
|
||||
Future<SPLToken?> getSPLToken(String mintAddress) async {
|
||||
// Convert SPL token mint address to public key
|
||||
final mintPublicKey = Ed25519HDPublicKey.fromBase58(mintAddress);
|
||||
final Ed25519HDPublicKey mintPublicKey;
|
||||
try {
|
||||
mintPublicKey = Ed25519HDPublicKey.fromBase58(mintAddress);
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Fetch token's metadata account
|
||||
try {
|
||||
|
@ -480,10 +483,12 @@ abstract class SolanaWalletBase
|
|||
iconPath = await _client.getIconImageFromTokenUri(token.uri);
|
||||
} catch (_) {}
|
||||
|
||||
String filteredTokenSymbol = token.symbol.replaceFirst(RegExp('^\\\$'), '');
|
||||
|
||||
return SPLToken.fromMetadata(
|
||||
name: token.name,
|
||||
mint: token.mint,
|
||||
symbol: token.symbol,
|
||||
symbol: filteredTokenSymbol,
|
||||
mintAddress: mintAddress,
|
||||
iconPath: iconPath,
|
||||
);
|
||||
|
@ -519,7 +524,7 @@ abstract class SolanaWalletBase
|
|||
_transactionsUpdateTimer!.cancel();
|
||||
}
|
||||
|
||||
_transactionsUpdateTimer = Timer.periodic(const Duration(seconds: 20), (_) {
|
||||
_transactionsUpdateTimer = Timer.periodic(const Duration(seconds: 30), (_) {
|
||||
_updateBalance();
|
||||
_updateNativeSOLTransactions();
|
||||
_updateSPLTokenTransactions();
|
||||
|
|
|
@ -2,7 +2,10 @@ import 'dart:io';
|
|||
|
||||
import 'package:bip39/bip39.dart' as bip39;
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:cw_core/balance.dart';
|
||||
import 'package:cw_core/pathForWallet.dart';
|
||||
import 'package:cw_core/transaction_history.dart';
|
||||
import 'package:cw_core/transaction_info.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/wallet_service.dart';
|
||||
|
@ -13,7 +16,7 @@ import 'package:cw_solana/solana_wallet_creation_credentials.dart';
|
|||
import 'package:hive/hive.dart';
|
||||
|
||||
class SolanaWalletService extends WalletService<SolanaNewWalletCredentials,
|
||||
SolanaRestoreWalletFromSeedCredentials, SolanaRestoreWalletFromPrivateKey> {
|
||||
SolanaRestoreWalletFromSeedCredentials, SolanaRestoreWalletFromPrivateKey, SolanaNewWalletCredentials> {
|
||||
SolanaWalletService(this.walletInfoSource);
|
||||
|
||||
final Box<WalletInfo> walletInfoSource;
|
||||
|
@ -134,4 +137,10 @@ class SolanaWalletService extends WalletService<SolanaNewWalletCredentials,
|
|||
|
||||
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo>> restoreFromHardwareWallet(SolanaNewWalletCredentials credentials) {
|
||||
// TODO: implement restoreFromHardwareWallet
|
||||
throw UnimplementedError();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ class SPLToken extends CryptoCurrency with HiveObjectMixin {
|
|||
@HiveField(3)
|
||||
final int decimal;
|
||||
|
||||
@HiveField(4, defaultValue: false)
|
||||
@HiveField(4, defaultValue: true)
|
||||
bool _enabled;
|
||||
|
||||
@HiveField(5)
|
||||
|
@ -39,7 +39,7 @@ class SPLToken extends CryptoCurrency with HiveObjectMixin {
|
|||
required this.mint,
|
||||
this.iconPath,
|
||||
this.tag = 'SOL',
|
||||
bool enabled = false,
|
||||
bool enabled = true,
|
||||
}) : _enabled = enabled,
|
||||
super(
|
||||
name: mint.toLowerCase(),
|
||||
|
|
|
@ -26,10 +26,13 @@ dev_dependencies:
|
|||
flutter_test:
|
||||
sdk: flutter
|
||||
flutter_lints: ^2.0.0
|
||||
build_runner: ^2.1.11
|
||||
build_runner: ^2.4.7
|
||||
mobx_codegen: ^2.0.7
|
||||
hive_generator: ^1.1.3
|
||||
|
||||
dependency_overrides:
|
||||
watcher: ^1.1.0
|
||||
|
||||
flutter:
|
||||
# assets:
|
||||
# - images/a_dot_burr.jpeg
|
||||
|
|
30
cw_tron/.gitignore
vendored
Normal file
30
cw_tron/.gitignore
vendored
Normal 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_tron/.metadata
Normal file
10
cw_tron/.metadata
Normal 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_tron/CHANGELOG.md
Normal file
3
cw_tron/CHANGELOG.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
## 0.0.1
|
||||
|
||||
* TODO: Describe initial release.
|
1
cw_tron/LICENSE
Normal file
1
cw_tron/LICENSE
Normal file
|
@ -0,0 +1 @@
|
|||
TODO: Add your license here.
|
39
cw_tron/README.md
Normal file
39
cw_tron/README.md
Normal 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.
|
4
cw_tron/analysis_options.yaml
Normal file
4
cw_tron/analysis_options.yaml
Normal 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
|
7
cw_tron/lib/cw_tron.dart
Normal file
7
cw_tron/lib/cw_tron.dart
Normal file
|
@ -0,0 +1,7 @@
|
|||
library cw_tron;
|
||||
|
||||
/// A Calculator.
|
||||
class Calculator {
|
||||
/// Returns [value] plus 1.
|
||||
int addOne(int value) => value + 1;
|
||||
}
|
103
cw_tron/lib/default_tron_tokens.dart
Normal file
103
cw_tron/lib/default_tron_tokens.dart
Normal file
|
@ -0,0 +1,103 @@
|
|||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_tron/tron_token.dart';
|
||||
|
||||
class DefaultTronTokens {
|
||||
final List<TronToken> _defaultTokens = [
|
||||
TronToken(
|
||||
name: "Tether USD",
|
||||
symbol: "USDT",
|
||||
contractAddress: "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
|
||||
decimal: 6,
|
||||
enabled: true,
|
||||
),
|
||||
TronToken(
|
||||
name: "USD Coin",
|
||||
symbol: "USDC",
|
||||
contractAddress: "TEkxiTehnzSmSe2XqrBj4w32RUN966rdz8",
|
||||
decimal: 6,
|
||||
enabled: true,
|
||||
),
|
||||
TronToken(
|
||||
name: "Bitcoin",
|
||||
symbol: "BTC",
|
||||
contractAddress: "TN3W4H6rK2ce4vX9YnFQHwKENnHjoxb3m9",
|
||||
decimal: 8,
|
||||
enabled: false,
|
||||
),
|
||||
TronToken(
|
||||
name: "Ethereum",
|
||||
symbol: "ETH",
|
||||
contractAddress: "TRFe3hT5oYhjSZ6f3ji5FJ7YCfrkWnHRvh",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
),
|
||||
TronToken(
|
||||
name: "Wrapped BTC",
|
||||
symbol: "WBTC",
|
||||
contractAddress: "TXpw8XeWYeTUd4quDskoUqeQPowRh4jY65",
|
||||
decimal: 8,
|
||||
enabled: true,
|
||||
),
|
||||
TronToken(
|
||||
name: "Dogecoin",
|
||||
symbol: "DOGE",
|
||||
contractAddress: "THbVQp8kMjStKNnf2iCY6NEzThKMK5aBHg",
|
||||
decimal: 8,
|
||||
enabled: true,
|
||||
),
|
||||
TronToken(
|
||||
name: "JUST Stablecoin",
|
||||
symbol: "USDJ",
|
||||
contractAddress: "TMwFHYXLJaRUPeW6421aqXL4ZEzPRFGkGT",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
),
|
||||
TronToken(
|
||||
name: "SUN",
|
||||
symbol: "SUN",
|
||||
contractAddress: "TSSMHYeV2uE9qYH95DqyoCuNCzEL1NvU3S",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
),
|
||||
TronToken(
|
||||
name: "Wrapped TRX",
|
||||
symbol: "WTRX",
|
||||
contractAddress: "TNUC9Qb1rRpS5CbWLmNMxXBjyFoydXjWFR",
|
||||
decimal: 6,
|
||||
enabled: false,
|
||||
),
|
||||
TronToken(
|
||||
name: "BitTorent",
|
||||
symbol: "BTT",
|
||||
contractAddress: "TAFjULxiVgT4qWk6UZwjqwZXTSaGaqnVp4",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
),
|
||||
TronToken(
|
||||
name: "BUSD Token",
|
||||
symbol: "BUSD",
|
||||
contractAddress: "TMz2SWatiAtZVVcH2ebpsbVtYwUPT9EdjH",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
),
|
||||
TronToken(
|
||||
name: "HTX",
|
||||
symbol: "HTX",
|
||||
contractAddress: "TUPM7K8REVzD2UdV4R5fe5M8XbnR2DdoJ6",
|
||||
decimal: 18,
|
||||
enabled: false,
|
||||
),
|
||||
];
|
||||
|
||||
List<TronToken> get initialTronTokens => _defaultTokens.map((token) {
|
||||
String? iconPath;
|
||||
try {
|
||||
iconPath = CryptoCurrency.all
|
||||
.firstWhere((element) =>
|
||||
element.title.toUpperCase() == token.symbol.split(".").first.toUpperCase())
|
||||
.iconPath;
|
||||
} catch (_) {}
|
||||
|
||||
return TronToken.copyWith(token, iconPath, 'TRX');
|
||||
}).toList();
|
||||
}
|
39
cw_tron/lib/file.dart
Normal file
39
cw_tron/lib/file.dart
Normal file
|
@ -0,0 +1,39 @@
|
|||
import 'dart:io';
|
||||
import 'package:cw_core/key.dart';
|
||||
import 'package:encrypt/encrypt.dart' as encrypt;
|
||||
|
||||
Future<void> 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<void> writeData(
|
||||
{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<String> read({required String path, required String password}) async {
|
||||
final file = File(path);
|
||||
|
||||
if (!file.existsSync()) {
|
||||
file.createSync();
|
||||
}
|
||||
|
||||
final encrypted = file.readAsStringSync();
|
||||
|
||||
return decode(password: password, data: encrypted);
|
||||
}
|
33
cw_tron/lib/pending_tron_transaction.dart
Normal file
33
cw_tron/lib/pending_tron_transaction.dart
Normal file
|
@ -0,0 +1,33 @@
|
|||
|
||||
|
||||
import 'package:cw_core/pending_transaction.dart';
|
||||
import 'package:web3dart/crypto.dart';
|
||||
|
||||
class PendingTronTransaction with PendingTransaction {
|
||||
final Function sendTransaction;
|
||||
final List<int> signedTransaction;
|
||||
final String fee;
|
||||
final String amount;
|
||||
|
||||
PendingTronTransaction({
|
||||
required this.sendTransaction,
|
||||
required this.signedTransaction,
|
||||
required this.fee,
|
||||
required this.amount,
|
||||
});
|
||||
|
||||
@override
|
||||
String get amountFormatted => amount;
|
||||
|
||||
@override
|
||||
Future<void> commit() async => await sendTransaction();
|
||||
|
||||
@override
|
||||
String get feeFormatted => fee;
|
||||
|
||||
@override
|
||||
String get hex => bytesToHex(signedTransaction);
|
||||
|
||||
@override
|
||||
String get id => '';
|
||||
}
|
436
cw_tron/lib/tron_abi.dart
Normal file
436
cw_tron/lib/tron_abi.dart
Normal file
|
@ -0,0 +1,436 @@
|
|||
final trc20Abi = [
|
||||
{"inputs": [], "stateMutability": "nonpayable", "type": "constructor"},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{"indexed": true, "internalType": "address", "name": "owner", "type": "address"},
|
||||
{"indexed": true, "internalType": "address", "name": "spender", "type": "address"},
|
||||
{"indexed": false, "internalType": "uint256", "name": "value", "type": "uint256"}
|
||||
],
|
||||
"name": "Approval",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{"indexed": false, "internalType": "uint256", "name": "total", "type": "uint256"},
|
||||
{"indexed": true, "internalType": "uint16", "name": "order_id", "type": "uint16"},
|
||||
{"indexed": true, "internalType": "address", "name": "buyer", "type": "address"},
|
||||
{"indexed": true, "internalType": "address", "name": "seller", "type": "address"},
|
||||
{"indexed": false, "internalType": "address", "name": "contract_address", "type": "address"}
|
||||
],
|
||||
"name": "OrderPaid",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{"indexed": true, "internalType": "address", "name": "previousOwner", "type": "address"},
|
||||
{"indexed": true, "internalType": "address", "name": "newOwner", "type": "address"}
|
||||
],
|
||||
"name": "OwnershipTransferred",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{"indexed": false, "internalType": "address", "name": "token", "type": "address"},
|
||||
{"indexed": false, "internalType": "bool", "name": "active", "type": "bool"}
|
||||
],
|
||||
"name": "TokenUpdate",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{"indexed": true, "internalType": "address", "name": "from", "type": "address"},
|
||||
{"indexed": true, "internalType": "address", "name": "to", "type": "address"},
|
||||
{"indexed": false, "internalType": "uint256", "name": "value", "type": "uint256"}
|
||||
],
|
||||
"name": "Transfer",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{"indexed": false, "internalType": "string", "name": "username", "type": "string"},
|
||||
{"indexed": true, "internalType": "address", "name": "seller", "type": "address"}
|
||||
],
|
||||
"name": "UserRegistred",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{"indexed": true, "internalType": "uint16", "name": "order_id", "type": "uint16"},
|
||||
{"indexed": true, "internalType": "address", "name": "buyer", "type": "address"},
|
||||
{"indexed": false, "internalType": "address", "name": "seller", "type": "address"}
|
||||
],
|
||||
"name": "WBuyer",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{"indexed": true, "internalType": "uint16", "name": "order_id", "type": "uint16"},
|
||||
{"indexed": true, "internalType": "address", "name": "seller", "type": "address"},
|
||||
{"indexed": false, "internalType": "address", "name": "buyer", "type": "address"}
|
||||
],
|
||||
"name": "WSeller",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "CONTRACTPERCENTAGE",
|
||||
"outputs": [
|
||||
{"internalType": "uint8", "name": "", "type": "uint8"}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{"internalType": "uint16", "name": "order_id", "type": "uint16"},
|
||||
{"internalType": "uint256", "name": "order_total", "type": "uint256"},
|
||||
{"internalType": "address", "name": "contractAddress", "type": "address"},
|
||||
{"internalType": "address", "name": "seller", "type": "address"}
|
||||
],
|
||||
"name": "PayWithTokens",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "TOKENINCREAMENT",
|
||||
"outputs": [
|
||||
{"internalType": "uint16", "name": "", "type": "uint16"}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{"internalType": "address", "name": "", "type": "address"}
|
||||
],
|
||||
"name": "_signer",
|
||||
"outputs": [
|
||||
{"internalType": "bool", "name": "", "type": "bool"}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{"internalType": "address", "name": "", "type": "address"}
|
||||
],
|
||||
"name": "_tokens",
|
||||
"outputs": [
|
||||
{"internalType": "bool", "name": "active", "type": "bool"},
|
||||
{"internalType": "uint16", "name": "token", "type": "uint16"}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{"internalType": "address", "name": "", "type": "address"}
|
||||
],
|
||||
"name": "_users",
|
||||
"outputs": [
|
||||
{"internalType": "bool", "name": "active", "type": "bool"}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{"internalType": "address", "name": "owner", "type": "address"},
|
||||
{"internalType": "address", "name": "spender", "type": "address"}
|
||||
],
|
||||
"name": "allowance",
|
||||
"outputs": [
|
||||
{"internalType": "uint256", "name": "", "type": "uint256"}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{"internalType": "address", "name": "spender", "type": "address"},
|
||||
{"internalType": "uint256", "name": "amount", "type": "uint256"}
|
||||
],
|
||||
"name": "approve",
|
||||
"outputs": [
|
||||
{"internalType": "bool", "name": "", "type": "bool"}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{"internalType": "address", "name": "account", "type": "address"}
|
||||
],
|
||||
"name": "balanceOf",
|
||||
"outputs": [
|
||||
{"internalType": "uint256", "name": "", "type": "uint256"}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{"internalType": "address", "name": "token", "type": "address"}
|
||||
],
|
||||
"name": "balanceOfContract",
|
||||
"outputs": [
|
||||
{"internalType": "uint256", "name": "", "type": "uint256"}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{"internalType": "uint256", "name": "amount", "type": "uint256"}
|
||||
],
|
||||
"name": "burn",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{"internalType": "address", "name": "account", "type": "address"},
|
||||
{"internalType": "uint256", "name": "amount", "type": "uint256"}
|
||||
],
|
||||
"name": "burnFrom",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{"internalType": "uint256", "name": "value", "type": "uint256"},
|
||||
{"internalType": "address", "name": "_contractAddress", "type": "address"}
|
||||
],
|
||||
"name": "contractWithdraw",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "decimals",
|
||||
"outputs": [
|
||||
{"internalType": "uint8", "name": "", "type": "uint8"}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{"internalType": "address", "name": "spender", "type": "address"},
|
||||
{"internalType": "uint256", "name": "subtractedValue", "type": "uint256"}
|
||||
],
|
||||
"name": "decreaseAllowance",
|
||||
"outputs": [
|
||||
{"internalType": "bool", "name": "", "type": "bool"}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{"internalType": "address", "name": "spender", "type": "address"},
|
||||
{"internalType": "uint256", "name": "addedValue", "type": "uint256"}
|
||||
],
|
||||
"name": "increaseAllowance",
|
||||
"outputs": [
|
||||
{"internalType": "bool", "name": "", "type": "bool"}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{"internalType": "address", "name": "to", "type": "address"},
|
||||
{"internalType": "uint256", "name": "amount", "type": "uint256"}
|
||||
],
|
||||
"name": "mint",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "name",
|
||||
"outputs": [
|
||||
{"internalType": "string", "name": "", "type": "string"}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "owner",
|
||||
"outputs": [
|
||||
{"internalType": "address", "name": "", "type": "address"}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{"internalType": "address", "name": "token", "type": "address"},
|
||||
{"internalType": "uint256", "name": "value", "type": "uint256"}
|
||||
],
|
||||
"name": "payToContract",
|
||||
"outputs": [],
|
||||
"stateMutability": "payable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{"internalType": "uint16", "name": "order_id", "type": "uint16"},
|
||||
{"internalType": "address", "name": "seller", "type": "address"}
|
||||
],
|
||||
"name": "payWithNativeToken",
|
||||
"outputs": [],
|
||||
"stateMutability": "payable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{"internalType": "string", "name": "username", "type": "string"}
|
||||
],
|
||||
"name": "regiserUser",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "renounceOwnership",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{"internalType": "uint16", "name": "id", "type": "uint16"},
|
||||
{"internalType": "address", "name": "buyer", "type": "address"},
|
||||
{"internalType": "address", "name": "seller", "type": "address"}
|
||||
],
|
||||
"name": "selectOrder",
|
||||
"outputs": [
|
||||
{"internalType": "uint232", "name": "", "type": "uint232"},
|
||||
{"internalType": "uint16", "name": "", "type": "uint16"},
|
||||
{"internalType": "uint8", "name": "", "type": "uint8"}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "symbol",
|
||||
"outputs": [
|
||||
{"internalType": "string", "name": "", "type": "string"}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{"internalType": "address", "name": "signer", "type": "address"}
|
||||
],
|
||||
"name": "toggleSigner",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{"internalType": "address", "name": "tokenAddress", "type": "address"}
|
||||
],
|
||||
"name": "toggleToken",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "totalSupply",
|
||||
"outputs": [
|
||||
{"internalType": "uint256", "name": "", "type": "uint256"}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{"internalType": "address", "name": "to", "type": "address"},
|
||||
{"internalType": "uint256", "name": "amount", "type": "uint256"}
|
||||
],
|
||||
"name": "transfer",
|
||||
"outputs": [
|
||||
{"internalType": "bool", "name": "", "type": "bool"}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{"internalType": "address", "name": "from", "type": "address"},
|
||||
{"internalType": "address", "name": "to", "type": "address"},
|
||||
{"internalType": "uint256", "name": "amount", "type": "uint256"}
|
||||
],
|
||||
"name": "transferFrom",
|
||||
"outputs": [
|
||||
{"internalType": "bool", "name": "", "type": "bool"}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{"internalType": "address", "name": "newOwner", "type": "address"}
|
||||
],
|
||||
"name": "transferOwnership",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{"internalType": "uint8", "name": "newPercentage", "type": "uint8"}
|
||||
],
|
||||
"name": "updateContractPercentage",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{"internalType": "address[]", "name": "buyer", "type": "address[]"},
|
||||
{"internalType": "bytes[]", "name": "signature", "type": "bytes[]"},
|
||||
{"internalType": "uint16[]", "name": "order_id", "type": "uint16[]"},
|
||||
{"internalType": "address", "name": "contractAddress", "type": "address"}
|
||||
],
|
||||
"name": "widthrawForSellers",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{"internalType": "address", "name": "seller", "type": "address"},
|
||||
{"internalType": "bytes", "name": "signature", "type": "bytes"},
|
||||
{"internalType": "uint16", "name": "order_id", "type": "uint16"},
|
||||
{"internalType": "address", "name": "contractAddress", "type": "address"}
|
||||
],
|
||||
"name": "widthrowForBuyers",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
}
|
||||
];
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue