diff --git a/.fvmrc b/.fvmrc
index 4cac08f..3135e2b 100644
--- a/.fvmrc
+++ b/.fvmrc
@@ -1,3 +1,3 @@
{
- "flutter": "3.29.2"
+ "flutter": "3.32.2"
}
\ No newline at end of file
diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml
index 2a75e03..de75e55 100644
--- a/.github/workflows/pull-request.yml
+++ b/.github/workflows/pull-request.yml
@@ -30,7 +30,7 @@ jobs:
- uses: subosito/flutter-action@v2
with:
channel: "stable"
- flutter-version: 3.29.2
+ flutter-version: 3.32.2
- name: Install dependencies
run: flutter pub get && dart run build_runner build --delete-conflicting-outputs && flutter gen-l10n && dart run routefly && dart run web3kit:generate_abis
- name: Run tests
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 1638f1f..ad61127 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -10,6 +10,9 @@
"flutterMode": "release",
"toolArgs": [
"--dart-define=env=local",
+ ],
+ "args": [
+ "--web-experimental-hot-reload"
]
},
{
@@ -21,6 +24,9 @@
"flutterMode": "debug",
"toolArgs": [
"--dart-define=env=local",
+ ],
+ "args": [
+ "--web-experimental-hot-reload"
]
},
{
@@ -31,6 +37,9 @@
"program": "./lib/main.dart",
"toolArgs": [
"--dart-define=env=local",
+ ],
+ "args": [
+ "--web-experimental-hot-reload"
]
},
]
diff --git a/.vscode/settings.json b/.vscode/settings.json
index e4ebc86..de056e2 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,4 +1,4 @@
{
- "dart.flutterSdkPath": ".fvm/versions/3.29.2",
+ "dart.flutterSdkPath": ".fvm/versions/3.32.2",
"dart.lineLength": 120
}
\ No newline at end of file
diff --git a/assets/icons/text.document.svg b/assets/icons/text.document.svg
new file mode 100644
index 0000000..21695be
--- /dev/null
+++ b/assets/icons/text.document.svg
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/assets/logos/unichain.svg b/assets/logos/unichain.svg
new file mode 100644
index 0000000..d1015bd
--- /dev/null
+++ b/assets/logos/unichain.svg
@@ -0,0 +1,3 @@
+
diff --git a/lib/abis/uniswap_permit2.abi.json b/lib/abis/uniswap_permit2.abi.json
new file mode 100644
index 0000000..d1380fa
--- /dev/null
+++ b/lib/abis/uniswap_permit2.abi.json
@@ -0,0 +1,69 @@
+[
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "name": "allowance",
+ "outputs": [
+ {
+ "internalType": "uint160",
+ "name": "amount",
+ "type": "uint160"
+ },
+ {
+ "internalType": "uint48",
+ "name": "expiration",
+ "type": "uint48"
+ },
+ {
+ "internalType": "uint48",
+ "name": "nonce",
+ "type": "uint48"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "token",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "spender",
+ "type": "address"
+ },
+ {
+ "internalType": "uint160",
+ "name": "amount",
+ "type": "uint160"
+ },
+ {
+ "internalType": "uint48",
+ "name": "expiration",
+ "type": "uint48"
+ }
+ ],
+ "name": "approve",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ }
+]
\ No newline at end of file
diff --git a/lib/abis/uniswap_position_manager.abi.json b/lib/abis/uniswap_v3_position_manager.abi.json
similarity index 100%
rename from lib/abis/uniswap_position_manager.abi.json
rename to lib/abis/uniswap_v3_position_manager.abi.json
diff --git a/lib/abis/uniswap_v4_position_manager.abi.json b/lib/abis/uniswap_v4_position_manager.abi.json
new file mode 100644
index 0000000..5562e4c
--- /dev/null
+++ b/lib/abis/uniswap_v4_position_manager.abi.json
@@ -0,0 +1,20 @@
+[
+ {
+ "type": "function",
+ "name": "modifyLiquidities",
+ "inputs": [
+ {
+ "name": "unlockData",
+ "type": "bytes",
+ "internalType": "bytes"
+ },
+ {
+ "name": "deadline",
+ "type": "uint256",
+ "internalType": "uint256"
+ }
+ ],
+ "outputs": [],
+ "stateMutability": "payable"
+ }
+]
\ No newline at end of file
diff --git a/lib/abis/uniswap_v4_state_view.abi.json b/lib/abis/uniswap_v4_state_view.abi.json
new file mode 100644
index 0000000..fe9cd4f
--- /dev/null
+++ b/lib/abis/uniswap_v4_state_view.abi.json
@@ -0,0 +1,365 @@
+[
+ {
+ "inputs": [
+ {
+ "internalType": "contract IPoolManager",
+ "name": "_poolManager",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "nonpayable",
+ "type": "constructor"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "PoolId",
+ "name": "poolId",
+ "type": "bytes32"
+ }
+ ],
+ "name": "getFeeGrowthGlobals",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "feeGrowthGlobal0",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "feeGrowthGlobal1",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "PoolId",
+ "name": "poolId",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "int24",
+ "name": "tickLower",
+ "type": "int24"
+ },
+ {
+ "internalType": "int24",
+ "name": "tickUpper",
+ "type": "int24"
+ }
+ ],
+ "name": "getFeeGrowthInside",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "feeGrowthInside0X128",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "feeGrowthInside1X128",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "PoolId",
+ "name": "poolId",
+ "type": "bytes32"
+ }
+ ],
+ "name": "getLiquidity",
+ "outputs": [
+ {
+ "internalType": "uint128",
+ "name": "liquidity",
+ "type": "uint128"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "PoolId",
+ "name": "poolId",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "positionId",
+ "type": "bytes32"
+ }
+ ],
+ "name": "getPositionInfo",
+ "outputs": [
+ {
+ "internalType": "uint128",
+ "name": "liquidity",
+ "type": "uint128"
+ },
+ {
+ "internalType": "uint256",
+ "name": "feeGrowthInside0LastX128",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "feeGrowthInside1LastX128",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "PoolId",
+ "name": "poolId",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "address",
+ "name": "owner",
+ "type": "address"
+ },
+ {
+ "internalType": "int24",
+ "name": "tickLower",
+ "type": "int24"
+ },
+ {
+ "internalType": "int24",
+ "name": "tickUpper",
+ "type": "int24"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "salt",
+ "type": "bytes32"
+ }
+ ],
+ "name": "getPositionInfo",
+ "outputs": [
+ {
+ "internalType": "uint128",
+ "name": "liquidity",
+ "type": "uint128"
+ },
+ {
+ "internalType": "uint256",
+ "name": "feeGrowthInside0LastX128",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "feeGrowthInside1LastX128",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "PoolId",
+ "name": "poolId",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "positionId",
+ "type": "bytes32"
+ }
+ ],
+ "name": "getPositionLiquidity",
+ "outputs": [
+ {
+ "internalType": "uint128",
+ "name": "liquidity",
+ "type": "uint128"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "PoolId",
+ "name": "poolId",
+ "type": "bytes32"
+ }
+ ],
+ "name": "getSlot0",
+ "outputs": [
+ {
+ "internalType": "uint160",
+ "name": "sqrtPriceX96",
+ "type": "uint160"
+ },
+ {
+ "internalType": "int24",
+ "name": "tick",
+ "type": "int24"
+ },
+ {
+ "internalType": "uint24",
+ "name": "protocolFee",
+ "type": "uint24"
+ },
+ {
+ "internalType": "uint24",
+ "name": "lpFee",
+ "type": "uint24"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "PoolId",
+ "name": "poolId",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "int16",
+ "name": "tick",
+ "type": "int16"
+ }
+ ],
+ "name": "getTickBitmap",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "tickBitmap",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "PoolId",
+ "name": "poolId",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "int24",
+ "name": "tick",
+ "type": "int24"
+ }
+ ],
+ "name": "getTickFeeGrowthOutside",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "feeGrowthOutside0X128",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "feeGrowthOutside1X128",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "PoolId",
+ "name": "poolId",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "int24",
+ "name": "tick",
+ "type": "int24"
+ }
+ ],
+ "name": "getTickInfo",
+ "outputs": [
+ {
+ "internalType": "uint128",
+ "name": "liquidityGross",
+ "type": "uint128"
+ },
+ {
+ "internalType": "int128",
+ "name": "liquidityNet",
+ "type": "int128"
+ },
+ {
+ "internalType": "uint256",
+ "name": "feeGrowthOutside0X128",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "feeGrowthOutside1X128",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "PoolId",
+ "name": "poolId",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "int24",
+ "name": "tick",
+ "type": "int24"
+ }
+ ],
+ "name": "getTickLiquidity",
+ "outputs": [
+ {
+ "internalType": "uint128",
+ "name": "liquidityGross",
+ "type": "uint128"
+ },
+ {
+ "internalType": "int128",
+ "name": "liquidityNet",
+ "type": "int128"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "poolManager",
+ "outputs": [
+ {
+ "internalType": "contract IPoolManager",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ }
+]
\ No newline at end of file
diff --git a/lib/app/create/create_page_select_tokens_stage.dart b/lib/app/create/create_page_select_tokens_stage.dart
index 1ca78bf..5989e8e 100644
--- a/lib/app/create/create_page_select_tokens_stage.dart
+++ b/lib/app/create/create_page_select_tokens_stage.dart
@@ -127,27 +127,35 @@ class _CreatePageState extends State with DeviceInf
),
const Spacer(),
StatefulBuilder(builder: (context, localSetState) {
- return Badge(
- alignment: const Alignment(0.8, -0.8),
- smallSize: cache.getPoolSearchSettings().isDefault ? 0 : 6,
- child: ZupIconButton(
- key: const Key("pool-search-settings-button"),
- backgroundColor: Colors.transparent,
- icon: Assets.icons.gear.svg(height: 18),
- padding: const EdgeInsets.all(10),
- iconColor: ZupColors.brand,
- onPressed: (buttonContext) => CreatePageSettingsDropdown.show(
- buttonContext,
- onClose: () {
- if (mounted) WidgetsBinding.instance.addPostFrameCallback((_) => localSetState(() {}));
- },
+ return Row(
+ children: [
+ Badge(
+ alignment: const Alignment(1.05, -1.05),
+ smallSize: cache.getPoolSearchSettings().isDefault ? 0 : 6,
+ backgroundColor: ZupColors.orange,
+ child: ZupPillButton(
+ key: const Key("pool-search-settings-button"),
+ onPressed: (buttonContext) => CreatePageSettingsDropdown.show(
+ buttonContext,
+ onClose: () {
+ if (mounted) {
+ WidgetsBinding.instance.addPostFrameCallback((_) => localSetState(() {}));
+ }
+ },
+ ),
+ foregroundColor: ZupColors.gray,
+ backgroundColor: ZupColors.gray6,
+ title: "Search settings",
+ icon: Assets.icons.gear.svg(
+ height: 18, colorFilter: const ColorFilter.mode(ZupColors.white, BlendMode.srcIn)),
+ ),
),
- ),
+ ],
);
})
],
),
- const SizedBox(height: 5),
+ const SizedBox(height: 12),
TokenSelectorButton(
key: const Key("token-a-selector"),
controller: token0SelectorController,
diff --git a/lib/app/create/deposit/deposit_cubit.dart b/lib/app/create/deposit/deposit_cubit.dart
index b83ec28..0f30cd1 100644
--- a/lib/app/create/deposit/deposit_cubit.dart
+++ b/lib/app/create/deposit/deposit_cubit.dart
@@ -3,7 +3,6 @@ import 'dart:async';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:web3kit/web3kit.dart';
-import 'package:zup_app/abis/uniswap_v3_pool.abi.g.dart';
import 'package:zup_app/app/app_cubit/app_cubit.dart';
import 'package:zup_app/core/cache.dart';
import 'package:zup_app/core/dtos/deposit_settings_dto.dart';
@@ -13,6 +12,7 @@ import 'package:zup_app/core/dtos/yields_dto.dart';
import 'package:zup_app/core/enums/networks.dart';
import 'package:zup_app/core/mixins/keys_mixin.dart';
import 'package:zup_app/core/mixins/v3_pool_conversors_mixin.dart';
+import 'package:zup_app/core/pool_service.dart';
import 'package:zup_app/core/repositories/yield_repository.dart';
import 'package:zup_app/core/slippage.dart';
import 'package:zup_app/core/zup_analytics.dart';
@@ -26,16 +26,16 @@ class DepositCubit extends Cubit with KeysMixin, V3PoolConversorsM
this._yieldRepository,
this._zupSingletonCache,
this._wallet,
- this._uniswapV3Pool,
this._cache,
this._appCubit,
this._zupAnalytics,
+ this._poolService,
) : super(const DepositState.initial());
final YieldRepository _yieldRepository;
final ZupSingletonCache _zupSingletonCache;
final Wallet _wallet;
- final UniswapV3Pool _uniswapV3Pool;
+ final PoolService _poolService;
final Cache _cache;
final AppCubit _appCubit;
final ZupAnalytics _zupAnalytics;
@@ -81,14 +81,14 @@ class DepositCubit extends Cubit with KeysMixin, V3PoolConversorsM
? await _yieldRepository.getAllNetworksYield(
token0InternalId: token0AddressOrId,
token1InternalId: token1AddressOrId,
- minTvlUsd: ignoreMinLiquidity ? 0 : poolSearchSettings.minLiquidityUSD,
+ searchSettings: ignoreMinLiquidity ? poolSearchSettings.copyWith(minLiquidityUSD: 0) : poolSearchSettings,
testnetMode: _appCubit.isTestnetMode,
)
: await _yieldRepository.getSingleNetworkYield(
token0Address: token0AddressOrId,
token1Address: token1AddressOrId,
network: _appCubit.selectedNetwork,
- minTvlUsd: ignoreMinLiquidity ? 0 : poolSearchSettings.minLiquidityUSD,
+ searchSettings: ignoreMinLiquidity ? poolSearchSettings.copyWith(minLiquidityUSD: 0) : poolSearchSettings,
);
if (yields.isEmpty) {
@@ -118,17 +118,12 @@ class DepositCubit extends Cubit with KeysMixin, V3PoolConversorsM
_pooltickStreamController.add(null);
final selectedYieldBeforeCall = selectedYield;
+ BigInt tick = await _poolService.getPoolTick(selectedYieldBeforeCall!);
- final uniswapV3Pool = _uniswapV3Pool.fromRpcProvider(
- contractAddress: selectedYieldBeforeCall!.poolAddress,
- rpcUrl: selectedYieldBeforeCall.network.rpcUrl,
- );
-
- final slot0 = await uniswapV3Pool.slot0();
if (selectedYieldBeforeCall != selectedYield) return await getSelectedPoolTick();
- _pooltickStreamController.add(slot0.tick);
- _latestPoolTick = slot0.tick;
+ _pooltickStreamController.add(tick);
+ _latestPoolTick = tick;
}
Future getWalletTokenAmount(String tokenAddress, {required AppNetworks network}) async {
diff --git a/lib/app/create/deposit/deposit_page.dart b/lib/app/create/deposit/deposit_page.dart
index 87f7204..d7c933d 100644
--- a/lib/app/create/deposit/deposit_page.dart
+++ b/lib/app/create/deposit/deposit_page.dart
@@ -6,7 +6,6 @@ import 'package:intl/intl.dart';
import 'package:lottie/lottie.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:web3kit/web3kit.dart';
-import 'package:zup_app/abis/uniswap_v3_pool.abi.g.dart';
import 'package:zup_app/app/app_cubit/app_cubit.dart';
import 'package:zup_app/app/create/deposit/deposit_cubit.dart';
import 'package:zup_app/app/create/deposit/widgets/deposit_settings_dropdown_child.dart';
@@ -26,9 +25,10 @@ import 'package:zup_app/core/extensions/widget_extension.dart';
import 'package:zup_app/core/injections.dart';
import 'package:zup_app/core/mixins/v3_pool_conversors_mixin.dart';
import 'package:zup_app/core/mixins/v3_pool_liquidity_calculations_mixin.dart';
+import 'package:zup_app/core/pool_service.dart';
import 'package:zup_app/core/repositories/yield_repository.dart';
import 'package:zup_app/core/slippage.dart';
-import 'package:zup_app/core/v3_pool_constants.dart';
+import 'package:zup_app/core/v3_v4_pool_constants.dart';
import 'package:zup_app/core/zup_analytics.dart';
import 'package:zup_app/core/zup_navigator.dart';
import 'package:zup_app/gen/assets.gen.dart';
@@ -47,10 +47,10 @@ Route routeBuilder(BuildContext context, RouteSettings settings) {
inject(),
inject(),
inject(),
- inject(),
inject(),
inject(),
inject(),
+ inject(),
),
child: const DepositPage(),
),
@@ -87,31 +87,11 @@ class _DepositPageState extends State
String get token0Address => _navigator.getParam(ZupNavigatorPaths.deposit.routeParamsName?.param0 ?? "") ?? "";
String get token1Address => _navigator.getParam(ZupNavigatorPaths.deposit.routeParamsName?.param1 ?? "") ?? "";
TokenDto get baseToken {
- return areTokensReversed
- ? _cubit.selectedYield!.maybeNativeToken1(permitNative: depositWithNativeToken)
- : _cubit.selectedYield!.maybeNativeToken0(permitNative: depositWithNativeToken);
+ return areTokensReversed ? _cubit.selectedYield!.token1 : _cubit.selectedYield!.token0;
}
TokenDto get quoteToken {
- return areTokensReversed
- ? _cubit.selectedYield!.maybeNativeToken0(permitNative: depositWithNativeToken)
- : _cubit.selectedYield!.maybeNativeToken1(permitNative: depositWithNativeToken);
- }
-
- bool get isBaseTokenNative {
- final currentChain = _cubit.selectedYield!.network.chainId;
-
- return (_cubit.selectedYield!.network.wrappedNative.addresses[currentChain]!
- .lowercasedEquals(baseToken.addresses[currentChain]!) &&
- depositWithNativeToken);
- }
-
- bool get isQuoteTokenNative {
- final currentChain = _cubit.selectedYield!.network.chainId;
-
- return (_cubit.selectedYield!.network.wrappedNative.addresses[currentChain]!
- .lowercasedEquals(quoteToken.addresses[currentChain]!) &&
- depositWithNativeToken);
+ return areTokensReversed ? _cubit.selectedYield!.token0 : _cubit.selectedYield!.token1;
}
bool areTokensReversed = false;
@@ -121,8 +101,6 @@ class _DepositPageState extends State
double minPrice = 0;
double maxPrice = 0;
- late bool depositWithNativeToken = true;
-
late Slippage selectedSlippage = _cubit.depositSettings.slippage;
late Duration selectedDeadline = _cubit.depositSettings.deadline;
@@ -206,13 +184,13 @@ class _DepositPageState extends State
if (isOutOfRange.maxPrice) return baseTokenAmountController.clear();
final maxTickPrice = tickToPrice(
- tick: V3PoolConstants.maxTick,
+ tick: V3V4PoolConstants.maxTick,
poolToken0Decimals: _cubit.selectedYield!.token0.decimals,
poolToken1Decimals: _cubit.selectedYield!.token1.decimals,
);
final minTickPrice = tickToPrice(
- tick: V3PoolConstants.minTick,
+ tick: V3V4PoolConstants.minTick,
poolToken0Decimals: _cubit.selectedYield!.token0.decimals,
poolToken1Decimals: _cubit.selectedYield!.token1.decimals,
);
@@ -258,12 +236,12 @@ class _DepositPageState extends State
Future<({String title, Widget? icon, Function()? onPressed})> depositButtonState() async {
final userWalletBaseTokenAmount = await _cubit.getWalletTokenAmount(
- isBaseTokenNative ? EthereumConstants.zeroAddress : baseToken.addresses[_cubit.selectedYield!.network.chainId]!,
+ baseToken.addresses[_cubit.selectedYield!.network.chainId]!,
network: _cubit.selectedYield!.network,
);
final userWalletQuoteTokenAmount = await _cubit.getWalletTokenAmount(
- isQuoteTokenNative ? EthereumConstants.zeroAddress : quoteToken.addresses[_cubit.selectedYield!.network.chainId]!,
+ quoteToken.addresses[_cubit.selectedYield!.network.chainId]!,
network: _cubit.selectedYield!.network,
);
@@ -308,7 +286,6 @@ class _DepositPageState extends State
PreviewDepositModal(
key: const Key("preview-deposit-modal"),
yieldTimeFrame: _cubit.selectedYieldTimeframe!,
- depositWithNativeToken: depositWithNativeToken,
deadline: selectedDeadline,
maxSlippage: selectedSlippage,
currentYield: _cubit.selectedYield!,
@@ -697,7 +674,7 @@ class _DepositPageState extends State
child: IgnorePointer(
ignoring: true,
child: Text(
- "${_cubit.selectedYield?.maybeNativeToken0(permitNative: depositWithNativeToken).symbol} / ${_cubit.selectedYield?.maybeNativeToken1(permitNative: depositWithNativeToken).symbol}",
+ "${_cubit.selectedYield?.token0.symbol} / ${_cubit.selectedYield?.token1.symbol}",
style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w500),
),
),
@@ -708,7 +685,7 @@ class _DepositPageState extends State
child: IgnorePointer(
ignoring: true,
child: Text(
- "${_cubit.selectedYield?.maybeNativeToken1(permitNative: depositWithNativeToken).symbol} / ${_cubit.selectedYield?.maybeNativeToken0(permitNative: depositWithNativeToken).symbol}",
+ "${_cubit.selectedYield?.token1.symbol} / ${_cubit.selectedYield?.token0.symbol}",
style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w500),
)),
),
@@ -866,36 +843,14 @@ class _DepositPageState extends State
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
- if (baseToken.addresses[_cubit.selectedYield!.network.chainId]!.lowercasedEquals(_cubit
- .selectedYield!.network.wrappedNative.addresses[_cubit.selectedYield!.network.chainId]!) ||
- quoteToken.addresses[_cubit.selectedYield!.network.chainId]!.lowercasedEquals(
- _cubit.selectedYield!.network.wrappedNative.addresses[_cubit.selectedYield!.network.chainId]!,
- ))
- Row(
- children: [
- _sectionTitle(S.of(context).depositPageDepositSectionTitle),
- const Spacer(),
- Text(
- S.of(context).depositPageDepositWithNativeToken(
- tokenSymbol: _cubit.selectedYield?.network.chainInfo.nativeCurrency!.symbol ?? "",
- ),
- style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w500),
- ),
- const SizedBox(width: 10),
- ZupSwitch(
- key: const Key("deposit-with-native-token-switch"),
- value: depositWithNativeToken,
- onChanged: (value) => setState(() => depositWithNativeToken = value),
- ),
- ],
- ),
+ _sectionTitle(S.of(context).depositPageDepositSectionTitle),
const SizedBox(height: 12),
TokenAmountInputCard(
key: const Key("base-token-input-card"),
token: baseToken,
- isNative: depositWithNativeToken &&
- baseToken.addresses[_cubit.selectedYield!.network.chainId]!.lowercasedEquals(_cubit
- .selectedYield!.network.wrappedNative.addresses[_cubit.selectedYield!.network.chainId]!),
+ isNative: baseToken.addresses[_cubit.selectedYield!.network.chainId]!.lowercasedEquals(
+ EthereumConstants.zeroAddress,
+ ),
onRefreshBalance: () => setState(() {}),
disabledText: () {
if (!isBaseTokenNeeded) {
@@ -922,9 +877,9 @@ class _DepositPageState extends State
TokenAmountInputCard(
key: const Key("quote-token-input-card"),
token: quoteToken,
- isNative: depositWithNativeToken &&
- quoteToken.addresses[_cubit.selectedYield!.network.chainId]!.lowercasedEquals(_cubit
- .selectedYield!.network.wrappedNative.addresses[_cubit.selectedYield!.network.chainId]!),
+ isNative: quoteToken.addresses[_cubit.selectedYield!.network.chainId]!.lowercasedEquals(
+ EthereumConstants.zeroAddress,
+ ),
onRefreshBalance: () => setState(() {}),
disabledText: () {
if (!isQuoteTokenNeeded) {
@@ -948,39 +903,46 @@ class _DepositPageState extends State
network: _cubit.selectedYield!.network,
),
const SizedBox(height: 20),
- StreamBuilder(
- key: const Key("deposit-button"),
- stream: wallet.signerStream,
- initialData: wallet.signer,
- builder: (context, signerSnapshot) {
- if (!signerSnapshot.hasData) {
- return ZupPrimaryButton(
- width: double.maxFinite,
- title: S.of(context).connectWallet,
- icon: Assets.icons.walletBifold.svg(),
- fixedIcon: true,
- alignCenter: true,
- hoverElevation: 0,
- backgroundColor: ZupColors.brand7,
- foregroundColor: ZupColors.brand,
- onPressed: () => ConnectModal().show(context),
- );
- }
-
- return FutureBuilder(
- future: depositButtonState(),
- builder: (context, stateSnapshot) {
- return ZupPrimaryButton(
- alignCenter: true,
- title: stateSnapshot.data?.title ?? "Loading...",
- icon: stateSnapshot.data?.icon,
- isLoading: stateSnapshot.connectionState == ConnectionState.waiting,
- fixedIcon: true,
- onPressed: stateSnapshot.data?.onPressed,
- width: double.maxFinite,
+ Row(
+ children: [
+ Expanded(
+ child: StreamBuilder(
+ key: const Key("deposit-button"),
+ stream: wallet.signerStream,
+ initialData: wallet.signer,
+ builder: (context, signerSnapshot) {
+ if (!signerSnapshot.hasData) {
+ return ZupPrimaryButton(
+ width: double.maxFinite,
+ title: S.of(context).connectWallet,
+ icon: Assets.icons.walletBifold.svg(),
+ fixedIcon: true,
+ alignCenter: true,
+ hoverElevation: 0,
+ backgroundColor: ZupColors.brand7,
+ foregroundColor: ZupColors.brand,
+ onPressed: () => ConnectModal().show(context),
+ );
+ }
+
+ return FutureBuilder(
+ future: depositButtonState(),
+ builder: (context, stateSnapshot) {
+ return ZupPrimaryButton(
+ alignCenter: true,
+ title: stateSnapshot.data?.title ?? "Loading...",
+ icon: stateSnapshot.data?.icon,
+ isLoading: stateSnapshot.connectionState == ConnectionState.waiting,
+ fixedIcon: true,
+ onPressed: stateSnapshot.data?.onPressed,
+ width: double.maxFinite,
+ );
+ },
);
- });
- },
+ },
+ ),
+ ),
+ ],
),
],
);
diff --git a/lib/app/create/deposit/widgets/deposit_success_modal.dart b/lib/app/create/deposit/widgets/deposit_success_modal.dart
index 66823dd..7cb5396 100644
--- a/lib/app/create/deposit/widgets/deposit_success_modal.dart
+++ b/lib/app/create/deposit/widgets/deposit_success_modal.dart
@@ -10,20 +10,18 @@ import 'package:zup_app/widgets/zup_cached_image.dart';
import 'package:zup_ui_kit/zup_ui_kit.dart';
class DepositSuccessModal extends StatefulWidget {
- const DepositSuccessModal({super.key, required this.depositedYield, required this.depositedWithNative});
+ const DepositSuccessModal({super.key, required this.depositedYield});
final YieldDto depositedYield;
- final bool depositedWithNative;
static Future show(
BuildContext context, {
required YieldDto depositedYield,
required showAsBottomSheet,
- required bool depositedWithNative,
}) async =>
ZupModal.show(
context,
- content: DepositSuccessModal(depositedYield: depositedYield, depositedWithNative: depositedWithNative),
+ content: DepositSuccessModal(depositedYield: depositedYield),
padding: const EdgeInsets.all(20),
showAsBottomSheet: showAsBottomSheet,
dismissible: true,
@@ -61,11 +59,11 @@ class _DepositSuccessModalState extends State {
children: [
ZupMergedWidgets(
firstWidget: TokenAvatar(
- asset: widget.depositedYield.maybeNativeToken0(permitNative: widget.depositedWithNative),
+ asset: widget.depositedYield.token0,
size: 70,
),
secondWidget: TokenAvatar(
- asset: widget.depositedYield.maybeNativeToken1(permitNative: widget.depositedWithNative),
+ asset: widget.depositedYield.token1,
size: 70,
),
spacing: 0,
@@ -103,8 +101,7 @@ class _DepositSuccessModalState extends State {
text: TextSpan(style: const TextStyle(color: ZupColors.gray, fontSize: 14), children: [
TextSpan(text: "${S.of(context).depositSuccessModalDescriptionPart1} "),
TextSpan(
- text:
- "${widget.depositedYield.maybeNativeToken0(permitNative: widget.depositedWithNative).symbol}/${widget.depositedYield.maybeNativeToken1(permitNative: widget.depositedWithNative).symbol}",
+ text: "${widget.depositedYield.token0.symbol}/${widget.depositedYield.token1.symbol}",
style: const TextStyle(
fontWeight: FontWeight.w700,
color: ZupColors.black,
diff --git a/lib/app/create/deposit/widgets/preview_deposit_modal/preview_deposit_modal.dart b/lib/app/create/deposit/widgets/preview_deposit_modal/preview_deposit_modal.dart
index f7855a8..fca2d4b 100644
--- a/lib/app/create/deposit/widgets/preview_deposit_modal/preview_deposit_modal.dart
+++ b/lib/app/create/deposit/widgets/preview_deposit_modal/preview_deposit_modal.dart
@@ -5,8 +5,8 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:web3kit/web3kit.dart';
import 'package:zup_app/abis/erc_20.abi.g.dart';
-import 'package:zup_app/abis/uniswap_position_manager.abi.g.dart';
-import 'package:zup_app/abis/uniswap_v3_pool.abi.g.dart';
+import 'package:zup_app/abis/uniswap_permit2.abi.g.dart';
+import 'package:zup_app/abis/uniswap_v3_position_manager.abi.g.dart';
import 'package:zup_app/app/create/deposit/widgets/deposit_success_modal.dart';
import 'package:zup_app/app/create/deposit/widgets/preview_deposit_modal/preview_deposit_modal_cubit.dart';
import 'package:zup_app/core/dtos/token_dto.dart';
@@ -14,9 +14,11 @@ import 'package:zup_app/core/dtos/yield_dto.dart';
import 'package:zup_app/core/extensions/num_extension.dart';
import 'package:zup_app/core/injections.dart';
import 'package:zup_app/core/mixins/v3_pool_conversors_mixin.dart';
+import 'package:zup_app/core/pool_service.dart';
import 'package:zup_app/core/slippage.dart';
-import 'package:zup_app/core/v3_pool_constants.dart';
+import 'package:zup_app/core/v3_v4_pool_constants.dart';
import 'package:zup_app/core/zup_analytics.dart';
+import 'package:zup_app/core/zup_links.dart';
import 'package:zup_app/core/zup_navigator.dart';
import 'package:zup_app/gen/assets.gen.dart';
import 'package:zup_app/l10n/gen/app_localizations.dart';
@@ -36,7 +38,6 @@ class PreviewDepositModal extends StatefulWidget with DeviceInfoMixin {
required this.token1DepositAmount,
required this.deadline,
required this.maxSlippage,
- required this.depositWithNativeToken,
required this.yieldTimeFrame,
});
@@ -49,7 +50,6 @@ class PreviewDepositModal extends StatefulWidget with DeviceInfoMixin {
final double token1DepositAmount;
final Duration deadline;
final Slippage maxSlippage;
- final bool depositWithNativeToken;
final double paddingSize = 20;
@@ -64,17 +64,16 @@ class PreviewDepositModal extends StatefulWidget with DeviceInfoMixin {
create: (context) => PreviewDepositModalCubit(
zupAnalytics: inject(),
currentYield: currentYield,
- uniswapPositionManager: inject(),
+ uniswapPositionManager: inject(),
erc20: inject(),
wallet: inject(),
- uniswapV3Pool: inject(),
+ poolService: inject(),
+ permit2: inject(),
initialPoolTick: currentPoolTick,
- depositWithNative: depositWithNativeToken,
navigatorKey: inject>(),
),
child: PreviewDepositModal(
deadline: deadline,
- depositWithNativeToken: depositWithNativeToken,
maxSlippage: maxSlippage,
token0DepositAmount: token0DepositAmount,
token1DepositAmount: token1DepositAmount,
@@ -95,6 +94,7 @@ class PreviewDepositModal extends StatefulWidget with DeviceInfoMixin {
class _PreviewDepositModalState extends State with V3PoolConversorsMixin, DeviceInfoMixin {
final zupCachedImage = inject();
final navigator = inject();
+ final zupLinks = inject();
final ScrollController appScrollController = inject(
instanceName: InjectInstanceNames.appScrollController,
@@ -102,18 +102,18 @@ class _PreviewDepositModalState extends State with V3PoolCo
TokenDto get baseToken {
if (isReversedLocal) {
- return widget.currentYield.maybeNativeToken1(permitNative: widget.depositWithNativeToken);
+ return widget.currentYield.token1;
}
- return widget.currentYield.maybeNativeToken0(permitNative: widget.depositWithNativeToken);
+ return widget.currentYield.token0;
}
TokenDto get quoteToken {
if (isReversedLocal) {
- return widget.currentYield.maybeNativeToken0(permitNative: widget.depositWithNativeToken);
+ return widget.currentYield.token0;
}
- return widget.currentYield.maybeNativeToken1(permitNative: widget.depositWithNativeToken);
+ return widget.currentYield.token1;
}
double get baseTokenAmount => isReversedLocal ? widget.token1DepositAmount : widget.token0DepositAmount;
@@ -138,7 +138,7 @@ class _PreviewDepositModalState extends State with V3PoolCo
double get minPrice {
BigInt tick() {
- if (widget.isReversed != isReversedLocal && widget.maxPrice.isInfinity) return V3PoolConstants.minTick;
+ if (widget.isReversed != isReversedLocal && widget.maxPrice.isInfinity) return V3V4PoolConstants.minTick;
return priceToTick(
price: (widget.isReversed == !isReversedLocal) ? widget.maxPrice.price : widget.minPrice.price,
@@ -159,7 +159,7 @@ class _PreviewDepositModalState extends State with V3PoolCo
double get maxPrice {
BigInt tick() {
- if (widget.isReversed != isReversedLocal && widget.minPrice.isInfinity) return V3PoolConstants.minTick;
+ if (widget.isReversed != isReversedLocal && widget.minPrice.isInfinity) return V3V4PoolConstants.minTick;
return priceToTick(
price: (widget.isReversed == !isReversedLocal) ? widget.minPrice.price : widget.maxPrice.price,
@@ -231,7 +231,7 @@ class _PreviewDepositModalState extends State with V3PoolCo
isLoading: true,
),
initial: (token0Allowance, token1Allowance) {
- if (!(widget.depositWithNativeToken && widget.currentYield.isToken0WrappedNative)) {
+ if (!widget.currentYield.isToken0Native) {
if (token0Allowance < token0DepositAmount) {
return (
title: S.of(context).previewDepositModalApproveToken(tokenSymbol: widget.currentYield.token0.symbol),
@@ -242,7 +242,7 @@ class _PreviewDepositModalState extends State with V3PoolCo
}
}
- if (!(widget.depositWithNativeToken && widget.currentYield.isToken1WrappedNative)) {
+ if (!widget.currentYield.isToken1Native) {
if (token1Allowance < token1DepositAmount) {
return (
title: S.of(context).previewDepositModalApproveToken(tokenSymbol: widget.currentYield.token1.symbol),
@@ -363,7 +363,6 @@ class _PreviewDepositModalState extends State with V3PoolCo
context,
depositedYield: widget.currentYield,
showAsBottomSheet: isMobileSize(context),
- depositedWithNative: widget.depositWithNativeToken,
);
},
slippageCheckError: () {
@@ -385,9 +384,7 @@ class _PreviewDepositModalState extends State with V3PoolCo
context,
helperButton: (
title: S.of(context).previewDepositModalTransactionErrorSnackBarHelperButtonTitle,
- onButtonTap: () {
- // TODO: Add contact us info
- }
+ onButtonTap: () => zupLinks.launchZupContactUs()
),
message: S.of(context).previewDepositModalTransactionErrorSnackBarMessage,
type: ZupSnackBarType.error,
@@ -447,7 +444,7 @@ class _PreviewDepositModalState extends State with V3PoolCo
child: SizedBox(
height: 15,
child: Text(
- "${widget.currentYield.maybeNativeToken0(permitNative: widget.depositWithNativeToken).symbol} / ${widget.currentYield.maybeNativeToken1(permitNative: widget.depositWithNativeToken).symbol}",
+ "${widget.currentYield.token0.symbol} / ${widget.currentYield.token1.symbol}",
style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w600),
),
),
@@ -460,7 +457,7 @@ class _PreviewDepositModalState extends State with V3PoolCo
child: SizedBox(
height: 16,
child: Text(
- "${widget.currentYield.maybeNativeToken1(permitNative: widget.depositWithNativeToken).symbol} / ${widget.currentYield.maybeNativeToken0(permitNative: widget.depositWithNativeToken).symbol}",
+ "${widget.currentYield.token1.symbol} / ${widget.currentYield.token0.symbol}",
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
diff --git a/lib/app/create/deposit/widgets/preview_deposit_modal/preview_deposit_modal_cubit.dart b/lib/app/create/deposit/widgets/preview_deposit_modal/preview_deposit_modal_cubit.dart
index 42b308e..24bca8c 100644
--- a/lib/app/create/deposit/widgets/preview_deposit_modal/preview_deposit_modal_cubit.dart
+++ b/lib/app/create/deposit/widgets/preview_deposit_modal/preview_deposit_modal_cubit.dart
@@ -1,6 +1,5 @@
import 'dart:async';
-import 'package:clock/clock.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
@@ -8,14 +7,15 @@ import 'package:web3kit/core/dtos/transaction_response.dart';
import 'package:web3kit/core/exceptions/ethers_exceptions.dart';
import 'package:web3kit/web3kit.dart';
import 'package:zup_app/abis/erc_20.abi.g.dart';
-import 'package:zup_app/abis/uniswap_position_manager.abi.g.dart';
-import 'package:zup_app/abis/uniswap_v3_pool.abi.g.dart';
+import 'package:zup_app/abis/uniswap_permit2.abi.g.dart';
+import 'package:zup_app/abis/uniswap_v3_position_manager.abi.g.dart';
import 'package:zup_app/app/create/deposit/widgets/deposit_success_modal.dart';
import 'package:zup_app/core/dtos/token_dto.dart';
import 'package:zup_app/core/dtos/yield_dto.dart';
import 'package:zup_app/core/mixins/v3_pool_conversors_mixin.dart';
+import 'package:zup_app/core/pool_service.dart';
import 'package:zup_app/core/slippage.dart';
-import 'package:zup_app/core/v3_pool_constants.dart';
+import 'package:zup_app/core/v3_v4_pool_constants.dart';
import 'package:zup_app/core/zup_analytics.dart';
import 'package:zup_app/l10n/gen/app_localizations.dart';
import 'package:zup_core/mixins/device_info_mixin.dart';
@@ -27,33 +27,32 @@ part "preview_deposit_modal_state.dart";
class PreviewDepositModalCubit extends Cubit with V3PoolConversorsMixin, DeviceInfoMixin {
PreviewDepositModalCubit({
required BigInt initialPoolTick,
- required UniswapV3Pool uniswapV3Pool,
+ required PoolService poolService,
required YieldDto currentYield,
required Erc20 erc20,
required Wallet wallet,
- required UniswapPositionManager uniswapPositionManager,
+ required UniswapV3PositionManager uniswapPositionManager,
+ required UniswapPermit2 permit2,
required GlobalKey navigatorKey,
- required bool depositWithNative,
required ZupAnalytics zupAnalytics,
}) : _yield = currentYield,
- _uniswapV3Pool = uniswapV3Pool,
+ _poolRepository = poolService,
_erc20 = erc20,
_wallet = wallet,
- _uniswapPositionManager = uniswapPositionManager,
_latestPoolTick = initialPoolTick,
_navigatorKey = navigatorKey,
- _depositWithNative = depositWithNative,
_zupAnalytics = zupAnalytics,
+ _permit2 = permit2,
super(const PreviewDepositModalState.loading());
- final UniswapV3Pool _uniswapV3Pool;
+ final PoolService _poolRepository;
final Erc20 _erc20;
final YieldDto _yield;
final Wallet _wallet;
- final UniswapPositionManager _uniswapPositionManager;
final GlobalKey _navigatorKey;
- final bool _depositWithNative;
+
final ZupAnalytics _zupAnalytics;
+ final UniswapPermit2 _permit2;
final StreamController _poolTickStreamController = StreamController.broadcast();
@@ -87,6 +86,7 @@ class PreviewDepositModalCubit extends Cubit with V3Po
Future approveToken(TokenDto token, BigInt value) async {
try {
+ final spender = _yield.poolType.isV4 ? _yield.permit2! : _yield.positionManagerAddress;
final tokenAddressInNetwork = token.addresses[_yield.network.chainId]!;
emit(PreviewDepositModalState.approvingToken(token.symbol));
@@ -96,7 +96,10 @@ class PreviewDepositModalCubit extends Cubit with V3Po
contractAddress: tokenAddressInNetwork,
signer: _wallet.signer!,
);
- final tx = await contract.approve(spender: _yield.positionManagerAddress, value: value);
+
+ final tx = await contract.approve(spender: spender, value: value);
+
+ if (_yield.poolType.isV4) checkOrApprovePermit2ForV4Pool(value, token);
emit(PreviewDepositModalState.waitingTransaction(txId: tx.hash, type: WaitingTransactionType.approve));
@@ -123,6 +126,33 @@ class PreviewDepositModalCubit extends Cubit with V3Po
}
}
+ Future checkOrApprovePermit2ForV4Pool(BigInt approveValue, TokenDto token) async {
+ final tokenAddressInNetwork = token.addresses[_yield.network.chainId]!;
+
+ final permit2Contract = _permit2.fromSigner(
+ contractAddress: _yield.permit2!,
+ signer: _wallet.signer!,
+ );
+
+ final permit2CurrentAllowance = await permit2Contract.allowance(
+ await _wallet.signer!.address,
+ tokenAddressInNetwork,
+ _yield.positionManagerAddress,
+ );
+
+ if (permit2CurrentAllowance.amount <= approveValue ||
+ permit2CurrentAllowance.expiration < BigInt.from(DateTime.now().millisecondsSinceEpoch / 1000)) {
+ final tx = await permit2Contract.approve(
+ token: tokenAddressInNetwork,
+ spender: _yield.positionManagerAddress,
+ amount: EthereumConstants.uint160Max,
+ expiration: EthereumConstants.uint48Max,
+ );
+
+ await tx.waitConfirmation();
+ }
+ }
+
Future deposit({
required BigInt token0Amount,
required BigInt token1Amount,
@@ -138,15 +168,10 @@ class PreviewDepositModalCubit extends Cubit with V3Po
emit(const PreviewDepositModalState.depositing());
await _maybeSwitchNetwork();
- final positionManagerContract = _uniswapPositionManager.fromSigner(
- contractAddress: _yield.positionManagerAddress,
- signer: _wallet.signer!,
- );
-
BigInt tickLower() {
BigInt convertPriceToTickLower() {
- if (isMinPriceInfinity && !isReversed) return V3PoolConstants.minTick;
- if (isReversed && isMaxPriceInfinity) return V3PoolConstants.minTick;
+ if (isMinPriceInfinity && !isReversed) return V3V4PoolConstants.minTick;
+ if (isReversed && isMaxPriceInfinity) return V3V4PoolConstants.minTick;
return priceToTick(
price: isReversed ? maxPrice : minPrice,
@@ -164,8 +189,8 @@ class PreviewDepositModalCubit extends Cubit with V3Po
BigInt tickUpper() {
BigInt convertPriceToTickUpper() {
- if (isMaxPriceInfinity && !isReversed) return V3PoolConstants.maxTick;
- if (isReversed && isMinPriceInfinity) return V3PoolConstants.maxTick;
+ if (isMaxPriceInfinity && !isReversed) return V3V4PoolConstants.maxTick;
+ if (isReversed && isMinPriceInfinity) return V3V4PoolConstants.maxTick;
return priceToTick(
price: isReversed ? minPrice : maxPrice,
@@ -183,60 +208,41 @@ class PreviewDepositModalCubit extends Cubit with V3Po
final amount0Desired = token0Amount;
final amount1Desired = token1Amount;
- final amount0Min = slippage.calculateTokenAmountFromSlippage(amount0Desired);
- final amount1Min = slippage.calculateTokenAmountFromSlippage(amount1Desired);
+ final amount0Min = slippage.calculateMinTokenAmountFromSlippage(amount0Desired);
+ final amount1Min = slippage.calculateMinTokenAmountFromSlippage(amount1Desired);
final recipient = await _wallet.signer!.address;
final TransactionResponse tx = await () async {
- if (_depositWithNative) {
- final mintCalldata = _uniswapPositionManager.getMintCalldata(
- params: (
- amount0Desired: amount0Desired,
- amount1Desired: amount1Desired,
- deadline: BigInt.from(clock.now().add(deadline).millisecondsSinceEpoch),
- amount0Min: amount0Min,
- amount1Min: amount1Min,
- recipient: recipient,
- tickLower: tickLower(),
- tickUpper: tickUpper(),
- fee: BigInt.from(_yield.feeTier),
- token0: _yield.token0.addresses[_yield.network.chainId]!,
- token1: _yield.token1.addresses[_yield.network.chainId]!,
- ),
- );
-
- return await positionManagerContract.multicall(
- data: [
- mintCalldata,
- if (_depositWithNative) _uniswapPositionManager.getRefundETHCalldata(),
- ],
- ethValue: () {
- if (_depositWithNative && _yield.isToken0WrappedNative) {
- return amount0Desired;
- }
-
- if (_depositWithNative && _yield.isToken1WrappedNative) {
- return amount1Desired;
- }
-
- return BigInt.zero;
- }.call());
- }
-
- return await positionManagerContract.mint(
- params: (
+ if (_yield.poolType.isV3) {
+ return await _poolRepository.sendV3PoolDepositTransaction(
+ _yield,
+ _wallet.signer!,
amount0Desired: amount0Desired,
amount1Desired: amount1Desired,
- deadline: BigInt.from(clock.now().add(deadline).millisecondsSinceEpoch),
+ deadline: deadline,
amount0Min: amount0Min,
amount1Min: amount1Min,
recipient: recipient,
tickLower: tickLower(),
tickUpper: tickUpper(),
- fee: BigInt.from(_yield.feeTier),
- token0: _yield.token0.addresses[_yield.network.chainId]!,
- token1: _yield.token1.addresses[_yield.network.chainId]!,
- ),
+ );
+ }
+
+ await checkOrApprovePermit2ForV4Pool(amount0Desired, _yield.token0);
+ await checkOrApprovePermit2ForV4Pool(amount1Desired, _yield.token1);
+
+ return await _poolRepository.sendV4PoolDepositTransaction(
+ _yield,
+ _wallet.signer!,
+ deadline: deadline,
+ tickLower: tickLower(),
+ tickUpper: tickUpper(),
+ amount0toDeposit: amount0Desired,
+ amount1ToDeposit: amount1Desired,
+ maxAmount0ToDeposit: slippage.calculateMaxTokenAmountFromSlippage(amount0Desired),
+ maxAmount1ToDeposit: slippage.calculateMaxTokenAmountFromSlippage(amount1Desired),
+ recipient: recipient,
+ currentPoolTick: _latestPoolTick,
);
}.call();
@@ -285,43 +291,42 @@ class PreviewDepositModalCubit extends Cubit with V3Po
Future _getTokensAllowance({bool canThrow = false}) async {
try {
- final token0contract = _erc20.fromRpcProvider(
- contractAddress: _yield.token0.addresses[_yield.network.chainId]!,
- rpcUrl: _yield.network.rpcUrl,
- );
+ final spender = _yield.poolType.isV4 ? _yield.permit2! : _yield.positionManagerAddress;
+ final owner = await _wallet.signer!.address;
- final token1contract = _erc20.fromRpcProvider(
- contractAddress: _yield.token1.addresses[_yield.network.chainId]!,
- rpcUrl: _yield.network.rpcUrl,
- );
+ if (!_yield.isToken0Native) {
+ final token0contract = _erc20.fromRpcProvider(
+ contractAddress: _yield.token0.addresses[_yield.network.chainId]!,
+ rpcUrl: _yield.network.rpcUrl,
+ );
- final token0Allowance = await token0contract.allowance(
- owner: await _wallet.signer!.address,
- spender: _yield.positionManagerAddress,
- );
+ _token0Allowance = await token0contract.allowance(
+ owner: owner,
+ spender: spender,
+ );
+ }
- final token1Allowance = await token1contract.allowance(
- owner: await _wallet.signer!.address,
- spender: _yield.positionManagerAddress,
- );
+ if (!_yield.isToken1Native) {
+ final token1contract = _erc20.fromRpcProvider(
+ contractAddress: _yield.token1.addresses[_yield.network.chainId]!,
+ rpcUrl: _yield.network.rpcUrl,
+ );
- _token0Allowance = token0Allowance;
- _token1Allowance = token1Allowance;
+ _token1Allowance = await token1contract.allowance(
+ owner: owner,
+ spender: spender,
+ );
+ }
} catch (e) {
if (canThrow) rethrow;
}
}
void _updateTick() {
- final uniswapV3PoolContract = _uniswapV3Pool.fromRpcProvider(
- contractAddress: _yield.poolAddress,
- rpcUrl: _yield.network.rpcUrl,
- );
-
try {
- uniswapV3PoolContract.slot0().then((slot0) {
- _poolTickStreamController.add(slot0.tick);
- _latestPoolTick = slot0.tick;
+ _poolRepository.getPoolTick(_yield).then((tick) {
+ _latestPoolTick = tick;
+ _poolTickStreamController.add(tick);
});
} catch (_) {
// DO NOTHING
@@ -338,8 +343,8 @@ class PreviewDepositModalCubit extends Cubit with V3Po
WaitingTransactionType.deposit => ZupSnackBar(
context,
message: "${S.of(context).previewDepositModalCubitDepositingSnackBarMessage(
- token0Symbol: _yield.maybeNativeToken0(permitNative: _depositWithNative).symbol,
- token1Symbol: _yield.maybeNativeToken1(permitNative: _depositWithNative).symbol,
+ token0Symbol: _yield.token0.symbol,
+ token1Symbol: _yield.token1.symbol,
)} ",
customIcon: const ZupCircularLoadingIndicator(size: 20),
type: ZupSnackBarType.info,
@@ -391,7 +396,6 @@ class PreviewDepositModalCubit extends Cubit with V3Po
context,
depositedYield: _yield,
showAsBottomSheet: isMobileSize(context),
- depositedWithNative: _depositWithNative,
);
},
orElse: () async {
diff --git a/lib/app/create/deposit/widgets/token_amount_input_card/token_amount_input_card.dart b/lib/app/create/deposit/widgets/token_amount_input_card/token_amount_input_card.dart
index b1dfd40..15054e4 100644
--- a/lib/app/create/deposit/widgets/token_amount_input_card/token_amount_input_card.dart
+++ b/lib/app/create/deposit/widgets/token_amount_input_card/token_amount_input_card.dart
@@ -1,12 +1,14 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:web3kit/web3kit.dart';
+import 'package:zup_app/app/create/deposit/widgets/token_amount_input_card/token_amount_input_card_cubit.dart';
import 'package:zup_app/app/create/deposit/widgets/token_amount_input_card/token_amount_input_card_user_balance_cubit.dart';
import 'package:zup_app/core/dtos/token_dto.dart';
import 'package:zup_app/core/enums/networks.dart';
import 'package:zup_app/core/extensions/num_extension.dart';
import 'package:zup_app/core/extensions/widget_extension.dart';
import 'package:zup_app/core/injections.dart';
+import 'package:zup_app/core/repositories/tokens_repository.dart';
import 'package:zup_app/core/token_amount_input_formatter.dart';
import 'package:zup_app/gen/assets.gen.dart';
import 'package:zup_app/widgets/position_token.dart';
@@ -42,6 +44,10 @@ class _TokenAmountInputCardState extends State with Single
Wallet get wallet => inject();
ZupSingletonCache get zupSingletonCache => inject();
+ final TokensRepository _tokensRepository = inject();
+ final ZupHolder _zupHolder = inject();
+
+ TokenAmountInputCardCubit? cubit;
late TokenAmountCardUserBalanceCubit userBalanceCubit = TokenAmountCardUserBalanceCubit(
wallet,
@@ -55,19 +61,22 @@ class _TokenAmountInputCardState extends State with Single
@override
void initState() {
+ cubit = TokenAmountInputCardCubit(_tokensRepository, zupSingletonCache, _zupHolder);
refreshBalanceAnimationController = AnimationController(vsync: this, duration: const Duration(milliseconds: 100));
WidgetsBinding.instance.addPostFrameCallback((tester) {
- if (wallet.signer != null) userBalanceCubit.getUserTokenAmount(isNative: widget.isNative);
+ userBalanceCubit.updateNativeTokenAndFetch(isNative: widget.isNative);
});
+
super.initState();
}
@override
- void didUpdateWidget(covariant TokenAmountInputCard oldWidget) {
- if (widget.isNative != oldWidget.isNative) {
+ void didUpdateWidget(TokenAmountInputCard oldWidget) {
+ if (widget.isNative != oldWidget.isNative &&
+ (widget.token.addresses[widget.network.chainId] ?? "").lowercasedEquals(EthereumConstants.zeroAddress)) {
WidgetsBinding.instance
- .addPostFrameCallback((_) => userBalanceCubit.getUserTokenAmount(isNative: widget.isNative));
+ .addPostFrameCallback((_) => userBalanceCubit.updateNativeTokenAndFetch(isNative: widget.isNative));
return super.didUpdateWidget(oldWidget);
}
@@ -199,12 +208,31 @@ class _TokenAmountInputCardState extends State with Single
Row(
children: [
Padding(
- padding: EdgeInsets.only(left: paddingValue),
- child: const Text(
- r"$-",
- style: TextStyle(fontSize: 15, fontWeight: FontWeight.w500, color: ZupColors.gray),
- ),
- ),
+ padding: EdgeInsets.only(left: paddingValue),
+ child: StreamBuilder(
+ stream: (() async* {
+ yield await cubit!.getTokenPrice(token: widget.token, network: widget.network);
+
+ await for (final _ in Stream.periodic(const Duration(seconds: 30))) {
+ yield await cubit!.getTokenPrice(token: widget.token, network: widget.network);
+ }
+ })(),
+ builder: (context, snapshot) {
+ if (!snapshot.hasData) return const ZupCircularLoadingIndicator(size: 10);
+
+ return Text(
+ widget.controller.value.text.isEmpty
+ ? "\$-"
+ : ((double.tryParse(widget.controller.value.text) ?? 0) * snapshot.data!)
+ .formatCurrency(),
+ style: const TextStyle(
+ fontSize: 15,
+ fontWeight: FontWeight.w500,
+ color: ZupColors.gray,
+ ),
+ );
+ },
+ )),
const Spacer(),
BlocProvider.value(
value: userBalanceCubit,
diff --git a/lib/app/create/deposit/widgets/token_amount_input_card/token_amount_input_card_cubit.dart b/lib/app/create/deposit/widgets/token_amount_input_card/token_amount_input_card_cubit.dart
new file mode 100644
index 0000000..d97b867
--- /dev/null
+++ b/lib/app/create/deposit/widgets/token_amount_input_card/token_amount_input_card_cubit.dart
@@ -0,0 +1,33 @@
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:freezed_annotation/freezed_annotation.dart';
+import 'package:zup_app/core/dtos/token_dto.dart';
+import 'package:zup_app/core/enums/networks.dart';
+import 'package:zup_app/core/mixins/keys_mixin.dart';
+import 'package:zup_app/core/repositories/tokens_repository.dart';
+import 'package:zup_core/zup_core.dart';
+
+part 'token_amount_input_card_cubit.freezed.dart';
+part 'token_amount_input_card_state.dart';
+
+class TokenAmountInputCardCubit extends Cubit with KeysMixin {
+ TokenAmountInputCardCubit(this._tokensRepository, this._zupSingletonCache, this._zupHolder)
+ : super(const TokenAmountInputCardState.initial());
+
+ final TokensRepository _tokensRepository;
+ final ZupSingletonCache _zupSingletonCache;
+ final ZupHolder _zupHolder;
+
+ Future getTokenPrice({required TokenDto token, required AppNetworks network}) async {
+ try {
+ final tokenAddress = token.addresses[network.chainId]!;
+
+ return await _zupHolder.hold(() async => await _zupSingletonCache.run(
+ () async => (await _tokensRepository.getTokenPrice(tokenAddress, network)).usdPrice,
+ expiration: const Duration(minutes: 1),
+ key: tokenPriceCacheKey(tokenAddress: tokenAddress, network: network),
+ ));
+ } catch (_) {
+ return 0;
+ }
+ }
+}
diff --git a/lib/app/create/deposit/widgets/token_amount_input_card/token_amount_input_card_state.dart b/lib/app/create/deposit/widgets/token_amount_input_card/token_amount_input_card_state.dart
new file mode 100644
index 0000000..6207e96
--- /dev/null
+++ b/lib/app/create/deposit/widgets/token_amount_input_card/token_amount_input_card_state.dart
@@ -0,0 +1,6 @@
+part of 'token_amount_input_card_cubit.dart';
+
+@freezed
+class TokenAmountInputCardState with _$TokenAmountInputCardState {
+ const factory TokenAmountInputCardState.initial() = _Initial;
+}
diff --git a/lib/app/create/deposit/widgets/token_amount_input_card/token_amount_input_card_user_balance_cubit.dart b/lib/app/create/deposit/widgets/token_amount_input_card/token_amount_input_card_user_balance_cubit.dart
index 7bc347b..e982f11 100644
--- a/lib/app/create/deposit/widgets/token_amount_input_card/token_amount_input_card_user_balance_cubit.dart
+++ b/lib/app/create/deposit/widgets/token_amount_input_card/token_amount_input_card_user_balance_cubit.dart
@@ -25,6 +25,7 @@ class TokenAmountCardUserBalanceCubit extends Cubit? _signerStreamSubscription;
double userBalance = 0;
@@ -32,17 +33,25 @@ class TokenAmountCardUserBalanceCubit extends Cubit updateTokenAndNetwork(String tokenAddress, AppNetworks network, {required bool asNativeToken}) async {
_tokenAddress = tokenAddress;
_network = network;
+ _isNative = asNativeToken;
if (_wallet.signer != null) await getUserTokenAmount(isNative: asNativeToken);
}
+ Future updateNativeTokenAndFetch({required bool isNative}) async {
+ _isNative = isNative;
+ if (isNative) _tokenAddress = EthereumConstants.zeroAddress;
+
+ if (_wallet.signer != null) await getUserTokenAmount(isNative: _isNative);
+ }
+
Future getUserTokenAmount({bool ignoreCache = false, bool isNative = false}) async {
try {
emit(const TokenAmountCardUserBalanceState.loadingUserBalance());
diff --git a/lib/app/create/widgets/create_page_settings_dropdown.dart b/lib/app/create/widgets/create_page_settings_dropdown.dart
index d919b21..8692fc7 100644
--- a/lib/app/create/widgets/create_page_settings_dropdown.dart
+++ b/lib/app/create/widgets/create_page_settings_dropdown.dart
@@ -162,6 +162,55 @@ class _CreatePageSettingsDropdownState extends State
)
: const SizedBox.shrink(),
),
+ const SizedBox(height: 10),
+ Row(
+ children: [
+ Text(
+ S.of(context).createPageSettingsDropdownAllowedPoolTypes,
+ style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 15),
+ ),
+ const SizedBox(width: 8),
+ ZupTooltip(
+ key: const Key("pool-types-allowed-tooltip"),
+ message: S.of(context).createPageSettingsDropdownAllowedPoolTypesDescription,
+ child: Assets.icons.infoCircle.svg(
+ colorFilter: const ColorFilter.mode(ZupColors.gray, BlendMode.srcIn),
+ ),
+ ),
+ ],
+ ),
+ const SizedBox(height: 5),
+ Row(
+ children: [
+ const Text("V4", style: TextStyle(fontWeight: FontWeight.w500, fontSize: 15)),
+ const SizedBox(width: 5),
+ ZupSwitch(
+ key: const Key("pool-types-allowed-v4-switch"),
+ value: cache.getPoolSearchSettings().allowV4Search,
+ onChanged: (value) async {
+ await cache.savePoolSearchSettings(
+ settings: cache.getPoolSearchSettings().copyWith(allowV4Search: value),
+ );
+
+ setState(() {});
+ },
+ ),
+ const SizedBox(width: 12),
+ const Text("V3", style: TextStyle(fontWeight: FontWeight.w500)),
+ const SizedBox(width: 5),
+ ZupSwitch(
+ key: const Key("pool-types-allowed-v3-switch"),
+ value: cache.getPoolSearchSettings().allowV3Search,
+ onChanged: (value) async {
+ await cache.savePoolSearchSettings(
+ settings: cache.getPoolSearchSettings().copyWith(allowV3Search: value),
+ );
+
+ setState(() {});
+ },
+ ),
+ ],
+ ),
],
),
);
diff --git a/lib/core/dtos/pool_search_settings_dto.dart b/lib/core/dtos/pool_search_settings_dto.dart
index 029b52a..9565b98 100644
--- a/lib/core/dtos/pool_search_settings_dto.dart
+++ b/lib/core/dtos/pool_search_settings_dto.dart
@@ -10,6 +10,8 @@ class PoolSearchSettingsDto with _$PoolSearchSettingsDto {
@JsonSerializable(explicitToJson: true)
factory PoolSearchSettingsDto({
@Default(PoolSearchSettingsDto.defaultMinLiquidityUSD) @JsonKey(name: 'min_liquidity_usd') num minLiquidityUSD,
+ @Default(true) bool allowV4Search,
+ @Default(true) bool allowV3Search,
}) = _PoolSearchSettingsDto;
const PoolSearchSettingsDto._();
diff --git a/lib/core/dtos/token_dto.dart b/lib/core/dtos/token_dto.dart
index 0c0c15f..e69ddef 100644
--- a/lib/core/dtos/token_dto.dart
+++ b/lib/core/dtos/token_dto.dart
@@ -23,7 +23,13 @@ class TokenDto with _$TokenDto {
factory TokenDto.fixture() => TokenDto(
symbol: 'WETH',
name: 'Wrapped Ether',
- addresses: {AppNetworks.sepolia.chainId: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"},
+ addresses: Map.fromEntries(
+ AppNetworks.values.where((network) => !network.isAllNetworks).map(
+ (network) {
+ return MapEntry(network.chainId, "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2");
+ },
+ ),
+ ),
logoUrl:
'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png',
);
diff --git a/lib/core/dtos/token_price_dto.dart b/lib/core/dtos/token_price_dto.dart
new file mode 100644
index 0000000..920427c
--- /dev/null
+++ b/lib/core/dtos/token_price_dto.dart
@@ -0,0 +1,17 @@
+import 'package:freezed_annotation/freezed_annotation.dart';
+
+part 'token_price_dto.freezed.dart';
+part 'token_price_dto.g.dart';
+
+@freezed
+class TokenPriceDto with _$TokenPriceDto {
+ @JsonSerializable(explicitToJson: true)
+ factory TokenPriceDto({
+ @Default(0) num usdPrice,
+ @Default("") String address,
+ }) = _TokenPriceDto;
+
+ factory TokenPriceDto.fromJson(Map json) => _$TokenPriceDtoFromJson(json);
+
+ factory TokenPriceDto.fixture() => TokenPriceDto(usdPrice: 1328.112);
+}
diff --git a/lib/core/dtos/yield_dto.dart b/lib/core/dtos/yield_dto.dart
index eb58a19..0ddd6e7 100644
--- a/lib/core/dtos/yield_dto.dart
+++ b/lib/core/dtos/yield_dto.dart
@@ -1,10 +1,11 @@
import 'package:flutter/material.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
+import 'package:web3kit/core/ethereum_constants.dart';
import 'package:zup_app/core/dtos/protocol_dto.dart';
import 'package:zup_app/core/dtos/token_dto.dart';
import 'package:zup_app/core/enums/networks.dart';
+import 'package:zup_app/core/enums/pool_type.dart';
import 'package:zup_app/l10n/gen/app_localizations.dart';
-import 'package:zup_core/zup_core.dart';
part 'yield_dto.freezed.dart';
part 'yield_dto.g.dart';
@@ -47,46 +48,18 @@ class YieldDto with _$YieldDto {
required num yield30d,
required num yield90d,
required int chainId,
+ required PoolType poolType,
@Default(0) num totalValueLockedUSD,
+ @Default(EthereumConstants.zeroAddress) @JsonKey(name: "hooksAddress") String v4Hooks,
+ @JsonKey(name: "poolManagerAddress") String? v4PoolManager,
+ @JsonKey(name: "stateViewAddress") String? v4StateView,
+ @JsonKey(name: "permit2Address") String? permit2,
}) = _YieldDto;
AppNetworks get network => AppNetworks.fromChainId(chainId)!;
- TokenDto maybeNativeToken0({required bool permitNative}) {
- if (permitNative &&
- (token0.addresses[network.chainId]!).lowercasedEquals(network.wrappedNative.addresses[network.chainId]!)) {
- return TokenDto(
- addresses: {network.chainId: network.wrappedNative.addresses[network.chainId]!},
- decimals: network.chainInfo.nativeCurrency!.decimals,
- logoUrl: network.chainInfo.nativeCurrency!.logoUrl,
- symbol: network.chainInfo.nativeCurrency!.symbol,
- name: network.chainInfo.nativeCurrency!.name,
- );
- }
-
- return token0;
- }
-
- TokenDto maybeNativeToken1({required bool permitNative}) {
- if (permitNative &&
- token1.addresses[network.chainId]!.lowercasedEquals(network.wrappedNative.addresses[network.chainId]!)) {
- return TokenDto(
- addresses: {network.chainId: network.wrappedNative.addresses[network.chainId]!},
- decimals: network.chainInfo.nativeCurrency!.decimals,
- logoUrl: network.chainInfo.nativeCurrency!.logoUrl,
- symbol: network.chainInfo.nativeCurrency!.symbol,
- name: network.chainInfo.nativeCurrency!.name,
- );
- }
-
- return token1;
- }
-
- bool get isToken0WrappedNative =>
- token0.addresses[network.chainId]!.lowercasedEquals(network.wrappedNativeTokenAddress);
-
- bool get isToken1WrappedNative =>
- token1.addresses[network.chainId]!.lowercasedEquals(network.wrappedNativeTokenAddress);
+ bool get isToken0Native => token0.addresses[network.chainId] == EthereumConstants.zeroAddress;
+ bool get isToken1Native => token1.addresses[network.chainId] == EthereumConstants.zeroAddress;
factory YieldDto.fromJson(Map json) => _$YieldDtoFromJson(json);
@@ -97,6 +70,7 @@ class YieldDto with _$YieldDto {
yield90d: 32.2,
positionManagerAddress: "0x5Df2f0aFb5b5bB2Df9D1e9C7b6f5f0DD5f9eD5e0",
poolAddress: "0x5Df2f0aFb5b5bB2Df9D1e9C7b6f5f0DD5f9eD5e0",
+ poolType: PoolType.v3,
token0: TokenDto.fixture().copyWith(
symbol: "USDC",
decimals: 6,
diff --git a/lib/core/dtos/yields_dto.dart b/lib/core/dtos/yields_dto.dart
index 291f72c..15a54a6 100644
--- a/lib/core/dtos/yields_dto.dart
+++ b/lib/core/dtos/yields_dto.dart
@@ -2,6 +2,7 @@ import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:zup_app/core/dtos/protocol_dto.dart';
import 'package:zup_app/core/dtos/token_dto.dart';
import 'package:zup_app/core/dtos/yield_dto.dart';
+import 'package:zup_app/core/enums/pool_type.dart';
part 'yields_dto.freezed.dart';
part 'yields_dto.g.dart';
@@ -33,6 +34,7 @@ class YieldsDto with _$YieldsDto {
pools: [
YieldDto(
positionManagerAddress: "0x06eFdBFf2a14a7c8E15944D1F4A48F9F95F663A4",
+ poolType: PoolType.v3,
token0: TokenDto(
addresses: {11155111: "0x02a3e7E0480B668bD46b42852C58363F93e3bA5C"},
decimals: 6,
diff --git a/lib/core/enums/networks.dart b/lib/core/enums/networks.dart
index d68d8e1..a29915a 100644
--- a/lib/core/enums/networks.dart
+++ b/lib/core/enums/networks.dart
@@ -8,6 +8,8 @@ import 'package:zup_app/gen/assets.gen.dart';
enum AppNetworks {
allNetworks,
mainnet,
+ // base,
+ unichain,
scroll,
sepolia;
@@ -43,6 +45,8 @@ enum AppNetworks {
scroll => false,
sepolia => true,
allNetworks => false,
+ // base => false,
+ unichain => false,
};
String get label => switch (this) {
@@ -50,12 +54,16 @@ enum AppNetworks {
mainnet => "Ethereum",
scroll => "Scroll",
allNetworks => "All Networks",
+ // base => "Base",
+ unichain => "Unichain",
};
Widget get icon => switch (this) {
sepolia => Assets.logos.ethereum.svg(),
mainnet => Assets.logos.ethereum.svg(),
scroll => Assets.logos.scroll.svg(),
+ // base => Assets.logos.base.svg(),
+ unichain => Assets.logos.unichain.svg(),
allNetworks => Assets.icons.all.svg(),
};
@@ -82,6 +90,20 @@ enum AppNetworks {
nativeCurrency: NativeCurrencies.eth.currencyInfo,
rpcUrls: [rpcUrl],
),
+ // base => ChainInfo(
+ // hexChainId: "0x2105",
+ // chainName: label,
+ // blockExplorerUrls: const ["https://basescan.org"],
+ // nativeCurrency: NativeCurrencies.eth.currencyInfo,
+ // rpcUrls: [rpcUrl],
+ // ),
+ unichain => ChainInfo(
+ hexChainId: "0x82",
+ chainName: label,
+ blockExplorerUrls: const ["https://uniscan.xyz/"],
+ nativeCurrency: NativeCurrencies.eth.currencyInfo,
+ rpcUrls: [rpcUrl],
+ ),
};
String get wrappedNativeTokenAddress => switch (this) {
@@ -89,6 +111,8 @@ enum AppNetworks {
sepolia => "0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14",
mainnet => "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
scroll => "0x5300000000000000000000000000000000000004",
+ // base => "0x4200000000000000000000000000000000000006",
+ unichain => "0x4200000000000000000000000000000000000006"
};
TokenDto get wrappedNative => switch (this) {
@@ -115,6 +139,21 @@ enum AppNetworks {
logoUrl:
"https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/scroll/assets/0x5300000000000000000000000000000000000004/logo.png",
),
+ // base => TokenDto(
+ // addresses: {chainId: wrappedNativeTokenAddress},
+ // decimals: NativeCurrencies.eth.currencyInfo.decimals,
+ // name: "Wrapped Ether",
+ // symbol: "WETH",
+ // logoUrl:
+ // "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/base/assets/0x4200000000000000000000000000000000000006/logo.png",
+ // ),
+ unichain => TokenDto(
+ addresses: {chainId: wrappedNativeTokenAddress},
+ decimals: NativeCurrencies.eth.currencyInfo.decimals,
+ name: "Wrapped Ether",
+ symbol: "WETH",
+ logoUrl: "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/unichain/logo.png",
+ ),
};
String get rpcUrl => switch (this) {
@@ -122,28 +161,8 @@ enum AppNetworks {
sepolia => "https://ethereum-sepolia-rpc.publicnode.com",
mainnet => "https://ethereum-rpc.publicnode.com",
scroll => "https://scroll-rpc.publicnode.com",
- };
-
- TokenDto get nativeCurrencyTokenDto => switch (this) {
- allNetworks => throw UnimplementedError("allNetworks is not a valid network"),
- sepolia => TokenDto(
- name: NativeCurrencies.eth.currencyInfo.name,
- decimals: NativeCurrencies.eth.currencyInfo.decimals,
- symbol: NativeCurrencies.eth.currencyInfo.symbol,
- logoUrl: NativeCurrencies.eth.currencyInfo.logoUrl,
- ),
- mainnet => TokenDto(
- name: NativeCurrencies.eth.currencyInfo.name,
- decimals: NativeCurrencies.eth.currencyInfo.decimals,
- symbol: NativeCurrencies.eth.currencyInfo.symbol,
- logoUrl: NativeCurrencies.eth.currencyInfo.logoUrl,
- ),
- scroll => TokenDto(
- name: NativeCurrencies.eth.currencyInfo.name,
- decimals: NativeCurrencies.eth.currencyInfo.decimals,
- symbol: NativeCurrencies.eth.currencyInfo.symbol,
- logoUrl: NativeCurrencies.eth.currencyInfo.logoUrl,
- ),
+ // base => "https://base-rpc.publicnode.com",
+ unichain => "https://unichain-rpc.publicnode.com",
};
Future openTx(String txHash) async {
diff --git a/lib/core/enums/pool_type.dart b/lib/core/enums/pool_type.dart
new file mode 100644
index 0000000..b8945e3
--- /dev/null
+++ b/lib/core/enums/pool_type.dart
@@ -0,0 +1,16 @@
+import 'package:freezed_annotation/freezed_annotation.dart';
+
+enum PoolType {
+ @JsonValue("V3")
+ v3,
+ @JsonValue("V4")
+ v4;
+
+ bool get isV3 => this == PoolType.v3;
+ bool get isV4 => this == PoolType.v4;
+
+ String get label => switch (this) {
+ PoolType.v3 => "V3",
+ PoolType.v4 => "V4",
+ };
+}
diff --git a/lib/core/extensions/bigint_extension.dart b/lib/core/extensions/bigint_extension.dart
index f5ceb0d..a4cfc49 100644
--- a/lib/core/extensions/bigint_extension.dart
+++ b/lib/core/extensions/bigint_extension.dart
@@ -1,6 +1,6 @@
-import 'package:zup_app/core/v3_pool_constants.dart';
+import 'package:zup_app/core/v3_v4_pool_constants.dart';
extension BigIntExtension on BigInt {
- bool get isMinTick => this == V3PoolConstants.minTick;
- bool get isMaxTick => this == V3PoolConstants.maxTick;
+ bool get isMinTick => this == V3V4PoolConstants.minTick;
+ bool get isMaxTick => this == V3V4PoolConstants.maxTick;
}
diff --git a/lib/core/extensions/num_extension.dart b/lib/core/extensions/num_extension.dart
index a86d15a..f5b30cf 100644
--- a/lib/core/extensions/num_extension.dart
+++ b/lib/core/extensions/num_extension.dart
@@ -39,7 +39,7 @@ extension NumExtension on num {
if (decimals > maxDecimals && (this > maxDecimalsNumber)) decimalsDigits = maxDecimals;
if (useLessThan && this < maxDecimalsNumber) return toAmount(useLessThan: true, maxFixedDigits: maxDecimals);
- if (this < 0.1) return Decimal.parse(toString()).toString();
+ if (this < 0.1) return "${(isUSD ? "\$" : "")}${Decimal.parse(toString()).toString()}";
return NumberFormat.simpleCurrency(
decimalDigits: decimalsDigits,
diff --git a/lib/core/injections.dart b/lib/core/injections.dart
index 460f76a..e6decbc 100644
--- a/lib/core/injections.dart
+++ b/lib/core/injections.dart
@@ -7,12 +7,16 @@ import 'package:lottie/lottie.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:web3kit/web3kit.dart';
import 'package:zup_app/abis/erc_20.abi.g.dart';
-import 'package:zup_app/abis/uniswap_position_manager.abi.g.dart';
+import 'package:zup_app/abis/uniswap_permit2.abi.g.dart';
import 'package:zup_app/abis/uniswap_v3_pool.abi.g.dart';
+import 'package:zup_app/abis/uniswap_v3_position_manager.abi.g.dart';
+import 'package:zup_app/abis/uniswap_v4_position_manager.abi.g.dart';
+import 'package:zup_app/abis/uniswap_v4_state_view.abi.g.dart';
import 'package:zup_app/app/app_cubit/app_cubit.dart';
import 'package:zup_app/core/cache.dart';
import 'package:zup_app/core/debouncer.dart';
import 'package:zup_app/core/enums/app_environment.dart';
+import 'package:zup_app/core/pool_service.dart';
import 'package:zup_app/core/repositories/positions_repository.dart';
import 'package:zup_app/core/repositories/tokens_repository.dart';
import 'package:zup_app/core/repositories/yield_repository.dart';
@@ -37,6 +41,7 @@ abstract class InjectInstanceNames {
static final lottieSearching = Assets.lotties.seaching.path;
static const zupAPIDio = 'zup_api_dio';
static const confettiController10s = 'confetti_controller_10s';
+ static const zupHolderFactory = 'zup_holder_factory';
}
Future setupInjections() async {
@@ -73,9 +78,10 @@ Future setupInjections() async {
inject.registerLazySingleton(() => FirebaseAnalytics.instance);
inject.registerLazySingleton(() => ZupAnalytics(inject()));
inject.registerLazySingleton(() => ZupHolder());
+ inject.registerFactory(() => ZupHolder(), instanceName: InjectInstanceNames.zupHolderFactory);
inject.registerLazySingleton(() => Erc20());
inject.registerLazySingleton>(() => GlobalKey());
- inject.registerLazySingleton(() => UniswapPositionManager());
+ inject.registerLazySingleton(() => UniswapV3PositionManager());
inject.registerLazySingleton(() => ZupSingletonCache.shared);
inject.registerFactory(() => ZupLinks());
@@ -103,6 +109,29 @@ Future setupInjections() async {
() => Assets.lotties.seaching.lottie(),
instanceName: InjectInstanceNames.lottieSearching,
);
+ inject.registerLazySingleton(
+ () => UniswapV4StateView(),
+ );
+
+ inject.registerLazySingleton(() => EthereumAbiCoder());
+
+ inject.registerLazySingleton(
+ () => PoolService(
+ inject(),
+ inject(),
+ inject(),
+ inject(),
+ inject(),
+ ),
+ );
+
+ inject.registerLazySingleton(
+ () => UniswapPermit2(),
+ );
+
+ inject.registerLazySingleton(
+ () => UniswapV4PositionManager(),
+ );
// WARNING: this should always be factory following the instructions
inject.registerFactory(
diff --git a/lib/core/mixins/keys_mixin.dart b/lib/core/mixins/keys_mixin.dart
index 0093135..35d41c6 100644
--- a/lib/core/mixins/keys_mixin.dart
+++ b/lib/core/mixins/keys_mixin.dart
@@ -8,4 +8,8 @@ mixin KeysMixin {
String poolTickCacheKey({required AppNetworks network, required String poolAddress}) {
return 'poolTick-$poolAddress-${network.name}';
}
+
+ String tokenPriceCacheKey({required String tokenAddress, required AppNetworks network}) {
+ return 'tokenPrice-$tokenAddress-${network.name}';
+ }
}
diff --git a/lib/core/mixins/v3_pool_conversors_mixin.dart b/lib/core/mixins/v3_pool_conversors_mixin.dart
index 03a88e5..e45c6b0 100644
--- a/lib/core/mixins/v3_pool_conversors_mixin.dart
+++ b/lib/core/mixins/v3_pool_conversors_mixin.dart
@@ -1,6 +1,6 @@
import 'dart:math';
-import 'package:zup_app/core/v3_pool_constants.dart';
+import 'package:zup_app/core/v3_v4_pool_constants.dart';
mixin V3PoolConversorsMixin {
({double priceAsQuoteToken, double priceAsBaseToken}) tickToPrice({
@@ -22,11 +22,11 @@ mixin V3PoolConversorsMixin {
final highestValidTickDistanceFromTick = (highestValidTick - tick).abs();
if (lowestValidTickDistanceFromTick < highestValidTickDistanceFromTick &&
- lowestValidTick >= V3PoolConstants.minTick) {
+ lowestValidTick >= V3V4PoolConstants.minTick) {
return lowestValidTick;
}
- return highestValidTick > V3PoolConstants.maxTick ? lowestValidTick : highestValidTick;
+ return highestValidTick > V3V4PoolConstants.maxTick ? lowestValidTick : highestValidTick;
}
BigInt priceToTick({
diff --git a/lib/core/mixins/v4_pool_liquidity_calculations_mixin.dart b/lib/core/mixins/v4_pool_liquidity_calculations_mixin.dart
new file mode 100644
index 0000000..4f1def4
--- /dev/null
+++ b/lib/core/mixins/v4_pool_liquidity_calculations_mixin.dart
@@ -0,0 +1,84 @@
+mixin V4PoolLiquidityCalculationsMixin {
+ final _q96 = BigInt.parse("0x1000000000000000000000000");
+
+ BigInt getLiquidityForAmount0(BigInt sqrtPriceAX96, BigInt sqrtPriceBX96, BigInt amount0) {
+ if (sqrtPriceAX96 > sqrtPriceBX96) (sqrtPriceAX96, sqrtPriceBX96) = (sqrtPriceBX96, sqrtPriceAX96);
+
+ BigInt intermediate = ((sqrtPriceAX96 * sqrtPriceBX96) ~/ _q96);
+ return (amount0 * intermediate) ~/ (sqrtPriceBX96 - sqrtPriceAX96);
+ }
+
+ BigInt getLiquidityForAmount1(BigInt sqrtPriceAX96, BigInt sqrtPriceBX96, BigInt amount1) {
+ if (sqrtPriceAX96 > sqrtPriceBX96) (sqrtPriceAX96, sqrtPriceBX96) = (sqrtPriceBX96, sqrtPriceAX96);
+ return (amount1 * _q96) ~/ (sqrtPriceBX96 - sqrtPriceAX96);
+ }
+
+ BigInt getLiquidityForAmounts(
+ BigInt sqrtPriceX96,
+ BigInt sqrtPriceAX96,
+ BigInt sqrtPriceBX96,
+ BigInt amount0,
+ BigInt amount1,
+ ) {
+ if (sqrtPriceAX96 > sqrtPriceBX96) (sqrtPriceAX96, sqrtPriceBX96) = (sqrtPriceBX96, sqrtPriceAX96);
+
+ if (sqrtPriceX96 <= sqrtPriceAX96) {
+ return getLiquidityForAmount0(sqrtPriceAX96, sqrtPriceBX96, amount0);
+ } else if (sqrtPriceX96 < sqrtPriceBX96) {
+ BigInt liquidity0 = getLiquidityForAmount0(sqrtPriceX96, sqrtPriceBX96, amount0);
+ BigInt liquidity1 = getLiquidityForAmount1(sqrtPriceAX96, sqrtPriceX96, amount1);
+
+ return liquidity0 < liquidity1 ? liquidity0 : liquidity1;
+ } else {
+ return getLiquidityForAmount1(sqrtPriceAX96, sqrtPriceBX96, amount1);
+ }
+ }
+
+ BigInt getSqrtPriceAtTick(BigInt tick) {
+ const int maxTick = 887272;
+ final List mulConstants = [
+ BigInt.parse('fff97272373d413259a46990580e213a', radix: 16),
+ BigInt.parse('fff2e50f5f656932ef12357cf3c7fdcc', radix: 16),
+ BigInt.parse('ffe5caca7e10e4e61c3624eaa0941cd0', radix: 16),
+ BigInt.parse('ffcb9843d60f6159c9db58835c926644', radix: 16),
+ BigInt.parse('ff973b41fa98c081472e6896dfb254c0', radix: 16),
+ BigInt.parse('ff2ea16466c96a3843ec78b326b52861', radix: 16),
+ BigInt.parse('fe5dee046a99a2a811c461f1969c3053', radix: 16),
+ BigInt.parse('fcbe86c7900a88aedcffc83b479aa3a4', radix: 16),
+ BigInt.parse('f987a7253ac413176f2b074cf7815e54', radix: 16),
+ BigInt.parse('f3392b0822b70005940c7a398e4b70f3', radix: 16),
+ BigInt.parse('e7159475a2c29b7443b29c7fa6e889d9', radix: 16),
+ BigInt.parse('d097f3bdfd2022b8845ad8f792aa5825', radix: 16),
+ BigInt.parse('a9f746462d870fdf8a65dc1f90e061e5', radix: 16),
+ BigInt.parse('70d869a156d2a1b890bb3df62baf32f7', radix: 16),
+ BigInt.parse('31be135f97d08fd981231505542fcfa6', radix: 16),
+ BigInt.parse('9aa508b5b7a84e1c677de54f3e99bc9', radix: 16),
+ BigInt.parse('5d6af8dedb81196699c329225ee604', radix: 16),
+ BigInt.parse('2216e584f5fa1ea926041bedfe98', radix: 16),
+ BigInt.parse('48a170391f7dc42444e8fa2', radix: 16),
+ ];
+
+ BigInt absTick = tick.isNegative ? -tick : tick;
+ if (absTick > BigInt.from(maxTick)) throw Exception('Tick out of range');
+
+ BigInt price =
+ (absTick.toInt() & 0x1) != 0 ? BigInt.parse('fffcb933bd6fad37aa2d162d1a594001', radix: 16) : BigInt.one << 128;
+
+ for (int i = 0; i < mulConstants.length; i++) {
+ if ((absTick.toInt() & (1 << (i + 1))) != 0) {
+ price = (price * mulConstants[i]) >> 128;
+ }
+ }
+
+ if (tick > BigInt.zero) {
+ BigInt maxUint256 = (BigInt.one << 256) - BigInt.one;
+ price = maxUint256 ~/ price;
+ }
+
+ BigInt maxUint32 = (BigInt.one << 32) - BigInt.one;
+ price = (price + maxUint32) >> 32;
+
+ BigInt mask160 = (BigInt.one << 160) - BigInt.one;
+ return price & mask160;
+ }
+}
diff --git a/lib/core/pool_service.dart b/lib/core/pool_service.dart
new file mode 100644
index 0000000..8a85d1a
--- /dev/null
+++ b/lib/core/pool_service.dart
@@ -0,0 +1,223 @@
+import 'package:clock/clock.dart';
+import 'package:web3kit/core/dtos/transaction_response.dart';
+import 'package:web3kit/web3kit.dart';
+import 'package:zup_app/abis/uniswap_v3_pool.abi.g.dart';
+import 'package:zup_app/abis/uniswap_v3_position_manager.abi.g.dart';
+import 'package:zup_app/abis/uniswap_v4_position_manager.abi.g.dart';
+import 'package:zup_app/abis/uniswap_v4_state_view.abi.g.dart';
+import 'package:zup_app/core/dtos/yield_dto.dart';
+import 'package:zup_app/core/mixins/v4_pool_liquidity_calculations_mixin.dart';
+import 'package:zup_app/core/v4_pool_constants.dart';
+
+class PoolService with V4PoolLiquidityCalculationsMixin {
+ final UniswapV4StateView _uniswapV4StateView;
+ final UniswapV3Pool _uniswapV3Pool;
+ final UniswapV3PositionManager _uniswapV3PositionManager;
+ final UniswapV4PositionManager _uniswapV4PositionManager;
+ final EthereumAbiCoder _ethereumAbiCoder;
+
+ PoolService(
+ this._uniswapV4StateView,
+ this._uniswapV3Pool,
+ this._uniswapV3PositionManager,
+ this._uniswapV4PositionManager,
+ this._ethereumAbiCoder,
+ );
+
+ Future getPoolTick(YieldDto forYield) async {
+ if (forYield.poolType.isV4) {
+ final stateView = _uniswapV4StateView.fromRpcProvider(
+ contractAddress: forYield.v4StateView!,
+ rpcUrl: forYield.network.rpcUrl,
+ );
+
+ return (await stateView.getSlot0(poolId: forYield.poolAddress)).tick;
+ }
+
+ final uniswapV3Pool = _uniswapV3Pool.fromRpcProvider(
+ contractAddress: forYield.poolAddress,
+ rpcUrl: forYield.network.rpcUrl,
+ );
+
+ return (await uniswapV3Pool.slot0()).tick;
+ }
+
+ Future sendV3PoolDepositTransaction(
+ YieldDto depositOnYield,
+ Signer signer, {
+ required BigInt amount0Desired,
+ required BigInt amount1Desired,
+ required Duration deadline,
+ required BigInt amount0Min,
+ required BigInt amount1Min,
+ required String recipient,
+ required BigInt tickLower,
+ required BigInt tickUpper,
+ }) async {
+ final v3PositionManagerContract = _uniswapV3PositionManager.fromSigner(
+ contractAddress: depositOnYield.positionManagerAddress,
+ signer: signer,
+ );
+
+ final TransactionResponse tx = await () async {
+ if (depositOnYield.isToken1Native || depositOnYield.isToken0Native) {
+ final mintCalldata = _uniswapV3PositionManager.getMintCalldata(
+ params: (
+ amount0Desired: amount0Desired,
+ amount1Desired: amount1Desired,
+ deadline: BigInt.from(clock.now().add(deadline).millisecondsSinceEpoch),
+ amount0Min: amount0Min,
+ amount1Min: amount1Min,
+ recipient: recipient,
+ tickLower: tickLower,
+ tickUpper: tickUpper,
+ fee: BigInt.from(depositOnYield.feeTier),
+ token0: depositOnYield.token0.addresses[depositOnYield.network.chainId]! == EthereumConstants.zeroAddress
+ ? depositOnYield.network.wrappedNativeTokenAddress
+ : depositOnYield.token0.addresses[depositOnYield.network.chainId]!,
+ token1: depositOnYield.token1.addresses[depositOnYield.network.chainId]! == EthereumConstants.zeroAddress
+ ? depositOnYield.network.wrappedNativeTokenAddress
+ : depositOnYield.token1.addresses[depositOnYield.network.chainId]!,
+ ),
+ );
+
+ return await v3PositionManagerContract.multicall(
+ data: [
+ mintCalldata,
+ _uniswapV3PositionManager.getRefundETHCalldata(),
+ ],
+ ethValue: () {
+ if (depositOnYield.isToken0Native) {
+ return amount0Desired;
+ }
+
+ return amount1Desired;
+ }.call());
+ }
+
+ return await v3PositionManagerContract.mint(
+ params: (
+ amount0Desired: amount0Desired,
+ amount1Desired: amount1Desired,
+ deadline: BigInt.from(clock.now().add(deadline).millisecondsSinceEpoch),
+ amount0Min: amount0Min,
+ amount1Min: amount1Min,
+ recipient: recipient,
+ tickLower: tickLower,
+ tickUpper: tickUpper,
+ fee: BigInt.from(depositOnYield.feeTier),
+ token0: depositOnYield.token0.addresses[depositOnYield.network.chainId]!,
+ token1: depositOnYield.token1.addresses[depositOnYield.network.chainId]!,
+ ),
+ );
+ }.call();
+
+ return tx;
+ }
+
+ Future sendV4PoolDepositTransaction(
+ YieldDto depositOnYield,
+ Signer signer, {
+ required Duration deadline,
+ required BigInt tickLower,
+ required BigInt tickUpper,
+ required BigInt amount0toDeposit,
+ required BigInt amount1ToDeposit,
+ required BigInt maxAmount0ToDeposit,
+ required BigInt maxAmount1ToDeposit,
+ required String recipient,
+ required BigInt currentPoolTick,
+ }) async {
+ final isNativeDeposit = depositOnYield.isToken0Native || depositOnYield.isToken1Native;
+
+ final actions = _ethereumAbiCoder.encodePacked([
+ "uint8",
+ "uint8",
+ if (isNativeDeposit) "uint8",
+ ], [
+ V4PoolConstants.mintPositionActionValue,
+ V4PoolConstants.settlePairActionValue,
+ if (isNativeDeposit) V4PoolConstants.sweepActionValue,
+ ]);
+
+ final mintPositionActionParams = _ethereumAbiCoder.encode([
+ "tuple(address,address,int32,int24,address)",
+ "int24",
+ "int24",
+ "uint256",
+ "uint128",
+ "uint128",
+ "address",
+ "bytes"
+ ], [
+ [
+ depositOnYield.token0.addresses[depositOnYield.network.chainId]!,
+ depositOnYield.token1.addresses[depositOnYield.network.chainId]!,
+ BigInt.from(depositOnYield.feeTier),
+ BigInt.from(depositOnYield.tickSpacing),
+ depositOnYield.v4Hooks,
+ ],
+ tickLower,
+ tickUpper,
+ getLiquidityForAmounts(
+ getSqrtPriceAtTick(currentPoolTick),
+ getSqrtPriceAtTick(tickLower),
+ getSqrtPriceAtTick(tickUpper),
+ amount0toDeposit,
+ amount1ToDeposit,
+ ),
+ maxAmount0ToDeposit,
+ maxAmount1ToDeposit,
+ recipient,
+ EthereumConstants.emptyBytes,
+ ]);
+
+ final settlePairActionParams = _ethereumAbiCoder.encode([
+ "address",
+ "address"
+ ], [
+ depositOnYield.token0.addresses[depositOnYield.network.chainId]!,
+ depositOnYield.token1.addresses[depositOnYield.network.chainId]!,
+ ]);
+
+ final sweepActionParams = isNativeDeposit
+ ? _ethereumAbiCoder.encode([
+ "address",
+ "address"
+ ], [
+ EthereumConstants.zeroAddress,
+ recipient,
+ ])
+ : null;
+
+ final uniswapV4PositionManagerContract = _uniswapV4PositionManager.fromSigner(
+ contractAddress: depositOnYield.positionManagerAddress,
+ signer: signer,
+ );
+
+ final params = [
+ mintPositionActionParams,
+ settlePairActionParams,
+ if (isNativeDeposit) sweepActionParams,
+ ];
+
+ final unlockData = _ethereumAbiCoder.encode([
+ "bytes",
+ "bytes[]"
+ ], [
+ actions,
+ params,
+ ]);
+
+ return await uniswapV4PositionManagerContract.modifyLiquidities(
+ deadline: BigInt.from(clock.now().add(deadline).millisecondsSinceEpoch),
+ unlockData: unlockData,
+ ethValue: () {
+ if (!isNativeDeposit) return null;
+ if (depositOnYield.isToken0Native) return amount0toDeposit;
+
+ return amount1ToDeposit;
+ }.call(),
+ );
+ }
+}
diff --git a/lib/core/repositories/tokens_repository.dart b/lib/core/repositories/tokens_repository.dart
index c9c119c..455af32 100644
--- a/lib/core/repositories/tokens_repository.dart
+++ b/lib/core/repositories/tokens_repository.dart
@@ -1,5 +1,6 @@
import 'package:dio/dio.dart';
import 'package:zup_app/core/dtos/token_dto.dart';
+import 'package:zup_app/core/dtos/token_price_dto.dart';
import 'package:zup_app/core/enums/networks.dart';
class TokensRepository {
@@ -35,4 +36,13 @@ class TokensRepository {
_searchTokenLastCancelToken = null;
return (response.data as List).map((token) => TokenDto.fromJson(token)).toList();
}
+
+ Future getTokenPrice(String address, AppNetworks network) async {
+ final response = await _zupAPIDio.get("/tokens/price", queryParameters: {
+ "address": address,
+ "chainId": network.chainId,
+ });
+
+ return TokenPriceDto.fromJson(response.data);
+ }
}
diff --git a/lib/core/repositories/yield_repository.dart b/lib/core/repositories/yield_repository.dart
index b856ebc..cde644d 100644
--- a/lib/core/repositories/yield_repository.dart
+++ b/lib/core/repositories/yield_repository.dart
@@ -1,5 +1,5 @@
import 'package:dio/dio.dart';
-import 'package:web3kit/core/ethereum_constants.dart';
+import 'package:zup_app/core/dtos/pool_search_settings_dto.dart';
import 'package:zup_app/core/dtos/yields_dto.dart';
import 'package:zup_app/core/enums/networks.dart';
@@ -12,31 +12,42 @@ class YieldRepository {
required String token0Address,
required String token1Address,
required AppNetworks network,
- num? minTvlUsd,
+ required PoolSearchSettingsDto searchSettings,
}) async {
- final response = await _zupAPIDio.get("/pools/search/${network.chainId}", queryParameters: {
- "token0Address": token0Address != EthereumConstants.zeroAddress
- ? token0Address
- : network.wrappedNative.addresses[network.chainId]!,
- "token1Address": token1Address != EthereumConstants.zeroAddress
- ? token1Address
- : network.wrappedNative.addresses[network.chainId]!,
- if (minTvlUsd != null) "minTvlUsd": minTvlUsd
+ final response = await _zupAPIDio.post("/pools/search/${network.chainId}", queryParameters: {
+ "token0Address": token0Address,
+ "token1Address": token1Address,
+ }, data: {
+ "filters": {
+ "minTvlUsd": searchSettings.minLiquidityUSD,
+ "allowedPoolTypes": [
+ if (searchSettings.allowV3Search) "V3",
+ if (searchSettings.allowV4Search) "V4",
+ ],
+ }
});
return YieldsDto.fromJson(response.data);
}
- Future getAllNetworksYield(
- {required String token0InternalId,
- required String token1InternalId,
- num? minTvlUsd,
- bool testnetMode = false}) async {
- final response = await _zupAPIDio.get("/pools/search/all", queryParameters: {
+ Future getAllNetworksYield({
+ required String token0InternalId,
+ required String token1InternalId,
+ required PoolSearchSettingsDto searchSettings,
+ bool testnetMode = false,
+ }) async {
+ final response = await _zupAPIDio.post("/pools/search/all", queryParameters: {
"token0Id": token0InternalId,
"token1Id": token1InternalId,
- "testnetMode": testnetMode,
- if (minTvlUsd != null) "minTvlUsd": minTvlUsd,
+ }, data: {
+ "filters": {
+ "minTvlUsd": searchSettings.minLiquidityUSD,
+ "testnetMode": testnetMode,
+ "allowedPoolTypes": [
+ if (searchSettings.allowV3Search) "V3",
+ if (searchSettings.allowV4Search) "V4",
+ ],
+ }
});
return YieldsDto.fromJson(response.data);
diff --git a/lib/core/slippage.dart b/lib/core/slippage.dart
index 37c159d..d1748ff 100644
--- a/lib/core/slippage.dart
+++ b/lib/core/slippage.dart
@@ -35,10 +35,14 @@ class Slippage extends Equatable {
return ZupColors.brand;
}
- BigInt calculateTokenAmountFromSlippage(BigInt amount) {
+ BigInt calculateMinTokenAmountFromSlippage(BigInt amount) {
return amount * (BigInt.from(10000) - BigInt.from(valueBasisPoints)) ~/ BigInt.from(10000);
}
+ BigInt calculateMaxTokenAmountFromSlippage(BigInt amount) {
+ return amount * (BigInt.from(10000) + BigInt.from(valueBasisPoints)) ~/ BigInt.from(10000);
+ }
+
bool get isCustom => this != zeroPointOnePercent && this != halfPercent && this != onePercent;
int get valueBasisPoints => (value * 100).toInt();
diff --git a/lib/core/v3_pool_constants.dart b/lib/core/v3_v4_pool_constants.dart
similarity index 73%
rename from lib/core/v3_pool_constants.dart
rename to lib/core/v3_v4_pool_constants.dart
index 22ce80b..9f6a3ec 100644
--- a/lib/core/v3_pool_constants.dart
+++ b/lib/core/v3_v4_pool_constants.dart
@@ -1,4 +1,4 @@
-abstract class V3PoolConstants {
+abstract class V3V4PoolConstants {
static final BigInt minTick = BigInt.from(-887272);
static final BigInt maxTick = -minTick;
}
diff --git a/lib/core/v4_pool_constants.dart b/lib/core/v4_pool_constants.dart
new file mode 100644
index 0000000..75ad585
--- /dev/null
+++ b/lib/core/v4_pool_constants.dart
@@ -0,0 +1,5 @@
+abstract class V4PoolConstants {
+ static const mintPositionActionValue = 0x02;
+ static const settlePairActionValue = 0x0d;
+ static const sweepActionValue = 0x14;
+}
diff --git a/lib/l10n/en.arb b/lib/l10n/en.arb
index fae6f3d..bdea6df 100644
--- a/lib/l10n/en.arb
+++ b/lib/l10n/en.arb
@@ -338,6 +338,8 @@
}
},
"createPageSettingsDropdownMinimumLiquidity": "Minimum Pool Liquidity",
+ "createPageSettingsDropdownAllowedPoolTypes": "Allowed Pool Types",
+ "createPageSettingsDropdownAllowedPoolTypesDescription": "Filter the types of liquidity pools to include in your search",
"createPageSettingsDropdownMinimumLiquidityExplanation": "Filter pools by minimum liquidity. We’ll exclude pools with less liquidity than specified, as low Liquidity can lead to misleading yields. This helps you find more reliable opportunities",
"createPageSettingsDropdownMiniumLiquidityLowWarning": "Low minimum TVL can lead to misleading yields.",
"appSettingsDropdownTestnetMode": "Testnet Mode",
diff --git a/lib/l10n/gen/app_localizations.dart b/lib/l10n/gen/app_localizations.dart
index 7186596..e0f6eac 100644
--- a/lib/l10n/gen/app_localizations.dart
+++ b/lib/l10n/gen/app_localizations.dart
@@ -965,6 +965,18 @@ abstract class S {
/// **'Minimum Pool Liquidity'**
String get createPageSettingsDropdownMinimumLiquidity;
+ /// No description provided for @createPageSettingsDropdownAllowedPoolTypes.
+ ///
+ /// In en, this message translates to:
+ /// **'Allowed Pool Types'**
+ String get createPageSettingsDropdownAllowedPoolTypes;
+
+ /// No description provided for @createPageSettingsDropdownAllowedPoolTypesDescription.
+ ///
+ /// In en, this message translates to:
+ /// **'Filter the types of liquidity pools to include in your search'**
+ String get createPageSettingsDropdownAllowedPoolTypesDescription;
+
/// No description provided for @createPageSettingsDropdownMinimumLiquidityExplanation.
///
/// In en, this message translates to:
diff --git a/lib/l10n/gen/app_localizations_en.dart b/lib/l10n/gen/app_localizations_en.dart
index 1a6d63f..b4ec1ad 100644
--- a/lib/l10n/gen/app_localizations_en.dart
+++ b/lib/l10n/gen/app_localizations_en.dart
@@ -543,6 +543,13 @@ class SEn extends S {
String get createPageSettingsDropdownMinimumLiquidity =>
'Minimum Pool Liquidity';
+ @override
+ String get createPageSettingsDropdownAllowedPoolTypes => 'Allowed Pool Types';
+
+ @override
+ String get createPageSettingsDropdownAllowedPoolTypesDescription =>
+ 'Filter the types of liquidity pools to include in your search';
+
@override
String get createPageSettingsDropdownMinimumLiquidityExplanation =>
'Filter pools by minimum liquidity. We’ll exclude pools with less liquidity than specified, as low Liquidity can lead to misleading yields. This helps you find more reliable opportunities';
diff --git a/macos/Podfile.lock b/macos/Podfile.lock
index 2e9a2db..52a7343 100644
--- a/macos/Podfile.lock
+++ b/macos/Podfile.lock
@@ -6,11 +6,11 @@ PODS:
- FirebaseAnalytics (~> 11.10.0)
- Firebase/CoreOnly (11.10.0):
- FirebaseCore (~> 11.10.0)
- - firebase_analytics (11.4.5):
+ - firebase_analytics (11.4.6):
- Firebase/Analytics (= 11.10.0)
- firebase_core
- FlutterMacOS
- - firebase_core (3.13.0):
+ - firebase_core (3.13.1):
- Firebase/CoreOnly (~> 11.10.0)
- FlutterMacOS
- FirebaseAnalytics (11.10.0):
@@ -102,7 +102,7 @@ PODS:
- shared_preferences_foundation (0.0.1):
- Flutter
- FlutterMacOS
- - sqflite (0.0.3):
+ - sqflite_darwin (0.0.4):
- Flutter
- FlutterMacOS
- url_launcher_macos (0.0.1):
@@ -114,7 +114,7 @@ DEPENDENCIES:
- FlutterMacOS (from `Flutter/ephemeral`)
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
- shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`)
- - sqflite (from `Flutter/ephemeral/.symlinks/plugins/sqflite/darwin`)
+ - sqflite_darwin (from `Flutter/ephemeral/.symlinks/plugins/sqflite_darwin/darwin`)
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
SPEC REPOS:
@@ -140,15 +140,15 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin
shared_preferences_foundation:
:path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin
- sqflite:
- :path: Flutter/ephemeral/.symlinks/plugins/sqflite/darwin
+ sqflite_darwin:
+ :path: Flutter/ephemeral/.symlinks/plugins/sqflite_darwin/darwin
url_launcher_macos:
:path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
SPEC CHECKSUMS:
Firebase: 1fe1c0a7d9aaea32efe01fbea5f0ebd8d70e53a2
- firebase_analytics: 934289da4d473663d5866f1563f0cae2a38ebd82
- firebase_core: bb06473757206589a00a36920cbf0f33646e19cc
+ firebase_analytics: 42038ac430b2701716daba1fe06325c2e922aaa0
+ firebase_core: 2a91d5383968c70446b882d94e2fa54d11a28661
FirebaseAnalytics: 4e42333f02cf78ed93703a5c36f36dd518aebdef
FirebaseCore: 8344daef5e2661eb004b177488d6f9f0f24251b7
FirebaseCoreInternal: ef4505d2afb1d0ebbc33162cb3795382904b5679
@@ -160,8 +160,8 @@ SPEC CHECKSUMS:
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
- sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec
- url_launcher_macos: 5f437abeda8c85500ceb03f5c1938a8c5a705399
+ sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d
+ url_launcher_macos: c82c93949963e55b228a30115bd219499a6fe404
PODFILE CHECKSUM: 9ebaf0ce3d369aaa26a9ea0e159195ed94724cf3
diff --git a/pubspec.lock b/pubspec.lock
index 482fc2c..e842ad6 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -58,10 +58,10 @@ packages:
dependency: transitive
description:
name: async
- sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63
+ sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb"
url: "https://pub.dev"
source: hosted
- version: "2.12.0"
+ version: "2.13.0"
bloc:
dependency: transitive
description:
@@ -266,10 +266,10 @@ packages:
dependency: "direct main"
description:
name: decimal
- sha256: "4140a688f9e443e2f4de3a1162387bf25e1ac6d51e24c9da263f245210f41440"
+ sha256: "28239b8b929c1bd8618702e6dbc96e2618cf99770bbe9cb040d6cf56a11e4ec3"
url: "https://pub.dev"
source: hosted
- version: "3.0.2"
+ version: "3.2.1"
dio:
dependency: "direct main"
description:
@@ -314,10 +314,10 @@ packages:
dependency: "direct dev"
description:
name: fake_async
- sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc"
+ sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
url: "https://pub.dev"
source: hosted
- version: "1.3.2"
+ version: "1.3.3"
ffi:
dependency: transitive
description:
@@ -590,10 +590,10 @@ packages:
dependency: "direct main"
description:
name: intl
- sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
+ sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5"
url: "https://pub.dev"
source: hosted
- version: "0.19.0"
+ version: "0.20.2"
io:
dependency: transitive
description:
@@ -630,10 +630,10 @@ packages:
dependency: transitive
description:
name: leak_tracker
- sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec
+ sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0"
url: "https://pub.dev"
source: hosted
- version: "10.0.8"
+ version: "10.0.9"
leak_tracker_flutter_testing:
dependency: transitive
description:
@@ -999,10 +999,10 @@ packages:
dependency: "direct main"
description:
name: skeletonizer
- sha256: "0dcacc51c144af4edaf37672072156f49e47036becbc394d7c51850c5c1e884b"
+ sha256: a9ddf63900947f4c0648372b6e9987bc2b028db9db843376db6767224d166c31
url: "https://pub.dev"
source: hosted
- version: "1.4.3"
+ version: "2.0.1"
sky_engine:
dependency: transitive
description: flutter
@@ -1268,10 +1268,10 @@ packages:
dependency: transitive
description:
name: vm_service
- sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14"
+ sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02
url: "https://pub.dev"
source: hosted
- version: "14.3.1"
+ version: "15.0.0"
watcher:
dependency: transitive
description:
@@ -1293,7 +1293,7 @@ packages:
description:
path: "."
ref: main
- resolved-ref: efecab377221d2984aaaa7329dfb5cebcccf58f2
+ resolved-ref: "42dfe9f12673e21d2bc15623a6f58a1094969f2c"
url: "https://github.com/Zup-Protocol/web3kit.git"
source: git
version: "0.0.1"
@@ -1357,4 +1357,4 @@ packages:
version: "0.0.1"
sdks:
dart: ">=3.7.0 <4.0.0"
- flutter: ">=3.29.2"
+ flutter: ">=3.32.2"
diff --git a/pubspec.yaml b/pubspec.yaml
index e6c1770..132d90b 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -3,7 +3,7 @@ publish_to: none
environment:
sdk: ^3.5.2
- flutter: 3.29.2
+ flutter: 3.32.2
dependencies:
build_runner: ^2.4.12
@@ -38,10 +38,10 @@ dependencies:
json_annotation: ^4.9.0
url_launcher: ^6.3.0
cached_network_image: ^3.4.1
- skeletonizer: ^1.4.2
+ skeletonizer: ^2.0.1
shared_preferences: ^2.3.2
dio: ^5.7.0
- intl: ^0.19.0
+ intl: ^0.20.2
lottie: ^3.1.2
equatable: ^2.0.5
decimal: ^3.0.2
diff --git a/test/app/create/deposit/deposit_cubit_test.dart b/test/app/create/deposit/deposit_cubit_test.dart
index e2768d3..d3d5284 100644
--- a/test/app/create/deposit/deposit_cubit_test.dart
+++ b/test/app/create/deposit/deposit_cubit_test.dart
@@ -12,6 +12,7 @@ import 'package:zup_app/core/dtos/pool_search_settings_dto.dart';
import 'package:zup_app/core/dtos/yield_dto.dart';
import 'package:zup_app/core/dtos/yields_dto.dart';
import 'package:zup_app/core/enums/networks.dart';
+import 'package:zup_app/core/pool_service.dart';
import 'package:zup_app/core/repositories/yield_repository.dart';
import 'package:zup_app/core/slippage.dart';
import 'package:zup_app/core/zup_analytics.dart';
@@ -29,12 +30,15 @@ void main() {
late Cache cache;
late AppCubit appCubit;
late ZupAnalytics zupAnalytics;
-
+ late PoolService poolService;
final poolTick = BigInt.from(31276567121);
setUp(() {
registerFallbackValue(DepositSettingsDto.fixture());
registerFallbackValue(AppNetworks.sepolia);
+ registerFallbackValue(PoolSearchSettingsDto.fixture());
+ registerFallbackValue(YieldDto.fixture());
+ poolService = PoolServiceMock();
yieldRepository = YieldRepositoryMock();
zupSingletonCache = ZupSingletonCache.shared;
@@ -49,10 +53,10 @@ void main() {
yieldRepository,
zupSingletonCache,
wallet,
- uniswapV3Pool,
cache,
appCubit,
zupAnalytics,
+ poolService,
);
when(() => appCubit.isTestnetMode).thenReturn(false);
@@ -60,7 +64,7 @@ void main() {
when(() => yieldRepository.getAllNetworksYield(
token0InternalId: any(named: "token0InternalId"),
token1InternalId: any(named: "token1InternalId"),
- minTvlUsd: any(named: "minTvlUsd"),
+ searchSettings: any(named: "searchSettings"),
testnetMode: any(named: "testnetMode"))).thenAnswer((_) async => YieldsDto.fixture());
when(() => appCubit.selectedNetwork).thenAnswer((_) => AppNetworks.sepolia);
@@ -87,6 +91,7 @@ void main() {
unlocked: true
));
+ when(() => poolService.getPoolTick(any())).thenAnswer((_) async => poolTick);
when(() => cache.getPoolSearchSettings()).thenReturn(PoolSearchSettingsDto(minLiquidityUSD: 129816));
});
@@ -180,7 +185,7 @@ void main() {
when(() => yieldRepository.getSingleNetworkYield(
token0Address: any(named: "token0Address"),
token1Address: any(named: "token1Address"),
- minTvlUsd: any(named: "minTvlUsd"),
+ searchSettings: any(named: "searchSettings"),
network: any(named: "network"))).thenAnswer(
(_) async => YieldsDto.fixture(),
);
@@ -193,7 +198,7 @@ void main() {
verify(() => yieldRepository.getSingleNetworkYield(
token0Address: token0Address,
token1Address: token1Address,
- minTvlUsd: any(named: "minTvlUsd"),
+ searchSettings: any(named: "searchSettings"),
network: any(named: "network"),
)).called(1);
});
@@ -206,7 +211,7 @@ void main() {
when(() => yieldRepository.getSingleNetworkYield(
token0Address: any(named: "token0Address"),
token1Address: any(named: "token1Address"),
- minTvlUsd: any(named: "minTvlUsd"),
+ searchSettings: any(named: "searchSettings"),
network: any(named: "network"))).thenAnswer(
(_) async => const YieldsDto(pools: [], minLiquidityUSD: minLiquidityUSD),
);
@@ -227,7 +232,7 @@ void main() {
when(() => yieldRepository.getSingleNetworkYield(
token0Address: any(named: "token0Address"),
token1Address: any(named: "token1Address"),
- minTvlUsd: any(named: "minTvlUsd"),
+ searchSettings: any(named: "searchSettings"),
network: any(named: "network"))).thenAnswer((_) async => pools);
expectLater(sut.stream, emitsInOrder([const DepositState.loading(), DepositState.success(pools)]));
@@ -239,7 +244,7 @@ void main() {
when(() => yieldRepository.getSingleNetworkYield(
token0Address: any(named: "token0Address"),
token1Address: any(named: "token1Address"),
- minTvlUsd: any(named: "minTvlUsd"),
+ searchSettings: any(named: "searchSettings"),
network: any(named: "network"))).thenThrow(Exception());
expectLater(sut.stream, emitsInOrder([const DepositState.loading(), const DepositState.error()]));
@@ -268,7 +273,7 @@ void main() {
await sut.selectYield(selectedYield, YieldTimeFrame.day);
- verify(() => uniswapV3PoolImpl.slot0()).called(1);
+ verify(() => poolService.getPoolTick(selectedYield)).called(1);
});
test("When calling `selectYield` but the yield is null, it should not get the pool tick", () async {
@@ -291,35 +296,20 @@ void main() {
await sut.getSelectedPoolTick();
});
- test("""When calling `getSelectedPoolTick` it should create the
- UniswapV3Pool contract from RPC, with the address of the selected
- pool""", () async {
- final selectedYield = YieldDto.fixture();
-
- await sut.selectYield(YieldDto.fixture(), YieldTimeFrame.day);
- await sut.getSelectedPoolTick();
-
- verify(
- () => uniswapV3Pool.fromRpcProvider(
- contractAddress: selectedYield.poolAddress,
- rpcUrl: selectedYield.network.rpcUrl,
- ),
- ).called(2); // 2 because of the `selectYield` and the `getSelectedPoolTick`
- });
-
- test("When calling `getSelectedPoolTick` it should use the slot0 from the UniswapV3Pool contract", () async {
- await sut.selectYield(YieldDto.fixture(), YieldTimeFrame.day);
+ test("When calling `getSelectedPoolTick` it should use the pool service to get it", () async {
+ final yieldDto = YieldDto.fixture();
+ await sut.selectYield(yieldDto, YieldTimeFrame.day);
await sut.getSelectedPoolTick();
- verify(() => uniswapV3PoolImpl.slot0()).called(2); // 2 because of the `selectYield` and the `getSelectedPoolTick`
+ verify(() => poolService.getPoolTick(yieldDto))
+ .called(2); // 2 because of the `selectYield` and the `getSelectedPoolTick`
});
test(""""
When calling `getSelectedPoolTick` for a selected pool,
but when the call to the contract completes, the selected pool
is not the same as the one passed to the call, it shoul re-call
- `getSelectedPoolTick` to get the correct pool tick
-""", () async {
+ `getSelectedPoolTick` to get the correct pool tick""", () async {
final expectedYieldBTick = BigInt.from(326287637265372111);
const yieldAPoolAddress = "0x3263782637263";
@@ -328,37 +318,20 @@ void main() {
final yieldA = YieldDto.fixture().copyWith(poolAddress: yieldAPoolAddress);
final yieldB = YieldDto.fixture().copyWith(poolAddress: yieldBPoolAddress);
- when(() => uniswapV3PoolImpl.slot0()).thenAnswer((_) async {
- when(() => uniswapV3PoolImpl.slot0()).thenAnswer((_) async {
- return (
- feeProtocol: BigInt.zero,
- observationCardinality: BigInt.zero,
- observationCardinalityNext: BigInt.zero,
- observationIndex: BigInt.zero,
- sqrtPriceX96: BigInt.zero,
- tick: expectedYieldBTick,
- unlocked: true
- );
+ when(() => poolService.getPoolTick(any())).thenAnswer((_) async {
+ when(() => poolService.getPoolTick(any())).thenAnswer((_) async {
+ return expectedYieldBTick;
});
await sut.selectYield(yieldB, YieldTimeFrame.day);
- return (
- feeProtocol: BigInt.zero,
- observationCardinality: BigInt.zero,
- observationCardinalityNext: BigInt.zero,
- observationIndex: BigInt.zero,
- sqrtPriceX96: BigInt.zero,
- tick: poolTick,
- unlocked: true
- );
+ return poolTick;
});
await sut.selectYield(yieldA, YieldTimeFrame.day); // assuming that select yield will call `getSelectedPoolTick`
- verify(
- () => uniswapV3Pool.fromRpcProvider(contractAddress: yieldBPoolAddress, rpcUrl: yieldB.network.rpcUrl),
- ).called(2); // 2 because of the check in the `getSelectedPoolTick` that will re-call, and the selection
+ verify(() => poolService.getPoolTick(yieldB))
+ .called(2); // 2 because of the check in the `getSelectedPoolTick` that will re-call, and the selection
expect(sut.latestPoolTick, expectedYieldBTick);
});
@@ -366,15 +339,7 @@ void main() {
test("When calling `getSelectedPoolTick` it should emit the pool tick got", () async {
final expectedPoolTick = BigInt.from(97866745634534392);
- when(() => uniswapV3PoolImpl.slot0()).thenAnswer((_) async => (
- feeProtocol: BigInt.zero,
- observationCardinality: BigInt.zero,
- observationCardinalityNext: BigInt.zero,
- observationIndex: BigInt.zero,
- sqrtPriceX96: BigInt.zero,
- tick: expectedPoolTick,
- unlocked: true
- ));
+ when(() => poolService.getPoolTick(any())).thenAnswer((_) async => expectedPoolTick);
expectLater(sut.poolTickStream, emitsInOrder([null, expectedPoolTick]));
@@ -385,15 +350,7 @@ void main() {
test("When calling `getSelectedPoolTick` it should save the pool tick in the cubit", () async {
final expectedPoolTick = BigInt.from(97866745634534392);
- when(() => uniswapV3PoolImpl.slot0()).thenAnswer((_) async => (
- feeProtocol: BigInt.zero,
- observationCardinality: BigInt.zero,
- observationCardinalityNext: BigInt.zero,
- observationIndex: BigInt.zero,
- sqrtPriceX96: BigInt.zero,
- tick: expectedPoolTick,
- unlocked: true
- ));
+ when(() => poolService.getPoolTick(any())).thenAnswer((_) async => expectedPoolTick);
await sut.selectYield(
YieldDto.fixture(), YieldTimeFrame.day); // assuming that select yield will call `getSelectedPoolTick`
@@ -404,15 +361,7 @@ void main() {
test("When calling `getSelectedPoolTick` it should save the same tick as the emitted ", () async {
final expectedPoolTick = BigInt.from(97866745634534392);
- when(() => uniswapV3PoolImpl.slot0()).thenAnswer((_) async => (
- feeProtocol: BigInt.zero,
- observationCardinality: BigInt.zero,
- observationCardinalityNext: BigInt.zero,
- observationIndex: BigInt.zero,
- sqrtPriceX96: BigInt.zero,
- tick: expectedPoolTick,
- unlocked: true
- ));
+ when(() => poolService.getPoolTick(any())).thenAnswer((_) async => expectedPoolTick);
expectLater(sut.poolTickStream, emitsInOrder([null, expectedPoolTick]));
await sut.selectYield(
@@ -588,7 +537,7 @@ void main() {
token0Address: any(named: "token0Address"),
token1Address: any(named: "token1Address"),
network: any(named: "network"),
- minTvlUsd: any(named: "minTvlUsd"),
+ searchSettings: any(named: "searchSettings"),
)).thenAnswer((_) async => YieldsDto.fixture());
await sut.getBestPools(token0AddressOrId: "0x", token1AddressOrId: "0x", ignoreMinLiquidity: true);
@@ -597,7 +546,26 @@ void main() {
token0Address: any(named: "token0Address"),
token1Address: any(named: "token1Address"),
network: any(named: "network"),
- minTvlUsd: 0,
+ searchSettings: PoolSearchSettingsDto.fixture().copyWith(minLiquidityUSD: 0),
+ )).called(1);
+ });
+
+ test("""When calling 'getBestPools' with the current network as all networks and the param
+ 'ignoreMinLiquidity' true, it should pass the minLiquidityUSD as 0 to the repository""", () async {
+ when(() => appCubit.selectedNetwork).thenReturn(AppNetworks.allNetworks);
+ when(() => cache.getPoolSearchSettings()).thenReturn(PoolSearchSettingsDto(minLiquidityUSD: 129816));
+ when(() => yieldRepository.getAllNetworksYield(
+ token0InternalId: any(named: "token0InternalId"),
+ token1InternalId: any(named: "token1InternalId"),
+ searchSettings: any(named: "searchSettings"),
+ )).thenAnswer((_) async => YieldsDto.fixture());
+
+ await sut.getBestPools(token0AddressOrId: "0x", token1AddressOrId: "0x", ignoreMinLiquidity: true);
+
+ verify(() => yieldRepository.getAllNetworksYield(
+ token0InternalId: any(named: "token0InternalId"),
+ token1InternalId: any(named: "token1InternalId"),
+ searchSettings: PoolSearchSettingsDto.fixture().copyWith(minLiquidityUSD: 0),
)).called(1);
});
@@ -610,7 +578,7 @@ void main() {
token0Address: any(named: "token0Address"),
token1Address: any(named: "token1Address"),
network: any(named: "network"),
- minTvlUsd: any(named: "minTvlUsd"),
+ searchSettings: any(named: "searchSettings"),
)).thenAnswer((_) async => YieldsDto.fixture());
await sut.getBestPools(token0AddressOrId: "0x", token1AddressOrId: "0x", ignoreMinLiquidity: false);
@@ -619,7 +587,7 @@ void main() {
token0Address: any(named: "token0Address"),
token1Address: any(named: "token1Address"),
network: any(named: "network"),
- minTvlUsd: minLiquiditySaved,
+ searchSettings: PoolSearchSettingsDto.fixture().copyWith(minLiquidityUSD: minLiquiditySaved),
)).called(1);
});
@@ -652,7 +620,7 @@ void main() {
verify(() => yieldRepository.getAllNetworksYield(
token0InternalId: token0Address,
token1InternalId: token1Address,
- minTvlUsd: any(named: "minTvlUsd"),
+ searchSettings: any(named: "searchSettings"),
)).called(1);
});
diff --git a/test/app/create/deposit/deposit_page_test.dart b/test/app/create/deposit/deposit_page_test.dart
index 5dd6e3a..ba41504 100644
--- a/test/app/create/deposit/deposit_page_test.dart
+++ b/test/app/create/deposit/deposit_page_test.dart
@@ -9,8 +9,9 @@ import 'package:mocktail/mocktail.dart';
import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart';
import 'package:web3kit/web3kit.dart';
import 'package:zup_app/abis/erc_20.abi.g.dart';
-import 'package:zup_app/abis/uniswap_position_manager.abi.g.dart';
+import 'package:zup_app/abis/uniswap_permit2.abi.g.dart';
import 'package:zup_app/abis/uniswap_v3_pool.abi.g.dart';
+import 'package:zup_app/abis/uniswap_v3_position_manager.abi.g.dart';
import 'package:zup_app/app/app_cubit/app_cubit.dart';
import 'package:zup_app/app/create/deposit/deposit_cubit.dart';
import 'package:zup_app/app/create/deposit/deposit_page.dart';
@@ -18,14 +19,17 @@ import 'package:zup_app/app/create/deposit/widgets/preview_deposit_modal/preview
import 'package:zup_app/core/cache.dart';
import 'package:zup_app/core/dtos/deposit_settings_dto.dart';
import 'package:zup_app/core/dtos/pool_search_settings_dto.dart';
-import 'package:zup_app/core/dtos/token_dto.dart';
+import 'package:zup_app/core/dtos/token_price_dto.dart';
import 'package:zup_app/core/dtos/yield_dto.dart';
import 'package:zup_app/core/dtos/yields_dto.dart';
import 'package:zup_app/core/enums/networks.dart';
import 'package:zup_app/core/enums/zup_navigator_paths.dart';
import 'package:zup_app/core/injections.dart';
+import 'package:zup_app/core/pool_service.dart';
+import 'package:zup_app/core/repositories/tokens_repository.dart';
import 'package:zup_app/core/slippage.dart';
import 'package:zup_app/core/zup_analytics.dart';
+import 'package:zup_app/core/zup_links.dart';
import 'package:zup_app/core/zup_navigator.dart';
import 'package:zup_app/gen/assets.gen.dart';
import 'package:zup_app/widgets/zup_cached_image.dart';
@@ -42,6 +46,10 @@ void main() {
late Cache cache;
late UniswapV3Pool uniswapV3pool;
late Erc20 erc20;
+ late TokensRepository tokensRepository;
+ late ZupHolder zupHolder;
+ late PoolService poolService;
+ late UniswapPermit2 permit2;
setUp(() async {
await Web3Kit.initializeForTest();
@@ -55,6 +63,10 @@ void main() {
uniswapV3pool = UniswapV3PoolMock();
erc20 = Erc20Mock();
cache = CacheMock();
+ tokensRepository = TokensRepositoryMock();
+ zupHolder = ZupHolder();
+ poolService = PoolServiceMock();
+ permit2 = UniswapPermit2Mock();
registerFallbackValue(BuildContextMock());
registerFallbackValue(AppNetworks.sepolia);
@@ -89,6 +101,7 @@ void main() {
inject.registerFactory(() => cache);
inject.registerFactory(() => ZupAnalyticsMock());
+ inject.registerFactory(() => ZupLinksMock());
inject.registerFactory>(() => GlobalKey());
inject.registerFactory(() => navigator);
inject.registerFactory(() => wallet);
@@ -98,8 +111,13 @@ void main() {
inject.registerFactory>(() => GlobalKey());
inject.registerFactory(() => uniswapV3pool);
inject.registerFactory(() => erc20);
- inject.registerFactory(() => UniswapPositionManagerMock());
+ inject.registerFactory(() => UniswapV3PositionManagerMock());
+ inject.registerFactory(() => tokensRepository);
+ inject.registerFactory(() => zupHolder);
+ inject.registerFactory(() => poolService);
+ inject.registerFactory(() => permit2);
+ when(() => tokensRepository.getTokenPrice(any(), any())).thenAnswer((_) async => TokenPriceDto.fixture());
when(() => cubit.stream).thenAnswer((_) => const Stream.empty());
when(() => cubit.state).thenAnswer((_) => const DepositState.initial());
when(() => cubit.getBestPools(
@@ -380,25 +398,27 @@ void main() {
"When the running device is mobile, the range section should be adapted to it",
goldenFileName: "deposit_page_range_section_mobile",
(tester) async {
- final selectedYield = YieldDto.fixture();
- final yields = YieldsDto.fixture();
+ await tester.runAsync(() async {
+ final selectedYield = YieldDto.fixture();
+ final yields = YieldsDto.fixture();
- when(() => cubit.depositSettings).thenReturn(DepositSettingsDto(
- deadlineMinutes: 10,
- maxSlippage: DepositSettingsDto.defaultMaxSlippage,
- ));
+ when(() => cubit.depositSettings).thenReturn(DepositSettingsDto(
+ deadlineMinutes: 10,
+ maxSlippage: DepositSettingsDto.defaultMaxSlippage,
+ ));
- when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
- when(() => cubit.selectedYield).thenReturn(selectedYield);
- when(() => cubit.state).thenReturn(DepositState.success(yields));
- when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.state).thenReturn(DepositState.success(yields));
+ when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
- await tester.pumpDeviceBuilder(await goldenBuilder(isMobile: true));
- await tester.pumpAndSettle();
- await tester.drag(find.byKey(const Key("full-range-button")), const Offset(0, -500));
- await tester.pumpAndSettle();
+ await tester.pumpDeviceBuilder(await goldenBuilder(isMobile: true));
+ await tester.pumpAndSettle();
+ await tester.drag(find.byKey(const Key("full-range-button")), const Offset(0, -500));
+ await tester.pumpAndSettle();
- await tester.pumpAndSettle();
+ await tester.pumpAndSettle();
+ });
},
);
@@ -446,15 +466,17 @@ void main() {
zGoldenTest("When the selected yield stream in the cubit emits a yield, it should select the yield",
goldenFileName: "deposit_page_selected_yield_stream", (tester) async {
- final yields = YieldsDto.fixture();
- final selectedYield = yields.best24hYield;
+ await tester.runAsync(() async {
+ final yields = YieldsDto.fixture();
+ final selectedYield = yields.best24hYield;
- when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
- when(() => cubit.selectedYield).thenReturn(selectedYield);
- when(() => cubit.state).thenReturn(DepositState.success(yields));
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.state).thenReturn(DepositState.success(yields));
- await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.pumpAndSettle();
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.pumpAndSettle();
+ });
});
zGoldenTest("When selecting a yield, it should call select yield in the cubit", (tester) async {
@@ -471,120 +493,135 @@ void main() {
zGoldenTest("When selecting a yield, it should scroll down to the range section",
goldenFileName: "deposit_page_select_yield_scroll", (tester) async {
- final selectedYield = YieldsDto.fixture().best24hYield;
- when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
- when(() => cubit.selectedYield).thenReturn(null);
- when(() => cubit.selectYield(any(), any())).thenAnswer((_) async {});
- when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
- when(() => cubit.selectedYield).thenReturn(selectedYield);
+ await tester.runAsync(() async {
+ final selectedYield = YieldsDto.fixture().best24hYield;
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.selectedYield).thenReturn(null);
+ when(() => cubit.selectYield(any(), any())).thenAnswer((_) async {});
+ when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
- await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.tap(find.byKey(const Key("yield-card-30d")));
- await tester.pumpAndSettle();
+ await tester.tap(find.byKey(const Key("yield-card-30d")));
+ await tester.pumpAndSettle();
- verify(() => cubit.selectYield(any(), any())).called(1);
+ verify(() => cubit.selectYield(any(), any())).called(1);
+ });
});
zGoldenTest(
"When clicking the segmented control to switch the base token to quote token, it should reverse the tokens",
goldenFileName: "deposit_page_reverse_tokens", (tester) async {
- final selectedYield = YieldsDto.fixture().best24hYield;
+ await tester.runAsync(() async {
+ final selectedYield = YieldsDto.fixture().best24hYield;
- when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
- when(() => cubit.selectedYield).thenReturn(selectedYield);
- when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
- await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.tap(find.byKey(const Key("reverse-tokens-reversed")));
- await tester.pumpAndSettle();
+ await tester.tap(find.byKey(const Key("reverse-tokens-reversed")));
+ await tester.pumpAndSettle();
+ });
});
zGoldenTest(
"When clicking the segmented control to switch back to base token, after reversing the tokens, it should reverse again",
goldenFileName: "deposit_page_reverse_tokens_back", (tester) async {
- final selectedYield = YieldsDto.fixture().best24hYield;
+ await tester.runAsync(() async {
+ final selectedYield = YieldsDto.fixture().best24hYield;
- when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
- when(() => cubit.selectedYield).thenReturn(selectedYield);
- when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
- await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.tap(find.byKey(const Key("reverse-tokens-reversed")));
- await tester.pumpAndSettle();
+ await tester.tap(find.byKey(const Key("reverse-tokens-reversed")));
+ await tester.pumpAndSettle();
- await tester.tap(find.byKey(const Key("reverse-tokens-not-reversed")));
- await tester.pumpAndSettle();
+ await tester.tap(find.byKey(const Key("reverse-tokens-not-reversed")));
+ await tester.pumpAndSettle();
+ });
});
zGoldenTest(
"When clicking the segmented control to switch back to base token, after reversing the tokens, it should reverse again",
goldenFileName: "deposit_page_reverse_tokens_back", (tester) async {
- final selectedYield = YieldsDto.fixture().best24hYield;
+ await tester.runAsync(() async {
+ final selectedYield = YieldsDto.fixture().best24hYield;
- when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
- when(() => cubit.selectedYield).thenReturn(selectedYield);
- when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
- await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.tap(find.byKey(const Key("reverse-tokens-reversed")));
- await tester.pumpAndSettle();
+ await tester.tap(find.byKey(const Key("reverse-tokens-reversed")));
+ await tester.pumpAndSettle();
- await tester.tap(find.byKey(const Key("reverse-tokens-not-reversed")));
- await tester.pumpAndSettle();
+ await tester.tap(find.byKey(const Key("reverse-tokens-not-reversed")));
+ await tester.pumpAndSettle();
+ });
});
zGoldenTest("""When emitting an event to the tick stream,
it should calculate the price of the selected yield assets""", goldenFileName: "deposit_page_calculate_price",
(tester) async {
- final selectedYield = YieldsDto.fixture().best24hYield;
+ await tester.runAsync(() async {
+ final selectedYield = YieldsDto.fixture().best24hYield;
- when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
- when(() => cubit.selectedYield).thenReturn(selectedYield);
- when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
- when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(BigInt.from(174072)));
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
+ when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(BigInt.from(174072)));
- await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.pumpAndSettle();
+ });
});
zGoldenTest(
"When reversing the tokens, it should calculate the price based on the reversed tokens, from a given tick in the cubit",
goldenFileName: "deposit_page_calculate_price_reversed", (tester) async {
- final selectedYield = YieldsDto.fixture().best24hYield;
+ await tester.runAsync(() async {
+ final selectedYield = YieldsDto.fixture().best24hYield;
- when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
- when(() => cubit.selectedYield).thenReturn(selectedYield);
- when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
- when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(BigInt.from(174072)));
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
+ when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(BigInt.from(174072)));
- await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.tap(find.byKey(const Key("reverse-tokens-reversed")));
- await tester.pumpAndSettle();
+ await tester.tap(find.byKey(const Key("reverse-tokens-reversed")));
+ await tester.pumpAndSettle();
+ });
});
zGoldenTest(
"When typing a min price more than the current price, it should show an alert saying that is out of range",
goldenFileName: "deposit_page_min_price_out_of_range",
(tester) async {
- final selectedYield = YieldsDto.fixture().best24hYield;
- final currentPriceAsTick = BigInt.from(174072);
+ await tester.runAsync(() async {
+ final selectedYield = YieldsDto.fixture().best24hYield;
+ final currentPriceAsTick = BigInt.from(174072);
- when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
- when(() => cubit.selectedYield).thenReturn(selectedYield);
- when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
- when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
- when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
+ when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
+ when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
- await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.enterText(find.byKey(const Key("min-price-selector")), "1000");
- FocusManager.instance.primaryFocus?.unfocus();
+ await tester.enterText(find.byKey(const Key("min-price-selector")), "1000");
+ FocusManager.instance.primaryFocus?.unfocus();
- await tester.pumpAndSettle();
+ await tester.pumpAndSettle();
+ });
},
);
@@ -595,22 +632,24 @@ void main() {
is not out of range, it should not show the alert""",
goldenFileName: "deposit_page_min_price_out_of_range_reversed_in_range",
(tester) async {
- final selectedYield = YieldsDto.fixture().best24hYield;
- final currentPriceAsTick = BigInt.from(174072);
+ await tester.runAsync(() async {
+ final selectedYield = YieldsDto.fixture().best24hYield;
+ final currentPriceAsTick = BigInt.from(174072);
- when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
- when(() => cubit.selectedYield).thenReturn(selectedYield);
- when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
- when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
- when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
+ when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
+ when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
- await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.enterText(find.byKey(const Key("min-price-selector")), "1000");
- FocusManager.instance.primaryFocus?.unfocus();
+ await tester.enterText(find.byKey(const Key("min-price-selector")), "1000");
+ FocusManager.instance.primaryFocus?.unfocus();
- await tester.tap(find.byKey(const Key("reverse-tokens-reversed")));
- await tester.pumpAndSettle();
+ await tester.tap(find.byKey(const Key("reverse-tokens-reversed")));
+ await tester.pumpAndSettle();
+ });
},
);
@@ -621,6 +660,30 @@ void main() {
is is still out of range, it should keep showing the alert""",
goldenFileName: "deposit_page_min_price_out_of_range_reversed",
(tester) async {
+ await tester.runAsync(() async {
+ final selectedYield = YieldsDto.fixture().best24hYield;
+ final currentPriceAsTick = BigInt.from(174072);
+
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
+ when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
+ when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
+
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+
+ await tester.enterText(find.byKey(const Key("min-price-selector")), "90000000000");
+ FocusManager.instance.primaryFocus?.unfocus();
+
+ await tester.tap(find.byKey(const Key("reverse-tokens-reversed")));
+ await tester.pumpAndSettle();
+ });
+ },
+ );
+
+ zGoldenTest("When typing a max price less than the min price, it should show an error message",
+ goldenFileName: "deposit_page_max_price_less_than_min_price", (tester) async {
+ await tester.runAsync(() async {
final selectedYield = YieldsDto.fixture().best24hYield;
final currentPriceAsTick = BigInt.from(174072);
@@ -632,191 +695,193 @@ void main() {
await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.enterText(find.byKey(const Key("min-price-selector")), "90000000000");
+ await tester.enterText(find.byKey(const Key("min-price-selector")), "1200");
FocusManager.instance.primaryFocus?.unfocus();
- await tester.tap(find.byKey(const Key("reverse-tokens-reversed")));
await tester.pumpAndSettle();
- },
- );
-
- zGoldenTest("When typing a max price less than the min price, it should show an error message",
- goldenFileName: "deposit_page_max_price_less_than_min_price", (tester) async {
- final selectedYield = YieldsDto.fixture().best24hYield;
- final currentPriceAsTick = BigInt.from(174072);
-
- when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
- when(() => cubit.selectedYield).thenReturn(selectedYield);
- when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
- when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
- when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
- await tester.pumpDeviceBuilder(await goldenBuilder());
-
- await tester.enterText(find.byKey(const Key("min-price-selector")), "1200");
- FocusManager.instance.primaryFocus?.unfocus();
+ await tester.enterText(find.byKey(const Key("max-price-selector")), "1000");
+ FocusManager.instance.primaryFocus?.unfocus();
- await tester.enterText(find.byKey(const Key("max-price-selector")), "1000");
- FocusManager.instance.primaryFocus?.unfocus();
+ await tester.pumpAndSettle();
+ });
});
zGoldenTest("""When typing a max price lower than the current price
but higher than min price, it shouw show a alert of out of range""",
goldenFileName: "deposit_page_max_price_out_of_range", (tester) async {
- final selectedYield = YieldsDto.fixture().best24hYield;
- final currentPriceAsTick = BigInt.from(174072);
+ await tester.runAsync(() async {
+ final selectedYield = YieldsDto.fixture().best24hYield;
+ final currentPriceAsTick = BigInt.from(174072);
- when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
- when(() => cubit.selectedYield).thenReturn(selectedYield);
- when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
- when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
- when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
+ when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
+ when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
- await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.enterText(find.byKey(const Key("min-price-selector")), "0.000000001");
- FocusManager.instance.primaryFocus?.unfocus();
+ await tester.enterText(find.byKey(const Key("min-price-selector")), "0.000000001");
+ FocusManager.instance.primaryFocus?.unfocus();
+ await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("max-price-selector")), "0.0000001");
- FocusManager.instance.primaryFocus?.unfocus();
+ await tester.enterText(find.byKey(const Key("max-price-selector")), "0.0000001");
+ FocusManager.instance.primaryFocus?.unfocus();
+ await tester.pumpAndSettle();
+ });
});
zGoldenTest("When typing 0 in the max price, it should set it to infinity max price",
goldenFileName: "deposit_page_max_price_set_to_infinity", (tester) async {
- final selectedYield = YieldsDto.fixture().best24hYield;
- final currentPriceAsTick = BigInt.from(174072);
+ await tester.runAsync(() async {
+ final selectedYield = YieldsDto.fixture().best24hYield;
+ final currentPriceAsTick = BigInt.from(174072);
- when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
- when(() => cubit.selectedYield).thenReturn(selectedYield);
- when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
- when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
- when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
+ when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
+ when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
- await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.enterText(find.byKey(const Key("min-price-selector")), "1");
- FocusManager.instance.primaryFocus?.unfocus();
+ await tester.enterText(find.byKey(const Key("min-price-selector")), "1");
+ FocusManager.instance.primaryFocus?.unfocus();
+ await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("max-price-selector")), "2");
- FocusManager.instance.primaryFocus?.unfocus();
+ await tester.enterText(find.byKey(const Key("max-price-selector")), "2");
+ FocusManager.instance.primaryFocus?.unfocus();
+ await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("max-price-selector")), "");
- await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("max-price-selector")), "0");
- FocusManager.instance.primaryFocus?.unfocus();
+ await tester.enterText(find.byKey(const Key("max-price-selector")), "");
+ await tester.pumpAndSettle();
+ await tester.enterText(find.byKey(const Key("max-price-selector")), "0");
+ FocusManager.instance.primaryFocus?.unfocus();
- await tester.pumpAndSettle();
+ await tester.pumpAndSettle();
+ });
});
zGoldenTest("When typing a min price, but then selecting the full range button, it should set it to 0",
goldenFileName: "deposit_page_min_price_set_to_full_range", (tester) async {
- final selectedYield = YieldsDto.fixture().best24hYield;
- final currentPriceAsTick = BigInt.from(174072);
+ await tester.runAsync(() async {
+ final selectedYield = YieldsDto.fixture().best24hYield;
+ final currentPriceAsTick = BigInt.from(174072);
- when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
- when(() => cubit.selectedYield).thenReturn(selectedYield);
- when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
- when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
- when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
+ when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
+ when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
- await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.enterText(find.byKey(const Key("min-price-selector")), "1");
- FocusManager.instance.primaryFocus?.unfocus();
+ await tester.enterText(find.byKey(const Key("min-price-selector")), "1");
+ FocusManager.instance.primaryFocus?.unfocus();
- await tester.tap(find.byKey(const Key("full-range-button")));
- await tester.pumpAndSettle();
+ await tester.tap(find.byKey(const Key("full-range-button")));
+ await tester.pumpAndSettle();
+ });
});
zGoldenTest("When typing a max price, but then selecting the full range button, it should set it to infinity",
goldenFileName: "deposit_page_max_price_set_to_full_range", (tester) async {
- final selectedYield = YieldsDto.fixture().best24hYield;
- final currentPriceAsTick = BigInt.from(174072);
+ await tester.runAsync(() async {
+ final selectedYield = YieldsDto.fixture().best24hYield;
+ final currentPriceAsTick = BigInt.from(174072);
- when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
- when(() => cubit.selectedYield).thenReturn(selectedYield);
- when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
- when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
- when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
+ when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
+ when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
- await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.enterText(find.byKey(const Key("max-price-selector")), "1");
- FocusManager.instance.primaryFocus?.unfocus();
+ await tester.enterText(find.byKey(const Key("max-price-selector")), "1");
+ FocusManager.instance.primaryFocus?.unfocus();
- await tester.tap(find.byKey(const Key("full-range-button")));
- await tester.pumpAndSettle();
+ await tester.tap(find.byKey(const Key("full-range-button")));
+ await tester.pumpAndSettle();
+ });
});
zGoldenTest("""When typing a min and max price and then clicking the full range button,
it should set the min price to 0 and the max price to infinity""",
goldenFileName: "deposit_page_min_and_max_price_set_to_full_range", (tester) async {
- final selectedYield = YieldsDto.fixture().best24hYield;
- final currentPriceAsTick = BigInt.from(174072);
-
- when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
- when(() => cubit.selectedYield).thenReturn(selectedYield);
- when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
- when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
- when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
-
- await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.runAsync(() async {
+ final selectedYield = YieldsDto.fixture().best24hYield;
+ final currentPriceAsTick = BigInt.from(174072);
- await tester.enterText(find.byKey(const Key("min-price-selector")), "1");
- FocusManager.instance.primaryFocus?.unfocus();
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
+ when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
+ when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
- await tester.enterText(find.byKey(const Key("max-price-selector")), "2");
- FocusManager.instance.primaryFocus?.unfocus();
+ await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.tap(find.byKey(const Key("full-range-button")));
- await tester.pumpAndSettle();
+ await tester.enterText(find.byKey(const Key("min-price-selector")), "1");
+ FocusManager.instance.primaryFocus?.unfocus();
+
+ await tester.enterText(find.byKey(const Key("max-price-selector")), "2");
+ FocusManager.instance.primaryFocus?.unfocus();
+
+ await tester.tap(find.byKey(const Key("full-range-button")));
+ await tester.pumpAndSettle();
+ });
});
zGoldenTest(
"When there's a invalid range, the deposit section should be disabled (with opacity) and cannot be clicked or typed",
goldenFileName: "deposit_page_invalid_range_deposit_section", (tester) async {
- final selectedYield = YieldsDto.fixture().best24hYield;
- final currentPriceAsTick = BigInt.from(174072);
+ await tester.runAsync(() async {
+ final selectedYield = YieldsDto.fixture().best24hYield;
+ final currentPriceAsTick = BigInt.from(174072);
- when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
- when(() => cubit.selectedYield).thenReturn(selectedYield);
- when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
- when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
- when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
+ when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
+ when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
- await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.enterText(find.byKey(const Key("min-price-selector")), "2");
- FocusManager.instance.primaryFocus?.unfocus();
+ await tester.enterText(find.byKey(const Key("min-price-selector")), "2");
+ FocusManager.instance.primaryFocus?.unfocus();
- await tester.enterText(find.byKey(const Key("max-price-selector")), "1");
- FocusManager.instance.primaryFocus?.unfocus();
+ await tester.enterText(find.byKey(const Key("max-price-selector")), "1");
+ FocusManager.instance.primaryFocus?.unfocus();
- await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
- await tester.pumpAndSettle();
- await tester.tap(find.byKey(const Key("deposit-button")));
- await tester.pumpAndSettle();
+ await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
+ await tester.pumpAndSettle();
+ await tester.tap(find.byKey(const Key("deposit-button")));
+ await tester.pumpAndSettle();
+ });
});
zGoldenTest(
"When inputing the base token amount, the quote amount token should be automatically calculated",
goldenFileName: "deposit_page_input_base_token_amount",
(tester) async {
- final selectedYield = YieldsDto.fixture().best24hYield;
- final currentPriceAsTick = BigInt.from(174072);
+ await tester.runAsync(() async {
+ final selectedYield = YieldsDto.fixture().best24hYield;
+ final currentPriceAsTick = BigInt.from(174072);
- when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
- when(() => cubit.selectedYield).thenReturn(selectedYield);
- when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
- when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
- when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
+ when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
+ when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
- await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
- await tester.pumpAndSettle();
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
+ await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("base-token-input-card")), "1");
- await tester.pumpAndSettle();
+ await tester.enterText(find.byKey(const Key("base-token-input-card")), "1");
+ await tester.pumpAndSettle();
+ });
},
);
@@ -824,21 +889,23 @@ void main() {
"When inputing the quote token amount, the base amount token should be automatically calculated",
goldenFileName: "deposit_page_input_quote_token_amount",
(tester) async {
- final selectedYield = YieldsDto.fixture().best24hYield;
- final currentPriceAsTick = BigInt.from(174072);
+ await tester.runAsync(() async {
+ final selectedYield = YieldsDto.fixture().best24hYield;
+ final currentPriceAsTick = BigInt.from(174072);
- when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
- when(() => cubit.selectedYield).thenReturn(selectedYield);
- when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
- when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
- when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
+ when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
+ when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
- await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
- await tester.pumpAndSettle();
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
+ await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("quote-token-input-card")), "1");
- await tester.pumpAndSettle();
+ await tester.enterText(find.byKey(const Key("quote-token-input-card")), "1");
+ await tester.pumpAndSettle();
+ });
},
);
@@ -848,24 +915,26 @@ void main() {
and the new base token amount should be automatically calculated""",
goldenFileName: "deposit_page_input_base_token_amount_and_reverse",
(tester) async {
- final selectedYield = YieldsDto.fixture().best24hYield;
- final currentPriceAsTick = BigInt.from(174072);
+ await tester.runAsync(() async {
+ final selectedYield = YieldsDto.fixture().best24hYield;
+ final currentPriceAsTick = BigInt.from(174072);
- when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
- when(() => cubit.selectedYield).thenReturn(selectedYield);
- when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
- when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
- when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
+ when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
+ when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
- await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
- await tester.pumpAndSettle();
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
+ await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("base-token-input-card")), "1");
- await tester.pumpAndSettle();
+ await tester.enterText(find.byKey(const Key("base-token-input-card")), "1");
+ await tester.pumpAndSettle();
- await tester.tap(find.byKey(const Key("reverse-tokens-reversed")));
- await tester.pumpAndSettle();
+ await tester.tap(find.byKey(const Key("reverse-tokens-reversed")));
+ await tester.pumpAndSettle();
+ });
},
);
@@ -875,24 +944,26 @@ void main() {
and the new quote token amount should be automatically calculated""",
goldenFileName: "deposit_page_input_quote_token_amount_and_reverse",
(tester) async {
- final selectedYield = YieldsDto.fixture().best24hYield;
- final currentPriceAsTick = BigInt.from(174072);
+ await tester.runAsync(() async {
+ final selectedYield = YieldsDto.fixture().best24hYield;
+ final currentPriceAsTick = BigInt.from(174072);
- when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
- when(() => cubit.selectedYield).thenReturn(selectedYield);
- when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
- when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
- when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
+ when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
+ when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
- await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
- await tester.pumpAndSettle();
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
+ await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("quote-token-input-card")), "1");
- await tester.pumpAndSettle();
+ await tester.enterText(find.byKey(const Key("quote-token-input-card")), "1");
+ await tester.pumpAndSettle();
- await tester.tap(find.byKey(const Key("reverse-tokens-reversed")));
- await tester.pumpAndSettle();
+ await tester.tap(find.byKey(const Key("reverse-tokens-reversed")));
+ await tester.pumpAndSettle();
+ });
},
);
@@ -900,24 +971,26 @@ void main() {
"""When inputing the base token amount with the tokens reversed, the quote token amount should be automatically calculated""",
goldenFileName: "deposit_page_input_base_token_amount_reversed",
(tester) async {
- final selectedYield = YieldsDto.fixture().best24hYield;
- final currentPriceAsTick = BigInt.from(174072);
+ await tester.runAsync(() async {
+ final selectedYield = YieldsDto.fixture().best24hYield;
+ final currentPriceAsTick = BigInt.from(174072);
- when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
- when(() => cubit.selectedYield).thenReturn(selectedYield);
- when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
- when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
- when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
+ when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
+ when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
- await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
- await tester.pumpAndSettle();
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
+ await tester.pumpAndSettle();
- await tester.tap(find.byKey(const Key("reverse-tokens-reversed")));
- await tester.pumpAndSettle();
+ await tester.tap(find.byKey(const Key("reverse-tokens-reversed")));
+ await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("base-token-input-card")), "1");
- await tester.pumpAndSettle();
+ await tester.enterText(find.byKey(const Key("base-token-input-card")), "1");
+ await tester.pumpAndSettle();
+ });
},
);
@@ -925,24 +998,26 @@ void main() {
"""When inputing the quote token amount with the tokens reversed, the base token amount should be automatically calculated""",
goldenFileName: "deposit_page_input_quote_token_amount_reversed",
(tester) async {
- final selectedYield = YieldsDto.fixture().best24hYield;
- final currentPriceAsTick = BigInt.from(174072);
+ await tester.runAsync(() async {
+ final selectedYield = YieldsDto.fixture().best24hYield;
+ final currentPriceAsTick = BigInt.from(174072);
- when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
- when(() => cubit.selectedYield).thenReturn(selectedYield);
- when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
- when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
- when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
+ when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
+ when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
- await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
- await tester.pumpAndSettle();
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
+ await tester.pumpAndSettle();
- await tester.tap(find.byKey(const Key("reverse-tokens-reversed")));
- await tester.pumpAndSettle();
+ await tester.tap(find.byKey(const Key("reverse-tokens-reversed")));
+ await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("quote-token-input-card")), "1");
- await tester.pumpAndSettle();
+ await tester.enterText(find.byKey(const Key("quote-token-input-card")), "1");
+ await tester.pumpAndSettle();
+ });
},
);
@@ -950,60 +1025,7 @@ void main() {
then turning them normal, the quote token amount should now be the
previous base token amount, and the new base token amount should be automatically calculated""",
goldenFileName: "deposit_page_input_base_token_amount_and_reverse_back", (tester) async {
- final selectedYield = YieldsDto.fixture().best24hYield;
- final currentPriceAsTick = BigInt.from(174072);
-
- when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
- when(() => cubit.selectedYield).thenReturn(selectedYield);
- when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
- when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
- when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
-
- await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
- await tester.pumpAndSettle();
-
- await tester.tap(find.byKey(const Key("reverse-tokens-reversed")));
- await tester.pumpAndSettle();
-
- await tester.enterText(find.byKey(const Key("base-token-input-card")), "1");
- await tester.pumpAndSettle();
-
- await tester.tap(find.byKey(const Key("reverse-tokens-not-reversed")));
- await tester.pumpAndSettle();
- });
-
- zGoldenTest("""When inputing the quote token amount with the tokens reversed,
- then turning them normal, the base token amount should now be the
- previous quote token amount, and the new quote token amount should be automatically calculated""",
- goldenFileName: "deposit_page_input_quote_token_amount_and_reverse_back", (tester) async {
- final selectedYield = YieldsDto.fixture().best24hYield;
- final currentPriceAsTick = BigInt.from(174072);
-
- when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
- when(() => cubit.selectedYield).thenReturn(selectedYield);
- when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
- when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
- when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
-
- await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
- await tester.pumpAndSettle();
-
- await tester.tap(find.byKey(const Key("reverse-tokens-reversed")));
- await tester.pumpAndSettle();
-
- await tester.enterText(find.byKey(const Key("quote-token-input-card")), "1");
- await tester.pumpAndSettle();
-
- await tester.tap(find.byKey(const Key("reverse-tokens-not-reversed")));
- await tester.pumpAndSettle();
- });
-
- zGoldenTest(
- "When inputing the base token amount, then changing the range, the quote token amount should be recalculated",
- goldenFileName: "deposit_page_input_base_token_amount_and_change_range",
- (tester) async {
+ await tester.runAsync(() async {
final selectedYield = YieldsDto.fixture().best24hYield;
final currentPriceAsTick = BigInt.from(174072);
@@ -1017,25 +1039,22 @@ void main() {
await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("base-token-input-card")), "1");
- await tester.pumpAndSettle();
-
- await tester.enterText(find.byKey(const Key("min-price-selector")), "0.00000001");
+ await tester.tap(find.byKey(const Key("reverse-tokens-reversed")));
await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("max-price-selector")), "0");
+ await tester.enterText(find.byKey(const Key("base-token-input-card")), "1");
await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("max-price-selector")), "3");
+ await tester.tap(find.byKey(const Key("reverse-tokens-not-reversed")));
await tester.pumpAndSettle();
- FocusManager.instance.primaryFocus?.unfocus();
- },
- );
+ });
+ });
- zGoldenTest(
- "When inputing the quote token amount, then changing the range, the base token amount should be recalculated",
- goldenFileName: "deposit_page_input_quote_token_amount_and_change_range",
- (tester) async {
+ zGoldenTest("""When inputing the quote token amount with the tokens reversed,
+ then turning them normal, the base token amount should now be the
+ previous quote token amount, and the new quote token amount should be automatically calculated""",
+ goldenFileName: "deposit_page_input_quote_token_amount_and_reverse_back", (tester) async {
+ await tester.runAsync(() async {
final selectedYield = YieldsDto.fixture().best24hYield;
final currentPriceAsTick = BigInt.from(174072);
@@ -1049,94 +1068,93 @@ void main() {
await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("quote-token-input-card")), "1");
+ await tester.tap(find.byKey(const Key("reverse-tokens-reversed")));
await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("min-price-selector")), "0.00000001");
+ await tester.enterText(find.byKey(const Key("quote-token-input-card")), "1");
await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("max-price-selector")), "0");
+ await tester.tap(find.byKey(const Key("reverse-tokens-not-reversed")));
await tester.pumpAndSettle();
-
- await tester.enterText(find.byKey(const Key("max-price-selector")), "3");
- await tester.pumpAndSettle();
-
- FocusManager.instance.primaryFocus?.unfocus();
- },
- );
+ });
+ });
zGoldenTest(
- "When inputing the base token amount, reversing the tokens and then changing the range, the base token amount should be recalculated",
- goldenFileName: "deposit_page_input_base_token_amount_reverse_tokens_and_change_range", (tester) async {
- final selectedYield = YieldsDto.fixture().best24hYield;
- final currentPriceAsTick = BigInt.from(174072);
-
- when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
- when(() => cubit.selectedYield).thenReturn(selectedYield);
- when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
- when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
- when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
+ "When inputing the base token amount, then changing the range, the quote token amount should be recalculated",
+ goldenFileName: "deposit_page_input_base_token_amount_and_change_range",
+ (tester) async {
+ await tester.runAsync(() async {
+ final selectedYield = YieldsDto.fixture().best24hYield;
+ final currentPriceAsTick = BigInt.from(174072);
- await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
- await tester.pumpAndSettle();
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
+ when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
+ when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
- await tester.enterText(find.byKey(const Key("base-token-input-card")), "1");
- await tester.pumpAndSettle();
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
+ await tester.pumpAndSettle();
- await tester.tap(find.byKey(const Key("reverse-tokens-reversed")));
- await tester.pumpAndSettle();
+ await tester.enterText(find.byKey(const Key("base-token-input-card")), "1");
+ await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("min-price-selector")), "1200");
- await tester.pumpAndSettle();
+ await tester.enterText(find.byKey(const Key("min-price-selector")), "0.00000001");
+ await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("max-price-selector")), "0");
- await tester.pumpAndSettle();
+ await tester.enterText(find.byKey(const Key("max-price-selector")), "0");
+ await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("max-price-selector")), "90000");
- await tester.pumpAndSettle();
+ await tester.enterText(find.byKey(const Key("max-price-selector")), "3");
+ await tester.pumpAndSettle();
- FocusManager.instance.primaryFocus?.unfocus();
- });
+ FocusManager.instance.primaryFocus?.unfocus();
+ await tester.pumpAndSettle();
+ });
+ },
+ );
zGoldenTest(
- "When inputing the quote token amount, reversing the tokens and then changing the range, the quote token amount should be recalculated",
- goldenFileName: "deposit_page_input_quote_token_amount_reverse_tokens_and_change_range", (tester) async {
- final selectedYield = YieldsDto.fixture().best24hYield;
- final currentPriceAsTick = BigInt.from(174072);
-
- when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
- when(() => cubit.selectedYield).thenReturn(selectedYield);
- when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
- when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
- when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
+ "When inputing the quote token amount, then changing the range, the base token amount should be recalculated",
+ goldenFileName: "deposit_page_input_quote_token_amount_and_change_range",
+ (tester) async {
+ await tester.runAsync(() async {
+ final selectedYield = YieldsDto.fixture().best24hYield;
+ final currentPriceAsTick = BigInt.from(174072);
- await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
- await tester.pumpAndSettle();
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
+ when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
+ when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
- await tester.enterText(find.byKey(const Key("quote-token-input-card")), "1");
- await tester.pumpAndSettle();
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
+ await tester.pumpAndSettle();
- await tester.tap(find.byKey(const Key("reverse-tokens-reversed")));
- await tester.pumpAndSettle();
+ await tester.enterText(find.byKey(const Key("quote-token-input-card")), "1");
+ await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("min-price-selector")), "1200");
- await tester.pumpAndSettle();
+ await tester.enterText(find.byKey(const Key("min-price-selector")), "0.00000001");
+ await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("max-price-selector")), "0");
- await tester.pumpAndSettle();
+ await tester.enterText(find.byKey(const Key("max-price-selector")), "0");
+ await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("max-price-selector")), "90000");
- await tester.pumpAndSettle();
+ await tester.enterText(find.byKey(const Key("max-price-selector")), "3");
+ await tester.pumpAndSettle();
- FocusManager.instance.primaryFocus?.unfocus();
- });
+ FocusManager.instance.primaryFocus?.unfocus();
+ await tester.pumpAndSettle();
+ });
+ },
+ );
zGoldenTest(
- "When inputing a range, then inputing the base token amount, the quote token amount should be automatically calculated",
- goldenFileName: "deposit_page_input_range_then_input_base_token_amount",
- (tester) async {
+ "When inputing the base token amount, reversing the tokens and then changing the range, the base token amount should be recalculated",
+ goldenFileName: "deposit_page_input_base_token_amount_reverse_tokens_and_change_range", (tester) async {
+ await tester.runAsync(() async {
final selectedYield = YieldsDto.fixture().best24hYield;
final currentPriceAsTick = BigInt.from(174072);
@@ -1150,26 +1168,30 @@ void main() {
await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("min-price-selector")), "0.000001");
+ await tester.enterText(find.byKey(const Key("base-token-input-card")), "1");
await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("max-price-selector")), "0");
+ await tester.tap(find.byKey(const Key("reverse-tokens-reversed")));
await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("max-price-selector")), "3");
+ await tester.enterText(find.byKey(const Key("min-price-selector")), "1200");
await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("base-token-input-card")), "1");
+ await tester.enterText(find.byKey(const Key("max-price-selector")), "0");
+ await tester.pumpAndSettle();
+
+ await tester.enterText(find.byKey(const Key("max-price-selector")), "90000");
await tester.pumpAndSettle();
FocusManager.instance.primaryFocus?.unfocus();
- },
- );
+ await tester.pumpAndSettle();
+ });
+ });
zGoldenTest(
- "When inputing a range, then inputing the quote token amount, the base token amount should be automatically calculated",
- goldenFileName: "deposit_page_input_range_then_input_quote_token_amount",
- (tester) async {
+ "When inputing the quote token amount, reversing the tokens and then changing the range, the quote token amount should be recalculated",
+ goldenFileName: "deposit_page_input_quote_token_amount_reverse_tokens_and_change_range", (tester) async {
+ await tester.runAsync(() async {
final selectedYield = YieldsDto.fixture().best24hYield;
final currentPriceAsTick = BigInt.from(174072);
@@ -1183,19 +1205,94 @@ void main() {
await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("min-price-selector")), "0.000001");
+ await tester.enterText(find.byKey(const Key("quote-token-input-card")), "1");
await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("max-price-selector")), "0");
+ await tester.tap(find.byKey(const Key("reverse-tokens-reversed")));
await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("max-price-selector")), "3");
+ await tester.enterText(find.byKey(const Key("min-price-selector")), "1200");
await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("quote-token-input-card")), "1");
+ await tester.enterText(find.byKey(const Key("max-price-selector")), "0");
+ await tester.pumpAndSettle();
+
+ await tester.enterText(find.byKey(const Key("max-price-selector")), "90000");
await tester.pumpAndSettle();
FocusManager.instance.primaryFocus?.unfocus();
+ await tester.pumpAndSettle();
+ });
+ });
+
+ zGoldenTest(
+ "When inputing a range, then inputing the base token amount, the quote token amount should be automatically calculated",
+ goldenFileName: "deposit_page_input_range_then_input_base_token_amount",
+ (tester) async {
+ await tester.runAsync(() async {
+ final selectedYield = YieldsDto.fixture().best24hYield;
+ final currentPriceAsTick = BigInt.from(174072);
+
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
+ when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
+ when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
+
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
+ await tester.pumpAndSettle();
+
+ await tester.enterText(find.byKey(const Key("min-price-selector")), "0.000001");
+ await tester.pumpAndSettle();
+
+ await tester.enterText(find.byKey(const Key("max-price-selector")), "0");
+ await tester.pumpAndSettle();
+
+ await tester.enterText(find.byKey(const Key("max-price-selector")), "3");
+ await tester.pumpAndSettle();
+
+ await tester.enterText(find.byKey(const Key("base-token-input-card")), "1");
+ await tester.pumpAndSettle();
+
+ FocusManager.instance.primaryFocus?.unfocus();
+ await tester.pumpAndSettle();
+ });
+ },
+ );
+
+ zGoldenTest(
+ "When inputing a range, then inputing the quote token amount, the base token amount should be automatically calculated",
+ goldenFileName: "deposit_page_input_range_then_input_quote_token_amount",
+ (tester) async {
+ await tester.runAsync(() async {
+ final selectedYield = YieldsDto.fixture().best24hYield;
+ final currentPriceAsTick = BigInt.from(174072);
+
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
+ when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
+ when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
+
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
+ await tester.pumpAndSettle();
+
+ await tester.enterText(find.byKey(const Key("min-price-selector")), "0.000001");
+ await tester.pumpAndSettle();
+
+ await tester.enterText(find.byKey(const Key("max-price-selector")), "0");
+ await tester.pumpAndSettle();
+
+ await tester.enterText(find.byKey(const Key("max-price-selector")), "3");
+ await tester.pumpAndSettle();
+
+ await tester.enterText(find.byKey(const Key("quote-token-input-card")), "1");
+ await tester.pumpAndSettle();
+
+ FocusManager.instance.primaryFocus?.unfocus();
+ });
},
);
@@ -1203,37 +1300,41 @@ void main() {
"When inputing a range,reversing the tokens, then inputing the base token amount, the quote token amount should be automatically calculated",
goldenFileName: "deposit_page_input_range_then_reverse_tokens_then_input_base_token_amount",
(tester) async {
- final selectedYield = YieldsDto.fixture().best24hYield;
- final currentPriceAsTick = BigInt.from(174072);
+ await tester.runAsync(() async {
+ final selectedYield = YieldsDto.fixture().best24hYield;
+ final currentPriceAsTick = BigInt.from(174072);
- when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
- when(() => cubit.selectedYield).thenReturn(selectedYield);
- when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
- when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
- when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
+ when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
+ when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
- await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
- await tester.pumpAndSettle();
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
+ await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("min-price-selector")), "1200");
- await tester.pumpAndSettle();
+ await tester.enterText(find.byKey(const Key("min-price-selector")), "1200");
+ await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("max-price-selector")), "0");
- await tester.pumpAndSettle();
+ await tester.enterText(find.byKey(const Key("max-price-selector")), "0");
+ await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("max-price-selector")), "90000");
- await tester.pumpAndSettle();
+ await tester.enterText(find.byKey(const Key("max-price-selector")), "90000");
+ await tester.pumpAndSettle();
- FocusManager.instance.primaryFocus?.unfocus(); // unfocus the fields to calculate the valid price
+ FocusManager.instance.primaryFocus?.unfocus(); // unfocus the fields to calculate the valid price
+ await tester.pumpAndSettle();
- await tester.tap(find.byKey(const Key("reverse-tokens-reversed")));
- await tester.pumpAndSettle();
+ await tester.tap(find.byKey(const Key("reverse-tokens-reversed")));
+ await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("base-token-input-card")), "1");
- await tester.pumpAndSettle();
+ await tester.enterText(find.byKey(const Key("base-token-input-card")), "1");
+ await tester.pumpAndSettle();
- FocusManager.instance.primaryFocus?.unfocus();
+ FocusManager.instance.primaryFocus?.unfocus();
+ await tester.pumpAndSettle();
+ });
},
);
@@ -1241,6 +1342,47 @@ void main() {
"When inputing a range, reversing the tokens, then inputing the quote token amount, the base token amount should be automatically calculated",
goldenFileName: "deposit_page_input_range_then_reverse_tokens_then_input_quote_token_amount",
(tester) async {
+ await tester.runAsync(() async {
+ final selectedYield = YieldsDto.fixture().best24hYield;
+ final currentPriceAsTick = BigInt.from(174072);
+
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
+ when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
+ when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
+
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
+ await tester.pumpAndSettle();
+
+ await tester.enterText(find.byKey(const Key("min-price-selector")), "1200");
+ await tester.pumpAndSettle();
+
+ await tester.enterText(find.byKey(const Key("max-price-selector")), "0");
+ await tester.pumpAndSettle();
+
+ await tester.enterText(find.byKey(const Key("max-price-selector")), "90000");
+ await tester.pumpAndSettle();
+
+ FocusManager.instance.primaryFocus?.unfocus(); // unfocus the fields to calculate the valid price
+
+ await tester.tap(find.byKey(const Key("reverse-tokens-reversed")));
+ await tester.pumpAndSettle();
+
+ await tester.enterText(find.byKey(const Key("quote-token-input-card")), "1");
+ await tester.pumpAndSettle();
+
+ FocusManager.instance.primaryFocus?.unfocus();
+ await tester.pumpAndSettle();
+ });
+ },
+ );
+
+ zGoldenTest("""When inputing base token amount, and then setting a max price out of range,
+ it should keep the quote token amount and disable the base token input""",
+ goldenFileName: "deposit_page_input_base_token_amount_then_set_max_price_out_of_range", (tester) async {
+ await tester.runAsync(() async {
final selectedYield = YieldsDto.fixture().best24hYield;
final currentPriceAsTick = BigInt.from(174072);
@@ -1254,159 +1396,135 @@ void main() {
await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("min-price-selector")), "1200");
+ await tester.enterText(find.byKey(const Key("base-token-input-card")), "1");
await tester.pumpAndSettle();
await tester.enterText(find.byKey(const Key("max-price-selector")), "0");
await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("max-price-selector")), "90000");
+ await tester.enterText(find.byKey(const Key("max-price-selector")), "0.00000001");
await tester.pumpAndSettle();
- FocusManager.instance.primaryFocus?.unfocus(); // unfocus the fields to calculate the valid price
-
- await tester.tap(find.byKey(const Key("reverse-tokens-reversed")));
+ FocusManager.instance.primaryFocus?.unfocus();
await tester.pumpAndSettle();
-
- await tester.enterText(find.byKey(const Key("quote-token-input-card")), "1");
- await tester.pumpAndSettle();
-
- FocusManager.instance.primaryFocus?.unfocus();
- },
- );
-
- zGoldenTest("""When inputing base token amount, and then setting a max price out of range,
- it should keep the quote token amount and disable the base token input""",
- goldenFileName: "deposit_page_input_base_token_amount_then_set_max_price_out_of_range", (tester) async {
- final selectedYield = YieldsDto.fixture().best24hYield;
- final currentPriceAsTick = BigInt.from(174072);
-
- when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
- when(() => cubit.selectedYield).thenReturn(selectedYield);
- when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
- when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
- when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
-
- await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
- await tester.pumpAndSettle();
-
- await tester.enterText(find.byKey(const Key("base-token-input-card")), "1");
- await tester.pumpAndSettle();
-
- await tester.enterText(find.byKey(const Key("max-price-selector")), "0");
- await tester.pumpAndSettle();
-
- await tester.enterText(find.byKey(const Key("max-price-selector")), "0.00000001");
- await tester.pumpAndSettle();
-
- FocusManager.instance.primaryFocus?.unfocus();
+ });
});
zGoldenTest("""When inputing quote token amount, and then setting a min price out of range,
it should keep the base token amount and disable the quote token input""",
goldenFileName: "deposit_page_input_quote_token_amount_then_set_min_price_out_of_range", (tester) async {
- final selectedYield = YieldsDto.fixture().best24hYield;
- final currentPriceAsTick = BigInt.from(174072);
+ await tester.runAsync(() async {
+ final selectedYield = YieldsDto.fixture().best24hYield;
+ final currentPriceAsTick = BigInt.from(174072);
- when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
- when(() => cubit.selectedYield).thenReturn(selectedYield);
- when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
- when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
- when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
+ when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
+ when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
- await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
- await tester.pumpAndSettle();
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
+ await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("quote-token-input-card")), "1");
- await tester.pumpAndSettle();
+ await tester.enterText(find.byKey(const Key("quote-token-input-card")), "1");
+ await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("min-price-selector")), "2");
- await tester.pumpAndSettle();
+ await tester.enterText(find.byKey(const Key("min-price-selector")), "2");
+ await tester.pumpAndSettle();
- FocusManager.instance.primaryFocus?.unfocus();
+ FocusManager.instance.primaryFocus?.unfocus();
+ await tester.pumpAndSettle();
+ });
});
zGoldenTest("""When inputing base token amount, reversing the tokens, and then setting a max price out of range,
it should keep the quote token amount and disable the base token input""",
goldenFileName: "deposit_page_input_base_token_amount_then_reverse_tokens_then_set_max_price_out_of_range",
(tester) async {
- final selectedYield = YieldsDto.fixture().best24hYield;
- final currentPriceAsTick = BigInt.from(174072);
+ await tester.runAsync(() async {
+ final selectedYield = YieldsDto.fixture().best24hYield;
+ final currentPriceAsTick = BigInt.from(174072);
- when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
- when(() => cubit.selectedYield).thenReturn(selectedYield);
- when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
- when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
- when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
+ when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
+ when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
- await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
- await tester.pumpAndSettle();
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
+ await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("base-token-input-card")), "1");
- await tester.pumpAndSettle();
+ await tester.enterText(find.byKey(const Key("base-token-input-card")), "1");
+ await tester.pumpAndSettle();
- await tester.tap(find.byKey(const Key("reverse-tokens-reversed")));
- await tester.pumpAndSettle();
+ await tester.tap(find.byKey(const Key("reverse-tokens-reversed")));
+ await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("max-price-selector")), "0");
- await tester.pumpAndSettle();
+ await tester.enterText(find.byKey(const Key("max-price-selector")), "0");
+ await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("max-price-selector")), "3");
- await tester.pumpAndSettle();
+ await tester.enterText(find.byKey(const Key("max-price-selector")), "3");
+ await tester.pumpAndSettle();
- FocusManager.instance.primaryFocus?.unfocus();
+ FocusManager.instance.primaryFocus?.unfocus();
+ await tester.pumpAndSettle();
+ });
});
zGoldenTest("""When inputing quote token amount, reversing the tokens, and then setting a min price out of range,
it should keep the base token amount and disable the quote token input""",
goldenFileName: "deposit_page_input_quote_token_amount_then_reverse_tokens_then_set_min_price_out_of_range",
(tester) async {
- final selectedYield = YieldsDto.fixture().best24hYield;
- final currentPriceAsTick = BigInt.from(174072);
+ await tester.runAsync(() async {
+ final selectedYield = YieldsDto.fixture().best24hYield;
+ final currentPriceAsTick = BigInt.from(174072);
- when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
- when(() => cubit.selectedYield).thenReturn(selectedYield);
- when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
- when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
- when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
+ when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
+ when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
- await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
- await tester.pumpAndSettle();
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
+ await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("quote-token-input-card")), "1");
- await tester.pumpAndSettle();
+ await tester.enterText(find.byKey(const Key("quote-token-input-card")), "1");
+ await tester.pumpAndSettle();
- await tester.tap(find.byKey(const Key("reverse-tokens-reversed")));
- await tester.pumpAndSettle();
+ await tester.tap(find.byKey(const Key("reverse-tokens-reversed")));
+ await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("min-price-selector")), "70000");
- await tester.pumpAndSettle();
+ await tester.enterText(find.byKey(const Key("min-price-selector")), "70000");
+ await tester.pumpAndSettle();
- FocusManager.instance.primaryFocus?.unfocus();
+ FocusManager.instance.primaryFocus?.unfocus();
+ await tester.pumpAndSettle();
+ });
});
zGoldenTest(
"When the user is is not connected, it should show the connect wallet button instead of the deposit button",
goldenFileName: "deposit_page_not_connected",
(tester) async {
- final selectedYield = YieldsDto.fixture().best24hYield;
- final currentPriceAsTick = BigInt.from(174072);
+ await tester.runAsync(() async {
+ final selectedYield = YieldsDto.fixture().best24hYield;
+ final currentPriceAsTick = BigInt.from(174072);
- when(() => wallet.signer).thenReturn(null);
- when(() => wallet.signerStream).thenAnswer((_) => Stream.value(null));
- when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
- when(() => cubit.selectedYield).thenReturn(selectedYield);
- when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
- when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
- when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
+ when(() => wallet.signer).thenReturn(null);
+ when(() => wallet.signerStream).thenAnswer((_) => Stream.value(null));
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
+ when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
+ when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
- await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
- await tester.pumpAndSettle();
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
+ await tester.pumpAndSettle();
+ });
},
);
@@ -1417,23 +1535,25 @@ void main() {
""",
goldenFileName: "deposit_page_not_connected_deposit_button_click",
(tester) async {
- final selectedYield = YieldsDto.fixture().best24hYield;
- final currentPriceAsTick = BigInt.from(174072);
+ await tester.runAsync(() async {
+ final selectedYield = YieldsDto.fixture().best24hYield;
+ final currentPriceAsTick = BigInt.from(174072);
- when(() => wallet.signer).thenReturn(null);
- when(() => wallet.signerStream).thenAnswer((_) => Stream.value(null));
- when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
- when(() => cubit.selectedYield).thenReturn(selectedYield);
- when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
- when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
- when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
+ when(() => wallet.signer).thenReturn(null);
+ when(() => wallet.signerStream).thenAnswer((_) => Stream.value(null));
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
+ when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
+ when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
- await tester.pumpDeviceBuilder(await goldenBuilder(), wrapper: GoldenConfig.localizationsWrapper());
- await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
- await tester.pumpAndSettle();
+ await tester.pumpDeviceBuilder(await goldenBuilder(), wrapper: GoldenConfig.localizationsWrapper());
+ await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
+ await tester.pumpAndSettle();
- await tester.tap(find.byKey(const Key("deposit-button")));
- await tester.pumpAndSettle();
+ await tester.tap(find.byKey(const Key("deposit-button")));
+ await tester.pumpAndSettle();
+ });
},
);
@@ -1442,24 +1562,26 @@ void main() {
the deposit button should should be disabled""",
goldenFileName: "deposit_page_no_amount_deposit_button",
(tester) async {
- final selectedYield = YieldsDto.fixture().best24hYield;
- final currentPriceAsTick = BigInt.from(174072);
- final signer = SignerMock();
+ await tester.runAsync(() async {
+ final selectedYield = YieldsDto.fixture().best24hYield;
+ final currentPriceAsTick = BigInt.from(174072);
+ final signer = SignerMock();
- when(() => wallet.signer).thenReturn(signer);
- when(() => wallet.signerStream).thenAnswer((_) => Stream.value(signer));
- when(() => cubit.getWalletTokenAmount(any(), network: any(named: "network"))).thenAnswer(
- (_) => Future.value(0.0),
- );
- when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
- when(() => cubit.selectedYield).thenReturn(selectedYield);
- when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
- when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
- when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
+ when(() => wallet.signer).thenReturn(signer);
+ when(() => wallet.signerStream).thenAnswer((_) => Stream.value(signer));
+ when(() => cubit.getWalletTokenAmount(any(), network: any(named: "network"))).thenAnswer(
+ (_) => Future.value(0.0),
+ );
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
+ when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
+ when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
- await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
- await tester.pumpAndSettle();
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
+ await tester.pumpAndSettle();
+ });
},
);
@@ -1469,27 +1591,29 @@ void main() {
the deposit button should should be disabled""",
goldenFileName: "deposit_page_not_enough_base_token_balance_deposit_button",
(tester) async {
- final selectedYield = YieldsDto.fixture().best24hYield;
- final currentPriceAsTick = BigInt.from(174072);
- final signer = SignerMock();
+ await tester.runAsync(() async {
+ final selectedYield = YieldsDto.fixture().best24hYield;
+ final currentPriceAsTick = BigInt.from(174072);
+ final signer = SignerMock();
- when(() => wallet.signer).thenReturn(signer);
- when(() => wallet.signerStream).thenAnswer((_) => Stream.value(signer));
- when(() => cubit.getWalletTokenAmount(any(), network: any(named: "network"))).thenAnswer(
- (_) => Future.value(0.0),
- );
- when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
- when(() => cubit.selectedYield).thenReturn(selectedYield);
- when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
- when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
- when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
+ when(() => wallet.signer).thenReturn(signer);
+ when(() => wallet.signerStream).thenAnswer((_) => Stream.value(signer));
+ when(() => cubit.getWalletTokenAmount(any(), network: any(named: "network"))).thenAnswer(
+ (_) => Future.value(0.0),
+ );
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
+ when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
+ when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
- await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
- await tester.pumpAndSettle();
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
+ await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("base-token-input-card")), "1");
- await tester.pumpAndSettle();
+ await tester.enterText(find.byKey(const Key("base-token-input-card")), "1");
+ await tester.pumpAndSettle();
+ });
},
);
@@ -1499,33 +1623,35 @@ void main() {
the deposit button should should be disabled""",
goldenFileName: "deposit_page_not_enough_quote_token_balance_deposit_button",
(tester) async {
- final selectedYield = YieldsDto.fixture().best24hYield;
- final currentPriceAsTick = BigInt.from(174072);
- final signer = SignerMock();
+ await tester.runAsync(() async {
+ final selectedYield = YieldsDto.fixture().best24hYield;
+ final currentPriceAsTick = BigInt.from(174072);
+ final signer = SignerMock();
- when(() => wallet.signer).thenReturn(signer);
- when(() => wallet.signerStream).thenAnswer((_) => Stream.value(signer));
+ when(() => wallet.signer).thenReturn(signer);
+ when(() => wallet.signerStream).thenAnswer((_) => Stream.value(signer));
- when(() => cubit.getWalletTokenAmount(selectedYield.token0.addresses[selectedYield.network.chainId]!,
- network: any(named: "network"))).thenAnswer(
- (_) => Future.value(32567352673),
- );
- when(() => cubit.getWalletTokenAmount(selectedYield.token1.addresses[selectedYield.network.chainId]!,
- network: any(named: "network"))).thenAnswer(
- (_) => Future.value(0),
- );
- when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
- when(() => cubit.selectedYield).thenReturn(selectedYield);
- when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
- when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
- when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
+ when(() => cubit.getWalletTokenAmount(selectedYield.token0.addresses[selectedYield.network.chainId]!,
+ network: any(named: "network"))).thenAnswer(
+ (_) => Future.value(32567352673),
+ );
+ when(() => cubit.getWalletTokenAmount(selectedYield.token1.addresses[selectedYield.network.chainId]!,
+ network: any(named: "network"))).thenAnswer(
+ (_) => Future.value(0),
+ );
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
+ when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
+ when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
- await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
- await tester.pumpAndSettle();
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
+ await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("quote-token-input-card")), "1");
- await tester.pumpAndSettle();
+ await tester.enterText(find.byKey(const Key("quote-token-input-card")), "1");
+ await tester.pumpAndSettle();
+ });
},
);
@@ -1534,51 +1660,129 @@ void main() {
without having enough balance of base token, the deposit button should should be disabled""",
goldenFileName: "deposit_page_not_enough_base_token_balance_deposit_button_after_connecting",
(tester) async {
- final selectedYield = YieldsDto.fixture().best24hYield;
- final currentPriceAsTick = BigInt.from(174072);
- final signerStreamController = StreamController.broadcast();
- final signer = SignerMock();
+ await tester.runAsync(() async {
+ final selectedYield = YieldsDto.fixture().best24hYield;
+ final currentPriceAsTick = BigInt.from(174072);
+ final signerStreamController = StreamController.broadcast();
+ final signer = SignerMock();
- when(() => wallet.signer).thenReturn(null);
- when(() => wallet.signerStream).thenAnswer((_) => signerStreamController.stream);
- when(() => cubit.getWalletTokenAmount(selectedYield.token0.addresses[selectedYield.network.chainId]!,
- network: any(named: "network"))).thenAnswer(
- (_) => Future.value(0),
- );
- when(() => cubit.getWalletTokenAmount(selectedYield.token1.addresses[selectedYield.network.chainId]!,
- network: any(named: "network"))).thenAnswer(
- (_) => Future.value(0),
- );
- when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
- when(() => cubit.selectedYield).thenReturn(selectedYield);
- when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
- when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
- when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
+ when(() => wallet.signer).thenReturn(null);
+ when(() => wallet.signerStream).thenAnswer((_) => signerStreamController.stream);
+ when(() => cubit.getWalletTokenAmount(selectedYield.token0.addresses[selectedYield.network.chainId]!,
+ network: any(named: "network"))).thenAnswer(
+ (_) => Future.value(0),
+ );
+ when(() => cubit.getWalletTokenAmount(selectedYield.token1.addresses[selectedYield.network.chainId]!,
+ network: any(named: "network"))).thenAnswer(
+ (_) => Future.value(0),
+ );
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
+ when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
+ when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
+
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
+ await tester.pumpAndSettle();
+
+ await tester.enterText(find.byKey(const Key("quote-token-input-card")), "1");
+ await tester.pumpAndSettle();
+
+ signerStreamController.add(signer);
+ await tester.pumpAndSettle();
+ });
+ },
+ );
+
+ zGoldenTest(
+ """When the user is not connected, type an amount to deposit, and then connect
+ without having enough balance of quote token, the deposit button should should be disabled""",
+ goldenFileName: "deposit_page_not_enough_quote_token_balance_deposit_button_after_connecting",
+ (tester) async {
+ await tester.runAsync(() async {
+ final selectedYield = YieldsDto.fixture().best24hYield;
+ final currentPriceAsTick = BigInt.from(174072);
+ final signerStreamController = StreamController.broadcast();
+ final signer = SignerMock();
+
+ when(() => wallet.signer).thenReturn(null);
+ when(() => wallet.signerStream).thenAnswer((_) => signerStreamController.stream);
+ when(() => cubit.getWalletTokenAmount(selectedYield.token0.addresses[selectedYield.network.chainId]!,
+ network: any(named: "network"))).thenAnswer(
+ (_) => Future.value(347537253),
+ );
+ when(() => cubit.getWalletTokenAmount(selectedYield.token1.addresses[selectedYield.network.chainId]!,
+ network: any(named: "network"))).thenAnswer(
+ (_) => Future.value(0),
+ );
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
+ when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
+ when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
+
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
+ await tester.pumpAndSettle();
+
+ await tester.enterText(find.byKey(const Key("quote-token-input-card")), "1");
+ await tester.pumpAndSettle();
+
+ signerStreamController.add(signer);
+ await tester.pumpAndSettle();
+ });
+ },
+ );
+
+ zGoldenTest(
+ """When the user is connected, type an amount to deposit, and have enough balance of both tokens
+ the deposit button should be enabled""",
+ goldenFileName: "deposit_page_enough_balance_deposit_button",
+ (tester) async {
+ await tester.runAsync(() async {
+ final selectedYield = YieldsDto.fixture().best24hYield;
+ final currentPriceAsTick = BigInt.from(174072);
- await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
- await tester.pumpAndSettle();
+ final signer = SignerMock();
- await tester.enterText(find.byKey(const Key("quote-token-input-card")), "1");
- await tester.pumpAndSettle();
+ when(() => wallet.signer).thenReturn(signer);
+ when(() => wallet.signerStream).thenAnswer((_) => Stream.value(signer));
+ when(() => cubit.getWalletTokenAmount(selectedYield.token0.addresses[selectedYield.network.chainId]!,
+ network: any(named: "network"))).thenAnswer(
+ (_) => Future.value(347537253),
+ );
+ when(() => cubit.getWalletTokenAmount(selectedYield.token1.addresses[selectedYield.network.chainId]!,
+ network: any(named: "network"))).thenAnswer(
+ (_) => Future.value(32576352673),
+ );
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
+ when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
+ when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
- signerStreamController.add(signer);
- await tester.pumpAndSettle();
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
+ await tester.pumpAndSettle();
+
+ await tester.enterText(find.byKey(const Key("quote-token-input-card")), "1");
+ await tester.pumpAndSettle();
+ });
},
);
- zGoldenTest(
- """When the user is not connected, type an amount to deposit, and then connect
- without having enough balance of quote token, the deposit button should should be disabled""",
- goldenFileName: "deposit_page_not_enough_quote_token_balance_deposit_button_after_connecting",
- (tester) async {
+ zGoldenTest("""When the min range is out of range, and the user does not have quote token balance
+ but has enough balance of base token, the deposit button should be enabled""",
+ goldenFileName: "deposit_page_min_range_out_of_range_deposit_button", (tester) async {
+ await tester.runAsync(() async {
final selectedYield = YieldsDto.fixture().best24hYield;
final currentPriceAsTick = BigInt.from(174072);
- final signerStreamController = StreamController.broadcast();
+
final signer = SignerMock();
- when(() => wallet.signer).thenReturn(null);
- when(() => wallet.signerStream).thenAnswer((_) => signerStreamController.stream);
+ when(() => wallet.signer).thenReturn(signer);
+ when(() => wallet.signerStream).thenAnswer((_) => Stream.value(signer));
when(() => cubit.getWalletTokenAmount(selectedYield.token0.addresses[selectedYield.network.chainId]!,
network: any(named: "network"))).thenAnswer(
(_) => Future.value(347537253),
@@ -1597,19 +1801,18 @@ void main() {
await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("quote-token-input-card")), "1");
+ await tester.enterText(find.byKey(const Key("min-price-selector")), "1");
await tester.pumpAndSettle();
- signerStreamController.add(signer);
+ await tester.enterText(find.byKey(const Key("base-token-input-card")), "1");
await tester.pumpAndSettle();
- },
- );
+ });
+ });
- zGoldenTest(
- """When the user is connected, type an amount to deposit, and have enough balance of both tokens
- the deposit button should be enabled""",
- goldenFileName: "deposit_page_enough_balance_deposit_button",
- (tester) async {
+ zGoldenTest("""When the max range is out of range, and the user does not have base token balance
+ but has enough balance of quote token, the deposit button should be enabled""",
+ goldenFileName: "deposit_page_max_range_out_of_range_deposit_button", (tester) async {
+ await tester.runAsync(() async {
final selectedYield = YieldsDto.fixture().best24hYield;
final currentPriceAsTick = BigInt.from(174072);
@@ -1619,11 +1822,11 @@ void main() {
when(() => wallet.signerStream).thenAnswer((_) => Stream.value(signer));
when(() => cubit.getWalletTokenAmount(selectedYield.token0.addresses[selectedYield.network.chainId]!,
network: any(named: "network"))).thenAnswer(
- (_) => Future.value(347537253),
+ (_) => Future.value(0),
);
when(() => cubit.getWalletTokenAmount(selectedYield.token1.addresses[selectedYield.network.chainId]!,
network: any(named: "network"))).thenAnswer(
- (_) => Future.value(32576352673),
+ (_) => Future.value(3237526),
);
when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
when(() => cubit.selectedYield).thenReturn(selectedYield);
@@ -1635,85 +1838,18 @@ void main() {
await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("quote-token-input-card")), "1");
+ await tester.enterText(find.byKey(const Key("min-price-selector")), "0.0000001");
await tester.pumpAndSettle();
- },
- );
-
- zGoldenTest("""When the min range is out of range, and the user does not have quote token balance
- but has enough balance of base token, the deposit button should be enabled""",
- goldenFileName: "deposit_page_min_range_out_of_range_deposit_button", (tester) async {
- final selectedYield = YieldsDto.fixture().best24hYield;
- final currentPriceAsTick = BigInt.from(174072);
-
- final signer = SignerMock();
-
- when(() => wallet.signer).thenReturn(signer);
- when(() => wallet.signerStream).thenAnswer((_) => Stream.value(signer));
- when(() => cubit.getWalletTokenAmount(selectedYield.token0.addresses[selectedYield.network.chainId]!,
- network: any(named: "network"))).thenAnswer(
- (_) => Future.value(347537253),
- );
- when(() => cubit.getWalletTokenAmount(selectedYield.token1.addresses[selectedYield.network.chainId]!,
- network: any(named: "network"))).thenAnswer(
- (_) => Future.value(0),
- );
- when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
- when(() => cubit.selectedYield).thenReturn(selectedYield);
- when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
- when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
- when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
-
- await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
- await tester.pumpAndSettle();
-
- await tester.enterText(find.byKey(const Key("min-price-selector")), "1");
- await tester.pumpAndSettle();
-
- await tester.enterText(find.byKey(const Key("base-token-input-card")), "1");
- await tester.pumpAndSettle();
- });
-
- zGoldenTest("""When the max range is out of range, and the user does not have base token balance
- but has enough balance of quote token, the deposit button should be enabled""",
- goldenFileName: "deposit_page_max_range_out_of_range_deposit_button", (tester) async {
- final selectedYield = YieldsDto.fixture().best24hYield;
- final currentPriceAsTick = BigInt.from(174072);
-
- final signer = SignerMock();
-
- when(() => wallet.signer).thenReturn(signer);
- when(() => wallet.signerStream).thenAnswer((_) => Stream.value(signer));
- when(() => cubit.getWalletTokenAmount(selectedYield.token0.addresses[selectedYield.network.chainId]!,
- network: any(named: "network"))).thenAnswer(
- (_) => Future.value(0),
- );
- when(() => cubit.getWalletTokenAmount(selectedYield.token1.addresses[selectedYield.network.chainId]!,
- network: any(named: "network"))).thenAnswer(
- (_) => Future.value(3237526),
- );
- when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
- when(() => cubit.selectedYield).thenReturn(selectedYield);
- when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
- when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
- when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
- await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
- await tester.pumpAndSettle();
-
- await tester.enterText(find.byKey(const Key("min-price-selector")), "0.0000001");
- await tester.pumpAndSettle();
-
- await tester.enterText(find.byKey(const Key("max-price-selector")), "0");
- await tester.pumpAndSettle();
+ await tester.enterText(find.byKey(const Key("max-price-selector")), "0");
+ await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("max-price-selector")), "0.000001");
- await tester.pumpAndSettle();
+ await tester.enterText(find.byKey(const Key("max-price-selector")), "0.000001");
+ await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("quote-token-input-card")), "1");
- await tester.pumpAndSettle();
+ await tester.enterText(find.byKey(const Key("quote-token-input-card")), "1");
+ await tester.pumpAndSettle();
+ });
});
zGoldenTest("When clicking the enabled deposit button, it should show the preview modal of the deposit",
@@ -1760,20 +1896,22 @@ void main() {
the quote token input should be loading""",
goldenFileName: "deposit_page_quote_token_input_loading",
(tester) async {
- final selectedYield = YieldsDto.fixture().best24hYield;
+ await tester.runAsync(() async {
+ final selectedYield = YieldsDto.fixture().best24hYield;
- when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
- when(() => cubit.selectedYield).thenReturn(selectedYield);
- when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
- when(() => cubit.poolTickStream).thenAnswer((_) => const Stream.empty());
- when(() => cubit.latestPoolTick).thenReturn(null);
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
+ when(() => cubit.poolTickStream).thenAnswer((_) => const Stream.empty());
+ when(() => cubit.latestPoolTick).thenReturn(null);
- await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
- await tester.pumpAndSettle();
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
+ await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("base-token-input-card")), "1");
- await tester.pumpAndSettle();
+ await tester.enterText(find.byKey(const Key("base-token-input-card")), "1");
+ await tester.pumpAndSettle();
+ });
},
);
@@ -1782,20 +1920,22 @@ void main() {
the base token input should be loading""",
goldenFileName: "deposit_page_base_token_input_loading",
(tester) async {
- final selectedYield = YieldsDto.fixture().best24hYield;
+ await tester.runAsync(() async {
+ final selectedYield = YieldsDto.fixture().best24hYield;
- when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
- when(() => cubit.selectedYield).thenReturn(selectedYield);
- when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
- when(() => cubit.poolTickStream).thenAnswer((_) => const Stream.empty());
- when(() => cubit.latestPoolTick).thenReturn(null);
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
+ when(() => cubit.poolTickStream).thenAnswer((_) => const Stream.empty());
+ when(() => cubit.latestPoolTick).thenReturn(null);
- await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
- await tester.pumpAndSettle();
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
+ await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("quote-token-input-card")), "1");
- await tester.pumpAndSettle();
+ await tester.enterText(find.byKey(const Key("quote-token-input-card")), "1");
+ await tester.pumpAndSettle();
+ });
},
);
@@ -1805,20 +1945,22 @@ void main() {
""",
goldenFileName: "deposit_page_quote_token_input_enabled_after_loading",
(tester) async {
- final selectedYield = YieldsDto.fixture().best24hYield;
+ await tester.runAsync(() async {
+ final selectedYield = YieldsDto.fixture().best24hYield;
- when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
- when(() => cubit.selectedYield).thenReturn(selectedYield);
- when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
- when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(BigInt.from(2131)));
- when(() => cubit.latestPoolTick).thenReturn(null);
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
+ when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(BigInt.from(2131)));
+ when(() => cubit.latestPoolTick).thenReturn(null);
- await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
- await tester.pumpAndSettle();
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
+ await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("base-token-input-card")), "1");
- await tester.pumpAndSettle();
+ await tester.enterText(find.byKey(const Key("base-token-input-card")), "1");
+ await tester.pumpAndSettle();
+ });
},
);
@@ -1828,20 +1970,22 @@ void main() {
""",
goldenFileName: "deposit_page_base_token_input_enabled_after_loading",
(tester) async {
- final selectedYield = YieldsDto.fixture().best24hYield;
+ await tester.runAsync(() async {
+ final selectedYield = YieldsDto.fixture().best24hYield;
- when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
- when(() => cubit.selectedYield).thenReturn(selectedYield);
- when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
- when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(BigInt.from(2131)));
- when(() => cubit.latestPoolTick).thenReturn(null);
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
+ when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(BigInt.from(2131)));
+ when(() => cubit.latestPoolTick).thenReturn(null);
- await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
- await tester.pumpAndSettle();
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
+ await tester.pumpAndSettle();
- await tester.enterText(find.byKey(const Key("quote-token-input-card")), "1");
- await tester.pumpAndSettle();
+ await tester.enterText(find.byKey(const Key("quote-token-input-card")), "1");
+ await tester.pumpAndSettle();
+ });
},
);
@@ -2078,113 +2222,6 @@ void main() {
},
);
- zGoldenTest(
- """When the user has not the wrapped native token amount but has the native token amount to deposit,
- and the switch to deposit with native is enabled, it should allow the user to proceed with the deposit
- (open the preview modal)""",
- (tester) async {
- await tester.runAsync(() async {
- final selectedYield = YieldsDto.fixture().best24hYield.copyWith(
- token0: TokenDto(addresses: YieldsDto.fixture().best24hYield.network.wrappedNative.addresses),
- token1: TokenDto(addresses: YieldsDto.fixture().best24hYield.network.wrappedNative.addresses),
- );
-
- final currentPriceAsTick = BigInt.from(174072);
- final signer = SignerMock();
-
- when(() => wallet.signer).thenReturn(signer);
- when(() => wallet.signerStream).thenAnswer((_) => Stream.value(signer));
- when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
- when(() => cubit.selectedYield).thenReturn(selectedYield);
- when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
- when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
- when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
- when(() => cubit.selectYield(any(), any())).thenAnswer((_) async => () {});
- when(() => cubit.selectedYieldTimeframe).thenReturn(YieldTimeFrame.day);
- when(() => cubit.getWalletTokenAmount(EthereumConstants.zeroAddress, network: any(named: "network")))
- .thenAnswer(
- (_) => Future.value(32576352673),
- );
- when(() => cubit.getWalletTokenAmount(any(that: isNot(EthereumConstants.zeroAddress)),
- network: any(named: "network"))).thenAnswer((_) => Future.value(0));
-
- await tester.pumpDeviceBuilder(await goldenBuilder(), wrapper: GoldenConfig.localizationsWrapper());
- await tester.pumpAndSettle();
-
- await tester.tap(find.byKey(const Key("yield-card-24h")));
- await tester.pumpAndSettle();
-
- await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
- await tester.pumpAndSettle();
-
- await tester.enterText(find.byKey(const Key("quote-token-input-card")), "1");
- await tester.pumpAndSettle();
-
- await tester.tap(find.byKey(const Key("deposit-button")));
- await tester.pumpAndSettle();
-
- expect(find.byType(PreviewDepositModal), findsOneWidget); // the next step after configuring the pool
- });
- },
- );
-
- zGoldenTest(
- """When the user has not the native token amount but has the wrapped native token amount to deposit,
- and the switch to deposit with native is false, it should allow the user to proceed with the deposit
- (open the preview modal)""",
- (tester) async {
- await tester.runAsync(() async {
- final selectedYield = YieldsDto.fixture().best24hYield.copyWith(
- token0: TokenDto.fixture()
- .copyWith(addresses: YieldsDto.fixture().best24hYield.network.wrappedNative.addresses),
- token1: TokenDto.fixture()
- .copyWith(addresses: YieldsDto.fixture().best24hYield.network.wrappedNative.addresses),
- );
-
- final currentPriceAsTick = BigInt.from(174072);
- final signer = SignerMock();
-
- when(() => wallet.signer).thenReturn(signer);
- when(() => wallet.signerStream).thenAnswer((_) => Stream.value(signer));
- when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
- when(() => cubit.selectedYield).thenReturn(selectedYield);
- when(() => cubit.state).thenReturn(DepositState.success(YieldsDto.fixture()));
- when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(currentPriceAsTick));
- when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick);
- when(() => cubit.selectYield(any(), any())).thenAnswer((_) async => () {});
- when(() => cubit.selectedYieldTimeframe).thenReturn(YieldTimeFrame.day);
- when(() => cubit.getWalletTokenAmount(
- selectedYield.network.wrappedNative.addresses[selectedYield.network.chainId]!,
- network: any(named: "network"))).thenAnswer(
- (_) => Future.value(32576352673),
- );
-
- when(() => cubit.getWalletTokenAmount(any(that: matches(EthereumConstants.zeroAddress)),
- network: any(named: "network"))).thenAnswer((_) => Future.value(0));
-
- await tester.pumpDeviceBuilder(await goldenBuilder(), wrapper: GoldenConfig.localizationsWrapper());
- await tester.pumpAndSettle();
-
- await tester.tap(find.byKey(const Key("yield-card-24h")));
- await tester.pumpAndSettle();
-
- await tester.drag(find.byKey(const Key("deposit-section")), const Offset(0, -500));
- await tester.pumpAndSettle();
-
- await tester.tap(find.byKey(const Key("deposit-with-native-token-switch")));
- await tester.pumpAndSettle();
-
- await tester.enterText(find.byKey(const Key("quote-token-input-card")), "1");
- await tester.pumpAndSettle();
-
- await tester.tap(find.byKey(const Key("deposit-button")));
- await tester.pumpAndSettle();
-
- expect(find.byType(PreviewDepositModal), findsOneWidget); // the next step after configuring the pool
- });
- },
- );
-
zGoldenTest(
"When loading the screen, and the network in the path param is different from the selected one, it should switch the network",
(tester) async {
diff --git a/test/app/create/deposit/goldens/deposit_page_base_token_input_enabled_after_loading.png b/test/app/create/deposit/goldens/deposit_page_base_token_input_enabled_after_loading.png
index 904de8f..4bd8863 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_base_token_input_enabled_after_loading.png and b/test/app/create/deposit/goldens/deposit_page_base_token_input_enabled_after_loading.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_base_token_input_loading.png b/test/app/create/deposit/goldens/deposit_page_base_token_input_loading.png
index 6161c04..652ad91 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_base_token_input_loading.png and b/test/app/create/deposit/goldens/deposit_page_base_token_input_loading.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_calculate_price.png b/test/app/create/deposit/goldens/deposit_page_calculate_price.png
index 34ce737..368ab27 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_calculate_price.png and b/test/app/create/deposit/goldens/deposit_page_calculate_price.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_calculate_price_reversed.png b/test/app/create/deposit/goldens/deposit_page_calculate_price_reversed.png
index 7614039..719ee74 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_calculate_price_reversed.png and b/test/app/create/deposit/goldens/deposit_page_calculate_price_reversed.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_enough_balance_deposit_button.png b/test/app/create/deposit/goldens/deposit_page_enough_balance_deposit_button.png
index 46ed296..a4db142 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_enough_balance_deposit_button.png and b/test/app/create/deposit/goldens/deposit_page_enough_balance_deposit_button.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_input_base_token_amount.png b/test/app/create/deposit/goldens/deposit_page_input_base_token_amount.png
index a909a39..906fddd 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_input_base_token_amount.png and b/test/app/create/deposit/goldens/deposit_page_input_base_token_amount.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_input_base_token_amount_and_change_range.png b/test/app/create/deposit/goldens/deposit_page_input_base_token_amount_and_change_range.png
index 7a4da94..f83f505 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_input_base_token_amount_and_change_range.png and b/test/app/create/deposit/goldens/deposit_page_input_base_token_amount_and_change_range.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_input_base_token_amount_and_reverse.png b/test/app/create/deposit/goldens/deposit_page_input_base_token_amount_and_reverse.png
index 309119d..9913e78 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_input_base_token_amount_and_reverse.png and b/test/app/create/deposit/goldens/deposit_page_input_base_token_amount_and_reverse.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_input_base_token_amount_and_reverse_back.png b/test/app/create/deposit/goldens/deposit_page_input_base_token_amount_and_reverse_back.png
index d07a0cf..5e9bf9e 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_input_base_token_amount_and_reverse_back.png and b/test/app/create/deposit/goldens/deposit_page_input_base_token_amount_and_reverse_back.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_input_base_token_amount_reverse_tokens_and_change_range.png b/test/app/create/deposit/goldens/deposit_page_input_base_token_amount_reverse_tokens_and_change_range.png
index e834e52..cdc4e9f 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_input_base_token_amount_reverse_tokens_and_change_range.png and b/test/app/create/deposit/goldens/deposit_page_input_base_token_amount_reverse_tokens_and_change_range.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_input_base_token_amount_reversed.png b/test/app/create/deposit/goldens/deposit_page_input_base_token_amount_reversed.png
index 5d4822b..c79530b 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_input_base_token_amount_reversed.png and b/test/app/create/deposit/goldens/deposit_page_input_base_token_amount_reversed.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_input_base_token_amount_then_reverse_tokens_then_set_max_price_out_of_range.png b/test/app/create/deposit/goldens/deposit_page_input_base_token_amount_then_reverse_tokens_then_set_max_price_out_of_range.png
index 4341cb3..08ad629 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_input_base_token_amount_then_reverse_tokens_then_set_max_price_out_of_range.png and b/test/app/create/deposit/goldens/deposit_page_input_base_token_amount_then_reverse_tokens_then_set_max_price_out_of_range.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_input_base_token_amount_then_set_max_price_out_of_range.png b/test/app/create/deposit/goldens/deposit_page_input_base_token_amount_then_set_max_price_out_of_range.png
index 9f994b2..13d3ef2 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_input_base_token_amount_then_set_max_price_out_of_range.png and b/test/app/create/deposit/goldens/deposit_page_input_base_token_amount_then_set_max_price_out_of_range.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_input_quote_token_amount.png b/test/app/create/deposit/goldens/deposit_page_input_quote_token_amount.png
index 8896a87..5c56177 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_input_quote_token_amount.png and b/test/app/create/deposit/goldens/deposit_page_input_quote_token_amount.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_input_quote_token_amount_and_change_range.png b/test/app/create/deposit/goldens/deposit_page_input_quote_token_amount_and_change_range.png
index 01e7fcb..10d5ae0 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_input_quote_token_amount_and_change_range.png and b/test/app/create/deposit/goldens/deposit_page_input_quote_token_amount_and_change_range.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_input_quote_token_amount_and_reverse.png b/test/app/create/deposit/goldens/deposit_page_input_quote_token_amount_and_reverse.png
index 95ef2ab..f89f954 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_input_quote_token_amount_and_reverse.png and b/test/app/create/deposit/goldens/deposit_page_input_quote_token_amount_and_reverse.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_input_quote_token_amount_and_reverse_back.png b/test/app/create/deposit/goldens/deposit_page_input_quote_token_amount_and_reverse_back.png
index 658deaf..e381e1c 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_input_quote_token_amount_and_reverse_back.png and b/test/app/create/deposit/goldens/deposit_page_input_quote_token_amount_and_reverse_back.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_input_quote_token_amount_reverse_tokens_and_change_range.png b/test/app/create/deposit/goldens/deposit_page_input_quote_token_amount_reverse_tokens_and_change_range.png
index 782df4d..624f263 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_input_quote_token_amount_reverse_tokens_and_change_range.png and b/test/app/create/deposit/goldens/deposit_page_input_quote_token_amount_reverse_tokens_and_change_range.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_input_quote_token_amount_reversed.png b/test/app/create/deposit/goldens/deposit_page_input_quote_token_amount_reversed.png
index 0986374..2ac1fac 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_input_quote_token_amount_reversed.png and b/test/app/create/deposit/goldens/deposit_page_input_quote_token_amount_reversed.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_input_quote_token_amount_then_reverse_tokens_then_set_min_price_out_of_range.png b/test/app/create/deposit/goldens/deposit_page_input_quote_token_amount_then_reverse_tokens_then_set_min_price_out_of_range.png
index 6fb02e2..6aa2343 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_input_quote_token_amount_then_reverse_tokens_then_set_min_price_out_of_range.png and b/test/app/create/deposit/goldens/deposit_page_input_quote_token_amount_then_reverse_tokens_then_set_min_price_out_of_range.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_input_quote_token_amount_then_set_min_price_out_of_range.png b/test/app/create/deposit/goldens/deposit_page_input_quote_token_amount_then_set_min_price_out_of_range.png
index 4683ea4..a8f94b2 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_input_quote_token_amount_then_set_min_price_out_of_range.png and b/test/app/create/deposit/goldens/deposit_page_input_quote_token_amount_then_set_min_price_out_of_range.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_input_range_then_input_base_token_amount.png b/test/app/create/deposit/goldens/deposit_page_input_range_then_input_base_token_amount.png
index bf0e2c7..2cc9066 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_input_range_then_input_base_token_amount.png and b/test/app/create/deposit/goldens/deposit_page_input_range_then_input_base_token_amount.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_input_range_then_input_quote_token_amount.png b/test/app/create/deposit/goldens/deposit_page_input_range_then_input_quote_token_amount.png
index 4ea6e5b..8f0d814 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_input_range_then_input_quote_token_amount.png and b/test/app/create/deposit/goldens/deposit_page_input_range_then_input_quote_token_amount.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_input_range_then_reverse_tokens_then_input_base_token_amount.png b/test/app/create/deposit/goldens/deposit_page_input_range_then_reverse_tokens_then_input_base_token_amount.png
index 782df4d..624f263 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_input_range_then_reverse_tokens_then_input_base_token_amount.png and b/test/app/create/deposit/goldens/deposit_page_input_range_then_reverse_tokens_then_input_base_token_amount.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_input_range_then_reverse_tokens_then_input_quote_token_amount.png b/test/app/create/deposit/goldens/deposit_page_input_range_then_reverse_tokens_then_input_quote_token_amount.png
index e834e52..cdc4e9f 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_input_range_then_reverse_tokens_then_input_quote_token_amount.png and b/test/app/create/deposit/goldens/deposit_page_input_range_then_reverse_tokens_then_input_quote_token_amount.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_invalid_range_deposit_section.png b/test/app/create/deposit/goldens/deposit_page_invalid_range_deposit_section.png
index dcae01e..4620174 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_invalid_range_deposit_section.png and b/test/app/create/deposit/goldens/deposit_page_invalid_range_deposit_section.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_max_price_less_than_min_price.png b/test/app/create/deposit/goldens/deposit_page_max_price_less_than_min_price.png
index a8c13cc..cb22fde 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_max_price_less_than_min_price.png and b/test/app/create/deposit/goldens/deposit_page_max_price_less_than_min_price.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_max_price_out_of_range.png b/test/app/create/deposit/goldens/deposit_page_max_price_out_of_range.png
index 61d825e..6d48682 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_max_price_out_of_range.png and b/test/app/create/deposit/goldens/deposit_page_max_price_out_of_range.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_max_price_set_to_full_range.png b/test/app/create/deposit/goldens/deposit_page_max_price_set_to_full_range.png
index 34ce737..a69ec57 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_max_price_set_to_full_range.png and b/test/app/create/deposit/goldens/deposit_page_max_price_set_to_full_range.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_max_price_set_to_infinity.png b/test/app/create/deposit/goldens/deposit_page_max_price_set_to_infinity.png
index 933ae3d..5720cea 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_max_price_set_to_infinity.png and b/test/app/create/deposit/goldens/deposit_page_max_price_set_to_infinity.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_max_range_out_of_range_deposit_button.png b/test/app/create/deposit/goldens/deposit_page_max_range_out_of_range_deposit_button.png
index a6b9824..34ef2b6 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_max_range_out_of_range_deposit_button.png and b/test/app/create/deposit/goldens/deposit_page_max_range_out_of_range_deposit_button.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_min_and_max_price_set_to_full_range.png b/test/app/create/deposit/goldens/deposit_page_min_and_max_price_set_to_full_range.png
index 34ce737..a69ec57 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_min_and_max_price_set_to_full_range.png and b/test/app/create/deposit/goldens/deposit_page_min_and_max_price_set_to_full_range.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_min_price_out_of_range.png b/test/app/create/deposit/goldens/deposit_page_min_price_out_of_range.png
index dbd9e62..1e960e1 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_min_price_out_of_range.png and b/test/app/create/deposit/goldens/deposit_page_min_price_out_of_range.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_min_price_out_of_range_reversed.png b/test/app/create/deposit/goldens/deposit_page_min_price_out_of_range_reversed.png
index 5828808..dd43559 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_min_price_out_of_range_reversed.png and b/test/app/create/deposit/goldens/deposit_page_min_price_out_of_range_reversed.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_min_price_out_of_range_reversed_in_range.png b/test/app/create/deposit/goldens/deposit_page_min_price_out_of_range_reversed_in_range.png
index 01b4f8e..68deb04 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_min_price_out_of_range_reversed_in_range.png and b/test/app/create/deposit/goldens/deposit_page_min_price_out_of_range_reversed_in_range.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_min_price_set_to_full_range.png b/test/app/create/deposit/goldens/deposit_page_min_price_set_to_full_range.png
index 34ce737..a69ec57 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_min_price_set_to_full_range.png and b/test/app/create/deposit/goldens/deposit_page_min_price_set_to_full_range.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_min_range_out_of_range_deposit_button.png b/test/app/create/deposit/goldens/deposit_page_min_range_out_of_range_deposit_button.png
index 9a27744..341a677 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_min_range_out_of_range_deposit_button.png and b/test/app/create/deposit/goldens/deposit_page_min_range_out_of_range_deposit_button.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_no_amount_deposit_button.png b/test/app/create/deposit/goldens/deposit_page_no_amount_deposit_button.png
index 78ff412..f551014 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_no_amount_deposit_button.png and b/test/app/create/deposit/goldens/deposit_page_no_amount_deposit_button.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_not_connected.png b/test/app/create/deposit/goldens/deposit_page_not_connected.png
index 80ca84d..eb229fb 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_not_connected.png and b/test/app/create/deposit/goldens/deposit_page_not_connected.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_not_connected_deposit_button_click.png b/test/app/create/deposit/goldens/deposit_page_not_connected_deposit_button_click.png
index 3388211..abf8220 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_not_connected_deposit_button_click.png and b/test/app/create/deposit/goldens/deposit_page_not_connected_deposit_button_click.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_not_enough_base_token_balance_deposit_button.png b/test/app/create/deposit/goldens/deposit_page_not_enough_base_token_balance_deposit_button.png
index c322acf..774f534 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_not_enough_base_token_balance_deposit_button.png and b/test/app/create/deposit/goldens/deposit_page_not_enough_base_token_balance_deposit_button.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_not_enough_base_token_balance_deposit_button_after_connecting.png b/test/app/create/deposit/goldens/deposit_page_not_enough_base_token_balance_deposit_button_after_connecting.png
index f0bfd9a..200e1a1 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_not_enough_base_token_balance_deposit_button_after_connecting.png and b/test/app/create/deposit/goldens/deposit_page_not_enough_base_token_balance_deposit_button_after_connecting.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_not_enough_quote_token_balance_deposit_button.png b/test/app/create/deposit/goldens/deposit_page_not_enough_quote_token_balance_deposit_button.png
index 4ce269e..448b36e 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_not_enough_quote_token_balance_deposit_button.png and b/test/app/create/deposit/goldens/deposit_page_not_enough_quote_token_balance_deposit_button.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_not_enough_quote_token_balance_deposit_button_after_connecting.png b/test/app/create/deposit/goldens/deposit_page_not_enough_quote_token_balance_deposit_button_after_connecting.png
index 4ce269e..448b36e 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_not_enough_quote_token_balance_deposit_button_after_connecting.png and b/test/app/create/deposit/goldens/deposit_page_not_enough_quote_token_balance_deposit_button_after_connecting.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_preview_modal.png b/test/app/create/deposit/goldens/deposit_page_preview_modal.png
index 21d2cec..0bb3184 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_preview_modal.png and b/test/app/create/deposit/goldens/deposit_page_preview_modal.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_quote_token_input_enabled_after_loading.png b/test/app/create/deposit/goldens/deposit_page_quote_token_input_enabled_after_loading.png
index e44b71c..09a5502 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_quote_token_input_enabled_after_loading.png and b/test/app/create/deposit/goldens/deposit_page_quote_token_input_enabled_after_loading.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_quote_token_input_loading.png b/test/app/create/deposit/goldens/deposit_page_quote_token_input_loading.png
index 3d0d898..197c07d 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_quote_token_input_loading.png and b/test/app/create/deposit/goldens/deposit_page_quote_token_input_loading.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_range_section_mobile.png b/test/app/create/deposit/goldens/deposit_page_range_section_mobile.png
index 48308b8..40defe1 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_range_section_mobile.png and b/test/app/create/deposit/goldens/deposit_page_range_section_mobile.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_reverse_tokens.png b/test/app/create/deposit/goldens/deposit_page_reverse_tokens.png
index 0743e50..7aff470 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_reverse_tokens.png and b/test/app/create/deposit/goldens/deposit_page_reverse_tokens.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_reverse_tokens_back.png b/test/app/create/deposit/goldens/deposit_page_reverse_tokens_back.png
index 086db15..111650d 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_reverse_tokens_back.png and b/test/app/create/deposit/goldens/deposit_page_reverse_tokens_back.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_select_yield_scroll.png b/test/app/create/deposit/goldens/deposit_page_select_yield_scroll.png
index 87d0acb..d20acbc 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_select_yield_scroll.png and b/test/app/create/deposit/goldens/deposit_page_select_yield_scroll.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_selected_yield_stream.png b/test/app/create/deposit/goldens/deposit_page_selected_yield_stream.png
index 0a04faf..2bd391e 100644
Binary files a/test/app/create/deposit/goldens/deposit_page_selected_yield_stream.png and b/test/app/create/deposit/goldens/deposit_page_selected_yield_stream.png differ
diff --git a/test/app/create/deposit/widgets/deposit_success_modal_test.dart b/test/app/create/deposit/widgets/deposit_success_modal_test.dart
index 9a8bd42..1d9ce4e 100644
--- a/test/app/create/deposit/widgets/deposit_success_modal_test.dart
+++ b/test/app/create/deposit/widgets/deposit_success_modal_test.dart
@@ -7,9 +7,7 @@ import 'package:url_launcher_platform_interface/url_launcher_platform_interface.
import 'package:zup_app/app/create/deposit/widgets/deposit_success_modal.dart';
import 'package:zup_app/core/dtos/protocol_dto.dart';
import 'package:zup_app/core/dtos/yield_dto.dart';
-import 'package:zup_app/core/enums/networks.dart';
import 'package:zup_app/core/injections.dart';
-import 'package:zup_app/widgets/token_avatar.dart';
import 'package:zup_app/widgets/zup_cached_image.dart';
import '../../../../golden_config.dart';
@@ -38,7 +36,6 @@ void main() {
Future goldenBuilder({
YieldDto? customYield,
bool showAsBottomSheet = false,
- bool depositedWithNative = false,
}) async =>
await goldenDeviceBuilder(Builder(builder: (context) {
WidgetsBinding.instance.addPostFrameCallback((_) {
@@ -46,7 +43,6 @@ void main() {
context,
depositedYield: customYield ?? YieldDto.fixture().copyWith(),
showAsBottomSheet: showAsBottomSheet,
- depositedWithNative: depositedWithNative,
);
});
@@ -115,57 +111,4 @@ void main() {
);
await tester.pumpAndSettle();
});
-
- zGoldenTest(
- """When passing depositedWithNative to true, the modal should use the native token symbol in the description
- if the token0 or token1 are wrapped natives""", (tester) async {
- final wrappedNativeYield = YieldDto.fixture().copyWith(
- chainId: AppNetworks.sepolia.chainId,
- token0: AppNetworks.sepolia.wrappedNative,
- token1: AppNetworks.sepolia.wrappedNative,
- );
-
- await tester.pumpDeviceBuilder(
- await goldenBuilder(
- depositedWithNative: true,
- customYield: wrappedNativeYield,
- ),
- wrapper: GoldenConfig.localizationsWrapper(),
- );
- await tester.pumpAndSettle();
-
- expect(
- find.text(
- "You have successfully deposited into ETH/ETH Pool at ${wrappedNativeYield.protocol.name} on ${wrappedNativeYield.network.label}",
- findRichText: true,
- ),
- findsOne,
- );
- });
-
- zGoldenTest(
- """When passing depositedWithNative to true, the modal should use the native token images in the tokenavatars
- (if the token0 or token1 are wrapped natives)""", (tester) async {
- final wrappedNativeYield = YieldDto.fixture().copyWith(
- chainId: AppNetworks.sepolia.chainId,
- token0: AppNetworks.sepolia.wrappedNative,
- token1: AppNetworks.sepolia.wrappedNative,
- );
-
- await tester.pumpDeviceBuilder(
- await goldenBuilder(
- depositedWithNative: true,
- customYield: wrappedNativeYield,
- ),
- wrapper: GoldenConfig.localizationsWrapper(),
- );
- await tester.pumpAndSettle();
-
- final tokenAvatars = find.byType(TokenAvatar).evaluate();
- final token0Avatar = tokenAvatars.first.widget as TokenAvatar;
- final token1Avatar = tokenAvatars.last.widget as TokenAvatar;
-
- expect(token0Avatar.asset, wrappedNativeYield.maybeNativeToken0(permitNative: true));
- expect(token1Avatar.asset, wrappedNativeYield.maybeNativeToken1(permitNative: true));
- });
}
diff --git a/test/app/create/deposit/widgets/preview_deposit_modal/preview_deposit_modal_cubit_test.dart b/test/app/create/deposit/widgets/preview_deposit_modal/preview_deposit_modal_cubit_test.dart
index d5191c1..464a145 100644
--- a/test/app/create/deposit/widgets/preview_deposit_modal/preview_deposit_modal_cubit_test.dart
+++ b/test/app/create/deposit/widgets/preview_deposit_modal/preview_deposit_modal_cubit_test.dart
@@ -10,20 +10,22 @@ import 'package:web3kit/core/dtos/transaction_receipt.dart';
import 'package:web3kit/core/dtos/transaction_response.dart';
import 'package:web3kit/core/exceptions/ethers_exceptions.dart';
import 'package:zup_app/abis/erc_20.abi.g.dart';
-import 'package:zup_app/abis/uniswap_position_manager.abi.g.dart';
+import 'package:zup_app/abis/uniswap_permit2.abi.g.dart';
import 'package:zup_app/abis/uniswap_v3_pool.abi.g.dart';
+import 'package:zup_app/abis/uniswap_v3_position_manager.abi.g.dart';
import 'package:zup_app/app/create/deposit/widgets/preview_deposit_modal/preview_deposit_modal_cubit.dart';
import 'package:zup_app/core/dtos/token_dto.dart';
import 'package:zup_app/core/dtos/yield_dto.dart';
import 'package:zup_app/core/enums/networks.dart';
+import 'package:zup_app/core/enums/pool_type.dart';
import 'package:zup_app/core/injections.dart';
+import 'package:zup_app/core/pool_service.dart';
import 'package:zup_app/core/slippage.dart';
-import 'package:zup_app/core/v3_pool_constants.dart';
+import 'package:zup_app/core/v3_v4_pool_constants.dart';
import 'package:zup_app/core/zup_analytics.dart';
import 'package:zup_app/widgets/zup_cached_image.dart';
import '../../../../../golden_config.dart';
-import '../../../../../matchers.dart';
import '../../../../../mocks.dart';
import '../../../../../wrappers.dart';
@@ -40,9 +42,12 @@ void main() {
late Wallet wallet;
late Signer signer;
late TransactionResponse transactionResponse;
- late UniswapPositionManager uniswapPositionManager;
- late UniswapPositionManagerImpl uniswapPositionManagerImpl;
+ late UniswapV3PositionManager uniswapPositionManager;
+ late UniswapV3PositionManagerImpl uniswapPositionManagerImpl;
late ZupAnalytics zupAnalytics;
+ late UniswapPermit2 permit2;
+ late UniswapPermit2Impl permit2Impl;
+ late PoolService poolService;
setUp(() {
uniswapV3Pool = UniswapV3PoolMock();
@@ -52,20 +57,23 @@ void main() {
erc20Impl = Erc20ImplMock();
signer = SignerMock();
transactionResponse = TransactionResponseMock();
- uniswapPositionManager = UniswapPositionManagerMock();
- uniswapPositionManagerImpl = UniswapPositionManagerImplMock();
+ uniswapPositionManager = UniswapV3PositionManagerMock();
+ uniswapPositionManagerImpl = UniswapV3PositionManagerImplMock();
zupAnalytics = ZupAnalyticsMock();
+ permit2 = UniswapPermit2Mock();
+ permit2Impl = UniswapPermit2ImplMock();
+ poolService = PoolServiceMock();
sut = PreviewDepositModalCubit(
initialPoolTick: initialPoolTick,
- uniswapV3Pool: uniswapV3Pool,
currentYield: currentYield,
erc20: erc20,
wallet: wallet,
- depositWithNative: false,
uniswapPositionManager: uniswapPositionManager,
navigatorKey: GlobalKey(),
zupAnalytics: zupAnalytics,
+ permit2: permit2,
+ poolService: poolService,
);
registerFallbackValue(const ChainInfo(hexChainId: "0x1"));
@@ -73,6 +81,7 @@ void main() {
registerFallbackValue(BigInt.one);
registerFallbackValue((amount: BigInt.from(1), token: ""));
registerFallbackValue(YieldDto.fixture());
+ registerFallbackValue(Duration.zero);
registerFallbackValue((
amount0Desired: BigInt.zero,
amount0Min: BigInt.zero,
@@ -113,6 +122,11 @@ void main() {
unlocked: true,
));
+ when(() => permit2.fromSigner(contractAddress: any(named: "contractAddress"), signer: any(named: "signer")))
+ .thenReturn(permit2Impl);
+ when(() => permit2.fromRpcProvider(contractAddress: any(named: "contractAddress"), rpcUrl: any(named: "rpcUrl")))
+ .thenReturn(permit2Impl);
+
when(() => erc20.fromRpcProvider(contractAddress: any(named: "contractAddress"), rpcUrl: any(named: "rpcUrl")))
.thenReturn(erc20Impl);
@@ -131,6 +145,18 @@ void main() {
(_) async => TransactionReceipt(hash: transactionHash),
);
+ when(
+ () => permit2Impl.approve(
+ token: any(named: "token"),
+ spender: any(named: "spender"),
+ amount: any(named: "amount"),
+ expiration: any(named: "expiration")),
+ ).thenAnswer((_) async => transactionResponse);
+
+ when(() => permit2Impl.allowance(any(), any(), any())).thenAnswer(
+ (_) async => (amount: BigInt.zero, expiration: BigInt.zero, nonce: BigInt.zero),
+ );
+
when(() => wallet.connectedNetwork).thenAnswer((_) async => currentYield.network.chainInfo);
when(() => uniswapPositionManager.fromRpcProvider(
@@ -147,6 +173,16 @@ void main() {
).thenAnswer((_) async => transactionResponse);
when(() => uniswapPositionManager.getMintCalldata(params: any(named: "params"))).thenReturn("0x");
+
+ when(() => poolService.sendV3PoolDepositTransaction(any(), any(),
+ amount0Desired: any(named: "amount0Desired"),
+ amount1Desired: any(named: "amount1Desired"),
+ amount0Min: any(named: "amount0Min"),
+ amount1Min: any(named: "amount1Min"),
+ deadline: any(named: "deadline"),
+ recipient: any(named: "recipient"),
+ tickLower: any(named: "tickLower"),
+ tickUpper: any(named: "tickUpper"))).thenAnswer((_) async => transactionResponse);
});
void sutCopyWith({
@@ -155,17 +191,19 @@ void main() {
UniswapV3Pool? customUniswapV3Pool,
Erc20? customErc20,
Wallet? customWallet,
- UniswapPositionManager? customUniswapPositionManager,
+ UniswapV3PositionManager? customUniswapPositionManager,
YieldDto? customYield,
GlobalKey? customNavigatorKey,
+ UniswapPermit2? customPermit2,
+ PoolService? customPoolService,
}) {
sut = PreviewDepositModalCubit(
initialPoolTick: customInitialPoolTick ?? initialPoolTick,
- uniswapV3Pool: customUniswapV3Pool ?? uniswapV3Pool,
+ permit2: customPermit2 ?? permit2,
+ poolService: customPoolService ?? poolService,
currentYield: customYield ?? currentYield,
erc20: customErc20 ?? erc20,
wallet: customWallet ?? wallet,
- depositWithNative: customDepositWithNative ?? false,
uniswapPositionManager: customUniswapPositionManager ?? uniswapPositionManager,
navigatorKey: customNavigatorKey ?? GlobalKey(),
zupAnalytics: zupAnalytics,
@@ -197,15 +235,7 @@ void main() {
BigInt latestEmittedTick = BigInt.zero;
const minutesPassed = 2;
- when(() => uniswapV3PoolImpl.slot0()).thenAnswer((_) async => (
- feeProtocol: BigInt.zero,
- observationCardinality: BigInt.zero,
- observationCardinalityNext: BigInt.zero,
- observationIndex: BigInt.zero,
- sqrtPriceX96: BigInt.zero,
- tick: expectedEmittedTick,
- unlocked: true,
- ));
+ when(() => poolService.getPoolTick(any())).thenAnswer((_) async => expectedEmittedTick);
fakeAsync((async) {
sut.setup();
@@ -233,7 +263,7 @@ void main() {
expectedEmittedTick,
reason: "`latestPoolTick` should be updated",
);
- verify(() => uniswapV3PoolImpl.slot0()).called(2);
+ verify(() => poolService.getPoolTick(any())).called(2);
},
);
@@ -271,11 +301,11 @@ void main() {
sut = PreviewDepositModalCubit(
uniswapPositionManager: uniswapPositionManager,
initialPoolTick: initialPoolTick,
- uniswapV3Pool: uniswapV3Pool,
+ permit2: permit2,
+ poolService: poolService,
currentYield: customYield,
erc20: erc20,
wallet: wallet,
- depositWithNative: false,
navigatorKey: GlobalKey(),
zupAnalytics: zupAnalytics);
@@ -331,16 +361,19 @@ void main() {
sut = PreviewDepositModalCubit(
navigatorKey: GlobalKey(),
initialPoolTick: initialPoolTick,
- uniswapV3Pool: uniswapV3Pool,
+ permit2: permit2,
+ poolService: poolService,
currentYield: customYield,
erc20: erc20,
wallet: wallet,
- depositWithNative: false,
uniswapPositionManager: uniswapPositionManager,
zupAnalytics: zupAnalytics);
when(() => wallet.switchOrAddNetwork(any())).thenAnswer((_) async {});
when(() => wallet.connectedNetwork).thenAnswer((_) async => AppNetworks.mainnet.chainInfo);
+ when(() => permit2Impl.allowance(any(), any(), any())).thenAnswer(
+ (_) async => (amount: BigInt.zero, expiration: BigInt.zero, nonce: BigInt.zero),
+ );
await sut.approveToken(currentYield.token0, BigInt.from(32761));
@@ -360,11 +393,11 @@ void main() {
navigatorKey: GlobalKey(),
uniswapPositionManager: uniswapPositionManager,
initialPoolTick: initialPoolTick,
- uniswapV3Pool: uniswapV3Pool,
+ permit2: permit2,
+ poolService: poolService,
currentYield: customYield,
erc20: erc20,
wallet: wallet,
- depositWithNative: false,
zupAnalytics: zupAnalytics);
when(() => wallet.switchOrAddNetwork(any())).thenAnswer((_) async {});
@@ -720,7 +753,7 @@ void main() {
},
);
- test("When calling `deposit`, the tokens amount sent to the contract call should match the passed values", () async {
+ test("When calling `deposit`, the tokens amount sent to the pool service to deposit should be correct", () async {
final token0Amount = BigInt.from(32421);
final token1Amount = BigInt.from(8729889);
@@ -737,25 +770,24 @@ void main() {
);
verify(
- () => uniswapPositionManagerImpl.mint(
- params: any(
- named: "params",
- that: ExpectedMatcher(
- expects: (item) {
- expect(item.amount0Desired, token0Amount);
- expect(item.amount1Desired, token1Amount);
- expect(item.token0, currentYield.token0.addresses[currentYield.network.chainId]!);
- expect(item.token1, currentYield.token1.addresses[currentYield.network.chainId]!);
- },
- ),
- ),
+ () => poolService.sendV3PoolDepositTransaction(
+ any(),
+ any(),
+ amount0Desired: token0Amount,
+ amount1Desired: token1Amount,
+ amount0Min: any(named: "amount0Min"),
+ amount1Min: any(named: "amount1Min"),
+ deadline: any(named: "deadline"),
+ recipient: any(named: "recipient"),
+ tickLower: any(named: "tickLower"),
+ tickUpper: any(named: "tickUpper"),
),
);
});
test(
- """When calling `deposit`, the amount1Min in the depositData should be
- the passed token1 amount minus the slippage percent""",
+ """When calling `deposit`, the amount1Min sent to the pool service to deposit
+ should be the one calculated from the slippage""",
() async {
final token1Amount = BigInt.from(6721);
const slippage = Slippage.halfPercent;
@@ -777,35 +809,29 @@ void main() {
);
verify(
- () => uniswapPositionManager.getMintCalldata(
- params: any(
- named: "params",
- that: ExpectedMatcher(
- expects: (item) {
- expect(
- item.amount1Min,
- slippage.calculateTokenAmountFromSlippage(token1Amount),
- );
- },
- ),
- ),
+ () => poolService.sendV3PoolDepositTransaction(
+ any(),
+ any(),
+ amount0Desired: any(named: "amount0Desired"),
+ amount1Desired: any(named: "amount1Desired"),
+ amount0Min: any(named: "amount0Min"),
+ amount1Min: slippage.calculateMinTokenAmountFromSlippage(token1Amount),
+ deadline: any(named: "deadline"),
+ recipient: any(named: "recipient"),
+ tickLower: any(named: "tickLower"),
+ tickUpper: any(named: "tickUpper"),
),
).called(1);
},
);
test(
- """When calling `deposit`, the amount0Min in the depositData should be
- the passed token0 amount minus the slippage percent""",
+ """When calling `deposit`, the amount0Min sent to the pool service to deposit
+ should be the one calculated from the slippage""",
() async {
final token0Amount = BigInt.from(32421);
final slippage = Slippage.fromValue(50);
- // when(() => feeControllerImpl.calculateJoinPoolFee(
- // token0Amount: any(named: "token0Amount"), token1Amount: any(named: "token1Amount"))).thenAnswer(
- // (_) async => (feeToken0: feeAmount, feeToken1: feeAmount),
- // );
-
when(() => uniswapPositionManager.getMintCalldata(params: any(named: "params"))).thenReturn("");
sutCopyWith(customDepositWithNative: true);
@@ -822,25 +848,24 @@ void main() {
);
verify(
- () => uniswapPositionManager.getMintCalldata(
- params: any(
- named: "params",
- that: ExpectedMatcher(
- expects: (item) {
- expect(
- item.amount0Min,
- slippage.calculateTokenAmountFromSlippage(token0Amount),
- );
- },
- ),
- ),
+ () => poolService.sendV3PoolDepositTransaction(
+ any(),
+ any(),
+ amount0Desired: any(named: "amount0Desired"),
+ amount1Desired: any(named: "amount1Desired"),
+ amount0Min: slippage.calculateMinTokenAmountFromSlippage(token0Amount),
+ amount1Min: any(named: "amount1Min"),
+ deadline: any(named: "deadline"),
+ recipient: any(named: "recipient"),
+ tickLower: any(named: "tickLower"),
+ tickUpper: any(named: "tickUpper"),
),
).called(1);
},
);
test(
- "When calling `deposit`, the recipient in the depositData, should be the connected signer address",
+ "When calling `deposit`, the recipient sent to the pool service to deposit should be the signer address",
() async {
const signerAddress = "0x0000000000000000000000000000000000000231";
when(() => signer.address).thenAnswer((_) async => signerAddress);
@@ -859,17 +884,25 @@ void main() {
isReversed: false,
);
- verify(() => uniswapPositionManager.getMintCalldata(
- params: any(
- named: "params",
- that: ExpectedMatcher(expects: (item) => expect(item.recipient, signerAddress)),
- ),
- )).called(1);
+ verify(
+ () => poolService.sendV3PoolDepositTransaction(
+ any(),
+ any(),
+ amount0Desired: any(named: "amount0Desired"),
+ amount1Desired: any(named: "amount1Desired"),
+ amount0Min: any(named: "amount0Min"),
+ amount1Min: any(named: "amount1Min"),
+ deadline: any(named: "deadline"),
+ recipient: signerAddress,
+ tickLower: any(named: "tickLower"),
+ tickUpper: any(named: "tickUpper"),
+ ),
+ ).called(1);
},
);
test("""When calling `deposit` the minPrice is infinity, and is not reversed,
- the tick lower in the depositData should be the min tick (adjusted for the tick spacing)""", () async {
+ the tick lower send to the pool service should be the min tick (adjusted for the tick spacing)""", () async {
sutCopyWith(customDepositWithNative: true);
await sut.deposit(
@@ -885,25 +918,27 @@ void main() {
);
verify(
- () => uniswapPositionManager.getMintCalldata(
- params: any(
- named: "params",
- that: ExpectedMatcher(
- expects: (item) => expect(
- item.tickLower,
- V3PoolConversorsMixinWrapper().tickToClosestValidTick(
- tick: V3PoolConstants.minTick,
- tickSpacing: currentYield.tickSpacing,
- ),
- ),
- ),
+ () => poolService.sendV3PoolDepositTransaction(
+ any(),
+ any(),
+ amount0Desired: any(named: "amount0Desired"),
+ amount1Desired: any(named: "amount1Desired"),
+ amount0Min: any(named: "amount0Min"),
+ amount1Min: any(named: "amount1Min"),
+ deadline: any(named: "deadline"),
+ recipient: any(named: "recipient"),
+ tickLower: V3PoolConversorsMixinWrapper().tickToClosestValidTick(
+ tick: V3V4PoolConstants.minTick,
+ tickSpacing: currentYield.tickSpacing,
),
+ tickUpper: any(named: "tickUpper"),
),
).called(1);
});
test("""When calling `deposit` with the maxPrice infinity, and reversed,
- the tick lower in the depositData should be the min tick (but adjusted for the tick spacing)""", () async {
+ the tick lower sent to the pool service should be the min tick
+ (but adjusted for the tick spacing)""", () async {
sutCopyWith(customDepositWithNative: true);
await sut.deposit(
@@ -919,25 +954,26 @@ void main() {
);
verify(
- () => uniswapPositionManager.getMintCalldata(
- params: any(
- named: "params",
- that: ExpectedMatcher(
- expects: (item) => expect(
- item.tickLower,
- V3PoolConversorsMixinWrapper().tickToClosestValidTick(
- tick: V3PoolConstants.minTick,
- tickSpacing: currentYield.tickSpacing,
- ),
- ),
- ),
+ () => poolService.sendV3PoolDepositTransaction(
+ any(),
+ any(),
+ amount0Desired: any(named: "amount0Desired"),
+ amount1Desired: any(named: "amount1Desired"),
+ amount0Min: any(named: "amount0Min"),
+ amount1Min: any(named: "amount1Min"),
+ deadline: any(named: "deadline"),
+ recipient: any(named: "recipient"),
+ tickLower: V3PoolConversorsMixinWrapper().tickToClosestValidTick(
+ tick: V3V4PoolConstants.minTick,
+ tickSpacing: currentYield.tickSpacing,
),
+ tickUpper: any(named: "tickUpper"),
),
).called(1);
});
test(
- "When calling `deposit` with a min price that is not infinity, it should calculate the correct tickLower for the depositData",
+ "When calling `deposit` with a min price that is not infinity, it should calculate the correct tickLower and send it to the pool service",
() async {
sutCopyWith(customDepositWithNative: true);
@@ -956,24 +992,25 @@ void main() {
);
verify(
- () => uniswapPositionManager.getMintCalldata(
- params: any(
- named: "params",
- that: ExpectedMatcher(
- expects: (item) => expect(
- item.tickLower,
- V3PoolConversorsMixinWrapper().tickToClosestValidTick(
- tick: V3PoolConversorsMixinWrapper().priceToTick(
- price: minPrice,
- poolToken0Decimals: currentYield.token0.decimals,
- poolToken1Decimals: currentYield.token1.decimals,
- isReversed: false,
- ),
- tickSpacing: currentYield.tickSpacing,
- ),
- ),
+ () => poolService.sendV3PoolDepositTransaction(
+ any(),
+ any(),
+ amount0Desired: any(named: "amount0Desired"),
+ amount1Desired: any(named: "amount1Desired"),
+ amount0Min: any(named: "amount0Min"),
+ amount1Min: any(named: "amount1Min"),
+ deadline: any(named: "deadline"),
+ recipient: any(named: "recipient"),
+ tickLower: V3PoolConversorsMixinWrapper().tickToClosestValidTick(
+ tick: V3PoolConversorsMixinWrapper().priceToTick(
+ price: minPrice,
+ poolToken0Decimals: currentYield.token0.decimals,
+ poolToken1Decimals: currentYield.token1.decimals,
+ isReversed: false,
),
+ tickSpacing: currentYield.tickSpacing,
),
+ tickUpper: any(named: "tickUpper"),
),
).called(1);
},
@@ -981,8 +1018,8 @@ void main() {
test(
"""When calling `deposit` with a max price that is not infinity, and it's reversed,
- it should calculate the correct tickLower for the depositData, using the max price
- (because it's reversed)
+ it should calculate the correct tickLower and, using the max price
+ (because it's reversed), and send it to the pool service
""",
() async {
sutCopyWith(customDepositWithNative: true);
@@ -1003,24 +1040,25 @@ void main() {
);
verify(
- () => uniswapPositionManager.getMintCalldata(
- params: any(
- named: "params",
- that: ExpectedMatcher(
- expects: (item) => expect(
- item.tickLower,
- V3PoolConversorsMixinWrapper().tickToClosestValidTick(
- tick: V3PoolConversorsMixinWrapper().priceToTick(
- price: maxPrice,
- poolToken0Decimals: currentYield.token0.decimals,
- poolToken1Decimals: currentYield.token1.decimals,
- isReversed: isReversed,
- ),
- tickSpacing: currentYield.tickSpacing,
- ),
- ),
+ () => poolService.sendV3PoolDepositTransaction(
+ any(),
+ any(),
+ amount0Desired: any(named: "amount0Desired"),
+ amount1Desired: any(named: "amount1Desired"),
+ amount0Min: any(named: "amount0Min"),
+ amount1Min: any(named: "amount1Min"),
+ deadline: any(named: "deadline"),
+ recipient: any(named: "recipient"),
+ tickLower: V3PoolConversorsMixinWrapper().tickToClosestValidTick(
+ tick: V3PoolConversorsMixinWrapper().priceToTick(
+ price: maxPrice,
+ poolToken0Decimals: currentYield.token0.decimals,
+ poolToken1Decimals: currentYield.token1.decimals,
+ isReversed: isReversed,
),
+ tickSpacing: currentYield.tickSpacing,
),
+ tickUpper: any(named: "tickUpper"),
),
).called(1);
},
@@ -1028,7 +1066,7 @@ void main() {
test(
"""When calling `deposit` with a max price that is infinity, and it's not reversed,
- the tick upper in the depositData should be the max tick (but adjusted for the tick spacing)""",
+ the tick upper should be the max tick (but adjusted for the tick spacing)""",
() async {
sutCopyWith(customDepositWithNative: true);
@@ -1045,18 +1083,19 @@ void main() {
);
verify(
- () => uniswapPositionManager.getMintCalldata(
- params: any(
- named: "params",
- that: ExpectedMatcher(
- expects: (item) => expect(
- item.tickUpper,
- V3PoolConversorsMixinWrapper().tickToClosestValidTick(
- tick: V3PoolConstants.maxTick,
- tickSpacing: currentYield.tickSpacing,
- ),
- ),
- ),
+ () => poolService.sendV3PoolDepositTransaction(
+ any(),
+ any(),
+ amount0Desired: any(named: "amount0Desired"),
+ amount1Desired: any(named: "amount1Desired"),
+ amount0Min: any(named: "amount0Min"),
+ amount1Min: any(named: "amount1Min"),
+ deadline: any(named: "deadline"),
+ recipient: any(named: "recipient"),
+ tickLower: any(named: "tickLower"),
+ tickUpper: V3PoolConversorsMixinWrapper().tickToClosestValidTick(
+ tick: V3V4PoolConstants.maxTick,
+ tickSpacing: currentYield.tickSpacing,
),
),
).called(1);
@@ -1065,7 +1104,7 @@ void main() {
test(
"""When calling `deposit` with a min price that is infinity, and it's reversed,
- the tick upper in the depositData should be the max tick (but adjusted for the tick spacing)""",
+ the tick upper should be the max tick (but adjusted for the tick spacing)""",
() async {
sutCopyWith(customDepositWithNative: true);
@@ -1083,20 +1122,20 @@ void main() {
isMaxPriceInfinity: false,
isReversed: isReversed,
);
-
verify(
- () => uniswapPositionManager.getMintCalldata(
- params: any(
- named: "params",
- that: ExpectedMatcher(
- expects: (item) => expect(
- item.tickUpper,
- V3PoolConversorsMixinWrapper().tickToClosestValidTick(
- tick: V3PoolConstants.maxTick,
- tickSpacing: currentYield.tickSpacing,
- ),
- ),
- ),
+ () => poolService.sendV3PoolDepositTransaction(
+ any(),
+ any(),
+ amount0Desired: any(named: "amount0Desired"),
+ amount1Desired: any(named: "amount1Desired"),
+ amount0Min: any(named: "amount0Min"),
+ amount1Min: any(named: "amount1Min"),
+ deadline: any(named: "deadline"),
+ recipient: any(named: "recipient"),
+ tickLower: any(named: "tickLower"),
+ tickUpper: V3PoolConversorsMixinWrapper().tickToClosestValidTick(
+ tick: V3V4PoolConstants.maxTick,
+ tickSpacing: currentYield.tickSpacing,
),
),
).called(1);
@@ -1105,7 +1144,7 @@ void main() {
test(
"""When calling `deposit` with a max price that is not infinity,
- and it's not reversed, the tick upper in the depositData should be
+ and it's not reversed, the tick upper should be
calculated based on the max price""",
() async {
sutCopyWith(customDepositWithNative: true);
@@ -1125,23 +1164,24 @@ void main() {
);
verify(
- () => uniswapPositionManager.getMintCalldata(
- params: any(
- named: "params",
- that: ExpectedMatcher(
- expects: (item) => expect(
- item.tickUpper,
- V3PoolConversorsMixinWrapper().tickToClosestValidTick(
- tick: V3PoolConversorsMixinWrapper().priceToTick(
- price: maxPrice,
- poolToken0Decimals: currentYield.token0.decimals,
- poolToken1Decimals: currentYield.token1.decimals,
- isReversed: false,
- ),
- tickSpacing: currentYield.tickSpacing,
- ),
- ),
+ () => poolService.sendV3PoolDepositTransaction(
+ any(),
+ any(),
+ amount0Desired: any(named: "amount0Desired"),
+ amount1Desired: any(named: "amount1Desired"),
+ amount0Min: any(named: "amount0Min"),
+ amount1Min: any(named: "amount1Min"),
+ deadline: any(named: "deadline"),
+ recipient: any(named: "recipient"),
+ tickLower: any(named: "tickLower"),
+ tickUpper: V3PoolConversorsMixinWrapper().tickToClosestValidTick(
+ tick: V3PoolConversorsMixinWrapper().priceToTick(
+ price: maxPrice,
+ poolToken0Decimals: currentYield.token0.decimals,
+ poolToken1Decimals: currentYield.token1.decimals,
+ isReversed: false,
),
+ tickSpacing: currentYield.tickSpacing,
),
),
).called(1);
@@ -1150,7 +1190,7 @@ void main() {
test(
"""When calling `deposit` with a max price that is not infinity,
- and it's reversed, the tick upper in the depositData should be
+ and it's reversed, the tick upper should be
calculated based on the min price""",
() async {
sutCopyWith(customDepositWithNative: true);
@@ -1171,110 +1211,30 @@ void main() {
);
verify(
- () => uniswapPositionManager.getMintCalldata(
- params: any(
- named: "params",
- that: ExpectedMatcher(
- expects: (item) => expect(
- item.tickUpper,
- V3PoolConversorsMixinWrapper().tickToClosestValidTick(
- tick: V3PoolConversorsMixinWrapper().priceToTick(
- price: minPrice,
- poolToken0Decimals: currentYield.token0.decimals,
- poolToken1Decimals: currentYield.token1.decimals,
- isReversed: isReversed,
- ),
- tickSpacing: currentYield.tickSpacing,
- ),
- ),
+ () => poolService.sendV3PoolDepositTransaction(
+ any(),
+ any(),
+ amount0Desired: any(named: "amount0Desired"),
+ amount1Desired: any(named: "amount1Desired"),
+ amount0Min: any(named: "amount0Min"),
+ amount1Min: any(named: "amount1Min"),
+ deadline: any(named: "deadline"),
+ recipient: any(named: "recipient"),
+ tickLower: any(named: "tickLower"),
+ tickUpper: V3PoolConversorsMixinWrapper().tickToClosestValidTick(
+ tick: V3PoolConversorsMixinWrapper().priceToTick(
+ price: minPrice,
+ poolToken0Decimals: currentYield.token0.decimals,
+ poolToken1Decimals: currentYield.token1.decimals,
+ isReversed: isReversed,
),
+ tickSpacing: currentYield.tickSpacing,
),
),
).called(1);
},
);
- test("When calling `deposit` the fee in the depositData should be the feeTier of the yield pool", () async {
- sutCopyWith(customDepositWithNative: true);
-
- await sut.deposit(
- deadline: const Duration(minutes: 30),
- slippage: Slippage.halfPercent,
- token0Amount: BigInt.one,
- token1Amount: BigInt.one,
- minPrice: 1200,
- maxPrice: 3000.50,
- isMinPriceInfinity: false,
- isMaxPriceInfinity: false,
- isReversed: false,
- );
-
- verify(
- () => uniswapPositionManager.getMintCalldata(
- params: any(
- named: "params",
- that: ExpectedMatcher(
- expects: (item) => expect(item.fee, BigInt.from(currentYield.feeTier)),
- ),
- ),
- ),
- ).called(1);
- });
-
- test("When calling `deposit` the token0 in the depositData should be the token0 of the yield pool", () async {
- sutCopyWith(customDepositWithNative: true);
-
- await sut.deposit(
- deadline: const Duration(minutes: 30),
- slippage: Slippage.halfPercent,
- token0Amount: BigInt.one,
- token1Amount: BigInt.one,
- minPrice: 1200,
- maxPrice: 3000.50,
- isMinPriceInfinity: false,
- isMaxPriceInfinity: false,
- isReversed: false,
- );
-
- verify(
- () => uniswapPositionManager.getMintCalldata(
- params: any(
- named: "params",
- that: ExpectedMatcher(
- expects: (item) => expect(item.token0, currentYield.token0.addresses[currentYield.network.chainId]!),
- ),
- ),
- ),
- ).called(1);
- });
-
- test("When calling `deposit` the token1 in the depositData should be the token1 of the yield pool", () async {
- sutCopyWith(customDepositWithNative: true);
-
- await sut.deposit(
- deadline: const Duration(minutes: 30),
- slippage: Slippage.halfPercent,
- token0Amount: BigInt.one,
- token1Amount: BigInt.one,
- minPrice: 1200,
- maxPrice: 3000.50,
- isMinPriceInfinity: false,
- isMaxPriceInfinity: false,
- isReversed: false,
- );
-
- verify(
- () => uniswapPositionManager.getMintCalldata(
- params: any(
- named: "params",
- that: ExpectedMatcher(
- expects: (item) => expect(item.token1, currentYield.token1.addresses[currentYield.network.chainId]!),
- ),
- ),
- ),
- ).called(1);
- });
-
test(
"When calling `deposit`, after the transcation to deposit is sent, it should emit the waiting transaction state",
() async {
@@ -1283,8 +1243,17 @@ void main() {
when(() => transactionResponse.hash).thenReturn(txId);
when(
- () => uniswapPositionManagerImpl.mint(
- params: any(named: "params"),
+ () => poolService.sendV3PoolDepositTransaction(
+ any(),
+ any(),
+ amount0Desired: any(named: "amount0Desired"),
+ amount1Desired: any(named: "amount1Desired"),
+ amount0Min: any(named: "amount0Min"),
+ amount1Min: any(named: "amount1Min"),
+ deadline: any(named: "deadline"),
+ recipient: any(named: "recipient"),
+ tickLower: any(named: "tickLower"),
+ tickUpper: any(named: "tickUpper"),
),
).thenAnswer((_) async {
expectLater(
@@ -1381,7 +1350,18 @@ void main() {
and right after, it should emit the initial state""",
() async {
when(
- () => uniswapPositionManagerImpl.mint(params: any(named: "params")),
+ () => poolService.sendV3PoolDepositTransaction(
+ any(),
+ any(),
+ amount0Desired: any(named: "amount0Desired"),
+ amount1Desired: any(named: "amount1Desired"),
+ amount0Min: any(named: "amount0Min"),
+ amount1Min: any(named: "amount1Min"),
+ deadline: any(named: "deadline"),
+ recipient: any(named: "recipient"),
+ tickLower: any(named: "tickLower"),
+ tickUpper: any(named: "tickUpper"),
+ ),
).thenThrow("dale error");
expectLater(
@@ -1411,7 +1391,18 @@ void main() {
"When calling `deposit` and an error of User rejected action occurs, it should emit the initial state",
() async {
when(
- () => uniswapPositionManagerImpl.mint(params: any(named: "params")),
+ () => poolService.sendV3PoolDepositTransaction(
+ any(),
+ any(),
+ amount0Desired: any(named: "amount0Desired"),
+ amount1Desired: any(named: "amount1Desired"),
+ amount0Min: any(named: "amount0Min"),
+ amount1Min: any(named: "amount1Min"),
+ deadline: any(named: "deadline"),
+ recipient: any(named: "recipient"),
+ tickLower: any(named: "tickLower"),
+ tickUpper: any(named: "tickUpper"),
+ ),
).thenThrow(UserRejectedAction());
expectLater(
@@ -1443,7 +1434,6 @@ void main() {
withClock(Clock(() => date), () async {
const deadline = Duration(minutes: 54);
- final expectedDeadline = date.add(deadline).millisecondsSinceEpoch;
when(() => uniswapPositionManagerImpl.multicall(data: any(named: "data")))
.thenAnswer((_) async => transactionResponse);
@@ -1463,13 +1453,17 @@ void main() {
);
verify(
- () => uniswapPositionManager.getMintCalldata(
- params: any(
- named: "params",
- that: ExpectedMatcher(
- expects: (item) => expect(item.deadline, BigInt.from(expectedDeadline)),
- ),
- ),
+ () => poolService.sendV3PoolDepositTransaction(
+ any(),
+ any(),
+ amount0Desired: any(named: "amount0Desired"),
+ amount1Desired: any(named: "amount1Desired"),
+ amount0Min: any(named: "amount0Min"),
+ amount1Min: any(named: "amount1Min"),
+ deadline: deadline,
+ recipient: any(named: "recipient"),
+ tickLower: any(named: "tickLower"),
+ tickUpper: any(named: "tickUpper"),
),
).called(1);
});
@@ -1650,7 +1644,18 @@ void main() {
it should emit the slippage check error state and the initial state""",
() async {
when(
- () => uniswapPositionManagerImpl.mint(params: any(named: "params")),
+ () => poolService.sendV3PoolDepositTransaction(
+ any(),
+ any(),
+ amount0Desired: any(named: "amount0Desired"),
+ amount1Desired: any(named: "amount1Desired"),
+ amount0Min: any(named: "amount0Min"),
+ amount1Min: any(named: "amount1Min"),
+ deadline: any(named: "deadline"),
+ recipient: any(named: "recipient"),
+ tickLower: any(named: "tickLower"),
+ tickUpper: any(named: "tickUpper"),
+ ),
).thenThrow("SLIPPAGE_ERROR");
expectLater(
@@ -1729,4 +1734,259 @@ void main() {
));
},
);
+
+ test(
+ "When calling `approveToken` and the pool type is v4, it should approve the permit2 contract as well ",
+ () async {
+ when(() => permit2Impl.allowance(any(), any(), any()))
+ .thenAnswer((_) async => (amount: BigInt.zero, expiration: BigInt.zero, nonce: BigInt.zero));
+
+ when(
+ () => permit2Impl.approve(
+ token: any(named: "token"),
+ spender: any(named: "spender"),
+ amount: any(named: "amount"),
+ expiration: any(named: "expiration")),
+ ).thenAnswer((_) async => transactionResponse);
+
+ const permit2Address = "0x1234";
+ final currentYield0 = currentYield.copyWith(poolType: PoolType.v4, permit2: permit2Address);
+
+ sut = PreviewDepositModalCubit(
+ initialPoolTick: initialPoolTick,
+ poolService: poolService,
+ currentYield: currentYield0,
+ erc20: erc20,
+ wallet: wallet,
+ uniswapPositionManager: uniswapPositionManager,
+ permit2: permit2,
+ navigatorKey: GlobalKey(),
+ zupAnalytics: zupAnalytics,
+ );
+
+ final token = currentYield.token0;
+ final value = BigInt.one;
+
+ await sut.approveToken(token, value);
+
+ verify(
+ () => permit2Impl.approve(
+ token: token.addresses[currentYield.network.chainId]!,
+ spender: currentYield0.positionManagerAddress,
+ amount: EthereumConstants.uint160Max,
+ expiration: EthereumConstants.uint48Max,
+ ),
+ ).called(1);
+ },
+ );
+
+ test(
+ "When calling `approveToken` with the pool type v4, the allowance is more than the needed value, and the expiration is not expired, it should not approve the permit2 contract ",
+ () async {
+ final allowedAmount = BigInt.from(1275);
+
+ when(() => permit2Impl.allowance(any(), any(), any())).thenAnswer(
+ (_) async => (amount: allowedAmount, expiration: EthereumConstants.uint48Max, nonce: BigInt.zero),
+ );
+
+ when(
+ () => permit2Impl.approve(
+ token: any(named: "token"),
+ spender: any(named: "spender"),
+ amount: any(named: "amount"),
+ expiration: any(named: "expiration")),
+ ).thenAnswer((_) async => transactionResponse);
+
+ const permit2Address = "0x1234";
+ final currentYield0 = currentYield.copyWith(poolType: PoolType.v4, permit2: permit2Address);
+
+ sut = PreviewDepositModalCubit(
+ initialPoolTick: initialPoolTick,
+ poolService: poolService,
+ currentYield: currentYield0,
+ erc20: erc20,
+ wallet: wallet,
+ uniswapPositionManager: uniswapPositionManager,
+ permit2: permit2,
+ navigatorKey: GlobalKey(),
+ zupAnalytics: zupAnalytics,
+ );
+
+ final token = currentYield.token0;
+ final value = allowedAmount - BigInt.one;
+
+ await sut.approveToken(token, value);
+
+ verifyNever(
+ () => permit2Impl.approve(
+ token: token.addresses[currentYield.network.chainId]!,
+ spender: currentYield0.positionManagerAddress,
+ amount: EthereumConstants.uint160Max,
+ expiration: EthereumConstants.uint48Max,
+ ),
+ );
+ },
+ );
+
+ test(
+ """When calling `approveToken` with the pool type v4, the allowance is more than the needed value,
+ but the expiration is already expired,it should approve the permit2 contract""",
+ () async {
+ final allowedAmount = BigInt.from(1275);
+
+ when(() => permit2Impl.allowance(any(), any(), any())).thenAnswer(
+ (_) async => (
+ amount: allowedAmount,
+ expiration: BigInt.from((DateTime.now().millisecondsSinceEpoch / 1000) - 1),
+ nonce: BigInt.zero
+ ),
+ );
+
+ when(
+ () => permit2Impl.approve(
+ token: any(named: "token"),
+ spender: any(named: "spender"),
+ amount: any(named: "amount"),
+ expiration: any(named: "expiration")),
+ ).thenAnswer((_) async => transactionResponse);
+
+ const permit2Address = "0x1234";
+ final currentYield0 = currentYield.copyWith(poolType: PoolType.v4, permit2: permit2Address);
+
+ sut = PreviewDepositModalCubit(
+ initialPoolTick: initialPoolTick,
+ poolService: poolService,
+ currentYield: currentYield0,
+ erc20: erc20,
+ wallet: wallet,
+ uniswapPositionManager: uniswapPositionManager,
+ permit2: permit2,
+ navigatorKey: GlobalKey(),
+ zupAnalytics: zupAnalytics,
+ );
+
+ final token = currentYield.token0;
+ final value = allowedAmount - BigInt.one;
+
+ await sut.approveToken(token, value);
+
+ verify(
+ () => permit2Impl.approve(
+ token: token.addresses[currentYield.network.chainId]!,
+ spender: currentYield0.positionManagerAddress,
+ amount: EthereumConstants.uint160Max,
+ expiration: EthereumConstants.uint48Max,
+ ),
+ ).called(1);
+ },
+ );
+
+ test(
+ "When calling `approveToken` and the pool type is v4, it should approve the token for the permit2 address",
+ () async {
+ const permit2Address = "0x1234";
+ final currentYield0 = currentYield.copyWith(poolType: PoolType.v4, permit2: permit2Address);
+ when(() => permit2Impl.allowance(any(), any(), any())).thenAnswer(
+ (_) async => (amount: BigInt.zero, expiration: BigInt.zero, nonce: BigInt.zero),
+ );
+
+ sut = PreviewDepositModalCubit(
+ initialPoolTick: initialPoolTick,
+ poolService: poolService,
+ currentYield: currentYield0,
+ erc20: erc20,
+ wallet: wallet,
+ uniswapPositionManager: uniswapPositionManager,
+ permit2: permit2,
+ navigatorKey: GlobalKey(),
+ zupAnalytics: zupAnalytics,
+ );
+
+ final token = currentYield.token0;
+ final value = BigInt.one;
+
+ await sut.approveToken(token, value);
+
+ verify(
+ () => erc20Impl.approve(
+ spender: permit2Address,
+ value: value,
+ ),
+ ).called(1);
+ },
+ );
+
+ test(
+ "when calling `deposit` and the pool type is v4, it should call the pool service to deposit on v4 with the correct parameters",
+ () async {
+ final currentYield0 = currentYield.copyWith(
+ poolType: PoolType.v4,
+ permit2: "0x1234",
+ );
+ final token0Amount = BigInt.one;
+ final token1Amount = BigInt.two;
+ const minPrice = 1200.43;
+ const maxPrice = 4000.12;
+ const isMinPriceInfinity = false;
+ const isMaxPriceInfinity = false;
+ const isReversed = false;
+ final slippage = Slippage.fromValue(32);
+ const deadline = Duration(minutes: 30);
+ final recipient = await signer.address;
+ final tickLower = V3PoolConversorsMixinWrapper().tickToClosestValidTick(
+ tick: V3PoolConversorsMixinWrapper().priceToTick(
+ price: minPrice,
+ poolToken0Decimals: currentYield0.token0.decimals,
+ poolToken1Decimals: currentYield0.token1.decimals,
+ ),
+ tickSpacing: currentYield0.tickSpacing);
+
+ final tickUpper = V3PoolConversorsMixinWrapper().tickToClosestValidTick(
+ tick: V3PoolConversorsMixinWrapper().priceToTick(
+ price: maxPrice,
+ poolToken0Decimals: currentYield0.token0.decimals,
+ poolToken1Decimals: currentYield0.token1.decimals,
+ ),
+ tickSpacing: currentYield0.tickSpacing);
+
+ sut = PreviewDepositModalCubit(
+ initialPoolTick: initialPoolTick,
+ poolService: poolService,
+ currentYield: currentYield0,
+ erc20: erc20,
+ wallet: wallet,
+ uniswapPositionManager: uniswapPositionManager,
+ permit2: permit2,
+ navigatorKey: GlobalKey(),
+ zupAnalytics: zupAnalytics,
+ );
+
+ await sut.deposit(
+ token0Amount: token0Amount,
+ token1Amount: token1Amount,
+ minPrice: minPrice,
+ maxPrice: maxPrice,
+ isMinPriceInfinity: isMinPriceInfinity,
+ isMaxPriceInfinity: isMaxPriceInfinity,
+ isReversed: isReversed,
+ slippage: slippage,
+ deadline: deadline,
+ );
+
+ verify(
+ () => poolService.sendV4PoolDepositTransaction(
+ currentYield0,
+ signer,
+ amount0toDeposit: token0Amount,
+ amount1ToDeposit: token1Amount,
+ maxAmount0ToDeposit: slippage.calculateMaxTokenAmountFromSlippage(token0Amount),
+ maxAmount1ToDeposit: slippage.calculateMaxTokenAmountFromSlippage(token1Amount),
+ deadline: deadline,
+ tickLower: tickLower,
+ tickUpper: tickUpper,
+ recipient: recipient,
+ currentPoolTick: initialPoolTick,
+ ),
+ ).called(1);
+ });
}
diff --git a/test/app/create/deposit/widgets/preview_deposit_modal/preview_deposit_modal_test.dart b/test/app/create/deposit/widgets/preview_deposit_modal/preview_deposit_modal_test.dart
index 296fcd4..1581893 100644
--- a/test/app/create/deposit/widgets/preview_deposit_modal/preview_deposit_modal_test.dart
+++ b/test/app/create/deposit/widgets/preview_deposit_modal/preview_deposit_modal_test.dart
@@ -9,17 +9,20 @@ import 'package:mocktail/mocktail.dart';
import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart';
import 'package:web3kit/web3kit.dart';
import 'package:zup_app/abis/erc_20.abi.g.dart';
-import 'package:zup_app/abis/uniswap_position_manager.abi.g.dart';
+import 'package:zup_app/abis/uniswap_permit2.abi.g.dart';
import 'package:zup_app/abis/uniswap_v3_pool.abi.g.dart';
+import 'package:zup_app/abis/uniswap_v3_position_manager.abi.g.dart';
import 'package:zup_app/app/create/deposit/widgets/preview_deposit_modal/preview_deposit_modal.dart';
import 'package:zup_app/app/create/deposit/widgets/preview_deposit_modal/preview_deposit_modal_cubit.dart';
import 'package:zup_app/core/dtos/token_dto.dart';
import 'package:zup_app/core/dtos/yield_dto.dart';
import 'package:zup_app/core/enums/networks.dart';
import 'package:zup_app/core/injections.dart';
+import 'package:zup_app/core/pool_service.dart';
import 'package:zup_app/core/slippage.dart';
-import 'package:zup_app/core/v3_pool_constants.dart';
+import 'package:zup_app/core/v3_v4_pool_constants.dart';
import 'package:zup_app/core/zup_analytics.dart';
+import 'package:zup_app/core/zup_links.dart';
import 'package:zup_app/core/zup_navigator.dart';
import 'package:zup_app/widgets/zup_cached_image.dart';
import 'package:zup_ui_kit/zup_ui_kit.dart';
@@ -33,6 +36,8 @@ void main() {
late ZupNavigator navigator;
late UrlLauncherPlatform urlLauncherPlatform;
late ConfettiController confettiController;
+ late PoolService poolService;
+ late UniswapPermit2 permit2;
final scaffoldMessengerKey = GlobalKey();
final currentYield = YieldDto.fixture();
@@ -41,6 +46,8 @@ void main() {
cubit = PreviewDepositModalCubitMock();
urlLauncherPlatform = UrlLauncherPlatformCustomMock();
confettiController = ConfettiControllerMock();
+ poolService = PoolServiceMock();
+ permit2 = UniswapPermit2Mock();
UrlLauncherPlatform.instance = urlLauncherPlatform;
@@ -52,10 +59,12 @@ void main() {
() => confettiController,
instanceName: InjectInstanceNames.confettiController10s,
);
+ inject.registerFactory(() => ZupLinksMock());
+ inject.registerFactory(() => permit2);
inject.registerFactory(() => ZupAnalyticsMock());
inject.registerFactory(() => UniswapV3PoolMock());
inject.registerFactory(() => Erc20Mock());
- inject.registerFactory(() => UniswapPositionManagerMock());
+ inject.registerFactory(() => UniswapV3PositionManagerMock());
inject.registerFactory(() => WalletMock());
inject.registerLazySingleton(() => cubit);
inject.registerFactory(() => mockZupCachedImage());
@@ -65,10 +74,11 @@ void main() {
() => GoldenConfig.scrollController,
instanceName: InjectInstanceNames.appScrollController,
);
+ inject.registerFactory(() => poolService);
inject.registerFactory>(() => GlobalKey());
when(() => cubit.setup()).thenAnswer((_) async {});
- when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(V3PoolConstants.maxTick));
+ when(() => cubit.poolTickStream).thenAnswer((_) => Stream.value(V3V4PoolConstants.maxTick));
when(() => cubit.latestPoolTick).thenReturn(BigInt.from(3247));
when(() => cubit.stream).thenAnswer((_) => Stream.value(
PreviewDepositModalState.initial(
@@ -116,7 +126,6 @@ void main() {
double token1DepositAmount = 3,
Duration deadline = const Duration(minutes: 30),
Slippage slippage = Slippage.halfPercent,
- bool depositWithNativeToken = false,
}) =>
goldenDeviceBuilder(
Builder(builder: (context) {
@@ -129,7 +138,6 @@ void main() {
value: cubit,
child: PreviewDepositModal(
yieldTimeFrame: YieldTimeFrame.day,
- depositWithNativeToken: depositWithNativeToken,
deadline: deadline,
maxSlippage: slippage,
currentYield: customYield ?? currentYield,
@@ -292,28 +300,6 @@ void main() {
},
);
- zGoldenTest(
- "When depositWithNativeToken is true, the wrapped native token should be the native token",
- goldenFileName: "preview_deposit_modal_deposit_with_native_token",
- (tester) async {
- await tester.pumpDeviceBuilder(
- await goldenBuilder(
- depositWithNativeToken: true,
- customYield: currentYield.copyWith(
- token0: TokenDto.fixture().copyWith(addresses: {
- currentYield.network.chainId: currentYield.network.wrappedNative.addresses[currentYield.network.chainId]
- }),
- token1: TokenDto.fixture().copyWith(addresses: {
- currentYield.network.chainId: currentYield.network.wrappedNative.addresses[currentYield.network.chainId]
- }),
- ),
- ),
- wrapper: GoldenConfig.localizationsWrapper());
-
- await tester.pumpAndSettle();
- },
- );
-
zGoldenTest(
"When the min price is higher than the current price, it should be in out of range state",
goldenFileName: "preview_deposit_modal_out_of_range_min_price",
@@ -928,7 +914,6 @@ void main() {
await tester.pumpAndSettle();
},
);
-
zGoldenTest(
"When calling `.show` method and the device is mobile, it should should a bottom sheet instead of a dialog",
goldenFileName: "preview_deposit_modal_show_mobile",
@@ -940,7 +925,6 @@ void main() {
WidgetsBinding.instance.addPostFrameCallback((_) async {
PreviewDepositModal(
yieldTimeFrame: YieldTimeFrame.day,
- depositWithNativeToken: false,
currentYield: currentYield,
isReversed: true,
minPrice: (isInfinity: true, price: 0),
@@ -974,7 +958,6 @@ void main() {
WidgetsBinding.instance.addPostFrameCallback((_) async {
PreviewDepositModal(
yieldTimeFrame: YieldTimeFrame.day,
- depositWithNativeToken: false,
currentYield: currentYield,
isReversed: true,
minPrice: (isInfinity: true, price: 0),
diff --git a/test/app/create/deposit/widgets/token_amount_input_card/goldens/token_amount_card_connected.png b/test/app/create/deposit/widgets/token_amount_input_card/goldens/token_amount_card_connected.png
index 81f4516..394ab0a 100644
Binary files a/test/app/create/deposit/widgets/token_amount_input_card/goldens/token_amount_card_connected.png and b/test/app/create/deposit/widgets/token_amount_input_card/goldens/token_amount_card_connected.png differ
diff --git a/test/app/create/deposit/widgets/token_amount_input_card/goldens/token_amount_card_disabled.png b/test/app/create/deposit/widgets/token_amount_input_card/goldens/token_amount_card_disabled.png
index 3211f70..21d16e9 100644
Binary files a/test/app/create/deposit/widgets/token_amount_input_card/goldens/token_amount_card_disabled.png and b/test/app/create/deposit/widgets/token_amount_input_card/goldens/token_amount_card_disabled.png differ
diff --git a/test/app/create/deposit/widgets/token_amount_input_card/goldens/token_amount_card_large_number_left_border.png b/test/app/create/deposit/widgets/token_amount_input_card/goldens/token_amount_card_large_number_left_border.png
index cd7499d..d19adf8 100644
Binary files a/test/app/create/deposit/widgets/token_amount_input_card/goldens/token_amount_card_large_number_left_border.png and b/test/app/create/deposit/widgets/token_amount_input_card/goldens/token_amount_card_large_number_left_border.png differ
diff --git a/test/app/create/deposit/widgets/token_amount_input_card/goldens/token_amount_card_large_number_right_border.png b/test/app/create/deposit/widgets/token_amount_input_card/goldens/token_amount_card_large_number_right_border.png
index c074267..367683f 100644
Binary files a/test/app/create/deposit/widgets/token_amount_input_card/goldens/token_amount_card_large_number_right_border.png and b/test/app/create/deposit/widgets/token_amount_input_card/goldens/token_amount_card_large_number_right_border.png differ
diff --git a/test/app/create/deposit/widgets/token_amount_input_card/goldens/token_amount_card_non_number.png b/test/app/create/deposit/widgets/token_amount_input_card/goldens/token_amount_card_non_number.png
index 052da22..71d3e31 100644
Binary files a/test/app/create/deposit/widgets/token_amount_input_card/goldens/token_amount_card_non_number.png and b/test/app/create/deposit/widgets/token_amount_input_card/goldens/token_amount_card_non_number.png differ
diff --git a/test/app/create/deposit/widgets/token_amount_input_card/goldens/token_amount_card_signer_changes.png b/test/app/create/deposit/widgets/token_amount_input_card/goldens/token_amount_card_signer_changes.png
index 81f4516..394ab0a 100644
Binary files a/test/app/create/deposit/widgets/token_amount_input_card/goldens/token_amount_card_signer_changes.png and b/test/app/create/deposit/widgets/token_amount_input_card/goldens/token_amount_card_signer_changes.png differ
diff --git a/test/app/create/deposit/widgets/token_amount_input_card/goldens/token_amount_card_update_token.png b/test/app/create/deposit/widgets/token_amount_input_card/goldens/token_amount_card_update_token.png
index 40ccda9..e6ac881 100644
Binary files a/test/app/create/deposit/widgets/token_amount_input_card/goldens/token_amount_card_update_token.png and b/test/app/create/deposit/widgets/token_amount_input_card/goldens/token_amount_card_update_token.png differ
diff --git a/test/app/create/deposit/widgets/token_amount_input_card/goldens/token_amount_card_usd_equivalent.png b/test/app/create/deposit/widgets/token_amount_input_card/goldens/token_amount_card_usd_equivalent.png
new file mode 100644
index 0000000..259cc15
Binary files /dev/null and b/test/app/create/deposit/widgets/token_amount_input_card/goldens/token_amount_card_usd_equivalent.png differ
diff --git a/test/app/create/deposit/widgets/token_amount_input_card/goldens/token_amount_card_user_balance_click.png b/test/app/create/deposit/widgets/token_amount_input_card/goldens/token_amount_card_user_balance_click.png
index 81f4516..4d41159 100644
Binary files a/test/app/create/deposit/widgets/token_amount_input_card/goldens/token_amount_card_user_balance_click.png and b/test/app/create/deposit/widgets/token_amount_input_card/goldens/token_amount_card_user_balance_click.png differ
diff --git a/test/app/create/deposit/widgets/token_amount_input_card/token_amount_input_card_cubit_test.dart b/test/app/create/deposit/widgets/token_amount_input_card/token_amount_input_card_cubit_test.dart
new file mode 100644
index 0000000..c337c76
--- /dev/null
+++ b/test/app/create/deposit/widgets/token_amount_input_card/token_amount_input_card_cubit_test.dart
@@ -0,0 +1,66 @@
+import 'package:flutter_test/flutter_test.dart';
+import 'package:mocktail/mocktail.dart';
+import 'package:zup_app/app/create/deposit/widgets/token_amount_input_card/token_amount_input_card_cubit.dart';
+import 'package:zup_app/core/dtos/token_dto.dart';
+import 'package:zup_app/core/dtos/token_price_dto.dart';
+import 'package:zup_app/core/enums/networks.dart';
+import 'package:zup_app/core/mixins/keys_mixin.dart';
+import 'package:zup_app/core/repositories/tokens_repository.dart';
+import 'package:zup_core/zup_core.dart';
+
+import '../../../../../mocks.dart';
+
+class _KeysMixinWrapper with KeysMixin {}
+
+void main() {
+ late TokenAmountInputCardCubit sut;
+ late TokensRepository tokensRepository;
+ late ZupSingletonCache zupSingletonCache;
+ late ZupHolder zupHolder;
+
+ setUp(() {
+ registerFallbackValue(AppNetworks.sepolia);
+
+ tokensRepository = TokensRepositoryMock();
+ zupSingletonCache = ZupSingletonCache.shared;
+ zupHolder = ZupHolder();
+
+ sut = TokenAmountInputCardCubit(tokensRepository, zupSingletonCache, zupHolder);
+
+ when(() => tokensRepository.getTokenPrice(any(), any())).thenAnswer((_) async => TokenPriceDto.fixture());
+ });
+
+ test("When calling `getTokenPrice` it should use ZupHolder to not make too many requests at once", () async {
+ zupHolder = ZupHolderMock();
+
+ when(() => zupHolder.hold(any())).thenAnswer((_) async => 31);
+ final sut0 = TokenAmountInputCardCubit(tokensRepository, zupSingletonCache, zupHolder);
+
+ await sut0.getTokenPrice(token: TokenDto.fixture(), network: AppNetworks.sepolia);
+ verify(() => zupHolder.hold(any())).called(1);
+ });
+
+ test("When calling `getTokenPrice` it should use ZupSingletonCache with a expiration of 1 minute", () async {
+ zupSingletonCache = ZupSingletonCacheMock();
+ final sut0 = TokenAmountInputCardCubit(tokensRepository, zupSingletonCache, zupHolder);
+ final token = TokenDto.fixture();
+ const network = AppNetworks.sepolia;
+
+ when(() => zupSingletonCache.run(any(), expiration: any(named: "expiration"), key: any(named: "key")))
+ .thenAnswer((_) async => 31);
+
+ await sut0.getTokenPrice(token: token, network: network);
+ verify(() => zupSingletonCache.run(any(),
+ expiration: const Duration(minutes: 1),
+ key: _KeysMixinWrapper()
+ .tokenPriceCacheKey(tokenAddress: token.addresses[network.chainId]!, network: network))).called(1);
+ });
+
+ test("When calling `getTokenPrice` it should use tokensRepository to get the token price", () async {
+ final token = TokenDto.fixture();
+ const network = AppNetworks.sepolia;
+
+ await sut.getTokenPrice(token: token, network: network);
+ verify(() => tokensRepository.getTokenPrice(token.addresses[network.chainId]!, network)).called(1);
+ });
+}
diff --git a/test/app/create/deposit/widgets/token_amount_input_card/token_amount_input_card_test.dart b/test/app/create/deposit/widgets/token_amount_input_card/token_amount_input_card_test.dart
index fff6c63..c195907 100644
--- a/test/app/create/deposit/widgets/token_amount_input_card/token_amount_input_card_test.dart
+++ b/test/app/create/deposit/widgets/token_amount_input_card/token_amount_input_card_test.dart
@@ -7,8 +7,10 @@ import 'package:mocktail/mocktail.dart';
import 'package:web3kit/web3kit.dart';
import 'package:zup_app/app/create/deposit/widgets/token_amount_input_card/token_amount_input_card.dart';
import 'package:zup_app/core/dtos/token_dto.dart';
+import 'package:zup_app/core/dtos/token_price_dto.dart';
import 'package:zup_app/core/enums/networks.dart';
import 'package:zup_app/core/injections.dart';
+import 'package:zup_app/core/repositories/tokens_repository.dart';
import 'package:zup_app/widgets/zup_cached_image.dart';
import 'package:zup_core/zup_core.dart';
@@ -18,19 +20,28 @@ import '../../../../../mocks.dart';
void main() {
late Wallet wallet;
late Signer signer;
+ late TokensRepository tokensRepository;
setUp(() {
+ registerFallbackValue(AppNetworks.sepolia);
+
wallet = WalletMock();
signer = SignerMock();
+ tokensRepository = TokensRepositoryMock();
inject.registerFactory(() => wallet);
inject.registerFactory(() => ZupSingletonCache.shared);
inject.registerFactory(() => mockZupCachedImage());
+ inject.registerFactory(() => tokensRepository);
+ inject.registerFactory(() => ZupHolder());
+ when(() => tokensRepository.getTokenPrice(any(), any())).thenAnswer((_) async => TokenPriceDto.fixture());
when(() => wallet.signer).thenReturn(signer);
when(() => signer.address).thenAnswer((_) => Future.value("0x99E3CfADCD8Feecb5DdF91f88998cFfB3145F78c"));
when(() => wallet.tokenBalance(any(), rpcUrl: any(named: "rpcUrl"))).thenAnswer((_) => Future.value(12.1));
when(() => wallet.signerStream).thenAnswer((_) => const Stream.empty());
+ when(() => wallet.nativeOrTokenBalance(any(), rpcUrl: any(named: "rpcUrl")))
+ .thenAnswer((_) => Future.value(43727653762.1));
});
tearDown(() async {
@@ -70,10 +81,12 @@ void main() {
zGoldenTest("When there is not a connected wallet, it should not show the user balance",
goldenFileName: "token_amount_card_not_connected", (tester) async {
- when(() => wallet.signer).thenReturn(null);
+ await tester.runAsync(() async {
+ when(() => wallet.signer).thenReturn(null);
- await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.pumpAndSettle();
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.pumpAndSettle();
+ });
});
zGoldenTest(""""
@@ -82,19 +95,21 @@ void main() {
it should get the balance of the new connected wallet
and display it
""", goldenFileName: "token_amount_card_signer_changes", (tester) async {
- final signerStreamController = StreamController.broadcast();
+ await tester.runAsync(() async {
+ final signerStreamController = StreamController.broadcast();
- when(() => wallet.signerStream).thenAnswer((_) => signerStreamController.stream);
- when(() => wallet.signer).thenReturn(null);
+ when(() => wallet.signerStream).thenAnswer((_) => signerStreamController.stream);
+ when(() => wallet.signer).thenReturn(null);
- await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.pumpDeviceBuilder(await goldenBuilder());
- signerStreamController.add(signer);
- when(() => wallet.signer).thenReturn(signer);
+ signerStreamController.add(signer);
+ when(() => wallet.signer).thenReturn(signer);
- await tester.pumpAndSettle();
+ await tester.pumpAndSettle();
- verify(() => wallet.nativeOrTokenBalance(any(), rpcUrl: any(named: "rpcUrl"))).called(1);
+ verify(() => wallet.nativeOrTokenBalance(any(), rpcUrl: any(named: "rpcUrl"))).called(1);
+ });
});
zGoldenTest(""""
@@ -103,177 +118,200 @@ void main() {
it should get the balance of the new connected wallet
and display it
""", goldenFileName: "token_amount_card_signer_changes_not_null", (tester) async {
- final signer1 = signer;
- final signer2 = SignerMock();
+ await tester.runAsync(() async {
+ final signer1 = signer;
+ final signer2 = SignerMock();
- final signerStreamController = StreamController.broadcast();
+ final signerStreamController = StreamController.broadcast();
- when(() => wallet.signerStream).thenAnswer((_) => signerStreamController.stream);
- when(() => wallet.signer).thenReturn(signer1);
- when(() => signer2.address).thenAnswer((_) => Future.value("0x99E3CfADCD8Feecb5DdF91f88998cFfB3145F78c"));
- when(() => wallet.nativeOrTokenBalance(any(), rpcUrl: any(named: "rpcUrl")))
- .thenAnswer((_) => Future.value(43727653762.1));
+ when(() => wallet.signerStream).thenAnswer((_) => signerStreamController.stream);
+ when(() => wallet.signer).thenReturn(signer1);
+ when(() => signer2.address).thenAnswer((_) => Future.value("0x99E3CfADCD8Feecb5DdF91f88998cFfB3145F78c"));
+ when(() => wallet.nativeOrTokenBalance(any(), rpcUrl: any(named: "rpcUrl")))
+ .thenAnswer((_) => Future.value(43727653762.1));
- await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.pumpAndSettle();
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.pumpAndSettle();
- signerStreamController.add(signer2);
- when(() => wallet.signer).thenReturn(signer2);
+ signerStreamController.add(signer2);
+ when(() => wallet.signer).thenReturn(signer2);
- await tester.pumpAndSettle();
- verify(() => wallet.nativeOrTokenBalance(any(), rpcUrl: any(named: "rpcUrl"))).called(1);
+ await tester.pumpAndSettle();
+ verify(() => wallet.nativeOrTokenBalance(any(), rpcUrl: any(named: "rpcUrl"))).called(1);
+ });
});
zGoldenTest("""When there is a connected wallet,
it should get the user balance and show it in a button""", goldenFileName: "token_amount_card_connected",
(tester) async {
- when(() => wallet.signer).thenReturn(signer);
+ await tester.runAsync(() async {
+ when(() => wallet.signer).thenReturn(signer);
- await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ });
});
zGoldenTest("""When the disabled text param is passed,
the card should be disabled and with the text passed
""", goldenFileName: "token_amount_card_disabled", (tester) async {
- await tester.pumpDeviceBuilder(await goldenBuilder(disabledText: "This card is disabled"));
+ await tester.runAsync(() async {
+ await tester.pumpDeviceBuilder(await goldenBuilder(disabledText: "This card is disabled"));
+ });
});
zGoldenTest("When inputting something in the textfield, it should callback with the value", (tester) async {
- double expectedValue = 1.2;
- double? actualValue;
- await tester.pumpDeviceBuilder(await goldenBuilder(onInput: (typed) => actualValue = typed));
+ await tester.runAsync(() async {
+ double expectedValue = 1.2;
+ double? actualValue;
+ await tester.pumpDeviceBuilder(await goldenBuilder(onInput: (typed) => actualValue = typed));
- await tester.enterText(find.byType(TextField), expectedValue.toString());
- await tester.pumpAndSettle();
+ await tester.enterText(find.byType(TextField), expectedValue.toString());
+ await tester.pumpAndSettle();
- expect(actualValue, expectedValue);
+ expect(actualValue, expectedValue);
+ });
});
zGoldenTest("When inputting a non-number in the textfield, it should not accept",
goldenFileName: "token_amount_card_non_number", (tester) async {
- await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.runAsync(() async {
+ await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.enterText(find.byType(TextField), "abcdefgj");
- await tester.pumpAndSettle();
+ await tester.enterText(find.byType(TextField), "abcdefgj");
+ await tester.pumpAndSettle();
+ });
});
zGoldenTest("When clicking in the user balance button, it should input the balance",
goldenFileName: "token_amount_card_user_balance_click", (tester) async {
- when(() => wallet.tokenBalance(any(), rpcUrl: any(named: "rpcUrl"))).thenAnswer((_) => Future.value(43727653762.1));
+ await tester.runAsync(() async {
+ when(() => wallet.tokenBalance(any(), rpcUrl: any(named: "rpcUrl")))
+ .thenAnswer((_) => Future.value(43727653762.1));
- await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.tap(find.byKey(const Key("user-balance-button")));
- await tester.pumpAndSettle();
+ await tester.tap(find.byKey(const Key("user-balance-button")));
+ await tester.pumpAndSettle();
+ });
});
zGoldenTest("When clicking in the user balance button, it should callback with the balance", (tester) async {
- const expectedValue = 43727653762.1;
- double? actualValue;
+ await tester.runAsync(() async {
+ const expectedValue = 43727653762.1;
+ double? actualValue;
- when(() => wallet.nativeOrTokenBalance(any(), rpcUrl: any(named: "rpcUrl")))
- .thenAnswer((_) => Future.value(expectedValue));
+ when(() => wallet.nativeOrTokenBalance(any(), rpcUrl: any(named: "rpcUrl")))
+ .thenAnswer((_) => Future.value(expectedValue));
- await tester.pumpDeviceBuilder(await goldenBuilder(onInput: (value) => actualValue = value));
+ await tester.pumpDeviceBuilder(await goldenBuilder(onInput: (value) => actualValue = value));
- await tester.tap(find.byKey(const Key("user-balance-button")));
- await tester.pumpAndSettle();
+ await tester.tap(find.byKey(const Key("user-balance-button")));
+ await tester.pumpAndSettle();
- expect(actualValue, expectedValue);
+ expect(actualValue, expectedValue);
+ });
});
zGoldenTest("When clicking in the refresh button, it should get the token amount again, ignoring the cache",
goldenFileName: "token_amount_card_refresh", (tester) async {
- when(() => wallet.nativeOrTokenBalance(any(), rpcUrl: any(named: "rpcUrl")))
- .thenAnswer((_) => Future.value(43727653762.1));
+ await tester.runAsync(() async {
+ when(() => wallet.nativeOrTokenBalance(any(), rpcUrl: any(named: "rpcUrl")))
+ .thenAnswer((_) => Future.value(43727653762.1));
- await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.pumpDeviceBuilder(await goldenBuilder());
- when(() => wallet.nativeOrTokenBalance(any(), rpcUrl: any(named: "rpcUrl")))
- .thenAnswer((_) => Future.value(12345.43));
- await tester.tap(find.byKey(const Key("refresh-balance-button")));
+ when(() => wallet.nativeOrTokenBalance(any(), rpcUrl: any(named: "rpcUrl")))
+ .thenAnswer((_) => Future.value(12345.43));
+ await tester.tap(find.byKey(const Key("refresh-balance-button")));
- await tester.pumpAndSettle();
+ await tester.pumpAndSettle();
+ });
});
zGoldenTest(
"When updating the widget with a different token, it should update the token in the cubit and get the balance again",
goldenFileName: "token_amount_card_update_token",
(tester) async {
- const key = Key("token-amount-card");
- const newTokenAddress = "0xN3W_T0K3N";
- final newToken = TokenDto.fixture().copyWith(
- addresses: {AppNetworks.sepolia.chainId: newTokenAddress},
- symbol: "NEW_TOKEN",
- );
-
- await tester.pumpDeviceBuilder(await goldenBuilder(key: key));
- await tester.pumpDeviceBuilder(await goldenBuilder(key: key, token: newToken));
-
- verify(() => wallet.nativeOrTokenBalance(newTokenAddress, rpcUrl: any(named: "rpcUrl"))).called(1);
+ await tester.runAsync(() async {
+ const key = Key("token-amount-card");
+ const newTokenAddress = "0xN3W_T0K3N";
+ final newToken = TokenDto.fixture().copyWith(
+ addresses: {AppNetworks.sepolia.chainId: newTokenAddress},
+ symbol: "NEW_TOKEN",
+ );
+
+ await tester.pumpDeviceBuilder(await goldenBuilder(key: key));
+ await tester.pumpDeviceBuilder(await goldenBuilder(key: key, token: newToken));
+
+ verify(() => wallet.nativeOrTokenBalance(newTokenAddress, rpcUrl: any(named: "rpcUrl"))).called(1);
+ });
},
);
zGoldenTest(
"When updating the widget from a native token, for a different native token, it should update the token in the cubit and get the balance again",
(tester) async {
- const key = Key("token-amount-card");
-
- const oldTokenNetwork = AppNetworks.scroll;
- const newTokenNetwork = AppNetworks.sepolia;
-
- final oldTokenAddress = AppNetworks.scroll.wrappedNativeTokenAddress;
- final oldToken = TokenDto.fixture().copyWith(
- addresses: {AppNetworks.scroll.chainId: oldTokenAddress},
- symbol: "OLD_TOKEN",
- );
-
- final newTokenAddress = AppNetworks.sepolia.wrappedNativeTokenAddress;
- final newToken = TokenDto.fixture().copyWith(
- addresses: {AppNetworks.sepolia.chainId: newTokenAddress},
- symbol: "NEW_TOKEN",
- );
-
- await tester.pumpDeviceBuilder(
- await goldenBuilder(key: key, token: oldToken, isNative: true, network: oldTokenNetwork),
- );
- await tester.pumpDeviceBuilder(
- await goldenBuilder(key: key, token: newToken, isNative: true, network: newTokenNetwork),
- );
-
- verify(() => wallet.nativeOrTokenBalance(EthereumConstants.zeroAddress, rpcUrl: newTokenNetwork.rpcUrl))
- .called(1);
+ await tester.runAsync(() async {
+ const key = Key("token-amount-card");
+
+ const oldTokenNetwork = AppNetworks.scroll;
+ const newTokenNetwork = AppNetworks.sepolia;
+
+ final oldTokenAddress = AppNetworks.scroll.wrappedNativeTokenAddress;
+ final oldToken = TokenDto.fixture().copyWith(
+ addresses: {AppNetworks.scroll.chainId: oldTokenAddress},
+ symbol: "OLD_TOKEN",
+ );
+
+ final newTokenAddress = AppNetworks.sepolia.wrappedNativeTokenAddress;
+ final newToken = TokenDto.fixture().copyWith(
+ addresses: {AppNetworks.sepolia.chainId: newTokenAddress},
+ symbol: "NEW_TOKEN",
+ );
+
+ await tester.pumpDeviceBuilder(
+ await goldenBuilder(key: key, token: oldToken, isNative: true, network: oldTokenNetwork),
+ );
+ await tester.pumpDeviceBuilder(
+ await goldenBuilder(key: key, token: newToken, isNative: true, network: newTokenNetwork),
+ );
+
+ verify(() => wallet.nativeOrTokenBalance(EthereumConstants.zeroAddress, rpcUrl: newTokenNetwork.rpcUrl))
+ .called(1);
+ });
},
);
zGoldenTest(
"When updating the widget from a non-native token, for a different non-native token, it should update the token in the cubit and get the balance again",
(tester) async {
- const key = Key("token-amount-card");
-
- const oldTokenNetwork = AppNetworks.scroll;
- const newTokenNetwork = AppNetworks.sepolia;
-
- final oldTokenAddress = AppNetworks.scroll.wrappedNativeTokenAddress;
- final oldToken = TokenDto.fixture().copyWith(
- addresses: {AppNetworks.scroll.chainId: oldTokenAddress},
- symbol: "OLD_TOKEN",
- );
-
- final newTokenAddress = AppNetworks.sepolia.wrappedNativeTokenAddress;
- final newToken = TokenDto.fixture().copyWith(
- addresses: {AppNetworks.sepolia.chainId: newTokenAddress},
- symbol: "NEW_TOKEN",
- );
-
- await tester.pumpDeviceBuilder(
- await goldenBuilder(key: key, token: oldToken, isNative: false, network: oldTokenNetwork),
- );
- await tester.pumpDeviceBuilder(
- await goldenBuilder(key: key, token: newToken, isNative: false, network: newTokenNetwork),
- );
-
- verify(() => wallet.nativeOrTokenBalance(newTokenAddress, rpcUrl: newTokenNetwork.rpcUrl)).called(1);
+ await tester.runAsync(() async {
+ const key = Key("token-amount-card");
+
+ const oldTokenNetwork = AppNetworks.scroll;
+ const newTokenNetwork = AppNetworks.sepolia;
+
+ final oldTokenAddress = AppNetworks.scroll.wrappedNativeTokenAddress;
+ final oldToken = TokenDto.fixture().copyWith(
+ addresses: {AppNetworks.scroll.chainId: oldTokenAddress},
+ symbol: "OLD_TOKEN",
+ );
+
+ final newTokenAddress = AppNetworks.sepolia.wrappedNativeTokenAddress;
+ final newToken = TokenDto.fixture().copyWith(
+ addresses: {AppNetworks.sepolia.chainId: newTokenAddress},
+ symbol: "NEW_TOKEN",
+ );
+
+ await tester.pumpDeviceBuilder(
+ await goldenBuilder(key: key, token: oldToken, isNative: false, network: oldTokenNetwork),
+ );
+ await tester.pumpDeviceBuilder(
+ await goldenBuilder(key: key, token: newToken, isNative: false, network: newTokenNetwork),
+ );
+
+ verify(() => wallet.nativeOrTokenBalance(newTokenAddress, rpcUrl: newTokenNetwork.rpcUrl)).called(1);
+ });
},
);
@@ -282,8 +320,10 @@ void main() {
but instead do a soft clip with a gradient""",
goldenFileName: "token_amount_card_large_number_left_border",
(tester) async {
- await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.enterText(find.byType(TextField), "1234567890123456789021762561752615261");
+ await tester.runAsync(() async {
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.enterText(find.byType(TextField), "1234567890123456789021762561752615261");
+ });
},
);
@@ -292,11 +332,92 @@ void main() {
but instead do a soft clip with a gradient""",
goldenFileName: "token_amount_card_large_number_right_border",
(tester) async {
- await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.enterText(find.byType(TextField), "1234567890123456789021762561752615261");
- await tester.drag(find.byType(TextField), const Offset(-1, 0));
+ await tester.runAsync(() async {
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.enterText(find.byType(TextField), "1234567890123456789021762561752615261");
+ await tester.drag(find.byType(TextField), const Offset(-1, 0));
+
+ FocusManager.instance.primaryFocus?.unfocus();
+ });
+ },
+ );
+
+ zGoldenTest(
+ "When instanciating the widget, it should update the native token variable in the cubit",
+ (tester) async {
+ await tester.runAsync(() async {
+ await tester.pumpDeviceBuilder(await goldenBuilder(isNative: true));
+ await tester.pumpAndSettle();
- FocusManager.instance.primaryFocus?.unfocus();
+ verify(() => wallet.nativeOrTokenBalance(EthereumConstants.zeroAddress, rpcUrl: any(named: "rpcUrl")))
+ .called(1);
+ });
},
);
+
+ zGoldenTest(
+ "When the wallet emits a new signer, and the current token isNative, it should fetch the native balance",
+ (tester) async {
+ await tester.runAsync(() async {
+ final signerStreamController = StreamController.broadcast();
+ when(() => wallet.signer).thenReturn(null);
+ when(() => wallet.signerStream).thenAnswer((_) => signerStreamController.stream);
+
+ await tester.pumpDeviceBuilder(await goldenBuilder(isNative: true));
+ await tester.pumpAndSettle();
+
+ when(() => wallet.signer).thenReturn(signer);
+
+ signerStreamController.add(SignerMock());
+ await tester.pumpAndSettle();
+
+ verify(() => wallet.nativeOrTokenBalance(EthereumConstants.zeroAddress, rpcUrl: any(named: "rpcUrl")))
+ .called(1);
+ verifyNever(() =>
+ wallet.nativeOrTokenBalance(any(that: isNot(EthereumConstants.zeroAddress)), rpcUrl: any(named: "rpcUrl")));
+ });
+ },
+ );
+
+ zGoldenTest(
+ "When the wallet emits a new signer, and the current token is not native, it should fetch the non-native balance",
+ (tester) async {
+ await tester.runAsync(() async {
+ final token = TokenDto.fixture();
+ const network = AppNetworks.scroll;
+
+ final signerStreamController = StreamController.broadcast();
+ when(() => wallet.signer).thenReturn(null);
+ when(() => wallet.signerStream).thenAnswer((_) => signerStreamController.stream);
+
+ await tester.pumpDeviceBuilder(await goldenBuilder(isNative: false));
+ await tester.pumpAndSettle();
+
+ when(() => wallet.signer).thenReturn(signer);
+
+ signerStreamController.add(SignerMock());
+ await tester.pumpAndSettle();
+
+ verify(() => wallet.nativeOrTokenBalance(token.addresses[network.chainId]!, rpcUrl: any(named: "rpcUrl")))
+ .called(1);
+ verifyNever(() => wallet.nativeOrTokenBalance(EthereumConstants.zeroAddress, rpcUrl: any(named: "rpcUrl")));
+ });
+ },
+ );
+
+ zGoldenTest("When typing an amount, it should show the USD equivalent of the amount",
+ goldenFileName: "token_amount_card_usd_equivalent", (tester) async {
+ await tester.runAsync(() async {
+ const tokenUSDPrice = 121.85;
+ const tokenAmount = 1.2;
+
+ when(() => tokensRepository.getTokenPrice(any(), any()))
+ .thenAnswer((_) async => TokenPriceDto(usdPrice: tokenUSDPrice, address: ""));
+
+ await tester.pumpDeviceBuilder(await goldenBuilder(controller: TextEditingController(text: "$tokenAmount")));
+ await tester.pumpAndSettle();
+
+ expect(find.text("\$${(tokenAmount * tokenUSDPrice).toStringAsFixed(2)}"), findsOneWidget);
+ });
+ });
}
diff --git a/test/app/create/deposit/widgets/token_amount_input_card/token_amount_input_card_user_balance_cubit_test.dart b/test/app/create/deposit/widgets/token_amount_input_card/token_amount_input_card_user_balance_cubit_test.dart
index 9995855..f40a9df 100644
--- a/test/app/create/deposit/widgets/token_amount_input_card/token_amount_input_card_user_balance_cubit_test.dart
+++ b/test/app/create/deposit/widgets/token_amount_input_card/token_amount_input_card_user_balance_cubit_test.dart
@@ -5,6 +5,7 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:web3kit/web3kit.dart';
import 'package:zup_app/app/create/deposit/widgets/token_amount_input_card/token_amount_input_card_user_balance_cubit.dart';
+import 'package:zup_app/core/dtos/token_dto.dart';
import 'package:zup_app/core/enums/networks.dart';
import 'package:zup_core/zup_core.dart';
@@ -300,6 +301,8 @@ void main() {
"""When calling 'updateTokenAndNetwork' with 'asNativeToken' true,
it should pass the address zero to wallet to get the native balance""",
() async {
+ when(() => wallet.signerStream).thenAnswer((_) => const Stream.empty());
+
const tokenAddress = "0x99E3CfADCD8Feecb5DdF91f88998cFfB3145F78c";
final sut0 = TokenAmountCardUserBalanceCubit(
@@ -315,4 +318,132 @@ void main() {
verify(() => wallet.nativeOrTokenBalance(EthereumConstants.zeroAddress, rpcUrl: any(named: "rpcUrl"))).called(1);
},
);
+
+ test(
+ """When calling 'updateNativeTokenAndFetch' it should update the is native variable
+ so when a new signer is emitted it will get the native balance based on the variable.
+ (isNative = true testcase)""",
+ () async {
+ final wallet0 = WalletMock();
+
+ final signerStreamController = StreamController.broadcast();
+ final signerStream = signerStreamController.stream;
+ when(() => wallet0.signerStream).thenAnswer((_) => signerStream);
+ when(() => wallet0.signer).thenAnswer((_) => null);
+
+ final sut0 = TokenAmountCardUserBalanceCubit(
+ wallet0,
+ "0x99E3CfADCD8Feecb5DdF91f88998cFfB3145F78c",
+ AppNetworks.sepolia,
+ ZupSingletonCache.shared,
+ () {},
+ );
+
+ await sut0.updateNativeTokenAndFetch(isNative: true);
+
+ when(() => wallet0.signer).thenAnswer((_) => signer);
+
+ await ZupSingletonCache.shared.clear();
+ signerStreamController.add(signer);
+
+ await Future.delayed(const Duration(seconds: 0));
+
+ verify(() => wallet0.nativeOrTokenBalance(EthereumConstants.zeroAddress, rpcUrl: any(named: "rpcUrl"))).called(1);
+ },
+ );
+
+ test(
+ """When calling 'updateNativeTokenAndFetch' it should update the is native variable
+ so when a new signer is emitted it will get the native balance based on the variable.
+ (isNative = false testcase)""",
+ () async {
+ final wallet0 = WalletMock();
+ final signerStreamController = StreamController.broadcast();
+ final signerStream = signerStreamController.stream;
+ const tokenAddress = "0x99E3CfADCD8Feecb5DdF91f88998cFfB3145F78c";
+
+ when(() => wallet0.signerStream).thenAnswer((_) => signerStream);
+ when(() => wallet0.signer).thenAnswer((_) => null);
+
+ final sut0 = TokenAmountCardUserBalanceCubit(
+ wallet0,
+ tokenAddress,
+ AppNetworks.sepolia,
+ ZupSingletonCache.shared,
+ () {},
+ );
+
+ await sut0.updateNativeTokenAndFetch(isNative: false);
+
+ when(() => wallet0.signer).thenAnswer((_) => signer);
+
+ await ZupSingletonCache.shared.clear();
+ signerStreamController.add(signer);
+
+ await Future.delayed(const Duration(seconds: 0));
+
+ verify(() => wallet0.nativeOrTokenBalance(tokenAddress, rpcUrl: any(named: "rpcUrl"))).called(1);
+ },
+ );
+
+ test("When calling 'updateNativeTokenAndFetch' and the signer is null, it should not get the user token balance",
+ () async {
+ final sut0 = TokenAmountCardUserBalanceCubit(
+ wallet,
+ tokenAddress,
+ AppNetworks.sepolia,
+ ZupSingletonCache.shared,
+ () {},
+ );
+
+ when(() => wallet.signer).thenAnswer((_) => null);
+
+ await sut0.updateNativeTokenAndFetch(isNative: false);
+
+ verifyNever(() => wallet.nativeOrTokenBalance(any(), rpcUrl: any(named: "rpcUrl")));
+ });
+
+ test("When calling `updateNativeTokenAndFetch` and the signer is not null, it should get the user token balance",
+ () async {
+ final sut0 = TokenAmountCardUserBalanceCubit(
+ wallet,
+ tokenAddress,
+ AppNetworks.sepolia,
+ ZupSingletonCache.shared,
+ () {},
+ );
+
+ when(() => wallet.signer).thenAnswer((_) => signer);
+
+ await sut0.updateNativeTokenAndFetch(isNative: false);
+
+ verify(() => wallet.nativeOrTokenBalance(any(), rpcUrl: any(named: "rpcUrl")))
+ .called(2); // two times because of the initial load
+ });
+
+ test("""When calling `updateTokenAndNetwork` passsing the is native true, it should update the isNative variable,
+ and when a new signer is emitted it will get the native balance""", () async {
+ final signerStreamController = StreamController.broadcast();
+
+ when(() => wallet.signerStream).thenAnswer((_) => signerStreamController.stream);
+
+ final sut0 = TokenAmountCardUserBalanceCubit(
+ wallet,
+ tokenAddress,
+ AppNetworks.sepolia,
+ ZupSingletonCache.shared,
+ () {},
+ );
+
+ final token = TokenDto.fixture();
+ const network = AppNetworks.sepolia;
+
+ await sut0.updateTokenAndNetwork(token.addresses[network.chainId]!, network, asNativeToken: true);
+
+ signerStreamController.add(signer);
+ await Future.delayed(const Duration(seconds: 0));
+
+ verify(() => wallet.nativeOrTokenBalance(EthereumConstants.zeroAddress, rpcUrl: any(named: "rpcUrl")))
+ .called(2); // two because of the initial load
+ });
}
diff --git a/test/app/create/goldens/create_page_initial_stage.png b/test/app/create/goldens/create_page_initial_stage.png
index f4f50bf..2c36037 100644
Binary files a/test/app/create/goldens/create_page_initial_stage.png and b/test/app/create/goldens/create_page_initial_stage.png differ
diff --git a/test/app/create/goldens/create_page_select_tokens_stage_change_a_token_to_same_token_as_b.png b/test/app/create/goldens/create_page_select_tokens_stage_change_a_token_to_same_token_as_b.png
index 117a3bb..013ee4c 100644
Binary files a/test/app/create/goldens/create_page_select_tokens_stage_change_a_token_to_same_token_as_b.png and b/test/app/create/goldens/create_page_select_tokens_stage_change_a_token_to_same_token_as_b.png differ
diff --git a/test/app/create/goldens/create_page_select_tokens_stage_change_b_token_to_same_token_as_a.png b/test/app/create/goldens/create_page_select_tokens_stage_change_b_token_to_same_token_as_a.png
index 5da265f..42a26ae 100644
Binary files a/test/app/create/goldens/create_page_select_tokens_stage_change_b_token_to_same_token_as_a.png and b/test/app/create/goldens/create_page_select_tokens_stage_change_b_token_to_same_token_as_a.png differ
diff --git a/test/app/create/goldens/create_page_select_tokens_stage_default_a_token.png b/test/app/create/goldens/create_page_select_tokens_stage_default_a_token.png
index f4f50bf..2c36037 100644
Binary files a/test/app/create/goldens/create_page_select_tokens_stage_default_a_token.png and b/test/app/create/goldens/create_page_select_tokens_stage_default_a_token.png differ
diff --git a/test/app/create/goldens/create_page_select_tokens_stage_mobile.png b/test/app/create/goldens/create_page_select_tokens_stage_mobile.png
index 6563383..1c452b6 100644
Binary files a/test/app/create/goldens/create_page_select_tokens_stage_mobile.png and b/test/app/create/goldens/create_page_select_tokens_stage_mobile.png differ
diff --git a/test/app/create/goldens/create_page_select_tokens_stage_pool_search_settings_add_badge.png b/test/app/create/goldens/create_page_select_tokens_stage_pool_search_settings_add_badge.png
index 6a3ac07..9f74f5e 100644
Binary files a/test/app/create/goldens/create_page_select_tokens_stage_pool_search_settings_add_badge.png and b/test/app/create/goldens/create_page_select_tokens_stage_pool_search_settings_add_badge.png differ
diff --git a/test/app/create/goldens/create_page_select_tokens_stage_pool_search_settings_default.png b/test/app/create/goldens/create_page_select_tokens_stage_pool_search_settings_default.png
index d135433..50a4715 100644
Binary files a/test/app/create/goldens/create_page_select_tokens_stage_pool_search_settings_default.png and b/test/app/create/goldens/create_page_select_tokens_stage_pool_search_settings_default.png differ
diff --git a/test/app/create/goldens/create_page_select_tokens_stage_pool_search_settings_not_default.png b/test/app/create/goldens/create_page_select_tokens_stage_pool_search_settings_not_default.png
index 6a3ac07..9f74f5e 100644
Binary files a/test/app/create/goldens/create_page_select_tokens_stage_pool_search_settings_not_default.png and b/test/app/create/goldens/create_page_select_tokens_stage_pool_search_settings_not_default.png differ
diff --git a/test/app/create/goldens/create_page_select_tokens_stage_pool_search_settings_open.png b/test/app/create/goldens/create_page_select_tokens_stage_pool_search_settings_open.png
index 09b6f2f..ebcd14b 100644
Binary files a/test/app/create/goldens/create_page_select_tokens_stage_pool_search_settings_open.png and b/test/app/create/goldens/create_page_select_tokens_stage_pool_search_settings_open.png differ
diff --git a/test/app/create/goldens/create_page_select_tokens_stage_pool_search_settings_remove_badge.png b/test/app/create/goldens/create_page_select_tokens_stage_pool_search_settings_remove_badge.png
index d135433..50a4715 100644
Binary files a/test/app/create/goldens/create_page_select_tokens_stage_pool_search_settings_remove_badge.png and b/test/app/create/goldens/create_page_select_tokens_stage_pool_search_settings_remove_badge.png differ
diff --git a/test/app/create/goldens/create_page_select_tokens_stage_reset_tokens_from_network.png b/test/app/create/goldens/create_page_select_tokens_stage_reset_tokens_from_network.png
index 6a3ac07..9f74f5e 100644
Binary files a/test/app/create/goldens/create_page_select_tokens_stage_reset_tokens_from_network.png and b/test/app/create/goldens/create_page_select_tokens_stage_reset_tokens_from_network.png differ
diff --git a/test/app/create/goldens/create_page_select_tokens_stage_token_a_selected_disabled_button.png b/test/app/create/goldens/create_page_select_tokens_stage_token_a_selected_disabled_button.png
index 117a3bb..013ee4c 100644
Binary files a/test/app/create/goldens/create_page_select_tokens_stage_token_a_selected_disabled_button.png and b/test/app/create/goldens/create_page_select_tokens_stage_token_a_selected_disabled_button.png differ
diff --git a/test/app/create/goldens/create_page_select_tokens_stage_token_enabled_button.png b/test/app/create/goldens/create_page_select_tokens_stage_token_enabled_button.png
index 0464053..1ca3ee3 100644
Binary files a/test/app/create/goldens/create_page_select_tokens_stage_token_enabled_button.png and b/test/app/create/goldens/create_page_select_tokens_stage_token_enabled_button.png differ
diff --git a/test/app/create/widgets/create_page_settings_dropdown_test.dart b/test/app/create/widgets/create_page_settings_dropdown_test.dart
index 97f58cf..ebfe786 100644
--- a/test/app/create/widgets/create_page_settings_dropdown_test.dart
+++ b/test/app/create/widgets/create_page_settings_dropdown_test.dart
@@ -127,4 +127,157 @@ void main() {
).called(1);
},
);
+
+ zGoldenTest(
+ "When hovering over the info icon in the allowed pool types section, it should show a tooltip explaining the field",
+ goldenFileName: "create_page_settings_dropdown_pool_types_tooltip_hover",
+ (tester) async {
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.pumpAndSettle();
+
+ await tester.hover(find.byKey(const Key("pool-types-allowed-tooltip")));
+ await tester.pumpAndSettle();
+ },
+ );
+
+ zGoldenTest("When clicking to disable the v4 switch, it should update the UI",
+ goldenFileName: "create_page_settings_dropdown_v4_pool_type_disabled", (tester) async {
+ when(() => cache.getPoolSearchSettings()).thenReturn(
+ PoolSearchSettingsDto().copyWith(allowV4Search: true),
+ );
+
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.pumpAndSettle();
+
+ when(() => cache.getPoolSearchSettings()).thenReturn(
+ PoolSearchSettingsDto().copyWith(allowV4Search: false),
+ ); // using this when because it fetches again after click
+
+ await tester.tap(find.byKey(const Key("pool-types-allowed-v4-switch")));
+ await tester.pumpAndSettle();
+ });
+
+ zGoldenTest("When clicking to disable the v3 switch, it should update the UI",
+ goldenFileName: "create_page_settings_dropdown_v3_pool_type_disabled", (tester) async {
+ when(() => cache.getPoolSearchSettings()).thenReturn(
+ PoolSearchSettingsDto().copyWith(allowV3Search: true),
+ );
+
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.pumpAndSettle();
+
+ when(() => cache.getPoolSearchSettings()).thenReturn(
+ PoolSearchSettingsDto().copyWith(allowV3Search: false),
+ ); // using this when because it fetches again after click
+
+ await tester.tap(find.byKey(const Key("pool-types-allowed-v3-switch")));
+ await tester.pumpAndSettle();
+ });
+
+ zGoldenTest(
+ "When clicking to disable the v4 switch, it should call the cache to update the settings only for the v4 switch",
+ (tester) async {
+ final initialSettings = PoolSearchSettingsDto.fixture().copyWith(allowV3Search: true, allowV4Search: true);
+ when(() => cache.getPoolSearchSettings()).thenReturn(initialSettings);
+
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.pumpAndSettle();
+
+ await tester.tap(find.byKey(const Key("pool-types-allowed-v4-switch")));
+ await tester.pumpAndSettle();
+
+ verify(
+ () => cache.savePoolSearchSettings(settings: initialSettings.copyWith(allowV4Search: false)),
+ ).called(1);
+ },
+ );
+
+ zGoldenTest(
+ "When clicking to disable the v3 switch, it should call the cache to update the settings only for the v3 switch",
+ (tester) async {
+ final initialSettings = PoolSearchSettingsDto.fixture().copyWith(allowV3Search: true, allowV4Search: true);
+ when(() => cache.getPoolSearchSettings()).thenReturn(initialSettings);
+
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.pumpAndSettle();
+
+ await tester.tap(find.byKey(const Key("pool-types-allowed-v3-switch")));
+ await tester.pumpAndSettle();
+
+ verify(
+ () => cache.savePoolSearchSettings(settings: initialSettings.copyWith(allowV3Search: false)),
+ ).called(1);
+ },
+ );
+
+ zGoldenTest("When clicking to enable the v4 switch, it should update the UI",
+ goldenFileName: "create_page_settings_dropdown_v4_pool_type_enable", (tester) async {
+ final initialSettings = PoolSearchSettingsDto.fixture().copyWith(allowV3Search: false, allowV4Search: false);
+ when(() => cache.getPoolSearchSettings()).thenReturn(
+ initialSettings,
+ );
+
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.pumpAndSettle();
+
+ when(() => cache.getPoolSearchSettings()).thenReturn(
+ initialSettings.copyWith(allowV4Search: true),
+ ); // using this when because it fetches again after click
+
+ await tester.tap(find.byKey(const Key("pool-types-allowed-v4-switch")));
+ await tester.pumpAndSettle();
+ });
+
+ zGoldenTest("When clicking to disable the v3 switch, it should update the UI",
+ goldenFileName: "create_page_settings_dropdown_v3_pool_type_enable", (tester) async {
+ final initialSettings = PoolSearchSettingsDto.fixture().copyWith(allowV3Search: false, allowV4Search: false);
+
+ when(() => cache.getPoolSearchSettings()).thenReturn(initialSettings);
+
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.pumpAndSettle();
+
+ when(() => cache.getPoolSearchSettings()).thenReturn(
+ initialSettings.copyWith(allowV3Search: true),
+ ); // using this when because it fetches again after click
+
+ await tester.tap(find.byKey(const Key("pool-types-allowed-v3-switch")));
+ await tester.pumpAndSettle();
+ });
+
+ zGoldenTest(
+ "When clicking to enable the v4 switch, it should call the cache to update the settings only for the v4 switch",
+ (tester) async {
+ final initialSettings = PoolSearchSettingsDto.fixture().copyWith(allowV3Search: false, allowV4Search: false);
+ when(() => cache.getPoolSearchSettings()).thenReturn(initialSettings);
+
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.pumpAndSettle();
+
+ await tester.tap(find.byKey(const Key("pool-types-allowed-v4-switch")));
+ await tester.pumpAndSettle();
+
+ verify(
+ () => cache.savePoolSearchSettings(settings: initialSettings.copyWith(allowV4Search: true)),
+ ).called(1);
+ },
+ );
+
+ zGoldenTest(
+ "When clicking to disable the v3 switch, it should call the cache to update the settings only for the v3 switch",
+ (tester) async {
+ final initialSettings = PoolSearchSettingsDto.fixture().copyWith(allowV3Search: false, allowV4Search: false);
+ when(() => cache.getPoolSearchSettings()).thenReturn(initialSettings);
+
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.pumpAndSettle();
+
+ await tester.tap(find.byKey(const Key("pool-types-allowed-v3-switch")));
+ await tester.pumpAndSettle();
+
+ verify(
+ () => cache.savePoolSearchSettings(settings: initialSettings.copyWith(allowV3Search: true)),
+ ).called(1);
+ },
+ );
}
diff --git a/test/app/create/widgets/goldens/create_page_setting_dropdown_min_liquidity_non_numeric.png b/test/app/create/widgets/goldens/create_page_setting_dropdown_min_liquidity_non_numeric.png
index a9aed59..f399b79 100644
Binary files a/test/app/create/widgets/goldens/create_page_setting_dropdown_min_liquidity_non_numeric.png and b/test/app/create/widgets/goldens/create_page_setting_dropdown_min_liquidity_non_numeric.png differ
diff --git a/test/app/create/widgets/goldens/create_page_settings_dropdown.png b/test/app/create/widgets/goldens/create_page_settings_dropdown.png
index dbeddd0..8d2ed69 100644
Binary files a/test/app/create/widgets/goldens/create_page_settings_dropdown.png and b/test/app/create/widgets/goldens/create_page_settings_dropdown.png differ
diff --git a/test/app/create/widgets/goldens/create_page_settings_dropdown_low_min_tvl_warning.png b/test/app/create/widgets/goldens/create_page_settings_dropdown_low_min_tvl_warning.png
index 98cccc3..88cf67b 100644
Binary files a/test/app/create/widgets/goldens/create_page_settings_dropdown_low_min_tvl_warning.png and b/test/app/create/widgets/goldens/create_page_settings_dropdown_low_min_tvl_warning.png differ
diff --git a/test/app/create/widgets/goldens/create_page_settings_dropdown_min_liquidity_tooltip.png b/test/app/create/widgets/goldens/create_page_settings_dropdown_min_liquidity_tooltip.png
index 0f0da21..63d3ca9 100644
Binary files a/test/app/create/widgets/goldens/create_page_settings_dropdown_min_liquidity_tooltip.png and b/test/app/create/widgets/goldens/create_page_settings_dropdown_min_liquidity_tooltip.png differ
diff --git a/test/app/create/widgets/goldens/create_page_settings_dropdown_min_liquidity_warning_field.png b/test/app/create/widgets/goldens/create_page_settings_dropdown_min_liquidity_warning_field.png
index 6a51c36..fcc6d03 100644
Binary files a/test/app/create/widgets/goldens/create_page_settings_dropdown_min_liquidity_warning_field.png and b/test/app/create/widgets/goldens/create_page_settings_dropdown_min_liquidity_warning_field.png differ
diff --git a/test/app/create/widgets/goldens/create_page_settings_dropdown_pool_types_tooltip_hover.png b/test/app/create/widgets/goldens/create_page_settings_dropdown_pool_types_tooltip_hover.png
new file mode 100644
index 0000000..501f6fe
Binary files /dev/null and b/test/app/create/widgets/goldens/create_page_settings_dropdown_pool_types_tooltip_hover.png differ
diff --git a/test/app/create/widgets/goldens/create_page_settings_dropdown_v3_pool_type_disabled.png b/test/app/create/widgets/goldens/create_page_settings_dropdown_v3_pool_type_disabled.png
new file mode 100644
index 0000000..268ed13
Binary files /dev/null and b/test/app/create/widgets/goldens/create_page_settings_dropdown_v3_pool_type_disabled.png differ
diff --git a/test/app/create/widgets/goldens/create_page_settings_dropdown_v3_pool_type_enable.png b/test/app/create/widgets/goldens/create_page_settings_dropdown_v3_pool_type_enable.png
new file mode 100644
index 0000000..c619409
Binary files /dev/null and b/test/app/create/widgets/goldens/create_page_settings_dropdown_v3_pool_type_enable.png differ
diff --git a/test/app/create/widgets/goldens/create_page_settings_dropdown_v4_pool_type_disabled.png b/test/app/create/widgets/goldens/create_page_settings_dropdown_v4_pool_type_disabled.png
new file mode 100644
index 0000000..303eebf
Binary files /dev/null and b/test/app/create/widgets/goldens/create_page_settings_dropdown_v4_pool_type_disabled.png differ
diff --git a/test/app/create/widgets/goldens/create_page_settings_dropdown_v4_pool_type_enable.png b/test/app/create/widgets/goldens/create_page_settings_dropdown_v4_pool_type_enable.png
new file mode 100644
index 0000000..650fd94
Binary files /dev/null and b/test/app/create/widgets/goldens/create_page_settings_dropdown_v4_pool_type_enable.png differ
diff --git a/test/core/bigint_extension_test.dart b/test/core/bigint_extension_test.dart
index 100a460..8997dfb 100644
--- a/test/core/bigint_extension_test.dart
+++ b/test/core/bigint_extension_test.dart
@@ -1,16 +1,16 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:zup_app/core/extensions/bigint_extension.dart';
-import 'package:zup_app/core/v3_pool_constants.dart';
+import 'package:zup_app/core/v3_v4_pool_constants.dart';
void main() {
test("`isMaxTick` extension should return if the bigInt passed is the max tick based on the V3 pool", () {
- BigInt maxTick = V3PoolConstants.maxTick;
+ BigInt maxTick = V3V4PoolConstants.maxTick;
expect(maxTick.isMaxTick, true);
});
test("`isMinTick` extension should return if the bigInt passed is the min tick based on the V3 pool", () {
- BigInt minTick = V3PoolConstants.minTick;
+ BigInt minTick = V3V4PoolConstants.minTick;
expect(minTick.isMinTick, true);
});
diff --git a/test/core/dto/yield_dto_test.dart b/test/core/dto/yield_dto_test.dart
deleted file mode 100644
index be677c9..0000000
--- a/test/core/dto/yield_dto_test.dart
+++ /dev/null
@@ -1,97 +0,0 @@
-import 'package:flutter_test/flutter_test.dart';
-import 'package:zup_app/core/dtos/token_dto.dart';
-import 'package:zup_app/core/dtos/yield_dto.dart';
-import 'package:zup_app/core/enums/networks.dart';
-
-void main() {
- test("When calling `maybeNativeToken1` with `permitNative` false, it should return the yield token1", () {
- final sut = YieldDto.fixture();
- final token = sut.maybeNativeToken1(permitNative: false);
-
- expect(token, sut.token1);
- });
-
- test("When calling `maybeNativeToken0` with `permitNative` false, it should return the yield token0", () {
- final sut = YieldDto.fixture();
- final token = sut.maybeNativeToken0(permitNative: false);
-
- expect(token, sut.token0);
- });
-
- test("""When calling `maybeNativeToken1` with `permitNative` true,
- and a token 1 that is not the wrapped native address,
- it should return the yield token1""", () {
- final chainId = AppNetworks.mainnet.chainId;
-
- final sut = YieldDto.fixture().copyWith(
- chainId: chainId,
- token1: TokenDto(
- addresses: {chainId: "0x123"},
- ),
- );
- final token = sut.maybeNativeToken1(permitNative: true);
-
- expect(token, sut.token1);
- });
-
- test("""When calling `maybeNativeToken0` with `permitNative` true,
- and a token 0 that is not the wrapped native address,
- it should return the yield token0""", () {
- final chainId = AppNetworks.mainnet.chainId;
-
- final sut = YieldDto.fixture().copyWith(
- chainId: chainId,
- token0: TokenDto(
- addresses: {chainId: "0x123"},
- ),
- );
- final token = sut.maybeNativeToken0(permitNative: true);
-
- expect(token, sut.token0);
- });
-
- test("""When calling `maybeNativeToken1` with `permitNative` true,
- and a token 1 that is the wrapped native address,
- it should return the native token for the yield network""", () {
- const network = AppNetworks.sepolia;
- final sut = YieldDto.fixture().copyWith(
- token1: TokenDto(addresses: {network.chainId: network.wrappedNativeTokenAddress}),
- chainId: network.chainId,
- );
-
- final token = sut.maybeNativeToken1(permitNative: true);
-
- expect(
- token,
- TokenDto(
- addresses: {network.chainId: network.wrappedNativeTokenAddress},
- decimals: network.chainInfo.nativeCurrency!.decimals,
- logoUrl: network.chainInfo.nativeCurrency!.logoUrl,
- symbol: network.chainInfo.nativeCurrency!.symbol,
- name: network.chainInfo.nativeCurrency!.name,
- ));
- });
-
- test("""When calling `maybeNativeToken0` with `permitNative` true,
- and a token 0 that is the wrapped native address,
- it should return the native token for the yield network""", () {
- const network = AppNetworks.sepolia;
-
- final sut = YieldDto.fixture().copyWith(
- token0: TokenDto(addresses: {network.chainId: network.wrappedNativeTokenAddress}),
- chainId: network.chainId,
- );
-
- final token = sut.maybeNativeToken0(permitNative: true);
-
- expect(
- token,
- TokenDto(
- addresses: {network.chainId: network.wrappedNativeTokenAddress},
- decimals: network.chainInfo.nativeCurrency!.decimals,
- logoUrl: network.chainInfo.nativeCurrency!.logoUrl,
- symbol: network.chainInfo.nativeCurrency!.symbol,
- name: network.chainInfo.nativeCurrency!.name,
- ));
- });
-}
diff --git a/test/core/dtos/yield_dto_test.dart b/test/core/dtos/yield_dto_test.dart
new file mode 100644
index 0000000..b33cfc1
--- /dev/null
+++ b/test/core/dtos/yield_dto_test.dart
@@ -0,0 +1,51 @@
+import 'package:flutter_test/flutter_test.dart';
+import 'package:web3kit/core/ethereum_constants.dart';
+import 'package:zup_app/core/dtos/token_dto.dart';
+import 'package:zup_app/core/dtos/yield_dto.dart';
+import 'package:zup_app/core/enums/networks.dart';
+
+void main() {
+ test("When calling `isToken0Native` and the token0 address in the yield network is zero, it should return true", () {
+ const network = AppNetworks.sepolia;
+ expect(
+ YieldDto.fixture()
+ .copyWith(
+ chainId: network.chainId,
+ token0: TokenDto.fixture().copyWith(addresses: {network.chainId: EthereumConstants.zeroAddress}))
+ .isToken0Native,
+ true,
+ );
+ });
+
+ test("When calling `isToken1Native` and the token1 address in the yield network is zero, it should return true", () {
+ const network = AppNetworks.sepolia;
+ expect(
+ YieldDto.fixture()
+ .copyWith(
+ chainId: network.chainId,
+ token1: TokenDto.fixture().copyWith(addresses: {network.chainId: EthereumConstants.zeroAddress}))
+ .isToken1Native,
+ true,
+ );
+ });
+
+ test("When calling `isToken0Native` and the token0 address in the yield network is not, it should return false", () {
+ const network = AppNetworks.sepolia;
+ expect(
+ YieldDto.fixture()
+ .copyWith(chainId: network.chainId, token0: TokenDto.fixture().copyWith(addresses: {network.chainId: "0x1"}))
+ .isToken0Native,
+ false,
+ );
+ });
+
+ test("When calling `isToken1Native` and the token1 address in the yield network is not, it should return false", () {
+ const network = AppNetworks.sepolia;
+ expect(
+ YieldDto.fixture()
+ .copyWith(chainId: network.chainId, token1: TokenDto.fixture().copyWith(addresses: {network.chainId: "0x1"}))
+ .isToken1Native,
+ false,
+ );
+ });
+}
diff --git a/test/core/enums/goldens/scroll_network_icon.png b/test/core/enums/goldens/scroll_network_icon.png
new file mode 100644
index 0000000..a72a778
Binary files /dev/null and b/test/core/enums/goldens/scroll_network_icon.png differ
diff --git a/test/core/enums/goldens/unichain_network_icon.png b/test/core/enums/goldens/unichain_network_icon.png
new file mode 100644
index 0000000..33e339e
Binary files /dev/null and b/test/core/enums/goldens/unichain_network_icon.png differ
diff --git a/test/core/enums/networks_test.dart b/test/core/enums/networks_test.dart
index 2d7b151..41fa609 100644
--- a/test/core/enums/networks_test.dart
+++ b/test/core/enums/networks_test.dart
@@ -18,15 +18,21 @@ void main() {
});
test("When calling 'fromValue' it should get a network from a string value", () {
- expect(AppNetworks.fromValue("sepolia"), AppNetworks.sepolia);
- expect(AppNetworks.fromValue("mainnet"), AppNetworks.mainnet);
- expect(AppNetworks.fromValue("scroll"), AppNetworks.scroll);
+ expect(AppNetworks.fromValue("sepolia"), AppNetworks.sepolia, reason: "Sepolia should match");
+ expect(AppNetworks.fromValue("mainnet"), AppNetworks.mainnet, reason: "Mainnet should match");
+ expect(AppNetworks.fromValue("scroll"), AppNetworks.scroll, reason: "Scroll should match");
+ expect(AppNetworks.fromValue("allNetworks"), AppNetworks.allNetworks, reason: "All networks should match");
+ // expect(AppNetworks.fromValue("base"), AppNetworks.base, reason: "Base should match");
+ expect(AppNetworks.fromValue("unichain"), AppNetworks.unichain, reason: "Unichain should match");
});
test("Label extension should match for all networks", () {
expect(AppNetworks.sepolia.label, "Sepolia", reason: "Sepolia Label should match");
expect(AppNetworks.mainnet.label, "Ethereum", reason: "Ethereum Label should match");
expect(AppNetworks.scroll.label, "Scroll", reason: "Scroll Label should match");
+ expect(AppNetworks.allNetworks.label, "All Networks", reason: "All Networks Label should match");
+ // expect(AppNetworks.base.label, "Base", reason: "Base Label should match");
+ expect(AppNetworks.unichain.label, "Unichain", reason: "Unichain Label should match");
});
test("`testnets` method should return all testnets in the enum, excluding the 'all networks'", () {
@@ -34,7 +40,16 @@ void main() {
});
test("`mainnets` method should return all mainnets in the enum, including the 'all networks'", () {
- expect(AppNetworks.mainnets, [AppNetworks.allNetworks, AppNetworks.mainnet, AppNetworks.scroll]);
+ expect(
+ AppNetworks.mainnets,
+ containsAll([
+ AppNetworks.allNetworks,
+ AppNetworks.mainnet,
+ AppNetworks.scroll,
+ // AppNetworks.base,
+ AppNetworks.unichain,
+ ]),
+ );
});
test("`isTestnet` method should return true for sepolia", () {
@@ -49,6 +64,14 @@ void main() {
expect(AppNetworks.scroll.isTestnet, false);
});
+ test("`isTestnet` method should return false for base", () {
+ // expect(AppNetworks.base.isTestnet, false);
+ });
+
+ test("`isTestnet` method should return false for unichain", () {
+ expect(AppNetworks.unichain.isTestnet, false);
+ });
+
test("Chain info extension should match for all networks", () {
expect(
AppNetworks.sepolia.chainInfo,
@@ -84,6 +107,30 @@ void main() {
),
reason: "Scroll ChainInfo should match",
);
+
+ // expect(
+ // AppNetworks.base.chainInfo,
+ // ChainInfo(
+ // hexChainId: "0x2105",
+ // chainName: "Base",
+ // blockExplorerUrls: const ["https://basescan.org"],
+ // nativeCurrency: NativeCurrencies.eth.currencyInfo,
+ // rpcUrls: const ["https://base-rpc.publicnode.com"],
+ // ),
+ // reason: "Base ChainInfo should match",
+ // );
+
+ expect(
+ AppNetworks.unichain.chainInfo,
+ ChainInfo(
+ hexChainId: "0x82",
+ chainName: "Unichain",
+ blockExplorerUrls: const ["https://uniscan.xyz/"],
+ nativeCurrency: NativeCurrencies.eth.currencyInfo,
+ rpcUrls: const ["https://unichain-rpc.publicnode.com"],
+ ),
+ reason: "Unichain ChainInfo should match",
+ );
});
test("wrapped native token address should match for all networks", () {
@@ -104,6 +151,18 @@ void main() {
"0x5300000000000000000000000000000000000004",
reason: "Scroll wrapped native token address should match",
);
+
+ // expect(
+ // AppNetworks.base.wrappedNativeTokenAddress,
+ // "0x4200000000000000000000000000000000000006",
+ // reason: "Base wrapped native token address should match",
+ // );
+
+ expect(
+ AppNetworks.unichain.wrappedNativeTokenAddress,
+ "0x4200000000000000000000000000000000000006",
+ reason: "Unichain wrapped native token address should match",
+ );
});
test("wrapped native token should match for all networks", () {
@@ -149,6 +208,35 @@ void main() {
),
reason: "Scroll default token should match",
);
+
+ // expect(
+ // AppNetworks.base.wrappedNative,
+ // TokenDto(
+ // addresses: {
+ // AppNetworks.base.chainId: "0x4200000000000000000000000000000000000006",
+ // },
+ // name: "Wrapped Ether",
+ // decimals: 18,
+ // symbol: "WETH",
+ // logoUrl:
+ // "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/base/assets/0x4200000000000000000000000000000000000006/logo.png",
+ // ),
+ // reason: "Base default token should match",
+ // );
+
+ expect(
+ AppNetworks.unichain.wrappedNative,
+ TokenDto(
+ addresses: {
+ AppNetworks.unichain.chainId: "0x4200000000000000000000000000000000000006",
+ },
+ name: "Wrapped Ether",
+ decimals: 18,
+ symbol: "WETH",
+ logoUrl: "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/unichain/logo.png",
+ ),
+ reason: "Unichain default token should match",
+ );
});
test("RpcUrl extension should return the correct rpc url", () {
@@ -169,6 +257,18 @@ void main() {
"https://scroll-rpc.publicnode.com",
reason: "Scroll rpc url should match",
);
+
+ // expect(
+ // AppNetworks.base.rpcUrl,
+ // "https://base-rpc.publicnode.com",
+ // reason: "Base rpc url should match",
+ // );
+
+ expect(
+ AppNetworks.unichain.rpcUrl,
+ "https://unichain-rpc.publicnode.com",
+ reason: "Unichain rpc url should match",
+ );
});
test("openTx should open the correct url for each network", () async {
@@ -187,48 +287,6 @@ void main() {
}
});
- test("'nativeCurrency' should return the correct currency for sepolia network", () {
- expect(
- AppNetworks.sepolia.nativeCurrencyTokenDto,
- TokenDto(
- addresses: {},
- name: NativeCurrencies.eth.currencyInfo.name,
- decimals: NativeCurrencies.eth.currencyInfo.decimals,
- symbol: NativeCurrencies.eth.currencyInfo.symbol,
- logoUrl: NativeCurrencies.eth.currencyInfo.logoUrl,
- ),
- reason: "Sepolia native currency should match",
- );
- });
-
- test("'nativeCurrency' should return the correct currency for ethereum network", () {
- expect(
- AppNetworks.mainnet.nativeCurrencyTokenDto,
- TokenDto(
- addresses: {},
- name: NativeCurrencies.eth.currencyInfo.name,
- decimals: NativeCurrencies.eth.currencyInfo.decimals,
- symbol: NativeCurrencies.eth.currencyInfo.symbol,
- logoUrl: NativeCurrencies.eth.currencyInfo.logoUrl,
- ),
- reason: "Ethereum native currency should match",
- );
- });
-
- test("'nativeCurrency' should return the correct currency for scroll network", () {
- expect(
- AppNetworks.scroll.nativeCurrencyTokenDto,
- TokenDto(
- addresses: {},
- name: NativeCurrencies.eth.currencyInfo.name,
- decimals: NativeCurrencies.eth.currencyInfo.decimals,
- symbol: NativeCurrencies.eth.currencyInfo.symbol,
- logoUrl: NativeCurrencies.eth.currencyInfo.logoUrl,
- ),
- reason: "Scroll native currency should match",
- );
- });
-
test("'fromChainId' should return the correct network from the chain id", () {
for (final network in AppNetworks.values) {
if (network.isAllNetworks) continue;
@@ -247,6 +305,7 @@ void main() {
test("'isAllNetworks' should return false if the network is not all networks", () {
expect(AppNetworks.scroll.isAllNetworks, false);
+ expect(AppNetworks.unichain.isAllNetworks, false);
});
test("'chainId' should return the correct chain id for each network", () {
@@ -270,4 +329,25 @@ void main() {
device: GoldenDevice.square,
));
});
+
+ // zGoldenTest("Base network icon should match", goldenFileName: "base_network_icon", (tester) async {
+ // await tester.pumpDeviceBuilder(await goldenDeviceBuilder(
+ // AppNetworks.base.icon,
+ // device: GoldenDevice.square,
+ // ));
+ // });
+
+ zGoldenTest("Scroll network icon should match", goldenFileName: "scroll_network_icon", (tester) async {
+ await tester.pumpDeviceBuilder(await goldenDeviceBuilder(
+ AppNetworks.scroll.icon,
+ device: GoldenDevice.square,
+ ));
+ });
+
+ zGoldenTest("Unichain network icon should match", goldenFileName: "unichain_network_icon", (tester) async {
+ await tester.pumpDeviceBuilder(await goldenDeviceBuilder(
+ AppNetworks.unichain.icon,
+ device: GoldenDevice.square,
+ ));
+ });
}
diff --git a/test/core/enums/pool_type_test.dart b/test/core/enums/pool_type_test.dart
new file mode 100644
index 0000000..ddcfa80
--- /dev/null
+++ b/test/core/enums/pool_type_test.dart
@@ -0,0 +1,25 @@
+import 'package:flutter_test/flutter_test.dart';
+import 'package:zup_app/core/enums/pool_type.dart';
+
+void main() {
+ test('When calling `isV3` and the pool is indeed v3, it should return true', () {
+ expect(PoolType.v3.isV3, true);
+ });
+
+ test('When calling `isV3` and the pool is not v3, it should return false', () {
+ expect(PoolType.v4.isV3, false);
+ });
+
+ test('When calling `isV4` and the pool is indeed v4, it should return true', () {
+ expect(PoolType.v4.isV4, true);
+ });
+
+ test('When calling `isV4` and the pool is not v4, it should return false', () {
+ expect(PoolType.v3.isV4, false);
+ });
+
+ test('label should return correct string', () {
+ expect(PoolType.v3.label, "V3");
+ expect(PoolType.v4.label, "V4");
+ });
+}
diff --git a/test/core/mixins/keys_mixin_test.dart b/test/core/mixins/keys_mixin_test.dart
index 7378699..c71dfd7 100644
--- a/test/core/mixins/keys_mixin_test.dart
+++ b/test/core/mixins/keys_mixin_test.dart
@@ -30,4 +30,16 @@ void main() {
expect(key, 'poolTick-$poolAddress-${network.name}');
});
+
+ test("`tokenPriceCacheKey` should return correct key", () {
+ const tokenAddress = '0xTokenAddress';
+ const network = AppNetworks.sepolia;
+
+ final key = _KeysMixinWrapper().tokenPriceCacheKey(
+ tokenAddress: tokenAddress,
+ network: network,
+ );
+
+ expect(key, 'tokenPrice-$tokenAddress-${network.name}');
+ });
}
diff --git a/test/core/mixins/v3_pool_conversors_mixin_test.dart b/test/core/mixins/v3_pool_conversors_mixin_test.dart
index 46417ea..b8d7aef 100644
--- a/test/core/mixins/v3_pool_conversors_mixin_test.dart
+++ b/test/core/mixins/v3_pool_conversors_mixin_test.dart
@@ -1,6 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:zup_app/core/mixins/v3_pool_conversors_mixin.dart';
-import 'package:zup_app/core/v3_pool_constants.dart';
+import 'package:zup_app/core/v3_v4_pool_constants.dart';
class _V3PoolConversorsMixinTest with V3PoolConversorsMixin {}
@@ -29,21 +29,21 @@ void main() {
test("""When calling `tickToClosestValidTick` and the closest valid tick is lower
than the minimum tick, it should return the higher valid tick""", () {
final closestValidTick = _V3PoolConversorsMixinTest().tickToClosestValidTick(
- tick: V3PoolConstants.minTick - BigInt.from(1),
+ tick: V3V4PoolConstants.minTick - BigInt.from(1),
tickSpacing: 1,
);
- expect(closestValidTick, V3PoolConstants.minTick);
+ expect(closestValidTick, V3V4PoolConstants.minTick);
});
test("""When calling `tickToClosestValidTick` and the closest valid tick is higher
than the maximum tick, it should return the lower valid tick""", () {
final closestValidTick = _V3PoolConversorsMixinTest().tickToClosestValidTick(
- tick: V3PoolConstants.maxTick + BigInt.from(2),
+ tick: V3V4PoolConstants.maxTick + BigInt.from(2),
tickSpacing: 4,
);
- expect(closestValidTick, V3PoolConstants.maxTick);
+ expect(closestValidTick, V3V4PoolConstants.maxTick);
});
test("""`tickToClosestValidTick` should correctly convert a
diff --git a/test/core/mixins/v4_pool_liquidity_calculations_mixin_test.dart b/test/core/mixins/v4_pool_liquidity_calculations_mixin_test.dart
new file mode 100644
index 0000000..e71d746
--- /dev/null
+++ b/test/core/mixins/v4_pool_liquidity_calculations_mixin_test.dart
@@ -0,0 +1,95 @@
+import 'package:flutter_test/flutter_test.dart';
+import 'package:zup_app/core/mixins/v4_pool_liquidity_calculations_mixin.dart';
+
+class _V4PoolLiquidityCalculationsMixinTest with V4PoolLiquidityCalculationsMixin {}
+
+void main() {
+ test(
+ "When calling `getLiquidityForAmount0` it should return the correct value based on the v4 pool math",
+ () {
+ BigInt sqrtPriceAX96 = BigInt.parse("4242269098745952767280720");
+ BigInt sqrtPriceBX96 = BigInt.parse("4242269098745952767280721");
+ BigInt amount0 = BigInt.from(1241555);
+
+ expect(
+ _V4PoolLiquidityCalculationsMixinTest().getLiquidityForAmount0(sqrtPriceAX96, sqrtPriceBX96, amount0),
+ BigInt.parse("282021882116526385819866125"),
+ );
+ },
+ );
+
+ test(
+ "When calling `getLiquidityForAmount1` it should return the correct value based on the v4 pool math",
+ () {
+ BigInt sqrtPriceAX96 = BigInt.parse("4242269098745952767280720");
+ BigInt sqrtPriceBX96 = BigInt.parse("4242269098745952767280721");
+ BigInt amount0 = BigInt.from(1241555);
+
+ expect(
+ _V4PoolLiquidityCalculationsMixinTest().getLiquidityForAmount1(sqrtPriceAX96, sqrtPriceBX96, amount0),
+ BigInt.parse("98366121310397459660952459259412480"),
+ );
+ },
+ );
+
+ test(
+ "When calling `getLiquidityForAmounts` and the sqrtpricea is bigger, it should return the token0 liquidity calculated",
+ () {
+ BigInt sqrtPriceX96 = BigInt.parse("4242269098745952767280720");
+ BigInt sqrtPriceAX96 = BigInt.parse("4242269098745952767280720");
+ BigInt sqrtPriceBX96 = BigInt.parse("4242269098745952767280721");
+ BigInt amount0 = BigInt.from(1241555);
+ BigInt amount1 = BigInt.from(1241555);
+
+ expect(
+ _V4PoolLiquidityCalculationsMixinTest()
+ .getLiquidityForAmounts(sqrtPriceX96, sqrtPriceAX96, sqrtPriceBX96, amount0, amount1),
+ BigInt.parse("282021882116526385819866125"),
+ );
+ },
+ );
+
+ test(
+ "When calling `getLiquidityForAmounts` and the sqrtpriceA is lower, it should return the token1 liquidity calculated",
+ () {
+ BigInt sqrtPriceX96 = BigInt.parse("4242269098745952767280720");
+ BigInt sqrtPriceAX96 = BigInt.parse("4242269098745952767280724");
+ BigInt sqrtPriceBX96 = BigInt.parse("4242269098745952767280721");
+ BigInt amount0 = BigInt.from(1241555);
+ BigInt amount1 = BigInt.from(1241555);
+
+ expect(
+ _V4PoolLiquidityCalculationsMixinTest()
+ .getLiquidityForAmounts(sqrtPriceX96, sqrtPriceAX96, sqrtPriceBX96, amount0, amount1),
+ BigInt.parse("94007294038842128606622041"),
+ );
+ },
+ );
+
+ test('getSqrtPriceAtTick should return correct value for tick 0', () {
+ final result = _V4PoolLiquidityCalculationsMixinTest().getSqrtPriceAtTick(BigInt.zero);
+ expect(result, BigInt.parse('79228162514264337593543950336'));
+ });
+
+ test('getSqrtPriceAtTick should return correct value for positive tick', () {
+ final result = _V4PoolLiquidityCalculationsMixinTest().getSqrtPriceAtTick(BigInt.from(60));
+ expect(result, BigInt.parse('79466191966197645195421774833'));
+ });
+
+ test('getSqrtPriceAtTick should return correct value for negative tick', () {
+ final result = _V4PoolLiquidityCalculationsMixinTest().getSqrtPriceAtTick(BigInt.from(-60));
+ expect(result, BigInt.parse('78990846045029531151608375686'));
+ });
+
+ test('getSqrtPriceAtTick should throw for tick out of range', () {
+ expect(
+ () => _V4PoolLiquidityCalculationsMixinTest().getSqrtPriceAtTick(BigInt.from(887273)),
+ throwsException,
+ );
+
+ expect(
+ () => _V4PoolLiquidityCalculationsMixinTest().getSqrtPriceAtTick(BigInt.from(-887273)),
+ throwsException,
+ );
+ });
+}
diff --git a/test/core/pool_service_test.dart b/test/core/pool_service_test.dart
new file mode 100644
index 0000000..c19d037
--- /dev/null
+++ b/test/core/pool_service_test.dart
@@ -0,0 +1,1334 @@
+import 'package:clock/clock.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:mocktail/mocktail.dart';
+import 'package:web3kit/core/dtos/transaction_receipt.dart';
+import 'package:web3kit/core/dtos/transaction_response.dart';
+import 'package:web3kit/web3kit.dart';
+import 'package:zup_app/abis/uniswap_v3_pool.abi.g.dart';
+import 'package:zup_app/abis/uniswap_v3_position_manager.abi.g.dart';
+import 'package:zup_app/abis/uniswap_v4_position_manager.abi.g.dart';
+import 'package:zup_app/abis/uniswap_v4_state_view.abi.g.dart';
+import 'package:zup_app/core/dtos/token_dto.dart';
+import 'package:zup_app/core/dtos/yield_dto.dart';
+import 'package:zup_app/core/enums/networks.dart';
+import 'package:zup_app/core/enums/pool_type.dart';
+import 'package:zup_app/core/mixins/v4_pool_liquidity_calculations_mixin.dart';
+import 'package:zup_app/core/pool_service.dart';
+import 'package:zup_app/core/v4_pool_constants.dart';
+
+import '../mocks.dart';
+
+class _V4PoolLiquidityCalculationsMixinWrapper with V4PoolLiquidityCalculationsMixin {}
+
+void main() {
+ late PoolService sut;
+ late UniswapV4StateView stateView;
+ late UniswapV3Pool uniswapV3Pool;
+ late UniswapV3PositionManager positionManagerV3;
+ late UniswapV4PositionManager positionManagerV4;
+ late Signer signer;
+ late YieldDto currentYield;
+ late TransactionResponse transactionResponse;
+
+ late UniswapV4StateViewImpl stateViewImpl;
+ late UniswapV3PoolImpl uniswapV3PoolImpl;
+ late UniswapV3PositionManagerImpl positionManagerV3Impl;
+ late UniswapV4PositionManagerImpl positionManagerV4Impl;
+ late EthereumAbiCoder ethereumAbiCoder;
+
+ setUp(() {
+ registerFallbackValue((
+ amount0Desired: BigInt.zero,
+ amount0Min: BigInt.zero,
+ amount1Desired: BigInt.zero,
+ amount1Min: BigInt.zero,
+ deadline: BigInt.zero,
+ fee: BigInt.zero,
+ recipient: "",
+ tickLower: BigInt.zero,
+ tickUpper: BigInt.zero,
+ token0: "",
+ token1: "",
+ ));
+ registerFallbackValue(SignerMock());
+ registerFallbackValue(BigInt.zero);
+
+ transactionResponse = TransactionResponseMock();
+ stateView = UniswapV4StateViewMock();
+ uniswapV3Pool = UniswapV3PoolMock();
+ positionManagerV3 = UniswapV3PositionManagerMock();
+ positionManagerV4 = UniswapV4PositionManagerMock();
+ ethereumAbiCoder = EthereumAbiCoderMock();
+ signer = SignerMock();
+
+ stateViewImpl = UniswapV4StateViewImplMock();
+ uniswapV3PoolImpl = UniswapV3PoolImplMock();
+ positionManagerV3Impl = UniswapV3PositionManagerImplMock();
+ positionManagerV4Impl = UniswapV4PositionManagerImplMock();
+
+ currentYield = YieldDto.fixture();
+
+ sut = PoolService(stateView, uniswapV3Pool, positionManagerV3, positionManagerV4, ethereumAbiCoder);
+
+ when(() => stateView.fromRpcProvider(contractAddress: any(named: "contractAddress"), rpcUrl: any(named: "rpcUrl")))
+ .thenReturn(stateViewImpl);
+
+ when(() => uniswapV3Pool.fromRpcProvider(
+ contractAddress: any(named: "contractAddress"),
+ rpcUrl: any(named: "rpcUrl"),
+ )).thenReturn(uniswapV3PoolImpl);
+
+ when(() => positionManagerV3.fromRpcProvider(
+ contractAddress: any(named: "contractAddress"),
+ rpcUrl: any(named: "rpcUrl"))).thenReturn(positionManagerV3Impl);
+
+ when(() =>
+ positionManagerV3.fromSigner(contractAddress: any(named: "contractAddress"), signer: any(named: "signer")))
+ .thenReturn(positionManagerV3Impl);
+
+ when(() =>
+ positionManagerV4.fromSigner(contractAddress: any(named: "contractAddress"), signer: any(named: "signer")))
+ .thenReturn(positionManagerV4Impl);
+
+ when(() => positionManagerV4.fromRpcProvider(
+ contractAddress: any(named: "contractAddress"),
+ rpcUrl: any(named: "rpcUrl"))).thenReturn(positionManagerV4Impl);
+
+ when(() => signer.address).thenAnswer((_) async => "0xS0M3_4ddr355");
+
+ when(() => transactionResponse.waitConfirmation()).thenAnswer((_) async => TransactionReceipt(hash: "0x123"));
+ when(() => transactionResponse.hash).thenReturn("0x123");
+ });
+
+ test(
+ "When calling `getPoolTick` and the pool is v4, it should use the state view contract to get it",
+ () async {
+ final expectedTick = BigInt.from(87654);
+ when(() => stateViewImpl.getSlot0(poolId: any(named: "poolId"))).thenAnswer((_) async => (
+ lpFee: BigInt.from(0),
+ protocolFee: BigInt.from(0),
+ sqrtPriceX96: BigInt.from(0),
+ tick: expectedTick,
+ ));
+ final currentYield0 = currentYield.copyWith(poolType: PoolType.v4, v4StateView: "0x123");
+ final result = await sut.getPoolTick(currentYield0);
+
+ expect(result, expectedTick);
+ verify(() => stateViewImpl.getSlot0(poolId: currentYield0.poolAddress)).called(1);
+ },
+ );
+
+ test(
+ "When calling `getPoolTick` and the pool is v3, it should use the v3 pool contract to get it",
+ () async {
+ final expectedTick = BigInt.from(2127);
+ when(() => uniswapV3PoolImpl.slot0()).thenAnswer((_) async => (
+ feeProtocol: BigInt.from(0),
+ observationCardinality: BigInt.from(0),
+ observationCardinalityNext: BigInt.from(0),
+ observationIndex: BigInt.from(0),
+ sqrtPriceX96: BigInt.from(0),
+ tick: expectedTick,
+ unlocked: true
+ ));
+
+ final currentYield0 = currentYield.copyWith(poolType: PoolType.v3);
+ final result = await sut.getPoolTick(currentYield0);
+
+ expect(result, expectedTick);
+ verify(() => uniswapV3PoolImpl.slot0()).called(1);
+ },
+ );
+
+ test(
+ """when calling `sendV3PoolDepositTransaction` with token0 native,
+ it should send a multicall transaction with the mint calldata and a native
+ refund calldata""",
+ () async {
+ const mintCalldata = "0x25";
+ const refundCalldata = "0x26";
+
+ const network = AppNetworks.mainnet;
+ final currentYield0 = currentYield.copyWith(
+ poolType: PoolType.v3,
+ chainId: network.chainId,
+ token0: TokenDto.fixture().copyWith(addresses: {
+ network.chainId: EthereumConstants.zeroAddress,
+ }),
+ token1: TokenDto.fixture().copyWith(addresses: {
+ network.chainId: "0x123",
+ }));
+
+ when(() => positionManagerV3.getMintCalldata(params: any(named: "params"))).thenReturn(mintCalldata);
+ when(() => positionManagerV3.getRefundETHCalldata()).thenReturn(refundCalldata);
+ when(() => positionManagerV3Impl.multicall(data: any(named: "data"), ethValue: any(named: "ethValue")))
+ .thenAnswer(
+ (_) async => transactionResponse,
+ );
+
+ final amount0Desired = BigInt.from(100);
+ final amount1Desired = BigInt.from(100);
+ const deadline = Duration.zero;
+ final amount0Min = BigInt.from(12);
+ final amount1Min = BigInt.from(12);
+ final recipient = await signer.address;
+ final tickLower = BigInt.from(0);
+ final tickUpper = BigInt.from(0);
+
+ await sut.sendV3PoolDepositTransaction(
+ currentYield0,
+ signer,
+ amount0Desired: amount0Desired,
+ amount1Desired: amount1Desired,
+ deadline: deadline,
+ amount0Min: amount0Min,
+ amount1Min: amount1Min,
+ recipient: recipient,
+ tickLower: tickLower,
+ tickUpper: tickUpper,
+ );
+
+ verify(
+ () => positionManagerV3Impl.multicall(
+ data: [mintCalldata, refundCalldata],
+ ethValue: any(named: "ethValue"),
+ ),
+ ).called(1);
+ },
+ );
+
+ test(
+ """when calling `sendV3PoolDepositTransaction` with token0 native, it should correctly
+ pass the params to get the mint calldata, with the token0 being the wrapped native address""",
+ () async {
+ withClock(Clock.fixed(DateTime(2028)), () async {
+ const mintCalldata = "0x25";
+ const refundCalldata = "0x26";
+ const token0Address = EthereumConstants.zeroAddress;
+ const token1Address = "0x20172891";
+
+ const network = AppNetworks.mainnet;
+ final currentYield0 = currentYield.copyWith(
+ poolType: PoolType.v3,
+ chainId: network.chainId,
+ token0: TokenDto.fixture().copyWith(addresses: {network.chainId: token0Address}),
+ token1: TokenDto.fixture().copyWith(addresses: {network.chainId: token1Address}));
+
+ when(() => positionManagerV3.getMintCalldata(params: any(named: "params"))).thenReturn(mintCalldata);
+ when(() => positionManagerV3.getRefundETHCalldata()).thenReturn(refundCalldata);
+ when(() => positionManagerV3Impl.multicall(data: any(named: "data"), ethValue: any(named: "ethValue")))
+ .thenAnswer(
+ (_) async => transactionResponse,
+ );
+
+ final amount0Desired = BigInt.from(4311);
+ final amount1Desired = BigInt.from(1031900);
+ const deadline = Duration(days: 1);
+ final amount0Min = BigInt.from(1390);
+ final amount1Min = BigInt.from(432);
+ final recipient = await signer.address;
+ final tickLower = BigInt.from(321);
+ final tickUpper = BigInt.from(1222);
+
+ await sut.sendV3PoolDepositTransaction(
+ currentYield0,
+ signer,
+ amount0Desired: amount0Desired,
+ amount1Desired: amount1Desired,
+ deadline: deadline,
+ amount0Min: amount0Min,
+ amount1Min: amount1Min,
+ recipient: recipient,
+ tickLower: tickLower,
+ tickUpper: tickUpper,
+ );
+
+ verify(
+ () => positionManagerV3.getMintCalldata(params: (
+ amount0Desired: amount0Desired,
+ amount0Min: amount0Min,
+ amount1Desired: amount1Desired,
+ amount1Min: amount1Min,
+ deadline: BigInt.from(clock.now().add(deadline).millisecondsSinceEpoch),
+ fee: BigInt.from(currentYield0.feeTier),
+ recipient: recipient,
+ tickLower: tickLower,
+ tickUpper: tickUpper,
+ token0: network.wrappedNative.addresses[network.chainId]!,
+ token1: token1Address,
+ )),
+ ).called(1);
+ });
+ },
+ );
+
+ test(
+ """when calling `sendV3PoolDepositTransaction` with token1 native, it should correctly
+ pass the params to get the mint calldata, with the token1 being the wrapped native address""",
+ () async {
+ withClock(Clock.fixed(DateTime(2028)), () async {
+ const mintCalldata = "0x25";
+ const refundCalldata = "0x26";
+ const token0Address = "0x20172891";
+ const token1Address = EthereumConstants.zeroAddress;
+
+ const network = AppNetworks.mainnet;
+ final currentYield0 = currentYield.copyWith(
+ poolType: PoolType.v3,
+ chainId: network.chainId,
+ token0: TokenDto.fixture().copyWith(addresses: {network.chainId: token0Address}),
+ token1: TokenDto.fixture().copyWith(addresses: {network.chainId: token1Address}));
+
+ when(() => positionManagerV3.getMintCalldata(params: any(named: "params"))).thenReturn(mintCalldata);
+ when(() => positionManagerV3.getRefundETHCalldata()).thenReturn(refundCalldata);
+ when(() => positionManagerV3Impl.multicall(data: any(named: "data"), ethValue: any(named: "ethValue")))
+ .thenAnswer(
+ (_) async => transactionResponse,
+ );
+
+ final amount0Desired = BigInt.from(100);
+ final amount1Desired = BigInt.from(31);
+ const deadline = Duration.zero;
+ final amount0Min = BigInt.from(320);
+ final amount1Min = BigInt.from(12);
+ final recipient = await signer.address;
+ final tickLower = BigInt.from(32);
+ final tickUpper = BigInt.from(14489);
+
+ await sut.sendV3PoolDepositTransaction(
+ currentYield0,
+ signer,
+ amount0Desired: amount0Desired,
+ amount1Desired: amount1Desired,
+ deadline: deadline,
+ amount0Min: amount0Min,
+ amount1Min: amount1Min,
+ recipient: recipient,
+ tickLower: tickLower,
+ tickUpper: tickUpper,
+ );
+
+ verify(
+ () => positionManagerV3.getMintCalldata(params: (
+ amount0Desired: amount0Desired,
+ amount0Min: amount0Min,
+ amount1Desired: amount1Desired,
+ amount1Min: amount1Min,
+ deadline: BigInt.from(clock.now().add(deadline).millisecondsSinceEpoch),
+ fee: BigInt.from(currentYield0.feeTier),
+ recipient: recipient,
+ tickLower: tickLower,
+ tickUpper: tickUpper,
+ token0: token0Address,
+ token1: network.wrappedNativeTokenAddress,
+ )),
+ ).called(1);
+ });
+ },
+ );
+
+ test(
+ """when calling `sendV3PoolDepositTransaction` with token0 native, it should correctly
+ send the token0amount as ethValue""",
+ () async {
+ withClock(Clock.fixed(DateTime(2028)), () async {
+ const mintCalldata = "0x25";
+ const refundCalldata = "0x26";
+ const token0Address = EthereumConstants.zeroAddress;
+ const token1Address = "0x20172891";
+
+ const network = AppNetworks.mainnet;
+ final currentYield0 = currentYield.copyWith(
+ poolType: PoolType.v3,
+ chainId: network.chainId,
+ token0: TokenDto.fixture().copyWith(addresses: {network.chainId: token0Address}),
+ token1: TokenDto.fixture().copyWith(addresses: {network.chainId: token1Address}));
+
+ when(() => positionManagerV3.getMintCalldata(params: any(named: "params"))).thenReturn(mintCalldata);
+ when(() => positionManagerV3.getRefundETHCalldata()).thenReturn(refundCalldata);
+ when(() => positionManagerV3Impl.multicall(data: any(named: "data"), ethValue: any(named: "ethValue")))
+ .thenAnswer(
+ (_) async => transactionResponse,
+ );
+
+ final amount0Desired = BigInt.from(4311);
+ final amount1Desired = BigInt.from(1031900);
+ const deadline = Duration(days: 1);
+ final amount0Min = BigInt.from(1390);
+ final amount1Min = BigInt.from(432);
+ final recipient = await signer.address;
+ final tickLower = BigInt.from(321);
+ final tickUpper = BigInt.from(1222);
+
+ await sut.sendV3PoolDepositTransaction(
+ currentYield0,
+ signer,
+ amount0Desired: amount0Desired,
+ amount1Desired: amount1Desired,
+ deadline: deadline,
+ amount0Min: amount0Min,
+ amount1Min: amount1Min,
+ recipient: recipient,
+ tickLower: tickLower,
+ tickUpper: tickUpper,
+ );
+
+ verify(
+ () => positionManagerV3Impl.multicall(ethValue: amount0Desired, data: any(named: "data")),
+ ).called(1);
+ });
+ },
+ );
+
+ test(
+ """when calling `sendV3PoolDepositTransaction` with token1 native, it should correctly
+ send the token1amount as ethValue""",
+ () async {
+ withClock(Clock.fixed(DateTime(2028)), () async {
+ const mintCalldata = "0x25";
+ const refundCalldata = "0x26";
+ const token1Address = EthereumConstants.zeroAddress;
+ const token0Address = "0x20172891";
+
+ const network = AppNetworks.mainnet;
+ final currentYield0 = currentYield.copyWith(
+ poolType: PoolType.v3,
+ chainId: network.chainId,
+ token0: TokenDto.fixture().copyWith(addresses: {network.chainId: token0Address}),
+ token1: TokenDto.fixture().copyWith(addresses: {network.chainId: token1Address}));
+
+ when(() => positionManagerV3.getMintCalldata(params: any(named: "params"))).thenReturn(mintCalldata);
+ when(() => positionManagerV3.getRefundETHCalldata()).thenReturn(refundCalldata);
+ when(() => positionManagerV3Impl.multicall(data: any(named: "data"), ethValue: any(named: "ethValue")))
+ .thenAnswer(
+ (_) async => transactionResponse,
+ );
+
+ final amount0Desired = BigInt.from(4311);
+ final amount1Desired = BigInt.from(1031900);
+ const deadline = Duration(days: 1);
+ final amount0Min = BigInt.from(1390);
+ final amount1Min = BigInt.from(432);
+ final recipient = await signer.address;
+ final tickLower = BigInt.from(321);
+ final tickUpper = BigInt.from(1222);
+
+ await sut.sendV3PoolDepositTransaction(
+ currentYield0,
+ signer,
+ amount0Desired: amount0Desired,
+ amount1Desired: amount1Desired,
+ deadline: deadline,
+ amount0Min: amount0Min,
+ amount1Min: amount1Min,
+ recipient: recipient,
+ tickLower: tickLower,
+ tickUpper: tickUpper,
+ );
+
+ verify(
+ () => positionManagerV3Impl.multicall(ethValue: amount1Desired, data: any(named: "data")),
+ ).called(1);
+ });
+ },
+ );
+
+ test(
+ """When calling `sendV3PoolDepositTransaction` and there is no native token,
+ it should call `mint` in the v3 position manager passing the correct params""",
+ () {
+ withClock(Clock.fixed(DateTime(2028)), () async {
+ const token1Address = "0x315768";
+ const token0Address = "0x20172891";
+
+ when(() => positionManagerV3Impl.mint(params: any(named: "params"), ethValue: any(named: "ethValue")))
+ .thenAnswer(
+ (_) async => transactionResponse,
+ );
+
+ const network = AppNetworks.mainnet;
+ final currentYield0 = currentYield.copyWith(
+ feeTier: 3982,
+ poolType: PoolType.v3,
+ chainId: network.chainId,
+ token0: TokenDto.fixture().copyWith(addresses: {network.chainId: token0Address}),
+ token1: TokenDto.fixture().copyWith(addresses: {network.chainId: token1Address}));
+
+ final amount0Desired = BigInt.from(4311);
+ final amount1Desired = BigInt.from(1031900);
+ const deadline = Duration(days: 1);
+ final amount0Min = BigInt.from(1390);
+ final amount1Min = BigInt.from(432);
+ final recipient = await signer.address;
+ final tickLower = BigInt.from(321);
+ final tickUpper = BigInt.from(1222);
+
+ await sut.sendV3PoolDepositTransaction(
+ currentYield0,
+ signer,
+ amount0Desired: amount0Desired,
+ amount1Desired: amount1Desired,
+ deadline: deadline,
+ amount0Min: amount0Min,
+ amount1Min: amount1Min,
+ recipient: recipient,
+ tickLower: tickLower,
+ tickUpper: tickUpper,
+ );
+
+ verify(
+ () => positionManagerV3Impl.mint(
+ params: (
+ token0: token0Address,
+ token1: token1Address,
+ fee: BigInt.from(currentYield0.feeTier),
+ tickLower: tickLower,
+ tickUpper: tickUpper,
+ amount0Desired: amount0Desired,
+ amount1Desired: amount1Desired,
+ amount0Min: amount0Min,
+ amount1Min: amount1Min,
+ recipient: recipient,
+ deadline: BigInt.from(clock.now().add(deadline).millisecondsSinceEpoch),
+ ),
+ ethValue: null,
+ ),
+ ).called(1);
+ });
+ },
+ );
+
+ test(
+ "When calling `sendV4PoolDepositTransaction` and the token0 is native, it should encode packed the correct actions including the sweep",
+ () async {
+ when(() => ethereumAbiCoder.encodePacked(any(), any())).thenReturn("0x");
+ when(() => ethereumAbiCoder.encode(any(), any())).thenReturn("0x");
+ when(
+ () => positionManagerV4Impl.modifyLiquidities(
+ unlockData: any(named: "unlockData"), deadline: any(named: "deadline"), ethValue: any(named: "ethValue")),
+ ).thenAnswer((_) async => transactionResponse);
+
+ const network = AppNetworks.mainnet;
+ final amount0Desired = BigInt.from(4311);
+ final amount1Desired = BigInt.from(1031900);
+ const deadline = Duration(days: 1);
+ final amount0Max = BigInt.from(4312);
+ final amount1Max = BigInt.from(1031901);
+ final recipient = await signer.address;
+ final tickLower = BigInt.from(321);
+ final tickUpper = BigInt.from(1222);
+ final currentPoolTick = BigInt.from(123);
+ final currentYield0 = currentYield.copyWith(
+ chainId: network.chainId,
+ token0: TokenDto.fixture().copyWith(addresses: {network.chainId: EthereumConstants.zeroAddress}),
+ token1: TokenDto.fixture().copyWith(addresses: {network.chainId: "0x1"}),
+ );
+
+ await sut.sendV4PoolDepositTransaction(
+ currentYield0,
+ signer,
+ deadline: deadline,
+ tickLower: tickLower,
+ tickUpper: tickUpper,
+ amount0toDeposit: amount0Desired,
+ amount1ToDeposit: amount1Desired,
+ maxAmount0ToDeposit: amount0Max,
+ maxAmount1ToDeposit: amount1Max,
+ recipient: recipient,
+ currentPoolTick: currentPoolTick,
+ );
+
+ verify(() => ethereumAbiCoder.encodePacked([
+ "uint8",
+ "uint8",
+ "uint8"
+ ], [
+ V4PoolConstants.mintPositionActionValue,
+ V4PoolConstants.settlePairActionValue,
+ V4PoolConstants.sweepActionValue
+ ])).called(1);
+ });
+
+ test(
+ "When calling `sendV4PoolDepositTransaction` and the token1 is native, it should encode packed the correct actions including the sweep",
+ () async {
+ when(() => ethereumAbiCoder.encodePacked(any(), any())).thenReturn("0x");
+ when(() => ethereumAbiCoder.encode(any(), any())).thenReturn("0x");
+ when(
+ () => positionManagerV4Impl.modifyLiquidities(
+ unlockData: any(named: "unlockData"), deadline: any(named: "deadline"), ethValue: any(named: "ethValue")),
+ ).thenAnswer((_) async => transactionResponse);
+
+ const network = AppNetworks.mainnet;
+ final amount0Desired = BigInt.from(4311);
+ final amount1Desired = BigInt.from(1031900);
+ const deadline = Duration(days: 1);
+ final amount0Max = BigInt.from(4312);
+ final amount1Max = BigInt.from(1031901);
+ final recipient = await signer.address;
+ final tickLower = BigInt.from(321);
+ final tickUpper = BigInt.from(1222);
+ final currentPoolTick = BigInt.from(123);
+ final currentYield0 = currentYield.copyWith(
+ chainId: network.chainId,
+ token1: TokenDto.fixture().copyWith(addresses: {network.chainId: EthereumConstants.zeroAddress}),
+ token0: TokenDto.fixture().copyWith(addresses: {network.chainId: "0x1"}),
+ );
+
+ await sut.sendV4PoolDepositTransaction(
+ currentYield0,
+ signer,
+ deadline: deadline,
+ tickLower: tickLower,
+ tickUpper: tickUpper,
+ amount0toDeposit: amount0Desired,
+ amount1ToDeposit: amount1Desired,
+ maxAmount0ToDeposit: amount0Max,
+ maxAmount1ToDeposit: amount1Max,
+ recipient: recipient,
+ currentPoolTick: currentPoolTick,
+ );
+
+ verify(() => ethereumAbiCoder.encodePacked([
+ "uint8",
+ "uint8",
+ "uint8"
+ ], [
+ V4PoolConstants.mintPositionActionValue,
+ V4PoolConstants.settlePairActionValue,
+ V4PoolConstants.sweepActionValue
+ ])).called(1);
+ });
+
+ test(
+ "When calling `sendV4PoolDepositTransaction` and none of the tokens are native, it should not include the sweep action",
+ () async {
+ when(() => ethereumAbiCoder.encodePacked(any(), any())).thenReturn("0x");
+ when(() => ethereumAbiCoder.encode(any(), any())).thenReturn("0x");
+ when(
+ () => positionManagerV4Impl.modifyLiquidities(
+ unlockData: any(named: "unlockData"), deadline: any(named: "deadline"), ethValue: any(named: "ethValue")),
+ ).thenAnswer((_) async => transactionResponse);
+
+ const network = AppNetworks.mainnet;
+ final amount0Desired = BigInt.from(4311);
+ final amount1Desired = BigInt.from(1031900);
+ const deadline = Duration(days: 1);
+ final amount0Max = BigInt.from(4312);
+ final amount1Max = BigInt.from(1031901);
+ final recipient = await signer.address;
+ final tickLower = BigInt.from(321);
+ final tickUpper = BigInt.from(1222);
+ final currentPoolTick = BigInt.from(123);
+ final currentYield0 = currentYield.copyWith(
+ chainId: network.chainId,
+ token0: TokenDto.fixture().copyWith(addresses: {network.chainId: "0x2"}),
+ token1: TokenDto.fixture().copyWith(addresses: {network.chainId: "0x1"}),
+ );
+
+ await sut.sendV4PoolDepositTransaction(
+ currentYield0,
+ signer,
+ deadline: deadline,
+ tickLower: tickLower,
+ tickUpper: tickUpper,
+ amount0toDeposit: amount0Desired,
+ amount1ToDeposit: amount1Desired,
+ maxAmount0ToDeposit: amount0Max,
+ maxAmount1ToDeposit: amount1Max,
+ recipient: recipient,
+ currentPoolTick: currentPoolTick,
+ );
+
+ verify(() => ethereumAbiCoder.encodePacked([
+ "uint8",
+ "uint8",
+ ], [
+ V4PoolConstants.mintPositionActionValue,
+ V4PoolConstants.settlePairActionValue,
+ ])).called(1);
+ });
+
+ test(
+ "When calling `sendV4PoolDepositTransaction` the mint action params should be correctly encoded",
+ () async {
+ when(() => ethereumAbiCoder.encodePacked(any(), any())).thenReturn("0x");
+ when(() => ethereumAbiCoder.encode(any(), any())).thenReturn("0x");
+ when(
+ () => positionManagerV4Impl.modifyLiquidities(
+ unlockData: any(named: "unlockData"), deadline: any(named: "deadline"), ethValue: any(named: "ethValue")),
+ ).thenAnswer((_) async => transactionResponse);
+
+ const token0Address = "0x1";
+ const token1Address = "0x2";
+ const network = AppNetworks.mainnet;
+ final amount0Desired = BigInt.from(4311);
+ final amount1Desired = BigInt.from(1031900);
+ const deadline = Duration(days: 1);
+ final amount0Max = BigInt.from(4312);
+ final amount1Max = BigInt.from(1031901);
+ final recipient = await signer.address;
+ final tickLower = BigInt.from(321);
+ final tickUpper = BigInt.from(1222);
+ final currentPoolTick = BigInt.from(123);
+ final currentYield0 = currentYield.copyWith(
+ chainId: network.chainId,
+ token1: TokenDto.fixture().copyWith(addresses: {network.chainId: token1Address}),
+ token0: TokenDto.fixture().copyWith(addresses: {network.chainId: token0Address}),
+ );
+
+ await sut.sendV4PoolDepositTransaction(
+ currentYield0,
+ signer,
+ deadline: deadline,
+ tickLower: tickLower,
+ tickUpper: tickUpper,
+ amount0toDeposit: amount0Desired,
+ amount1ToDeposit: amount1Desired,
+ maxAmount0ToDeposit: amount0Max,
+ maxAmount1ToDeposit: amount1Max,
+ recipient: recipient,
+ currentPoolTick: currentPoolTick,
+ );
+
+ verify(() => ethereumAbiCoder.encode([
+ "tuple(address,address,int32,int24,address)",
+ "int24",
+ "int24",
+ "uint256",
+ "uint128",
+ "uint128",
+ "address",
+ "bytes"
+ ], [
+ [
+ token0Address,
+ token1Address,
+ BigInt.from(currentYield0.feeTier),
+ BigInt.from(currentYield0.tickSpacing),
+ currentYield0.v4Hooks,
+ ],
+ tickLower,
+ tickUpper,
+ _V4PoolLiquidityCalculationsMixinWrapper().getLiquidityForAmounts(
+ _V4PoolLiquidityCalculationsMixinWrapper().getSqrtPriceAtTick(currentPoolTick),
+ _V4PoolLiquidityCalculationsMixinWrapper().getSqrtPriceAtTick(tickLower),
+ _V4PoolLiquidityCalculationsMixinWrapper().getSqrtPriceAtTick(tickUpper),
+ amount0Desired,
+ amount1Desired,
+ ),
+ amount0Max,
+ amount1Max,
+ recipient,
+ EthereumConstants.emptyBytes,
+ ])).called(1);
+ },
+ );
+
+ test(
+ "When calling `sendV4PoolDepositTransaction` the settle pair action params should be correctly encoded",
+ () async {
+ when(() => ethereumAbiCoder.encodePacked(any(), any())).thenReturn("0x");
+ when(() => ethereumAbiCoder.encode(any(), any())).thenReturn("0x");
+ when(
+ () => positionManagerV4Impl.modifyLiquidities(
+ unlockData: any(named: "unlockData"), deadline: any(named: "deadline"), ethValue: any(named: "ethValue")),
+ ).thenAnswer((_) async => transactionResponse);
+
+ const token0Address = "0x1";
+ const token1Address = "0x2";
+ const network = AppNetworks.mainnet;
+ final amount0Desired = BigInt.from(4311);
+ final amount1Desired = BigInt.from(1031900);
+ const deadline = Duration(days: 1);
+ final amount0Max = BigInt.from(4312);
+ final amount1Max = BigInt.from(1031901);
+ final recipient = await signer.address;
+ final tickLower = BigInt.from(321);
+ final tickUpper = BigInt.from(1222);
+ final currentPoolTick = BigInt.from(123);
+ final currentYield0 = currentYield.copyWith(
+ chainId: network.chainId,
+ token1: TokenDto.fixture().copyWith(addresses: {network.chainId: token1Address}),
+ token0: TokenDto.fixture().copyWith(addresses: {network.chainId: token0Address}),
+ );
+
+ await sut.sendV4PoolDepositTransaction(
+ currentYield0,
+ signer,
+ deadline: deadline,
+ tickLower: tickLower,
+ tickUpper: tickUpper,
+ amount0toDeposit: amount0Desired,
+ amount1ToDeposit: amount1Desired,
+ maxAmount0ToDeposit: amount0Max,
+ maxAmount1ToDeposit: amount1Max,
+ recipient: recipient,
+ currentPoolTick: currentPoolTick,
+ );
+
+ verify(() => ethereumAbiCoder.encode(["address", "address"], [token0Address, token1Address])).called(1);
+ },
+ );
+
+ test(
+ "When calling `sendV4PoolDepositTransaction` and the token0 is native the sweep action params should be correctly encoded",
+ () async {
+ when(() => ethereumAbiCoder.encodePacked(any(), any())).thenReturn("0x");
+ when(() => ethereumAbiCoder.encode(any(), any())).thenReturn("0x");
+ when(
+ () => positionManagerV4Impl.modifyLiquidities(
+ unlockData: any(named: "unlockData"), deadline: any(named: "deadline"), ethValue: any(named: "ethValue")),
+ ).thenAnswer((_) async => transactionResponse);
+
+ const token0Address = EthereumConstants.zeroAddress;
+ const token1Address = "0x2";
+ const network = AppNetworks.mainnet;
+ final amount0Desired = BigInt.from(4311);
+ final amount1Desired = BigInt.from(1031900);
+ const deadline = Duration(days: 1);
+ final amount0Max = BigInt.from(4312);
+ final amount1Max = BigInt.from(1031901);
+ final recipient = await signer.address;
+ final tickLower = BigInt.from(321);
+ final tickUpper = BigInt.from(1222);
+ final currentPoolTick = BigInt.from(123);
+ final currentYield0 = currentYield.copyWith(
+ chainId: network.chainId,
+ token1: TokenDto.fixture().copyWith(addresses: {network.chainId: token1Address}),
+ token0: TokenDto.fixture().copyWith(addresses: {network.chainId: token0Address}),
+ );
+
+ await sut.sendV4PoolDepositTransaction(
+ currentYield0,
+ signer,
+ deadline: deadline,
+ tickLower: tickLower,
+ tickUpper: tickUpper,
+ amount0toDeposit: amount0Desired,
+ amount1ToDeposit: amount1Desired,
+ maxAmount0ToDeposit: amount0Max,
+ maxAmount1ToDeposit: amount1Max,
+ recipient: recipient,
+ currentPoolTick: currentPoolTick,
+ );
+
+ verify(
+ () => ethereumAbiCoder.encode(
+ ["address", "address"],
+ [EthereumConstants.zeroAddress, recipient],
+ ),
+ ).called(1);
+ },
+ );
+
+ test(
+ "When calling `sendV4PoolDepositTransaction` and the token1 is native the sweep action params should be correctly encoded",
+ () async {
+ when(() => ethereumAbiCoder.encodePacked(any(), any())).thenReturn("0x");
+ when(() => ethereumAbiCoder.encode(any(), any())).thenReturn("0x");
+ when(
+ () => positionManagerV4Impl.modifyLiquidities(
+ unlockData: any(named: "unlockData"), deadline: any(named: "deadline"), ethValue: any(named: "ethValue")),
+ ).thenAnswer((_) async => transactionResponse);
+
+ const token0Address = "0x2";
+ const token1Address = EthereumConstants.zeroAddress;
+ const network = AppNetworks.mainnet;
+ final amount0Desired = BigInt.from(4311);
+ final amount1Desired = BigInt.from(1031900);
+ const deadline = Duration(days: 1);
+ final amount0Max = BigInt.from(4312);
+ final amount1Max = BigInt.from(1031901);
+ final recipient = await signer.address;
+ final tickLower = BigInt.from(321);
+ final tickUpper = BigInt.from(1222);
+ final currentPoolTick = BigInt.from(123);
+ final currentYield0 = currentYield.copyWith(
+ chainId: network.chainId,
+ token0: TokenDto.fixture().copyWith(addresses: {network.chainId: token0Address}),
+ token1: TokenDto.fixture().copyWith(addresses: {network.chainId: token1Address}),
+ );
+
+ await sut.sendV4PoolDepositTransaction(
+ currentYield0,
+ signer,
+ deadline: deadline,
+ tickLower: tickLower,
+ tickUpper: tickUpper,
+ amount0toDeposit: amount0Desired,
+ amount1ToDeposit: amount1Desired,
+ maxAmount0ToDeposit: amount0Max,
+ maxAmount1ToDeposit: amount1Max,
+ recipient: recipient,
+ currentPoolTick: currentPoolTick,
+ );
+
+ verify(
+ () => ethereumAbiCoder.encode(
+ ["address", "address"],
+ [EthereumConstants.zeroAddress, recipient],
+ ),
+ ).called(1);
+ },
+ );
+
+ test(
+ """When calling `sendV4PoolDepositTransaction` and the token0 is native,
+ it should send the correct unlock data to the contract to add liquidity""",
+ () async {
+ const actionsEncoded = "0xhvaaa";
+ const mintPositionActionParamsEncoded = "0xaaaa";
+ const settlePairActionParamsEncoded = "0xbbbb";
+ const sweepActionParamsEncoded = "0xcccc";
+ const unlockData = "0xaaaaa77777AAA";
+
+ const token0Address = EthereumConstants.zeroAddress;
+ const token1Address = "0x2";
+ const network = AppNetworks.mainnet;
+ final amount0Desired = BigInt.from(4311);
+ final amount1Desired = BigInt.from(1031900);
+ const deadline = Duration(days: 1);
+ final amount0Max = BigInt.from(4312);
+ final amount1Max = BigInt.from(1031901);
+ final recipient = await signer.address;
+ final tickLower = BigInt.from(321);
+ final tickUpper = BigInt.from(1222);
+ final currentPoolTick = BigInt.from(123);
+ final currentYield0 = currentYield.copyWith(
+ chainId: network.chainId,
+ token0: TokenDto.fixture().copyWith(addresses: {network.chainId: token0Address}),
+ token1: TokenDto.fixture().copyWith(addresses: {network.chainId: token1Address}),
+ );
+
+ when(() => ethereumAbiCoder.encodePacked(["uint8", "uint8", "uint8"], any())).thenReturn(actionsEncoded);
+ when(() => ethereumAbiCoder.encode([
+ "tuple(address,address,int32,int24,address)",
+ "int24",
+ "int24",
+ "uint256",
+ "uint128",
+ "uint128",
+ "address",
+ "bytes"
+ ], any())).thenReturn(mintPositionActionParamsEncoded);
+ when(() => ethereumAbiCoder.encode(["address", "address"], [token0Address, token1Address]))
+ .thenReturn(settlePairActionParamsEncoded);
+
+ when(() => ethereumAbiCoder.encode(["address", "address"], [EthereumConstants.zeroAddress, recipient]))
+ .thenReturn(sweepActionParamsEncoded);
+
+ when(() => ethereumAbiCoder.encode([
+ "bytes",
+ "bytes[]"
+ ], [
+ actionsEncoded,
+ [mintPositionActionParamsEncoded, settlePairActionParamsEncoded, sweepActionParamsEncoded]
+ ])).thenReturn(unlockData);
+
+ when(
+ () => positionManagerV4Impl.modifyLiquidities(
+ unlockData: any(named: "unlockData"), deadline: any(named: "deadline"), ethValue: any(named: "ethValue")),
+ ).thenAnswer((_) async => transactionResponse);
+
+ await sut.sendV4PoolDepositTransaction(
+ currentYield0,
+ signer,
+ deadline: deadline,
+ tickLower: tickLower,
+ tickUpper: tickUpper,
+ amount0toDeposit: amount0Desired,
+ amount1ToDeposit: amount1Desired,
+ maxAmount0ToDeposit: amount0Max,
+ maxAmount1ToDeposit: amount1Max,
+ recipient: recipient,
+ currentPoolTick: currentPoolTick,
+ );
+
+ verify(
+ () => positionManagerV4Impl.modifyLiquidities(
+ unlockData: unlockData,
+ deadline: any(named: "deadline"),
+ ethValue: any(named: "ethValue"),
+ ),
+ ).called(1);
+ },
+ );
+
+ test(
+ """When calling `sendV4PoolDepositTransaction` and the token1 is native,
+ it should send the correct unlock data to the contract to add liquidity""",
+ () async {
+ const actionsEncoded = "0xhvaaa";
+ const mintPositionActionParamsEncoded = "0xaaaa";
+ const settlePairActionParamsEncoded = "0xbbbb";
+ const sweepActionParamsEncoded = "0xcccc";
+ const unlockData = "0xaaaaa77777AAA";
+
+ const token1Address = EthereumConstants.zeroAddress;
+ const token0Address = "0x2";
+ const network = AppNetworks.mainnet;
+ final amount0Desired = BigInt.from(4311);
+ final amount1Desired = BigInt.from(1031900);
+ const deadline = Duration(days: 1);
+ final amount0Max = BigInt.from(4312);
+ final amount1Max = BigInt.from(1031901);
+ final recipient = await signer.address;
+ final tickLower = BigInt.from(321);
+ final tickUpper = BigInt.from(1222);
+ final currentPoolTick = BigInt.from(123);
+ final currentYield0 = currentYield.copyWith(
+ chainId: network.chainId,
+ token0: TokenDto.fixture().copyWith(addresses: {network.chainId: token0Address}),
+ token1: TokenDto.fixture().copyWith(addresses: {network.chainId: token1Address}),
+ );
+
+ when(() => ethereumAbiCoder.encodePacked(["uint8", "uint8", "uint8"], any())).thenReturn(actionsEncoded);
+ when(() => ethereumAbiCoder.encode([
+ "tuple(address,address,int32,int24,address)",
+ "int24",
+ "int24",
+ "uint256",
+ "uint128",
+ "uint128",
+ "address",
+ "bytes"
+ ], any())).thenReturn(mintPositionActionParamsEncoded);
+ when(() => ethereumAbiCoder.encode(["address", "address"], [token0Address, token1Address]))
+ .thenReturn(settlePairActionParamsEncoded);
+
+ when(() => ethereumAbiCoder.encode(["address", "address"], [EthereumConstants.zeroAddress, recipient]))
+ .thenReturn(sweepActionParamsEncoded);
+
+ when(() => ethereumAbiCoder.encode([
+ "bytes",
+ "bytes[]"
+ ], [
+ actionsEncoded,
+ [mintPositionActionParamsEncoded, settlePairActionParamsEncoded, sweepActionParamsEncoded]
+ ])).thenReturn(unlockData);
+
+ when(
+ () => positionManagerV4Impl.modifyLiquidities(
+ unlockData: any(named: "unlockData"), deadline: any(named: "deadline"), ethValue: any(named: "ethValue")),
+ ).thenAnswer((_) async => transactionResponse);
+
+ await sut.sendV4PoolDepositTransaction(
+ currentYield0,
+ signer,
+ deadline: deadline,
+ tickLower: tickLower,
+ tickUpper: tickUpper,
+ amount0toDeposit: amount0Desired,
+ amount1ToDeposit: amount1Desired,
+ maxAmount0ToDeposit: amount0Max,
+ maxAmount1ToDeposit: amount1Max,
+ recipient: recipient,
+ currentPoolTick: currentPoolTick,
+ );
+
+ verify(
+ () => positionManagerV4Impl.modifyLiquidities(
+ unlockData: unlockData,
+ deadline: any(named: "deadline"),
+ ethValue: any(named: "ethValue"),
+ ),
+ ).called(1);
+ },
+ );
+
+ test(
+ """When calling `sendV4PoolDepositTransaction` and none of the tokens are native,
+ it should send the correct unlock data to the contract to add liquidity (without sweep)""",
+ () async {
+ const actionsEncoded = "0xhvaaa";
+ const mintPositionActionParamsEncoded = "0xaaaa";
+ const settlePairActionParamsEncoded = "0xbbbb";
+ const unlockData = "0xaaaaa77777AAA";
+
+ const token0Address = "0x1";
+ const token1Address = "0x2";
+ const network = AppNetworks.mainnet;
+ final amount0Desired = BigInt.from(4311);
+ final amount1Desired = BigInt.from(1031900);
+ const deadline = Duration(days: 1);
+ final amount0Max = BigInt.from(4312);
+ final amount1Max = BigInt.from(1031901);
+ final recipient = await signer.address;
+ final tickLower = BigInt.from(321);
+ final tickUpper = BigInt.from(1222);
+ final currentPoolTick = BigInt.from(123);
+ final currentYield0 = currentYield.copyWith(
+ chainId: network.chainId,
+ token0: TokenDto.fixture().copyWith(addresses: {network.chainId: token0Address}),
+ token1: TokenDto.fixture().copyWith(addresses: {network.chainId: token1Address}),
+ );
+
+ when(() => ethereumAbiCoder.encodePacked(["uint8", "uint8"], any())).thenReturn(actionsEncoded);
+ when(() => ethereumAbiCoder.encode([
+ "tuple(address,address,int32,int24,address)",
+ "int24",
+ "int24",
+ "uint256",
+ "uint128",
+ "uint128",
+ "address",
+ "bytes"
+ ], any())).thenReturn(mintPositionActionParamsEncoded);
+ when(() => ethereumAbiCoder.encode(["address", "address"], [token0Address, token1Address]))
+ .thenReturn(settlePairActionParamsEncoded);
+
+ when(() => ethereumAbiCoder.encode([
+ "bytes",
+ "bytes[]"
+ ], [
+ actionsEncoded,
+ [mintPositionActionParamsEncoded, settlePairActionParamsEncoded]
+ ])).thenReturn(unlockData);
+
+ when(
+ () => positionManagerV4Impl.modifyLiquidities(
+ unlockData: any(named: "unlockData"), deadline: any(named: "deadline"), ethValue: any(named: "ethValue")),
+ ).thenAnswer((_) async => transactionResponse);
+
+ await sut.sendV4PoolDepositTransaction(
+ currentYield0,
+ signer,
+ deadline: deadline,
+ tickLower: tickLower,
+ tickUpper: tickUpper,
+ amount0toDeposit: amount0Desired,
+ amount1ToDeposit: amount1Desired,
+ maxAmount0ToDeposit: amount0Max,
+ maxAmount1ToDeposit: amount1Max,
+ recipient: recipient,
+ currentPoolTick: currentPoolTick,
+ );
+
+ verify(
+ () => positionManagerV4Impl.modifyLiquidities(
+ unlockData: unlockData,
+ deadline: any(named: "deadline"),
+ ethValue: any(named: "ethValue"),
+ ),
+ ).called(1);
+ },
+ );
+
+ test(
+ """When calling `sendV4PoolDepositTransaction` it should send the correct deadline to the contract to add liquidity
+ (now + deadline)""",
+ () async {
+ withClock(Clock(() => DateTime(2022, 1, 1)), () async {
+ const token0Address = "0x1";
+ const token1Address = "0x2";
+ const network = AppNetworks.mainnet;
+ final amount0Desired = BigInt.from(4311);
+ final amount1Desired = BigInt.from(1031900);
+ const deadline = Duration(days: 1);
+ final amount0Max = BigInt.from(4312);
+ final amount1Max = BigInt.from(1031901);
+ final recipient = await signer.address;
+ final tickLower = BigInt.from(321);
+ final tickUpper = BigInt.from(1222);
+ final currentPoolTick = BigInt.from(123);
+ final currentYield0 = currentYield.copyWith(
+ chainId: network.chainId,
+ token0: TokenDto.fixture().copyWith(addresses: {network.chainId: token0Address}),
+ token1: TokenDto.fixture().copyWith(addresses: {network.chainId: token1Address}),
+ );
+
+ when(() => ethereumAbiCoder.encodePacked(any(), any())).thenReturn("0x");
+ when(() => ethereumAbiCoder.encode(any(), any())).thenReturn("0x");
+
+ when(
+ () => positionManagerV4Impl.modifyLiquidities(
+ unlockData: any(named: "unlockData"), deadline: any(named: "deadline"), ethValue: any(named: "ethValue")),
+ ).thenAnswer((_) async => transactionResponse);
+
+ await sut.sendV4PoolDepositTransaction(
+ currentYield0,
+ signer,
+ deadline: deadline,
+ tickLower: tickLower,
+ tickUpper: tickUpper,
+ amount0toDeposit: amount0Desired,
+ amount1ToDeposit: amount1Desired,
+ maxAmount0ToDeposit: amount0Max,
+ maxAmount1ToDeposit: amount1Max,
+ recipient: recipient,
+ currentPoolTick: currentPoolTick,
+ );
+
+ verify(
+ () => positionManagerV4Impl.modifyLiquidities(
+ unlockData: any(named: "unlockData"),
+ deadline: BigInt.from(clock.now().add(deadline).millisecondsSinceEpoch),
+ ethValue: any(named: "ethValue"),
+ ),
+ ).called(1);
+ });
+ },
+ );
+
+ test(
+ """When calling `sendV4PoolDepositTransaction` and none of the tokens are native, it should not send any eth value""",
+ () async {
+ const token0Address = "0x1";
+ const token1Address = "0x2";
+ const network = AppNetworks.mainnet;
+ final amount0Desired = BigInt.from(4311);
+ final amount1Desired = BigInt.from(1031900);
+ const deadline = Duration(days: 1);
+ final amount0Max = BigInt.from(4312);
+ final amount1Max = BigInt.from(1031901);
+ final recipient = await signer.address;
+ final tickLower = BigInt.from(321);
+ final tickUpper = BigInt.from(1222);
+ final currentPoolTick = BigInt.from(123);
+ final currentYield0 = currentYield.copyWith(
+ chainId: network.chainId,
+ token0: TokenDto.fixture().copyWith(addresses: {network.chainId: token0Address}),
+ token1: TokenDto.fixture().copyWith(addresses: {network.chainId: token1Address}),
+ );
+
+ when(() => ethereumAbiCoder.encodePacked(any(), any())).thenReturn("0x");
+ when(() => ethereumAbiCoder.encode(any(), any())).thenReturn("0x");
+
+ when(
+ () => positionManagerV4Impl.modifyLiquidities(
+ unlockData: any(named: "unlockData"), deadline: any(named: "deadline"), ethValue: any(named: "ethValue")),
+ ).thenAnswer((_) async => transactionResponse);
+
+ await sut.sendV4PoolDepositTransaction(
+ currentYield0,
+ signer,
+ deadline: deadline,
+ tickLower: tickLower,
+ tickUpper: tickUpper,
+ amount0toDeposit: amount0Desired,
+ amount1ToDeposit: amount1Desired,
+ maxAmount0ToDeposit: amount0Max,
+ maxAmount1ToDeposit: amount1Max,
+ recipient: recipient,
+ currentPoolTick: currentPoolTick,
+ );
+
+ verify(
+ () => positionManagerV4Impl.modifyLiquidities(
+ unlockData: any(named: "unlockData"),
+ deadline: any(named: "deadline"),
+ ethValue: null,
+ ),
+ ).called(1);
+ },
+ );
+
+ test(
+ """When calling `sendV4PoolDepositTransaction` and the token0 is native, it should send the eth value from the
+ token0amount""",
+ () async {
+ const token0Address = EthereumConstants.zeroAddress;
+ const token1Address = "0x2";
+ const network = AppNetworks.mainnet;
+ final amount0Desired = BigInt.from(4311);
+ final amount1Desired = BigInt.from(1031900);
+ const deadline = Duration(days: 1);
+ final amount0Max = BigInt.from(4312);
+ final amount1Max = BigInt.from(1031901);
+ final recipient = await signer.address;
+ final tickLower = BigInt.from(321);
+ final tickUpper = BigInt.from(1222);
+ final currentPoolTick = BigInt.from(123);
+ final currentYield0 = currentYield.copyWith(
+ chainId: network.chainId,
+ token0: TokenDto.fixture().copyWith(addresses: {network.chainId: token0Address}),
+ token1: TokenDto.fixture().copyWith(addresses: {network.chainId: token1Address}),
+ );
+
+ when(() => ethereumAbiCoder.encodePacked(any(), any())).thenReturn("0x");
+ when(() => ethereumAbiCoder.encode(any(), any())).thenReturn("0x");
+
+ when(
+ () => positionManagerV4Impl.modifyLiquidities(
+ unlockData: any(named: "unlockData"), deadline: any(named: "deadline"), ethValue: any(named: "ethValue")),
+ ).thenAnswer((_) async => transactionResponse);
+
+ await sut.sendV4PoolDepositTransaction(
+ currentYield0,
+ signer,
+ deadline: deadline,
+ tickLower: tickLower,
+ tickUpper: tickUpper,
+ amount0toDeposit: amount0Desired,
+ amount1ToDeposit: amount1Desired,
+ maxAmount0ToDeposit: amount0Max,
+ maxAmount1ToDeposit: amount1Max,
+ recipient: recipient,
+ currentPoolTick: currentPoolTick,
+ );
+
+ verify(
+ () => positionManagerV4Impl.modifyLiquidities(
+ unlockData: any(named: "unlockData"),
+ deadline: any(named: "deadline"),
+ ethValue: amount0Desired,
+ ),
+ ).called(1);
+ },
+ );
+
+ test(
+ """When calling `sendV4PoolDepositTransaction` and the token1 is native, it should send the eth value from the
+ token1amount""",
+ () async {
+ const token0Address = "0x1";
+ const token1Address = EthereumConstants.zeroAddress;
+ const network = AppNetworks.mainnet;
+ final amount0Desired = BigInt.from(4311);
+ final amount1Desired = BigInt.from(1031900);
+ const deadline = Duration(days: 1);
+ final amount0Max = BigInt.from(4312);
+ final amount1Max = BigInt.from(1031901);
+ final recipient = await signer.address;
+ final tickLower = BigInt.from(321);
+ final tickUpper = BigInt.from(1222);
+ final currentPoolTick = BigInt.from(123);
+ final currentYield0 = currentYield.copyWith(
+ chainId: network.chainId,
+ token0: TokenDto.fixture().copyWith(addresses: {network.chainId: token0Address}),
+ token1: TokenDto.fixture().copyWith(addresses: {network.chainId: token1Address}),
+ );
+
+ when(() => ethereumAbiCoder.encodePacked(any(), any())).thenReturn("0x");
+ when(() => ethereumAbiCoder.encode(any(), any())).thenReturn("0x");
+
+ when(
+ () => positionManagerV4Impl.modifyLiquidities(
+ unlockData: any(named: "unlockData"), deadline: any(named: "deadline"), ethValue: any(named: "ethValue")),
+ ).thenAnswer((_) async => transactionResponse);
+
+ await sut.sendV4PoolDepositTransaction(
+ currentYield0,
+ signer,
+ deadline: deadline,
+ tickLower: tickLower,
+ tickUpper: tickUpper,
+ amount0toDeposit: amount0Desired,
+ amount1ToDeposit: amount1Desired,
+ maxAmount0ToDeposit: amount0Max,
+ maxAmount1ToDeposit: amount1Max,
+ recipient: recipient,
+ currentPoolTick: currentPoolTick,
+ );
+
+ verify(
+ () => positionManagerV4Impl.modifyLiquidities(
+ unlockData: any(named: "unlockData"),
+ deadline: any(named: "deadline"),
+ ethValue: amount1Desired,
+ ),
+ ).called(1);
+ },
+ );
+}
diff --git a/test/core/repositories/tokens_repository_test.dart b/test/core/repositories/tokens_repository_test.dart
index beb937c..a6c8070 100644
--- a/test/core/repositories/tokens_repository_test.dart
+++ b/test/core/repositories/tokens_repository_test.dart
@@ -2,6 +2,7 @@ import 'package:dio/dio.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:zup_app/core/dtos/token_dto.dart';
+import 'package:zup_app/core/dtos/token_price_dto.dart';
import 'package:zup_app/core/enums/networks.dart';
import 'package:zup_app/core/repositories/tokens_repository.dart';
@@ -123,4 +124,45 @@ void main() {
"chainId": network.chainId,
})).called(1);
});
+
+ test("When calling `getTokenPrice` it should call the correct endpoint with correct params", () async {
+ const address = "0x123";
+ const network = AppNetworks.sepolia;
+
+ when(() => dio.get(any(), queryParameters: any(named: "queryParameters"))).thenAnswer(
+ (_) async => Response(
+ data: TokenPriceDto.fixture().toJson(),
+ statusCode: 200,
+ requestOptions: RequestOptions(),
+ ),
+ );
+
+ await sut.getTokenPrice(address, network);
+
+ verify(() => dio.get(
+ "/tokens/price",
+ queryParameters: {
+ "address": address,
+ "chainId": network.chainId,
+ },
+ )).called(1);
+ });
+
+ test("When calling `getTokenPrice` it should correctly parse the response", () async {
+ const address = "0x123";
+ const network = AppNetworks.sepolia;
+ final tokenPriceDto = TokenPriceDto.fixture();
+
+ when(() => dio.get(any(), queryParameters: any(named: "queryParameters"))).thenAnswer(
+ (_) async => Response(
+ data: tokenPriceDto.toJson(),
+ statusCode: 200,
+ requestOptions: RequestOptions(),
+ ),
+ );
+
+ final response = await sut.getTokenPrice(address, network);
+
+ expect(response, tokenPriceDto);
+ });
}
diff --git a/test/core/repositories/yield_repository_test.dart b/test/core/repositories/yield_repository_test.dart
index ec26e06..2962c19 100644
--- a/test/core/repositories/yield_repository_test.dart
+++ b/test/core/repositories/yield_repository_test.dart
@@ -1,6 +1,7 @@
import 'package:dio/dio.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
+import 'package:zup_app/core/dtos/pool_search_settings_dto.dart';
import 'package:zup_app/core/dtos/yields_dto.dart';
import 'package:zup_app/core/enums/networks.dart';
import 'package:zup_app/core/repositories/yield_repository.dart';
@@ -16,10 +17,10 @@ void main() {
sut = YieldRepository(dio);
});
- test("When calling `getYields` it should call the correct endpoint with the correct params", () async {
+ test("When calling `getSingleNetworkYield` it should call the correct endpoint with the correct params", () async {
final yields = YieldsDto.fixture();
- when(() => dio.get(any(), queryParameters: any(named: "queryParameters"))).thenAnswer(
+ when(() => dio.post(any(), queryParameters: any(named: "queryParameters"), data: any(named: "data"))).thenAnswer(
(_) async => Response(
data: {"bestYields": yields.toJson()},
statusCode: 200,
@@ -31,23 +32,109 @@ void main() {
const token1Address = "0x456";
const network = AppNetworks.sepolia;
const minTvlUsd = 1213;
+ final searchSettings = PoolSearchSettingsDto.fixture().copyWith(minLiquidityUSD: minTvlUsd);
await sut.getSingleNetworkYield(
token0Address: token0Address,
token1Address: token1Address,
network: network,
- minTvlUsd: minTvlUsd,
+ searchSettings: searchSettings,
);
- verify(() => dio.get("/pools/search/${network.chainId}",
- queryParameters: {"token0Address": token0Address, "token1Address": token1Address, "minTvlUsd": minTvlUsd}))
- .called(1);
+ verify(
+ () => dio.post("/pools/search/${network.chainId}", queryParameters: {
+ "token0Address": token0Address,
+ "token1Address": token1Address
+ }, data: {
+ "filters": {
+ "minTvlUsd": searchSettings.minLiquidityUSD,
+ "allowedPoolTypes": [
+ "V3",
+ "V4",
+ ],
+ }
+ }),
+ ).called(1);
+ });
+
+ test("When the V3 Pool is disabled in the search settings, it should not be included in the request", () async {
+ final yields = YieldsDto.fixture();
+
+ when(() => dio.post(any(), queryParameters: any(named: "queryParameters"), data: any(named: "data"))).thenAnswer(
+ (_) async => Response(
+ data: {"bestYields": yields.toJson()},
+ statusCode: 200,
+ requestOptions: RequestOptions(),
+ ),
+ );
+
+ const token0Address = "0x123";
+ const token1Address = "0x456";
+ const network = AppNetworks.sepolia;
+ final searchSettings = PoolSearchSettingsDto.fixture().copyWith(allowV3Search: false);
+
+ await sut.getSingleNetworkYield(
+ token0Address: token0Address,
+ token1Address: token1Address,
+ network: network,
+ searchSettings: searchSettings,
+ );
+
+ verify(
+ () => dio.post("/pools/search/${network.chainId}", queryParameters: {
+ "token0Address": token0Address,
+ "token1Address": token1Address
+ }, data: {
+ "filters": {
+ "minTvlUsd": searchSettings.minLiquidityUSD,
+ "allowedPoolTypes": [
+ "V4",
+ ],
+ }
+ }),
+ ).called(1);
+ });
+
+ test("When the V4 Pool is disabled in the search settings, it should not be included in the request", () async {
+ final yields = YieldsDto.fixture();
+
+ when(() => dio.post(any(), queryParameters: any(named: "queryParameters"), data: any(named: "data"))).thenAnswer(
+ (_) async => Response(
+ data: {"bestYields": yields.toJson()},
+ statusCode: 200,
+ requestOptions: RequestOptions(),
+ ),
+ );
+
+ const token0Address = "0x123";
+ const token1Address = "0x456";
+ const network = AppNetworks.sepolia;
+ final searchSettings = PoolSearchSettingsDto.fixture().copyWith(allowV4Search: false);
+
+ await sut.getSingleNetworkYield(
+ token0Address: token0Address,
+ token1Address: token1Address,
+ network: network,
+ searchSettings: searchSettings,
+ );
+
+ verify(
+ () => dio.post("/pools/search/${network.chainId}", queryParameters: {
+ "token0Address": token0Address,
+ "token1Address": token1Address
+ }, data: {
+ "filters": {
+ "minTvlUsd": searchSettings.minLiquidityUSD,
+ "allowedPoolTypes": ["V3"],
+ }
+ }),
+ ).called(1);
});
test("When calling `getYields` it should correctly parse the response", () async {
final yields = YieldsDto.fixture();
- when(() => dio.get(any(), queryParameters: any(named: "queryParameters"))).thenAnswer(
+ when(() => dio.post(any(), queryParameters: any(named: "queryParameters"), data: any(named: "data"))).thenAnswer(
(_) async => Response(
data: yields.toJson(),
statusCode: 200,
@@ -63,7 +150,7 @@ void main() {
token0Address: token0Address,
token1Address: token1Address,
network: network,
- minTvlUsd: 0,
+ searchSettings: PoolSearchSettingsDto.fixture().copyWith(minLiquidityUSD: 0),
);
expect(response, yields);
@@ -72,7 +159,90 @@ void main() {
test("when calling 'getAllNetworksYield' it should call the correct endpoint with the correct params", () async {
final yields = YieldsDto.fixture();
- when(() => dio.get(any(), queryParameters: any(named: "queryParameters"))).thenAnswer(
+ when(() => dio.post(any(), queryParameters: any(named: "queryParameters"), data: any(named: "data"))).thenAnswer(
+ (_) async => Response(
+ data: {"bestYields": yields.toJson()},
+ statusCode: 200,
+ requestOptions: RequestOptions(),
+ ),
+ );
+
+ const token0Id = "0x123";
+ const token1Id = "0x456";
+ const minTvlUsd = 1213;
+ final searchSettings = PoolSearchSettingsDto.fixture().copyWith(minLiquidityUSD: minTvlUsd);
+
+ await sut.getAllNetworksYield(
+ token0InternalId: token0Id,
+ token1InternalId: token1Id,
+ searchSettings: searchSettings,
+ testnetMode: true,
+ );
+
+ verify(
+ () => dio.post("/pools/search/all", queryParameters: {
+ "token0Id": token0Id,
+ "token1Id": token1Id,
+ }, data: {
+ "filters": {
+ "testnetMode": true,
+ "minTvlUsd": searchSettings.minLiquidityUSD,
+ "allowedPoolTypes": [
+ "V3",
+ "V4",
+ ],
+ }
+ }),
+ ).called(1);
+ });
+
+ test(
+ "when calling 'getAllNetworksYield' and the search settings has the v4 pool disallowed, it should not be included in the params",
+ () async {
+ final yields = YieldsDto.fixture();
+
+ when(() => dio.post(any(), queryParameters: any(named: "queryParameters"), data: any(named: "data"))).thenAnswer(
+ (_) async => Response(
+ data: {"bestYields": yields.toJson()},
+ statusCode: 200,
+ requestOptions: RequestOptions(),
+ ),
+ );
+
+ const token0Id = "0x123";
+ const token1Id = "0x456";
+ const minTvlUsd = 1213;
+ final searchSettings = PoolSearchSettingsDto.fixture().copyWith(minLiquidityUSD: minTvlUsd, allowV4Search: false);
+
+ await sut.getAllNetworksYield(
+ token0InternalId: token0Id,
+ token1InternalId: token1Id,
+ searchSettings: searchSettings,
+ testnetMode: true,
+ );
+
+ verify(
+ () => dio.post("/pools/search/all", queryParameters: {
+ "token0Id": token0Id,
+ "token1Id": token1Id,
+ }, data: {
+ "filters": {
+ "testnetMode": true,
+ "minTvlUsd": searchSettings.minLiquidityUSD,
+ "allowedPoolTypes": [
+ "V3",
+ ],
+ }
+ }),
+ ).called(1);
+ });
+
+ test(
+ "when calling 'getAllNetworksYield' and the search settings has the v3 pool disallowed, it should not be included in the params",
+ () async {
+ final yields = YieldsDto.fixture();
+
+ when(() => dio.post(any(), queryParameters: any(named: "queryParameters"), data: any(named: "data"))).thenAnswer(
(_) async => Response(
data: {"bestYields": yields.toJson()},
statusCode: 200,
@@ -83,23 +253,35 @@ void main() {
const token0Id = "0x123";
const token1Id = "0x456";
const minTvlUsd = 1213;
+ final searchSettings = PoolSearchSettingsDto.fixture().copyWith(minLiquidityUSD: minTvlUsd, allowV3Search: false);
await sut.getAllNetworksYield(
token0InternalId: token0Id,
token1InternalId: token1Id,
- minTvlUsd: minTvlUsd,
+ searchSettings: searchSettings,
testnetMode: true,
);
- verify(() => dio.get("/pools/search/all",
- queryParameters: {"token0Id": token0Id, "token1Id": token1Id, "testnetMode": true, "minTvlUsd": minTvlUsd}))
- .called(1);
+ verify(
+ () => dio.post("/pools/search/all", queryParameters: {
+ "token0Id": token0Id,
+ "token1Id": token1Id,
+ }, data: {
+ "filters": {
+ "testnetMode": true,
+ "minTvlUsd": searchSettings.minLiquidityUSD,
+ "allowedPoolTypes": [
+ "V4",
+ ],
+ }
+ }),
+ ).called(1);
});
test("when calling 'getAllNetworksYield' it should correctly parse the response", () async {
final yields = YieldsDto.fixture();
- when(() => dio.get(any(), queryParameters: any(named: "queryParameters"))).thenAnswer(
+ when(() => dio.post(any(), queryParameters: any(named: "queryParameters"), data: any(named: "data"))).thenAnswer(
(_) async => Response(
data: yields.toJson(),
statusCode: 200,
@@ -113,7 +295,7 @@ void main() {
final response = await sut.getAllNetworksYield(
token0InternalId: token0Id,
token1InternalId: token1Id,
- minTvlUsd: 0,
+ searchSettings: PoolSearchSettingsDto.fixture().copyWith(minLiquidityUSD: 0),
testnetMode: true,
);
diff --git a/test/core/slippage_test.dart b/test/core/slippage_test.dart
index e436db8..b373578 100644
--- a/test/core/slippage_test.dart
+++ b/test/core/slippage_test.dart
@@ -89,37 +89,68 @@ void main() {
expect(Slippage.zeroPointOnePercent.props, [Slippage.zeroPointOnePercent.value]);
});
- group("""`calculateTokenAmountFromSlippage` should calculate the token amount with the slippage applied.
+ group("""`calculateMinTokenAmountFromSlippage` should calculate the token amount with the slippage applied.
basicaly it's the amount - (x)%""", () {
test("(50% test case)", () {
expect(
- Slippage.fromValue(50).calculateTokenAmountFromSlippage(BigInt.from(1000000)),
+ Slippage.fromValue(50).calculateMinTokenAmountFromSlippage(BigInt.from(1000000)),
BigInt.from(500000),
);
});
test("(10% test case)", () {
expect(
- Slippage.fromValue(10).calculateTokenAmountFromSlippage(BigInt.from(1000000)),
+ Slippage.fromValue(10).calculateMinTokenAmountFromSlippage(BigInt.from(1000000)),
BigInt.from(900000),
);
});
test("(0% test case)", () {
expect(
- Slippage.fromValue(0).calculateTokenAmountFromSlippage(BigInt.from(1000000)),
+ Slippage.fromValue(0).calculateMinTokenAmountFromSlippage(BigInt.from(1000000)),
BigInt.from(1000000),
);
});
test("(100% test case)", () {
expect(
- Slippage.fromValue(100).calculateTokenAmountFromSlippage(BigInt.from(1000000)),
+ Slippage.fromValue(100).calculateMinTokenAmountFromSlippage(BigInt.from(1000000)),
BigInt.from(0),
);
});
});
+ group("""`calculateMaxTokenAmountFromSlippage` should calculate the token amount with the slippage applied.
+ basicaly it's the amount + (x)%""", () {
+ test("(50% test case)", () {
+ expect(
+ Slippage.fromValue(50).calculateMaxTokenAmountFromSlippage(BigInt.from(1000000)),
+ BigInt.from(1500000),
+ );
+ });
+
+ test("(10% test case)", () {
+ expect(
+ Slippage.fromValue(10).calculateMaxTokenAmountFromSlippage(BigInt.from(1000000)),
+ BigInt.from(1100000),
+ );
+ });
+
+ test("(0% test case)", () {
+ expect(
+ Slippage.fromValue(0).calculateMaxTokenAmountFromSlippage(BigInt.from(1000000)),
+ BigInt.from(1000000),
+ );
+ });
+
+ test("(100% test case)", () {
+ expect(
+ Slippage.fromValue(100).calculateMaxTokenAmountFromSlippage(BigInt.from(1000000)),
+ BigInt.from(2000000),
+ );
+ });
+ });
+
test("`valueBasisPoints` should return the slippage value in basis points", () {
expect(Slippage.zeroPointOnePercent.valueBasisPoints, 10, reason: "zeroPointOnePercent is 10 basis points");
expect(Slippage.halfPercent.valueBasisPoints, 50, reason: "halfPercent is 50 basis points");
diff --git a/test/core/v4_pool_constants_test.dart b/test/core/v4_pool_constants_test.dart
new file mode 100644
index 0000000..09e994b
--- /dev/null
+++ b/test/core/v4_pool_constants_test.dart
@@ -0,0 +1,16 @@
+import 'package:flutter_test/flutter_test.dart';
+import 'package:zup_app/core/v4_pool_constants.dart';
+
+void main() {
+ test('mintPositionActionValue should return the correct value', () {
+ expect(V4PoolConstants.mintPositionActionValue, 0x02);
+ });
+
+ test('settlePairActionValue should return the correct value', () {
+ expect(V4PoolConstants.settlePairActionValue, 0x0d);
+ });
+
+ test('sweepActionValue should return the correct value', () {
+ expect(V4PoolConstants.sweepActionValue, 0x14);
+ });
+}
diff --git a/test/mocks.dart b/test/mocks.dart
index 24163c9..f167e89 100644
--- a/test/mocks.dart
+++ b/test/mocks.dart
@@ -12,13 +12,17 @@ import 'package:url_launcher_platform_interface/url_launcher_platform_interface.
import 'package:web3kit/core/dtos/transaction_response.dart';
import 'package:web3kit/web3kit.dart';
import 'package:zup_app/abis/erc_20.abi.g.dart';
-import 'package:zup_app/abis/uniswap_position_manager.abi.g.dart';
+import 'package:zup_app/abis/uniswap_permit2.abi.g.dart';
import 'package:zup_app/abis/uniswap_v3_pool.abi.g.dart';
+import 'package:zup_app/abis/uniswap_v3_position_manager.abi.g.dart';
+import 'package:zup_app/abis/uniswap_v4_position_manager.abi.g.dart';
+import 'package:zup_app/abis/uniswap_v4_state_view.abi.g.dart';
import 'package:zup_app/app/app_cubit/app_cubit.dart';
import 'package:zup_app/app/create/deposit/deposit_cubit.dart';
import 'package:zup_app/app/create/deposit/widgets/preview_deposit_modal/preview_deposit_modal_cubit.dart';
import 'package:zup_app/core/cache.dart';
import 'package:zup_app/core/debouncer.dart';
+import 'package:zup_app/core/pool_service.dart';
import 'package:zup_app/core/repositories/positions_repository.dart';
import 'package:zup_app/core/repositories/tokens_repository.dart';
import 'package:zup_app/core/repositories/yield_repository.dart';
@@ -28,6 +32,7 @@ import 'package:zup_app/core/zup_navigator.dart';
import 'package:zup_app/gen/assets.gen.dart';
import 'package:zup_app/widgets/token_selector_modal/token_selector_modal_cubit.dart';
import 'package:zup_app/widgets/zup_cached_image.dart';
+import 'package:zup_core/zup_holder.dart';
import 'package:zup_core/zup_singleton_cache.dart';
class $AssetsLottiesGenMock extends Mock implements $AssetsLottiesGen {}
@@ -62,9 +67,25 @@ class TokensRepositoryMock extends Mock implements TokensRepository {}
class TransactionResponseMock extends Mock implements TransactionResponse {}
-class UniswapPositionManagerImplMock extends Mock implements UniswapPositionManagerImpl {}
+class UniswapV3PositionManagerImplMock extends Mock implements UniswapV3PositionManagerImpl {}
-class UniswapPositionManagerMock extends Mock implements UniswapPositionManager {}
+class EthereumAbiCoderMock extends Mock implements EthereumAbiCoder {}
+
+class UniswapV3PositionManagerMock extends Mock implements UniswapV3PositionManager {}
+
+class PoolServiceMock extends Mock implements PoolService {}
+
+class UniswapPermit2Mock extends Mock implements UniswapPermit2 {}
+
+class UniswapPermit2ImplMock extends Mock implements UniswapPermit2Impl {}
+
+class UniswapV4StateViewMock extends Mock implements UniswapV4StateView {}
+
+class UniswapV4StateViewImplMock extends Mock implements UniswapV4StateViewImpl {}
+
+class UniswapV4PositionManagerMock extends Mock implements UniswapV4PositionManager {}
+
+class UniswapV4PositionManagerImplMock extends Mock implements UniswapV4PositionManagerImpl {}
class UniswapV3PoolImplMock extends Mock implements UniswapV3PoolImpl {}
@@ -90,6 +111,8 @@ class ConfettiControllerMock extends Mock implements ConfettiController {}
class FirebaseAnalyticsMock extends Mock implements FirebaseAnalytics {}
+class ZupHolderMock extends Mock implements ZupHolder {}
+
class ChangeNotifierMock extends Mock with ChangeNotifier {
void notify() => notifyListeners();
}
diff --git a/web/index.html b/web/index.html
index e49f19e..a6bc3ec 100644
--- a/web/index.html
+++ b/web/index.html
@@ -61,6 +61,6 @@
inject();
-
+