mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2025-01-23 19:16:09 +00:00
Add send ERC-20 tokens initial flow
This commit is contained in:
parent
f08ab62359
commit
94216e6987
11 changed files with 515 additions and 421 deletions
|
@ -67,6 +67,7 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
|
||||||
CryptoCurrency.uni,
|
CryptoCurrency.uni,
|
||||||
CryptoCurrency.stx,
|
CryptoCurrency.stx,
|
||||||
CryptoCurrency.btcln,
|
CryptoCurrency.btcln,
|
||||||
|
CryptoCurrency.shib,
|
||||||
];
|
];
|
||||||
|
|
||||||
static const havenCurrencies = [
|
static const havenCurrencies = [
|
||||||
|
@ -152,7 +153,7 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
|
||||||
static const uni = CryptoCurrency(title: 'UNI', tag: 'ETH', fullName: 'Uniswap', raw: 60, name: 'uni', iconPath: 'assets/images/uni_icon.png');
|
static const uni = CryptoCurrency(title: 'UNI', tag: 'ETH', fullName: 'Uniswap', raw: 60, name: 'uni', iconPath: 'assets/images/uni_icon.png');
|
||||||
static const stx = CryptoCurrency(title: 'STX', fullName: 'Stacks', raw: 61, name: 'stx', iconPath: 'assets/images/stx_icon.png');
|
static const stx = CryptoCurrency(title: 'STX', fullName: 'Stacks', raw: 61, name: 'stx', iconPath: 'assets/images/stx_icon.png');
|
||||||
static const btcln = CryptoCurrency(title: 'BTC', tag: 'LN', fullName: 'Bitcoin Lightning Network', raw: 62, name: 'btcln', iconPath: 'assets/images/btc.png');
|
static const btcln = CryptoCurrency(title: 'BTC', tag: 'LN', fullName: 'Bitcoin Lightning Network', raw: 62, name: 'btcln', iconPath: 'assets/images/btc.png');
|
||||||
static const shib = CryptoCurrency(title: 'SHIB', tag: 'ETH', fullName: 'SHIBA INU', raw: 63, name: 'shib', iconPath: 'assets/images/shib.png'); // TODO: add image
|
static const shib = CryptoCurrency(title: 'SHIB', tag: 'ETH', fullName: 'SHIBA INU', raw: 63, name: 'shib', iconPath: 'assets/images/shib_icon.png');
|
||||||
|
|
||||||
|
|
||||||
static final Map<int, CryptoCurrency> _rawCurrencyMap =
|
static final Map<int, CryptoCurrency> _rawCurrencyMap =
|
||||||
|
|
|
@ -14,6 +14,8 @@ class EthereumClient {
|
||||||
CryptoCurrency.shib: "0x95aD61b0a150d79219dCF64E1E6Cc01f0B64C4cE",
|
CryptoCurrency.shib: "0x95aD61b0a150d79219dCF64E1E6Cc01f0B64C4cE",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Map<CryptoCurrency, String> get erc20Currencies => _erc20Currencies;
|
||||||
|
|
||||||
Web3Client? _client;
|
Web3Client? _client;
|
||||||
|
|
||||||
bool connect(Node node) {
|
bool connect(Node node) {
|
||||||
|
@ -47,13 +49,14 @@ class EthereumClient {
|
||||||
return result.map((e) => e.toInt()).toList();
|
return result.map((e) => e.toInt()).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<PendingEthereumTransaction> signTransaction(
|
Future<PendingEthereumTransaction> signTransaction({
|
||||||
EthPrivateKey privateKey,
|
required EthPrivateKey privateKey,
|
||||||
String toAddress,
|
required String toAddress,
|
||||||
String amount,
|
required String amount,
|
||||||
int gas,
|
required int gas,
|
||||||
EthereumTransactionPriority priority,
|
required EthereumTransactionPriority priority,
|
||||||
) async {
|
required CryptoCurrency currency,
|
||||||
|
}) async {
|
||||||
final estimatedGas = await _client!.estimateGas(
|
final estimatedGas = await _client!.estimateGas(
|
||||||
maxPriorityFeePerGas: EtherAmount.fromUnitAndValue(EtherUnit.gwei, priority.tip),
|
maxPriorityFeePerGas: EtherAmount.fromUnitAndValue(EtherUnit.gwei, priority.tip),
|
||||||
maxFeePerGas: EtherAmount.fromUnitAndValue(EtherUnit.gwei, 100),
|
maxFeePerGas: EtherAmount.fromUnitAndValue(EtherUnit.gwei, 100),
|
||||||
|
@ -64,13 +67,36 @@ class EthereumClient {
|
||||||
|
|
||||||
final price = await _client!.getGasPrice();
|
final price = await _client!.getGasPrice();
|
||||||
|
|
||||||
final transaction = Transaction(
|
final Transaction transaction;
|
||||||
from: privateKey.address,
|
|
||||||
to: EthereumAddress.fromHex(toAddress),
|
if (erc20Currencies.containsKey(currency)) {
|
||||||
maxGas: gas,
|
final String abi = await rootBundle.loadString("assets/abi_json/erc20_abi.json");
|
||||||
gasPrice: price,
|
final contractAbi = ContractAbi.fromJson(abi, "ERC20");
|
||||||
value: EtherAmount.inWei(BigInt.parse(amount)),
|
|
||||||
);
|
final contract = DeployedContract(
|
||||||
|
contractAbi,
|
||||||
|
EthereumAddress.fromHex(_erc20Currencies[currency]!),
|
||||||
|
);
|
||||||
|
|
||||||
|
final transferFunction = contract.function('transfer');
|
||||||
|
transaction = Transaction.callContract(
|
||||||
|
contract: contract,
|
||||||
|
function: transferFunction,
|
||||||
|
parameters: [EthereumAddress.fromHex(toAddress), BigInt.parse(amount)],
|
||||||
|
from: privateKey.address,
|
||||||
|
maxGas: gas,
|
||||||
|
gasPrice: price,
|
||||||
|
value: EtherAmount.inWei(BigInt.parse(amount)),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
transaction = Transaction(
|
||||||
|
from: privateKey.address,
|
||||||
|
to: EthereumAddress.fromHex(toAddress),
|
||||||
|
maxGas: gas,
|
||||||
|
gasPrice: price,
|
||||||
|
value: EtherAmount.inWei(BigInt.parse(amount)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
final signedTransaction = await _client!.signTransaction(privateKey, transaction);
|
final signedTransaction = await _client!.signTransaction(privateKey, transaction);
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,17 @@
|
||||||
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
import 'package:cw_core/output_info.dart';
|
import 'package:cw_core/output_info.dart';
|
||||||
import 'package:cw_ethereum/ethereum_transaction_priority.dart';
|
import 'package:cw_ethereum/ethereum_transaction_priority.dart';
|
||||||
|
|
||||||
class EthereumTransactionCredentials {
|
class EthereumTransactionCredentials {
|
||||||
EthereumTransactionCredentials(this.outputs, {required this.priority, this.feeRate});
|
EthereumTransactionCredentials(
|
||||||
|
this.outputs, {
|
||||||
|
required this.priority,
|
||||||
|
required this.currency,
|
||||||
|
this.feeRate,
|
||||||
|
});
|
||||||
|
|
||||||
final List<OutputInfo> outputs;
|
final List<OutputInfo> outputs;
|
||||||
final EthereumTransactionPriority? priority;
|
final EthereumTransactionPriority? priority;
|
||||||
final int? feeRate;
|
final int? feeRate;
|
||||||
|
final CryptoCurrency currency;
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,7 +123,7 @@ abstract class EthereumWalletBase
|
||||||
final _credentials = credentials as EthereumTransactionCredentials;
|
final _credentials = credentials as EthereumTransactionCredentials;
|
||||||
final outputs = _credentials.outputs;
|
final outputs = _credentials.outputs;
|
||||||
final hasMultiDestination = outputs.length > 1;
|
final hasMultiDestination = outputs.length > 1;
|
||||||
final balance = await _client.getBalance(_privateKey.address);
|
final _erc20Balance = balance[_credentials.currency]!;
|
||||||
int totalAmount = 0;
|
int totalAmount = 0;
|
||||||
|
|
||||||
if (hasMultiDestination) {
|
if (hasMultiDestination) {
|
||||||
|
@ -133,27 +133,28 @@ abstract class EthereumWalletBase
|
||||||
|
|
||||||
totalAmount = outputs.fold(0, (acc, value) => acc + (value.formattedCryptoAmount ?? 0));
|
totalAmount = outputs.fold(0, (acc, value) => acc + (value.formattedCryptoAmount ?? 0));
|
||||||
|
|
||||||
if (balance.getInWei < EtherAmount.inWei(totalAmount as BigInt).getInWei) {
|
if (_erc20Balance.balance < EtherAmount.inWei(totalAmount as BigInt).getInWei) {
|
||||||
throw EthereumTransactionCreationException();
|
throw EthereumTransactionCreationException();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
final output = outputs.first;
|
final output = outputs.first;
|
||||||
final int allAmount = balance.getInWei.toInt() - feeRate(_credentials.priority!);
|
final int allAmount = _erc20Balance.balance.toInt() - feeRate(_credentials.priority!);
|
||||||
totalAmount = output.sendAll ? allAmount : output.formattedCryptoAmount ?? 0;
|
totalAmount = output.sendAll ? allAmount : output.formattedCryptoAmount ?? 0;
|
||||||
|
|
||||||
if ((output.sendAll &&
|
if ((output.sendAll &&
|
||||||
balance.getInWei < EtherAmount.inWei(totalAmount as BigInt).getInWei) ||
|
_erc20Balance.balance < EtherAmount.inWei(totalAmount as BigInt).getInWei) ||
|
||||||
(!output.sendAll && balance.getInWei.toInt() <= 0)) {
|
(!output.sendAll && _erc20Balance.balance.toInt() <= 0)) {
|
||||||
throw EthereumTransactionCreationException();
|
throw EthereumTransactionCreationException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final pendingEthereumTransaction = await _client.signTransaction(
|
final pendingEthereumTransaction = await _client.signTransaction(
|
||||||
_privateKey,
|
privateKey: _privateKey,
|
||||||
_credentials.outputs.first.address,
|
toAddress: _credentials.outputs.first.address,
|
||||||
totalAmount.toString(),
|
amount: totalAmount.toString(),
|
||||||
_priorityFees[_credentials.priority!.raw],
|
gas: _priorityFees[_credentials.priority!.raw],
|
||||||
_credentials.priority!,
|
priority: _credentials.priority!,
|
||||||
|
currency: _credentials.currency,
|
||||||
);
|
);
|
||||||
|
|
||||||
return pendingEthereumTransaction;
|
return pendingEthereumTransaction;
|
||||||
|
@ -233,8 +234,7 @@ abstract class EthereumWalletBase
|
||||||
final jsonSource = await read(path: path, password: password);
|
final jsonSource = await read(path: path, password: password);
|
||||||
final data = json.decode(jsonSource) as Map;
|
final data = json.decode(jsonSource) as Map;
|
||||||
final mnemonic = data['mnemonic'] as String;
|
final mnemonic = data['mnemonic'] as String;
|
||||||
final balance =
|
final balance = ERC20Balance.fromJSON(data['balance'] as String) ?? ERC20Balance(BigInt.zero);
|
||||||
ERC20Balance.fromJSON(data['balance'] as String) ?? ERC20Balance(BigInt.zero);
|
|
||||||
|
|
||||||
return EthereumWallet(
|
return EthereumWallet(
|
||||||
walletInfo: walletInfo,
|
walletInfo: walletInfo,
|
||||||
|
@ -268,4 +268,6 @@ abstract class EthereumWalletBase
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void>? updateBalance() => null;
|
Future<void>? updateBalance() => null;
|
||||||
|
|
||||||
|
List<CryptoCurrency> get erc20Currencies => _client.erc20Currencies.keys.toList();
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,7 @@ class AddressValidator extends TextValidator {
|
||||||
case CryptoCurrency.oxt:
|
case CryptoCurrency.oxt:
|
||||||
case CryptoCurrency.paxg:
|
case CryptoCurrency.paxg:
|
||||||
case CryptoCurrency.uni:
|
case CryptoCurrency.uni:
|
||||||
|
case CryptoCurrency.shib:
|
||||||
return '0x[0-9a-zA-Z]';
|
return '0x[0-9a-zA-Z]';
|
||||||
case CryptoCurrency.xrp:
|
case CryptoCurrency.xrp:
|
||||||
return '^[0-9a-zA-Z]{34}\$|^X[0-9a-zA-Z]{46}\$';
|
return '^[0-9a-zA-Z]{34}\$|^X[0-9a-zA-Z]{46}\$';
|
||||||
|
@ -118,6 +119,7 @@ class AddressValidator extends TextValidator {
|
||||||
case CryptoCurrency.eos:
|
case CryptoCurrency.eos:
|
||||||
return [42];
|
return [42];
|
||||||
case CryptoCurrency.eth:
|
case CryptoCurrency.eth:
|
||||||
|
case CryptoCurrency.shib:
|
||||||
return [42];
|
return [42];
|
||||||
case CryptoCurrency.ltc:
|
case CryptoCurrency.ltc:
|
||||||
return [34, 43, 63];
|
return [34, 43, 63];
|
||||||
|
|
|
@ -41,8 +41,12 @@ class CWEthereum extends Ethereum {
|
||||||
return ethereumWallet.feeRate(priority);
|
return ethereumWallet.feeRate(priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
Object createEthereumTransactionCredentials(List<Output> outputs,
|
Object createEthereumTransactionCredentials(
|
||||||
{required TransactionPriority priority, int? feeRate}) =>
|
List<Output> outputs, {
|
||||||
|
required TransactionPriority priority,
|
||||||
|
required CryptoCurrency currency,
|
||||||
|
int? feeRate,
|
||||||
|
}) =>
|
||||||
EthereumTransactionCredentials(
|
EthereumTransactionCredentials(
|
||||||
outputs
|
outputs
|
||||||
.map((out) => OutputInfo(
|
.map((out) => OutputInfo(
|
||||||
|
@ -56,17 +60,29 @@ class CWEthereum extends Ethereum {
|
||||||
formattedCryptoAmount: out.formattedCryptoAmount))
|
formattedCryptoAmount: out.formattedCryptoAmount))
|
||||||
.toList(),
|
.toList(),
|
||||||
priority: priority as EthereumTransactionPriority,
|
priority: priority as EthereumTransactionPriority,
|
||||||
|
currency: currency,
|
||||||
feeRate: feeRate,
|
feeRate: feeRate,
|
||||||
);
|
);
|
||||||
|
|
||||||
Object createEthereumTransactionCredentialsRaw(List<OutputInfo> outputs,
|
Object createEthereumTransactionCredentialsRaw(
|
||||||
{TransactionPriority? priority, required int feeRate}) =>
|
List<OutputInfo> outputs, {
|
||||||
|
TransactionPriority? priority,
|
||||||
|
required CryptoCurrency currency,
|
||||||
|
required int feeRate,
|
||||||
|
}) =>
|
||||||
EthereumTransactionCredentials(
|
EthereumTransactionCredentials(
|
||||||
outputs,
|
outputs,
|
||||||
priority: priority as EthereumTransactionPriority,
|
priority: priority as EthereumTransactionPriority?,
|
||||||
|
currency: currency,
|
||||||
feeRate: feeRate,
|
feeRate: feeRate,
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int formatterEthereumParseAmount(String amount) => EthereumFormatter.parseEthereumAmount(amount);
|
int formatterEthereumParseAmount(String amount) => EthereumFormatter.parseEthereumAmount(amount);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<CryptoCurrency> getERC20Currencies(Object wallet) {
|
||||||
|
final ethereumWallet = wallet as EthereumWallet;
|
||||||
|
return ethereumWallet.erc20Currencies;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ import 'dart:async';
|
||||||
import 'package:cake_wallet/core/fiat_conversion_service.dart';
|
import 'package:cake_wallet/core/fiat_conversion_service.dart';
|
||||||
import 'package:cake_wallet/entities/fiat_api_mode.dart';
|
import 'package:cake_wallet/entities/fiat_api_mode.dart';
|
||||||
import 'package:cake_wallet/entities/update_haven_rate.dart';
|
import 'package:cake_wallet/entities/update_haven_rate.dart';
|
||||||
|
import 'package:cake_wallet/ethereum/ethereum.dart';
|
||||||
import 'package:cake_wallet/store/app_store.dart';
|
import 'package:cake_wallet/store/app_store.dart';
|
||||||
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
|
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
|
||||||
import 'package:cake_wallet/store/settings_store.dart';
|
import 'package:cake_wallet/store/settings_store.dart';
|
||||||
|
@ -30,6 +31,17 @@ Future<void> startFiatRateUpdate(
|
||||||
fiat: settingsStore.fiatCurrency,
|
fiat: settingsStore.fiatCurrency,
|
||||||
torOnly: settingsStore.fiatApiMode == FiatApiMode.torOnly);
|
torOnly: settingsStore.fiatApiMode == FiatApiMode.torOnly);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (appStore.wallet!.type == WalletType.ethereum) {
|
||||||
|
final currencies = ethereum!.getERC20Currencies(appStore.wallet!);
|
||||||
|
|
||||||
|
for (final currency in currencies) {
|
||||||
|
fiatConversionStore.prices[currency] = await FiatConversionService.fetchPrice(
|
||||||
|
crypto: currency,
|
||||||
|
fiat: settingsStore.fiatCurrency,
|
||||||
|
torOnly: settingsStore.fiatApiMode == FiatApiMode.torOnly);
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print(e);
|
print(e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -511,17 +511,16 @@ class ExchangeCardState extends State<ExchangeCard> {
|
||||||
|
|
||||||
void _presentPicker(BuildContext context) {
|
void _presentPicker(BuildContext context) {
|
||||||
showPopUp<void>(
|
showPopUp<void>(
|
||||||
builder: (_) => CurrencyPicker(
|
context: context,
|
||||||
selectedAtIndex: widget.currencies.indexOf(_selectedCurrency),
|
builder: (_) => CurrencyPicker(
|
||||||
items: widget.currencies,
|
selectedAtIndex: widget.currencies.indexOf(_selectedCurrency),
|
||||||
hintText: S.of(context).search_currency,
|
items: widget.currencies,
|
||||||
isMoneroWallet: _isMoneroWallet,
|
hintText: S.of(context).search_currency,
|
||||||
isConvertFrom: widget.hasRefundAddress,
|
isMoneroWallet: _isMoneroWallet,
|
||||||
onItemSelected: (Currency item) =>
|
isConvertFrom: widget.hasRefundAddress,
|
||||||
widget.onCurrencySelected != null
|
onItemSelected: (Currency item) => widget.onCurrencySelected(item as CryptoCurrency),
|
||||||
? widget.onCurrencySelected(item as CryptoCurrency)
|
),
|
||||||
: null),
|
);
|
||||||
context: context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showAmountPopup(BuildContext context, PaymentRequest paymentRequest) {
|
void _showAmountPopup(BuildContext context, PaymentRequest paymentRequest) {
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
import 'package:cake_wallet/entities/priority_for_wallet_type.dart';
|
import 'package:cake_wallet/entities/priority_for_wallet_type.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/exchange/widgets/currency_picker.dart';
|
||||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||||
import 'package:cake_wallet/utils/payment_request.dart';
|
import 'package:cake_wallet/utils/payment_request.dart';
|
||||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||||
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
|
import 'package:cw_core/currency.dart';
|
||||||
import 'package:cw_core/transaction_priority.dart';
|
import 'package:cw_core/transaction_priority.dart';
|
||||||
import 'package:cake_wallet/routes.dart';
|
import 'package:cake_wallet/routes.dart';
|
||||||
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
|
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
|
||||||
|
@ -32,18 +35,14 @@ class SendCard extends StatefulWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
SendCardState createState() => SendCardState(
|
SendCardState createState() => SendCardState(
|
||||||
output: output,
|
output: output,
|
||||||
sendViewModel: sendViewModel,
|
sendViewModel: sendViewModel,
|
||||||
initialPaymentRequest: initialPaymentRequest,
|
initialPaymentRequest: initialPaymentRequest,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class SendCardState extends State<SendCard>
|
class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<SendCard> {
|
||||||
with AutomaticKeepAliveClientMixin<SendCard> {
|
SendCardState({required this.output, required this.sendViewModel, this.initialPaymentRequest})
|
||||||
SendCardState({
|
|
||||||
required this.output,
|
|
||||||
required this.sendViewModel,
|
|
||||||
this.initialPaymentRequest})
|
|
||||||
: addressController = TextEditingController(),
|
: addressController = TextEditingController(),
|
||||||
cryptoAmountController = TextEditingController(),
|
cryptoAmountController = TextEditingController(),
|
||||||
fiatAmountController = TextEditingController(),
|
fiatAmountController = TextEditingController(),
|
||||||
|
@ -100,40 +99,39 @@ class SendCardState extends State<SendCard>
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
KeyboardActions(
|
KeyboardActions(
|
||||||
config: KeyboardActionsConfig(
|
config: KeyboardActionsConfig(
|
||||||
keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
|
keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
|
||||||
keyboardBarColor: Theme.of(context)
|
keyboardBarColor: Theme.of(context).accentTextTheme.bodyLarge!.backgroundColor!,
|
||||||
.accentTextTheme!
|
nextFocus: false,
|
||||||
.bodyLarge!
|
actions: [
|
||||||
.backgroundColor!,
|
KeyboardActionsItem(
|
||||||
nextFocus: false,
|
focusNode: cryptoAmountFocus,
|
||||||
actions: [
|
toolbarButtons: [(_) => KeyboardDoneButton()],
|
||||||
KeyboardActionsItem(
|
),
|
||||||
focusNode: cryptoAmountFocus,
|
KeyboardActionsItem(
|
||||||
toolbarButtons: [(_) => KeyboardDoneButton()],
|
focusNode: fiatAmountFocus,
|
||||||
),
|
toolbarButtons: [(_) => KeyboardDoneButton()],
|
||||||
KeyboardActionsItem(
|
)
|
||||||
focusNode: fiatAmountFocus,
|
],
|
||||||
toolbarButtons: [(_) => KeyboardDoneButton()],
|
),
|
||||||
)
|
child: Container(
|
||||||
]),
|
height: 0,
|
||||||
child: Container(
|
color: Colors.transparent,
|
||||||
height: 0,
|
),
|
||||||
color: Colors.transparent,
|
),
|
||||||
)),
|
|
||||||
Container(
|
Container(
|
||||||
decoration: ResponsiveLayoutUtil.instance.isMobile(context) ? BoxDecoration(
|
decoration: ResponsiveLayoutUtil.instance.isMobile(context)
|
||||||
borderRadius: BorderRadius.only(
|
? BoxDecoration(
|
||||||
bottomLeft: Radius.circular(24),
|
borderRadius: BorderRadius.only(
|
||||||
bottomRight: Radius.circular(24)),
|
bottomLeft: Radius.circular(24),
|
||||||
gradient: LinearGradient(colors: [
|
bottomRight: Radius.circular(24),
|
||||||
Theme.of(context).primaryTextTheme!.titleMedium!.color!,
|
),
|
||||||
Theme.of(context)
|
gradient: LinearGradient(colors: [
|
||||||
.primaryTextTheme!
|
Theme.of(context).primaryTextTheme.titleMedium!.color!,
|
||||||
.titleMedium!
|
Theme.of(context).primaryTextTheme.titleMedium!.decorationColor!,
|
||||||
.decorationColor!,
|
], begin: Alignment.topLeft, end: Alignment.bottomRight),
|
||||||
], begin: Alignment.topLeft, end: Alignment.bottomRight),
|
)
|
||||||
) : null,
|
: null,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.fromLTRB(
|
padding: EdgeInsets.fromLTRB(
|
||||||
24,
|
24,
|
||||||
|
@ -142,7 +140,8 @@ class SendCardState extends State<SendCard>
|
||||||
ResponsiveLayoutUtil.instance.isMobile(context) ? 32 : 0,
|
ResponsiveLayoutUtil.instance.isMobile(context) ? 32 : 0,
|
||||||
),
|
),
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
child: Observer(builder: (_) => Column(
|
child: Observer(
|
||||||
|
builder: (_) => Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Observer(builder: (_) {
|
Observer(builder: (_) {
|
||||||
|
@ -164,24 +163,15 @@ class SendCardState extends State<SendCard>
|
||||||
AddressTextFieldOption.qrCode,
|
AddressTextFieldOption.qrCode,
|
||||||
AddressTextFieldOption.addressBook
|
AddressTextFieldOption.addressBook
|
||||||
],
|
],
|
||||||
buttonColor: Theme.of(context)
|
buttonColor: Theme.of(context).primaryTextTheme.headlineMedium!.color!,
|
||||||
.primaryTextTheme!
|
borderColor: Theme.of(context).primaryTextTheme.headlineSmall!.color!,
|
||||||
.headlineMedium!
|
|
||||||
.color!,
|
|
||||||
borderColor: Theme.of(context)
|
|
||||||
.primaryTextTheme!
|
|
||||||
.headlineSmall!
|
|
||||||
.color!,
|
|
||||||
textStyle: TextStyle(
|
textStyle: TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14, fontWeight: FontWeight.w500, color: Colors.white),
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: Colors.white),
|
|
||||||
hintStyle: TextStyle(
|
hintStyle: TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
color: Theme.of(context)
|
color: Theme.of(context)
|
||||||
.primaryTextTheme!
|
.primaryTextTheme.headlineSmall!
|
||||||
.headlineSmall!
|
|
||||||
.decorationColor!),
|
.decorationColor!),
|
||||||
onPushPasteButton: (context) async {
|
onPushPasteButton: (context) async {
|
||||||
output.resetParsedAddress();
|
output.resetParsedAddress();
|
||||||
|
@ -195,181 +185,209 @@ class SendCardState extends State<SendCard>
|
||||||
selectedCurrency: sendViewModel.currency,
|
selectedCurrency: sendViewModel.currency,
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
if (output.isParsedAddress) Padding(
|
if (output.isParsedAddress)
|
||||||
padding: const EdgeInsets.only(top: 20),
|
Padding(
|
||||||
child: BaseTextFormField(
|
padding: const EdgeInsets.only(top: 20),
|
||||||
controller: extractedAddressController,
|
child: BaseTextFormField(
|
||||||
readOnly: true,
|
controller: extractedAddressController,
|
||||||
borderColor: Theme.of(context)
|
readOnly: true,
|
||||||
.primaryTextTheme!
|
borderColor:
|
||||||
.headlineSmall!
|
Theme.of(context).primaryTextTheme.headlineSmall!.color!,
|
||||||
.color!,
|
textStyle: TextStyle(
|
||||||
textStyle: TextStyle(
|
fontSize: 14, fontWeight: FontWeight.w500, color: Colors.white),
|
||||||
fontSize: 14,
|
validator: sendViewModel.addressValidator)),
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: Colors.white),
|
|
||||||
validator: sendViewModel.addressValidator
|
|
||||||
)
|
|
||||||
),
|
|
||||||
Observer(
|
Observer(
|
||||||
builder: (_) => Padding(
|
builder: (_) => Padding(
|
||||||
padding: const EdgeInsets.only(top: 20),
|
padding: const EdgeInsets.only(top: 20),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 8.0),
|
padding: const EdgeInsets.only(bottom: 8.0),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
sendViewModel.hasMultipleTokens
|
||||||
sendViewModel.selectedCryptoCurrency.title,
|
? Container(
|
||||||
style: TextStyle(
|
padding: EdgeInsets.only(right: 8),
|
||||||
fontSize: 16,
|
height: 32,
|
||||||
fontWeight: FontWeight.w600,
|
child: InkWell(
|
||||||
color: Colors.white,
|
onTap: () => _presentPicker(context),
|
||||||
)),
|
child: Row(
|
||||||
sendViewModel.selectedCryptoCurrency.tag != null ? Padding(
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
padding: const EdgeInsets.fromLTRB(3.0,0,3.0,0),
|
mainAxisSize: MainAxisSize.min,
|
||||||
child: Container(
|
children: <Widget>[
|
||||||
height: 32,
|
Padding(
|
||||||
decoration: BoxDecoration(
|
padding: EdgeInsets.only(right: 5),
|
||||||
color: Theme.of(context)
|
child: Image.asset(
|
||||||
.primaryTextTheme!
|
'assets/images/arrow_bottom_purple_icon.png',
|
||||||
.headlineMedium!
|
color: Colors.white,
|
||||||
.color!,
|
height: 8,
|
||||||
borderRadius:
|
),
|
||||||
BorderRadius.all(Radius.circular(6))),
|
),
|
||||||
child: Center(
|
Text(
|
||||||
child: Padding(
|
sendViewModel.selectedCryptoCurrency.title,
|
||||||
padding: const EdgeInsets.all(6.0),
|
style: TextStyle(
|
||||||
child: Text( sendViewModel.selectedCryptoCurrency.tag!,
|
fontWeight: FontWeight.w600,
|
||||||
style: TextStyle(
|
fontSize: 16,
|
||||||
fontSize: 12,
|
color: Colors.white),
|
||||||
fontWeight: FontWeight.bold,
|
),
|
||||||
color: Theme.of(context)
|
],
|
||||||
.primaryTextTheme!
|
),
|
||||||
.headlineMedium!
|
|
||||||
.decorationColor!)),
|
|
||||||
),
|
),
|
||||||
),
|
)
|
||||||
),
|
: Text(
|
||||||
) : Container(),
|
sendViewModel.selectedCryptoCurrency.title,
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(right: 10.0),
|
|
||||||
child: Text(':',
|
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
color: Colors.white)),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: Stack(
|
|
||||||
children: [
|
|
||||||
BaseTextFormField(
|
|
||||||
focusNode: cryptoAmountFocus,
|
|
||||||
controller: cryptoAmountController,
|
|
||||||
keyboardType:
|
|
||||||
TextInputType.numberWithOptions(
|
|
||||||
signed: false, decimal: true),
|
|
||||||
inputFormatters: [
|
|
||||||
FilteringTextInputFormatter.deny(RegExp('[\\-|\\ ]'))
|
|
||||||
],
|
|
||||||
suffixIcon: SizedBox(
|
|
||||||
width: prefixIconWidth,
|
|
||||||
),
|
|
||||||
hintText: '0.0000',
|
|
||||||
borderColor: Colors.transparent,
|
|
||||||
textStyle: TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: Colors.white),
|
color: Colors.white),
|
||||||
placeholderTextStyle: TextStyle(
|
),
|
||||||
color: Theme.of(context)
|
sendViewModel.selectedCryptoCurrency.tag != null
|
||||||
.primaryTextTheme!
|
? Padding(
|
||||||
.headlineSmall!
|
padding: const EdgeInsets.fromLTRB(3.0, 0, 3.0, 0),
|
||||||
.decorationColor!,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
fontSize: 14),
|
|
||||||
validator: output.sendAll
|
|
||||||
? sendViewModel.allAmountValidator
|
|
||||||
: sendViewModel
|
|
||||||
.amountValidator),
|
|
||||||
if (!sendViewModel.isBatchSending) Positioned(
|
|
||||||
top: 2,
|
|
||||||
right: 0,
|
|
||||||
child: Container(
|
child: Container(
|
||||||
width: prefixIconWidth,
|
height: 32,
|
||||||
height: prefixIconHeight,
|
decoration: BoxDecoration(
|
||||||
child: InkWell(
|
color: Theme.of(context)
|
||||||
onTap: () async =>
|
.primaryTextTheme.headlineMedium!
|
||||||
output.setSendAll(),
|
.color!,
|
||||||
child: Container(
|
borderRadius: BorderRadius.all(
|
||||||
decoration: BoxDecoration(
|
Radius.circular(6),
|
||||||
color: Theme.of(context)
|
)),
|
||||||
.primaryTextTheme!
|
child: Center(
|
||||||
.headlineMedium!
|
child: Padding(
|
||||||
.color!,
|
padding: const EdgeInsets.all(6.0),
|
||||||
borderRadius:
|
child: Text(
|
||||||
BorderRadius.all(
|
sendViewModel.selectedCryptoCurrency.tag!,
|
||||||
Radius.circular(6))),
|
style: TextStyle(
|
||||||
child: Center(
|
fontSize: 12,
|
||||||
child: Text(
|
fontWeight: FontWeight.bold,
|
||||||
S.of(context).all,
|
color: Theme.of(context)
|
||||||
textAlign:
|
.primaryTextTheme.headlineMedium!
|
||||||
TextAlign.center,
|
.decorationColor!),
|
||||||
style: TextStyle(
|
),
|
||||||
fontSize: 12,
|
),
|
||||||
fontWeight:
|
),
|
||||||
FontWeight.bold,
|
),
|
||||||
color:
|
)
|
||||||
Theme.of(context)
|
: Container(),
|
||||||
.primaryTextTheme!
|
Padding(
|
||||||
.headlineMedium!
|
padding: const EdgeInsets.only(right: 10.0),
|
||||||
.decorationColor!))),
|
child: Text(
|
||||||
))))]),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
)),
|
|
||||||
Divider(height: 1,color: Theme.of(context)
|
|
||||||
.primaryTextTheme!
|
|
||||||
.headlineSmall!
|
|
||||||
.decorationColor!),
|
|
||||||
Observer(
|
|
||||||
builder: (_) => Padding(
|
|
||||||
padding: EdgeInsets.only(top: 10),
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.max,
|
|
||||||
mainAxisAlignment:
|
|
||||||
MainAxisAlignment.spaceBetween,
|
|
||||||
children: <Widget>[
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
S.of(context).available_balance +
|
|
||||||
':',
|
':',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12,
|
fontWeight: FontWeight.w600,
|
||||||
fontWeight: FontWeight.w600,
|
fontSize: 16,
|
||||||
color: Theme.of(context)
|
color: Colors.white),
|
||||||
.primaryTextTheme!
|
),
|
||||||
.headlineSmall!
|
),
|
||||||
.decorationColor!),
|
],
|
||||||
)),
|
),
|
||||||
Text(
|
),
|
||||||
sendViewModel.balance,
|
Expanded(
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
BaseTextFormField(
|
||||||
|
focusNode: cryptoAmountFocus,
|
||||||
|
controller: cryptoAmountController,
|
||||||
|
keyboardType: TextInputType.numberWithOptions(
|
||||||
|
signed: false, decimal: true),
|
||||||
|
inputFormatters: [
|
||||||
|
FilteringTextInputFormatter.deny(RegExp('[\\-|\\ ]'))
|
||||||
|
],
|
||||||
|
suffixIcon: SizedBox(
|
||||||
|
width: prefixIconWidth,
|
||||||
|
),
|
||||||
|
hintText: '0.0000',
|
||||||
|
borderColor: Colors.transparent,
|
||||||
|
textStyle: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Colors.white),
|
||||||
|
placeholderTextStyle: TextStyle(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.primaryTextTheme.headlineSmall!
|
||||||
|
.decorationColor!,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
fontSize: 14),
|
||||||
|
validator: output.sendAll
|
||||||
|
? sendViewModel.allAmountValidator
|
||||||
|
: sendViewModel.amountValidator,
|
||||||
|
),
|
||||||
|
if (!sendViewModel.isBatchSending)
|
||||||
|
Positioned(
|
||||||
|
top: 2,
|
||||||
|
right: 0,
|
||||||
|
child: Container(
|
||||||
|
width: prefixIconWidth,
|
||||||
|
height: prefixIconHeight,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () async => output.setSendAll(),
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.primaryTextTheme.headlineMedium!
|
||||||
|
.color!,
|
||||||
|
borderRadius: BorderRadius.all(
|
||||||
|
Radius.circular(6),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
S.of(context).all,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.primaryTextTheme.headlineMedium!
|
||||||
|
.decorationColor!,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
Divider(
|
||||||
|
height: 1,
|
||||||
|
color: Theme.of(context).primaryTextTheme.headlineSmall!.decorationColor!),
|
||||||
|
Observer(
|
||||||
|
builder: (_) => Padding(
|
||||||
|
padding: EdgeInsets.only(top: 10),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: <Widget>[
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
S.of(context).available_balance + ':',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
color: Theme.of(context)
|
color: Theme.of(context)
|
||||||
.primaryTextTheme!
|
.primaryTextTheme.headlineSmall!
|
||||||
.headlineSmall!
|
|
||||||
.decorationColor!),
|
.decorationColor!),
|
||||||
)
|
),
|
||||||
],
|
),
|
||||||
),
|
Text(
|
||||||
)),
|
sendViewModel.balance,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.primaryTextTheme.headlineSmall!
|
||||||
|
.decorationColor!),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
if (!sendViewModel.isFiatDisabled)
|
if (!sendViewModel.isFiatDisabled)
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(top: 20),
|
padding: const EdgeInsets.only(top: 20),
|
||||||
|
@ -377,171 +395,154 @@ class SendCardState extends State<SendCard>
|
||||||
focusNode: fiatAmountFocus,
|
focusNode: fiatAmountFocus,
|
||||||
controller: fiatAmountController,
|
controller: fiatAmountController,
|
||||||
keyboardType:
|
keyboardType:
|
||||||
TextInputType.numberWithOptions(
|
TextInputType.numberWithOptions(signed: false, decimal: true),
|
||||||
signed: false, decimal: true),
|
|
||||||
inputFormatters: [
|
inputFormatters: [
|
||||||
FilteringTextInputFormatter.deny(RegExp('[\\-|\\ ]'))
|
FilteringTextInputFormatter.deny(
|
||||||
|
RegExp('[\\-|\\ ]'),
|
||||||
|
)
|
||||||
],
|
],
|
||||||
prefixIcon: Padding(
|
prefixIcon: Padding(
|
||||||
padding: EdgeInsets.only(top: 9),
|
padding: EdgeInsets.only(top: 9),
|
||||||
child:
|
child: Text(
|
||||||
Text(sendViewModel.fiat.title + ':',
|
sendViewModel.fiat.title + ':',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
)),
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
hintText: '0.00',
|
hintText: '0.00',
|
||||||
borderColor: Theme.of(context)
|
borderColor: Theme.of(context).primaryTextTheme.headlineSmall!.color!,
|
||||||
.primaryTextTheme!
|
|
||||||
.headlineSmall!
|
|
||||||
.color!,
|
|
||||||
textStyle: TextStyle(
|
textStyle: TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14, fontWeight: FontWeight.w500, color: Colors.white),
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: Colors.white),
|
|
||||||
placeholderTextStyle: TextStyle(
|
placeholderTextStyle: TextStyle(
|
||||||
color: Theme.of(context)
|
color: Theme.of(context)
|
||||||
.primaryTextTheme!.headlineSmall!.decorationColor!,
|
.primaryTextTheme.headlineSmall!
|
||||||
|
.decorationColor!,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
fontSize: 14),
|
fontSize: 14),
|
||||||
)),
|
),
|
||||||
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.only(top: 20),
|
padding: EdgeInsets.only(top: 20),
|
||||||
child: BaseTextFormField(
|
child: BaseTextFormField(
|
||||||
controller: noteController,
|
controller: noteController,
|
||||||
keyboardType: TextInputType.multiline,
|
keyboardType: TextInputType.multiline,
|
||||||
maxLines: null,
|
maxLines: null,
|
||||||
borderColor: Theme.of(context)
|
borderColor: Theme.of(context).primaryTextTheme.headlineSmall!.color!,
|
||||||
.primaryTextTheme!
|
|
||||||
.headlineSmall!
|
|
||||||
.color!,
|
|
||||||
textStyle: TextStyle(
|
textStyle: TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14, fontWeight: FontWeight.w500, color: Colors.white),
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: Colors.white),
|
|
||||||
hintText: S.of(context).note_optional,
|
hintText: S.of(context).note_optional,
|
||||||
placeholderTextStyle: TextStyle(
|
placeholderTextStyle: TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
color: Theme.of(context)
|
color: Theme.of(context)
|
||||||
.primaryTextTheme!
|
.primaryTextTheme.headlineSmall!
|
||||||
.headlineSmall!
|
|
||||||
.decorationColor!),
|
.decorationColor!),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Observer(
|
Observer(
|
||||||
builder: (_) => GestureDetector(
|
builder: (_) => GestureDetector(
|
||||||
onTap: () =>
|
onTap: () => _setTransactionPriority(context),
|
||||||
_setTransactionPriority(context),
|
child: Container(
|
||||||
child: Container(
|
padding: EdgeInsets.only(top: 24),
|
||||||
padding: EdgeInsets.only(top: 24),
|
child: Row(
|
||||||
child: Row(
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
mainAxisAlignment:
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
MainAxisAlignment.spaceBetween,
|
children: <Widget>[
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
Text(
|
||||||
children: <Widget>[
|
S.of(context).send_estimated_fee,
|
||||||
Text(
|
style: TextStyle(
|
||||||
S
|
fontSize: 12,
|
||||||
.of(context)
|
fontWeight: FontWeight.w500,
|
||||||
.send_estimated_fee,
|
//color: Theme.of(context).primaryTextTheme!.displaySmall!.color!,
|
||||||
style: TextStyle(
|
color: Colors.white),
|
||||||
fontSize: 12,
|
),
|
||||||
fontWeight:
|
Container(
|
||||||
FontWeight.w500,
|
child: Row(
|
||||||
//color: Theme.of(context).primaryTextTheme!.displaySmall!.color!,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
color: Colors.white)),
|
children: <Widget>[
|
||||||
Container(
|
Column(
|
||||||
child: Row(
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
children: <Widget>[
|
children: [
|
||||||
Column(
|
Text(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
output.estimatedFee.toString() +
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
' ' +
|
||||||
children: [
|
sendViewModel.selectedCryptoCurrency.toString(),
|
||||||
Text(
|
style: TextStyle(
|
||||||
output
|
fontSize: 12,
|
||||||
.estimatedFee
|
fontWeight: FontWeight.w600,
|
||||||
.toString() +
|
//color: Theme.of(context).primaryTextTheme!.displaySmall!.color!,
|
||||||
' ' +
|
color: Colors.white,
|
||||||
sendViewModel
|
|
||||||
.selectedCryptoCurrency.toString(),
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight:
|
|
||||||
FontWeight.w600,
|
|
||||||
//color: Theme.of(context).primaryTextTheme!.displaySmall!.color!,
|
|
||||||
color:
|
|
||||||
Colors.white)),
|
|
||||||
Padding(
|
|
||||||
padding:
|
|
||||||
EdgeInsets.only(top: 5),
|
|
||||||
child: sendViewModel.isFiatDisabled
|
|
||||||
? const SizedBox(height: 14)
|
|
||||||
: Text(output
|
|
||||||
.estimatedFeeFiatAmount
|
|
||||||
+ ' ' +
|
|
||||||
sendViewModel
|
|
||||||
.fiat.title,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight:
|
|
||||||
FontWeight.w600,
|
|
||||||
color: Theme
|
|
||||||
.of(context)
|
|
||||||
.primaryTextTheme!
|
|
||||||
.headlineSmall!
|
|
||||||
.decorationColor!))
|
|
||||||
),
|
),
|
||||||
],
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsets.only(
|
|
||||||
top: 2,
|
|
||||||
left: 5),
|
|
||||||
child: Icon(
|
|
||||||
Icons.arrow_forward_ios,
|
|
||||||
size: 12,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
),
|
||||||
)
|
Padding(
|
||||||
],
|
padding: EdgeInsets.only(top: 5),
|
||||||
),
|
child: sendViewModel.isFiatDisabled
|
||||||
)
|
? const SizedBox(height: 14)
|
||||||
|
: Text(
|
||||||
|
output.estimatedFeeFiatAmount +
|
||||||
|
' ' +
|
||||||
|
sendViewModel.fiat.title,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.primaryTextTheme.headlineSmall!
|
||||||
|
.decorationColor!,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(top: 2, left: 5),
|
||||||
|
child: Icon(
|
||||||
|
Icons.arrow_forward_ios,
|
||||||
|
size: 12,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (sendViewModel.isElectrumWallet)
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(top: 6),
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () => Navigator.of(context).pushNamed(Routes.unspentCoinsList),
|
||||||
|
child: Container(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
S.of(context).coin_control,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Colors.white),
|
||||||
|
),
|
||||||
|
Icon(
|
||||||
|
Icons.arrow_forward_ios,
|
||||||
|
size: 12,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)),
|
),
|
||||||
if (sendViewModel.isElectrumWallet) Padding(
|
),
|
||||||
padding: EdgeInsets.only(top: 6),
|
|
||||||
child: GestureDetector(
|
|
||||||
onTap: () => Navigator.of(context)
|
|
||||||
.pushNamed(Routes.unspentCoinsList),
|
|
||||||
child: Container(
|
|
||||||
color: Colors.transparent,
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment:
|
|
||||||
MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
S.of(context).coin_control,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: Colors.white)),
|
|
||||||
Icon(
|
|
||||||
Icons.arrow_forward_ios,
|
|
||||||
size: 12,
|
|
||||||
color: Colors.white,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
],
|
],
|
||||||
))
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -550,7 +551,7 @@ class SendCardState extends State<SendCard>
|
||||||
}
|
}
|
||||||
|
|
||||||
void _setEffects(BuildContext context) {
|
void _setEffects(BuildContext context) {
|
||||||
if (_effectsInstalled) {
|
if (_effectsInstalled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -586,7 +587,7 @@ class SendCardState extends State<SendCard>
|
||||||
});
|
});
|
||||||
|
|
||||||
noteController.addListener(() {
|
noteController.addListener(() {
|
||||||
final note = noteController.text ?? '';
|
final note = noteController.text;
|
||||||
|
|
||||||
if (note != output.note) {
|
if (note != output.note) {
|
||||||
output.note = note;
|
output.note = note;
|
||||||
|
@ -662,16 +663,30 @@ class SendCardState extends State<SendCard>
|
||||||
final selectedItem = items.indexOf(sendViewModel.transactionPriority);
|
final selectedItem = items.indexOf(sendViewModel.transactionPriority);
|
||||||
|
|
||||||
await showPopUp<void>(
|
await showPopUp<void>(
|
||||||
builder: (_) => Picker(
|
context: context,
|
||||||
items: items,
|
builder: (_) => Picker(
|
||||||
displayItem: sendViewModel.displayFeeRate,
|
items: items,
|
||||||
selectedAtIndex: selectedItem,
|
displayItem: sendViewModel.displayFeeRate,
|
||||||
title: S.of(context).please_select,
|
selectedAtIndex: selectedItem,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
title: S.of(context).please_select,
|
||||||
onItemSelected: (TransactionPriority priority) =>
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
sendViewModel.setTransactionPriority(priority),
|
onItemSelected: (TransactionPriority priority) =>
|
||||||
),
|
sendViewModel.setTransactionPriority(priority),
|
||||||
context: context);
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _presentPicker(BuildContext context) {
|
||||||
|
showPopUp<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (_) => CurrencyPicker(
|
||||||
|
selectedAtIndex: sendViewModel.currencies.indexOf(sendViewModel.selectedCryptoCurrency),
|
||||||
|
items: sendViewModel.currencies,
|
||||||
|
hintText: S.of(context).search_currency,
|
||||||
|
onItemSelected: (Currency cur) =>
|
||||||
|
sendViewModel.selectedCryptoCurrency = (cur as CryptoCurrency),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import 'package:cake_wallet/entities/balance_display_mode.dart';
|
|
||||||
import 'package:cake_wallet/entities/priority_for_wallet_type.dart';
|
import 'package:cake_wallet/entities/priority_for_wallet_type.dart';
|
||||||
import 'package:cake_wallet/entities/transaction_description.dart';
|
import 'package:cake_wallet/entities/transaction_description.dart';
|
||||||
import 'package:cake_wallet/ethereum/ethereum.dart';
|
import 'package:cake_wallet/ethereum/ethereum.dart';
|
||||||
|
@ -43,6 +42,7 @@ abstract class SendViewModelBase with Store {
|
||||||
: state = InitialExecutionState(),
|
: state = InitialExecutionState(),
|
||||||
currencies = _wallet.balance.keys.toList(),
|
currencies = _wallet.balance.keys.toList(),
|
||||||
selectedCryptoCurrency = _wallet.currency,
|
selectedCryptoCurrency = _wallet.currency,
|
||||||
|
hasMultipleTokens = _wallet.type == WalletType.ethereum,
|
||||||
outputs = ObservableList<Output>(),
|
outputs = ObservableList<Output>(),
|
||||||
fiatFromSettings = _settingsStore.fiatCurrency {
|
fiatFromSettings = _settingsStore.fiatCurrency {
|
||||||
final priority = _settingsStore.priority[_wallet.type];
|
final priority = _settingsStore.priority[_wallet.type];
|
||||||
|
@ -127,14 +127,14 @@ abstract class SendViewModelBase with Store {
|
||||||
|
|
||||||
CryptoCurrency get currency => _wallet.currency;
|
CryptoCurrency get currency => _wallet.currency;
|
||||||
|
|
||||||
Validator get amountValidator =>
|
Validator<String> get amountValidator =>
|
||||||
AmountValidator(currency: walletTypeToCryptoCurrency(_wallet.type));
|
AmountValidator(currency: walletTypeToCryptoCurrency(_wallet.type));
|
||||||
|
|
||||||
Validator get allAmountValidator => AllAmountValidator();
|
Validator<String> get allAmountValidator => AllAmountValidator();
|
||||||
|
|
||||||
Validator get addressValidator => AddressValidator(type: selectedCryptoCurrency);
|
Validator<String> get addressValidator => AddressValidator(type: selectedCryptoCurrency);
|
||||||
|
|
||||||
Validator get textValidator => TextValidator();
|
Validator<String> get textValidator => TextValidator();
|
||||||
|
|
||||||
final FiatCurrency fiatFromSettings;
|
final FiatCurrency fiatFromSettings;
|
||||||
|
|
||||||
|
@ -142,7 +142,7 @@ abstract class SendViewModelBase with Store {
|
||||||
PendingTransaction? pendingTransaction;
|
PendingTransaction? pendingTransaction;
|
||||||
|
|
||||||
@computed
|
@computed
|
||||||
String get balance => balanceViewModel.availableBalance;
|
String get balance => _wallet.balance[selectedCryptoCurrency]!.formattedAvailableBalance;
|
||||||
|
|
||||||
@computed
|
@computed
|
||||||
bool get isFiatDisabled => balanceViewModel.isFiatDisabled;
|
bool get isFiatDisabled => balanceViewModel.isFiatDisabled;
|
||||||
|
@ -196,6 +196,7 @@ abstract class SendViewModelBase with Store {
|
||||||
final BalanceViewModel balanceViewModel;
|
final BalanceViewModel balanceViewModel;
|
||||||
final FiatConversionStore _fiatConversationStore;
|
final FiatConversionStore _fiatConversationStore;
|
||||||
final Box<TransactionDescription> transactionDescriptionBox;
|
final Box<TransactionDescription> transactionDescriptionBox;
|
||||||
|
final bool hasMultipleTokens;
|
||||||
|
|
||||||
@action
|
@action
|
||||||
Future<void> createTransaction() async {
|
Future<void> createTransaction() async {
|
||||||
|
@ -295,7 +296,8 @@ abstract class SendViewModelBase with Store {
|
||||||
throw Exception('Priority is null for wallet type: ${_wallet.type}');
|
throw Exception('Priority is null for wallet type: ${_wallet.type}');
|
||||||
}
|
}
|
||||||
|
|
||||||
return ethereum!.createEthereumTransactionCredentials(outputs, priority: priority);
|
return ethereum!.createEthereumTransactionCredentials(
|
||||||
|
outputs, priority: priority, currency: selectedCryptoCurrency);
|
||||||
default:
|
default:
|
||||||
throw Exception('Unexpected wallet type: ${_wallet.type}');
|
throw Exception('Unexpected wallet type: ${_wallet.type}');
|
||||||
}
|
}
|
||||||
|
|
|
@ -508,10 +508,22 @@ abstract class Ethereum {
|
||||||
TransactionPriority deserializeEthereumTransactionPriority(int raw);
|
TransactionPriority deserializeEthereumTransactionPriority(int raw);
|
||||||
int getEstimatedFee(Object wallet, TransactionPriority priority);
|
int getEstimatedFee(Object wallet, TransactionPriority priority);
|
||||||
|
|
||||||
Object createEthereumTransactionCredentials(List<Output> outputs, {required TransactionPriority priority, int? feeRate});
|
Object createEthereumTransactionCredentials(
|
||||||
Object createEthereumTransactionCredentialsRaw(List<OutputInfo> outputs, {TransactionPriority? priority, required int feeRate});
|
List<Output> outputs, {
|
||||||
|
required TransactionPriority priority,
|
||||||
|
required CryptoCurrency currency,
|
||||||
|
int? feeRate,
|
||||||
|
});
|
||||||
|
|
||||||
|
Object createEthereumTransactionCredentialsRaw(
|
||||||
|
List<OutputInfo> outputs, {
|
||||||
|
TransactionPriority? priority,
|
||||||
|
required CryptoCurrency currency,
|
||||||
|
required int feeRate,
|
||||||
|
});
|
||||||
|
|
||||||
int formatterEthereumParseAmount(String amount);
|
int formatterEthereumParseAmount(String amount);
|
||||||
|
List<CryptoCurrency> getERC20Currencies(Object wallet);
|
||||||
}
|
}
|
||||||
""";
|
""";
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue