diff --git a/assets/icons/chart.bar.svg b/assets/icons/chart.bar.svg
new file mode 100644
index 0000000..70d2aee
--- /dev/null
+++ b/assets/icons/chart.bar.svg
@@ -0,0 +1,13 @@
+
+
+
+
diff --git a/assets/icons/checkmark.circle.fill.svg b/assets/icons/checkmark.circle.fill.svg
new file mode 100644
index 0000000..edb9145
--- /dev/null
+++ b/assets/icons/checkmark.circle.fill.svg
@@ -0,0 +1,11 @@
+
+
+
+
diff --git a/assets/icons/document.on.document.svg b/assets/icons/document.on.document.svg
new file mode 100644
index 0000000..c0ddb1c
--- /dev/null
+++ b/assets/icons/document.on.document.svg
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/assets/logos/dexscreener.svg b/assets/logos/dexscreener.svg
new file mode 100644
index 0000000..f26fca9
--- /dev/null
+++ b/assets/logos/dexscreener.svg
@@ -0,0 +1,11 @@
+
diff --git a/lib/app/app_layout.dart b/lib/app/app_layout.dart
index b568aa8..96d6994 100644
--- a/lib/app/app_layout.dart
+++ b/lib/app/app_layout.dart
@@ -48,25 +48,24 @@ class _AppPageState extends State with DeviceInfoMixin {
if (cache.getCookiesConsentStatus() == null) Overlay.of(context).insert(overlayEntry);
- ScaffoldMessenger.of(context).showMaterialBanner(
- MaterialBanner(
- backgroundColor: ZupColors.orange6,
- padding: const EdgeInsets.only(left: 20, right: 10, bottom: 5, top: 5),
- dividerColor: Colors.transparent,
- content: const Text(
- "⚠️ 24h and 7d Yields on Base Network are temporarily delayed, we’re on it! 🚧 30d, and 90d Yields are still running fine.",
- style: TextStyle(color: ZupColors.orange),
- ),
- actions: [
- ZupIconButton(
- iconColor: ZupColors.orange,
- backgroundColor: ZupColors.orange5,
- icon: const Icon(Icons.close),
- onPressed: (context) => ScaffoldMessenger.of(context).clearMaterialBanners(),
- ),
- ],
- ),
- );
+ // ScaffoldMessenger.of(context).showMaterialBanner(
+ // MaterialBanner(
+ // backgroundColor: ZupColors.orange6,
+ // padding: const EdgeInsets.only(left: 20, right: 10, bottom: 5, top: 5),
+ // dividerColor: Colors.transparent,
+ // content: const Text(
+ // "⚠️ 24h Yields on Base Network are temporarily delayed, we’re on it! 🚧 7d, 30d, and 90d Yields are still running fine.",
+ // style: TextStyle(color: ZupColors.orange),
+ // ),
+ // actions: [
+ // ZupIconButton(
+ // backgroundColor: ZupColors.orange5,
+ // icon: const Icon(Icons.close, color: ZupColors.orange),
+ // onPressed: (context) => ScaffoldMessenger.of(context).clearMaterialBanners(),
+ // ),
+ // ],
+ // ),
+ // );
});
}
@@ -77,27 +76,29 @@ class _AppPageState extends State with DeviceInfoMixin {
backgroundColor: ZupThemeColors.background.themed(context.brightness),
bottomNavigationBar: shouldShowBottomNavigationBar ? const AppBottomNavigationBar() : null,
extendBody: shouldShowBottomNavigationBar,
- body: CustomScrollView(
+ body: PrimaryScrollController(
controller: appScrollController,
- shrinkWrap: true,
- physics: const ClampingScrollPhysics(),
- slivers: [
- SliverAppBar(
- clipBehavior: Clip.none,
- forceMaterialTransparency: true,
- pinned: true,
- titleSpacing: 0,
- title: AppHeader(height: appBarHeight),
- toolbarHeight: appBarHeight,
- ),
- const SliverFillRemaining(hasScrollBody: false, child: RouterOutlet(key: Key("screen"))),
- SliverToBoxAdapter(
- child: Padding(
- padding: EdgeInsets.only(bottom: shouldShowBottomNavigationBar ? AppBottomNavigationBar.height : 0),
- child: const AppFooter(),
+ child: CustomScrollView(
+ primary: true,
+ physics: const ClampingScrollPhysics(),
+ slivers: [
+ SliverAppBar(
+ clipBehavior: Clip.none,
+ forceMaterialTransparency: true,
+ pinned: true,
+ titleSpacing: 0,
+ title: AppHeader(height: appBarHeight),
+ toolbarHeight: appBarHeight,
),
- ),
- ],
+ const SliverFillRemaining(hasScrollBody: false, child: RouterOutlet(key: Key("screen"))),
+ SliverToBoxAdapter(
+ child: Padding(
+ padding: EdgeInsets.only(bottom: shouldShowBottomNavigationBar ? AppBottomNavigationBar.height : 0),
+ child: const AppFooter(),
+ ),
+ ),
+ ],
+ ),
),
),
);
diff --git a/lib/app/create/create_page_select_tokens_stage.dart b/lib/app/create/create_page_select_tokens_stage.dart
index 98f302d..c7e8587 100644
--- a/lib/app/create/create_page_select_tokens_stage.dart
+++ b/lib/app/create/create_page_select_tokens_stage.dart
@@ -6,7 +6,7 @@ import 'package:zup_app/app/app_cubit/app_cubit.dart';
import 'package:zup_app/app/create/widgets/create_page_settings_dropdown/create_page_settings_dropdown.dart';
import 'package:zup_app/app/create/widgets/exchanges_filter_dropdown_button/exchanges_filter_dropdown_button.dart';
import 'package:zup_app/core/cache.dart';
-import 'package:zup_app/core/dtos/token_dto.dart';
+import 'package:zup_app/core/dtos/multi_chain_token_dto.dart';
import 'package:zup_app/core/injections.dart';
import 'package:zup_app/core/zup_navigator.dart';
import 'package:zup_app/gen/assets.gen.dart';
@@ -35,7 +35,7 @@ class _CreatePageState extends State with DeviceInf
StreamSubscription? _token1SelectorStreamSubscription;
StreamSubscription? _selectedNetworkStreamSubscription;
- bool areTokensEqual(TokenDto? token0, TokenDto? token1) {
+ bool areTokensEqual(MultiChainTokenDto? token0, MultiChainTokenDto? token1) {
if (token0 == null || token1 == null) return false;
if (appCubit.selectedNetwork.isAllNetworks) return token0.internalId == token1.internalId;
diff --git a/lib/app/create/widgets/create_page_settings_dropdown/create_page_settings_dropdown.dart b/lib/app/create/widgets/create_page_settings_dropdown/create_page_settings_dropdown.dart
index 1f1aff3..eff91cf 100644
--- a/lib/app/create/widgets/create_page_settings_dropdown/create_page_settings_dropdown.dart
+++ b/lib/app/create/widgets/create_page_settings_dropdown/create_page_settings_dropdown.dart
@@ -163,65 +163,6 @@ class _CreatePageSettingsDropdownState extends State
)
: const SizedBox.shrink(),
),
- const SizedBox(height: 10),
- Row(
- children: [
- sectionTitle(S.of(context).createPageSettingsDropdownAllowedPoolTypes),
- const SizedBox(width: 8),
- ZupTooltip.text(
- 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: [
- Text(
- "V4",
- style: TextStyle(
- fontWeight: FontWeight.w500,
- fontSize: 15,
- color: ZupThemeColors.primaryText.themed(context.brightness),
- ),
- ),
- 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),
- Text(
- "V3",
- style: TextStyle(
- fontWeight: FontWeight.w500,
- color: ZupThemeColors.primaryText.themed(context.brightness),
- ),
- ),
- 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/app/create/widgets/exchanges_filter_dropdown_button/exchanges_filter_dropdown_button.dart b/lib/app/create/widgets/exchanges_filter_dropdown_button/exchanges_filter_dropdown_button.dart
index 8386ef6..9c4faf3 100644
--- a/lib/app/create/widgets/exchanges_filter_dropdown_button/exchanges_filter_dropdown_button.dart
+++ b/lib/app/create/widgets/exchanges_filter_dropdown_button/exchanges_filter_dropdown_button.dart
@@ -6,7 +6,6 @@ import 'package:zup_app/core/injections.dart';
import 'package:zup_app/core/repositories/protocol_repository.dart';
import 'package:zup_app/gen/assets.gen.dart';
import 'package:zup_app/l10n/gen/app_localizations.dart';
-import 'package:zup_app/widgets/zup_cached_image.dart';
import 'package:zup_core/extensions/extensions.dart';
import 'package:zup_core/zup_singleton_cache.dart';
import 'package:zup_ui_kit/zup_ui_kit.dart';
@@ -21,7 +20,7 @@ class ExchangesFilterDropdownButton extends StatefulWidget {
class _ExchangesFilterDropdownButtonState extends State {
final zupSingletonCache = inject();
final protocolRepository = inject();
- final zupCachedImage = inject();
+ final zupNetworkImage = inject();
final cache = inject();
ExchangesFilterDropdownButtonCubit? cubit;
@@ -114,7 +113,7 @@ class _ExchangesFilterDropdownButtonState extends State ZupCheckboxItem(
id: protocol.rawId,
title: protocol.name,
- icon: zupCachedImage.build(
+ icon: zupNetworkImage.load(
context,
protocol.logo,
radius: 20,
diff --git a/lib/app/create/yields/[id]/deposit/deposit_cubit.dart b/lib/app/create/yields/[id]/deposit/deposit_cubit.dart
index 63aedab..84b51b6 100644
--- a/lib/app/create/yields/[id]/deposit/deposit_cubit.dart
+++ b/lib/app/create/yields/[id]/deposit/deposit_cubit.dart
@@ -7,8 +7,8 @@ import 'package:zup_app/core/cache.dart';
import 'package:zup_app/core/concentrated_liquidity_utils/cl_pool_conversors_mixin.dart';
import 'package:zup_app/core/dtos/deposit_page_arguments_dto.dart';
import 'package:zup_app/core/dtos/deposit_settings_dto.dart';
+import 'package:zup_app/core/dtos/liquidity_pool_dto.dart';
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/enums/networks.dart';
import 'package:zup_app/core/mixins/keys_mixin.dart';
import 'package:zup_app/core/pool_service.dart';
@@ -57,7 +57,7 @@ class DepositCubit extends Cubit with KeysMixin, CLPoolConversorsM
final Duration _poolSqrtPriceX96CacheExpiration = const Duration(seconds: 30);
BigInt? _latestPoolSqrtPriceX96;
- YieldDto? yieldPool;
+ LiquidityPoolDto? yieldPool;
late final Stream poolSqrtPriceX96Stream = _poolSqrtPriceX96StreamController.stream;
diff --git a/lib/app/create/yields/[id]/deposit/deposit_page.dart b/lib/app/create/yields/[id]/deposit/deposit_page.dart
index da047e3..0332f1c 100644
--- a/lib/app/create/yields/[id]/deposit/deposit_page.dart
+++ b/lib/app/create/yields/[id]/deposit/deposit_page.dart
@@ -16,10 +16,10 @@ import 'package:zup_app/core/concentrated_liquidity_utils/cl_pool_conversors_mix
import 'package:zup_app/core/concentrated_liquidity_utils/cl_pool_liquidity_calculations_mixin.dart';
import 'package:zup_app/core/concentrated_liquidity_utils/cl_sqrt_price_math_mixin.dart';
import 'package:zup_app/core/dtos/deposit_settings_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/dtos/single_chain_token_dto.dart';
+import 'package:zup_app/core/dtos/liquidity_pool_dto.dart';
import 'package:zup_app/core/enums/networks.dart';
-import 'package:zup_app/core/enums/yield_timeframe.dart';
+import 'package:zup_app/core/enums/pool_data_timeframe.dart';
import 'package:zup_app/core/extensions/num_extension.dart';
import 'package:zup_app/core/extensions/string_extension.dart';
import 'package:zup_app/core/extensions/widget_extension.dart';
@@ -32,6 +32,7 @@ import 'package:zup_app/core/zup_navigator.dart';
import 'package:zup_app/core/zup_route_params_names.dart';
import 'package:zup_app/gen/assets.gen.dart';
import 'package:zup_app/l10n/gen/app_localizations.dart';
+import 'package:zup_app/widgets/pool_info_modal/pool_info_modal.dart';
import 'package:zup_app/widgets/yield_card.dart';
import 'package:zup_app/widgets/zup_skeletonizer.dart';
import 'package:zup_core/zup_core.dart';
@@ -86,14 +87,15 @@ class _DepositPageState extends State
AppNetworks get networkFromUrl =>
AppNetworks.fromValue(_navigator.getQueryParam(DepositRouteParamsNames().network)!) ?? AppNetworks.mainnet;
- YieldTimeFrame get yieldTimeFrameFromUrl =>
- YieldTimeFrame.fromValue(_navigator.getQueryParam(DepositRouteParamsNames().timeframe)!) ?? YieldTimeFrame.day;
+ PoolDataTimeframe get yieldTimeFrameFromUrl =>
+ PoolDataTimeframe.fromValue(_navigator.getQueryParam(DepositRouteParamsNames().timeframe)!) ??
+ PoolDataTimeframe.day;
- TokenDto get baseToken {
+ SingleChainTokenDto get baseToken {
return areTokensReversed ? _cubit.yieldPool!.token1 : _cubit.yieldPool!.token0;
}
- TokenDto get quoteToken {
+ SingleChainTokenDto get quoteToken {
return areTokensReversed ? _cubit.yieldPool!.token0 : _cubit.yieldPool!.token1;
}
@@ -108,7 +110,7 @@ class _DepositPageState extends State
RangeController minRangeController = RangeController();
RangeController maxRangeController = RangeController();
StreamSubscription? _poolSqrtPriceX96StreamSubscription;
- YieldTimeFrame yieldPoolTimeFrame = YieldTimeFrame.day;
+ PoolDataTimeframe yieldPoolTimeFrame = PoolDataTimeframe.day;
late Slippage selectedSlippage = _cubit.depositSettings.slippage;
late Duration selectedDeadline = _cubit.depositSettings.deadline;
@@ -129,8 +131,8 @@ class _DepositPageState extends State
final price = sqrtPriceX96ToPrice(
sqrtPriceX96: _cubit.latestPoolSqrtPriceX96!,
- poolToken0Decimals: _cubit.yieldPool!.token0NetworkDecimals,
- poolToken1Decimals: _cubit.yieldPool!.token1NetworkDecimals,
+ poolToken0Decimals: _cubit.yieldPool!.token0.decimals,
+ poolToken1Decimals: _cubit.yieldPool!.token1.decimals,
);
return areTokensReversed ? price.token1PerToken0 : price.token0PerToken1;
@@ -205,14 +207,14 @@ class _DepositPageState extends State
final maxTickPrice = tickToPrice(
tick: CLPoolConstants.maxTick,
- poolToken0Decimals: _cubit.yieldPool!.token0NetworkDecimals,
- poolToken1Decimals: _cubit.yieldPool!.token1NetworkDecimals,
+ poolToken0Decimals: _cubit.yieldPool!.token0.decimals,
+ poolToken1Decimals: _cubit.yieldPool!.token1.decimals,
);
final minTickPrice = tickToPrice(
tick: CLPoolConstants.minTick,
- poolToken0Decimals: _cubit.yieldPool!.token0NetworkDecimals,
- poolToken1Decimals: _cubit.yieldPool!.token1NetworkDecimals,
+ poolToken0Decimals: _cubit.yieldPool!.token0.decimals,
+ poolToken1Decimals: _cubit.yieldPool!.token1.decimals,
);
double getMinPrice() {
@@ -234,7 +236,7 @@ class _DepositPageState extends State
priceUpper: getMaxPrice(),
tokenXAmount: double.tryParse(baseTokenAmountController.text) ?? 0,
).toString(),
- )?.toStringAsFixed(quoteToken.decimals[_cubit.yieldPool!.network.chainId]!);
+ )?.toStringAsFixed(quoteToken.decimals);
final newBaseTokenAmount = Decimal.tryParse(
calculateToken0AmountFromToken1(
@@ -243,7 +245,7 @@ class _DepositPageState extends State
priceUpper: getMaxPrice(),
tokenYAmount: double.tryParse(quoteTokenAmountController.text) ?? 0,
).toString(),
- )?.toStringAsFixed(baseToken.decimals[_cubit.yieldPool!.network.chainId]!);
+ )?.toStringAsFixed(baseToken.decimals);
if (isBaseTokenAmountUserInput) {
if (newQuoteTokenAmount?.isEmptyOrZero ?? true) return quoteTokenAmountController.clear();
@@ -258,12 +260,12 @@ class _DepositPageState extends State
Future<({String title, Widget? icon, Function()? onPressed})> depositButtonState() async {
final userWalletBaseTokenAmount = await _cubit.getWalletTokenAmount(
- baseToken.addresses[_cubit.yieldPool!.network.chainId]!,
+ baseToken.address,
network: _cubit.yieldPool!.network,
);
final userWalletQuoteTokenAmount = await _cubit.getWalletTokenAmount(
- quoteToken.addresses[_cubit.yieldPool!.network.chainId]!,
+ quoteToken.address,
network: _cubit.yieldPool!.network,
);
@@ -398,7 +400,7 @@ class _DepositPageState extends State
? S
.of(context)
.depositPagePercentSlippage(
- valuePercent: selectedSlippage.value.formatPercent,
+ valuePercent: selectedSlippage.value.formatRoundingPercent,
)
: null,
onPressed: (buttonContext) => ZupPopover.show(
@@ -494,8 +496,8 @@ class _DepositPageState extends State
children: [
ZupSkeletonizer(
child: YieldCard(
- yieldPool: YieldDto.fixture().copyWith(chainId: networkFromUrl.chainId),
- yieldTimeFrame: YieldTimeFrame.day,
+ yieldPool: LiquidityPoolDto.fixture().copyWith(chainId: networkFromUrl.chainId),
+ yieldTimeFrame: PoolDataTimeframe.day,
showHotestYieldAnimation: false,
),
).animate(
@@ -523,19 +525,25 @@ class _DepositPageState extends State
);
}
- Widget _buildYieldCard(YieldDto yieldPool) => YieldCard(
+ Widget _buildYieldCard(LiquidityPoolDto yieldPool) => YieldCard(
yieldPool: yieldPool,
yieldTimeFrame: yieldTimeFrameFromUrl,
expandWidth: shouldYieldCardBeInColumn,
showHotestYieldAnimation: false,
showTimeframe: !_navigator.canBack(context),
- mainButton: const ZupPrimaryButton(
- title: "Pool Stats (Soon)",
+ mainButton: ZupPrimaryButton(
+ title: "Pool Stats",
fixedIcon: true,
- isTrailingIcon: true,
- onPressed: null,
- backgroundColor: ZupColors.gray6,
+ isTrailingIcon: false,
+ onPressed: (_) => PoolInfoModal.show(
+ context,
+ showAsBottomSheet: isMobileSize(context),
+ liquidityPool: yieldPool,
+ selectedTimeframe: yieldTimeFrameFromUrl,
+ ),
+ backgroundColor: ZupThemeColors.tertiaryButtonBackground.themed(context.brightness),
foregroundColor: ZupColors.brand,
+ icon: Assets.icons.chartBar.svg(height: 13, width: 13),
hoverElevation: 0,
),
);
@@ -605,7 +613,7 @@ class _DepositPageState extends State
builder: (context, poolSqrtPriceX96Snaphot) {
return Text(
"1 ${baseToken.symbol} ≈ ${() {
- final currentPrice = sqrtPriceX96ToPrice(sqrtPriceX96: poolSqrtPriceX96Snaphot.data ?? BigInt.zero, poolToken0Decimals: _cubit.yieldPool!.token0NetworkDecimals, poolToken1Decimals: _cubit.yieldPool!.token1NetworkDecimals);
+ final currentPrice = sqrtPriceX96ToPrice(sqrtPriceX96: poolSqrtPriceX96Snaphot.data ?? BigInt.zero, poolToken0Decimals: _cubit.yieldPool!.token0.decimals, poolToken1Decimals: _cubit.yieldPool!.token1.decimals);
return areTokensReversed ? currentPrice.token1PerToken0 : currentPrice.token0PerToken1;
}.call().formatCurrency(useLessThan: true, maxDecimals: 4, isUSD: false)} ${quoteToken.symbol}",
@@ -685,8 +693,8 @@ class _DepositPageState extends State
});
},
initialPrice: minPrice,
- poolToken0Decimals: _cubit.yieldPool!.token0NetworkDecimals,
- poolToken1Decimals: _cubit.yieldPool!.token1NetworkDecimals,
+ poolToken0Decimals: _cubit.yieldPool!.token0.decimals,
+ poolToken1Decimals: _cubit.yieldPool!.token1.decimals,
isReversed: areTokensReversed,
displayBaseTokenSymbol: baseToken.symbol,
displayQuoteTokenSymbol: quoteToken.symbol,
@@ -733,8 +741,8 @@ class _DepositPageState extends State
type: RangeSelectorType.maxPrice,
isInfinity: isMaxRangeInfinity,
initialPrice: maxPrice,
- poolToken0Decimals: _cubit.yieldPool!.token0NetworkDecimals,
- poolToken1Decimals: _cubit.yieldPool!.token1NetworkDecimals,
+ poolToken0Decimals: _cubit.yieldPool!.token0.decimals,
+ poolToken1Decimals: _cubit.yieldPool!.token1.decimals,
isReversed: areTokensReversed,
tickSpacing: _cubit.yieldPool!.tickSpacing,
rangeController: maxRangeController,
@@ -780,9 +788,7 @@ class _DepositPageState extends State
TokenAmountInputCard(
key: const Key("base-token-input-card"),
token: baseToken,
- isNative: baseToken.addresses[_cubit.yieldPool!.network.chainId]!.lowercasedEquals(
- EthereumConstants.zeroAddress,
- ),
+ isNative: baseToken.address.lowercasedEquals(EthereumConstants.zeroAddress),
onRefreshBalance: () => setState(() {}),
disabledText: () {
if (!isBaseTokenNeeded) {
@@ -809,9 +815,7 @@ class _DepositPageState extends State
TokenAmountInputCard(
key: const Key("quote-token-input-card"),
token: quoteToken,
- isNative: quoteToken.addresses[_cubit.yieldPool!.network.chainId]!.lowercasedEquals(
- EthereumConstants.zeroAddress,
- ),
+ isNative: quoteToken.address.lowercasedEquals(EthereumConstants.zeroAddress),
onRefreshBalance: () => setState(() {}),
disabledText: () {
if (!isQuoteTokenNeeded) {
diff --git a/lib/app/create/yields/[id]/deposit/deposit_state.dart b/lib/app/create/yields/[id]/deposit/deposit_state.dart
index 6accdbd..792d102 100644
--- a/lib/app/create/yields/[id]/deposit/deposit_state.dart
+++ b/lib/app/create/yields/[id]/deposit/deposit_state.dart
@@ -4,6 +4,6 @@ part of 'deposit_cubit.dart';
class DepositState with _$DepositState {
const factory DepositState.initial() = _Initial;
const factory DepositState.loading() = _Loading;
- const factory DepositState.success(YieldDto yieldPool) = _Success;
+ const factory DepositState.success(LiquidityPoolDto yieldPool) = _Success;
const factory DepositState.error() = _Error;
}
diff --git a/lib/app/create/yields/[id]/deposit/widgets/deposit_success_modal.dart b/lib/app/create/yields/[id]/deposit/widgets/deposit_success_modal.dart
index bccb52f..7fc1244 100644
--- a/lib/app/create/yields/[id]/deposit/widgets/deposit_success_modal.dart
+++ b/lib/app/create/yields/[id]/deposit/widgets/deposit_success_modal.dart
@@ -1,23 +1,21 @@
import 'package:confetti/confetti.dart';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
-import 'package:zup_app/core/dtos/yield_dto.dart';
+import 'package:zup_app/core/dtos/liquidity_pool_dto.dart';
import 'package:zup_app/core/injections.dart';
import 'package:zup_app/gen/assets.gen.dart';
import 'package:zup_app/l10n/gen/app_localizations.dart';
-import 'package:zup_app/widgets/token_avatar.dart';
-import 'package:zup_app/widgets/zup_cached_image.dart';
import 'package:zup_core/zup_core.dart';
import 'package:zup_ui_kit/zup_ui_kit.dart';
class DepositSuccessModal extends StatefulWidget {
const DepositSuccessModal({super.key, required this.depositedYield});
- final YieldDto depositedYield;
+ final LiquidityPoolDto depositedYield;
static Future show(
BuildContext context, {
- required YieldDto depositedYield,
+ required LiquidityPoolDto depositedYield,
required showAsBottomSheet,
}) async => ZupModal.show(
context,
@@ -33,7 +31,7 @@ class DepositSuccessModal extends StatefulWidget {
}
class _DepositSuccessModalState extends State {
- final zupCachedImage = inject();
+ final zupNetworkImage = inject();
final confettiController = inject(instanceName: InjectInstanceNames.confettiController10s);
@override
@@ -58,8 +56,18 @@ class _DepositSuccessModalState extends State {
mainAxisAlignment: MainAxisAlignment.center,
children: [
ZupMergedWidgets(
- firstWidget: TokenAvatar(asset: widget.depositedYield.token0, size: 70),
- secondWidget: TokenAvatar(asset: widget.depositedYield.token1, size: 70),
+ firstWidget: ZupRemoteAvatar(
+ zupNetworkImage: zupNetworkImage,
+ avatarUrl: widget.depositedYield.token0.logoUrl,
+ errorPlaceholder: widget.depositedYield.token0.name[0],
+ size: 70,
+ ),
+ secondWidget: ZupRemoteAvatar(
+ zupNetworkImage: zupNetworkImage,
+ avatarUrl: widget.depositedYield.token1.logoUrl,
+ errorPlaceholder: widget.depositedYield.token1.name[0],
+ size: 70,
+ ),
spacing: 0,
),
const SizedBox(width: 20),
@@ -79,7 +87,7 @@ class _DepositSuccessModalState extends State {
particleDrag: 0.03,
),
const SizedBox(width: 20),
- zupCachedImage.build(context, widget.depositedYield.protocol.logo, radius: 50, height: 70, width: 70),
+ zupNetworkImage.load(context, widget.depositedYield.protocol.logo, radius: 50, height: 70, width: 70),
],
),
const SizedBox(height: 30),
diff --git a/lib/app/create/yields/[id]/deposit/widgets/preview_deposit_modal/preview_deposit_modal.dart b/lib/app/create/yields/[id]/deposit/widgets/preview_deposit_modal/preview_deposit_modal.dart
index cbf576e..c9fb67c 100644
--- a/lib/app/create/yields/[id]/deposit/widgets/preview_deposit_modal/preview_deposit_modal.dart
+++ b/lib/app/create/yields/[id]/deposit/widgets/preview_deposit_modal/preview_deposit_modal.dart
@@ -12,9 +12,9 @@ import 'package:zup_app/app/create/yields/%5Bid%5D/deposit/widgets/preview_depos
import 'package:zup_app/core/concentrated_liquidity_utils/cl_pool_constants.dart';
import 'package:zup_app/core/concentrated_liquidity_utils/cl_pool_conversors_mixin.dart';
import 'package:zup_app/core/concentrated_liquidity_utils/cl_sqrt_price_math_mixin.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/yield_timeframe.dart';
+import 'package:zup_app/core/dtos/liquidity_pool_dto.dart';
+import 'package:zup_app/core/dtos/single_chain_token_dto.dart';
+import 'package:zup_app/core/enums/pool_data_timeframe.dart';
import 'package:zup_app/core/extensions/num_extension.dart';
import 'package:zup_app/core/injections.dart';
import 'package:zup_app/core/pool_service.dart';
@@ -24,8 +24,6 @@ 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';
-import 'package:zup_app/widgets/token_avatar.dart';
-import 'package:zup_app/widgets/zup_cached_image.dart';
import 'package:zup_core/extensions/text_edititing_controller_extension.dart';
import 'package:zup_core/zup_core.dart';
import 'package:zup_ui_kit/zup_ui_kit.dart';
@@ -44,8 +42,8 @@ class PreviewDepositModal extends StatefulWidget with DeviceInfoMixin {
required this.yieldTimeFrame,
});
- final YieldDto currentYield;
- final YieldTimeFrame yieldTimeFrame;
+ final LiquidityPoolDto currentYield;
+ final PoolDataTimeframe yieldTimeFrame;
final bool isReversed;
final ({double price, bool isInfinity}) minPrice;
final ({double price, bool isInfinity}) maxPrice;
@@ -96,7 +94,7 @@ class PreviewDepositModal extends StatefulWidget with DeviceInfoMixin {
class _PreviewDepositModalState extends State
with CLPoolConversorsMixin, CLSqrtPriceMath, DeviceInfoMixin {
- final zupCachedImage = inject();
+ final zupNetworkImage = inject();
final navigator = inject();
final zupLinks = inject();
@@ -108,7 +106,7 @@ class _PreviewDepositModalState extends State
WidgetsBinding.instance.addPostFrameCallback((_) => setState(() {}));
};
- TokenDto get baseToken {
+ SingleChainTokenDto get baseToken {
if (isReversedLocal) {
return widget.currentYield.token1;
}
@@ -116,7 +114,7 @@ class _PreviewDepositModalState extends State
return widget.currentYield.token0;
}
- TokenDto get quoteToken {
+ SingleChainTokenDto get quoteToken {
if (isReversedLocal) {
return widget.currentYield.token0;
}
@@ -135,18 +133,18 @@ class _PreviewDepositModalState extends State
: widget.token1DepositAmountController.parseTextToDoubleOrZero;
BigInt get token0DepositAmount => widget.token0DepositAmountController.parseTextToDoubleOrZero.parseTokenAmount(
- decimals: widget.currentYield.token0NetworkDecimals,
+ decimals: widget.currentYield.token0.decimals,
);
BigInt get token1DepositAmount => widget.token1DepositAmountController.parseTextToDoubleOrZero.parseTokenAmount(
- decimals: widget.currentYield.token1NetworkDecimals,
+ decimals: widget.currentYield.token1.decimals,
);
double get currentPrice {
final price = sqrtPriceX96ToPrice(
sqrtPriceX96: cubit.latestPriceX96,
- poolToken0Decimals: widget.currentYield.token0NetworkDecimals,
- poolToken1Decimals: widget.currentYield.token1NetworkDecimals,
+ poolToken0Decimals: widget.currentYield.token0.decimals,
+ poolToken1Decimals: widget.currentYield.token1.decimals,
);
return isReversedLocal ? price.token1PerToken0 : price.token0PerToken1;
@@ -158,16 +156,16 @@ class _PreviewDepositModalState extends State
return priceToTick(
price: (widget.isReversed == !isReversedLocal) ? widget.maxPrice.price : widget.minPrice.price,
- poolToken0Decimals: widget.currentYield.token0NetworkDecimals,
- poolToken1Decimals: widget.currentYield.token1NetworkDecimals,
+ poolToken0Decimals: widget.currentYield.token0.decimals,
+ poolToken1Decimals: widget.currentYield.token1.decimals,
isReversed: widget.isReversed,
);
}
({double priceAsBaseToken, double priceAsQuoteToken}) price() => tickToPrice(
tick: tick(),
- poolToken0Decimals: widget.currentYield.token0NetworkDecimals,
- poolToken1Decimals: widget.currentYield.token1NetworkDecimals,
+ poolToken0Decimals: widget.currentYield.token0.decimals,
+ poolToken1Decimals: widget.currentYield.token1.decimals,
);
return isReversedLocal ? price().priceAsQuoteToken : price().priceAsBaseToken;
@@ -179,16 +177,16 @@ class _PreviewDepositModalState extends State
return priceToTick(
price: (widget.isReversed == !isReversedLocal) ? widget.minPrice.price : widget.maxPrice.price,
- poolToken0Decimals: widget.currentYield.token0NetworkDecimals,
- poolToken1Decimals: widget.currentYield.token1NetworkDecimals,
+ poolToken0Decimals: widget.currentYield.token0.decimals,
+ poolToken1Decimals: widget.currentYield.token1.decimals,
isReversed: widget.isReversed,
);
}
({double priceAsBaseToken, double priceAsQuoteToken}) price() => tickToPrice(
tick: tick(),
- poolToken0Decimals: widget.currentYield.token0NetworkDecimals,
- poolToken1Decimals: widget.currentYield.token1NetworkDecimals,
+ poolToken0Decimals: widget.currentYield.token0.decimals,
+ poolToken1Decimals: widget.currentYield.token1.decimals,
);
return isReversedLocal ? price().priceAsQuoteToken : price().priceAsBaseToken;
@@ -405,8 +403,17 @@ class _PreviewDepositModalState extends State
children: [
ZupMergedWidgets(
spacing: 0,
- firstWidget: TokenAvatar(asset: baseToken, size: 35),
- secondWidget: TokenAvatar(asset: quoteToken, size: 35),
+ firstWidget: ZupRemoteAvatar(
+ zupNetworkImage: zupNetworkImage,
+ avatarUrl: baseToken.logoUrl,
+ errorPlaceholder: baseToken.name[0],
+ size: 35,
+ ),
+ secondWidget: ZupRemoteAvatar(
+ avatarUrl: quoteToken.logoUrl,
+ errorPlaceholder: quoteToken.name[0],
+ size: 35,
+ ),
),
const SizedBox(width: 5),
Text(
@@ -417,7 +424,7 @@ class _PreviewDepositModalState extends State
StreamBuilder(
stream: cubit.poolSqrtPriceX96Stream,
builder: (context, _) {
- return ZupTag(
+ return ZupOutlinedTag(
title: isOutOfRange.any
? S.of(context).previewDepositModalOutOfRange
: S.of(context).previewDepositModalInRange,
@@ -468,7 +475,7 @@ class _PreviewDepositModalState extends State
const SizedBox(height: 10),
Row(
children: [
- zupCachedImage.build(
+ zupNetworkImage.load(
context,
baseToken.logoUrl,
height: 30,
@@ -496,7 +503,7 @@ class _PreviewDepositModalState extends State
const SizedBox(height: 15),
Row(
children: [
- zupCachedImage.build(
+ zupNetworkImage.load(
context,
quoteToken.logoUrl,
height: 30,
@@ -532,7 +539,7 @@ class _PreviewDepositModalState extends State
fit: FlexFit.loose,
child: _fieldColumn(
title: S.of(context).previewDepositModalProtocol,
- image: zupCachedImage.build(
+ image: zupNetworkImage.load(
context,
widget.currentYield.protocol.logo,
width: 30,
@@ -572,7 +579,7 @@ class _PreviewDepositModalState extends State
spacing: 0,
title:
"${S.of(context).previewDepositModalYearlyYield} (${widget.yieldTimeFrame.label(context)})",
- value: widget.currentYield.yieldTimeframed(widget.yieldTimeFrame).formatPercent,
+ value: widget.currentYield.yieldTimeframed(widget.yieldTimeFrame).formatRoundingPercent,
),
const SizedBox(height: 10),
const ZupDivider(),
diff --git a/lib/app/create/yields/[id]/deposit/widgets/preview_deposit_modal/preview_deposit_modal_cubit.dart b/lib/app/create/yields/[id]/deposit/widgets/preview_deposit_modal/preview_deposit_modal_cubit.dart
index 8687653..5d8c7ba 100644
--- a/lib/app/create/yields/[id]/deposit/widgets/preview_deposit_modal/preview_deposit_modal_cubit.dart
+++ b/lib/app/create/yields/[id]/deposit/widgets/preview_deposit_modal/preview_deposit_modal_cubit.dart
@@ -12,8 +12,8 @@ import 'package:zup_app/abis/uniswap_v3_position_manager.abi.g.dart';
import 'package:zup_app/app/create/yields/%5Bid%5D/deposit/widgets/deposit_success_modal.dart';
import 'package:zup_app/core/concentrated_liquidity_utils/cl_pool_constants.dart';
import 'package:zup_app/core/concentrated_liquidity_utils/cl_pool_conversors_mixin.dart';
-import 'package:zup_app/core/dtos/token_dto.dart';
-import 'package:zup_app/core/dtos/yield_dto.dart';
+import 'package:zup_app/core/dtos/liquidity_pool_dto.dart';
+import 'package:zup_app/core/dtos/single_chain_token_dto.dart';
import 'package:zup_app/core/pool_service.dart';
import 'package:zup_app/core/slippage.dart';
import 'package:zup_app/core/zup_analytics.dart';
@@ -28,7 +28,7 @@ class PreviewDepositModalCubit extends Cubit with CLPo
PreviewDepositModalCubit({
required BigInt currentPriceX96,
required PoolService poolService,
- required YieldDto currentYield,
+ required LiquidityPoolDto currentYield,
required Erc20 erc20,
required Wallet wallet,
required UniswapV3PositionManager uniswapPositionManager,
@@ -47,7 +47,7 @@ class PreviewDepositModalCubit extends Cubit with CLPo
final PoolService _poolRepository;
final Erc20 _erc20;
- final YieldDto _yield;
+ final LiquidityPoolDto _yield;
final Wallet _wallet;
final GlobalKey _navigatorKey;
@@ -79,10 +79,10 @@ class PreviewDepositModalCubit extends Cubit with CLPo
emit(PreviewDepositModalState.initial(token0Allowance: _token0Allowance, token1Allowance: _token1Allowance));
}
- Future approveToken(TokenDto token, BigInt value) async {
+ Future approveToken(SingleChainTokenDto token, BigInt value) async {
try {
final spender = _yield.poolType.isV4 ? _yield.permit2! : _yield.positionManagerAddress;
- final tokenAddressInNetwork = token.addresses[_yield.network.chainId]!;
+ final tokenAddressInNetwork = token.address;
emit(PreviewDepositModalState.approvingToken(token.symbol));
await _maybeSwitchNetwork();
@@ -100,8 +100,8 @@ class PreviewDepositModalCubit extends Cubit with CLPo
try {
await _getTokensAllowance(canThrow: true);
} catch (e) {
- if (_yield.token0.addresses[_yield.network.chainId] == tokenAddressInNetwork) _token0Allowance = value;
- if (_yield.token1.addresses[_yield.network.chainId] == tokenAddressInNetwork) _token1Allowance = value;
+ if (_yield.token0.address == tokenAddressInNetwork) _token0Allowance = value;
+ if (_yield.token1.address == tokenAddressInNetwork) _token1Allowance = value;
}
emit(PreviewDepositModalState.approveSuccess(txId: tx.hash, symbol: token.symbol));
@@ -118,23 +118,21 @@ class PreviewDepositModalCubit extends Cubit with CLPo
}
}
- Future checkOrApprovePermit2ForV4Pool(BigInt approveValue, TokenDto token) async {
- final tokenAddressInNetwork = token.addresses[_yield.network.chainId]!;
-
- if (tokenAddressInNetwork == EthereumConstants.zeroAddress) return;
+ Future checkOrApprovePermit2ForV4Pool(BigInt approveValue, SingleChainTokenDto token) async {
+ if (token.address == EthereumConstants.zeroAddress) return;
final permit2Contract = _permit2.fromSigner(contractAddress: _yield.permit2!, signer: _wallet.signer!);
final permit2CurrentAllowance = await permit2Contract.allowance(
await _wallet.signer!.address,
- tokenAddressInNetwork,
+ token.address,
_yield.positionManagerAddress,
);
if (permit2CurrentAllowance.amount <= approveValue ||
permit2CurrentAllowance.expiration < BigInt.from(DateTime.now().millisecondsSinceEpoch / 1000)) {
final tx = await permit2Contract.approve(
- token: tokenAddressInNetwork,
+ token: token.address,
spender: _yield.positionManagerAddress,
amount: EthereumConstants.uint160Max,
expiration: EthereumConstants.uint48Max,
@@ -166,8 +164,8 @@ class PreviewDepositModalCubit extends Cubit with CLPo
return priceToTick(
price: isReversed ? maxPrice : minPrice,
- poolToken0Decimals: _yield.token0NetworkDecimals,
- poolToken1Decimals: _yield.token1NetworkDecimals,
+ poolToken0Decimals: _yield.token0.decimals,
+ poolToken1Decimals: _yield.token1.decimals,
isReversed: isReversed,
);
}
@@ -182,8 +180,8 @@ class PreviewDepositModalCubit extends Cubit with CLPo
return priceToTick(
price: isReversed ? minPrice : maxPrice,
- poolToken0Decimals: _yield.token0NetworkDecimals,
- poolToken1Decimals: _yield.token1NetworkDecimals,
+ poolToken0Decimals: _yield.token0.decimals,
+ poolToken1Decimals: _yield.token1.decimals,
isReversed: isReversed,
);
}
@@ -233,8 +231,8 @@ class PreviewDepositModalCubit extends Cubit with CLPo
emit(PreviewDepositModalState.depositSuccess(txId: tx.hash));
_zupAnalytics.logDeposit(
depositedYield: _yield,
- amount0Formatted: amount0Desired.parseTokenAmount(decimals: _yield.token0NetworkDecimals),
- amount1Formatted: amount1Desired.parseTokenAmount(decimals: _yield.token1NetworkDecimals),
+ amount0Formatted: amount0Desired.parseTokenAmount(decimals: _yield.token0.decimals),
+ amount1Formatted: amount1Desired.parseTokenAmount(decimals: _yield.token1.decimals),
walletAddress: recipient,
);
} catch (e) {
@@ -273,7 +271,7 @@ class PreviewDepositModalCubit extends Cubit with CLPo
if (!_yield.isToken0Native) {
final token0contract = _erc20.fromRpcProvider(
- contractAddress: _yield.token0.addresses[_yield.network.chainId]!,
+ contractAddress: _yield.token0.address,
rpcUrl: _yield.network.rpcUrl,
);
@@ -282,7 +280,7 @@ class PreviewDepositModalCubit extends Cubit with CLPo
if (!_yield.isToken1Native) {
final token1contract = _erc20.fromRpcProvider(
- contractAddress: _yield.token1.addresses[_yield.network.chainId]!,
+ contractAddress: _yield.token1.address,
rpcUrl: _yield.network.rpcUrl,
);
diff --git a/lib/app/create/yields/[id]/deposit/widgets/range_selector.dart b/lib/app/create/yields/[id]/deposit/widgets/range_selector.dart
index c6c8b69..19d8143 100644
--- a/lib/app/create/yields/[id]/deposit/widgets/range_selector.dart
+++ b/lib/app/create/yields/[id]/deposit/widgets/range_selector.dart
@@ -362,11 +362,15 @@ class _RangeSelectorState extends State with CLPoolConversorsMixi
const SizedBox(width: 8),
ZupIconButton(
key: const Key("increase-button"),
- icon: Assets.icons.plus.svg(),
+ icon: Assets.icons.plus.svg(
+ colorFilter: ColorFilter.mode(
+ widget.state.type.adjustmentIconForegroundColor(context),
+ BlendMode.srcIn,
+ ),
+ ),
circle: false,
minimumHeight: 40,
padding: const EdgeInsets.all(12),
- iconColor: widget.state.type.adjustmentIconForegroundColor(context),
backgroundColor: widget.state.type.adjustmentIconBackgroundColor(context),
onPressed: (_) => increaseOrDecrease(increasing: true),
),
@@ -376,9 +380,13 @@ class _RangeSelectorState extends State with CLPoolConversorsMixi
circle: false,
padding: const EdgeInsets.all(12),
minimumHeight: 40,
- icon: Assets.icons.minus.svg(),
+ icon: Assets.icons.minus.svg(
+ colorFilter: ColorFilter.mode(
+ widget.state.type.adjustmentIconForegroundColor(context),
+ BlendMode.srcIn,
+ ),
+ ),
backgroundColor: widget.state.type.adjustmentIconBackgroundColor(context),
- iconColor: widget.state.type.adjustmentIconForegroundColor(context),
onPressed: (_) => increaseOrDecrease(increasing: false),
),
],
diff --git a/lib/app/create/yields/[id]/deposit/widgets/token_amount_input_card/token_amount_input_card.dart b/lib/app/create/yields/[id]/deposit/widgets/token_amount_input_card/token_amount_input_card.dart
index 140c133..a96edda 100644
--- a/lib/app/create/yields/[id]/deposit/widgets/token_amount_input_card/token_amount_input_card.dart
+++ b/lib/app/create/yields/[id]/deposit/widgets/token_amount_input_card/token_amount_input_card.dart
@@ -3,7 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:web3kit/web3kit.dart';
import 'package:zup_app/app/create/yields/%5Bid%5D/deposit/widgets/token_amount_input_card/token_amount_input_card_cubit.dart';
import 'package:zup_app/app/create/yields/%5Bid%5D/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/dtos/single_chain_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';
@@ -11,7 +11,7 @@ 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';
+import 'package:zup_app/widgets/pool_token.dart';
import 'package:zup_core/zup_core.dart';
import 'package:zup_ui_kit/zup_ui_kit.dart';
@@ -27,7 +27,7 @@ class TokenAmountInputCard extends StatefulWidget {
this.isNative = false,
});
- final TokenDto token;
+ final SingleChainTokenDto token;
final AppNetworks network;
final TextEditingController controller;
final Function(double value) onInput;
@@ -51,7 +51,7 @@ class _TokenAmountInputCardState extends State with Single
late TokenAmountCardUserBalanceCubit userBalanceCubit = TokenAmountCardUserBalanceCubit(
wallet,
- widget.token.addresses[widget.network.chainId]!,
+ widget.token.address,
widget.network,
zupSingletonCache,
widget.onRefreshBalance,
@@ -74,7 +74,7 @@ class _TokenAmountInputCardState extends State with Single
@override
void didUpdateWidget(TokenAmountInputCard oldWidget) {
if ((widget.isNative != oldWidget.isNative || widget.network != oldWidget.network) &&
- (widget.token.addresses[widget.network.chainId] ?? "").lowercasedEquals(EthereumConstants.zeroAddress)) {
+ (widget.token.address).lowercasedEquals(EthereumConstants.zeroAddress)) {
WidgetsBinding.instance.addPostFrameCallback(
(_) => userBalanceCubit.updateNativeTokenAndFetch(isNative: widget.isNative, network: widget.network),
);
@@ -84,11 +84,7 @@ class _TokenAmountInputCardState extends State with Single
if (widget.token != oldWidget.token || widget.network != oldWidget.network) {
WidgetsBinding.instance.addPostFrameCallback((_) {
- userBalanceCubit.updateTokenAndNetwork(
- widget.token.addresses[widget.network.chainId]!,
- widget.network,
- asNativeToken: widget.isNative,
- );
+ userBalanceCubit.updateTokenAndNetwork(widget.token.address, widget.network, asNativeToken: widget.isNative);
});
super.didUpdateWidget(oldWidget);
@@ -214,7 +210,7 @@ class _TokenAmountInputCardState extends State with Single
color: ZupThemeColors.borderOnBackgroundSurface.themed(context.brightness),
),
),
- child: PositionToken(token: widget.token),
+ child: PoolToken(token: widget.token),
),
],
),
diff --git a/lib/app/create/yields/[id]/deposit/widgets/token_amount_input_card/token_amount_input_card_cubit.dart b/lib/app/create/yields/[id]/deposit/widgets/token_amount_input_card/token_amount_input_card_cubit.dart
index d97b867..e17e945 100644
--- a/lib/app/create/yields/[id]/deposit/widgets/token_amount_input_card/token_amount_input_card_cubit.dart
+++ b/lib/app/create/yields/[id]/deposit/widgets/token_amount_input_card/token_amount_input_card_cubit.dart
@@ -1,6 +1,6 @@
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/dtos/single_chain_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';
@@ -11,21 +11,21 @@ part 'token_amount_input_card_state.dart';
class TokenAmountInputCardCubit extends Cubit with KeysMixin {
TokenAmountInputCardCubit(this._tokensRepository, this._zupSingletonCache, this._zupHolder)
- : super(const TokenAmountInputCardState.initial());
+ : super(const TokenAmountInputCardState.initial());
final TokensRepository _tokensRepository;
final ZupSingletonCache _zupSingletonCache;
final ZupHolder _zupHolder;
- Future getTokenPrice({required TokenDto token, required AppNetworks network}) async {
+ Future getTokenPrice({required SingleChainTokenDto 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),
- ));
+ return await _zupHolder.hold(
+ () async => await _zupSingletonCache.run(
+ () async => (await _tokensRepository.getTokenPrice(token.address, network)).usdPrice,
+ expiration: const Duration(minutes: 1),
+ key: tokenPriceCacheKey(tokenAddress: token.address, network: network),
+ ),
+ );
} catch (_) {
return 0;
}
diff --git a/lib/app/create/yields/[id]/deposit/widgets/yield_card_temp.dart b/lib/app/create/yields/[id]/deposit/widgets/yield_card_temp.dart
deleted file mode 100644
index 8128ca6..0000000
--- a/lib/app/create/yields/[id]/deposit/widgets/yield_card_temp.dart
+++ /dev/null
@@ -1,207 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:flutter_animate/flutter_animate.dart';
-import 'package:intl/intl.dart';
-import 'package:url_launcher/url_launcher.dart';
-import 'package:zup_app/app/app_cubit/app_cubit.dart';
-import 'package:zup_app/core/dtos/yield_dto.dart';
-import 'package:zup_app/core/enums/yield_timeframe.dart';
-import 'package:zup_app/core/extensions/num_extension.dart';
-import 'package:zup_app/core/injections.dart';
-import 'package:zup_app/l10n/gen/app_localizations.dart';
-import 'package:zup_app/widgets/token_avatar.dart';
-import 'package:zup_app/widgets/zup_cached_image.dart';
-import 'package:zup_core/extensions/extensions.dart';
-import 'package:zup_ui_kit/zup_ui_kit.dart';
-
-class YieldCardTemp extends StatefulWidget {
- const YieldCardTemp({
- super.key,
- required this.currentYield,
- required this.onChangeSelection,
- required this.isSelected,
- required this.timeFrame,
- this.isHotestYield = false,
- });
-
- final YieldDto currentYield;
- final bool isSelected;
- final Function(YieldDto? yield) onChangeSelection;
- final YieldTimeFrame timeFrame;
- final bool isHotestYield;
-
- @override
- State createState() => _YieldCardTempState();
-}
-
-class _YieldCardTempState extends State {
- final zupCachedImage = inject();
- final appCubit = inject();
- final infinityAnimationAutoPlay = inject(instanceName: InjectInstanceNames.infinityAnimationAutoPlay);
-
- final selectionAnimationDuration = const Duration(milliseconds: 150);
-
- Widget get yieldText => Text(
- widget.currentYield.yieldTimeframed(widget.timeFrame).formatPercent,
- style: TextStyle(
- fontSize: 26,
- fontWeight: FontWeight.w600,
- color: ZupThemeColors.backgroundInverse.themed(context.brightness),
- ),
- );
-
- @override
- Widget build(BuildContext context) {
- return ZupSelectableCard(
- isSelected: widget.isSelected,
- selectionAnimationDuration: selectionAnimationDuration,
-
- onPressed: () {
- return widget.onChangeSelection(widget.isSelected ? null : widget.currentYield);
- },
- padding: const EdgeInsets.all(10).copyWith(right: 2, top: 2, bottom: 0),
- child: Stack(
- children: [
- if (appCubit.selectedNetwork.isAllNetworks)
- Positioned(
- right: 2,
- top: 2,
- child: ZupTooltip.text(
- message: S.of(context).yieldCardThisPoolIsAtNetwork(network: widget.currentYield.network.label),
- trailingIcon: widget.currentYield.network.icon(context.brightness),
- child: Container(
- height: 40,
- padding: const EdgeInsets.all(6),
- width: 40,
- decoration: BoxDecoration(
- color: widget.isSelected
- ? ZupColors.brand.withValues(alpha: 0.1)
- : (context.brightness.isDark ? ZupColors.black2 : ZupColors.gray6),
- borderRadius: BorderRadius.circular(8),
- ),
- child: widget.currentYield.network.icon(context.brightness),
- ),
- ),
- ),
- Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Padding(
- padding: const EdgeInsets.only(top: 10),
- child: Text(
- widget.timeFrame.isDay
- ? S.of(context).yieldCardYearlyYield
- : S.of(context).yieldCardAverageYieldYearly,
- style: TextStyle(fontSize: 14, color: ZupThemeColors.primaryText.themed(context.brightness)),
- ),
- ),
- Row(
- mainAxisSize: MainAxisSize.min,
- children: [
- if (widget.isHotestYield) ...[
- yieldText.animate(
- effects: [
- const ScaleEffect(
- duration: Duration(milliseconds: 200),
- alignment: Alignment.center,
- begin: Offset(1.1, 1.1),
- end: Offset(1, 1),
- ),
- ShimmerEffect(
- duration: const Duration(seconds: 2),
- color: ZupThemeColors.shimmer.themed(context.brightness),
- curve: Curves.decelerate,
- angle: 90,
- size: 1,
- ),
- ],
- autoPlay: infinityAnimationAutoPlay,
- onComplete: (controller) => controller.repeat(reverse: true),
- ),
- ] else
- yieldText,
- ],
- ),
- Text(
- "${NumberFormat.compactSimpleCurrency(decimalDigits: 2).format(widget.currentYield.totalValueLockedUSD)} ${S.of(context).tvl}",
- style: const TextStyle(fontSize: 14, height: 1, color: ZupColors.gray),
- ),
- const SizedBox(height: 10),
- Row(
- mainAxisSize: MainAxisSize.min,
- children: [
- Container(
- padding: const EdgeInsets.all(6),
- decoration: BoxDecoration(
- color: widget.isSelected
- ? (context.brightness.isDark ? ZupColors.brand.withValues(alpha: 0.1) : ZupColors.brand5)
- : (context.brightness.isDark ? ZupColors.black2 : ZupColors.gray6),
- borderRadius: BorderRadius.circular(8),
- ),
- child: Row(
- mainAxisSize: MainAxisSize.min,
- children: [
- ZupMergedWidgets(
- firstWidget: TokenAvatar(asset: widget.currentYield.token0, size: 25),
- secondWidget: TokenAvatar(asset: widget.currentYield.token1, size: 25),
- spacing: 0,
- ),
- const SizedBox(width: 10),
- Text(
- "${widget.currentYield.token0.symbol}/${widget.currentYield.token1.symbol}",
- style: TextStyle(
- fontSize: 14,
- fontWeight: FontWeight.w600,
- color: ZupThemeColors.primaryText.themed(context.brightness),
- ),
- ),
- ],
- ),
- ),
- const SizedBox(width: 10),
- const Text(">", style: TextStyle(color: ZupColors.gray)),
- const SizedBox(width: 10),
- if (widget.currentYield.protocol.logo.isNotEmpty)
- zupCachedImage.build(
- context,
- widget.currentYield.protocol.logo,
- height: 25,
- width: 25,
- radius: 50,
- errorWidget: (context, error, stackTrace) {
- return Container(
- height: 25,
- width: 25,
- decoration: BoxDecoration(color: ZupColors.gray6, borderRadius: BorderRadius.circular(50)),
- );
- },
- ),
- const SizedBox(width: 5),
- Flexible(
- child: ZupTooltip.text(
- message: "",
- helperButtonTitle: S
- .of(context)
- .yieldCardVisitProtocol(protocolName: widget.currentYield.protocol.name),
- helperButtonOnPressed: () => launchUrl(Uri.parse(widget.currentYield.protocol.url)),
- child: Text(
- widget.currentYield.protocol.name,
- overflow: TextOverflow.ellipsis,
- maxLines: 1,
- style: TextStyle(
- fontSize: 14,
- fontWeight: FontWeight.w600,
- color: ZupThemeColors.backgroundInverse.themed(context.brightness),
- ),
- ),
- ),
- ),
- const SizedBox(width: 20),
- ],
- ),
- ],
- ),
- ],
- ),
- );
- }
-}
diff --git a/lib/app/create/yields/yields_cubit.dart b/lib/app/create/yields/yields_cubit.dart
index 9fcea02..0f48148 100644
--- a/lib/app/create/yields/yields_cubit.dart
+++ b/lib/app/create/yields/yields_cubit.dart
@@ -2,8 +2,8 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:zup_app/app/app_cubit/app_cubit.dart';
import 'package:zup_app/core/cache.dart';
+import 'package:zup_app/core/dtos/liquidity_pools_search_result_dto.dart';
import 'package:zup_app/core/dtos/pool_search_filters_dto.dart';
-import 'package:zup_app/core/dtos/yields_dto.dart';
import 'package:zup_app/core/repositories/yield_repository.dart';
import 'package:zup_app/core/zup_analytics.dart';
diff --git a/lib/app/create/yields/yields_page.dart b/lib/app/create/yields/yields_page.dart
index a7b6f5f..f315b08 100644
--- a/lib/app/create/yields/yields_page.dart
+++ b/lib/app/create/yields/yields_page.dart
@@ -6,10 +6,10 @@ import 'package:skeletonizer/skeletonizer.dart';
import 'package:zup_app/app/app_cubit/app_cubit.dart';
import 'package:zup_app/app/create/yields/yields_cubit.dart';
import 'package:zup_app/core/cache.dart';
+import 'package:zup_app/core/dtos/liquidity_pools_search_result_dto.dart';
import 'package:zup_app/core/dtos/pool_search_filters_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/yield_timeframe.dart';
+import 'package:zup_app/core/enums/pool_data_timeframe.dart';
import 'package:zup_app/core/enums/zup_navigator_paths.dart';
import 'package:zup_app/core/extensions/num_extension.dart';
import 'package:zup_app/core/injections.dart';
@@ -20,6 +20,7 @@ import 'package:zup_app/core/zup_navigator.dart';
import 'package:zup_app/core/zup_route_params_names.dart';
import 'package:zup_app/gen/assets.gen.dart';
import 'package:zup_app/l10n/gen/app_localizations.dart';
+import 'package:zup_app/widgets/pool_info_modal/pool_info_modal.dart';
import 'package:zup_app/widgets/timeframe_selector.dart';
import 'package:zup_app/widgets/yield_card.dart';
import 'package:zup_app/widgets/zup_page_title.dart';
@@ -84,7 +85,7 @@ class _YieldsPageState extends State with DeviceInfoMixin, SingleTic
return navigator.getQueryParam(ZupNavigatorPaths.yields.routeParamsNames().group1);
}
- YieldTimeFrame selectedYieldTimeFrame = YieldTimeFrame.day;
+ PoolDataTimeframe selectedYieldTimeFrame = PoolDataTimeframe.day;
num currentYieldPage = 0;
bool isYieldsPageGoingBackwards = false;
@@ -145,7 +146,7 @@ class _YieldsPageState extends State with DeviceInfoMixin, SingleTic
);
}
- Widget _buildSuccessState(YieldsDto yields) {
+ Widget _buildSuccessState(LiquidityPoolsSearchResultDto yields) {
int getYieldDisplayCountPerPage() {
return yields.pools.length.clamp(1, isMobileSize(context) ? 1 : 2);
}
@@ -171,7 +172,11 @@ class _YieldsPageState extends State with DeviceInfoMixin, SingleTic
child: Padding(
padding: const EdgeInsets.all(20),
child: ZupIconButton(
- icon: Assets.icons.arrowLeft.svg(height: 12, width: 12),
+ icon: Assets.icons.arrowLeft.svg(
+ height: 12,
+ width: 12,
+ colorFilter: const ColorFilter.mode(ZupColors.gray, BlendMode.srcIn),
+ ),
onPressed: (context) async {
if (currentYieldPage.equals(0)) return;
isYieldsPageGoingBackwards = true;
@@ -209,18 +214,20 @@ class _YieldsPageState extends State with DeviceInfoMixin, SingleTic
),
const SizedBox(height: 10),
TimeframeSelector(
+ title: S.of(context).yieldsPageTimeframeSelectorTitle,
+ tooltipMessage: S.of(context).yieldsPageTimeframeExplanation,
selectedTimeframe: selectedYieldTimeFrame,
onTimeframeSelected: (timeframe) {
setState(() {
pageController.jumpTo(0);
- selectedYieldTimeFrame = timeframe ?? YieldTimeFrame.day;
+ selectedYieldTimeFrame = timeframe;
});
},
),
],
),
),
- const SizedBox(height: 20),
+ const SizedBox(height: 10),
_buildYieldsSection(
yieldsPagesCount: yieldsPagesCount,
yieldsCardPerPage: getYieldDisplayCountPerPage(),
@@ -244,7 +251,11 @@ class _YieldsPageState extends State with DeviceInfoMixin, SingleTic
child: Padding(
padding: const EdgeInsets.all(20),
child: ZupIconButton(
- icon: Assets.icons.arrowRight.svg(height: 12, width: 12),
+ icon: Assets.icons.arrowRight.svg(
+ height: 12,
+ width: 12,
+ colorFilter: const ColorFilter.mode(ZupColors.gray, BlendMode.srcIn),
+ ),
onPressed: (context) {
if (currentYieldPage.equals(yieldsPagesCount - 1)) return;
isYieldsPageGoingBackwards = false;
@@ -267,7 +278,7 @@ class _YieldsPageState extends State with DeviceInfoMixin, SingleTic
Widget _buildYieldsSection({
required int yieldsPagesCount,
required int yieldsCardPerPage,
- required YieldsDto yields,
+ required LiquidityPoolsSearchResultDto yields,
}) {
final poolsSortedByTimeframe = yields.poolsSortedByTimeframe(selectedYieldTimeFrame);
@@ -309,6 +320,22 @@ class _YieldsPageState extends State with DeviceInfoMixin, SingleTic
yieldTimeFrame: selectedYieldTimeFrame,
showHotestYieldAnimation: yieldItem.equals(poolsSortedByTimeframe.first),
expandWidth: isMobileSize(context),
+ secondaryButton: ZupIconButton(
+ key: Key("pool-stats-button-${yieldItem.poolAddress}"),
+ icon: Assets.icons.chartBar.svg(
+ height: 15,
+ width: 15,
+ colorFilter: const ColorFilter.mode(ZupColors.brand4, BlendMode.srcIn),
+ ),
+ onPressed: (_) => PoolInfoModal.show(
+ context,
+ showAsBottomSheet: isMobileSize(context),
+ liquidityPool: yieldItem,
+ selectedTimeframe: selectedYieldTimeFrame,
+ ),
+ backgroundColor: ZupThemeColors.tertiaryButtonBackground.themed(context.brightness),
+ padding: const EdgeInsets.all(15),
+ ),
mainButton: ZupPrimaryButton(
key: Key("deposit-button-${yieldItem.poolAddress}"),
title: S.of(context).yieldCardDeposit,
diff --git a/lib/app/create/yields/yields_state.dart b/lib/app/create/yields/yields_state.dart
index 2b7bbad..e0a921d 100644
--- a/lib/app/create/yields/yields_state.dart
+++ b/lib/app/create/yields/yields_state.dart
@@ -4,7 +4,7 @@ part of 'yields_cubit.dart';
abstract class YieldsState with _$YieldsState {
const factory YieldsState.initial() = _Initial;
const factory YieldsState.loading() = _Loading;
- const factory YieldsState.success(YieldsDto yields) = _Success;
+ const factory YieldsState.success(LiquidityPoolsSearchResultDto yields) = _Success;
const factory YieldsState.error(String message, String stackTrace) = _Error;
const factory YieldsState.noYields({required PoolSearchFiltersDto filtersApplied}) = _NoYields;
}
diff --git a/lib/core/concentrated_liquidity_utils/cl_pool_constants.dart b/lib/core/concentrated_liquidity_utils/cl_pool_constants.dart
index 8ef3400..7f4d356 100644
--- a/lib/core/concentrated_liquidity_utils/cl_pool_constants.dart
+++ b/lib/core/concentrated_liquidity_utils/cl_pool_constants.dart
@@ -1,6 +1,11 @@
abstract class CLPoolConstants {
static final BigInt minTick = BigInt.from(-887272);
+
static final BigInt maxTick = -minTick;
+
static final BigInt q96 = BigInt.from(2).pow(96);
+
static final BigInt q32 = BigInt.from(2).pow(32);
+
+ static const int feeTierFactor = 10000;
}
diff --git a/lib/core/dtos/deposit_page_arguments_dto.dart b/lib/core/dtos/deposit_page_arguments_dto.dart
index 3393f21..f8cbacc 100644
--- a/lib/core/dtos/deposit_page_arguments_dto.dart
+++ b/lib/core/dtos/deposit_page_arguments_dto.dart
@@ -1,5 +1,5 @@
import 'package:freezed_annotation/freezed_annotation.dart';
-import 'package:zup_app/core/dtos/yield_dto.dart';
+import 'package:zup_app/core/dtos/liquidity_pool_dto.dart';
part 'deposit_page_arguments_dto.freezed.dart';
part 'deposit_page_arguments_dto.g.dart';
@@ -7,7 +7,7 @@ part 'deposit_page_arguments_dto.g.dart';
@freezed
sealed class DepositPageArgumentsDto with _$DepositPageArgumentsDto {
@JsonSerializable(explicitToJson: true)
- const factory DepositPageArgumentsDto({YieldDto? yieldPool}) = _DepositPageArgumentsDto;
+ const factory DepositPageArgumentsDto({LiquidityPoolDto? yieldPool}) = _DepositPageArgumentsDto;
factory DepositPageArgumentsDto.fromJson(Map json) => _$DepositPageArgumentsDtoFromJson(json);
}
diff --git a/lib/core/dtos/hook_dto.dart b/lib/core/dtos/hook_dto.dart
new file mode 100644
index 0000000..322d3c1
--- /dev/null
+++ b/lib/core/dtos/hook_dto.dart
@@ -0,0 +1,16 @@
+import 'package:freezed_annotation/freezed_annotation.dart';
+import 'package:web3kit/core/ethereum_constants.dart';
+
+part 'hook_dto.freezed.dart';
+part 'hook_dto.g.dart';
+
+@freezed
+sealed class HookDto with _$HookDto {
+ @JsonSerializable(explicitToJson: true)
+ const factory HookDto({@Default(EthereumConstants.zeroAddress) String address, @Default(false) bool isDynamicFee}) =
+ _HookDto;
+
+ factory HookDto.fromJson(Map json) => _$HookDtoFromJson(json);
+
+ factory HookDto.fixture() => const HookDto(address: EthereumConstants.zeroAddress, isDynamicFee: false);
+}
diff --git a/lib/core/dtos/liquidity_pool_dto.dart b/lib/core/dtos/liquidity_pool_dto.dart
new file mode 100644
index 0000000..b5626c4
--- /dev/null
+++ b/lib/core/dtos/liquidity_pool_dto.dart
@@ -0,0 +1,157 @@
+import 'package:clock/clock.dart';
+import 'package:freezed_annotation/freezed_annotation.dart';
+import 'package:web3kit/core/ethereum_constants.dart';
+import 'package:zup_app/core/concentrated_liquidity_utils/cl_pool_constants.dart';
+import 'package:zup_app/core/dtos/hook_dto.dart';
+import 'package:zup_app/core/dtos/pool_stats_dto.dart';
+import 'package:zup_app/core/dtos/protocol_dto.dart';
+import 'package:zup_app/core/dtos/single_chain_token_dto.dart';
+import 'package:zup_app/core/enums/networks.dart';
+import 'package:zup_app/core/enums/pool_data_timeframe.dart';
+import 'package:zup_app/core/enums/pool_type.dart';
+import 'package:zup_app/core/extensions/num_extension.dart';
+
+part 'liquidity_pool_dto.freezed.dart';
+part 'liquidity_pool_dto.g.dart';
+
+@freezed
+sealed class LiquidityPoolDto with _$LiquidityPoolDto {
+ const LiquidityPoolDto._();
+ @JsonSerializable(explicitToJson: true)
+ const factory LiquidityPoolDto({
+ required SingleChainTokenDto token0,
+ required SingleChainTokenDto token1,
+ required String poolAddress,
+ required String positionManagerAddress,
+ required int tickSpacing,
+ required ProtocolDto protocol,
+ required int initialFeeTier,
+ required int currentFeeTier,
+ required int chainId,
+ PoolTotalStatsDTO? total24hStats,
+ PoolTotalStatsDTO? total7dStats,
+ PoolTotalStatsDTO? total30dStats,
+ PoolTotalStatsDTO? total90dStats,
+ HookDto? hook,
+ @Default(0) int createdAtTimestamp,
+ @Default(PoolType.unknown) @JsonKey(unknownEnumValue: PoolType.unknown) PoolType poolType,
+ @Default("0") String latestTick,
+ @Default("0") String latestSqrtPriceX96,
+ @Default(0) num totalValueLockedUSD,
+ @Default(EthereumConstants.zeroAddress) @JsonKey(name: "deployerAddress") String deployerAddress,
+ @JsonKey(name: "poolManagerAddress") String? v4PoolManager,
+ @JsonKey(name: "stateViewAddress") String? v4StateView,
+ @JsonKey(name: "permit2Address") String? permit2,
+ }) = _LiquidityPoolDto;
+
+ AppNetworks get network => AppNetworks.fromChainId(chainId)!;
+
+ bool get isToken0Native => token0.address == EthereumConstants.zeroAddress;
+ bool get isToken1Native => token1.address == EthereumConstants.zeroAddress;
+
+ String get currentFeeTierFormatted {
+ return "${(currentFeeTier / CLPoolConstants.feeTierFactor)}%";
+ }
+
+ bool get isDynamicFee => hook?.isDynamicFee ?? false;
+
+ int get createdAtMillisecondsTimestamp => createdAtTimestamp * 1000;
+
+ String timeframedYieldFormatted(PoolDataTimeframe yieldTimeFrame) {
+ switch (yieldTimeFrame) {
+ case PoolDataTimeframe.day:
+ return total24hStats?.yearlyYield == 0 ? "-" : total24hStats?.yearlyYield.formatRoundingPercent ?? "-";
+ case PoolDataTimeframe.week:
+ return total7dStats?.yearlyYield == 0 ? "-" : total7dStats?.yearlyYield.formatRoundingPercent ?? "-";
+ case PoolDataTimeframe.month:
+ return total30dStats?.yearlyYield == 0 ? "-" : total30dStats?.yearlyYield.formatRoundingPercent ?? "-";
+ case PoolDataTimeframe.threeMonth:
+ return total90dStats?.yearlyYield == 0 ? "-" : total90dStats?.yearlyYield.formatRoundingPercent ?? "-";
+ }
+ }
+
+ num volumeTimeframed(PoolDataTimeframe yieldTimeFrame) {
+ switch (yieldTimeFrame) {
+ case PoolDataTimeframe.day:
+ return total24hStats?.totalVolume ?? 0;
+ case PoolDataTimeframe.week:
+ return total7dStats?.totalVolume ?? 0;
+ case PoolDataTimeframe.month:
+ return total30dStats?.totalVolume ?? 0;
+ case PoolDataTimeframe.threeMonth:
+ return total90dStats?.totalVolume ?? 0;
+ }
+ }
+
+ num feesTimeframed(PoolDataTimeframe yieldTimeFrame) {
+ switch (yieldTimeFrame) {
+ case PoolDataTimeframe.day:
+ return total24hStats?.totalFees ?? 0;
+ case PoolDataTimeframe.week:
+ return total7dStats?.totalFees ?? 0;
+ case PoolDataTimeframe.month:
+ return total30dStats?.totalFees ?? 0;
+ case PoolDataTimeframe.threeMonth:
+ return total90dStats?.totalFees ?? 0;
+ }
+ }
+
+ num netInflowTimeframed(PoolDataTimeframe yieldTimeFrame) {
+ switch (yieldTimeFrame) {
+ case PoolDataTimeframe.day:
+ return total24hStats?.totalNetInflow ?? 0;
+ case PoolDataTimeframe.week:
+ return total7dStats?.totalNetInflow ?? 0;
+ case PoolDataTimeframe.month:
+ return total30dStats?.totalNetInflow ?? 0;
+ case PoolDataTimeframe.threeMonth:
+ return total90dStats?.totalNetInflow ?? 0;
+ }
+ }
+
+ num yieldTimeframed(PoolDataTimeframe yieldTimeFrame) {
+ switch (yieldTimeFrame) {
+ case PoolDataTimeframe.day:
+ return total24hStats?.yearlyYield ?? 0;
+ case PoolDataTimeframe.week:
+ return total7dStats?.yearlyYield ?? 0;
+ case PoolDataTimeframe.month:
+ return total30dStats?.yearlyYield ?? 0;
+ case PoolDataTimeframe.threeMonth:
+ return total90dStats?.yearlyYield ?? 0;
+ }
+ }
+
+ factory LiquidityPoolDto.fromJson(Map json) => _$LiquidityPoolDtoFromJson(json);
+
+ factory LiquidityPoolDto.fixture() => LiquidityPoolDto(
+ initialFeeTier: 0,
+ currentFeeTier: 0,
+ createdAtTimestamp: (clock.now().copyWith(year: 2024, month: 2, day: 23).millisecondsSinceEpoch / 1000).toInt(),
+ total24hStats: PoolTotalStatsDTO.fixture(),
+ total7dStats: PoolTotalStatsDTO.fixture(),
+ total30dStats: PoolTotalStatsDTO.fixture(),
+ total90dStats: PoolTotalStatsDTO.fixture(),
+ latestTick: "1567241",
+ positionManagerAddress: "0x5Df2f0aFb5b5bB2Df9D1e9C7b6f5f0DD5f9eD5e0",
+ poolAddress: "0x5Df2f0aFb5b5bB2Df9D1e9C7b6f5f0DD5f9eD5e0",
+ poolType: PoolType.v3,
+ token0: SingleChainTokenDto.fixture().copyWith(
+ symbol: "USDC",
+ decimals: 6,
+ address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
+ logoUrl:
+ "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png",
+ ),
+ token1: SingleChainTokenDto.fixture().copyWith(
+ symbol: "WETH",
+ decimals: 18,
+ address: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
+ logoUrl:
+ "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png",
+ ),
+ tickSpacing: 10,
+ protocol: ProtocolDto.fixture(),
+ chainId: AppNetworks.sepolia.chainId,
+ );
+}
diff --git a/lib/core/dtos/liquidity_pools_search_result_dto.dart b/lib/core/dtos/liquidity_pools_search_result_dto.dart
new file mode 100644
index 0000000..50487c3
--- /dev/null
+++ b/lib/core/dtos/liquidity_pools_search_result_dto.dart
@@ -0,0 +1,105 @@
+import 'package:freezed_annotation/freezed_annotation.dart';
+import 'package:zup_app/core/dtos/liquidity_pool_dto.dart';
+import 'package:zup_app/core/dtos/pool_search_filters_dto.dart';
+import 'package:zup_app/core/dtos/pool_stats_dto.dart';
+import 'package:zup_app/core/dtos/protocol_dto.dart';
+import 'package:zup_app/core/dtos/single_chain_token_dto.dart';
+import 'package:zup_app/core/enums/pool_data_timeframe.dart';
+import 'package:zup_app/core/enums/pool_type.dart';
+import 'package:zup_app/core/enums/protocol_id.dart';
+
+part 'liquidity_pools_search_result_dto.freezed.dart';
+part 'liquidity_pools_search_result_dto.g.dart';
+
+@freezed
+sealed class LiquidityPoolsSearchResultDto with _$LiquidityPoolsSearchResultDto {
+ const LiquidityPoolsSearchResultDto._();
+
+ @JsonSerializable(explicitToJson: true)
+ const factory LiquidityPoolsSearchResultDto({
+ @Default([]) @JsonKey(name: "pools") List pools,
+ @Default(PoolSearchFiltersDto()) PoolSearchFiltersDto filters,
+ }) = _LiquidityPoolsSearchResultDto;
+
+ bool get isEmpty => pools.isEmpty;
+
+ List get poolsSortedBy24hYield {
+ return [...pools]..sort((a, b) => b.total24hStats?.yearlyYield.compareTo(a.total24hStats?.yearlyYield ?? 0) ?? 0);
+ }
+
+ List get poolsSortedBy7dYield {
+ return [...pools]..sort((a, b) => b.total7dStats?.yearlyYield.compareTo(a.total7dStats?.yearlyYield ?? 0) ?? 0);
+ }
+
+ List get poolsSortedBy30dYield {
+ return [...pools]..sort((a, b) => b.total30dStats?.yearlyYield.compareTo(a.total30dStats?.yearlyYield ?? 0) ?? 0);
+ }
+
+ List get poolsSortedBy90dYield {
+ return [...pools]..sort((a, b) => b.total90dStats?.yearlyYield.compareTo(a.total90dStats?.yearlyYield ?? 0) ?? 0);
+ }
+
+ List poolsSortedByTimeframe(PoolDataTimeframe timeframe) {
+ switch (timeframe) {
+ case PoolDataTimeframe.day:
+ return poolsSortedBy24hYield;
+ case PoolDataTimeframe.week:
+ return poolsSortedBy7dYield;
+ case PoolDataTimeframe.month:
+ return poolsSortedBy30dYield;
+ case PoolDataTimeframe.threeMonth:
+ return poolsSortedBy90dYield;
+ }
+ }
+
+ factory LiquidityPoolsSearchResultDto.fromJson(Map json) =>
+ _$LiquidityPoolsSearchResultDtoFromJson(json);
+
+ factory LiquidityPoolsSearchResultDto.empty() => const LiquidityPoolsSearchResultDto(pools: []);
+
+ factory LiquidityPoolsSearchResultDto.fixture() => LiquidityPoolsSearchResultDto(
+ filters: PoolSearchFiltersDto.fixture(),
+ pools: [
+ LiquidityPoolDto(
+ createdAtTimestamp: DateTime(1992).millisecondsSinceEpoch,
+ latestTick: "637812562",
+ latestSqrtPriceX96: "5240418162556390792557189",
+ positionManagerAddress: "0x06eFdBFf2a14a7c8E15944D1F4A48F9F95F663A4",
+ poolType: PoolType.v3,
+ total24hStats: PoolTotalStatsDTO.fixture(),
+ total7dStats: PoolTotalStatsDTO.fixture(),
+ total30dStats: PoolTotalStatsDTO.fixture(),
+ total90dStats: PoolTotalStatsDTO.fixture(),
+ token0: const SingleChainTokenDto(
+ address: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
+ decimals: 18,
+ name: "Wrapped Ether",
+ symbol: "WETH",
+ logoUrl:
+ "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/scroll/assets/0x5300000000000000000000000000000000000004/logo.png",
+ ),
+ token1: const SingleChainTokenDto(
+ address: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
+ decimals: 6,
+ logoUrl:
+ "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/scroll/assets/0x06eFdBFf2a14a7c8E15944D1F4A48F9F95F663A4/logo.png",
+ name: "USDC",
+ symbol: "USDC",
+ ),
+ chainId: 1,
+ poolAddress: "0x4040CE732c1A538A4Ac3157FDC35179D73ea76cd",
+ tickSpacing: 10,
+
+ totalValueLockedUSD: 65434567890.21,
+ initialFeeTier: 500,
+ currentFeeTier: 500,
+ protocol: ProtocolDto(
+ id: ProtocolId.pancakeSwapInfinityCL,
+ rawId: ProtocolId.pancakeSwapInfinityCL.toRawJsonValue,
+ name: "PancakeSwap",
+ logo: "https://raw.githubusercontent.com/trustwallet/assets/master/dapps/exchange.pancakeswap.finance.png",
+ ),
+ ),
+ ],
+ );
+}
diff --git a/lib/core/dtos/multi_chain_token_dto.dart b/lib/core/dtos/multi_chain_token_dto.dart
new file mode 100644
index 0000000..03a51c1
--- /dev/null
+++ b/lib/core/dtos/multi_chain_token_dto.dart
@@ -0,0 +1,39 @@
+import 'package:freezed_annotation/freezed_annotation.dart';
+import 'package:zup_app/core/enums/networks.dart';
+
+part 'multi_chain_token_dto.freezed.dart';
+part 'multi_chain_token_dto.g.dart';
+
+@freezed
+sealed class MultiChainTokenDto with _$MultiChainTokenDto {
+ @JsonSerializable(explicitToJson: true)
+ const factory MultiChainTokenDto({
+ @JsonKey(name: "id") String? internalId,
+ @Default("") String symbol,
+ @Default("") String name,
+ @Default("") String logoUrl,
+ @Default({}) Map addresses,
+ @Default({}) Map decimals,
+ }) = _MultiChainTokenDto;
+
+ factory MultiChainTokenDto.fromJson(Map json) => _$MultiChainTokenDtoFromJson(json);
+
+ factory MultiChainTokenDto.empty() => const MultiChainTokenDto();
+
+ factory MultiChainTokenDto.fixture() => MultiChainTokenDto(
+ symbol: 'WETH',
+ name: 'Wrapped Ether',
+ decimals: Map.fromEntries(
+ AppNetworks.values.where((network) => !network.isAllNetworks).map((network) {
+ return MapEntry(network.chainId, 18);
+ }),
+ ),
+ 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/pool_search_filters_dto.dart b/lib/core/dtos/pool_search_filters_dto.dart
index 4c589d1..11bcf6f 100644
--- a/lib/core/dtos/pool_search_filters_dto.dart
+++ b/lib/core/dtos/pool_search_filters_dto.dart
@@ -6,21 +6,13 @@ part 'pool_search_filters_dto.g.dart';
@freezed
sealed class PoolSearchFiltersDto with _$PoolSearchFiltersDto {
@JsonSerializable(explicitToJson: true)
- const factory PoolSearchFiltersDto({
- @Default(0) num minTvlUsd,
- @Default(false) bool testnetMode,
- @Default([]) List allowedPoolTypes,
- @Default([]) List blockedProtocols,
- }) = _PoolSearchFiltersDto;
+ const factory PoolSearchFiltersDto({@Default(0) num minTvlUsd, @Default([]) List blockedProtocols}) =
+ _PoolSearchFiltersDto;
const PoolSearchFiltersDto._();
factory PoolSearchFiltersDto.fromJson(Map json) => _$PoolSearchFiltersDtoFromJson(json);
- factory PoolSearchFiltersDto.fixture() => const PoolSearchFiltersDto(
- allowedPoolTypes: ["V3", "V4"],
- blockedProtocols: ["nuri-exchange"],
- minTvlUsd: 121782617,
- testnetMode: false,
- );
+ factory PoolSearchFiltersDto.fixture() =>
+ const PoolSearchFiltersDto(blockedProtocols: ["nuri-exchange"], minTvlUsd: 121782617);
}
diff --git a/lib/core/dtos/pool_search_settings_dto.dart b/lib/core/dtos/pool_search_settings_dto.dart
index 6846618..e14edfe 100644
--- a/lib/core/dtos/pool_search_settings_dto.dart
+++ b/lib/core/dtos/pool_search_settings_dto.dart
@@ -10,8 +10,6 @@ sealed 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/pool_stats_dto.dart b/lib/core/dtos/pool_stats_dto.dart
new file mode 100644
index 0000000..3cf0f04
--- /dev/null
+++ b/lib/core/dtos/pool_stats_dto.dart
@@ -0,0 +1,19 @@
+import 'package:freezed_annotation/freezed_annotation.dart';
+
+part 'pool_stats_dto.freezed.dart';
+part 'pool_stats_dto.g.dart';
+
+@freezed
+sealed class PoolTotalStatsDTO with _$PoolTotalStatsDTO {
+ @JsonSerializable(explicitToJson: true)
+ const factory PoolTotalStatsDTO({
+ @Default(0) num totalVolume,
+ @Default(0) num totalFees,
+ @Default(0) @JsonKey(name: "yield") num yearlyYield,
+ @Default(0) num totalNetInflow,
+ }) = _PoolTotalStatsDTO;
+
+ factory PoolTotalStatsDTO.fromJson(Map json) => _$PoolTotalStatsDTOFromJson(json);
+
+ factory PoolTotalStatsDTO.fixture() => const PoolTotalStatsDTO(totalVolume: 100, totalFees: 10, yearlyYield: 10);
+}
diff --git a/lib/core/dtos/position_dto.dart b/lib/core/dtos/position_dto.dart
deleted file mode 100644
index fb9e012..0000000
--- a/lib/core/dtos/position_dto.dart
+++ /dev/null
@@ -1,39 +0,0 @@
-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/enums/networks.dart';
-import 'package:zup_app/core/enums/position_status.dart';
-
-part 'position_dto.freezed.dart';
-part 'position_dto.g.dart';
-
-@freezed
-sealed class PositionDto with _$PositionDto {
- const PositionDto._();
-
- @JsonSerializable(explicitToJson: true)
- const factory PositionDto({
- TokenDto? token0,
- TokenDto? token1,
- @Default(null) AppNetworks? network,
- @Default(PositionStatus.unknown) @JsonKey(unknownEnumValue: PositionStatus.unknown) PositionStatus status,
- @Default(null) ProtocolDto? protocol,
- @Default(0) num minRange,
- @Default(0) num maxRange,
- @Default(0) @JsonKey(name: "liquidity") num liquidityUSD,
- @Default(0) @JsonKey(name: 'unclaimed_fees') num unclaimedFeesUSD,
- }) = _PositionDto;
-
- factory PositionDto.fromJson(Map json) => _$PositionDtoFromJson(json);
-
- factory PositionDto.fixture() => PositionDto(
- liquidityUSD: 123543.43,
- minRange: 0.4,
- maxRange: 123413.43,
- status: PositionStatus.inRange,
- token0: TokenDto.fixture(),
- token1: TokenDto.fixture(),
- protocol: ProtocolDto.fixture(),
- unclaimedFeesUSD: 1543.43,
- );
-}
diff --git a/lib/core/dtos/protocol_dto.dart b/lib/core/dtos/protocol_dto.dart
index b61682d..7c354e5 100644
--- a/lib/core/dtos/protocol_dto.dart
+++ b/lib/core/dtos/protocol_dto.dart
@@ -24,8 +24,8 @@ sealed class ProtocolDto with _$ProtocolDto {
factory ProtocolDto.fixture() => ProtocolDto(
rawId: ProtocolId.velodromeSlipstream.toRawJsonValue,
id: ProtocolId.velodromeSlipstream,
- name: "Uniswap",
+ name: "Uniswap V3",
url: "https://app.uniswap.org/pool",
- logo: "https://raw.githubusercontent.com/trustwallet/assets/master/dapps/app.uniswap.org.png",
+ logo: "https://assets-cdn.trustwallet.com/dapps/app.uniswap.org.png",
);
}
diff --git a/lib/core/dtos/single_chain_token_dto.dart b/lib/core/dtos/single_chain_token_dto.dart
new file mode 100644
index 0000000..047cc97
--- /dev/null
+++ b/lib/core/dtos/single_chain_token_dto.dart
@@ -0,0 +1,25 @@
+import 'package:freezed_annotation/freezed_annotation.dart';
+
+part 'single_chain_token_dto.freezed.dart';
+part 'single_chain_token_dto.g.dart';
+
+@freezed
+sealed class SingleChainTokenDto with _$SingleChainTokenDto {
+ @JsonSerializable(explicitToJson: true)
+ const factory SingleChainTokenDto({
+ required String address,
+ @Default(18) int decimals,
+ @Default("") String name,
+ @Default("") String symbol,
+ @Default("") String logoUrl,
+ }) = _SingleChainTokenDto;
+
+ factory SingleChainTokenDto.fromJson(Map json) => _$SingleChainTokenDtoFromJson(json);
+
+ factory SingleChainTokenDto.fixture() => const SingleChainTokenDto(
+ address: '0x0000000000000000000000000000000000000000',
+ decimals: 18,
+ name: 'Test Token',
+ symbol: 'TEST',
+ );
+}
diff --git a/lib/core/dtos/token_dto.dart b/lib/core/dtos/token_dto.dart
deleted file mode 100644
index 7cbf435..0000000
--- a/lib/core/dtos/token_dto.dart
+++ /dev/null
@@ -1,43 +0,0 @@
-import 'package:freezed_annotation/freezed_annotation.dart';
-import 'package:zup_app/core/enums/networks.dart';
-
-part 'token_dto.freezed.dart';
-part 'token_dto.g.dart';
-
-@freezed
-sealed class TokenDto with _$TokenDto {
- @JsonSerializable(explicitToJson: true)
- const factory TokenDto({
- @JsonKey(name: "id") String? internalId,
- @Default("") String symbol,
- @Default("") String name,
- @Default("") String logoUrl,
- @Default({}) Map addresses,
- @Default({}) Map decimals,
- }) = _TokenDto;
-
- factory TokenDto.fromJson(Map json) => _$TokenDtoFromJson(json);
-
- factory TokenDto.empty() => const TokenDto();
-
- factory TokenDto.fixture() => TokenDto(
- symbol: 'WETH',
- name: 'Wrapped Ether',
- decimals: Map.fromEntries(
- AppNetworks.values.where((network) => !network.isAllNetworks).map(
- (network) {
- return MapEntry(network.chainId, 18);
- },
- ),
- ),
- 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_group_dto.dart b/lib/core/dtos/token_group_dto.dart
index 689e235..74292b6 100644
--- a/lib/core/dtos/token_group_dto.dart
+++ b/lib/core/dtos/token_group_dto.dart
@@ -1,5 +1,5 @@
import 'package:freezed_annotation/freezed_annotation.dart';
-import 'package:zup_app/core/dtos/token_dto.dart';
+import 'package:zup_app/core/dtos/multi_chain_token_dto.dart';
import 'package:zup_app/core/enums/app_environment.dart';
part 'token_group_dto.freezed.dart';
@@ -13,7 +13,7 @@ sealed class TokenGroupDto with _$TokenGroupDto {
const factory TokenGroupDto({
@Default("") String id,
@Default("") String name,
- @Default([]) List tokens,
+ @Default([]) List tokens,
}) = _TokenGroupDto;
String get logoUrl => "${AppEnvironment.current.apiUrl}/static/group-icons/$id.png";
@@ -21,5 +21,5 @@ sealed class TokenGroupDto with _$TokenGroupDto {
factory TokenGroupDto.fromJson(Map json) => _$TokenGroupDtoFromJson(json);
factory TokenGroupDto.fixture() =>
- TokenGroupDto(id: "usd-stablecoins-group", name: "USD Stablecoins", tokens: [TokenDto.fixture()]);
+ TokenGroupDto(id: "usd-stablecoins-group", name: "USD Stablecoins", tokens: [MultiChainTokenDto.fixture()]);
}
diff --git a/lib/core/dtos/token_list_dto.dart b/lib/core/dtos/token_list_dto.dart
index 7d89bab..02a3fc6 100644
--- a/lib/core/dtos/token_list_dto.dart
+++ b/lib/core/dtos/token_list_dto.dart
@@ -1,5 +1,5 @@
import 'package:freezed_annotation/freezed_annotation.dart';
-import 'package:zup_app/core/dtos/token_dto.dart';
+import 'package:zup_app/core/dtos/multi_chain_token_dto.dart';
import 'package:zup_app/core/dtos/token_group_dto.dart';
part 'token_list_dto.freezed.dart';
@@ -10,17 +10,11 @@ sealed class TokenListDto with _$TokenListDto {
@JsonSerializable(explicitToJson: true)
const factory TokenListDto({
@Default([]) List tokenGroups,
- @Default([]) List popularTokens,
+ @Default([]) List popularTokens,
}) = _TokenListDto;
factory TokenListDto.fromJson(Map json) => _$TokenListDtoFromJson(json);
- factory TokenListDto.fixture() => TokenListDto(
- popularTokens: [
- TokenDto.fixture(),
- ],
- tokenGroups: [
- TokenGroupDto.fixture(),
- ],
- );
+ factory TokenListDto.fixture() =>
+ TokenListDto(popularTokens: [MultiChainTokenDto.fixture()], tokenGroups: [TokenGroupDto.fixture()]);
}
diff --git a/lib/core/dtos/yield_dto.dart b/lib/core/dtos/yield_dto.dart
deleted file mode 100644
index 8d7685c..0000000
--- a/lib/core/dtos/yield_dto.dart
+++ /dev/null
@@ -1,107 +0,0 @@
-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/core/enums/yield_timeframe.dart';
-import 'package:zup_app/core/extensions/num_extension.dart';
-
-part 'yield_dto.freezed.dart';
-part 'yield_dto.g.dart';
-
-@freezed
-sealed class YieldDto with _$YieldDto {
- const YieldDto._();
- @JsonSerializable(explicitToJson: true)
- const factory YieldDto({
- required TokenDto token0,
- required TokenDto token1,
- required String poolAddress,
- required String positionManagerAddress,
- required int tickSpacing,
- required ProtocolDto protocol,
- required int initialFeeTier,
- required int currentFeeTier,
- required int chainId,
- @Default(0) num yield24h,
- @Default(0) num yield7d,
- @Default(0) num yield30d,
- @Default(0) num yield90d,
- @Default(PoolType.unknown) @JsonKey(unknownEnumValue: PoolType.unknown) PoolType poolType,
- @Default("0") String latestTick,
- @Default("0") String latestSqrtPriceX96,
- @Default(0) num totalValueLockedUSD,
- @Default(EthereumConstants.zeroAddress) @JsonKey(name: "hooksAddress") String v4Hooks,
- @Default(EthereumConstants.zeroAddress) @JsonKey(name: "deployerAddress") String deployerAddress,
- @JsonKey(name: "poolManagerAddress") String? v4PoolManager,
- @JsonKey(name: "stateViewAddress") String? v4StateView,
- @JsonKey(name: "permit2Address") String? permit2,
- }) = _YieldDto;
-
- AppNetworks get network => AppNetworks.fromChainId(chainId)!;
-
- bool get isToken0Native => token0.addresses[network.chainId] == EthereumConstants.zeroAddress;
- bool get isToken1Native => token1.addresses[network.chainId] == EthereumConstants.zeroAddress;
-
- int get token0NetworkDecimals => token0.decimals[network.chainId]!;
- int get token1NetworkDecimals => token1.decimals[network.chainId]!;
-
- String timeframedYieldFormatted(YieldTimeFrame yieldTimeFrame) {
- switch (yieldTimeFrame) {
- case YieldTimeFrame.day:
- return yield24h == 0 ? "-" : yield24h.formatPercent;
- case YieldTimeFrame.week:
- return yield7d == 0 ? "-" : yield7d.formatPercent;
- case YieldTimeFrame.month:
- return yield30d == 0 ? "-" : yield30d.formatPercent;
- case YieldTimeFrame.threeMonth:
- return yield90d == 0 ? "-" : yield90d.formatPercent;
- }
- }
-
- num yieldTimeframed(YieldTimeFrame yieldTimeFrame) {
- switch (yieldTimeFrame) {
- case YieldTimeFrame.day:
- return yield24h;
- case YieldTimeFrame.week:
- return yield7d;
- case YieldTimeFrame.month:
- return yield30d;
- case YieldTimeFrame.threeMonth:
- return yield90d;
- }
- }
-
- factory YieldDto.fromJson(Map json) => _$YieldDtoFromJson(json);
-
- factory YieldDto.fixture() => YieldDto(
- initialFeeTier: 0,
- currentFeeTier: 0,
- yield24h: 32.2,
- yield30d: 32.2,
- yield90d: 32.2,
- yield7d: 12,
- latestTick: "1567241",
- positionManagerAddress: "0x5Df2f0aFb5b5bB2Df9D1e9C7b6f5f0DD5f9eD5e0",
- poolAddress: "0x5Df2f0aFb5b5bB2Df9D1e9C7b6f5f0DD5f9eD5e0",
- poolType: PoolType.v3,
- token0: TokenDto.fixture().copyWith(
- symbol: "USDC",
- decimals: {AppNetworks.sepolia.chainId: 6},
- addresses: {AppNetworks.sepolia.chainId: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"},
- logoUrl:
- "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png",
- ),
- token1: TokenDto.fixture().copyWith(
- symbol: "WETH",
- decimals: {AppNetworks.sepolia.chainId: 18},
- addresses: {AppNetworks.sepolia.chainId: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"},
- logoUrl:
- "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png",
- ),
- tickSpacing: 10,
- protocol: ProtocolDto.fixture(),
- chainId: AppNetworks.sepolia.chainId,
- );
-}
diff --git a/lib/core/dtos/yields_dto.dart b/lib/core/dtos/yields_dto.dart
deleted file mode 100644
index c03d0b8..0000000
--- a/lib/core/dtos/yields_dto.dart
+++ /dev/null
@@ -1,101 +0,0 @@
-import 'package:freezed_annotation/freezed_annotation.dart';
-import 'package:zup_app/core/dtos/pool_search_filters_dto.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';
-import 'package:zup_app/core/enums/protocol_id.dart';
-import 'package:zup_app/core/enums/yield_timeframe.dart';
-
-part 'yields_dto.freezed.dart';
-part 'yields_dto.g.dart';
-
-@freezed
-sealed class YieldsDto with _$YieldsDto {
- const YieldsDto._();
-
- @JsonSerializable(explicitToJson: true)
- const factory YieldsDto({
- @Default([]) @JsonKey(name: "pools") List pools,
- @Default(PoolSearchFiltersDto()) PoolSearchFiltersDto filters,
- }) = _YieldsDto;
-
- bool get isEmpty => pools.isEmpty;
-
- List get poolsSortedBy24hYield {
- return [...pools]..sort((a, b) => b.yield24h.compareTo(a.yield24h));
- }
-
- List get poolsSortedBy7dYield {
- return [...pools]..sort((a, b) => b.yield7d.compareTo(a.yield7d));
- }
-
- List get poolsSortedBy30dYield {
- return [...pools]..sort((a, b) => b.yield30d.compareTo(a.yield30d));
- }
-
- List get poolsSortedBy90dYield {
- return [...pools]..sort((a, b) => b.yield90d.compareTo(a.yield90d));
- }
-
- List poolsSortedByTimeframe(YieldTimeFrame timeframe) {
- switch (timeframe) {
- case YieldTimeFrame.day:
- return poolsSortedBy24hYield;
- case YieldTimeFrame.week:
- return poolsSortedBy7dYield;
- case YieldTimeFrame.month:
- return poolsSortedBy30dYield;
- case YieldTimeFrame.threeMonth:
- return poolsSortedBy90dYield;
- }
- }
-
- factory YieldsDto.fromJson(Map json) => _$YieldsDtoFromJson(json);
-
- factory YieldsDto.empty() => const YieldsDto(pools: []);
-
- factory YieldsDto.fixture() => YieldsDto(
- filters: PoolSearchFiltersDto.fixture(),
- pools: [
- YieldDto(
- latestTick: "637812562",
- latestSqrtPriceX96: "5240418162556390792557189",
- positionManagerAddress: "0x06eFdBFf2a14a7c8E15944D1F4A48F9F95F663A4",
- poolType: PoolType.v3,
- yield7d: 21,
- token0: const TokenDto(
- addresses: {1: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 "},
- decimals: {1: 18},
- name: "Wrapped Ether",
- symbol: "WETH",
- logoUrl:
- "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/scroll/assets/0x5300000000000000000000000000000000000004/logo.png",
- ),
- token1: const TokenDto(
- addresses: {1: "0xdAC17F958D2ee523a2206206994597C13D831ec7"},
- decimals: {1: 6},
- logoUrl:
- "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/scroll/assets/0x06eFdBFf2a14a7c8E15944D1F4A48F9F95F663A4/logo.png",
- name: "USDC",
- symbol: "USDC",
- ),
- chainId: 1,
- poolAddress: "0x4040CE732c1A538A4Ac3157FDC35179D73ea76cd",
- tickSpacing: 10,
- yield24h: 1732.42,
- yield30d: 765.61,
- yield90d: 2022.99,
- totalValueLockedUSD: 65434567890.21,
- initialFeeTier: 500,
- currentFeeTier: 500,
- protocol: ProtocolDto(
- id: ProtocolId.pancakeSwapInfinityCL,
- rawId: ProtocolId.pancakeSwapInfinityCL.toRawJsonValue,
- name: "PancakeSwap",
- logo: "https://raw.githubusercontent.com/trustwallet/assets/master/dapps/exchange.pancakeswap.finance.png",
- ),
- ),
- ],
- );
-}
diff --git a/lib/core/enums/networks.dart b/lib/core/enums/networks.dart
index b50bacd..9daa081 100644
--- a/lib/core/enums/networks.dart
+++ b/lib/core/enums/networks.dart
@@ -155,10 +155,39 @@ enum AppNetworks {
// bnb => "https://bsc-rpc.publicnode.com"
};
+ String get websiteUrl => switch (this) {
+ allNetworks => throw UnimplementedError("allNetworks is not a valid network"),
+ sepolia => "https://ethereum.org",
+ mainnet => "https://ethereum.org",
+ scroll => "https://scroll.io",
+ base => "https://base.org",
+ unichain => "https://www.unichain.org",
+ hyperEvm => "https://hyperfoundation.org",
+ plasma => "https://www.plasma.to",
+ };
+
+ String? get dexscreenerUrl => switch (this) {
+ allNetworks => null,
+ sepolia => null,
+ mainnet => "https://dexscreener.com/ethereum",
+ scroll => "https://dexscreener.com/scroll",
+ base => "https://dexscreener.com/base",
+ unichain => "https://dexscreener.com/unichain",
+ hyperEvm => "https://dexscreener.com/hyperevm",
+ plasma => "https://dexscreener.com/plasma",
+ };
+
Future openTx(String txHash) async {
final url = "${chainInfo.blockExplorerUrls?.first}/tx/$txHash";
if (!await canLaunchUrl(Uri.parse(url))) return;
await launchUrl(Uri.parse(url));
}
+
+ Future openAddress(String address) async {
+ final url = "${chainInfo.blockExplorerUrls?.first}/address/$address";
+ if (!await canLaunchUrl(Uri.parse(url))) return;
+
+ await launchUrl(Uri.parse(url));
+ }
}
diff --git a/lib/core/enums/pool_data_timeframe.dart b/lib/core/enums/pool_data_timeframe.dart
new file mode 100644
index 0000000..8a3e29f
--- /dev/null
+++ b/lib/core/enums/pool_data_timeframe.dart
@@ -0,0 +1,37 @@
+import 'package:collection/collection.dart';
+import 'package:flutter/material.dart';
+import 'package:zup_app/l10n/gen/app_localizations.dart';
+
+enum PoolDataTimeframe {
+ day,
+ week,
+ month,
+ threeMonth;
+
+ static PoolDataTimeframe? fromValue(String name) {
+ return PoolDataTimeframe.values.firstWhereOrNull((e) => e.name == name);
+ }
+}
+
+extension YieldTimeFrameExtension on PoolDataTimeframe {
+ bool get isDay => this == PoolDataTimeframe.day;
+ bool get isWeek => this == PoolDataTimeframe.week;
+ bool get isMonth => this == PoolDataTimeframe.month;
+ bool get isThreeMonth => this == PoolDataTimeframe.threeMonth;
+
+ String label(BuildContext context) {
+ return switch (this) {
+ PoolDataTimeframe.day => S.of(context).twentyFourHours,
+ PoolDataTimeframe.week => S.of(context).week,
+ PoolDataTimeframe.month => S.of(context).month,
+ PoolDataTimeframe.threeMonth => S.of(context).threeMonths,
+ };
+ }
+
+ String compactDaysLabel(BuildContext context) => switch (this) {
+ PoolDataTimeframe.day => S.of(context).twentyFourHoursCompact,
+ PoolDataTimeframe.week => S.of(context).weekCompact,
+ PoolDataTimeframe.month => S.of(context).monthCompact,
+ PoolDataTimeframe.threeMonth => S.of(context).threeMonthsCompact,
+ };
+}
diff --git a/lib/core/enums/pool_type.dart b/lib/core/enums/pool_type.dart
index 9690241..297e002 100644
--- a/lib/core/enums/pool_type.dart
+++ b/lib/core/enums/pool_type.dart
@@ -1,4 +1,6 @@
+import 'package:flutter/widgets.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
+import 'package:zup_app/l10n/gen/app_localizations.dart';
enum PoolType {
@JsonValue("V3")
@@ -11,8 +13,14 @@ enum PoolType {
bool get isV4 => this == PoolType.v4;
String get label => switch (this) {
- PoolType.v3 => "V3",
- PoolType.v4 => "V4",
- PoolType.unknown => "Unknown",
- };
+ PoolType.v3 => "V3",
+ PoolType.v4 => "V4",
+ PoolType.unknown => "Unknown",
+ };
+
+ String description(BuildContext context) => switch (this) {
+ PoolType.v3 => S.of(context).poolTypeV3Description,
+ PoolType.v4 => S.of(context).poolTypeV4Description,
+ PoolType.unknown => "",
+ };
}
diff --git a/lib/core/enums/protocol_id.dart b/lib/core/enums/protocol_id.dart
index 4edb50d..8a054e8 100644
--- a/lib/core/enums/protocol_id.dart
+++ b/lib/core/enums/protocol_id.dart
@@ -14,6 +14,8 @@ enum ProtocolId {
gliquidV3,
@JsonValue("kittenswap-v3")
kittenswapV3,
+ @JsonValue("hx-finance-algebra")
+ hxFinanceAlgebra,
unknown;
bool get isPancakeSwapInfinityCL => this == ProtocolId.pancakeSwapInfinityCL;
@@ -21,7 +23,9 @@ enum ProtocolId {
(this == ProtocolId.aerodromeSlipstream || this == ProtocolId.velodromeSlipstream);
bool get isGLiquidV3 => this == ProtocolId.gliquidV3;
bool get isKittenswapV3 => this == ProtocolId.kittenswapV3;
- bool get isAlgebra1_2 => isGLiquidV3 || isKittenswapV3;
+ bool get isHxFinanceAlgebra => this == ProtocolId.hxFinanceAlgebra;
+
+ bool get isAlgebra1_2 => isGLiquidV3 || isKittenswapV3 || isHxFinanceAlgebra;
String get toRawJsonValue => _$ProtocolIdEnumMap[this]!;
}
diff --git a/lib/core/enums/yield_timeframe.dart b/lib/core/enums/yield_timeframe.dart
deleted file mode 100644
index bcc0c73..0000000
--- a/lib/core/enums/yield_timeframe.dart
+++ /dev/null
@@ -1,37 +0,0 @@
-import 'package:collection/collection.dart';
-import 'package:flutter/material.dart';
-import 'package:zup_app/l10n/gen/app_localizations.dart';
-
-enum YieldTimeFrame {
- day,
- week,
- month,
- threeMonth;
-
- static YieldTimeFrame? fromValue(String name) {
- return YieldTimeFrame.values.firstWhereOrNull((e) => e.name == name);
- }
-}
-
-extension YieldTimeFrameExtension on YieldTimeFrame {
- bool get isDay => this == YieldTimeFrame.day;
- bool get isWeek => this == YieldTimeFrame.week;
- bool get isMonth => this == YieldTimeFrame.month;
- bool get isThreeMonth => this == YieldTimeFrame.threeMonth;
-
- String label(BuildContext context) {
- return switch (this) {
- YieldTimeFrame.day => S.of(context).twentyFourHours,
- YieldTimeFrame.week => S.of(context).week,
- YieldTimeFrame.month => S.of(context).month,
- YieldTimeFrame.threeMonth => S.of(context).threeMonths,
- };
- }
-
- String compactDaysLabel(BuildContext context) => switch (this) {
- YieldTimeFrame.day => S.of(context).twentyFourHoursCompact,
- YieldTimeFrame.week => S.of(context).weekCompact,
- YieldTimeFrame.month => S.of(context).monthCompact,
- YieldTimeFrame.threeMonth => S.of(context).threeMonthsCompact,
- };
-}
diff --git a/lib/core/extensions/list_extension.dart b/lib/core/extensions/list_extension.dart
deleted file mode 100644
index d35d0a0..0000000
--- a/lib/core/extensions/list_extension.dart
+++ /dev/null
@@ -1,10 +0,0 @@
-import 'package:zup_app/core/dtos/yield_dto.dart';
-
-extension YieldDTOListExtension on List {
- YieldDto get bestYield {
- final yieldListClone = List.from(this);
-
- yieldListClone.sort((a, b) => b.yearlyYield.compareTo(a.yearlyYield));
- return yieldListClone.first;
- }
-}
diff --git a/lib/core/extensions/num_extension.dart b/lib/core/extensions/num_extension.dart
index 66c9642..e5a7b1f 100644
--- a/lib/core/extensions/num_extension.dart
+++ b/lib/core/extensions/num_extension.dart
@@ -44,10 +44,10 @@ extension NumExtension on num {
final maxWithoutMoreThan = maxBeforeMoreThan ?? pow(10, 12) * 999;
if (useMoreThan && this > maxWithoutMoreThan) {
- return NumberFormat.compactCurrency(decimalDigits: decimals, name: ">").format(maxWithoutMoreThan);
+ return NumberFormat.compactCurrency(decimalDigits: 2, name: ">").format(maxWithoutMoreThan);
}
- return NumberFormat.compactCurrency(decimalDigits: decimals, name: isUSD ? "USD " : "").format(this);
+ return NumberFormat.compactCurrency(decimalDigits: 2, name: isUSD ? r"$" : "").format(this);
}
String maybeFormatCompactCurrency({
@@ -67,8 +67,9 @@ extension NumExtension on num {
return formatCurrency(isUSD: isUSD, useLessThan: useLessThan);
}
- String get formatPercent {
+ String get formatRoundingPercent {
int decimalsDigits = 0;
+
if (decimals >= 1 && toString().split(".")[1] != "0") decimalsDigits = 1;
return "${NumberFormat.decimalPatternDigits(decimalDigits: decimalsDigits).format(this)}%";
diff --git a/lib/core/extensions/time_ago_extension.dart b/lib/core/extensions/time_ago_extension.dart
new file mode 100644
index 0000000..90432b6
--- /dev/null
+++ b/lib/core/extensions/time_ago_extension.dart
@@ -0,0 +1,18 @@
+import 'package:flutter/widgets.dart';
+import 'package:zup_app/l10n/gen/app_localizations.dart';
+import 'package:zup_core/enums/time_unit.dart';
+import 'package:zup_core/objects/time_ago.dart';
+
+extension TimeAgoExtension on TimeAgo {
+ String formattedTimeAgo(BuildContext context) {
+ return switch (amountTimeUnit) {
+ TimeUnit.milliseconds => S.of(context).xMillisecondsAgo(milliseconds: amount),
+ TimeUnit.seconds => S.of(context).xSecondsAgo(seconds: amount),
+ TimeUnit.minutes => S.of(context).xMinutesAndSecondsAgo(minutes: amount, seconds: remainder),
+ TimeUnit.hours => S.of(context).xHoursAndMinutesAgo(hours: amount, minutes: remainder),
+ TimeUnit.days => S.of(context).xDaysAndHoursAgo(days: amount, hours: remainder),
+ TimeUnit.months => S.of(context).xMonthsAndDaysAgo(months: amount, days: remainder),
+ TimeUnit.years => S.of(context).xYearsAndMonthsAgo(years: amount, months: remainder),
+ };
+ }
+}
diff --git a/lib/core/injections.dart b/lib/core/injections.dart
index 87d9b79..f2239ac 100644
--- a/lib/core/injections.dart
+++ b/lib/core/injections.dart
@@ -23,7 +23,6 @@ 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/protocol_repository.dart';
import 'package:zup_app/core/repositories/tokens_repository.dart';
import 'package:zup_app/core/repositories/yield_repository.dart';
@@ -32,8 +31,8 @@ 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/token_selector_modal/token_selector_modal_cubit.dart';
-import 'package:zup_app/widgets/zup_cached_image.dart';
import 'package:zup_core/zup_core.dart';
+import 'package:zup_ui_kit/zup_ui_kit.dart';
final inject = GetIt.instance;
@@ -72,8 +71,7 @@ Future setupInjections() async {
inject.registerLazySingleton(() => ZupNavigator());
inject.registerLazySingleton(() => Wallet.shared);
inject.registerLazySingleton(() => AppCubit(inject(), inject()));
- inject.registerLazySingleton(() => PositionsRepository());
- inject.registerLazySingleton(() => ZupCachedImage());
+ inject.registerLazySingleton(() => ZupNetworkImage());
inject.registerLazySingleton(() => true, instanceName: InjectInstanceNames.infinityAnimationAutoPlay);
inject.registerLazySingleton(
() => TokensRepository(inject(instanceName: InjectInstanceNames.zupAPIDio)),
diff --git a/lib/core/pool_service.dart b/lib/core/pool_service.dart
index 2cdb4f1..6dee5b1 100644
--- a/lib/core/pool_service.dart
+++ b/lib/core/pool_service.dart
@@ -15,7 +15,7 @@ import 'package:zup_app/core/concentrated_liquidity_utils/cl_pool_conversors_mix
import 'package:zup_app/core/concentrated_liquidity_utils/cl_pool_liquidity_calculations_mixin.dart';
import 'package:zup_app/core/concentrated_liquidity_utils/cl_sqrt_price_math_mixin.dart';
import 'package:zup_app/core/concentrated_liquidity_utils/v4_pool_constants.dart';
-import 'package:zup_app/core/dtos/yield_dto.dart';
+import 'package:zup_app/core/dtos/liquidity_pool_dto.dart';
import 'package:zup_app/core/slippage.dart';
class PoolService with CLPoolLiquidityCalculationsMixin, CLPoolConversorsMixin, CLSqrtPriceMath {
@@ -45,7 +45,7 @@ class PoolService with CLPoolLiquidityCalculationsMixin, CLPoolConversorsMixin,
this._algebra121PositionManager,
);
- Future getPoolTick(YieldDto forYield) async {
+ Future getPoolTick(LiquidityPoolDto forYield) async {
if (forYield.protocol.id.isAlgebra1_2) {
final algebraPool = _algebra121Pool.fromRpcProvider(
contractAddress: forYield.poolAddress,
@@ -90,7 +90,7 @@ class PoolService with CLPoolLiquidityCalculationsMixin, CLPoolConversorsMixin,
return (await uniswapV3Pool.slot0()).tick;
}
- Future getSqrtPriceX96(YieldDto forYield) async {
+ Future getSqrtPriceX96(LiquidityPoolDto forYield) async {
if (forYield.protocol.id.isPancakeSwapInfinityCL) {
final pancakeSwapInfinityCLPoolManagerContract = _pancakeSwapInfinityClPoolManager.fromRpcProvider(
contractAddress: forYield.v4PoolManager!,
@@ -140,7 +140,7 @@ class PoolService with CLPoolLiquidityCalculationsMixin, CLPoolConversorsMixin,
}
Future sendV3PoolDepositTransaction(
- YieldDto depositOnYield,
+ LiquidityPoolDto depositOnYield,
Signer signer, {
required BigInt amount0Desired,
required BigInt amount1Desired,
@@ -250,8 +250,8 @@ class PoolService with CLPoolLiquidityCalculationsMixin, CLPoolConversorsMixin,
tickLower: tickLower,
tickUpper: tickUpper,
fee: BigInt.from(depositOnYield.initialFeeTier),
- token0: depositOnYield.token0.addresses[depositOnYield.network.chainId]!,
- token1: depositOnYield.token1.addresses[depositOnYield.network.chainId]!,
+ token0: depositOnYield.token0.address,
+ token1: depositOnYield.token1.address,
),
);
}.call();
@@ -260,7 +260,7 @@ class PoolService with CLPoolLiquidityCalculationsMixin, CLPoolConversorsMixin,
}
Future sendV4PoolDepositTransaction(
- YieldDto depositOnYield,
+ LiquidityPoolDto depositOnYield,
Signer signer, {
required Duration deadline,
required BigInt tickLower,
@@ -337,11 +337,11 @@ class PoolService with CLPoolLiquidityCalculationsMixin, CLPoolConversorsMixin,
],
[
[
- depositOnYield.token0.addresses[depositOnYield.network.chainId]!,
- depositOnYield.token1.addresses[depositOnYield.network.chainId]!,
+ depositOnYield.token0.address,
+ depositOnYield.token1.address,
BigInt.from(depositOnYield.initialFeeTier),
BigInt.from(depositOnYield.tickSpacing),
- depositOnYield.v4Hooks,
+ depositOnYield.hook?.address ?? EthereumConstants.zeroAddress,
],
tickLower,
tickUpper,
@@ -355,10 +355,7 @@ class PoolService with CLPoolLiquidityCalculationsMixin, CLPoolConversorsMixin,
final settlePairActionParams = _ethereumAbiCoder.encode(
["address", "address"],
- [
- depositOnYield.token0.addresses[depositOnYield.network.chainId]!,
- depositOnYield.token1.addresses[depositOnYield.network.chainId]!,
- ],
+ [depositOnYield.token0.address, depositOnYield.token1.address],
);
final sweepActionParams = isNativeDeposit
@@ -387,7 +384,7 @@ class PoolService with CLPoolLiquidityCalculationsMixin, CLPoolConversorsMixin,
}
Future _sendV4PoolDepositTransactionForPancakeSwap(
- YieldDto depositOnYield,
+ LiquidityPoolDto depositOnYield,
Signer signer, {
required Duration deadline,
required BigInt tickLower,
@@ -425,9 +422,9 @@ class PoolService with CLPoolLiquidityCalculationsMixin, CLPoolConversorsMixin,
],
[
[
- depositOnYield.token0.addresses[depositOnYield.network.chainId]!,
- depositOnYield.token1.addresses[depositOnYield.network.chainId]!,
- depositOnYield.v4Hooks,
+ depositOnYield.token0.address,
+ depositOnYield.token1.address,
+ depositOnYield.hook?.address ?? EthereumConstants.zeroAddress,
depositOnYield.v4PoolManager,
depositOnYield.initialFeeTier,
await _getPancakeSwapInfinityPoolBytesParameters(depositOnYield),
@@ -442,15 +439,9 @@ class PoolService with CLPoolLiquidityCalculationsMixin, CLPoolConversorsMixin,
],
);
- final closeCurrency0ActionParams = _ethereumAbiCoder.encode(
- ["address"],
- [depositOnYield.token0.addresses[depositOnYield.network.chainId]!],
- );
+ final closeCurrency0ActionParams = _ethereumAbiCoder.encode(["address"], [depositOnYield.token0.address]);
- final closeCurrency1ActionParams = _ethereumAbiCoder.encode(
- ["address"],
- [depositOnYield.token1.addresses[depositOnYield.network.chainId]!],
- );
+ final closeCurrency1ActionParams = _ethereumAbiCoder.encode(["address"], [depositOnYield.token1.address]);
final pancakeSwapV4PositionManagerContract = _pancakeSwapInfinityClPositionManager.fromSigner(
contractAddress: depositOnYield.positionManagerAddress,
@@ -473,7 +464,7 @@ class PoolService with CLPoolLiquidityCalculationsMixin, CLPoolConversorsMixin,
}
Future _sendV3DepositTransactionForAlgebra121(
- YieldDto depositOnYield,
+ LiquidityPoolDto depositOnYield,
Signer signer, {
required BigInt amount0Desired,
required BigInt amount1Desired,
@@ -527,8 +518,8 @@ class PoolService with CLPoolLiquidityCalculationsMixin, CLPoolConversorsMixin,
tickLower: tickLower,
tickUpper: tickUpper,
deployer: depositOnYield.deployerAddress,
- token0: depositOnYield.token0.addresses[depositOnYield.network.chainId]!,
- token1: depositOnYield.token1.addresses[depositOnYield.network.chainId]!,
+ token0: depositOnYield.token0.address,
+ token1: depositOnYield.token1.address,
),
);
}.call();
@@ -537,7 +528,7 @@ class PoolService with CLPoolLiquidityCalculationsMixin, CLPoolConversorsMixin,
}
Future _sendV3DepositTransactionForSlipstream(
- YieldDto depositOnYield,
+ LiquidityPoolDto depositOnYield,
Signer signer, {
required BigInt amount0Desired,
required BigInt amount1Desired,
@@ -593,8 +584,8 @@ class PoolService with CLPoolLiquidityCalculationsMixin, CLPoolConversorsMixin,
tickUpper: tickUpper,
tickSpacing: BigInt.from(depositOnYield.tickSpacing),
sqrtPriceX96: BigInt.from(0),
- token0: depositOnYield.token0.addresses[depositOnYield.network.chainId]!,
- token1: depositOnYield.token1.addresses[depositOnYield.network.chainId]!,
+ token0: depositOnYield.token0.address,
+ token1: depositOnYield.token1.address,
),
);
}.call();
@@ -602,17 +593,17 @@ class PoolService with CLPoolLiquidityCalculationsMixin, CLPoolConversorsMixin,
return tx;
}
- String _getNativeV3PoolToken0Address(YieldDto forYield) {
+ String _getNativeV3PoolToken0Address(LiquidityPoolDto forYield) {
if (forYield.isToken0Native) return forYield.network.wrappedNativeTokenAddress;
- return forYield.token0.addresses[forYield.network.chainId]!;
+ return forYield.token0.address;
}
- String _getNativeV3PoolToken1Address(YieldDto forYield) {
+ String _getNativeV3PoolToken1Address(LiquidityPoolDto forYield) {
if (forYield.isToken1Native) return forYield.network.wrappedNativeTokenAddress;
- return forYield.token1.addresses[forYield.network.chainId]!;
+ return forYield.token1.address;
}
- Future _getPancakeSwapInfinityPoolBytesParameters(YieldDto forYield) async {
+ Future _getPancakeSwapInfinityPoolBytesParameters(LiquidityPoolDto forYield) async {
final contract = _pancakeSwapInfinityClPoolManager.fromRpcProvider(
contractAddress: forYield.v4PoolManager!,
rpcUrl: forYield.network.rpcUrl,
diff --git a/lib/core/repositories/positions_repository.dart b/lib/core/repositories/positions_repository.dart
deleted file mode 100644
index 7ce371e..0000000
--- a/lib/core/repositories/positions_repository.dart
+++ /dev/null
@@ -1,8 +0,0 @@
-import 'package:zup_app/core/dtos/position_dto.dart';
-
-class PositionsRepository {
- Future> fetchUserPositions() async {
- await Future.delayed(const Duration(seconds: 2));
- return [];
- }
-}
diff --git a/lib/core/repositories/tokens_repository.dart b/lib/core/repositories/tokens_repository.dart
index cb28103..728fd4b 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/multi_chain_token_dto.dart';
+import 'package:zup_app/core/dtos/single_chain_token_dto.dart';
import 'package:zup_app/core/dtos/token_list_dto.dart';
import 'package:zup_app/core/dtos/token_price_dto.dart';
import 'package:zup_app/core/enums/networks.dart';
@@ -20,28 +21,44 @@ class TokensRepository {
return TokenListDto.fromJson(request.data);
}
- Future> searchToken(String query, AppNetworks network) async {
- if (_searchTokenLastCancelToken != null) {
- _searchTokenLastCancelToken!.cancel();
- }
+ Future> searchToken(String query, AppNetworks network) async {
+ if (_searchTokenLastCancelToken != null) _searchTokenLastCancelToken!.cancel();
_searchTokenLastCancelToken = CancelToken();
- final response = await _zupAPIDio.get(
- "/tokens/search",
+ if (network.isAllNetworks) {
+ final allNetworksResponse = await _zupAPIDio.get(
+ "/tokens/search/all",
+ cancelToken: _searchTokenLastCancelToken,
+ queryParameters: {"query": query},
+ );
+
+ _searchTokenLastCancelToken = null;
+ return (allNetworksResponse.data as List).map((token) => MultiChainTokenDto.fromJson(token)).toList();
+ }
+
+ final singleNetworkResponse = await _zupAPIDio.get(
+ "/tokens/search/${network.chainId}",
cancelToken: _searchTokenLastCancelToken,
- queryParameters: {if (!network.isAllNetworks) "chainId": network.chainId, "query": query},
+ queryParameters: {"query": query},
);
_searchTokenLastCancelToken = null;
- return (response.data as List).map((token) => TokenDto.fromJson(token)).toList();
+ return (singleNetworkResponse.data as List).map((token) {
+ final singleChainToken = SingleChainTokenDto.fromJson(token);
+
+ return MultiChainTokenDto(
+ addresses: {network.chainId: singleChainToken.address},
+ decimals: {network.chainId: singleChainToken.decimals},
+ logoUrl: singleChainToken.logoUrl,
+ name: singleChainToken.name,
+ symbol: singleChainToken.symbol,
+ );
+ }).toList();
}
Future getTokenPrice(String address, AppNetworks network) async {
- final response = await _zupAPIDio.get(
- "/tokens/price",
- queryParameters: {"address": address, "chainId": network.chainId},
- );
+ final response = await _zupAPIDio.get("/tokens/$address/${network.chainId}/price");
return TokenPriceDto.fromJson(response.data);
}
diff --git a/lib/core/repositories/yield_repository.dart b/lib/core/repositories/yield_repository.dart
index 508a705..2f89c1b 100644
--- a/lib/core/repositories/yield_repository.dart
+++ b/lib/core/repositories/yield_repository.dart
@@ -1,7 +1,7 @@
import 'package:dio/dio.dart';
+import 'package:zup_app/core/dtos/liquidity_pool_dto.dart';
+import 'package:zup_app/core/dtos/liquidity_pools_search_result_dto.dart';
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';
class YieldRepository {
@@ -9,7 +9,7 @@ class YieldRepository {
final Dio _zupAPIDio;
- Future getSingleNetworkYield({
+ Future getSingleNetworkYield({
required String? token0Address,
required String? token1Address,
required String? group0Id,
@@ -27,18 +27,14 @@ class YieldRepository {
if (group1Id != null) "group1Id": group1Id,
},
data: {
- "filters": {
- "minTvlUsd": searchSettings.minLiquidityUSD,
- "blockedProtocols": blockedProtocolIds,
- "allowedPoolTypes": [if (searchSettings.allowV3Search) "V3", if (searchSettings.allowV4Search) "V4"],
- },
+ "filters": {"minimumTvlUsd": searchSettings.minLiquidityUSD, "blockedProtocols": blockedProtocolIds},
},
);
- return YieldsDto.fromJson(response.data);
+ return LiquidityPoolsSearchResultDto.fromJson(response.data);
}
- Future getAllNetworksYield({
+ Future getAllNetworksYield({
required String? token0InternalId,
required String? token1InternalId,
required String? group0Id,
@@ -56,19 +52,15 @@ class YieldRepository {
if (group1Id != null) "group1Id": group1Id,
},
data: {
- "filters": {
- "minTvlUsd": searchSettings.minLiquidityUSD,
- "testnetMode": testnetMode,
- "blockedProtocols": blockedProtocolIds,
- "allowedPoolTypes": [if (searchSettings.allowV3Search) "V3", if (searchSettings.allowV4Search) "V4"],
- },
+ "filters": {"minimumTvlUsd": searchSettings.minLiquidityUSD, "blockedProtocols": blockedProtocolIds},
+ "config": {"testnetMode": testnetMode},
},
);
- return YieldsDto.fromJson(response.data);
+ return LiquidityPoolsSearchResultDto.fromJson(response.data);
}
- Future getPoolInfo({
+ Future getPoolInfo({
required String poolAddress,
required AppNetworks poolNetwork,
bool parseWrappedToNative = true,
@@ -78,6 +70,6 @@ class YieldRepository {
queryParameters: {"parseWrappedToNative": parseWrappedToNative},
);
- return YieldDto.fromJson(response.data);
+ return LiquidityPoolDto.fromJson(response.data);
}
}
diff --git a/lib/core/zup_analytics.dart b/lib/core/zup_analytics.dart
index 2d56766..269b9b8 100644
--- a/lib/core/zup_analytics.dart
+++ b/lib/core/zup_analytics.dart
@@ -1,6 +1,6 @@
import 'package:firebase_analytics/firebase_analytics.dart';
import 'package:flutter/material.dart';
-import 'package:zup_app/core/dtos/yield_dto.dart';
+import 'package:zup_app/core/dtos/liquidity_pool_dto.dart';
import 'package:zup_app/core/repositories/tokens_repository.dart';
class ZupAnalytics {
@@ -11,12 +11,7 @@ class ZupAnalytics {
Future _log(String name, Map? parameters) async {
try {
- await firebaseAnalytics.logEvent(
- name: name,
- parameters: {
- ...?parameters,
- },
- );
+ await firebaseAnalytics.logEvent(name: name, parameters: {...?parameters});
debugPrint("ZupAnalytics: Event $name logged with parameters $parameters");
} catch (e, stacktraces) {
@@ -27,41 +22,32 @@ class ZupAnalytics {
}
Future logDeposit({
- required YieldDto depositedYield,
+ required LiquidityPoolDto depositedYield,
required num amount0Formatted,
required num amount1Formatted,
required String walletAddress,
}) async {
try {
final tokensPrice = await Future.wait([
- tokensRepository.getTokenPrice(
- depositedYield.token0.addresses[depositedYield.network.chainId]!,
- depositedYield.network,
- ),
- tokensRepository.getTokenPrice(
- depositedYield.token1.addresses[depositedYield.network.chainId]!,
- depositedYield.network,
- )
+ tokensRepository.getTokenPrice(depositedYield.token0.address, depositedYield.network),
+ tokensRepository.getTokenPrice(depositedYield.token1.address, depositedYield.network),
]);
final amount0UsdDeposited = amount0Formatted * tokensPrice[0].usdPrice;
final amount1UsdDeposited = amount1Formatted * tokensPrice[1].usdPrice;
final usdAmountDeposited = amount0UsdDeposited + amount1UsdDeposited;
- await _log(
- "user_deposited",
- {
- "token0_address": "hex:${depositedYield.token0.addresses[depositedYield.network.chainId]!}",
- "token1_address": "hex:${depositedYield.token1.addresses[depositedYield.network.chainId]!}",
- "amount0": amount0Formatted,
- "amount1": amount1Formatted,
- "amount_usd": usdAmountDeposited,
- "network": depositedYield.network.label,
- "wallet_address": "hex:$walletAddress",
- "pool_address": "hex:${depositedYield.poolAddress}",
- "protocol_name": depositedYield.protocol.name,
- },
- );
+ await _log("user_deposited", {
+ "token0_address": "hex:${depositedYield.token0.address}",
+ "token1_address": "hex:${depositedYield.token1.address}",
+ "amount0": amount0Formatted,
+ "amount1": amount1Formatted,
+ "amount_usd": usdAmountDeposited,
+ "network": depositedYield.network.label,
+ "wallet_address": "hex:$walletAddress",
+ "pool_address": "hex:${depositedYield.poolAddress}",
+ "protocol_name": depositedYield.protocol.name,
+ });
} catch (e) {
// ignore
}
@@ -74,15 +60,12 @@ class ZupAnalytics {
required String? group1,
required String network,
}) async {
- await _log(
- "user_searched_yields",
- {
- "token0": "id:$token0",
- "token1": "id:$token1",
- "group0": "id:$group0",
- "group1": "id:$group1",
- "network": network,
- },
- );
+ await _log("user_searched_yields", {
+ "token0": "id:$token0",
+ "token1": "id:$token1",
+ "group0": "id:$group0",
+ "group1": "id:$group1",
+ "network": network,
+ });
}
}
diff --git a/lib/core/zup_navigator.dart b/lib/core/zup_navigator.dart
index 5750bfc..b46b687 100644
--- a/lib/core/zup_navigator.dart
+++ b/lib/core/zup_navigator.dart
@@ -1,9 +1,9 @@
import 'package:flutter/material.dart';
import 'package:routefly/routefly.dart';
import 'package:zup_app/core/dtos/deposit_page_arguments_dto.dart';
-import 'package:zup_app/core/dtos/yield_dto.dart';
+import 'package:zup_app/core/dtos/liquidity_pool_dto.dart';
import 'package:zup_app/core/enums/networks.dart';
-import 'package:zup_app/core/enums/yield_timeframe.dart';
+import 'package:zup_app/core/enums/pool_data_timeframe.dart';
import 'package:zup_app/core/enums/zup_navigator_paths.dart';
import 'package:zup_app/core/zup_route_params_names.dart';
@@ -50,8 +50,8 @@ class ZupNavigator {
Future navigateToInitial() async => await Routefly.navigate(ZupNavigatorPaths.initial.path);
Future navigateToDeposit({
- required YieldDto yieldPool,
- required YieldTimeFrame selectedTimeframe,
+ required LiquidityPoolDto yieldPool,
+ required PoolDataTimeframe selectedTimeframe,
required bool parseWrappedToNative,
}) async {
final depositPathParamNames = ZupNavigatorPaths.deposit.routeParamsNames();
diff --git a/lib/l10n/en.arb b/lib/l10n/en.arb
index 21b217c..83872bf 100644
--- a/lib/l10n/en.arb
+++ b/lib/l10n/en.arb
@@ -27,18 +27,14 @@
"createPageShowMeTheMoney": "Show me the money!",
"createPageTitle": "New Position",
"dark": "Dark",
- "depositPageLoadingTitle": "Getting the pool ready for you...",
- "@depositPageLoadingTitle": {
- "description": "Used by the deposit page as title for the loading state, before the pool is ready for the user to deposit"
+ "depositPageBackToNewPositionButtonTitle": "Search other pools",
+ "@depositPageBackToNewPositionButtonTitle": {
+ "description": "Used by the deposit page as title for the back button to navigate back to the new position page, if the previous page isn't to select a yield, like paste the url to deposit directly"
},
"depositPageBackToYieldsButtonTitle": "Select Yield",
"@depositPageBackToYieldsButtonTitle": {
"description": "Used by the deposit page as title for the back button to navigate back to the yields page, if the previous page was to select a yield"
},
- "depositPageBackToNewPositionButtonTitle": "Search other pools",
- "@depositPageBackToNewPositionButtonTitle": {
- "description": "Used by the deposit page as title for the back button to navigate back to the new position page, if the previous page isn't to select a yield, like paste the url to deposit directly"
- },
"depositPageBestYieldsIn": "Best Yields in",
"depositPageDeposit": "Deposit",
"depositPageDepositSectionTitle": "Deposit",
@@ -95,6 +91,10 @@
"depositPageLoadingStep3Title": "Yield optimizer at work…",
"depositPageLoadingStep4Description": "Hang tight, we're filtering and organizing the best pools for you",
"depositPageLoadingStep4Title": "Organizing the best pools for you…",
+ "depositPageLoadingTitle": "Getting the pool ready for you...",
+ "@depositPageLoadingTitle": {
+ "description": "Used by the deposit page as title for the loading state, before the pool is ready for the user to deposit"
+ },
"depositPageMaxRangeOutOfRangeWarningText": "You will not earn fees until the market price move down into your range",
"depositPageMinLiquiditySearchAlert": "You’ve set the search to only show pools with more than {minLiquidity}.",
"@depositPageMinLiquiditySearchAlert": {
@@ -191,6 +191,243 @@
"monthCompact": "30d",
"newPosition": "New Position",
"noResultsFor": "No results for",
+ "poolInfoModalAboutPageCopyHook": "Copy Hook Address",
+ "@poolInfoModalAboutPageCopyHook": {
+ "description": "Used by the pool info modal to copy the pool hook address if any",
+ "type": "text"
+ },
+ "poolInfoModalAboutPageCopyToken": "Copy Token Address",
+ "@poolInfoModalAboutPageCopyToken": {
+ "description": "Used by the pool info modal to copy one of the pool tokens address",
+ "type": "text"
+ },
+ "poolInfoModalAboutPageFee": "Fee",
+ "@poolInfoModalAboutPageFee": {
+ "description": "Used by the pool info modal as a title for the fee tier field in the about page"
+ },
+ "poolInfoModalAboutPageFeeDescription": "This pool takes {isDynamicFee, select, true {a dynamic} other {{formattedFee}}} fee on each trade and distributes it to the liquidity providers",
+ "@poolInfoModalAboutPageFeeDescription": {
+ "placeholders": {
+ "isDynamicFee": {
+ "type": "String"
+ },
+ "formattedFee": {
+ "type": "String"
+ }
+ }
+ },
+ "poolInfoModalAboutPageHookCopied": "Copied!",
+ "@poolInfoModalAboutPageHookCopied": {
+ "description": "Used by the pool info modal to notify the user that the pool hook address was copied",
+ "type": "text"
+ },
+ "poolInfoModalAboutPageHooks": "Hooks",
+ "@poolInfoModalAboutPageHooks": {
+ "description": "Used by the pool info modal as a title for the hooks field in the about page"
+ },
+ "poolInfoModalAboutPageHooksDescription": "Some pools can include hooks, which are small smart contracts attached to the pool that allow pools to add extra features or rules when people trade or add liquidity",
+ "@poolInfoModalAboutPageHooksDescription": {
+ "description": "Used by the pool info modal as a description for the hooks field in the about page to explain what hooks are to the user"
+ },
+ "poolInfoModalAboutPageHooksNo": "No",
+ "@poolInfoModalAboutPageHooksNo": {
+ "description": "Used by the pool info modal as a value for the hooks field in the about page when there are no hooks in the pool"
+ },
+ "poolInfoModalAboutPageNetwork": "Network",
+ "@poolInfoModalAboutPageNetwork": {
+ "description": "Used by the pool info modal as a title for the section that has information about the pool's blockchain, in the about page",
+ "type": "text"
+ },
+ "poolInfoModalAboutPagePoolTypeValue": "{poolType} Pool",
+ "@poolInfoModalAboutPagePoolTypeValue": {
+ "description": "Used by the pool info modal as a value for the pool type field in the about page",
+ "placeholders": {
+ "poolType": {
+ "type": "String"
+ }
+ }
+ },
+ "poolInfoModalAboutPageProtocol": "Protocol",
+ "@poolInfoModalAboutPageProtocol": {
+ "description": "Used by the pool info modal as a title for the section that has information about the pool's protocol, in the about page",
+ "type": "text"
+ },
+ "poolInfoModalAboutPageTimeCreatedAgo": "Created {timeAgo}",
+ "@poolInfoModalAboutPageTimeCreatedAgo": {
+ "description": "Used by the pool info modal in the about page to show how long ago a pool was created",
+ "placeholders": {
+ "timeAgo": {
+ "description": "The time ago for the pool creation, in a human readable format like 1 day ago",
+ "type": "String"
+ }
+ }
+ },
+ "poolInfoModalAboutPageTokenCopied": "Copied!",
+ "@poolInfoModalAboutPageTokenCopied": {
+ "description": "Used by the pool info modal to notify the user that one of the pool tokens address was copied",
+ "type": "text"
+ },
+ "poolInfoModalAboutPageTokens": "Tokens",
+ "@poolInfoModalAboutPageTokens": {
+ "description": "Used by the pool info modal as a title for the tokens section of the about page. This section lists the tokens in the pool",
+ "type": "text"
+ },
+ "poolInfoModalAboutPageType": "Type",
+ "@poolInfoModalAboutPageType": {
+ "description": "Used by the pool info modal as a title for the pool type field in the about page"
+ },
+ "poolInfoModalAboutPoolTabTitle": "About Pool",
+ "@poolInfoModalAboutPoolTabTitle": {
+ "description": "Used by the pool info modal to title the about pool tab at a tab bar",
+ "type": "text"
+ },
+ "poolInfoModalAddLiquidity": "Add Liquidity",
+ "@poolInfoModalAddLiquidity": {
+ "description": "Used by the pool info modal to proceed to the add liquidity page with the current pool",
+ "type": "text"
+ },
+ "poolInfoModalBottomSheetTitle": "Pool Information",
+ "@poolInfoModalBottomSheetTitle": {
+ "description": "Used by the pool info modal as title for the bottom sheet that displays pool information"
+ },
+ "poolInfoModalCopied": "Copied!",
+ "@poolInfoModalCopied": {
+ "description": "Used by the pool info modal to notify the user that the pool address was copied",
+ "type": "text"
+ },
+ "poolInfoModalCopyPoolAddress": "Copy Pool Address",
+ "@poolInfoModalCopyPoolAddress": {
+ "description": "Used by the pool info modal to copy the current pool address",
+ "type": "text"
+ },
+ "poolInfoModalPoolStatsTabTitle": "Pool Stats",
+ "@poolInfoModalPoolStatsTabTitle": {
+ "description": "Used by the pool info modal to title the pool stats tab at a tab bar",
+ "type": "text"
+ },
+ "poolInfoModalStatePageSwapVolumeDescription": "{formattedVolume} in trades happened in the last {timeframeLabel} at this pool",
+ "@poolInfoModalStatePageSwapVolumeDescription": {
+ "description": "Used by the pool info modal to explain what's the swap volume of a pool to the user in the state page",
+ "placeholders": {
+ "formattedVolume": {
+ "description": "The formatted volume in USD for the passed timeframe",
+ "type": "String"
+ },
+ "timeframeLabel": {
+ "description": "The timeframe used for the swap volume calculation",
+ "type": "String"
+ }
+ }
+ },
+ "poolInfoModalStatsPageFees": "{timeframeLabel} Fees",
+ "@poolInfoModalStatsPageFees": {
+ "description": "Used by the pool info modal as a title for the fees field in the passed timeframe in the stats page",
+ "placeholders": {
+ "timeframeLabel": {
+ "description": "The timeframe used for the fees calculation",
+ "type": "String"
+ }
+ }
+ },
+ "poolInfoModalStatsPageFeesDescription": "This pool has distributed {formattedFees} of fees in ${timeframeLabel} to liquidity providers",
+ "@poolInfoModalStatsPageFeesDescription": {
+ "description": "Used by the pool info modal to explain what's the fees of a pool to the user in the stats page",
+ "placeholders": {
+ "formattedFees": {
+ "description": "The formatted fees in USD for the passed timeframe",
+ "type": "String"
+ },
+ "timeframeLabel": {
+ "description": "The timeframe used for the fees calculation",
+ "type": "String"
+ }
+ }
+ },
+ "poolInfoModalStatsPageNetInflow": "{timeframeLabel} Net Inflow",
+ "@poolInfoModalStatsPageNetInflow": {
+ "description": "Used by the pool info modal as a title for the net inflow of liquidity in the passed timeframe in the stats page",
+ "placeholders": {
+ "timeframeLabel": {
+ "description": "The timeframe used for the net inflow calculation",
+ "type": "String"
+ }
+ }
+ },
+ "poolInfoModalStatsPageNetInflowDescription": "This pool’s TVL changed by {formattedNetInflow} over the last {timeframeLabel} from liquidity provider deposits and withdrawals. It may differ from the current TVL due to price movements",
+ "@poolInfoModalStatsPageNetInflowDescription": {
+ "description": "Used by the pool info modal to explain what's the net inflow of a pool to the user in the state page",
+ "placeholders": {
+ "formattedNetInflow": {
+ "description": "The pool's net inflow formatted as USD",
+ "type": "String"
+ },
+ "timeframeLabel": {
+ "description": "The timeframe used for the net inflow calculation",
+ "type": "String"
+ }
+ }
+ },
+ "poolInfoModalStatsPageSwapVolume": "{timeframeLabel} Swap Volume",
+ "@poolInfoModalStatsPageSwapVolume": {
+ "description": "Used by the pool info modal as a title for the swap volume field in the passed timeframe in the stats page",
+ "placeholders": {
+ "timeframeLabel": {
+ "description": "The timeframe used for the swap volume calculation",
+ "type": "String"
+ }
+ }
+ },
+ "poolInfoModalStatsPageTimeframeTitle": "Stats Timeframe",
+ "@poolInfoModalStatsPageTimeframeTitle": {
+ "description": "Used by the pool info modal in the stats page to title the timeframe selector to change the stats time frame, like to 24h, 7d, 30d, etc...",
+ "type": "text"
+ },
+ "poolInfoModalStatsPageTvlDescription": "This pool has {formattedTvl} deposited in it, based on the current price of the tokens in the pool.",
+ "@poolInfoModalStatsPageTvlDescription": {
+ "description": "Used by the pool info modal to explain what's the TVL of a pool to the user in the stats page",
+ "placeholders": {
+ "formattedTvl": {
+ "description": "The TVL formatted as USD for the pool",
+ "type": "String"
+ }
+ }
+ },
+ "poolInfoModalStatsPageYearlyYield": "{timeframeLabel} Yearly Yield",
+ "@poolInfoModalStatsPageYearlyYield": {
+ "description": "Used by the pool info modal as a title for the yearly yield field in the passed timeframe in the stats page",
+ "placeholders": {
+ "timeframeLabel": {
+ "description": "The timeframe used for the yearly yield calculation",
+ "type": "String"
+ }
+ }
+ },
+ "poolInfoModalStatsPageYearlyYieldDescription": "This pool has an estimated yearly yield of {yearlyYieldFormatted} based on {timeframeLabel} of data from fees distributed to liquidity providers",
+ "@poolInfoModalStatsPageYearlyYieldDescription": {
+ "description": "Used by the pool info modal to explain what's the yearly yield of a pool to the user in the stats page",
+ "placeholders": {
+ "yearlyYieldFormatted": {
+ "description": "The yearly yield formatted as a percentage",
+ "type": "String"
+ },
+ "timeframeLabel": {
+ "description": "The timeframe used for the yearly yield calculation",
+ "type": "String"
+ }
+ }
+ },
+ "poolTokensButtonViewAtDexcreener": "View this pool at DEX Screener",
+ "@poolTokensButtonViewAtDexcreener": {
+ "description": "A tooltip message used by the pool tokens button to explain what clicking on the button does"
+ },
+ "poolTypeV3Description": "This pool uses the Uniswap V3 model, which allows more efficient trading and customizable liquidity ranges",
+ "@poolTypeV3Description": {
+ "description": "Used as tooltip message to explain what's a V3 pool to the user"
+ },
+ "poolTypeV4Description": "This pool uses the Uniswap V4 model, which introduces hooks and a more flexible architecture for custom pool logic",
+ "@poolTypeV4Description": {
+ "description": "Used as tooltip message to explain what's a V4 pool to the user"
+ },
"popularTokens": "Popular Tokens",
"positionCardLiquidity": "Liquidity: ",
"positionCardMax": "Max: ",
@@ -366,6 +603,10 @@
"tokenSelectorModalTokenGroups": "Token Groups",
"tokenSelectorModalTokenGroupsTooltipMessage": "Token groups let you search pools using multiple tokens in one go. Think of them like batch queries, want all USD stablecoins? Pick the group and we'll surface every relevant pool. You can also match groups against single tokens or other groups to discover deep liquidity.",
"tvl": "TVL",
+ "@tvl": {
+ "description": "Used anywhere that wants to show the total value locked of something",
+ "type": "text"
+ },
"twentyFourHours": "24h",
"twentyFourHoursCompact": "24h",
"understood": "Understood",
@@ -373,6 +614,84 @@
"week": "Week",
"weekCompact": "7d",
"whatsThisQuestionText": "What's this?",
+ "xDaysAndHoursAgo": "{days, plural, one{{days} day} other{{days} days}}{hours, plural, zero{} one{ and {hours} hour} other{ and {hours} hours}} ago",
+ "@xDaysAndHoursAgo": {
+ "description": "Used anywhere that implements days ago time, to describe how many days ago something happened",
+ "placeholders": {
+ "days": {
+ "type": "int"
+ },
+ "hours": {
+ "type": "int"
+ }
+ }
+ },
+ "xHoursAndMinutesAgo": "{hours, plural, one{{hours} hour} other{{hours} hours}}{minutes, plural, zero{} one{ and {minutes} minute} other{ and {minutes} minutes}} ago",
+ "@xHoursAndMinutesAgo": {
+ "description": "Used anywhere that implements hours ago time, to describe how many hours ago something happened",
+ "placeholders": {
+ "hours": {
+ "type": "int"
+ },
+ "minutes": {
+ "type": "int"
+ }
+ }
+ },
+ "xMillisecondsAgo": "{milliseconds, plural, one{{milliseconds} millisecond} other{{milliseconds} milliseconds}} ago",
+ "@xMillisecondsAgo": {
+ "description": "Used anywhere that implements milliseconds ago time, to describe how many milliseconds ago something happened",
+ "placeholders": {
+ "milliseconds": {
+ "type": "int"
+ }
+ }
+ },
+ "xMinutesAndSecondsAgo": "{minutes, plural, one{{minutes} minute} other{{minutes} minutes}}{seconds, plural, =0{} other{ and {seconds, plural, one{{seconds} second} other{{seconds} seconds}}}} ago",
+ "@xMinutesAndSecondsAgo": {
+ "description": "Used anywhere that implements minutes ago time, to describe how many minutes ago something happened",
+ "placeholders": {
+ "minutes": {
+ "type": "int"
+ },
+ "seconds": {
+ "type": "int"
+ }
+ }
+ },
+ "xMonthsAndDaysAgo": "{months, plural, one{{months} month} other{{months} months}}{days, plural, zero{} one{ and {days} day} other{ and {days} days}} ago",
+ "@xMonthsAndDaysAgo": {
+ "description": "Used anywhere that implements months ago time, to describe how many months ago something happened",
+ "placeholders": {
+ "months": {
+ "type": "int"
+ },
+ "days": {
+ "type": "int"
+ }
+ }
+ },
+ "xSecondsAgo": "{seconds, plural, one{{seconds} second} other{{seconds} seconds}} ago",
+ "@xSecondsAgo": {
+ "description": "Used anywhere that implements seconds ago time, to describe how many seconds ago something happened",
+ "placeholders": {
+ "seconds": {
+ "type": "int"
+ }
+ }
+ },
+ "xYearsAndMonthsAgo": "{years, plural, one{{years} year} other{{years} years}}{months, plural, zero{} one{ and {months} month} other{ and {months} months}} ago",
+ "@xYearsAndMonthsAgo": {
+ "description": "Used anywhere that implements years ago time, to describe how many years ago something happened",
+ "placeholders": {
+ "years": {
+ "type": "int"
+ },
+ "months": {
+ "type": "int"
+ }
+ }
+ },
"yieldCardAverageYieldYearly": "Average Yearly Yield",
"yieldCardDeposit": "Deposit",
"@yieldCardDeposit": {
diff --git a/lib/l10n/gen/app_localizations.dart b/lib/l10n/gen/app_localizations.dart
index e8d6fcd..3abd074 100644
--- a/lib/l10n/gen/app_localizations.dart
+++ b/lib/l10n/gen/app_localizations.dart
@@ -255,11 +255,11 @@ abstract class S {
/// **'Dark'**
String get dark;
- /// Used by the deposit page as title for the loading state, before the pool is ready for the user to deposit
+ /// Used by the deposit page as title for the back button to navigate back to the new position page, if the previous page isn't to select a yield, like paste the url to deposit directly
///
/// In en, this message translates to:
- /// **'Getting the pool ready for you...'**
- String get depositPageLoadingTitle;
+ /// **'Search other pools'**
+ String get depositPageBackToNewPositionButtonTitle;
/// Used by the deposit page as title for the back button to navigate back to the yields page, if the previous page was to select a yield
///
@@ -267,12 +267,6 @@ abstract class S {
/// **'Select Yield'**
String get depositPageBackToYieldsButtonTitle;
- /// Used by the deposit page as title for the back button to navigate back to the new position page, if the previous page isn't to select a yield, like paste the url to deposit directly
- ///
- /// In en, this message translates to:
- /// **'Search other pools'**
- String get depositPageBackToNewPositionButtonTitle;
-
/// No description provided for @depositPageBestYieldsIn.
///
/// In en, this message translates to:
@@ -405,6 +399,12 @@ abstract class S {
/// **'Organizing the best pools for you…'**
String get depositPageLoadingStep4Title;
+ /// Used by the deposit page as title for the loading state, before the pool is ready for the user to deposit
+ ///
+ /// In en, this message translates to:
+ /// **'Getting the pool ready for you...'**
+ String get depositPageLoadingTitle;
+
/// No description provided for @depositPageMaxRangeOutOfRangeWarningText.
///
/// In en, this message translates to:
@@ -675,6 +675,225 @@ abstract class S {
/// **'No results for'**
String get noResultsFor;
+ /// Used by the pool info modal to copy the pool hook address if any
+ ///
+ /// In en, this message translates to:
+ /// **'Copy Hook Address'**
+ String get poolInfoModalAboutPageCopyHook;
+
+ /// Used by the pool info modal to copy one of the pool tokens address
+ ///
+ /// In en, this message translates to:
+ /// **'Copy Token Address'**
+ String get poolInfoModalAboutPageCopyToken;
+
+ /// Used by the pool info modal as a title for the fee tier field in the about page
+ ///
+ /// In en, this message translates to:
+ /// **'Fee'**
+ String get poolInfoModalAboutPageFee;
+
+ /// No description provided for @poolInfoModalAboutPageFeeDescription.
+ ///
+ /// In en, this message translates to:
+ /// **'This pool takes {isDynamicFee, select, true {a dynamic} other {{formattedFee}}} fee on each trade and distributes it to the liquidity providers'**
+ String poolInfoModalAboutPageFeeDescription({
+ required String isDynamicFee,
+ required String formattedFee,
+ });
+
+ /// Used by the pool info modal to notify the user that the pool hook address was copied
+ ///
+ /// In en, this message translates to:
+ /// **'Copied!'**
+ String get poolInfoModalAboutPageHookCopied;
+
+ /// Used by the pool info modal as a title for the hooks field in the about page
+ ///
+ /// In en, this message translates to:
+ /// **'Hooks'**
+ String get poolInfoModalAboutPageHooks;
+
+ /// Used by the pool info modal as a description for the hooks field in the about page to explain what hooks are to the user
+ ///
+ /// In en, this message translates to:
+ /// **'Some pools can include hooks, which are small smart contracts attached to the pool that allow pools to add extra features or rules when people trade or add liquidity'**
+ String get poolInfoModalAboutPageHooksDescription;
+
+ /// Used by the pool info modal as a value for the hooks field in the about page when there are no hooks in the pool
+ ///
+ /// In en, this message translates to:
+ /// **'No'**
+ String get poolInfoModalAboutPageHooksNo;
+
+ /// Used by the pool info modal as a title for the section that has information about the pool's blockchain, in the about page
+ ///
+ /// In en, this message translates to:
+ /// **'Network'**
+ String get poolInfoModalAboutPageNetwork;
+
+ /// Used by the pool info modal as a value for the pool type field in the about page
+ ///
+ /// In en, this message translates to:
+ /// **'{poolType} Pool'**
+ String poolInfoModalAboutPagePoolTypeValue({required String poolType});
+
+ /// Used by the pool info modal as a title for the section that has information about the pool's protocol, in the about page
+ ///
+ /// In en, this message translates to:
+ /// **'Protocol'**
+ String get poolInfoModalAboutPageProtocol;
+
+ /// Used by the pool info modal in the about page to show how long ago a pool was created
+ ///
+ /// In en, this message translates to:
+ /// **'Created {timeAgo}'**
+ String poolInfoModalAboutPageTimeCreatedAgo({required String timeAgo});
+
+ /// Used by the pool info modal to notify the user that one of the pool tokens address was copied
+ ///
+ /// In en, this message translates to:
+ /// **'Copied!'**
+ String get poolInfoModalAboutPageTokenCopied;
+
+ /// Used by the pool info modal as a title for the tokens section of the about page. This section lists the tokens in the pool
+ ///
+ /// In en, this message translates to:
+ /// **'Tokens'**
+ String get poolInfoModalAboutPageTokens;
+
+ /// Used by the pool info modal as a title for the pool type field in the about page
+ ///
+ /// In en, this message translates to:
+ /// **'Type'**
+ String get poolInfoModalAboutPageType;
+
+ /// Used by the pool info modal to title the about pool tab at a tab bar
+ ///
+ /// In en, this message translates to:
+ /// **'About Pool'**
+ String get poolInfoModalAboutPoolTabTitle;
+
+ /// Used by the pool info modal to proceed to the add liquidity page with the current pool
+ ///
+ /// In en, this message translates to:
+ /// **'Add Liquidity'**
+ String get poolInfoModalAddLiquidity;
+
+ /// Used by the pool info modal as title for the bottom sheet that displays pool information
+ ///
+ /// In en, this message translates to:
+ /// **'Pool Information'**
+ String get poolInfoModalBottomSheetTitle;
+
+ /// Used by the pool info modal to notify the user that the pool address was copied
+ ///
+ /// In en, this message translates to:
+ /// **'Copied!'**
+ String get poolInfoModalCopied;
+
+ /// Used by the pool info modal to copy the current pool address
+ ///
+ /// In en, this message translates to:
+ /// **'Copy Pool Address'**
+ String get poolInfoModalCopyPoolAddress;
+
+ /// Used by the pool info modal to title the pool stats tab at a tab bar
+ ///
+ /// In en, this message translates to:
+ /// **'Pool Stats'**
+ String get poolInfoModalPoolStatsTabTitle;
+
+ /// Used by the pool info modal to explain what's the swap volume of a pool to the user in the state page
+ ///
+ /// In en, this message translates to:
+ /// **'{formattedVolume} in trades happened in the last {timeframeLabel} at this pool'**
+ String poolInfoModalStatePageSwapVolumeDescription({
+ required String formattedVolume,
+ required String timeframeLabel,
+ });
+
+ /// Used by the pool info modal as a title for the fees field in the passed timeframe in the stats page
+ ///
+ /// In en, this message translates to:
+ /// **'{timeframeLabel} Fees'**
+ String poolInfoModalStatsPageFees({required String timeframeLabel});
+
+ /// Used by the pool info modal to explain what's the fees of a pool to the user in the stats page
+ ///
+ /// In en, this message translates to:
+ /// **'This pool has distributed {formattedFees} of fees in \${timeframeLabel} to liquidity providers'**
+ String poolInfoModalStatsPageFeesDescription({
+ required String formattedFees,
+ required String timeframeLabel,
+ });
+
+ /// Used by the pool info modal as a title for the net inflow of liquidity in the passed timeframe in the stats page
+ ///
+ /// In en, this message translates to:
+ /// **'{timeframeLabel} Net Inflow'**
+ String poolInfoModalStatsPageNetInflow({required String timeframeLabel});
+
+ /// Used by the pool info modal to explain what's the net inflow of a pool to the user in the state page
+ ///
+ /// In en, this message translates to:
+ /// **'This pool’s TVL changed by {formattedNetInflow} over the last {timeframeLabel} from liquidity provider deposits and withdrawals. It may differ from the current TVL due to price movements'**
+ String poolInfoModalStatsPageNetInflowDescription({
+ required String formattedNetInflow,
+ required String timeframeLabel,
+ });
+
+ /// Used by the pool info modal as a title for the swap volume field in the passed timeframe in the stats page
+ ///
+ /// In en, this message translates to:
+ /// **'{timeframeLabel} Swap Volume'**
+ String poolInfoModalStatsPageSwapVolume({required String timeframeLabel});
+
+ /// Used by the pool info modal in the stats page to title the timeframe selector to change the stats time frame, like to 24h, 7d, 30d, etc...
+ ///
+ /// In en, this message translates to:
+ /// **'Stats Timeframe'**
+ String get poolInfoModalStatsPageTimeframeTitle;
+
+ /// Used by the pool info modal to explain what's the TVL of a pool to the user in the stats page
+ ///
+ /// In en, this message translates to:
+ /// **'This pool has {formattedTvl} deposited in it, based on the current price of the tokens in the pool.'**
+ String poolInfoModalStatsPageTvlDescription({required String formattedTvl});
+
+ /// Used by the pool info modal as a title for the yearly yield field in the passed timeframe in the stats page
+ ///
+ /// In en, this message translates to:
+ /// **'{timeframeLabel} Yearly Yield'**
+ String poolInfoModalStatsPageYearlyYield({required String timeframeLabel});
+
+ /// Used by the pool info modal to explain what's the yearly yield of a pool to the user in the stats page
+ ///
+ /// In en, this message translates to:
+ /// **'This pool has an estimated yearly yield of {yearlyYieldFormatted} based on {timeframeLabel} of data from fees distributed to liquidity providers'**
+ String poolInfoModalStatsPageYearlyYieldDescription({
+ required String yearlyYieldFormatted,
+ required String timeframeLabel,
+ });
+
+ /// A tooltip message used by the pool tokens button to explain what clicking on the button does
+ ///
+ /// In en, this message translates to:
+ /// **'View this pool at DEX Screener'**
+ String get poolTokensButtonViewAtDexcreener;
+
+ /// Used as tooltip message to explain what's a V3 pool to the user
+ ///
+ /// In en, this message translates to:
+ /// **'This pool uses the Uniswap V3 model, which allows more efficient trading and customizable liquidity ranges'**
+ String get poolTypeV3Description;
+
+ /// Used as tooltip message to explain what's a V4 pool to the user
+ ///
+ /// In en, this message translates to:
+ /// **'This pool uses the Uniswap V4 model, which introduces hooks and a more flexible architecture for custom pool logic'**
+ String get poolTypeV4Description;
+
/// No description provided for @popularTokens.
///
/// In en, this message translates to:
@@ -1117,7 +1336,7 @@ abstract class S {
/// **'Token groups let you search pools using multiple tokens in one go. Think of them like batch queries, want all USD stablecoins? Pick the group and we\'ll surface every relevant pool. You can also match groups against single tokens or other groups to discover deep liquidity.'**
String get tokenSelectorModalTokenGroupsTooltipMessage;
- /// No description provided for @tvl.
+ /// Used anywhere that wants to show the total value locked of something
///
/// In en, this message translates to:
/// **'TVL'**
@@ -1165,6 +1384,48 @@ abstract class S {
/// **'What\'s this?'**
String get whatsThisQuestionText;
+ /// Used anywhere that implements days ago time, to describe how many days ago something happened
+ ///
+ /// In en, this message translates to:
+ /// **'{days, plural, one{{days} day} other{{days} days}}{hours, plural, zero{} one{ and {hours} hour} other{ and {hours} hours}} ago'**
+ String xDaysAndHoursAgo({required int days, required int hours});
+
+ /// Used anywhere that implements hours ago time, to describe how many hours ago something happened
+ ///
+ /// In en, this message translates to:
+ /// **'{hours, plural, one{{hours} hour} other{{hours} hours}}{minutes, plural, zero{} one{ and {minutes} minute} other{ and {minutes} minutes}} ago'**
+ String xHoursAndMinutesAgo({required int hours, required int minutes});
+
+ /// Used anywhere that implements milliseconds ago time, to describe how many milliseconds ago something happened
+ ///
+ /// In en, this message translates to:
+ /// **'{milliseconds, plural, one{{milliseconds} millisecond} other{{milliseconds} milliseconds}} ago'**
+ String xMillisecondsAgo({required int milliseconds});
+
+ /// Used anywhere that implements minutes ago time, to describe how many minutes ago something happened
+ ///
+ /// In en, this message translates to:
+ /// **'{minutes, plural, one{{minutes} minute} other{{minutes} minutes}}{seconds, plural, =0{} other{ and {seconds, plural, one{{seconds} second} other{{seconds} seconds}}}} ago'**
+ String xMinutesAndSecondsAgo({required int minutes, required int seconds});
+
+ /// Used anywhere that implements months ago time, to describe how many months ago something happened
+ ///
+ /// In en, this message translates to:
+ /// **'{months, plural, one{{months} month} other{{months} months}}{days, plural, zero{} one{ and {days} day} other{ and {days} days}} ago'**
+ String xMonthsAndDaysAgo({required int months, required int days});
+
+ /// Used anywhere that implements seconds ago time, to describe how many seconds ago something happened
+ ///
+ /// In en, this message translates to:
+ /// **'{seconds, plural, one{{seconds} second} other{{seconds} seconds}} ago'**
+ String xSecondsAgo({required int seconds});
+
+ /// Used anywhere that implements years ago time, to describe how many years ago something happened
+ ///
+ /// In en, this message translates to:
+ /// **'{years, plural, one{{years} year} other{{years} years}}{months, plural, zero{} one{ and {months} month} other{ and {months} months}} ago'**
+ String xYearsAndMonthsAgo({required int years, required int months});
+
/// No description provided for @yieldCardAverageYieldYearly.
///
/// 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 675d3cf..014d6b0 100644
--- a/lib/l10n/gen/app_localizations_en.dart
+++ b/lib/l10n/gen/app_localizations_en.dart
@@ -96,14 +96,11 @@ class SEn extends S {
String get dark => 'Dark';
@override
- String get depositPageLoadingTitle => 'Getting the pool ready for you...';
+ String get depositPageBackToNewPositionButtonTitle => 'Search other pools';
@override
String get depositPageBackToYieldsButtonTitle => 'Select Yield';
- @override
- String get depositPageBackToNewPositionButtonTitle => 'Search other pools';
-
@override
String get depositPageBestYieldsIn => 'Best Yields in';
@@ -188,6 +185,9 @@ class SEn extends S {
String get depositPageLoadingStep4Title =>
'Organizing the best pools for you…';
+ @override
+ String get depositPageLoadingTitle => 'Getting the pool ready for you...';
+
@override
String get depositPageMaxRangeOutOfRangeWarningText =>
'You will not earn fees until the market price move down into your range';
@@ -353,6 +353,155 @@ class SEn extends S {
@override
String get noResultsFor => 'No results for';
+ @override
+ String get poolInfoModalAboutPageCopyHook => 'Copy Hook Address';
+
+ @override
+ String get poolInfoModalAboutPageCopyToken => 'Copy Token Address';
+
+ @override
+ String get poolInfoModalAboutPageFee => 'Fee';
+
+ @override
+ String poolInfoModalAboutPageFeeDescription({
+ required String isDynamicFee,
+ required String formattedFee,
+ }) {
+ String _temp0 = intl.Intl.selectLogic(isDynamicFee, {
+ 'true': 'a dynamic',
+ 'other': '$formattedFee',
+ });
+ return 'This pool takes $_temp0 fee on each trade and distributes it to the liquidity providers';
+ }
+
+ @override
+ String get poolInfoModalAboutPageHookCopied => 'Copied!';
+
+ @override
+ String get poolInfoModalAboutPageHooks => 'Hooks';
+
+ @override
+ String get poolInfoModalAboutPageHooksDescription =>
+ 'Some pools can include hooks, which are small smart contracts attached to the pool that allow pools to add extra features or rules when people trade or add liquidity';
+
+ @override
+ String get poolInfoModalAboutPageHooksNo => 'No';
+
+ @override
+ String get poolInfoModalAboutPageNetwork => 'Network';
+
+ @override
+ String poolInfoModalAboutPagePoolTypeValue({required String poolType}) {
+ return '$poolType Pool';
+ }
+
+ @override
+ String get poolInfoModalAboutPageProtocol => 'Protocol';
+
+ @override
+ String poolInfoModalAboutPageTimeCreatedAgo({required String timeAgo}) {
+ return 'Created $timeAgo';
+ }
+
+ @override
+ String get poolInfoModalAboutPageTokenCopied => 'Copied!';
+
+ @override
+ String get poolInfoModalAboutPageTokens => 'Tokens';
+
+ @override
+ String get poolInfoModalAboutPageType => 'Type';
+
+ @override
+ String get poolInfoModalAboutPoolTabTitle => 'About Pool';
+
+ @override
+ String get poolInfoModalAddLiquidity => 'Add Liquidity';
+
+ @override
+ String get poolInfoModalBottomSheetTitle => 'Pool Information';
+
+ @override
+ String get poolInfoModalCopied => 'Copied!';
+
+ @override
+ String get poolInfoModalCopyPoolAddress => 'Copy Pool Address';
+
+ @override
+ String get poolInfoModalPoolStatsTabTitle => 'Pool Stats';
+
+ @override
+ String poolInfoModalStatePageSwapVolumeDescription({
+ required String formattedVolume,
+ required String timeframeLabel,
+ }) {
+ return '$formattedVolume in trades happened in the last $timeframeLabel at this pool';
+ }
+
+ @override
+ String poolInfoModalStatsPageFees({required String timeframeLabel}) {
+ return '$timeframeLabel Fees';
+ }
+
+ @override
+ String poolInfoModalStatsPageFeesDescription({
+ required String formattedFees,
+ required String timeframeLabel,
+ }) {
+ return 'This pool has distributed $formattedFees of fees in \$$timeframeLabel to liquidity providers';
+ }
+
+ @override
+ String poolInfoModalStatsPageNetInflow({required String timeframeLabel}) {
+ return '$timeframeLabel Net Inflow';
+ }
+
+ @override
+ String poolInfoModalStatsPageNetInflowDescription({
+ required String formattedNetInflow,
+ required String timeframeLabel,
+ }) {
+ return 'This pool’s TVL changed by $formattedNetInflow over the last $timeframeLabel from liquidity provider deposits and withdrawals. It may differ from the current TVL due to price movements';
+ }
+
+ @override
+ String poolInfoModalStatsPageSwapVolume({required String timeframeLabel}) {
+ return '$timeframeLabel Swap Volume';
+ }
+
+ @override
+ String get poolInfoModalStatsPageTimeframeTitle => 'Stats Timeframe';
+
+ @override
+ String poolInfoModalStatsPageTvlDescription({required String formattedTvl}) {
+ return 'This pool has $formattedTvl deposited in it, based on the current price of the tokens in the pool.';
+ }
+
+ @override
+ String poolInfoModalStatsPageYearlyYield({required String timeframeLabel}) {
+ return '$timeframeLabel Yearly Yield';
+ }
+
+ @override
+ String poolInfoModalStatsPageYearlyYieldDescription({
+ required String yearlyYieldFormatted,
+ required String timeframeLabel,
+ }) {
+ return 'This pool has an estimated yearly yield of $yearlyYieldFormatted based on $timeframeLabel of data from fees distributed to liquidity providers';
+ }
+
+ @override
+ String get poolTokensButtonViewAtDexcreener =>
+ 'View this pool at DEX Screener';
+
+ @override
+ String get poolTypeV3Description =>
+ 'This pool uses the Uniswap V3 model, which allows more efficient trading and customizable liquidity ranges';
+
+ @override
+ String get poolTypeV4Description =>
+ 'This pool uses the Uniswap V4 model, which introduces hooks and a more flexible architecture for custom pool logic';
+
@override
String get popularTokens => 'Popular Tokens';
@@ -657,6 +806,123 @@ class SEn extends S {
@override
String get whatsThisQuestionText => 'What\'s this?';
+ @override
+ String xDaysAndHoursAgo({required int days, required int hours}) {
+ String _temp0 = intl.Intl.pluralLogic(
+ days,
+ locale: localeName,
+ other: '$days days',
+ one: '$days day',
+ );
+ String _temp1 = intl.Intl.pluralLogic(
+ hours,
+ locale: localeName,
+ other: ' and $hours hours',
+ one: ' and $hours hour',
+ zero: '',
+ );
+ return '$_temp0$_temp1 ago';
+ }
+
+ @override
+ String xHoursAndMinutesAgo({required int hours, required int minutes}) {
+ String _temp0 = intl.Intl.pluralLogic(
+ hours,
+ locale: localeName,
+ other: '$hours hours',
+ one: '$hours hour',
+ );
+ String _temp1 = intl.Intl.pluralLogic(
+ minutes,
+ locale: localeName,
+ other: ' and $minutes minutes',
+ one: ' and $minutes minute',
+ zero: '',
+ );
+ return '$_temp0$_temp1 ago';
+ }
+
+ @override
+ String xMillisecondsAgo({required int milliseconds}) {
+ String _temp0 = intl.Intl.pluralLogic(
+ milliseconds,
+ locale: localeName,
+ other: '$milliseconds milliseconds',
+ one: '$milliseconds millisecond',
+ );
+ return '$_temp0 ago';
+ }
+
+ @override
+ String xMinutesAndSecondsAgo({required int minutes, required int seconds}) {
+ String _temp0 = intl.Intl.pluralLogic(
+ minutes,
+ locale: localeName,
+ other: '$minutes minutes',
+ one: '$minutes minute',
+ );
+ String _temp1 = intl.Intl.pluralLogic(
+ seconds,
+ locale: localeName,
+ other: '$seconds seconds',
+ one: '$seconds second',
+ );
+ String _temp2 = intl.Intl.pluralLogic(
+ seconds,
+ locale: localeName,
+ other: ' and $_temp1',
+ zero: '',
+ );
+ return '$_temp0$_temp2 ago';
+ }
+
+ @override
+ String xMonthsAndDaysAgo({required int months, required int days}) {
+ String _temp0 = intl.Intl.pluralLogic(
+ months,
+ locale: localeName,
+ other: '$months months',
+ one: '$months month',
+ );
+ String _temp1 = intl.Intl.pluralLogic(
+ days,
+ locale: localeName,
+ other: ' and $days days',
+ one: ' and $days day',
+ zero: '',
+ );
+ return '$_temp0$_temp1 ago';
+ }
+
+ @override
+ String xSecondsAgo({required int seconds}) {
+ String _temp0 = intl.Intl.pluralLogic(
+ seconds,
+ locale: localeName,
+ other: '$seconds seconds',
+ one: '$seconds second',
+ );
+ return '$_temp0 ago';
+ }
+
+ @override
+ String xYearsAndMonthsAgo({required int years, required int months}) {
+ String _temp0 = intl.Intl.pluralLogic(
+ years,
+ locale: localeName,
+ other: '$years years',
+ one: '$years year',
+ );
+ String _temp1 = intl.Intl.pluralLogic(
+ months,
+ locale: localeName,
+ other: ' and $months months',
+ one: ' and $months month',
+ zero: '',
+ );
+ return '$_temp0$_temp1 ago';
+ }
+
@override
String get yieldCardAverageYieldYearly => 'Average Yearly Yield';
diff --git a/lib/widgets/app_header/app_header.dart b/lib/widgets/app_header/app_header.dart
index 3942ebc..5cf201a 100644
--- a/lib/widgets/app_header/app_header.dart
+++ b/lib/widgets/app_header/app_header.dart
@@ -144,9 +144,16 @@ class _AppHeaderState extends State with DeviceInfoMixin {
ConnectButton(compact: isMobileSize(context)),
const SizedBox(width: 12),
ZupIconButton(
- iconColor: context.brightness.isLight ? ZupColors.brand : ZupColors.white,
-
- icon: Transform.rotate(angle: pi / 2, child: Assets.icons.ellipsis.svg(height: 3.5)),
+ icon: Transform.rotate(
+ angle: pi / 2,
+ child: Assets.icons.ellipsis.svg(
+ height: 3.5,
+ colorFilter: ColorFilter.mode(
+ context.brightness.isLight ? ZupColors.brand : ZupColors.white,
+ BlendMode.srcIn,
+ ),
+ ),
+ ),
onPressed: (context) {
AppSettingsDropdown.show(context);
},
diff --git a/lib/widgets/pool_info_modal/pages/about_page.dart b/lib/widgets/pool_info_modal/pages/about_page.dart
new file mode 100644
index 0000000..2b748af
--- /dev/null
+++ b/lib/widgets/pool_info_modal/pages/about_page.dart
@@ -0,0 +1,276 @@
+part of '../pool_info_modal.dart';
+
+class _AboutPage extends StatefulWidget {
+ const _AboutPage(this.liquidityPool);
+
+ final LiquidityPoolDto liquidityPool;
+
+ @override
+ State<_AboutPage> createState() => _AboutPageState();
+}
+
+class _AboutPageState extends State<_AboutPage> with DeviceInfoMixin {
+ final zupNetworkImage = inject();
+
+ @override
+ Widget build(BuildContext context) {
+ return Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ const SizedBox(height: 20),
+ Wrap(
+ runSpacing: 10,
+ children: [
+ ZupTooltip.text(
+ message: isMobileDevice
+ ? S
+ .of(context)
+ .poolInfoModalAboutPageFeeDescription(
+ isDynamicFee: widget.liquidityPool.isDynamicFee.toString(),
+ formattedFee: widget.liquidityPool.currentFeeTierFormatted,
+ )
+ : "",
+ child: ZupTitledValueTag(
+ value: widget.liquidityPool.isDynamicFee ? "Dynamic" : widget.liquidityPool.currentFeeTierFormatted,
+ title: S.of(context).poolInfoModalAboutPageFee,
+ trailingIcon: ZupTooltip.text(
+ key: const Key("fee-tooltip"),
+ message: S
+ .of(context)
+ .poolInfoModalAboutPageFeeDescription(
+ isDynamicFee: widget.liquidityPool.isDynamicFee.toString(),
+ formattedFee: widget.liquidityPool.currentFeeTierFormatted,
+ ),
+ constraints: const BoxConstraints(maxWidth: 200),
+ child: Assets.icons.infoCircle.svg(
+ height: 15,
+ width: 15,
+ colorFilter: const ColorFilter.mode(ZupColors.gray, BlendMode.srcIn),
+ ),
+ ),
+ ),
+ ),
+ const SizedBox(width: 10),
+ ZupTooltip.text(
+ message: isMobileDevice ? widget.liquidityPool.poolType.description(context) : "",
+ child: ZupTitledValueTag(
+ title: S.of(context).poolInfoModalAboutPageType,
+ value: S.of(context).poolInfoModalAboutPagePoolTypeValue(poolType: widget.liquidityPool.poolType.label),
+ trailingIcon: ZupTooltip.text(
+ key: const Key("pool-type-tooltip"),
+ message: widget.liquidityPool.poolType.description(context),
+ constraints: const BoxConstraints(maxWidth: 200),
+ child: Assets.icons.infoCircle.svg(
+ height: 15,
+ width: 15,
+ colorFilter: const ColorFilter.mode(ZupColors.gray, BlendMode.srcIn),
+ ),
+ ),
+ ),
+ ),
+ if (widget.liquidityPool.poolType.isV4) ...[
+ const SizedBox(width: 10),
+ ZupTooltip.text(
+ message: isMobileDevice ? S.of(context).poolInfoModalAboutPageHooksDescription : "",
+ child: ZupTitledValueTag(
+ title: S.of(context).poolInfoModalAboutPageHooks,
+ value:
+ widget.liquidityPool.hook?.address.shortAddress(prefixAndSuffixLength: 4) ??
+ S.of(context).poolInfoModalAboutPageHooksNo,
+ trailingIcon: Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ if (widget.liquidityPool.hook != null) ...[
+ ZupSwitchingIconButton(
+ key: const Key("copy-hook-button"),
+ restIconTooltipMessage: S.of(context).poolInfoModalAboutPageCopyHook,
+ pressedIconTooltipMessage: S.of(context).poolInfoModalAboutPageHookCopied,
+ padding: const EdgeInsets.all(2),
+ backgroundColor: Colors.transparent,
+ restIcon: Assets.icons.documentOnDocument.svg(
+ height: 15,
+ width: 15,
+ colorFilter: ColorFilter.mode(
+ ZupThemeColors.backgroundInverse.themed(context.brightness),
+ BlendMode.srcIn,
+ ),
+ ),
+ pressedIcon: Assets.icons.checkmarkCircleFill.svg(
+ colorFilter: ColorFilter.mode(
+ ZupThemeColors.success.themed(context.brightness),
+ BlendMode.srcIn,
+ ),
+ ),
+ onPressed: (buttonContext) {
+ return Clipboard.setData(ClipboardData(text: widget.liquidityPool.hook!.address));
+ },
+ ),
+ const SizedBox(width: 5),
+ ],
+ ZupTooltip.text(
+ key: const Key("hooks-tooltip"),
+ message: S.of(context).poolInfoModalAboutPageHooksDescription,
+ constraints: const BoxConstraints(maxWidth: 200),
+ child: Assets.icons.infoCircle.svg(
+ height: 15,
+ width: 15,
+ colorFilter: const ColorFilter.mode(ZupColors.gray, BlendMode.srcIn),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ],
+ ],
+ ),
+ const SizedBox(height: 20),
+ Text(
+ S.of(context).poolInfoModalAboutPageTokens,
+ style: const TextStyle(fontWeight: FontWeight.w500, fontSize: 15, color: ZupColors.gray),
+ ),
+ const SizedBox(height: 5),
+ buildTokenButton(context, widget.liquidityPool.token0),
+ const SizedBox(height: 10),
+ buildTokenButton(context, widget.liquidityPool.token1),
+ const SizedBox(height: 20),
+ Text(
+ S.of(context).poolInfoModalAboutPageProtocol,
+ style: const TextStyle(fontWeight: FontWeight.w500, fontSize: 15, color: ZupColors.gray),
+ ),
+ const SizedBox(height: 5),
+ ZupOutlinedButton(
+ key: const Key("protocol-button"),
+ onPressed: () {
+ return launchUrl(Uri.parse(widget.liquidityPool.protocol.url), mode: LaunchMode.externalApplication);
+ },
+ leadingIcon: ZupRemoteAvatar(
+ avatarUrl: widget.liquidityPool.protocol.logo,
+ zupNetworkImage: zupNetworkImage,
+ size: 20,
+ ),
+
+ trailingIcon: Assets.icons.arrowUpRight.svg(
+ height: 9,
+ width: 9,
+ colorFilter: ColorFilter.mode(ZupThemeColors.primaryText.themed(context.brightness), BlendMode.srcIn),
+ ),
+ title: Text(
+ widget.liquidityPool.protocol.name,
+ style: TextStyle(
+ fontWeight: FontWeight.w600,
+ fontSize: 15,
+ color: ZupThemeColors.primaryText.themed(context.brightness),
+ ),
+ ),
+ ),
+ const SizedBox(height: 20),
+ Text(
+ S.of(context).poolInfoModalAboutPageNetwork,
+ style: const TextStyle(fontWeight: FontWeight.w500, fontSize: 15, color: ZupColors.gray),
+ ),
+ const SizedBox(height: 5),
+ ZupOutlinedButton(
+ key: const Key("network-button"),
+ onPressed: () {
+ launchUrl(Uri.parse(widget.liquidityPool.network.websiteUrl), mode: LaunchMode.externalApplication);
+ },
+ title: Text(
+ widget.liquidityPool.network.label,
+ style: TextStyle(
+ fontWeight: FontWeight.w600,
+ fontSize: 15,
+ color: ZupThemeColors.primaryText.themed(context.brightness),
+ ),
+ ),
+ leadingIcon: SizedBox(width: 24, height: 24, child: widget.liquidityPool.network.icon(context.brightness)),
+ trailingIcon: Assets.icons.arrowUpRight.svg(
+ height: 9,
+ width: 9,
+ colorFilter: ColorFilter.mode(ZupThemeColors.primaryText.themed(context.brightness), BlendMode.srcIn),
+ ),
+ ),
+
+ const SizedBox(height: 40),
+ const Spacer(),
+ Center(
+ child: Text(
+ S
+ .of(context)
+ .poolInfoModalAboutPageTimeCreatedAgo(
+ timeAgo: DateTime.fromMillisecondsSinceEpoch(
+ widget.liquidityPool.createdAtMillisecondsTimestamp,
+ ).timeAgo.formattedTimeAgo(context),
+ ),
+ style: const TextStyle(fontWeight: FontWeight.w500, fontSize: 13, color: ZupColors.gray),
+ ),
+ ),
+ ],
+ );
+ }
+
+ Widget buildTokenButton(BuildContext context, SingleChainTokenDto token) => Row(
+ children: [
+ ZupOutlinedButton(
+ key: Key("${token.address}-button"),
+ onPressed: token.address != EthereumConstants.zeroAddress
+ ? () => widget.liquidityPool.network.openAddress(token.address)
+ : null,
+ title: Text(
+ token.symbol,
+ style: TextStyle(
+ fontWeight: FontWeight.w600,
+ fontSize: 15,
+ color: token.address != EthereumConstants.zeroAddress
+ ? ZupThemeColors.primaryText.themed(context.brightness)
+ : ZupThemeColors.primaryText.themed(context.brightness),
+ ),
+ ),
+ leadingIcon: ZupRemoteAvatar(
+ avatarUrl: token.logoUrl,
+ size: 20,
+ errorPlaceholder: token.name[0],
+ zupNetworkImage: zupNetworkImage,
+ ),
+ trailingIcon: token.address == EthereumConstants.zeroAddress
+ ? null
+ : Row(
+ children: [
+ Text(
+ token.address.shortAddress(prefixAndSuffixLength: 3),
+ style: TextStyle(color: ZupThemeColors.primaryText.themed(context.brightness)),
+ ),
+ const SizedBox(width: 10),
+ Assets.icons.arrowUpRight.svg(
+ height: 9,
+ width: 9,
+ colorFilter: ColorFilter.mode(
+ ZupThemeColors.primaryText.themed(context.brightness),
+ BlendMode.srcIn,
+ ),
+ ),
+ ],
+ ),
+ ),
+ if (token.address != EthereumConstants.zeroAddress) ...[
+ const SizedBox(width: 10),
+ ZupSwitchingIconButton(
+ key: Key("${token.address}-copy-button"),
+ restIconTooltipMessage: S.of(context).poolInfoModalAboutPageCopyToken,
+ pressedIconTooltipMessage: S.of(context).poolInfoModalAboutPageTokenCopied,
+ restIcon: Assets.icons.documentOnDocument.svg(
+ height: 18,
+ width: 18,
+ colorFilter: const ColorFilter.mode(ZupColors.gray, BlendMode.srcIn),
+ ),
+ pressedIcon: Assets.icons.checkmarkCircleFill.svg(
+ height: 18,
+ width: 18,
+ colorFilter: ColorFilter.mode(ZupThemeColors.success.themed(context.brightness), BlendMode.srcIn),
+ ),
+ onPressed: (buttonContext) => Clipboard.setData(ClipboardData(text: token.address)),
+ ),
+ ],
+ ],
+ );
+}
diff --git a/lib/widgets/pool_info_modal/pages/stats_page.dart b/lib/widgets/pool_info_modal/pages/stats_page.dart
new file mode 100644
index 0000000..510212f
--- /dev/null
+++ b/lib/widgets/pool_info_modal/pages/stats_page.dart
@@ -0,0 +1,166 @@
+part of '../pool_info_modal.dart';
+
+class _StatsPage extends StatefulWidget {
+ const _StatsPage({required this.liquidityPool, required this.selectedTimeframe});
+
+ final LiquidityPoolDto liquidityPool;
+ final PoolDataTimeframe selectedTimeframe;
+
+ @override
+ State<_StatsPage> createState() => _StatsPageState();
+}
+
+class _StatsPageState extends State<_StatsPage> with DeviceInfoMixin {
+ late PoolDataTimeframe selectedTimeframe = widget.selectedTimeframe;
+
+ @override
+ Widget build(BuildContext context) {
+ return ConstrainedBox(
+ constraints: const BoxConstraints(minHeight: 370),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ const SizedBox(height: 20),
+ Align(
+ alignment: Alignment.centerLeft,
+ child: TimeframeSelector(
+ title: S.of(context).poolInfoModalStatsPageTimeframeTitle,
+ selectedTimeframe: selectedTimeframe,
+ onTimeframeSelected: (timeframe) => setState(() => selectedTimeframe = timeframe),
+ ),
+ ),
+ const SizedBox(height: 10),
+ FlexList(
+ children: [
+ _buildInfoField(
+ title: S.of(context).tvl,
+ value: widget.liquidityPool.totalValueLockedUSD.formatCompactCurrency(),
+ tooltipMessage: S
+ .of(context)
+ .poolInfoModalStatsPageTvlDescription(
+ formattedTvl: widget.liquidityPool.totalValueLockedUSD.formatCompactCurrency(),
+ ),
+ ),
+ _buildInfoField(
+ title: S
+ .of(context)
+ .poolInfoModalStatsPageSwapVolume(timeframeLabel: selectedTimeframe.compactDaysLabel(context)),
+ value: widget.liquidityPool.volumeTimeframed(selectedTimeframe).formatCompactCurrency(),
+ tooltipMessage: S
+ .of(context)
+ .poolInfoModalStatePageSwapVolumeDescription(
+ formattedVolume: widget.liquidityPool.volumeTimeframed(selectedTimeframe).formatCompactCurrency(),
+ timeframeLabel: selectedTimeframe.compactDaysLabel(context),
+ ),
+ ),
+ _buildInfoField(
+ title: S
+ .of(context)
+ .poolInfoModalStatsPageFees(timeframeLabel: selectedTimeframe.compactDaysLabel(context)),
+ value: widget.liquidityPool.feesTimeframed(selectedTimeframe).formatCompactCurrency(),
+ tooltipMessage: S
+ .of(context)
+ .poolInfoModalStatsPageFeesDescription(
+ formattedFees: widget.liquidityPool.feesTimeframed(selectedTimeframe).formatCompactCurrency(),
+ timeframeLabel: selectedTimeframe.compactDaysLabel(context),
+ ),
+ ),
+ _buildInfoField(
+ title: S
+ .of(context)
+ .poolInfoModalStatsPageNetInflow(timeframeLabel: selectedTimeframe.compactDaysLabel(context)),
+ value: widget.liquidityPool
+ .netInflowTimeframed(selectedTimeframe)
+ .formatCompactCurrency()
+ .asSignedStringNumber,
+ tooltipMessage: S
+ .of(context)
+ .poolInfoModalStatsPageNetInflowDescription(
+ timeframeLabel: selectedTimeframe.compactDaysLabel(context),
+ formattedNetInflow: widget.liquidityPool
+ .netInflowTimeframed(selectedTimeframe)
+ .formatCompactCurrency()
+ .asSignedStringNumber,
+ ),
+ titleColor: widget.liquidityPool.netInflowTimeframed(selectedTimeframe).trendColor(context),
+ ),
+ _buildInfoField(
+ title: S
+ .of(context)
+ .poolInfoModalStatsPageYearlyYield(timeframeLabel: selectedTimeframe.compactDaysLabel(context)),
+ value: widget.liquidityPool.timeframedYieldFormatted(selectedTimeframe),
+ titleColor: context.brightness.isDark ? ZupColors.brand5 : ZupColors.brand,
+ tooltipMessage: S
+ .of(context)
+ .poolInfoModalStatsPageYearlyYieldDescription(
+ yearlyYieldFormatted: widget.liquidityPool.timeframedYieldFormatted(selectedTimeframe),
+ timeframeLabel: selectedTimeframe.compactDaysLabel(context),
+ ),
+ ),
+ ],
+ ),
+ ],
+ ),
+ );
+ }
+
+ Widget _buildInfoField({
+ required String title,
+ required String value,
+ required String tooltipMessage,
+ Color? titleColor,
+ }) => ZupTooltip.text(
+ message: isMobileDevice ? tooltipMessage : "",
+ child: Container(
+ padding: const EdgeInsets.all(20),
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(20),
+ border: Border.all(
+ width: context.brightness.isDark ? 1 : 0.5,
+ color: ZupThemeColors.borderOnBackground.themed(context.brightness),
+ ),
+ ),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Text(
+ title,
+ style: const TextStyle(fontWeight: FontWeight.w500, fontSize: 15, color: ZupColors.gray),
+ ),
+ ],
+ ),
+ const SizedBox(width: 5),
+ Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Text(
+ value,
+ style: TextStyle(
+ fontWeight: FontWeight.w600,
+ fontSize: 20,
+ color: titleColor ?? ZupThemeColors.primaryText.themed(context.brightness),
+ ),
+ ),
+ const SizedBox(width: 5),
+ ZupTooltip.text(
+ key: Key("$title-tooltip"),
+ message: tooltipMessage,
+ constraints: const BoxConstraints(maxWidth: 250),
+ child: Assets.icons.infoCircle.svg(
+ colorFilter: const ColorFilter.mode(ZupColors.gray, BlendMode.srcIn),
+ width: 12,
+ height: 12,
+ ),
+ ),
+ ],
+ ),
+ ],
+ ),
+ ),
+ );
+}
diff --git a/lib/widgets/pool_info_modal/pool_info_modal.dart b/lib/widgets/pool_info_modal/pool_info_modal.dart
new file mode 100644
index 0000000..603fa47
--- /dev/null
+++ b/lib/widgets/pool_info_modal/pool_info_modal.dart
@@ -0,0 +1,171 @@
+import 'package:flex_list/flex_list.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:url_launcher/url_launcher.dart';
+import 'package:web3kit/web3kit.dart';
+import 'package:zup_app/core/dtos/liquidity_pool_dto.dart';
+import 'package:zup_app/core/dtos/single_chain_token_dto.dart';
+import 'package:zup_app/core/enums/pool_data_timeframe.dart';
+import 'package:zup_app/core/extensions/num_extension.dart';
+import 'package:zup_app/core/extensions/time_ago_extension.dart';
+import 'package:zup_app/core/injections.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';
+import 'package:zup_app/widgets/pool_tokens_button.dart';
+import 'package:zup_app/widgets/timeframe_selector.dart';
+import 'package:zup_core/zup_core.dart';
+import 'package:zup_ui_kit/zup_ui_kit.dart';
+
+part 'pages/about_page.dart';
+part 'pages/stats_page.dart';
+
+enum _PoolInfoPages { stats, about }
+
+class PoolInfoModal extends StatefulWidget with DeviceInfoMixin {
+ const PoolInfoModal({super.key, required this.liquidityPool, required this.selectedTimeframe});
+
+ final LiquidityPoolDto liquidityPool;
+ final PoolDataTimeframe selectedTimeframe;
+
+ static show(
+ BuildContext context, {
+ required LiquidityPoolDto liquidityPool,
+ required PoolDataTimeframe selectedTimeframe,
+ required bool showAsBottomSheet,
+ }) {
+ if (showAsBottomSheet) {
+ return ZupModal.show(
+ context,
+ dismissible: true,
+ content: PoolInfoModal(liquidityPool: liquidityPool, selectedTimeframe: selectedTimeframe),
+ title: S.of(context).poolInfoModalBottomSheetTitle,
+ backgroundColor: null,
+ description: null,
+ padding: EdgeInsets.zero,
+ showAsBottomSheet: true,
+ );
+ }
+
+ return ZupSidePanel.show(
+ context,
+ child: PoolInfoModal(liquidityPool: liquidityPool, selectedTimeframe: selectedTimeframe),
+ );
+ }
+
+ @override
+ State createState() => _PoolInfoModalState();
+}
+
+class _PoolInfoModalState extends State with SingleTickerProviderStateMixin, DeviceInfoMixin {
+ final navigator = inject();
+
+ _PoolInfoPages selectedTab = _PoolInfoPages.stats;
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ constraints: const BoxConstraints(maxWidth: 540),
+ child: ScrollbarTheme(
+ data: Theme.of(context).scrollbarTheme.copyWith(crossAxisMargin: 8, mainAxisMargin: 20),
+ child: CustomScrollView(
+ physics: const ClampingScrollPhysics(),
+ shrinkWrap: true,
+ slivers: [
+ SliverFillRemaining(
+ hasScrollBody: false,
+ child: Container(
+ padding: isMobileSize(context) ? const EdgeInsets.symmetric(horizontal: 20) : const EdgeInsets.all(20),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Row(
+ children: [
+ PoolTokensButton(liquidityPool: widget.liquidityPool),
+ const SizedBox(width: 10),
+ ZupSwitchingIconButton(
+ key: const Key("pool-info-modal-copy-pool-address"),
+ restIconTooltipMessage: S.of(context).poolInfoModalCopyPoolAddress,
+ pressedIconTooltipMessage: S.of(context).poolInfoModalCopied,
+ restIcon: Assets.icons.documentOnDocument.svg(
+ height: 18,
+ width: 18,
+ colorFilter: const ColorFilter.mode(ZupColors.gray, BlendMode.srcIn),
+ ),
+ pressedIcon: Assets.icons.checkmarkCircleFill.svg(
+ height: 17,
+ width: 17,
+ colorFilter: ColorFilter.mode(
+ ZupThemeColors.success.themed(context.brightness),
+ BlendMode.srcIn,
+ ),
+ ),
+ onPressed: (buttonContext) {
+ return Clipboard.setData(ClipboardData(text: widget.liquidityPool.poolAddress));
+ },
+ ),
+
+ if (!isMobileSize(context)) ...[
+ const Spacer(),
+ ZupPrimaryButton(
+ key: const Key("pool-info-modal-add-liquidity"),
+ title: S.of(context).poolInfoModalAddLiquidity,
+ onPressed: (buttonContext) {
+ Navigator.pop(context);
+ navigator.navigateToDeposit(
+ yieldPool: widget.liquidityPool,
+ selectedTimeframe: widget.selectedTimeframe,
+ parseWrappedToNative:
+ widget.liquidityPool.isToken0Native || widget.liquidityPool.isToken1Native,
+ );
+ },
+ fixedIcon: true,
+ isTrailingIcon: true,
+ icon: Assets.icons.arrowRight.svg(),
+ ),
+ ],
+ ],
+ ),
+ const SizedBox(height: 20),
+ ZupTabBar(
+ initialSelectedTabIndex: 0,
+ tabs: [
+ ZupTabBarItem(
+ key: const Key("stats-tab"),
+ title: S.of(context).poolInfoModalPoolStatsTabTitle,
+ icon: Assets.icons.chartBar.svg(),
+ onSelected: () {
+ setState(() => selectedTab = _PoolInfoPages.stats);
+ },
+ ),
+ ZupTabBarItem(
+ key: const Key("about-tab"),
+ title: S.of(context).poolInfoModalAboutPoolTabTitle,
+ icon: Assets.icons.infoCircle.svg(),
+ onSelected: () {
+ setState(() => selectedTab = _PoolInfoPages.about);
+ },
+ ),
+ ],
+ ),
+
+ Expanded(
+ child: switch (selectedTab) {
+ _PoolInfoPages.stats => _StatsPage(
+ liquidityPool: widget.liquidityPool,
+ selectedTimeframe: widget.selectedTimeframe,
+ ),
+ _PoolInfoPages.about => _AboutPage(widget.liquidityPool),
+ },
+ ),
+ const SizedBox(height: 20),
+ ],
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/widgets/pool_token.dart b/lib/widgets/pool_token.dart
new file mode 100644
index 0000000..16075c4
--- /dev/null
+++ b/lib/widgets/pool_token.dart
@@ -0,0 +1,29 @@
+import 'package:flutter/material.dart';
+import 'package:zup_app/core/dtos/single_chain_token_dto.dart';
+import 'package:zup_app/core/injections.dart';
+import 'package:zup_ui_kit/zup_network_image.dart';
+import 'package:zup_ui_kit/zup_remote_avatar.dart';
+
+class PoolToken extends StatelessWidget {
+ PoolToken({super.key, required this.token});
+
+ final SingleChainTokenDto token;
+ final zupNetworkImage = inject();
+
+ @override
+ Widget build(BuildContext context) {
+ return Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ ZupRemoteAvatar(
+ avatarUrl: token.logoUrl,
+ errorPlaceholder: token.name[0],
+ size: 30,
+ zupNetworkImage: zupNetworkImage,
+ ),
+ const SizedBox(width: 10),
+ Text(token.symbol, style: const TextStyle(fontSize: 17, fontWeight: FontWeight.w500)),
+ ],
+ );
+ }
+}
diff --git a/lib/widgets/pool_tokens_avatar.dart b/lib/widgets/pool_tokens_avatar.dart
new file mode 100644
index 0000000..89a43c3
--- /dev/null
+++ b/lib/widgets/pool_tokens_avatar.dart
@@ -0,0 +1,40 @@
+import 'package:flutter/material.dart';
+import 'package:zup_app/core/dtos/liquidity_pool_dto.dart';
+import 'package:zup_app/core/injections.dart';
+import 'package:zup_ui_kit/zup_ui_kit.dart';
+
+class PoolTokensAvatar extends StatelessWidget {
+ const PoolTokensAvatar({super.key, required this.liquidityPool, this.size = 27});
+
+ final LiquidityPoolDto liquidityPool;
+ final double size;
+
+ ZupNetworkImage get zupNetworkImage => inject();
+
+ @override
+ Widget build(BuildContext context) {
+ return SizedBox(
+ width: size * 2,
+ child: Stack(
+ clipBehavior: Clip.none,
+ children: [
+ ZupRemoteAvatar(
+ avatarUrl: liquidityPool.token0.logoUrl,
+ errorPlaceholder: liquidityPool.token0.name.isEmpty ? null : liquidityPool.token0.name[0],
+ size: size,
+ zupNetworkImage: zupNetworkImage,
+ ),
+ Positioned(
+ left: 20,
+ child: ZupRemoteAvatar(
+ avatarUrl: liquidityPool.token1.logoUrl,
+ errorPlaceholder: liquidityPool.token1.name.isEmpty ? null : liquidityPool.token1.name[0],
+ zupNetworkImage: zupNetworkImage,
+ size: size,
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/widgets/pool_tokens_button.dart b/lib/widgets/pool_tokens_button.dart
new file mode 100644
index 0000000..505a68a
--- /dev/null
+++ b/lib/widgets/pool_tokens_button.dart
@@ -0,0 +1,54 @@
+import 'package:flutter/material.dart';
+import 'package:url_launcher/url_launcher.dart';
+import 'package:zup_app/core/dtos/liquidity_pool_dto.dart';
+import 'package:zup_app/gen/assets.gen.dart';
+import 'package:zup_app/l10n/gen/app_localizations.dart';
+import 'package:zup_app/widgets/pool_tokens_avatar.dart';
+import 'package:zup_core/extensions/extensions.dart';
+import 'package:zup_ui_kit/zup_ui_kit.dart';
+
+class PoolTokensButton extends StatelessWidget {
+ const PoolTokensButton({super.key, required this.liquidityPool});
+
+ final LiquidityPoolDto liquidityPool;
+
+ bool get canLaunchAtDexscreener => liquidityPool.network.dexscreenerUrl != null;
+
+ @override
+ Widget build(BuildContext context) {
+ return ZupTooltip.text(
+ message: canLaunchAtDexscreener ? S.of(context).poolTokensButtonViewAtDexcreener : "",
+ margin: const EdgeInsets.only(top: 10),
+ constraints: const BoxConstraints(maxWidth: 200),
+ child: ZupOutlinedButton(
+ onPressed: canLaunchAtDexscreener
+ ? () => launchUrl(Uri.parse("${liquidityPool.network.dexscreenerUrl}/${liquidityPool.poolAddress}"))
+ : null,
+ leadingIconSpacing: 5,
+ title: Text(
+ "${liquidityPool.token0.symbol.clamped(8)}/${liquidityPool.token1.symbol.clamped(8)}",
+ style: TextStyle(
+ fontWeight: FontWeight.w600,
+ color: canLaunchAtDexscreener ? ZupColors.brand : ZupThemeColors.primaryText.themed(context.brightness),
+ fontSize: 16,
+ ),
+ ),
+ trailingIcon: canLaunchAtDexscreener
+ ? Container(
+ padding: const EdgeInsets.all(5),
+ decoration: BoxDecoration(
+ shape: BoxShape.circle,
+ color: ZupThemeColors.backgroundInverse.themed(context.brightness),
+ ),
+ child: Assets.logos.dexscreener.svg(
+ height: 15,
+ width: 15,
+ colorFilter: ColorFilter.mode(ZupThemeColors.background.themed(context.brightness), BlendMode.srcIn),
+ ),
+ )
+ : null,
+ leadingIcon: PoolTokensAvatar(liquidityPool: liquidityPool),
+ ),
+ );
+ }
+}
diff --git a/lib/widgets/position_token.dart b/lib/widgets/position_token.dart
deleted file mode 100644
index 5448f1f..0000000
--- a/lib/widgets/position_token.dart
+++ /dev/null
@@ -1,30 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:zup_app/core/dtos/token_dto.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';
-
-class PositionToken extends StatelessWidget {
- PositionToken({super.key, required this.token});
-
- final TokenDto token;
- final zupCachedImage = inject();
-
- @override
- Widget build(BuildContext context) {
- return Row(
- mainAxisSize: MainAxisSize.min,
- children: [
- TokenAvatar(asset: token, size: 30),
- const SizedBox(width: 10),
- Text(
- token.symbol,
- style: const TextStyle(
- fontSize: 17,
- fontWeight: FontWeight.w500,
- ),
- ),
- ],
- );
- }
-}
diff --git a/lib/widgets/timeframe_selector.dart b/lib/widgets/timeframe_selector.dart
index 9d391a2..ba37c13 100644
--- a/lib/widgets/timeframe_selector.dart
+++ b/lib/widgets/timeframe_selector.dart
@@ -1,15 +1,22 @@
import 'package:flutter/cupertino.dart';
-import 'package:zup_app/core/enums/yield_timeframe.dart';
+import 'package:zup_app/core/enums/pool_data_timeframe.dart';
import 'package:zup_app/gen/assets.gen.dart';
-import 'package:zup_app/l10n/gen/app_localizations.dart';
import 'package:zup_core/extensions/build_context_extension.dart';
import 'package:zup_ui_kit/zup_ui_kit.dart';
class TimeframeSelector extends StatefulWidget {
- const TimeframeSelector({super.key, required this.selectedTimeframe, required this.onTimeframeSelected});
+ const TimeframeSelector({
+ super.key,
+ required this.selectedTimeframe,
+ required this.onTimeframeSelected,
+ required this.title,
+ this.tooltipMessage,
+ });
- final YieldTimeFrame selectedTimeframe;
- final Function(YieldTimeFrame? newTimeframe) onTimeframeSelected;
+ final String title;
+ final String? tooltipMessage;
+ final PoolDataTimeframe selectedTimeframe;
+ final Function(PoolDataTimeframe newTimeframe) onTimeframeSelected;
@override
State createState() => _TimeframeSelectorState();
@@ -26,7 +33,7 @@ class _TimeframeSelectorState extends State {
crossAxisAlignment: WrapCrossAlignment.center,
children: [
Text(
- S.of(context).yieldsPageTimeframeSelectorTitle,
+ widget.title,
style: TextStyle(
color: ZupThemeColors.primaryText.themed(context.brightness),
fontSize: 14,
@@ -37,14 +44,14 @@ class _TimeframeSelectorState extends State {
Row(
mainAxisSize: MainAxisSize.min,
children: [
- CupertinoSlidingSegmentedControl(
+ CupertinoSlidingSegmentedControl(
proportionalWidth: true,
onValueChanged: (timeframe) async {
- widget.onTimeframeSelected(timeframe);
+ widget.onTimeframeSelected(timeframe ?? PoolDataTimeframe.day);
},
groupValue: widget.selectedTimeframe,
children: Map.fromEntries(
- YieldTimeFrame.values.map(
+ PoolDataTimeframe.values.map(
(timeframe) => MapEntry(
timeframe,
IgnorePointer(
@@ -64,14 +71,16 @@ class _TimeframeSelectorState extends State {
),
),
),
- const SizedBox(width: 10),
- ZupTooltip.text(
- key: const Key("timeframe-tooltip"),
- message: S.of(context).yieldsPageTimeframeExplanation,
- child: Assets.icons.infoCircle.svg(
- colorFilter: const ColorFilter.mode(ZupColors.gray, BlendMode.srcIn),
+ if (widget.tooltipMessage != null) ...[
+ const SizedBox(width: 10),
+ ZupTooltip.text(
+ key: const Key("timeframe-tooltip"),
+ message: widget.tooltipMessage!,
+ child: Assets.icons.infoCircle.svg(
+ colorFilter: const ColorFilter.mode(ZupColors.gray, BlendMode.srcIn),
+ ),
),
- ),
+ ],
],
),
],
diff --git a/lib/widgets/token_avatar.dart b/lib/widgets/token_avatar.dart
deleted file mode 100644
index c057a06..0000000
--- a/lib/widgets/token_avatar.dart
+++ /dev/null
@@ -1,49 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:skeletonizer/skeletonizer.dart';
-import 'package:zup_app/core/dtos/token_dto.dart';
-import 'package:zup_app/core/injections.dart';
-import 'package:zup_app/widgets/zup_cached_image.dart';
-import 'package:zup_ui_kit/zup_circular_loading_indicator.dart';
-import 'package:zup_ui_kit/zup_colors.dart';
-
-class TokenAvatar extends StatelessWidget {
- TokenAvatar({super.key, required this.asset, this.size = 30});
-
- final TokenDto asset;
- final double size;
-
- final zupCachedImage = inject();
-
- Widget genericAvatar() => SizedBox(
- height: size,
- width: size,
- child: FittedBox(
- child: CircleAvatar(
- backgroundColor: ZupColors.brand7,
- foregroundColor: ZupColors.brand,
- child: Text(asset.name.isNotEmpty ? asset.name[0] : ""),
- ),
- ),
- );
-
- @override
- Widget build(BuildContext context) {
- return asset.logoUrl.isEmpty
- ? genericAvatar()
- : zupCachedImage.build(
- context,
- asset.logoUrl,
- height: size,
- width: size,
- radius: 50,
- errorWidget: (_, __, ___) => genericAvatar(),
- placeholder: Skeleton.ignore(
- child: ZupCircularLoadingIndicator(
- size: size,
- backgroundColor: ZupColors.brand5,
- indicatorColor: ZupColors.brand,
- ),
- ),
- );
- }
-}
diff --git a/lib/widgets/token_card.dart b/lib/widgets/token_card.dart
index f6d78e8..fbbe930 100644
--- a/lib/widgets/token_card.dart
+++ b/lib/widgets/token_card.dart
@@ -1,15 +1,13 @@
import 'package:flutter/material.dart';
-import 'package:zup_app/core/dtos/token_dto.dart';
+import 'package:zup_app/core/dtos/multi_chain_token_dto.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 'package:zup_core/extensions/extensions.dart';
import 'package:zup_ui_kit/zup_ui_kit.dart';
class TokenCard extends StatefulWidget {
const TokenCard({super.key, required this.asset, required this.onClick});
- final TokenDto asset;
+ final MultiChainTokenDto asset;
final Function() onClick;
@override
@@ -19,7 +17,7 @@ class TokenCard extends StatefulWidget {
class _TokenCardState extends State {
bool isHovering = false;
- final zupCachedImage = inject();
+ final zupNetworkImage = inject();
@override
Widget build(BuildContext context) {
@@ -32,7 +30,12 @@ class _TokenCardState extends State {
},
child: Row(
children: [
- TokenAvatar(asset: widget.asset, size: 35),
+ ZupRemoteAvatar(
+ avatarUrl: widget.asset.logoUrl,
+ errorPlaceholder: widget.asset.name[0],
+ size: 35,
+ zupNetworkImage: zupNetworkImage,
+ ),
const SizedBox(width: 10),
Expanded(
child: Column(
diff --git a/lib/widgets/token_group_card.dart b/lib/widgets/token_group_card.dart
index 568a680..e0127da 100644
--- a/lib/widgets/token_group_card.dart
+++ b/lib/widgets/token_group_card.dart
@@ -3,14 +3,8 @@ import 'package:skeletonizer/skeletonizer.dart';
import 'package:zup_app/core/dtos/token_group_dto.dart';
import 'package:zup_app/core/injections.dart';
import 'package:zup_app/gen/assets.gen.dart';
-import 'package:zup_app/widgets/token_avatar.dart';
-import 'package:zup_app/widgets/zup_cached_image.dart';
import 'package:zup_core/extensions/extensions.dart';
-import 'package:zup_ui_kit/zup_circular_loading_indicator.dart';
-import 'package:zup_ui_kit/zup_colors.dart';
-import 'package:zup_ui_kit/zup_selectable_card.dart';
-import 'package:zup_ui_kit/zup_theme_colors.dart';
-import 'package:zup_ui_kit/zup_tooltip.dart';
+import 'package:zup_ui_kit/zup_ui_kit.dart';
class TokenGroupCard extends StatefulWidget {
const TokenGroupCard({super.key, required this.group, required this.onClick});
@@ -23,7 +17,7 @@ class TokenGroupCard extends StatefulWidget {
}
class _TokenGroupCardState extends State {
- final ZupCachedImage zupCachedImage = inject();
+ final ZupNetworkImage zupNetworkImage = inject();
bool isHovering = false;
@override
@@ -35,7 +29,7 @@ class _TokenGroupCardState extends State {
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
- zupCachedImage.build(
+ zupNetworkImage.load(
context,
widget.group.logoUrl,
height: 35,
@@ -52,7 +46,7 @@ class _TokenGroupCardState extends State {
placeholder: const Skeleton.ignore(
child: ZupCircularLoadingIndicator(
size: 35,
- backgroundColor: ZupColors.brand5,
+ trackColor: ZupColors.brand5,
indicatorColor: ZupColors.brand,
),
),
@@ -96,7 +90,11 @@ class _TokenGroupCardState extends State {
child: Row(
spacing: 10,
children: [
- TokenAvatar(asset: widget.group.tokens[index], size: 20),
+ ZupRemoteAvatar(
+ avatarUrl: widget.group.tokens[index].logoUrl,
+ errorPlaceholder: widget.group.tokens[index].name[0],
+ size: 20,
+ ),
Expanded(
child: Text(
overflow: TextOverflow.ellipsis,
diff --git a/lib/widgets/token_selector_button/token_selector_button.dart b/lib/widgets/token_selector_button/token_selector_button.dart
index 59f6f39..b7a0180 100644
--- a/lib/widgets/token_selector_button/token_selector_button.dart
+++ b/lib/widgets/token_selector_button/token_selector_button.dart
@@ -1,13 +1,11 @@
import 'package:flutter/material.dart';
-import 'package:zup_app/core/dtos/token_dto.dart';
+import 'package:zup_app/core/dtos/multi_chain_token_dto.dart';
import 'package:zup_app/core/dtos/token_group_dto.dart';
import 'package:zup_app/core/injections.dart';
import 'package:zup_app/gen/assets.gen.dart';
import 'package:zup_app/l10n/gen/app_localizations.dart';
-import 'package:zup_app/widgets/token_avatar.dart';
import 'package:zup_app/widgets/token_selector_button/token_selector_button_controller.dart';
import 'package:zup_app/widgets/token_selector_modal/token_selector_modal.dart';
-import 'package:zup_app/widgets/zup_cached_image.dart';
import 'package:zup_core/zup_core.dart';
import 'package:zup_ui_kit/zup_ui_kit.dart';
@@ -21,8 +19,8 @@ class TokenSelectorButton extends StatefulWidget {
}
class _TokenSelectorButtonState extends State with DeviceInfoMixin {
- final zupCachedImage = inject();
- TokenDto? get selectedToken => widget.controller.selectedToken;
+ final zupNetworkImage = inject();
+ MultiChainTokenDto? get selectedToken => widget.controller.selectedToken;
TokenGroupDto? get selectedGroup => widget.controller.selectedTokenGroup;
bool get hasSelection => widget.controller.hasSelection;
@@ -102,23 +100,15 @@ class _TokenSelectorButtonState extends State with DeviceIn
colorFilter: const ColorFilter.mode(ZupColors.brand, BlendMode.srcIn),
)
else ...[
- if (selectedToken != null) TokenAvatar(asset: selectedToken!, size: 30),
- if (selectedGroup != null)
- zupCachedImage.build(
- context,
- selectedGroup!.logoUrl,
- height: 30,
- width: 30,
- radius: 50,
- errorWidget: (_, __, ___) => Container(
- height: 30,
- width: 30,
- color: ZupColors.gray6,
- child: const Center(
- child: Text("?", style: TextStyle(color: ZupColors.gray, fontSize: 16)),
- ),
- ),
+ if (selectedToken != null)
+ ZupRemoteAvatar(
+ avatarUrl: selectedToken!.logoUrl,
+ errorPlaceholder: selectedToken!.name[0],
+ zupNetworkImage: zupNetworkImage,
+ size: 30,
),
+ if (selectedGroup != null)
+ ZupRemoteAvatar(avatarUrl: selectedGroup!.logoUrl, errorPlaceholder: "?", size: 30),
],
const SizedBox(width: 10),
Expanded(
diff --git a/lib/widgets/token_selector_button/token_selector_button_controller.dart b/lib/widgets/token_selector_button/token_selector_button_controller.dart
index 77653db..6fb1b5d 100644
--- a/lib/widgets/token_selector_button/token_selector_button_controller.dart
+++ b/lib/widgets/token_selector_button/token_selector_button_controller.dart
@@ -1,27 +1,28 @@
import 'dart:async';
import 'package:async/async.dart' show StreamGroup;
-import 'package:zup_app/core/dtos/token_dto.dart';
+import 'package:zup_app/core/dtos/multi_chain_token_dto.dart';
import 'package:zup_app/core/dtos/token_group_dto.dart';
class TokenSelectorButtonController {
- TokenDto? _selectedToken;
+ MultiChainTokenDto? _selectedToken;
TokenGroupDto? _selectedTokenGroup;
- final StreamController _selectedTokenStreamController = StreamController.broadcast();
+ final StreamController _selectedTokenStreamController =
+ StreamController.broadcast();
final StreamController _selectedTokenGroupStreamController =
StreamController.broadcast();
bool get hasSelection => _selectedToken != null || _selectedTokenGroup != null;
- TokenDto? get selectedToken => _selectedToken;
+ MultiChainTokenDto? get selectedToken => _selectedToken;
TokenGroupDto? get selectedTokenGroup => _selectedTokenGroup;
- Stream get selectedTokenStream => _selectedTokenStreamController.stream;
+ Stream get selectedTokenStream => _selectedTokenStreamController.stream;
Stream get selectedTokenGroupStream => _selectedTokenGroupStreamController.stream;
Stream get selectionStream => StreamGroup.mergeBroadcast([selectedTokenStream, selectedTokenGroupStream]);
- void changeToken(TokenDto? newToken) {
+ void changeToken(MultiChainTokenDto? newToken) {
if (newToken == _selectedToken) return;
_selectedToken = newToken;
diff --git a/lib/widgets/token_selector_modal/token_selector_modal.dart b/lib/widgets/token_selector_modal/token_selector_modal.dart
index 2f920da..b4e4c9a 100644
--- a/lib/widgets/token_selector_modal/token_selector_modal.dart
+++ b/lib/widgets/token_selector_modal/token_selector_modal.dart
@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:zup_app/app/app_cubit/app_cubit.dart';
import 'package:zup_app/core/debouncer.dart';
-import 'package:zup_app/core/dtos/token_dto.dart';
+import 'package:zup_app/core/dtos/multi_chain_token_dto.dart';
import 'package:zup_app/core/dtos/token_group_dto.dart';
import 'package:zup_app/core/injections.dart';
import 'package:zup_app/gen/assets.gen.dart';
@@ -18,13 +18,13 @@ import 'package:zup_ui_kit/zup_ui_kit.dart';
class TokenSelectorModal extends StatefulWidget {
const TokenSelectorModal({super.key, required this.onSelectToken, required this.onSelectTokenGroup});
- final Function(TokenDto token) onSelectToken;
+ final Function(MultiChainTokenDto token) onSelectToken;
final Function(TokenGroupDto token) onSelectTokenGroup;
static void show(
BuildContext context, {
required bool showAsBottomSheet,
- required Function(TokenDto token) onSelectToken,
+ required Function(MultiChainTokenDto token) onSelectToken,
required Function(TokenGroupDto token) onSelectTokenGroup,
}) async {
return ZupModal.show(
@@ -53,7 +53,7 @@ class _TokenSelectorModalState extends State with DeviceInfo
final _cubit = inject();
final _appCubit = inject();
- void _selectToken(TokenDto token) {
+ void _selectToken(MultiChainTokenDto token) {
widget.onSelectToken(token);
Navigator.of(context).pop();
}
@@ -253,7 +253,7 @@ class _TokenSelectorModalState extends State with DeviceInfo
3,
(index) => Padding(
padding: _paddingBetweenListItems,
- child: TokenCard(asset: TokenDto.fixture(), onClick: () {}),
+ child: TokenCard(asset: MultiChainTokenDto.fixture(), onClick: () {}),
),
),
),
@@ -306,7 +306,7 @@ class _TokenSelectorModalState extends State with DeviceInfo
4,
(index) => Padding(
padding: _paddingBetweenListItems,
- child: TokenCard(asset: TokenDto.fixture(), onClick: () {}),
+ child: TokenCard(asset: MultiChainTokenDto.fixture(), onClick: () {}),
),
),
),
diff --git a/lib/widgets/token_selector_modal/token_selector_modal_cubit.dart b/lib/widgets/token_selector_modal/token_selector_modal_cubit.dart
index 91fa325..333ff52 100644
--- a/lib/widgets/token_selector_modal/token_selector_modal_cubit.dart
+++ b/lib/widgets/token_selector_modal/token_selector_modal_cubit.dart
@@ -3,7 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:web3kit/web3kit.dart';
import 'package:zup_app/app/app_cubit/app_cubit.dart';
-import 'package:zup_app/core/dtos/token_dto.dart';
+import 'package:zup_app/core/dtos/multi_chain_token_dto.dart';
import 'package:zup_app/core/dtos/token_list_dto.dart';
import 'package:zup_app/core/enums/networks.dart';
import 'package:zup_app/core/repositories/tokens_repository.dart';
diff --git a/lib/widgets/token_selector_modal/token_selector_modal_state.dart b/lib/widgets/token_selector_modal/token_selector_modal_state.dart
index a7e44d7..8de595a 100644
--- a/lib/widgets/token_selector_modal/token_selector_modal_state.dart
+++ b/lib/widgets/token_selector_modal/token_selector_modal_state.dart
@@ -6,7 +6,7 @@ class TokenSelectorModalState with _$TokenSelectorModalState {
const factory TokenSelectorModalState.loading() = _Loading;
const factory TokenSelectorModalState.searchLoading() = _SearchLoading;
const factory TokenSelectorModalState.success(TokenListDto popularTokens) = _Success;
- const factory TokenSelectorModalState.searchSuccess(List result) = _SearchSuccess;
+ const factory TokenSelectorModalState.searchSuccess(List result) = _SearchSuccess;
const factory TokenSelectorModalState.error() = _Error;
const factory TokenSelectorModalState.searchError(String searchedTerm) = _SearchError;
const factory TokenSelectorModalState.searchNotFound(String searchedTerm) = _SearchNotFound;
diff --git a/lib/widgets/yield_card.dart b/lib/widgets/yield_card.dart
index d92daf7..410ce7a 100644
--- a/lib/widgets/yield_card.dart
+++ b/lib/widgets/yield_card.dart
@@ -3,14 +3,13 @@ import 'package:flutter/material.dart';
import 'package:flutter_animate/flutter_animate.dart';
import 'package:intl/intl.dart';
import 'package:skeletonizer/skeletonizer.dart' hide ShimmerEffect;
-import 'package:zup_app/core/dtos/yield_dto.dart';
-import 'package:zup_app/core/enums/yield_timeframe.dart';
+import 'package:zup_app/core/dtos/liquidity_pool_dto.dart';
+import 'package:zup_app/core/enums/pool_data_timeframe.dart';
import 'package:zup_app/core/injections.dart';
import 'package:zup_app/core/mixins/keys_mixin.dart';
import 'package:zup_app/gen/assets.gen.dart';
import 'package:zup_app/l10n/gen/app_localizations.dart';
-import 'package:zup_app/widgets/token_avatar.dart';
-import 'package:zup_app/widgets/zup_cached_image.dart';
+import 'package:zup_app/widgets/pool_tokens_avatar.dart';
import 'package:zup_core/extensions/extensions.dart';
import 'package:zup_core/mixins/device_info_mixin.dart';
import 'package:zup_ui_kit/zup_ui_kit.dart';
@@ -24,25 +23,27 @@ class YieldCard extends StatefulWidget {
this.mainButton,
this.expandWidth = false,
this.showTimeframe = false,
+ this.secondaryButton,
});
- final YieldDto yieldPool;
+ final LiquidityPoolDto yieldPool;
final bool showHotestYieldAnimation;
final bool showTimeframe;
final bool expandWidth;
- final YieldTimeFrame yieldTimeFrame;
- final Widget? mainButton;
+ final PoolDataTimeframe yieldTimeFrame;
+ final ZupPrimaryButton? mainButton;
+ final ZupIconButton? secondaryButton;
@override
State createState() => _YieldCardState();
}
class _YieldCardState extends State with DeviceInfoMixin, KeysMixin {
- final zupCachedImage = inject();
+ final zupNetworkImage = inject();
final infinityAnimationAutoPlay = inject(instanceName: InjectInstanceNames.infinityAnimationAutoPlay);
- List get timeframesExcludingCurrent {
- return YieldTimeFrame.values.where((timeframe) => timeframe != widget.yieldTimeFrame).toList();
+ List get timeframesExcludingCurrent {
+ return PoolDataTimeframe.values.where((timeframe) => timeframe != widget.yieldTimeFrame).toList();
}
@override
@@ -65,7 +66,7 @@ class _YieldCardState extends State with DeviceInfoMixin, KeysMixin {
constraints: const BoxConstraints(maxWidth: 450),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(24),
- color: context.brightness.isDark ? ZupColors.black3 : ZupColors.white,
+ color: context.brightness.isDark ? ZupColors.black2 : ZupColors.white,
border: context.brightness.isDark
? null
: Border.all(width: 0.5, color: ZupThemeColors.borderOnBackgroundSurface.themed(context.brightness)),
@@ -79,16 +80,10 @@ class _YieldCardState extends State with DeviceInfoMixin, KeysMixin {
Row(
mainAxisSize: MainAxisSize.min,
children: [
- Stack(
- clipBehavior: Clip.none,
- children: [
- TokenAvatar(asset: widget.yieldPool.token0, size: 27),
- Positioned(left: 20, child: TokenAvatar(asset: widget.yieldPool.token1, size: 27)),
- ],
- ),
- const SizedBox(width: 27),
+ PoolTokensAvatar(liquidityPool: widget.yieldPool, size: 27),
+ const SizedBox(width: 3),
Text(
- "${widget.yieldPool.token0.symbol.clampMax(8)}/${widget.yieldPool.token1.symbol.clampMax(8)}",
+ "${widget.yieldPool.token0.symbol.clamped(8)}/${widget.yieldPool.token1.symbol.clamped(8)}",
style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 15),
overflow: TextOverflow.ellipsis,
),
@@ -147,6 +142,7 @@ class _YieldCardState extends State with DeviceInfoMixin, KeysMixin {
const SizedBox(width: 5),
Skeleton.ignore(
child: ZupTooltip.widget(
+ isChildBounded: true,
key: Key("yield-breakdown-tooltip-${widget.yieldPool.poolAddress}"),
tooltipChild: yieldBreakdownTooltipChild,
child: Assets.icons.infoCircle.svg(
@@ -189,13 +185,10 @@ class _YieldCardState extends State with DeviceInfoMixin, KeysMixin {
Row(
mainAxisSize: MainAxisSize.min,
children: [
- zupCachedImage.build(
- context,
- widget.yieldPool.protocol.logo,
- radius: 20,
- height: 25,
- width: 25,
- backgroundColor: ZupColors.white,
+ ZupRemoteAvatar(
+ avatarUrl: widget.yieldPool.protocol.logo,
+ size: 20,
+ zupNetworkImage: zupNetworkImage,
),
const SizedBox(width: 10),
Flexible(
@@ -207,23 +200,17 @@ class _YieldCardState extends State with DeviceInfoMixin, KeysMixin {
),
],
),
- if (widget.mainButton != null) ...[
- const SizedBox(height: 30),
- Row(
- children: [
- // ZupIconButton(
- // icon: Assets.icons.chartBar.svg(height: 15, width: 15),
- // onPressed: (_) {},
- // backgroundColor: context.brightness.isDark
- // ? ZupThemeColors.background.themed(Brightness.dark)
- // : ZupColors.gray6,
- // padding: const EdgeInsets.all(15),
- // ),
- // const SizedBox(width: 20),
- Expanded(child: widget.mainButton!),
+
+ if (widget.mainButton != null || widget.secondaryButton != null) const SizedBox(height: 30),
+ Row(
+ children: [
+ if (widget.secondaryButton != null) ...[
+ widget.secondaryButton!,
+ if (widget.mainButton != null) const SizedBox(width: 10),
],
- ),
- ],
+ if (widget.mainButton != null) Expanded(child: widget.mainButton!),
+ ],
+ ),
],
),
),
diff --git a/lib/widgets/zup_cached_image.dart b/lib/widgets/zup_cached_image.dart
deleted file mode 100644
index 1afc19c..0000000
--- a/lib/widgets/zup_cached_image.dart
+++ /dev/null
@@ -1,59 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:skeletonizer/skeletonizer.dart';
-import 'package:zup_core/extensions/extensions.dart';
-import 'package:zup_ui_kit/zup_ui_kit.dart';
-
-class ZupCachedImage {
- String _parseImageUrl(String url) {
- if (url.startsWith("ipfs://")) {
- return url.replaceFirst("ipfs://", "https://ipfs.io/ipfs/");
- }
-
- return url;
- }
-
- Widget build(
- BuildContext context,
- String url, {
- double? height,
- double? width,
- double? radius,
- Widget? placeholder,
- Color? backgroundColor,
- ImageErrorWidgetBuilder? errorWidget,
- }) {
- return Container(
- key: ValueKey(url),
- decoration: BoxDecoration(
- borderRadius: BorderRadius.circular(radius ?? 0),
- border: Border.all(width: 0.5, color: ZupThemeColors.borderOnBackground.themed(context.brightness)),
- ),
- // cache not implemented yet because of web issue rendering images from other domains (https://github.com/Baseflow/flutter_cached_network_image/issues/972)
- child: ClipRRect(
- borderRadius: BorderRadius.circular(radius ?? 0),
- child: Skeleton.shade(
- child: Container(
- color: backgroundColor,
- child: Image.network(
- _parseImageUrl(url),
- height: height,
- width: width,
- fit: BoxFit.cover,
- errorBuilder: errorWidget,
- frameBuilder: (context, child, frame, wasSynchronouslyLoaded) {
- if (frame == null) {
- return Container(
- color: ZupThemeColors.background.themed(context.brightness),
- child: placeholder ?? ZupCircularLoadingIndicator(size: height ?? 20),
- );
- }
- return child;
- },
- webHtmlElementStrategy: WebHtmlElementStrategy.fallback,
- ),
- ),
- ),
- ),
- );
- }
-}
diff --git a/pubspec.lock b/pubspec.lock
index 12791b2..ef4b19a 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -386,6 +386,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.1.1"
+ flex_list:
+ dependency: "direct main"
+ description:
+ name: flex_list
+ sha256: c41dc497e972e58899a8173ab0329222e220d4bab4d83454e325e75e21655da2
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.2.0"
flutter:
dependency: "direct main"
description: flutter
@@ -1281,7 +1289,7 @@ packages:
description:
path: "."
ref: main
- resolved-ref: "0f6c0f2a93e858209109c9f436f53bbc7c5e3659"
+ resolved-ref: c198c66a75813e721037169eeeb5d5e95b324179
url: "https://github.com/Zup-Protocol/web3kit.git"
source: git
version: "0.0.1"
@@ -1330,7 +1338,7 @@ packages:
description:
path: "."
ref: main
- resolved-ref: "72ba56fe442b4b41ae9ef98b1a73a7e19e0f3ee8"
+ resolved-ref: "845fc3e2aeb77682c1070ae314924c52c7a58b21"
url: "https://github.com/Zup-Protocol/zup-core.git"
source: git
version: "0.0.1"
@@ -1339,7 +1347,7 @@ packages:
description:
path: "."
ref: main
- resolved-ref: "7e91f16e6cde62eaabeeccbb646b41803037f211"
+ resolved-ref: "7448b42d8821713fedc77b22049ce0ba6ca9fde6"
url: "https://github.com/Zup-Protocol/zup-ui-kit.git"
source: git
version: "0.0.1"
diff --git a/pubspec.yaml b/pubspec.yaml
index 40e560b..a2ff0b0 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -53,6 +53,7 @@ dependencies:
firebase_analytics: ^11.4.5
envied: ^1.1.1
async: ^2.13.0
+ flex_list: ^1.2.0
# dependency_overrides:
# zup_core:
diff --git a/test/app/create/create_page_select_token_stage_test.dart b/test/app/create/create_page_select_token_stage_test.dart
index 9dd0e37..2ec44dd 100644
--- a/test/app/create/create_page_select_token_stage_test.dart
+++ b/test/app/create/create_page_select_token_stage_test.dart
@@ -9,8 +9,8 @@ import 'package:zup_app/app/app_cubit/app_cubit.dart';
import 'package:zup_app/app/create/create_page_select_tokens_stage.dart';
import 'package:zup_app/core/cache.dart';
import 'package:zup_app/core/debouncer.dart';
+import 'package:zup_app/core/dtos/multi_chain_token_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_group_dto.dart';
import 'package:zup_app/core/dtos/token_list_dto.dart';
import 'package:zup_app/core/enums/networks.dart';
@@ -20,8 +20,8 @@ import 'package:zup_app/core/repositories/tokens_repository.dart';
import 'package:zup_app/core/zup_navigator.dart';
import 'package:zup_app/widgets/token_card.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_core.dart';
+import 'package:zup_ui_kit/zup_ui_kit.dart';
import '../../golden_config.dart';
import '../../mocks.dart';
@@ -47,7 +47,7 @@ void main() {
cache = CacheMock();
inject.registerFactory(() => appCubit);
- inject.registerFactory(() => mockZupCachedImage());
+ inject.registerFactory(() => mockZupNetworkImage());
inject.registerFactory(() => Debouncer(milliseconds: 0));
inject.registerFactory(() => zupNavigator);
inject.registerFactory(() => cache);
@@ -171,8 +171,8 @@ void main() {
when(() => tokensRepository.getTokenList(any())).thenAnswer(
(_) async => (TokenListDto(
popularTokens: [
- TokenDto(addresses: {appCubit.selectedNetwork.chainId: token1Name}, name: token0Name),
- TokenDto(addresses: {appCubit.selectedNetwork.chainId: token0Name}, name: token1Name),
+ MultiChainTokenDto(addresses: {appCubit.selectedNetwork.chainId: token1Name}, name: token0Name),
+ MultiChainTokenDto(addresses: {appCubit.selectedNetwork.chainId: token0Name}, name: token1Name),
],
)),
);
@@ -207,8 +207,8 @@ void main() {
when(() => tokensRepository.getTokenList(any())).thenAnswer(
(_) async => (TokenListDto(
popularTokens: [
- TokenDto(addresses: {appCubit.selectedNetwork.chainId: "token1"}, name: "Token1"),
- TokenDto(addresses: {appCubit.selectedNetwork.chainId: "token2"}, name: "Token2"),
+ MultiChainTokenDto(addresses: {appCubit.selectedNetwork.chainId: "token1"}, name: "Token1"),
+ MultiChainTokenDto(addresses: {appCubit.selectedNetwork.chainId: "token2"}, name: "Token2"),
],
)),
);
@@ -298,12 +298,12 @@ void main() {
const token1Id = "87";
final tokens = [
- TokenDto.fixture().copyWith(
+ MultiChainTokenDto.fixture().copyWith(
name: "TokenA",
internalId: token0Id,
addresses: {appCubit.selectedNetwork.chainId: token0Id},
),
- TokenDto.fixture().copyWith(
+ MultiChainTokenDto.fixture().copyWith(
name: "TokenB",
internalId: token1Id,
addresses: {appCubit.selectedNetwork.chainId: token1Id},
diff --git a/test/app/create/create_page_test.dart b/test/app/create/create_page_test.dart
index 8658efa..9a9bb7e 100644
--- a/test/app/create/create_page_test.dart
+++ b/test/app/create/create_page_test.dart
@@ -9,8 +9,8 @@ import 'package:zup_app/core/enums/networks.dart';
import 'package:zup_app/core/injections.dart';
import 'package:zup_app/core/repositories/protocol_repository.dart';
import 'package:zup_app/core/zup_navigator.dart';
-import 'package:zup_app/widgets/zup_cached_image.dart';
import 'package:zup_core/zup_core.dart';
+import 'package:zup_ui_kit/zup_ui_kit.dart';
import '../../golden_config.dart';
import '../../mocks.dart';
@@ -24,7 +24,7 @@ void main() {
cache = CacheMock();
inject.registerFactory(() => cache);
- inject.registerFactory(() => mockZupCachedImage());
+ inject.registerFactory(() => mockZupNetworkImage());
inject.registerFactory(() => appCubit);
inject.registerFactory(() => ZupNavigatorMock());
inject.registerFactory(() => ZupSingletonCache.shared);
@@ -38,9 +38,12 @@ void main() {
tearDown(() => inject.reset());
Future goldenBuilder() async => await goldenDeviceBuilder(const CreatePage());
- zGoldenTest("When loading the create page, it should show the token selection stage",
- goldenFileName: "create_page_initial_stage", (tester) async {
- await tester.pumpDeviceBuilder(await goldenBuilder());
- await tester.pumpAndSettle();
- });
+ zGoldenTest(
+ "When loading the create page, it should show the token selection stage",
+ goldenFileName: "create_page_initial_stage",
+ (tester) async {
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.pumpAndSettle();
+ },
+ );
}
diff --git a/test/app/create/deposit/deposit_cubit_test.dart b/test/app/create/deposit/deposit_cubit_test.dart
index 2ec9345..b50c1f7 100644
--- a/test/app/create/deposit/deposit_cubit_test.dart
+++ b/test/app/create/deposit/deposit_cubit_test.dart
@@ -9,9 +9,9 @@ import 'package:zup_app/app/create/yields/%5Bid%5D/deposit/deposit_cubit.dart';
import 'package:zup_app/core/cache.dart';
import 'package:zup_app/core/dtos/deposit_page_arguments_dto.dart';
import 'package:zup_app/core/dtos/deposit_settings_dto.dart';
+import 'package:zup_app/core/dtos/liquidity_pool_dto.dart';
+import 'package:zup_app/core/dtos/liquidity_pools_search_result_dto.dart';
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';
@@ -38,7 +38,7 @@ void main() {
registerFallbackValue(DepositSettingsDto.fixture());
registerFallbackValue(AppNetworks.sepolia);
registerFallbackValue(PoolSearchSettingsDto.fixture());
- registerFallbackValue(YieldDto.fixture());
+ registerFallbackValue(LiquidityPoolDto.fixture());
poolService = PoolServiceMock();
yieldRepository = YieldRepositoryMock();
@@ -51,7 +51,7 @@ void main() {
when(
() => navigator.currentPageArguments,
- ).thenReturn(const DepositPageArgumentsDto().copyWith(yieldPool: YieldDto.fixture()).toJson());
+ ).thenReturn(const DepositPageArgumentsDto().copyWith(yieldPool: LiquidityPoolDto.fixture()).toJson());
sut = DepositCubit(yieldRepository, zupSingletonCache, wallet, cache, poolService, navigator);
@@ -66,7 +66,7 @@ void main() {
group1Id: any(named: "group1Id"),
testnetMode: any(named: "testnetMode"),
),
- ).thenAnswer((_) async => YieldsDto.fixture());
+ ).thenAnswer((_) async => LiquidityPoolsSearchResultDto.fixture());
when(() => cache.getPoolSearchSettings()).thenReturn(PoolSearchSettingsDto.fixture());
when(
@@ -353,7 +353,7 @@ void main() {
"""When calling 'fetchCurrentPoolInfo', after fetching the pool data from the repository,
it should get the pool sqrt price from the pool service, and emit the new sqrt price gotten""",
() async {
- final pool = YieldDto.fixture();
+ final pool = LiquidityPoolDto.fixture();
final expectedSqrtPriceX96 = BigInt.from(126128912198);
when(() => poolService.getSqrtPriceX96(pool)).thenAnswer((_) async => expectedSqrtPriceX96);
@@ -381,7 +381,7 @@ void main() {
it should get the pool sqrt price from the pool service, and assign it to the latest
poolSqrtPriceX96 variable""",
() async {
- final pool = YieldDto.fixture();
+ final pool = LiquidityPoolDto.fixture();
final expectedSqrtPriceX96 = BigInt.from(1111);
when(() => poolService.getSqrtPriceX96(pool)).thenAnswer((_) async => expectedSqrtPriceX96);
@@ -407,7 +407,7 @@ void main() {
"""When calling 'fetchCurrentPoolInfo', after fetching the pool data from the repository,
it should emit the success state with the pool data""",
() async {
- final pool = YieldDto.fixture().copyWith(poolAddress: "pool for testing emit success");
+ final pool = LiquidityPoolDto.fixture().copyWith(poolAddress: "pool for testing emit success");
when(() => navigator.getQueryParam(DepositRouteParamsNames().network)).thenReturn("sepolia");
when(() => navigator.getIdFromPath).thenReturn("0xbas");
@@ -458,7 +458,7 @@ void main() {
"""When instaciating the cubit, and there is a pool coming from the arguments,
it should assign it to the yieldpool variable and not fetch the pool info from the repository""",
() {
- final pool = YieldDto.fixture().copyWith(poolAddress: "jajajajaja");
+ final pool = LiquidityPoolDto.fixture().copyWith(poolAddress: "jajajajaja");
when(() => navigator.currentPageArguments).thenReturn(DepositPageArgumentsDto(yieldPool: pool).toJson());
@@ -481,7 +481,7 @@ void main() {
it should emit a new pool sqrt price got from the yield dto""",
() async {
final expectedSqrtPriceX96 = BigInt.from(989998899);
- final pool = YieldDto.fixture().copyWith(
+ final pool = LiquidityPoolDto.fixture().copyWith(
poolAddress: "jajajajaja",
latestSqrtPriceX96: expectedSqrtPriceX96.toString(),
);
@@ -500,7 +500,7 @@ void main() {
it should assign the pool sqrt price to the latest pool sqrt price variable""",
() async {
final expectedSqrtPriceX96 = BigInt.from(5545529927344);
- final pool = YieldDto.fixture().copyWith(
+ final pool = LiquidityPoolDto.fixture().copyWith(
poolAddress: "jajajajaja",
latestSqrtPriceX96: expectedSqrtPriceX96.toString(),
);
@@ -517,7 +517,7 @@ void main() {
"""When instaciating the cubit, and there is a pool coming from the arguments,
it should emit the success state passing the pool from the arguments""",
() async {
- final pool = YieldDto.fixture().copyWith(poolAddress: "someel cool pool idool");
+ final pool = LiquidityPoolDto.fixture().copyWith(poolAddress: "someel cool pool idool");
when(() => navigator.currentPageArguments).thenReturn(DepositPageArgumentsDto(yieldPool: pool).toJson());
diff --git a/test/app/create/deposit/deposit_page_test.dart b/test/app/create/deposit/deposit_page_test.dart
index 36992cb..59ffa26 100644
--- a/test/app/create/deposit/deposit_page_test.dart
+++ b/test/app/create/deposit/deposit_page_test.dart
@@ -17,12 +17,12 @@ import 'package:zup_app/app/create/yields/%5Bid%5D/deposit/deposit_page.dart';
import 'package:zup_app/app/create/yields/%5Bid%5D/deposit/widgets/preview_deposit_modal/preview_deposit_modal.dart';
import 'package:zup_app/core/cache.dart';
import 'package:zup_app/core/dtos/deposit_settings_dto.dart';
+import 'package:zup_app/core/dtos/liquidity_pool_dto.dart';
+import 'package:zup_app/core/dtos/liquidity_pools_search_result_dto.dart';
import 'package:zup_app/core/dtos/pool_search_settings_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/yield_timeframe.dart';
+import 'package:zup_app/core/enums/pool_data_timeframe.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';
@@ -31,8 +31,8 @@ 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/core/zup_route_params_names.dart';
-import 'package:zup_app/widgets/zup_cached_image.dart';
import 'package:zup_core/zup_core.dart';
+import 'package:zup_ui_kit/zup_network_image.dart';
import '../../../golden_config.dart';
import '../../../mocks.dart';
@@ -85,7 +85,7 @@ void main() {
inject.registerFactory>(() => GlobalKey());
inject.registerFactory(() => navigator);
inject.registerFactory