Skip to content

Commit

Permalink
fix: valdidate staking amount
Browse files Browse the repository at this point in the history
  • Loading branch information
gabaldon committed Nov 27, 2024
1 parent 08cc388 commit b147fd5
Show file tree
Hide file tree
Showing 10 changed files with 110 additions and 76 deletions.
5 changes: 3 additions & 2 deletions integration_test/e2e_stake_unstake.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,9 @@ Future<void> e2eStakeUnstakeTest(WidgetTester tester) async {
await tester.pumpAndSettle();
await tapButton(tester, localization.stakeUnstake, semantics: true);
await tapButton(tester, localization.stake);
expect(widgetByText(localization.sendStakeTransaction), findsOneWidget);
await tapButton(tester, localization.stakeUnstake, semantics: true);
expect(widgetByText(localization.disableStakeTitle), findsOneWidget);
await tapButton(tester, localization.close);
await tester.pumpAndSettle();
await tapButton(tester, localization.unstake);
expect(widgetByText(localization.emptyStakeTitle), findsOneWidget);

Expand Down
6 changes: 3 additions & 3 deletions lib/constants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,6 @@ const List<String> CUSTOM_ICON_NAMES = [
'myWitWallet-title',
'myWitWallet-title-dark'
];

double MAX_STAKING_AMOUNT_NANOWIT = 10000000 * 1000000000;
double MIN_STAKING_AMOUNT_NANOWIT = 3;
double ONE_WIT_TO_NANO = 1000000000;
double MAX_STAKING_AMOUNT_NANOWIT = 10000000 * ONE_WIT_TO_NANO;
double MIN_STAKING_AMOUNT_NANOWIT = 10000 * ONE_WIT_TO_NANO;
2 changes: 1 addition & 1 deletion lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -357,5 +357,5 @@
"welcomeBack": "Welcome back",
"deleteWalletSettings": "Settings: Delete wallet",
"disableStakeTitle": "You don't have enough balance to stake",
"disableStakeMessage": "The minimun amount to stake is 10.000 WIT"
"disableStakeMessage": "The minimun amount to stake is 10,000 WIT"
}
1 change: 1 addition & 0 deletions lib/widgets/inputs/input_slider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ class _InputSliderState extends State<InputSlider> {
keyboardType: TextInputType.number,
onChanged: widget.onChanged,
onTap: widget.onTap,
inputFormatters: widget.inputFormatters,
onFieldSubmitted: widget.onFieldSubmitted,
onEditingComplete: widget.onEditingComplete,
),
Expand Down
6 changes: 4 additions & 2 deletions lib/widgets/stake_unstake.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,11 @@ class StakeUnstakeButtons extends StatelessWidget {
ApiDatabase db = Locator.instance.get<ApiDatabase>();
Wallet currentWallet = db.walletStorage.currentWallet;
late StakedBalanceInfo stakeInfo = currentWallet.stakedNanoWit();
bool allowStake = MIN_STAKING_AMOUNT_NANOWIT <
currentWallet.balanceNanoWit().availableNanoWit;

Future<void> _goToStakeScreen() async {
if (MIN_STAKING_AMOUNT_NANOWIT <
currentWallet.balanceNanoWit().availableNanoWit) {
if (allowStake) {
BlocProvider.of<TransactionBloc>(context).add(ResetTransactionEvent());
Navigator.push(
context,
Expand Down Expand Up @@ -68,6 +69,7 @@ class StakeUnstakeButtons extends StatelessWidget {
ScaffoldMessenger.of(context).clearSnackBars();
buildEmptyStakeModal(
theme: theme,
allowStake: allowStake,
context: context,
originRouteName: DashboardScreen.route,
originRoute: DashboardScreen());
Expand Down
28 changes: 19 additions & 9 deletions lib/widgets/validations/fee_amount_input.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import 'package:my_wit_wallet/constants.dart';
import 'package:my_wit_wallet/widgets/validations/vtt_amount_input.dart';
import 'package:my_wit_wallet/widgets/validations/tx_amount_input.dart';
import 'package:my_wit_wallet/widgets/validations/validation_utils.dart';
import 'package:my_wit_wallet/util/extensions/num_extensions.dart';
import 'package:my_wit_wallet/util/get_localization.dart';
Expand All @@ -10,7 +10,7 @@ Map<FeeInputError, String> errorMap = {
FeeInputError.minFee: localization.validationMinFee,
};

class FeeAmountInput extends VttAmountInput {
class FeeAmountInput extends TxAmountInput {
final int availableNanoWit;
final int? weightedAmount;
final bool allowZero;
Expand Down Expand Up @@ -38,20 +38,30 @@ class FeeAmountInput extends VttAmountInput {
this.allowZero = false,
this.allowValidation = false})
: super.dirty(
value: value,
allowZero: allowZero,
allowValidation: allowValidation,
availableNanoWit: availableNanoWit,
weightedAmount: weightedAmount);
value: value,
allowZero: allowZero,
allowValidation: allowValidation,
availableNanoWit: availableNanoWit,
);

// Override notEnoughFunds to handle validating taking into account the vttAmount
@override
bool notEnoughFunds({bool avoidWeightedAmountCheck = false}) {
int nanoWitAmount = super
.getNanoWitAmount(avoidWeightedAmountCheck: avoidWeightedAmountCheck);
int nanoWitAmount =
getNanoWitAmount(avoidWeightedAmountCheck: avoidWeightedAmountCheck);
return this.availableNanoWit < (nanoWitAmount + this.vttAmount);
}

int getNanoWitAmount({bool avoidWeightedAmountCheck = false}) {
int nanoWitAmount;
if (!avoidWeightedAmountCheck) {
nanoWitAmount = this.weightedAmount ?? witAmountToNanoWitNumber(value);
} else {
nanoWitAmount = witAmountToNanoWitNumber(value);
}
return nanoWitAmount;
}

// Override validator to handle validating a given input value.
@override
String? validator(String value, {bool avoidWeightedAmountCheck = false}) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ enum AmountInputError {
zero,
decimals,
invalidNumber,
lessThanMin,
greaterThanMax
}

Map<AmountInputError, String> errorMap = {
Expand All @@ -19,29 +21,32 @@ Map<AmountInputError, String> errorMap = {
AmountInputError.zero: 'Amount cannot be zero',
AmountInputError.invalid: 'Invalid amount',
AmountInputError.decimals: 'Only 9 decimal digits supported',
AmountInputError.lessThanMin: 'The amount is less than the minimum required',
AmountInputError.greaterThanMax:
'The amount is greater than the maximum allowed'
};

// Extend FormzInput and provide the input type and error type.
class VttAmountInput extends AmountInput {
class TxAmountInput extends AmountInput {
final int availableNanoWit;
final int? weightedAmount;
final bool allowZero;
final bool allowValidation;
final bool isStakeAmount;

// Call super.pure to represent an unmodified form input.
VttAmountInput.pure()
TxAmountInput.pure()
: availableNanoWit = 0,
allowZero = false,
weightedAmount = null,
isStakeAmount = false,
allowValidation = false,
super.pure();

// Call super.dirty to represent a modified form input.
VttAmountInput.dirty(
TxAmountInput.dirty(
{required this.availableNanoWit,
value = '',
this.weightedAmount,
this.allowZero = false,
this.isStakeAmount = false,
this.allowValidation = false})
: super.dirty(
value: value,
Expand All @@ -61,22 +66,20 @@ class VttAmountInput extends AmountInput {
}
}

int getNanoWitAmount({bool avoidWeightedAmountCheck = false}) {
int nanoWitAmount;
if (!avoidWeightedAmountCheck) {
nanoWitAmount = this.weightedAmount ?? witAmountToNanoWitNumber(value);
} else {
nanoWitAmount = witAmountToNanoWitNumber(value);
}
return nanoWitAmount;
int getNanoWitAmount() {
return witAmountToNanoWitNumber(value);
}

bool notEnoughFunds({bool avoidWeightedAmountCheck = false}) {
int nanoWitAmount =
getNanoWitAmount(avoidWeightedAmountCheck: avoidWeightedAmountCheck);
int nanoWitAmount = getNanoWitAmount();
return this.availableNanoWit < nanoWitAmount;
}

bool get lessThanMinimum =>
getNanoWitAmount() < MIN_STAKING_AMOUNT_NANOWIT.toInt();
bool get greaterThanMaximum =>
getNanoWitAmount() < MAX_STAKING_AMOUNT_NANOWIT.toInt();

// Override validator to handle validating a given input value.
@override
String? validator(String value, {bool avoidWeightedAmountCheck = false}) {
Expand All @@ -86,6 +89,12 @@ class VttAmountInput extends AmountInput {
if (error != null) {
return error;
}
if (isStakeAmount && lessThanMinimum) {
return validationUtils.getErrorText(AmountInputError.lessThanMin);
}
if (isStakeAmount && greaterThanMaximum) {
return validationUtils.getErrorText(AmountInputError.greaterThanMax);
}
if (notEnoughFunds(avoidWeightedAmountCheck: avoidWeightedAmountCheck))
return validationUtils.getErrorText(AmountInputError.notEnough);
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import 'package:my_wit_wallet/widgets/snack_bars.dart';
import 'package:my_wit_wallet/widgets/validations/address_input.dart';
import 'package:my_wit_wallet/widgets/validations/authorization_input.dart';
import 'package:my_wit_wallet/widgets/validations/validation_utils.dart';
import 'package:my_wit_wallet/widgets/validations/vtt_amount_input.dart';
import 'package:my_wit_wallet/widgets/validations/tx_amount_input.dart';
import 'package:my_wit_wallet/widgets/witnet/transactions/value_transfer/create_dialog_box/vtt_builder/timelock_input.dart';
import 'package:my_wit_wallet/widgets/witnet/transactions/value_transfer/create_dialog_box/vtt_builder/timelock_picker.dart';
import 'package:witnet/schema.dart';
Expand Down Expand Up @@ -62,13 +62,10 @@ class RecipientStepState extends State<RecipientStep>
late AnimationController _loadingController;
final _formKey = GlobalKey<FormState>();
AddressInput _address = AddressInput.pure();
VttAmountInput _amount = VttAmountInput.pure();
VttAmountInput _stakeAmount = VttAmountInput.pure();
TxAmountInput _amount = TxAmountInput.pure();
AuthorizationInput _authorization = AuthorizationInput.pure();
final _amountController = StyledTextController();
final _amountFocusNode = FocusNode();
final _stakeAmountController = StyledTextController();
final _stakeAmountFocusNode = FocusNode();
final _addressController = StyledTextController();
final _addressFocusNode = FocusNode();
final _authorizationController = StyledTextController();
Expand Down Expand Up @@ -122,11 +119,6 @@ class RecipientStepState extends State<RecipientStep>
int weeksToAdd = 2;
setMinimunTimelock(date.add(Duration(days: (7 * weeksToAdd).toInt())));
}
if (isStakeTarnsaction) {
_stakeAmountController.text = MIN_STAKING_AMOUNT_NANOWIT
.standardizeWitUnits()
.formatWithCommaSeparator();
}
}

@override
Expand All @@ -139,8 +131,6 @@ class RecipientStepState extends State<RecipientStep>
_amountFocusNode.dispose();
_authorizationController.dispose();
_authorizationFocusNode.dispose();
_stakeAmountController.dispose();
_stakeAmountFocusNode.dispose();
super.dispose();
}

Expand Down Expand Up @@ -190,10 +180,11 @@ class RecipientStepState extends State<RecipientStep>

void setAmount(String value, {bool? validate}) {
setState(() {
_amount = VttAmountInput.dirty(
_amount = TxAmountInput.dirty(
availableNanoWit: isUnstakeTransaction
? stakeInfo.stakedNanoWit
: balanceInfo.availableNanoWit,
isStakeAmount: isStakeTarnsaction,
allowValidation:
validate ?? validationUtils.isFormUnFocus(_formFocusElements()),
value: value);
Expand All @@ -217,6 +208,11 @@ class RecipientStepState extends State<RecipientStep>
}

void _setSavedTxData() {
if (isStakeTarnsaction) {
_amountController.text =
MIN_STAKING_AMOUNT_NANOWIT.standardizeWitUnits().toString();
setAmount(_amountController.text, validate: false);
}
if (vttBloc.state.transaction.hasOutput(widget.transactionType)) {
String? savedAddress =
vttBloc.state.transaction.get(widget.transactionType) != null
Expand Down Expand Up @@ -419,20 +415,20 @@ class RecipientStepState extends State<RecipientStep>
minAmount: minWitAmount,
inputFormatters: [WitValueFormatter()],
maxAmount: maxAmount,
errorText: _stakeAmount.error,
styledTextController: _stakeAmountController,
focusNode: _stakeAmountFocusNode,
errorText: _amount.error,
styledTextController: _amountController,
focusNode: _amountFocusNode,
keyboardType: TextInputType.number,
onChanged: (String value) {
_stakeAmountController.text = value;
_amountController.text = value;
setAmount(value);
},
onSuffixTap: () => {
_amountController.text = maxAmountWit,
setAmount(maxAmountWit),
_stakeAmountController.text = maxAmountWit,
},
onTap: () {
_stakeAmountFocusNode.requestFocus();
_amountFocusNode.requestFocus();
},
onFieldSubmitted: (String value) {
// hide keyboard
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ void buildEmptyStakeModal({
required ThemeData theme,
required BuildContext context,
required String originRouteName,
bool allowStake = true,
required Widget originRoute,
String iconName = 'empty',
}) {
Expand All @@ -20,30 +21,33 @@ void buildEmptyStakeModal({
padding: EdgeInsets.zero,
text: localization.close,
sizeCover: false,
type: CustomBtnType.secondary,
type: allowStake ? CustomBtnType.secondary : CustomBtnType.primary,
enabled: true,
onPressed: () => {
Navigator.popUntil(
context, ModalRoute.withName(originRouteName)),
ScaffoldMessenger.of(context).clearSnackBars(),
}),
CustomButton(
padding: EdgeInsets.zero,
text: localization.stake,
sizeCover: false,
type: CustomBtnType.primary,
enabled: true,
onPressed: () => {
Navigator.push(
context,
CustomPageRoute(
builder: (BuildContext context) {
return StakeScreen();
},
maintainState: false,
settings: RouteSettings(name: StakeScreen.route))),
ScaffoldMessenger.of(context).clearSnackBars(),
}),
allowStake
? CustomButton(
padding: EdgeInsets.zero,
text: localization.stake,
sizeCover: false,
type: CustomBtnType.primary,
enabled: true,
onPressed: () => {
Navigator.push(
context,
CustomPageRoute(
builder: (BuildContext context) {
return StakeScreen();
},
maintainState: false,
settings:
RouteSettings(name: StakeScreen.route))),
ScaffoldMessenger.of(context).clearSnackBars(),
})
: Container(),
],
image: Container(
width: 100, height: 100, child: svgImage(name: iconName, height: 50)),
Expand Down
Loading

0 comments on commit b147fd5

Please sign in to comment.