Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
b2ba712
feat[ZUP-88]: add support for ethereum mainnet
RyanHolanda Apr 4, 2025
b98ff0c
chore: add slippage check error message
RyanHolanda Apr 5, 2025
6123b8e
fix: images not loading on web
RyanHolanda Apr 5, 2025
eb0641b
fix: image not updating when changing the url
RyanHolanda Apr 5, 2025
2f6ad59
feat: add tvl info in the pool && min tvl filter
RyanHolanda Apr 10, 2025
6f117c8
chore: bump flutter bootstrap version
RyanHolanda Apr 10, 2025
abdc5da
ci: update install dependencies command
RyanHolanda Apr 10, 2025
686d1e1
ci: remove make commands from CI
RyanHolanda Apr 10, 2025
06fccdd
Merge branch 'main' into staging
RyanHolanda Apr 17, 2025
74a219c
docs: add instructions to add a new network in readme
RyanHolanda Apr 17, 2025
d9da377
feat: add scroll mainnet
RyanHolanda Apr 28, 2025
3f6e195
feat: add network to the deposit page path
RyanHolanda Apr 29, 2025
79261a4
feat: add privacy policy + terms and coditions
RyanHolanda Apr 29, 2025
46c7b33
fix: tests
RyanHolanda Apr 29, 2025
2050e71
Merge branch 'main' into staging
RyanHolanda Apr 30, 2025
e1e73a1
feat: add cookies consent
RyanHolanda Apr 30, 2025
175d4d0
Merge branch 'main' into staging
RyanHolanda May 13, 2025
58bbeac
feat: allow crosschain search
RyanHolanda May 16, 2025
0539501
feat: does not allow search by address in all networks
RyanHolanda May 17, 2025
326b85b
fix: user token amount not updating correctly when changing yields
RyanHolanda May 18, 2025
c933415
feat: add base network
RyanHolanda May 20, 2025
7ba9fa6
Merge branch 'main' into staging
RyanHolanda May 23, 2025
afad225
feat: calculate USD price of input asset
RyanHolanda May 26, 2025
b8a83cd
feat: allow uniswap v4
RyanHolanda Jun 11, 2025
396eae9
Update flutter version on CI
RyanHolanda Jun 24, 2025
1827998
fix: wrong token amount appearing when connecting wallet with tokens …
RyanHolanda Jul 6, 2025
a9f3dfd
chore: remove base support
RyanHolanda Jul 14, 2025
1da6c94
bump version
RyanHolanda Jul 14, 2025
1da2f67
Merge branch 'main' into staging
RyanHolanda Jul 14, 2025
add2ab8
Merge branch 'main' into staging
RyanHolanda Jul 14, 2025
1729fc4
feat: add prefixed percentage range buttons
RyanHolanda Jul 15, 2025
fe3e2a1
bump version
RyanHolanda Jul 15, 2025
f733f3a
Merge branch 'main' into staging
RyanHolanda Jul 15, 2025
b211dce
Merge branch 'main' into staging
RyanHolanda Jul 15, 2025
9fcf3e1
feat: add support to filter DEXs
RyanHolanda Jul 19, 2025
4b6cbdc
feat: add bnb chain support (#37)
RyanHolanda Jul 21, 2025
bb05a7c
chore: remove bnb support
RyanHolanda Jul 21, 2025
3536450
fix: uniswap v4 deposits with full amount available not working
RyanHolanda Jul 23, 2025
929db21
erge branch 'main' into staging
RyanHolanda Jul 23, 2025
1df5fd3
feat: add token groups
RyanHolanda Aug 1, 2025
c6f3612
bump version
RyanHolanda Aug 1, 2025
ef6270d
fix: not showing token groups in live app & preview modal displaying …
RyanHolanda Aug 1, 2025
2bb46c8
fix: loading icon for token group icon too big
RyanHolanda Aug 1, 2025
d4c653d
Merge branch 'main' into staging
RyanHolanda Aug 4, 2025
e4dcf74
feat: add support to dark mode
RyanHolanda Aug 5, 2025
2c5d286
fix: make token input card text color brigther
RyanHolanda Aug 6, 2025
f9ee458
Merge branch 'main' into staging
RyanHolanda Aug 6, 2025
15c7b2d
feat: add support to base & hyperliquid (#44)
RyanHolanda Aug 21, 2025
45c755e
fix: zup cached image loading not showing when setting background
RyanHolanda Aug 21, 2025
d4978f9
chore: update min default pool tvl for search to 5k usd
RyanHolanda Aug 22, 2025
ded2da6
fix: pancakeswap infinity deposit error
RyanHolanda Aug 22, 2025
8c545f0
Merge branch 'main' into staging
RyanHolanda Aug 22, 2025
4a6c07c
bump version
RyanHolanda Aug 22, 2025
95d2cc4
Merge branch 'main' into staging
RyanHolanda Aug 22, 2025
aa20a24
Merge branch 'main' into staging
RyanHolanda Aug 26, 2025
c3b65a2
feat: add auto slippage
RyanHolanda Aug 26, 2025
6d2bef2
bump version
RyanHolanda Aug 26, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 22 additions & 22 deletions lib/app/create/deposit/deposit_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ 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/cache.dart';
import 'package:zup_app/core/concentrated_liquidity_utils/cl_pool_conversors_mixin.dart';
import 'package:zup_app/core/dtos/deposit_settings_dto.dart';
import 'package:zup_app/core/dtos/pool_search_filters_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/mixins/keys_mixin.dart';
import 'package:zup_app/core/mixins/v3_pool_conversors_mixin.dart';
import 'package:zup_app/core/pool_service.dart';
import 'package:zup_app/core/repositories/yield_repository.dart';
import 'package:zup_app/core/slippage.dart';
Expand All @@ -22,7 +22,7 @@ import 'package:zup_core/zup_core.dart';
part 'deposit_cubit.freezed.dart';
part 'deposit_state.dart';

class DepositCubit extends Cubit<DepositState> with KeysMixin, V3PoolConversorsMixin {
class DepositCubit extends Cubit<DepositState> with KeysMixin, CLPoolConversorsMixin {
DepositCubit(
this._yieldRepository,
this._zupSingletonCache,
Expand All @@ -41,26 +41,26 @@ class DepositCubit extends Cubit<DepositState> with KeysMixin, V3PoolConversorsM
final AppCubit _appCubit;
final ZupAnalytics _zupAnalytics;

final StreamController<BigInt?> _pooltickStreamController = StreamController.broadcast();
final StreamController<BigInt?> _poolSqrtPriceX96StreamController = StreamController.broadcast();
final StreamController<YieldDto?> _selectedYieldStreamController = StreamController.broadcast();
final Duration _poolTickExpiration = const Duration(seconds: 30);
final Duration _poolSqrtPriceX96CacheExpiration = const Duration(seconds: 30);

BigInt? _latestPoolTick;
BigInt? _latestPoolSqrtPriceX96;
YieldDto? _selectedYield;

late final Stream<YieldDto?> selectedYieldStream = _selectedYieldStreamController.stream;
late final Stream<BigInt?> poolTickStream = _pooltickStreamController.stream;
late final Stream<BigInt?> poolSqrtPriceX96Stream = _poolSqrtPriceX96StreamController.stream;

YieldDto? get selectedYield => _selectedYield;
BigInt? get latestPoolTick => _latestPoolTick;
BigInt? get latestPoolSqrtPriceX96 => _latestPoolSqrtPriceX96;
DepositSettingsDto get depositSettings => _cache.getDepositSettings();
PoolSearchSettingsDto get poolSearchSettings => _cache.getPoolSearchSettings();

void setup() async {
Timer.periodic(_poolTickExpiration, (timer) {
if (_pooltickStreamController.isClosed) return timer.cancel();
Timer.periodic(_poolSqrtPriceX96CacheExpiration, (timer) {
if (_poolSqrtPriceX96StreamController.isClosed) return timer.cancel();

if (selectedYield != null) getSelectedPoolTick();
if (selectedYield != null) getSelectedPoolSqrtPriceX96();
});
}

Expand Down Expand Up @@ -116,29 +116,29 @@ class DepositCubit extends Cubit<DepositState> with KeysMixin, V3PoolConversorsM
_selectedYieldStreamController.add(selectedYield);

if (selectedYield != null) {
_latestPoolTick = BigInt.parse(yieldDto!.latestTick);
_pooltickStreamController.add(_latestPoolTick);
_latestPoolSqrtPriceX96 = BigInt.parse(yieldDto!.latestSqrtPriceX96);
_poolSqrtPriceX96StreamController.add(_latestPoolSqrtPriceX96);

await getSelectedPoolTick(forceRefresh: true);
await getSelectedPoolSqrtPriceX96(forceRefresh: true);
}
}

Future<void> getSelectedPoolTick({bool forceRefresh = false}) async {
Future<void> getSelectedPoolSqrtPriceX96({bool forceRefresh = false}) async {
if (selectedYield == null) return;

final selectedYieldBeforeCall = selectedYield;

final tick = await _zupSingletonCache.run(
() => _poolService.getPoolTick(selectedYieldBeforeCall!),
expiration: _poolTickExpiration - const Duration(seconds: 1),
final sqrtPriceX96 = await _zupSingletonCache.run(
() => _poolService.getSqrtPriceX96(selectedYieldBeforeCall!),
expiration: _poolSqrtPriceX96CacheExpiration - const Duration(seconds: 1),
ignoreCache: forceRefresh,
key: poolTickCacheKey(network: selectedYield!.network, poolAddress: selectedYield!.poolAddress),
key: poolSqrtPriceCacheKey(network: selectedYield!.network, poolAddress: selectedYield!.poolAddress),
);

if (selectedYieldBeforeCall != selectedYield) return await getSelectedPoolTick();
if (selectedYieldBeforeCall != selectedYield) return await getSelectedPoolSqrtPriceX96();

_pooltickStreamController.add(tick);
_latestPoolTick = tick;
_poolSqrtPriceX96StreamController.add(sqrtPriceX96);
_latestPoolSqrtPriceX96 = sqrtPriceX96;
}

Future<double> getWalletTokenAmount(String tokenAddress, {required AppNetworks network}) async {
Expand Down Expand Up @@ -172,7 +172,7 @@ class DepositCubit extends Cubit<DepositState> with KeysMixin, V3PoolConversorsM

@override
Future<void> close() async {
await _pooltickStreamController.close();
await _poolSqrtPriceX96StreamController.close();
await _selectedYieldStreamController.close();
return super.close();
}
Expand Down
86 changes: 46 additions & 40 deletions lib/app/create/deposit/deposit_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ import 'package:zup_app/app/create/deposit/widgets/preview_deposit_modal/preview
import 'package:zup_app/app/create/deposit/widgets/range_selector.dart';
import 'package:zup_app/app/create/deposit/widgets/token_amount_input_card/token_amount_input_card.dart';
import 'package:zup_app/core/cache.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/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/pool_search_filters_dto.dart';
import 'package:zup_app/core/dtos/token_dto.dart';
Expand All @@ -26,12 +30,9 @@ 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';
import 'package:zup_app/core/injections.dart';
import 'package:zup_app/core/mixins/v3_pool_conversors_mixin.dart';
import 'package:zup_app/core/mixins/v3_pool_liquidity_calculations_mixin.dart';
import 'package:zup_app/core/pool_service.dart';
import 'package:zup_app/core/repositories/yield_repository.dart';
import 'package:zup_app/core/slippage.dart';
import 'package:zup_app/core/v3_v4_pool_constants.dart';
import 'package:zup_app/core/zup_analytics.dart';
import 'package:zup_app/core/zup_navigator.dart';
import 'package:zup_app/core/zup_route_params_names.dart';
Expand Down Expand Up @@ -73,7 +74,12 @@ class DepositPage extends StatefulWidget {
}

class _DepositPageState extends State<DepositPage>
with V3PoolConversorsMixin, V3PoolLiquidityCalculationsMixin, DeviceInfoMixin {
with
CLPoolConversorsMixin,
CLPoolLiquidityCalculationsMixin,
DeviceInfoMixin,
CLPoolLiquidityCalculationsMixin,
CLSqrtPriceMath {
final lottieClick = inject<LottieBuilder>(instanceName: InjectInstanceNames.lottieClick);
final lottieEmpty = inject<LottieBuilder>(instanceName: InjectInstanceNames.lottieEmpty);
final lottieRadar = inject<LottieBuilder>(instanceName: InjectInstanceNames.lottieRadar);
Expand Down Expand Up @@ -125,7 +131,7 @@ class _DepositPageState extends State<DepositPage>
double maxPrice = 0;
RangeController minRangeController = RangeController();
RangeController maxRangeController = RangeController();
StreamSubscription<BigInt?>? _poolTickStreamSubscription;
StreamSubscription<BigInt?>? _poolSqrtPriceX96StreamSubscription;
YieldTimeFrame selectedYieldTimeFrame = YieldTimeFrame.day;

late Slippage selectedSlippage = _cubit.depositSettings.slippage;
Expand All @@ -142,19 +148,19 @@ class _DepositPageState extends State<DepositPage>
bool get isQuoteTokenNeeded => !isOutOfRange.minPrice;

double get currentPrice {
if (_cubit.latestPoolTick == null || _cubit.selectedYield == null) return 0;
if (_cubit.latestPoolSqrtPriceX96 == null || _cubit.selectedYield == null) return 0;

final price = tickToPrice(
tick: _cubit.latestPoolTick!,
final price = sqrtPriceX96ToPrice(
sqrtPriceX96: _cubit.latestPoolSqrtPriceX96!,
poolToken0Decimals: _cubit.selectedYield!.token0NetworkDecimals,
poolToken1Decimals: _cubit.selectedYield!.token1NetworkDecimals,
);

return areTokensReversed ? price.priceAsQuoteToken : price.priceAsBaseToken;
return areTokensReversed ? price.token1PerToken0 : price.token0PerToken1;
}

({bool minPrice, bool maxPrice, bool any}) get isOutOfRange {
if (_cubit.latestPoolTick == null) return (minPrice: false, maxPrice: false, any: false);
if (_cubit.latestPoolSqrtPriceX96 == null) return (minPrice: false, maxPrice: false, any: false);

final isMinPriceOutOfRange = !isMinRangeInfinity && (minPrice) > currentPrice;
final isMaxPriceOutOfRange = !isMaxRangeInfinity && (maxPrice) < currentPrice;
Expand Down Expand Up @@ -231,19 +237,19 @@ class _DepositPageState extends State<DepositPage>
}

void calculateDepositTokensAmount() {
if (_cubit.latestPoolTick == null || _cubit.selectedYield == null) return;
if (_cubit.latestPoolSqrtPriceX96 == null || _cubit.selectedYield == null) return;

if (isOutOfRange.minPrice) return quoteTokenAmountController.clear();
if (isOutOfRange.maxPrice) return baseTokenAmountController.clear();

final maxTickPrice = tickToPrice(
tick: V3V4PoolConstants.maxTick,
tick: CLPoolConstants.maxTick,
poolToken0Decimals: _cubit.selectedYield!.token0NetworkDecimals,
poolToken1Decimals: _cubit.selectedYield!.token1NetworkDecimals,
);

final minTickPrice = tickToPrice(
tick: V3V4PoolConstants.minTick,
tick: CLPoolConstants.minTick,
poolToken0Decimals: _cubit.selectedYield!.token0NetworkDecimals,
poolToken1Decimals: _cubit.selectedYield!.token1NetworkDecimals,
);
Expand All @@ -262,19 +268,19 @@ class _DepositPageState extends State<DepositPage>

final newQuoteTokenAmount = Decimal.tryParse(
calculateToken1AmountFromToken0(
double.tryParse(baseTokenAmountController.text) ?? 0,
currentPrice,
getMinPrice(),
getMaxPrice(),
currentPrice: currentPrice,
priceLower: getMinPrice(),
priceUpper: getMaxPrice(),
tokenXAmount: double.tryParse(baseTokenAmountController.text) ?? 0,
).toString(),
)?.toStringAsFixed(quoteToken.decimals[_cubit.selectedYield!.network.chainId]!);

final newBaseTokenAmount = Decimal.tryParse(
calculateToken0AmountFromToken1(
double.tryParse(quoteTokenAmountController.text) ?? 0,
currentPrice,
getMinPrice(),
getMaxPrice(),
currentPrice: currentPrice,
priceLower: getMinPrice(),
priceUpper: getMaxPrice(),
tokenYAmount: double.tryParse(quoteTokenAmountController.text) ?? 0,
).toString(),
)?.toStringAsFixed(baseToken.decimals[_cubit.selectedYield!.network.chainId]!);

Expand Down Expand Up @@ -349,7 +355,7 @@ class _DepositPageState extends State<DepositPage>
token1DepositAmountController: areTokensReversed ? baseTokenAmountController : quoteTokenAmountController,
maxPrice: (isInfinity: isMaxRangeInfinity, price: maxPrice),
minPrice: (isInfinity: isMinRangeInfinity, price: minPrice),
).show(context, currentPoolTick: _cubit.latestPoolTick ?? BigInt.zero);
).show(context, currentPriceX96: _cubit.latestPoolSqrtPriceX96 ?? BigInt.zero);
},
);
}
Expand Down Expand Up @@ -386,8 +392,8 @@ class _DepositPageState extends State<DepositPage>
);
});

_poolTickStreamSubscription = _cubit.poolTickStream.listen((poolTick) {
if (poolTick != null) {
_poolSqrtPriceX96StreamSubscription = _cubit.poolSqrtPriceX96Stream.listen((sqrtPriceX96) {
if (sqrtPriceX96 != null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
setState(() => calculateDepositTokensAmount());
});
Expand All @@ -401,7 +407,7 @@ class _DepositPageState extends State<DepositPage>
void dispose() {
minRangeController.dispose();
maxRangeController.dispose();
_poolTickStreamSubscription?.cancel();
_poolSqrtPriceX96StreamSubscription?.cancel();
super.dispose();
}

Expand Down Expand Up @@ -949,21 +955,21 @@ class _DepositPageState extends State<DepositPage>
if (isMobileSize(context)) ...[const SizedBox(height: 5), tokenSwitcher, const SizedBox(height: 5)],
const SizedBox(height: 10),
StreamBuilder(
stream: _cubit.poolTickStream,
initialData: _cubit.latestPoolTick,
builder: (context, poolTickSnapshot) {
stream: _cubit.poolSqrtPriceX96Stream,
initialData: _cubit.latestPoolSqrtPriceX96,
builder: (context, poolSqrtPriceX96Snaphot) {
return Text(
"1 ${baseToken.symbol} ≈ ${() {
final currentPrice = tickToPrice(tick: poolTickSnapshot.data ?? BigInt.zero, poolToken0Decimals: _cubit.selectedYield!.token0NetworkDecimals, poolToken1Decimals: _cubit.selectedYield!.token1NetworkDecimals);
final currentPrice = sqrtPriceX96ToPrice(sqrtPriceX96: poolSqrtPriceX96Snaphot.data ?? BigInt.zero, poolToken0Decimals: _cubit.selectedYield!.token0NetworkDecimals, poolToken1Decimals: _cubit.selectedYield!.token1NetworkDecimals);

return areTokensReversed ? currentPrice.priceAsQuoteToken : currentPrice.priceAsBaseToken;
return areTokensReversed ? currentPrice.token1PerToken0 : currentPrice.token0PerToken1;
}.call().formatCurrency(useLessThan: true, maxDecimals: 4, isUSD: false)} ${quoteToken.symbol}",
style: TextStyle(
fontSize: 17,
fontWeight: FontWeight.w500,
color: ZupThemeColors.primaryText.themed(context.brightness),
),
).redacted(enabled: poolTickSnapshot.data == null);
).redacted(enabled: poolSqrtPriceX96Snaphot.data == null);
},
),
const SizedBox(height: 10),
Expand Down Expand Up @@ -1006,8 +1012,8 @@ class _DepositPageState extends State<DepositPage>
),
const SizedBox(height: 10),
StreamBuilder(
stream: _cubit.poolTickStream,
builder: (context, snapshot) {
stream: _cubit.poolSqrtPriceX96Stream,
builder: (context, _) {
return RangeSelector(
key: const Key("min-price-selector"),
onUserType: () => percentRange = null,
Expand Down Expand Up @@ -1049,8 +1055,8 @@ class _DepositPageState extends State<DepositPage>
),
const SizedBox(height: 6),
StreamBuilder(
stream: _cubit.poolTickStream,
builder: (context, snapshot) {
stream: _cubit.poolSqrtPriceX96Stream,
builder: (context, _) {
return RangeSelector(
key: const Key("max-price-selector"),
displayBaseTokenSymbol: baseToken.symbol,
Expand Down Expand Up @@ -1109,9 +1115,9 @@ class _DepositPageState extends State<DepositPage>
duration: const Duration(milliseconds: 300),
opacity: isRangeInvalid ? 0.2 : 1,
child: StreamBuilder(
stream: _cubit.poolTickStream,
initialData: _cubit.latestPoolTick,
builder: (context, poolTickSnapshot) {
stream: _cubit.poolSqrtPriceX96Stream,
initialData: _cubit.latestPoolSqrtPriceX96,
builder: (context, sqrtPriceX96Snapshot) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expand All @@ -1130,7 +1136,7 @@ class _DepositPageState extends State<DepositPage>
}

if (!isBaseTokenAmountUserInput &&
!poolTickSnapshot.hasData &&
!sqrtPriceX96Snapshot.hasData &&
quoteTokenAmountController.text.isNotEmpty) {
return S.of(context).loading;
}
Expand Down Expand Up @@ -1159,7 +1165,7 @@ class _DepositPageState extends State<DepositPage>
}

if (isBaseTokenAmountUserInput &&
!poolTickSnapshot.hasData &&
!sqrtPriceX96Snapshot.hasData &&
baseTokenAmountController.text.isNotEmpty) {
return S.of(context).loading;
}
Expand Down
Loading
Loading