From 399255725e0f33b1e0d0c1c4b9d57a13e527d6e8 Mon Sep 17 00:00:00 2001 From: Ryan Holanda Date: Wed, 18 Jun 2025 17:52:05 +0200 Subject: [PATCH 1/2] feat: add bnb chain support --- assets/logos/bnb_chain.svg | 3 + lib/core/enums/networks.dart | 59 +++------- .../create_page_select_token_stage_test.dart | 4 +- test/core/enums/goldens/bnb_network_icon.png | Bin 0 -> 22092 bytes test/core/enums/networks_test.dart | 109 ++++++------------ test/core/pool_service_test.dart | 2 +- 6 files changed, 57 insertions(+), 120 deletions(-) create mode 100644 assets/logos/bnb_chain.svg create mode 100644 test/core/enums/goldens/bnb_network_icon.png diff --git a/assets/logos/bnb_chain.svg b/assets/logos/bnb_chain.svg new file mode 100644 index 0000000..7f8d0da --- /dev/null +++ b/assets/logos/bnb_chain.svg @@ -0,0 +1,3 @@ + + + diff --git a/lib/core/enums/networks.dart b/lib/core/enums/networks.dart index bc1f10d..171b3f5 100644 --- a/lib/core/enums/networks.dart +++ b/lib/core/enums/networks.dart @@ -2,13 +2,13 @@ import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:web3kit/web3kit.dart'; -import 'package:zup_app/core/dtos/token_dto.dart'; import 'package:zup_app/gen/assets.gen.dart'; enum AppNetworks { allNetworks, mainnet, base, + bnb, unichain, scroll, sepolia; @@ -47,6 +47,7 @@ enum AppNetworks { allNetworks => false, base => false, unichain => false, + bnb => false }; String get label => switch (this) { @@ -56,6 +57,7 @@ enum AppNetworks { allNetworks => "All Networks", base => "Base", unichain => "Unichain", + bnb => "BNB Chain", }; Widget get icon => switch (this) { @@ -65,6 +67,7 @@ enum AppNetworks { base => Assets.logos.base.svg(), unichain => Assets.logos.unichain.svg(), allNetworks => Assets.icons.all.svg(), + bnb => Assets.logos.bnbChain.svg() }; ChainInfo get chainInfo => switch (this) { @@ -100,10 +103,17 @@ enum AppNetworks { unichain => ChainInfo( hexChainId: "0x82", chainName: label, - blockExplorerUrls: const ["https://uniscan.xyz/"], + blockExplorerUrls: const ["https://uniscan.xyz"], nativeCurrency: NativeCurrencies.eth.currencyInfo, rpcUrls: [rpcUrl], ), + bnb => ChainInfo( + hexChainId: "0x38", + chainName: label, + blockExplorerUrls: const ["https://bscscan.com"], + nativeCurrency: NativeCurrencies.bnb.currencyInfo, + rpcUrls: [rpcUrl], + ), }; String get wrappedNativeTokenAddress => switch (this) { @@ -112,48 +122,8 @@ enum AppNetworks { mainnet => "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", scroll => "0x5300000000000000000000000000000000000004", base => "0x4200000000000000000000000000000000000006", - unichain => "0x4200000000000000000000000000000000000006" - }; - - TokenDto get wrappedNative => switch (this) { - allNetworks => throw UnimplementedError("allNetworks is not a valid network"), - sepolia => TokenDto( - addresses: {chainId: wrappedNativeTokenAddress}, - name: "Wrapped Ether", - decimals: NativeCurrencies.eth.currencyInfo.decimals, - symbol: "WETH", - logoUrl: "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/info/logo.png", - ), - mainnet => TokenDto( - addresses: {chainId: wrappedNativeTokenAddress}, - decimals: NativeCurrencies.eth.currencyInfo.decimals, - name: "Wrapped Ether", - symbol: "WETH", - logoUrl: "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/info/logo.png", - ), - scroll => TokenDto( - addresses: {chainId: wrappedNativeTokenAddress}, - decimals: NativeCurrencies.eth.currencyInfo.decimals, - name: "Wrapped Ether", - symbol: "WETH", - logoUrl: - "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/scroll/assets/0x5300000000000000000000000000000000000004/logo.png", - ), - base => TokenDto( - addresses: {chainId: wrappedNativeTokenAddress}, - decimals: NativeCurrencies.eth.currencyInfo.decimals, - name: "Wrapped Ether", - symbol: "WETH", - logoUrl: - "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/base/assets/0x4200000000000000000000000000000000000006/logo.png", - ), - unichain => TokenDto( - addresses: {chainId: wrappedNativeTokenAddress}, - decimals: NativeCurrencies.eth.currencyInfo.decimals, - name: "Wrapped Ether", - symbol: "WETH", - logoUrl: "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/unichain/logo.png", - ), + unichain => "0x4200000000000000000000000000000000000006", + bnb => "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c", }; String get rpcUrl => switch (this) { @@ -163,6 +133,7 @@ enum AppNetworks { scroll => "https://scroll-rpc.publicnode.com", base => "https://base-rpc.publicnode.com", unichain => "https://unichain-rpc.publicnode.com", + bnb => "https://bsc-rpc.publicnode.com" }; Future openTx(String txHash) async { 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 238a8c0..6860563 100644 --- a/test/app/create/create_page_select_token_stage_test.dart +++ b/test/app/create/create_page_select_token_stage_test.dart @@ -84,7 +84,7 @@ void main() { "When selecting the B token with the same address as A token, it should change the A token to null, and the B token to the selected token", goldenFileName: "create_page_select_tokens_stage_change_b_token_to_same_token_as_a", (tester) async { const selectedNetwork = AppNetworks.sepolia; - final token0 = selectedNetwork.wrappedNative; + final token0 = TokenDto.fixture(); when(() => tokensRepository.getPopularTokens(any())).thenAnswer( (_) async => [token0], @@ -104,7 +104,7 @@ void main() { "When selecting the A token with the same address as B token, it should change the B token to null and the A token to the selected token", goldenFileName: "create_page_select_tokens_stage_change_a_token_to_same_token_as_b", (tester) async { const selectedNetwork = AppNetworks.sepolia; - final token0 = selectedNetwork.wrappedNative; + final token0 = TokenDto.fixture(); when(() => appCubit.currentChainId).thenReturn(selectedNetwork.chainId); when(() => tokensRepository.getPopularTokens(any())).thenAnswer( diff --git a/test/core/enums/goldens/bnb_network_icon.png b/test/core/enums/goldens/bnb_network_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..198aeebd881d8c55ce1786104dcb49213fc9502b GIT binary patch literal 22092 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 Yvn2Dd$KwOMm=6`g)5PbIDN- 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 z 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 Date: Wed, 25 Jun 2025 13:28:25 +0200 Subject: [PATCH 2/2] fix: get pool tick for pancake swap v4 not working --- ...ake_swap_infinity_cl_pool_manager.abi.json | 36 ++++++++++++ lib/app/create/deposit/deposit_page.dart | 24 ++++---- .../preview_deposit_modal.dart | 24 ++++---- .../preview_deposit_modal_cubit.dart | 12 ++-- .../deposit/widgets/range_selector.dart | 38 +++++++------ lib/core/dtos/protocol_dto.dart | 2 + lib/core/dtos/token_dto.dart | 9 ++- lib/core/dtos/yield_dto.dart | 11 +++- lib/core/dtos/yields_dto.dart | 4 +- lib/core/enums/protocol_id.dart | 9 +++ lib/core/injections.dart | 14 ++--- lib/core/pool_service.dart | 12 ++++ ...ge_selector_is_infinity_increase_price.png | Bin 25911 -> 25891 bytes ...or_is_infinity_increase_price_reversed.png | Bin 27472 -> 25891 bytes .../preview_deposit_modal_cubit_test.dart | 28 ++++----- .../preview_deposit_modal_test.dart | 48 ++++++++-------- .../deposit/widgets/range_selector_test.dart | 35 +++++------- test/core/enums/protocol_id_test.dart | 18 ++++++ test/core/pool_service_test.dart | 53 +++++++++++++++++- test/mocks.dart | 5 ++ .../token_selector_modal_cubit_test.dart | 8 +-- 21 files changed, 267 insertions(+), 123 deletions(-) create mode 100644 lib/abis/pancake_swap_infinity_cl_pool_manager.abi.json create mode 100644 lib/core/enums/protocol_id.dart create mode 100644 test/core/enums/protocol_id_test.dart diff --git a/lib/abis/pancake_swap_infinity_cl_pool_manager.abi.json b/lib/abis/pancake_swap_infinity_cl_pool_manager.abi.json new file mode 100644 index 0000000..1117a3b --- /dev/null +++ b/lib/abis/pancake_swap_infinity_cl_pool_manager.abi.json @@ -0,0 +1,36 @@ +[ + { + "inputs": [ + { + "internalType": "PoolId", + "name": "id", + "type": "bytes32" + } + ], + "name": "getSlot0", + "outputs": [ + { + "internalType": "uint160", + "name": "sqrtPriceX96", + "type": "uint160" + }, + { + "internalType": "int24", + "name": "tick", + "type": "int24" + }, + { + "internalType": "uint24", + "name": "protocolFee", + "type": "uint24" + }, + { + "internalType": "uint24", + "name": "lpFee", + "type": "uint24" + } + ], + "stateMutability": "view", + "type": "function" + } +] \ No newline at end of file diff --git a/lib/app/create/deposit/deposit_page.dart b/lib/app/create/deposit/deposit_page.dart index d7c933d..5fc51c4 100644 --- a/lib/app/create/deposit/deposit_page.dart +++ b/lib/app/create/deposit/deposit_page.dart @@ -119,8 +119,8 @@ class _DepositPageState extends State final price = tickToPrice( tick: _cubit.latestPoolTick!, - poolToken0Decimals: _cubit.selectedYield!.token0.decimals, - poolToken1Decimals: _cubit.selectedYield!.token1.decimals, + poolToken0Decimals: _cubit.selectedYield!.token0NetworkDecimals, + poolToken1Decimals: _cubit.selectedYield!.token1NetworkDecimals, ); return areTokensReversed ? price.priceAsQuoteToken : price.priceAsBaseToken; @@ -185,14 +185,14 @@ class _DepositPageState extends State final maxTickPrice = tickToPrice( tick: V3V4PoolConstants.maxTick, - poolToken0Decimals: _cubit.selectedYield!.token0.decimals, - poolToken1Decimals: _cubit.selectedYield!.token1.decimals, + poolToken0Decimals: _cubit.selectedYield!.token0NetworkDecimals, + poolToken1Decimals: _cubit.selectedYield!.token1NetworkDecimals, ); final minTickPrice = tickToPrice( tick: V3V4PoolConstants.minTick, - poolToken0Decimals: _cubit.selectedYield!.token0.decimals, - poolToken1Decimals: _cubit.selectedYield!.token1.decimals, + poolToken0Decimals: _cubit.selectedYield!.token0NetworkDecimals, + poolToken1Decimals: _cubit.selectedYield!.token1NetworkDecimals, ); double getMinPrice() { @@ -727,8 +727,8 @@ class _DepositPageState extends State "1 ${baseToken.symbol} ≈ ${() { final currentPrice = tickToPrice( tick: poolTickSnapshot.data ?? BigInt.zero, - poolToken0Decimals: _cubit.selectedYield!.token0.decimals, - poolToken1Decimals: _cubit.selectedYield!.token1.decimals, + poolToken0Decimals: _cubit.selectedYield!.token0NetworkDecimals, + poolToken1Decimals: _cubit.selectedYield!.token1NetworkDecimals, ); return areTokensReversed ? currentPrice.priceAsQuoteToken : currentPrice.priceAsBaseToken; @@ -758,8 +758,8 @@ class _DepositPageState extends State }); }, initialPrice: minPrice, - poolToken0: _cubit.selectedYield!.token0, - poolToken1: _cubit.selectedYield!.token1, + poolToken0Decimals: _cubit.selectedYield!.token0NetworkDecimals, + poolToken1Decimals: _cubit.selectedYield!.token1NetworkDecimals, isReversed: areTokensReversed, displayBaseTokenSymbol: baseToken.symbol, displayQuoteTokenSymbol: quoteToken.symbol, @@ -803,8 +803,8 @@ class _DepositPageState extends State type: RangeSelectorType.maxPrice, isInfinity: isMaxRangeInfinity, initialPrice: maxPrice, - poolToken0: _cubit.selectedYield!.token0, - poolToken1: _cubit.selectedYield!.token1, + poolToken0Decimals: _cubit.selectedYield!.token0NetworkDecimals, + poolToken1Decimals: _cubit.selectedYield!.token1NetworkDecimals, isReversed: areTokensReversed, tickSpacing: _cubit.selectedYield!.tickSpacing, state: () { diff --git a/lib/app/create/deposit/widgets/preview_deposit_modal/preview_deposit_modal.dart b/lib/app/create/deposit/widgets/preview_deposit_modal/preview_deposit_modal.dart index 475849e..d4a47fb 100644 --- a/lib/app/create/deposit/widgets/preview_deposit_modal/preview_deposit_modal.dart +++ b/lib/app/create/deposit/widgets/preview_deposit_modal/preview_deposit_modal.dart @@ -118,17 +118,17 @@ class _PreviewDepositModalState extends State with V3PoolCo double get quoteTokenAmount => isReversedLocal ? widget.token0DepositAmount : widget.token1DepositAmount; PreviewDepositModalCubit get cubit => context.read(); BigInt get token0DepositAmount => - widget.token0DepositAmount.parseTokenAmount(decimals: widget.currentYield.token0.decimals); + widget.token0DepositAmount.parseTokenAmount(decimals: widget.currentYield.token0NetworkDecimals); BigInt get token1DepositAmount => - widget.token1DepositAmount.parseTokenAmount(decimals: widget.currentYield.token1.decimals); + widget.token1DepositAmount.parseTokenAmount(decimals: widget.currentYield.token1NetworkDecimals); double get currentPrice { final currentTick = cubit.latestPoolTick; final price = tickToPrice( tick: currentTick, - poolToken0Decimals: widget.currentYield.token0.decimals, - poolToken1Decimals: widget.currentYield.token1.decimals, + poolToken0Decimals: widget.currentYield.token0NetworkDecimals, + poolToken1Decimals: widget.currentYield.token1NetworkDecimals, ); return isReversedLocal ? price.priceAsQuoteToken : price.priceAsBaseToken; @@ -140,16 +140,16 @@ class _PreviewDepositModalState extends State with V3PoolCo return priceToTick( price: (widget.isReversed == !isReversedLocal) ? widget.maxPrice.price : widget.minPrice.price, - poolToken0Decimals: widget.currentYield.token0.decimals, - poolToken1Decimals: widget.currentYield.token1.decimals, + poolToken0Decimals: widget.currentYield.token0NetworkDecimals, + poolToken1Decimals: widget.currentYield.token1NetworkDecimals, isReversed: widget.isReversed, ); } ({double priceAsBaseToken, double priceAsQuoteToken}) price() => tickToPrice( tick: tick(), - poolToken0Decimals: widget.currentYield.token0.decimals, - poolToken1Decimals: widget.currentYield.token1.decimals, + poolToken0Decimals: widget.currentYield.token0NetworkDecimals, + poolToken1Decimals: widget.currentYield.token1NetworkDecimals, ); return isReversedLocal ? price().priceAsQuoteToken : price().priceAsBaseToken; @@ -161,16 +161,16 @@ class _PreviewDepositModalState extends State with V3PoolCo return priceToTick( price: (widget.isReversed == !isReversedLocal) ? widget.minPrice.price : widget.maxPrice.price, - poolToken0Decimals: widget.currentYield.token0.decimals, - poolToken1Decimals: widget.currentYield.token1.decimals, + poolToken0Decimals: widget.currentYield.token0NetworkDecimals, + poolToken1Decimals: widget.currentYield.token1NetworkDecimals, isReversed: widget.isReversed, ); } ({double priceAsBaseToken, double priceAsQuoteToken}) price() => tickToPrice( tick: tick(), - poolToken0Decimals: widget.currentYield.token0.decimals, - poolToken1Decimals: widget.currentYield.token1.decimals, + poolToken0Decimals: widget.currentYield.token0NetworkDecimals, + poolToken1Decimals: widget.currentYield.token1NetworkDecimals, ); return isReversedLocal ? price().priceAsQuoteToken : price().priceAsBaseToken; diff --git a/lib/app/create/deposit/widgets/preview_deposit_modal/preview_deposit_modal_cubit.dart b/lib/app/create/deposit/widgets/preview_deposit_modal/preview_deposit_modal_cubit.dart index 24bca8c..93ba468 100644 --- a/lib/app/create/deposit/widgets/preview_deposit_modal/preview_deposit_modal_cubit.dart +++ b/lib/app/create/deposit/widgets/preview_deposit_modal/preview_deposit_modal_cubit.dart @@ -175,8 +175,8 @@ class PreviewDepositModalCubit extends Cubit with V3Po return priceToTick( price: isReversed ? maxPrice : minPrice, - poolToken0Decimals: _yield.token0.decimals, - poolToken1Decimals: _yield.token1.decimals, + poolToken0Decimals: _yield.token0NetworkDecimals, + poolToken1Decimals: _yield.token1NetworkDecimals, isReversed: isReversed, ); } @@ -194,8 +194,8 @@ class PreviewDepositModalCubit extends Cubit with V3Po return priceToTick( price: isReversed ? minPrice : maxPrice, - poolToken0Decimals: _yield.token0.decimals, - poolToken1Decimals: _yield.token1.decimals, + poolToken0Decimals: _yield.token0NetworkDecimals, + poolToken1Decimals: _yield.token1NetworkDecimals, isReversed: isReversed, ); } @@ -253,8 +253,8 @@ class PreviewDepositModalCubit extends Cubit with V3Po emit(PreviewDepositModalState.depositSuccess(txId: tx.hash)); _zupAnalytics.logDeposit( depositedYield: _yield, - amount0: amount0Desired.parseTokenAmount(decimals: _yield.token0.decimals), - amount1: amount1Desired.parseTokenAmount(decimals: _yield.token1.decimals), + amount0: amount0Desired.parseTokenAmount(decimals: _yield.token0NetworkDecimals), + amount1: amount1Desired.parseTokenAmount(decimals: _yield.token1NetworkDecimals), walletAddress: recipient, ); } catch (e) { diff --git a/lib/app/create/deposit/widgets/range_selector.dart b/lib/app/create/deposit/widgets/range_selector.dart index de702e0..364f433 100644 --- a/lib/app/create/deposit/widgets/range_selector.dart +++ b/lib/app/create/deposit/widgets/range_selector.dart @@ -1,6 +1,5 @@ import 'package:decimal/decimal.dart'; import 'package:flutter/material.dart'; -import 'package:zup_app/core/dtos/token_dto.dart'; import 'package:zup_app/core/extensions/num_extension.dart'; import 'package:zup_app/core/mixins/v3_pool_conversors_mixin.dart'; import 'package:zup_app/core/token_amount_input_formatter.dart'; @@ -45,8 +44,8 @@ class RangeSelectorState { class RangeSelector extends StatefulWidget { const RangeSelector({ super.key, - required this.poolToken0, - required this.poolToken1, + required this.poolToken0Decimals, + required this.poolToken1Decimals, required this.displayBaseTokenSymbol, required this.displayQuoteTokenSymbol, required this.isReversed, @@ -58,8 +57,8 @@ class RangeSelector extends StatefulWidget { this.state = const RangeSelectorState(type: RangeSelectorStateType.regular), }); - final TokenDto poolToken0; - final TokenDto poolToken1; + final int poolToken0Decimals; + final int poolToken1Decimals; final String displayBaseTokenSymbol; final String displayQuoteTokenSymbol; final bool isReversed; @@ -102,8 +101,8 @@ class _RangeSelectorState extends State with V3PoolConversorsMixi double getAdjustedPrice(double price) { final adjustedPrice = priceToClosestValidPrice( price: price, - poolToken0Decimals: widget.poolToken0.decimals, - poolToken1Decimals: widget.poolToken1.decimals, + poolToken0Decimals: widget.poolToken0Decimals, + poolToken1Decimals: widget.poolToken1Decimals, tickSpacing: widget.tickSpacing, isReversed: widget.isReversed, ); @@ -117,13 +116,16 @@ class _RangeSelectorState extends State with V3PoolConversorsMixi if (currentPrice == 0 && !increasing) return; if ((currentPrice == 0 || widget.isInfinity) && increasing) { - final minimumPrice = tickToPrice( - tick: BigInt.from(widget.tickSpacing), - poolToken0Decimals: widget.poolToken0.decimals, - poolToken1Decimals: widget.poolToken1.decimals, + final minimumPrice = priceToClosestValidPrice( + price: 0.000000000000000001, + poolToken0Decimals: widget.poolToken0Decimals, + poolToken1Decimals: widget.poolToken1Decimals, + tickSpacing: widget.tickSpacing, + isReversed: widget.isReversed, ); - userTypedValue = minimumPrice.priceAsBaseToken.toString(); + userTypedValue = minimumPrice.price.toString(); + return adjustTypedAmountAndCallback(); } @@ -131,8 +133,8 @@ class _RangeSelectorState extends State with V3PoolConversorsMixi final BigInt currentTick = tickToClosestValidTick( tick: priceToTick( price: currentPrice, - poolToken0Decimals: widget.poolToken0.decimals, - poolToken1Decimals: widget.poolToken1.decimals, + poolToken0Decimals: widget.poolToken0Decimals, + poolToken1Decimals: widget.poolToken1Decimals, isReversed: widget.isReversed, ), tickSpacing: widget.tickSpacing, @@ -149,8 +151,8 @@ class _RangeSelectorState extends State with V3PoolConversorsMixi double nextPrice() { final nextPrice = tickToPrice( tick: nextTick(), - poolToken0Decimals: widget.poolToken0.decimals, - poolToken1Decimals: widget.poolToken1.decimals, + poolToken0Decimals: widget.poolToken0Decimals, + poolToken1Decimals: widget.poolToken1Decimals, ); if (widget.isReversed) return nextPrice.priceAsQuoteToken; @@ -165,8 +167,8 @@ class _RangeSelectorState extends State with V3PoolConversorsMixi if (widget.initialPrice != null) { final adjustedInitialPrice = priceToClosestValidPrice( price: widget.initialPrice ?? 0, - poolToken0Decimals: widget.poolToken0.decimals, - poolToken1Decimals: widget.poolToken1.decimals, + poolToken0Decimals: widget.poolToken0Decimals, + poolToken1Decimals: widget.poolToken1Decimals, tickSpacing: widget.tickSpacing, isReversed: widget.isReversed, ); diff --git a/lib/core/dtos/protocol_dto.dart b/lib/core/dtos/protocol_dto.dart index 78c7f44..d184d81 100644 --- a/lib/core/dtos/protocol_dto.dart +++ b/lib/core/dtos/protocol_dto.dart @@ -1,4 +1,5 @@ import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:zup_app/core/enums/protocol_id.dart'; part 'protocol_dto.freezed.dart'; part 'protocol_dto.g.dart'; @@ -9,6 +10,7 @@ class ProtocolDto with _$ProtocolDto { @JsonSerializable(explicitToJson: true) const factory ProtocolDto({ + @Default(ProtocolId.unknown) @JsonKey(unknownEnumValue: ProtocolId.unknown) ProtocolId id, @Default("") String name, @Default("") String url, @Default("") String logo, diff --git a/lib/core/dtos/token_dto.dart b/lib/core/dtos/token_dto.dart index e69ddef..2c20e2a 100644 --- a/lib/core/dtos/token_dto.dart +++ b/lib/core/dtos/token_dto.dart @@ -13,7 +13,7 @@ class TokenDto with _$TokenDto { @Default("") String name, @Default("") String logoUrl, @Default({}) Map addresses, - @Default(0) int decimals, + @Default({}) Map decimals, }) = _TokenDto; factory TokenDto.fromJson(Map json) => _$TokenDtoFromJson(json); @@ -23,6 +23,13 @@ class TokenDto with _$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) { diff --git a/lib/core/dtos/yield_dto.dart b/lib/core/dtos/yield_dto.dart index 0ddd6e7..dc9eac2 100644 --- a/lib/core/dtos/yield_dto.dart +++ b/lib/core/dtos/yield_dto.dart @@ -61,6 +61,9 @@ class YieldDto with _$YieldDto { 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]!; + factory YieldDto.fromJson(Map json) => _$YieldDtoFromJson(json); factory YieldDto.fixture() => YieldDto( @@ -73,7 +76,9 @@ class YieldDto with _$YieldDto { poolType: PoolType.v3, token0: TokenDto.fixture().copyWith( symbol: "USDC", - decimals: 6, + decimals: { + AppNetworks.sepolia.chainId: 6, + }, addresses: { AppNetworks.sepolia.chainId: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", }, @@ -82,7 +87,9 @@ class YieldDto with _$YieldDto { ), token1: TokenDto.fixture().copyWith( symbol: "WETH", - decimals: 18, + decimals: { + AppNetworks.sepolia.chainId: 18, + }, addresses: { AppNetworks.sepolia.chainId: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", }, diff --git a/lib/core/dtos/yields_dto.dart b/lib/core/dtos/yields_dto.dart index 15a54a6..2f160f6 100644 --- a/lib/core/dtos/yields_dto.dart +++ b/lib/core/dtos/yields_dto.dart @@ -37,7 +37,7 @@ class YieldsDto with _$YieldsDto { poolType: PoolType.v3, token0: TokenDto( addresses: {11155111: "0x02a3e7E0480B668bD46b42852C58363F93e3bA5C"}, - decimals: 6, + decimals: {11155111: 6}, logoUrl: "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/scroll/assets/0x06eFdBFf2a14a7c8E15944D1F4A48F9F95F663A4/logo.png", name: "USDC", @@ -45,7 +45,7 @@ class YieldsDto with _$YieldsDto { ), token1: TokenDto( addresses: {11155111: "0x5300000000000000000000000000000000000004"}, - decimals: 18, + decimals: {11155111: 18}, logoUrl: "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/scroll/assets/0x5300000000000000000000000000000000000004/logo.png", name: "Wrapped Ether", diff --git a/lib/core/enums/protocol_id.dart b/lib/core/enums/protocol_id.dart new file mode 100644 index 0000000..e955e37 --- /dev/null +++ b/lib/core/enums/protocol_id.dart @@ -0,0 +1,9 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +enum ProtocolId { + @JsonValue("pancake-v4-cl") + pancakeSwapInfinityCL, + unknown; + + bool get isPancakeSwapInfinityCL => this == ProtocolId.pancakeSwapInfinityCL; +} diff --git a/lib/core/injections.dart b/lib/core/injections.dart index e6decbc..7dfc5ec 100644 --- a/lib/core/injections.dart +++ b/lib/core/injections.dart @@ -7,6 +7,7 @@ import 'package:lottie/lottie.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:web3kit/web3kit.dart'; import 'package:zup_app/abis/erc_20.abi.g.dart'; +import 'package:zup_app/abis/pancake_swap_infinity_cl_pool_manager.abi.g.dart'; import 'package:zup_app/abis/uniswap_permit2.abi.g.dart'; import 'package:zup_app/abis/uniswap_v3_pool.abi.g.dart'; import 'package:zup_app/abis/uniswap_v3_position_manager.abi.g.dart'; @@ -115,14 +116,13 @@ Future setupInjections() async { inject.registerLazySingleton(() => EthereumAbiCoder()); + inject.registerLazySingleton( + () => PancakeSwapInfinityClPoolManager(), + ); + inject.registerLazySingleton( - () => PoolService( - inject(), - inject(), - inject(), - inject(), - inject(), - ), + () => PoolService(inject(), inject(), inject(), + inject(), inject(), inject()), ); inject.registerLazySingleton( diff --git a/lib/core/pool_service.dart b/lib/core/pool_service.dart index 8a85d1a..f02deae 100644 --- a/lib/core/pool_service.dart +++ b/lib/core/pool_service.dart @@ -1,6 +1,7 @@ import 'package:clock/clock.dart'; import 'package:web3kit/core/dtos/transaction_response.dart'; import 'package:web3kit/web3kit.dart'; +import 'package:zup_app/abis/pancake_swap_infinity_cl_pool_manager.abi.g.dart'; import 'package:zup_app/abis/uniswap_v3_pool.abi.g.dart'; import 'package:zup_app/abis/uniswap_v3_position_manager.abi.g.dart'; import 'package:zup_app/abis/uniswap_v4_position_manager.abi.g.dart'; @@ -15,6 +16,7 @@ class PoolService with V4PoolLiquidityCalculationsMixin { final UniswapV3PositionManager _uniswapV3PositionManager; final UniswapV4PositionManager _uniswapV4PositionManager; final EthereumAbiCoder _ethereumAbiCoder; + final PancakeSwapInfinityClPoolManager _pancakeSwapInfinityClPoolManager; PoolService( this._uniswapV4StateView, @@ -22,9 +24,19 @@ class PoolService with V4PoolLiquidityCalculationsMixin { this._uniswapV3PositionManager, this._uniswapV4PositionManager, this._ethereumAbiCoder, + this._pancakeSwapInfinityClPoolManager, ); Future getPoolTick(YieldDto forYield) async { + if (forYield.protocol.id.isPancakeSwapInfinityCL) { + final pancakeSwapInfinityCLPoolManagerContract = _pancakeSwapInfinityClPoolManager.fromRpcProvider( + contractAddress: forYield.v4PoolManager!, + rpcUrl: forYield.network.rpcUrl, + ); + + return (await pancakeSwapInfinityCLPoolManagerContract.getSlot0(id: forYield.poolAddress)).tick; + } + if (forYield.poolType.isV4) { final stateView = _uniswapV4StateView.fromRpcProvider( contractAddress: forYield.v4StateView!, diff --git a/test/app/create/deposit/widgets/goldens/range_selector_is_infinity_increase_price.png b/test/app/create/deposit/widgets/goldens/range_selector_is_infinity_increase_price.png index ad2038bfe5a7170abc318aae6cbff3c1f9aa0390..5aa5a7caf457c6bb2d64a87342b61b4deff5ff54 100644 GIT binary patch literal 25891 zcmeIb2UJsCw=RsY-)mQFfS`a1sC1=+G)2IOfRxaSg7hXObO<((E&)NhN)JtX2_Y&X zBE5tj5RjGtAwcK}cLntIJLB9j{(Hyy&lvaqzZt_#c3XR`x#oK2oX?y)2H#UtK6;4d z5FH)eQONCEnsjvgL+I%K#dBaEI8sDhWC4HnxN0ijq$_AWHv@M5<$4pMeE@uX4?KK9 zM|YkMa_fe+XVNUrFf7T+uYt5Z+mDj1k5K=!`y8a=0|(;elyjb5V-FAJusl%62QAqaGq$lDhB#S`|vi88eST#9Vt1^ zv}gCoeT(TIS0D8v?*6sA{~_eU>AkyOpPdN#dC{i>^}C1Y9y~MK{T6Wb=C$2#@@L+j z+5L7t?B0>xZ^tvOisbIQxs}dueNOE@>*%eZs;-KyVBb=cp)9xInfyr`8{hXk6m9hgS2w*HDq&jnS70)r7*_LD_}c>d6En6*{&L(L@^q$Jc+KUka&-&*? zusQU6e7Zjui9fsQk9ME;?WmdBZJh3QJ7nCr(@-70o@<&X2%ktLB(73?(3>(9J$-$8 z6x;~4Zhe(lX$}`nY}shGA{M~6vM_~(@NrYTxyNiS5{cPp66Bl6{@I*vp4aoS`1tsZ zVU7{h^hjF@Rew-Moap6h>@&v;uI{kz&aa@lL!mMYAD%*?u#M%1e%l!TdkP8)k4RXC(AgAVnNv;QT7C1jyhTS~s%8bz*Vx@=HxBg(stSc4k3n>{zIKUnUN zs}OVyySC=ZD!mqsL{^hvh57UI^GgJ8A0I;7_E^e#*umYVK5#Yjo?cyE?$)hc$w-J_ zUmp+mnC+9DBFuVzjF57hzNea!k{7IX3JNW=>B?LW``7Mm@EMk3>Ce$>a2<9`Ze?wt1)iDjs=sK)_U zJ(R|W^pZ*Ql=a_|Q}6EU>s!aLdr!;d_S^S_i`SO3ge!rIGRhqWb<}0owOJ(=FjKuU zD}CeSl@VWMzZKzBL!`#8dIrb`vB7I9SkOa3dm|xF5edyonN(@_t91GsT!d zMYE>Ze=de*Mc$L@k&(LLk-fBsB+l1vhREm-S&JnK&oSCpVG56p2uTv@&oDFR7_QWm znNSyvVdU6cLR1h&7Ye0KYvc9RUPCyApDBNP<>^QcN{$jw6EhTgl%Bq~VZIXW=!*+q zO8K=e#h4Vw4!b!Gl=7H6Uk>+ZR6C7XP!!CQLrtj@x^uK(TWi)1BenS~;a^;Pa5Ymc zu%#67UXpo3F4A@!dN$7X)~?XcH`_;5B5jfVHD>CJf*ZlVmhY=%U9Sr|-DfiVhcUi1rRKF2%p&Cd`E z#c?oQ%7Dyj9$t^{7koucvV6~++-V3{|m>C&#jv| zdpJ3v{kN&17s09U->00)^@c(|UmscfqQ6@3<@Ym;E;4$Y3^vp;kzd%^Mm3lHVMbu7e#8-fk zSTh>Pa3b{iFXcH_W=7JITl+X9F3v(uK>O%^Zv1Bi_m9y=Kp8B@!bdztl2%kxr1kv> zJ^3#N@BPvpI@eM#=?1Hvaqj0r)l(%j{Oo$E+w8pGuLD5=~4{=WVWQ7O|xru$_9(tjg5X<>nPKO_E&g04+^cJYdl~jfY zk2l!e@5=NZzbsqCdHJ_g{&s7$Vyw94JlofnYEg5m;_W{=!Kjy2^FPo_fa$CV?b#ZOl7x%?g?W21eM=hng~OU(v;C6Vdc@4kdi_ZNBEKTO|OVLxNP zbn*89??-`IF(28oJnDdxs%QyU!nsW)lZz7_4c`CT^B&KkJbD9-QI>v|&eyZlld^kf zaj5(MHVQ7GLE+FrNx!jM#&07q1CvNY7W`WMO})%}Prg^KY416nlBDjnNiYqPX1lWYde^8X#m_ zKgKS%ARitc=EfLo*jhg}C+O3k^luZ_EC!JCi!lm6!%ltF>J!N-aZlNQIW6HQhU|-! zga7V|PQcVqVD;XH6wh~TYYRbyGtFd|c@43ml$Mj{a3TU*-zfJ1jcJ*V#Ay{t`Xs6`g zpW(p9R*)!1nZXade=yuzdG)u34Y`~*BcXhdkMtq zT(P+!?teyfX(_m#Noi~`?AH>o&1G_I+(-er0ImC4jj%X{d)ERj$b4BuQ;uLn)@|%|N9RA zAKCqp-R}DQe~1kKD9Rs2`J*U168K}u{+R9mU1IGIeExyYKk)enK4}v86ZQO}C;yl8 z692z~&rGYT{UBa$>h5+^y_-~!lQUy066?c47ckL_#X|S)rC@P$cfl1Z${>!u4@r`A zE&tMR8HqyW_T(`yC|sfYAgQI*B`Pbc(bd%@Dj{(XjrK3zOLw2N-gA)d!@KKVE;UGG zPa!)p(EKwU9n;MGJQ7COXJ=`d`Q?imcBae9c5XnlNrsV*PFJEqf{sp4`?7k3E*x%} zZ+!jA$-Q)?9o8b?l8<--L2{dYj*jkr5p6>G5=f+AmNU~Ho44o!B&P|4{Kt=}EbOUz z^BRCbeR7a?Nl0U(Ft)49SVjidBy))F1F~y_O;pm$vPyXm-S`MmKG4Z-6h)Xcsw$P`!emYp8vEeT{X!1XEb4dDt$p+WQ~WJU-DoL~zm602z@G)dCqEe>utRr1=wHA56|i^w z;elVa80?+@di)m$1NP)!Uiih$fW3h83cnb8ut#?z@Xb&5pSJhp$c>+wMB3iJnEy); zl$T%fl-?jUu)m6q?&?(inM^S_y3=Us!4dd2+RV(%Z9H83+to0sCQoTL)kHDXYuBzB zRJ-MH&Xlytc72WFfYlupU&~-6qE}Nk$OK)&RLxRrS984Z1j%#Dg`L(-I=VNKCurk+ zqxxD7UKCSIbqt}(G+Vz~tWIAqh_X%_@!8>wtxY-h4Er{82NwzTY zB`GdddiOX3PF!10FI#SFl-}Q=!LAUU`RUV521Z6?X{q*MCV?1EeuL86*FEO*V9A4X zmgPSM{7Vj!DzSVWU&7D&)f%F`%XA8i^mFY&U%*(ibWQ!RTaFvcGb}<6U%1Z=WN>R` zUb%EhDOn2E`IKHZ``fo~mns@YA5A|wEO4(aMTQHKtCKI(#;^BYzAlJ94(AxxV%gPi zm734MtMhs)G$f?E&{PiRHI-~o<5|>tohYr($4Dmhsta4SE?2dU)&;@3RK;_&veZI? zgF8xXx=PLJ4{=?-Ojw^vUFF{FP-dS=a&3*OH|wCaiP@SEJUDeJwEvwcP1 zT3TLlx-t`VO}rGRl08zY9R2Q~vzSmn|Y&Unp2tDL#+$5XozcDjVWFB-! zMMV@`4_Q1Ib9QmLOWm&d^)(h@%P>TAwCKaH$0414MdqT9zRN>2U0jNXy{1?*va;-k zYrF-#;2uK6;S03Cs=n1bk4U z0YZQn7a!l<3Y>c)r%g|uz9#m_14uQw$IzlkkHxsxp#qmVjP_|7ePliMIT(??yu7Sr zoER1LmP;kpx)H(k?d#W9v%j|mZATFsTSqrGkn0-EA>|D%8=6prgoUvfI6QS}SMd%w?f{{+mNZ*r0fy=g=~NL!bH+7g#F_f*BSahqN~Nrh z^-hHew_c6B4#RUocmxHVy2}dDD+x#H+9O!6H|wKEM@L;G&RNFiKz3Vr!BrXAEF65M4nEI#7Dxn(x}u`%#K=BI;YK}SBJ-|<#y%w z*Wn$^ABe%r_&K4;IuId+fkek@2+~S$Y|8ha4cQuLoDcx5J-w!I{<^C)%0@%WB%7LPfjGerb&~p22u1WX>Ix;x)+(=_C|l3c$2LI;*Bqc%jb+ z)Mdyqtx7@Vb`vO+>_n^Vradj!iRcD;Lerw^^~uCn zuU}^i7*}uQ`Tw;u@biy$jN}{D>7M*VPHrwPMa1b7Cuj;i-DBv4eWw_DnuUc2GP&4= zo$w*!xgadkp6~T_$+gK*8Qvup_R637yLcF}Z z5a~Wdw60d>T@71XB%9Zd6QG6y8Y#2#T74547gNwL3&Ho7WO3#=jnqzhu=}sQHSwBY zFm~%utlnB|B7ZsSG!0B=9H*k9V*JEpYjQ3PHbWvJdK%c_c@@LnDsuy6U0|*U@Bac| zX+H2&C569FGCuz%%C-2p>;M?2gMo%d@Vm6cDGF&}v@YC0R#IGC*V);5a;mdaQ}NcV z*Qrh4zCAn>*ye#e_WSqmd#c99%orFLxUXH) zU<_51l#~pKj1+HUVr2X_`sfNL=jW+vQ0=nbd}jFlgQo!6{P38yvCy*nAODhBZddTe z=VbX76z1h=1AVxupkUV`4RpXk#kJNPtc&vteN(a1?Rr*EEp}AmyYAM>60dU-kK`c` z2qGO=Bpv|)wZn{jS68KnOKqa7)P;cmBV9wg_YF78sndh%dUmuqJy!u1Cz`rJ@FvwW zi2~!kvkSMg8`RUaYRVXF(p!_G6W+c$s-oW34zvO( z>*sCr>Dgfde}$tl5}@Iyx;iqj!@2K1={xlUP}Jk*Zag~dF+b$xg2FZU%2G>hu`K+i zwV$R?FOQ=Th?6kty7_4a2H2;8-6axGy^B^@6}5RJ0HZsIZ%s{Q9!}N3CSfyFtRR}2 znq$`pSFT)%d2mL`R|2E>WGq7^uIcI#;8kgve~IC~_({X63Wr*~!p%)no2UEfJ4fr* z5$V7VA>Vz<@uzIuM&#+_4pcb7n>QIxpZ+@GvRNfg8&S;QT+f>ummAHBzRtMW5{LbO zEh_{@Jk8Ltiu9~}*LKmB{p?_9WMmgW4o*l_m6taWt$zms>8W&{s&MOK$pCKHetJvZ zOP36uKUXv;u}p52^?8`@;^h?tH8FX+XYU3Cp%fuqCoOK(+Ax5TYw|tj@UgE5v#>7h zvl1lr==+;vY|T1ZYOIEN#V7*6$#b1SI|LMMa%ZtvJgJ zV^UJyzBmw64O7w3&?p-ATlKTBV9kb#iHobV`z}No3B-Z^T!d_FEeV2pHOuFLf!WHD zj-V<@Xn(QBmBGZ>_DdO%t8Eru>@P+A&h@ciX8HeS=bK(zU_z2X&X8KYMmXvMP6D zm6*G@=rT8w3e6o_nY#{TyB{xwRXC}> zcc9!MXlxv)L*RpH^s1%JHkA)H-EIPb1b=I6#A3E(3;~oO(t(%EE9{EV)Dq_bum%Q9 zyAPB#^=A7!4OQyf^%nGIr3$nc7~23jnMdwMUjaG+%(TeMdTC`Im-XbyP1t-=I&Wc( z5h5nO%G7nLRm&EylqwT;_|TzJPtwUMoMSkmKq~U4*?S(pZpo&t2yb~<4TOyE7j}eNxT~A(Dii}S; z%0sYZ7Fe}1poL{lBPP+8)e)QyV3z{X*_@a zoQH=;^X}cd$qt59F5#Rrpe!|tlh*5w4vm2lt5hR_idmYc+7#=6NPoO{zex26SmV9b zmft_@p)GMR0*ih16B82ymChx=;K1g}bQ4yan}-A;Kxuk`9%^f9B0D=fkyZ$Bp*u>o zvC)_d0=j$R#4!-Nv6lGh8ad}^i@L6Vcx<0g5^NXkHOh%#J;j1k-(41K>^rX0 zUjZIsPI`K;+bY2Q6B7(DlOlgVB=89@qHqFrS2-cH@dye=V{M_Ho|V-e>K)u!M@Ur# zlf{iXxgM9}kWF^ZRYt(IO{KZslA^V%0*BFH;T&alDdw~x^vnU@l)SI?Xik+j3^c$A zq*kgUnbnm8W^%3hbS&`3@Al8R_s@w*o;yaK>4MY+rP`)c4En8JFE_}a6t=q!tK@?K z@DB+Mr7c0=le_;o8oI{$@EBf}e`K@1>F3zAfkkdrf2%hD<;V)gwNaZb_*0Ovrd zO{nbJcMecA3k8k~ij(`o1rrb}k_=G*nK$tha&|oE17F}l1 zB$n)r=f3<9G7aKs2+||oaaZa|61xunl9~DSE7HzU>d}SXbcDM7Mqm%<@6wM6 z?!`U!Q~f1!-9V+F_wHo_TPt)Vt?>qk@%_qt*AD?|;8qYlJHW1<4f5HCMS5-#*8?Oj385=*M+eG~~w8_&Qsz4!lbqh4~^{Z&p-kNOl?Q37*S*Ij_BfkQh z`VeTb9-q*W$Q5E62#c7ZbMs3jgk4AFL!0?e$W=YT%zPoSCG+do+o?pS$_LfJP?z~_ zs_`3F8>wY=S+S4x^&Knjx3`?W?mYG{7$G(<`Fa;81WZi_u%Gb{mgwcS(zCO#C@TBJ zB}9ZSndqW1U~$g&X@G7YQ}#@D;OFLsMzY~-pMOS(*mlQ*KG9Hevc=G%Vh~s(O*Cb` zJ14EG8&>8%XXXZ05;rHOHW*gNKpBDIq*<>;oUxf~DiCmeeZ5YZI4DK*?88S3oB;jc|`+u0UP{_Ql2 z2waSF5sv_FMp$I+x?2W&R7^}vo+gHJ@f~_)cjuvkB25{zcAeW*O2>@b#%z+5F~D2ToV@xH~ig0AWnsgD+91XF)fx$+=jqp@h&@L^8;)+bog+tN6rL(Lzd$9 z7`Px=Abv$XHWBwN1S0OY7jumqvt^4^Mi!;I82c4Cswh6!x^}HYb_8W40AWlG7@Bi$ zGArsnRIrCmeo6Ag4o7$3+)5Rflt@qiqxe%jv?8ew>iEMdT-!yvZGTK&7My??deCOS`wc8Bqh(k zsN&(Wdj7+O?cj?@%c9#W_qavw76&CwGg74kFF>Wxas_uF%Bvk0pFi{5WIBnT;{|sg zKVrWM$i-7f89c>WG8^^QmwSYkc|2AEEZbSrC0 zwe|$l=_@=!nr~INM*QL38YKm##x@2RS#sr^90;b~S@><=p!5unpq zUEH}?9u;0yHr`}S{UU9ROI||_0z_w(R}91Nv1YIQCpm200~-b_ip`u%&*IY>=8k?# zo*2I0Z|UUsm=x^06Wa`~{>Z$8>&+cy^0VAwPForq8#@%}rQa3S8t4&*0`^eIpQ9t~ zetbRAUYt2YcpYFv}Y4jS)QC^dMC002oa~T-Bky=055oJY1 z+s=vT4np=|L&XB-C!sL$%!~@N3`O-zs>|MqMs-o6!Uo^7-|1 zA&(?}Jd)@xgv(Lzfds%NVa&_Nt$`=x`8v{xx|EBoI#00h^ z`=#!uclW9?OFJw4!QoOVMMbWV2*=vhhHi2Wpd|)l;-mWSSM6u_mKPscBwX75L;;XT1LBZZrk#afu#5NEJg2;u?!t9jov+4*-fx{>H zWY#{^9ql>6z>w2B>K(1H*_Gqm7pq|{wd6|l1!r?hi1=sQ zrNH_g9U<51s*xhv64#q`RdKV^CSPy-Q6_)nmjjZi7tB4GB5q z&|d?E`z~_3x{r}D2?dw8*MbaSH49sz-ZS-a8OWDo;MJz~#;58!x1K3nFDJGeI#Cry z{86_M-C0iT{2PxJ)xrL%~avObhQiR!(7{EV)uy;CF5Yu_K5Mz;ITC}*I zi;HWcJ1RA`@Z;0Kgw)7YufGl822E%7gKZaW>py>HYJ#<=-zq}LYG~MfGe5z*1qukb z&_jz$WXv{ySC_hsd0>Sgdh6XK)#~o7V|7>E=0v;7mN&N{h%b?t+aAL(l=tdP{vE3f zF~@;)40S@XBw5$kFN3-(`w7Mlr;J& ze@;mC8seb$99b97nh0ou0u)tBHOVu$8b&Mt@sr(PkAwHBDY0?%(bp5B?JeTlN>785 zjI0bJVnLKRHg*wAc7L82*un}gUlBV(BRYnQp>i#(r{frX;v!eF+OIZtEHmH|pJSdr zN;D1$vthj$h)6sIo;yQ|0#X{$?pgKZf15`Bvh z(2E>{DO2j;NXt^gX3byua``r4d%@O%wxymKw@RUgdd-6_;_Vv%XVM~M6)xS7otL*P ziSZ;=Osu63J?*?z6+_1|sZC-~(+P9hN%vvky2uW*m=9i?{35ZUspZ4A9=; zqy>dYmR#M!99Y3*U$ZBH9cF^L?oiG-j9&;H`e_uev5!RAeamKL1 zQP_RH&n7)%uAssfxI`M7nr2|=XB{gxUz4|uS`XEIZL=9tpMU%%uBDCCgJX#~EtZc9 zU+3nwpE2>9G0d``!BZwVROpDGPH^||z*s~JW zZ!%R=FF|OQO42p|nu*_p=+YX<>wuh?UcL|rA(r9W9+B(o$?#f&tAEcX`|FYwf1ftv zJ9qEqL4$CPU4HaKR{qpM6~WCUQ&lXOq?`e<_8Ul7VN+AQ zHNju5e^Xm0i++mT^E1RwAvp@7Ob0wJs3 z50Tf$Wo8~IF10i6LSB)di8`FCMvi+J=t}XiIlpw_dP43R(HW$Vxl{`E%%!+cK zAJTV{1Ze@+hGSJSY>ijDGgO#B+EJeqTQL$hI44FZ?@xfAGOAE<5r5D&d?)LN{il;y zN8P5N91E@%T@SBQH{EFqKYy2VOMDsU(T5_v;gcE<8$B93ht17me*MtWQVdC*ecC@s z%GR(F)$4Jrm;o8~C{B<_+e}g!nW9LW{Wu_bE@B@+a;Z6Lk_3XgmT}7FYGOlHl2H0b zslZ-OMtyyK(Fpq24dj;c+@AKOL+RAg%xlL=sim>B?V4R!-KRP(S|ES4C(3pDrI!(U z%DL>tivvrQH8nNEQnszg;S!&U5yB&20#xF~~EeE3UlSbP66+ynLA&;grUN!LTsF!&8 z@DDr!veeh)Zt{PIH**NSw=k7+CX0dBZ$K{^1j2Eq0wQum9O3L~kREH1HYrZ0l3y?h zYz;^Y2nc{^E6SAcwXuW>&IyAZc(xnz%l)g?rChg ziXC5@Z<`Z*O4wW%0VvND3ZZ23t}*snw8z#n(j@6Ay>uRkCT6Tn`PMXrhc|-SJVdP& zH`5}Tn>pGfgm^T&vWyFvQs5nz&%~tL4XHGYpBe(-v!dyZCB8fw`!#VeNZ4r$Y!2y^CUPp487L?YU@2c zy5DWF@#zm2RGIAr4|tzF^;KeYF7R5?LD)4@)WCkN&<@`x@d@2zs2kI3g7R9L$w(TUozY`lle+-2GjEjVmH~lM>8F=QVuY0&qikl<|!eZOsPc54K2cqbqYGTAM}< z0tj>jyxzdYW^!YG4r#w(k8i)%sM*kcC^m&A{5L;~1KMl-_5Bps zw)@Oilhy=+P1X|M7Ova5RvY#8Z1M3rnws9tQnvdfS+8H}mYB~T8^iJNS$oQ9K<9e( zS^2O@6g`>iMb-QbOErHL$mQFI+4Y<{K(S%iLFl?ODu}w#_U6@*E^-$<1Jo=a!3rbR zEETks0of!hjoRb<39N{8Hks;PpR&v#?e@H$AY9_v(rm$FFEy*lP7z{b^C0~5*`#ixZNU|)0ZunAGIP#;yCXtQz%bRw__QnZJEI~QtnHofu`m=M&I=+ zM&DX_;B@KWH;&SB3?Q-<^=_`J-U@*LJKr_zH*8T6t%b0|vY$NpF3!{oqHF5S-?3u7EFz%|ib(OFRF}QrTvoXzA+yoP zQN3BuzG<^4kWz%*qrmk)^3PB;YfnuBWkS)EX?7qyf@iB^8)eygynjluW(=f7->AL_&!_&4eoMq`)_yDo*%i^aCv!s? z&Rr=dPe$-DZ(!q0MfJ)-06N0Iu7&_vKEYI1?AtFDBLXD-o^k;|Xgp-|z`H#ZGj9o) z44RPZsV>NLViLR->1QQ;343M9;VDdHx#q8&{}@-WjDOjfPV<^1LH4Rk6-bFW45gC^ zpycQ2zN3pitj^B0g9l?`doZ6pAgb2>Js?3STs7`Gi69I^AZ*B5AB?YG}HqZZ6 z@E*Y8xxqM4RI&G)?Cbobq;pUy-WHhjkGprh{aw$W9Uks%UTNfrrVbDg z>9T&C)4=_+abZO)uM84jH}r1fr7*n@$_!v1D>)L!3$|>aXmXw&DcWnO!qjxBlG}WV zvZ~S|?{PnT!56H+nAb7sAj@-_ojnWi)7i$G?jaE&nh&*OE?&IYOo`z~e5(6&nvrpm zQp=-SHaIs~gvznwVBf!a@PMTHEj>eacngc;?XL$bY259ji$Cjq-Ve?tNKYJs1TMNP zEiY@q3znD&+Z4+p=ISJ$c}W(xVxqkW$g%;t#-=R=T~>fz?@4SKS7!4sZS8-7@*sAY zrmTl*GaON^atUc$ZvwnVr6)10g2}`)r}cgo0DgM8we0ov9+uTt=_Y6&o2@laRCQQ( z`zHAJPKOobLF<`JGaHA#aR=01za70mbAexS9^UiWRU`T_(T}T9AxK^fX z+Utd>eADWmZQD3w=va%LZ6f-6R50Jy*&OM#klJi^C4U<+ebqGs;<$$KjuDd$lGDilpqna6QHZoQSx<^Xi9!=+u*)ns6=_Z?-)22)FeR(ub?VpNodCN^v= zB3X(@R@MX*jmoYb$S-1cbPcM%@Sw#2y~;+Oo%6YYDtd4Bo zQusSXwPdE~G5=a7urUE*^)q=OT3xD<%XCu#Wsd&opFUlrl#X2nwPTRC58f8v2ElEp z>}wfadb#Bn`qaWUihiT=oQ7|clVg{bx=fk}bzLw#^{NSpq#S!q6;x_~Nc>f7xnX?# zX4yjSb5I%(b%9$(Mggq-^g&0oF(^IYf&gYFt5tH!K6L}sI?akD0=m-Y)vMSUxg{z` zP~C;dhBS~{<~QDu!>z(s?XXC!CRP?qonKZ&Y!V``zeM&W@(B`glDLQj84kd1^(?ia z-@q}ewb~3%w&o_7?B*aKr@>AkBzsJ_>DU4`vEhH>&yRZyd+Ewi7t$Dgsk9P>&)+`}Z}(+W2t3kL7b$DRd&Q)B8J@68f|uI&yZBN2|Ee#w%lh{14#@wv zBb+c@u5Gn%^h+!^r6XUvU-K_ifHpnTm5T4iVidQ$QBfrF)mq)kkAk^ z%7?0M>R0yu;|8&E$(=I4y86|z0xLBIa1GB}IMa`j9l6g+Ua*Wb23>A_v95RimUz#;C+G%g%U z8roNw3V?<(lXKb6Qc3r^@7jCEno)6svSMmR3Y0+vL+T;OZc1;KMA-iQ_utik>LS-O znTqN4hKu9gG^Bc#wz^`?OPoxrf6=&Z5~oKc7D0I}uHbE5pqOWB^+OBRdp3~ZJh4RY zN*YMY5jv-~;0Q7vtv8NmiD@g{Eu5JGJk+Ei!`{pbEQsxmX3NshL+(QzzT>UP)0A zpXA!s5eBaTRlIH4u>5*64S)Z~J^992ptxbWcK>`n!;5Eu&+%g_mq3lQL6w&mC^A`* zl-mmHA_oDwmWD|Q&bmvoR!P^46oQUo9|QaM_3MJ9+LZt`v(fJ38>cc&=MRPbc{72Kf)FirY1Zhd2(@n<^?g*etNcLNx6q#@1vJ1rbI#d zgWRJbBbJc?*n=zD`ug!6`4Ku7;2>j5%H~6f-wI|1+6uR=`PUnaF;=3Bm%DfcDEYHO%nJdvs z))SuWRJ}-ysx8~cjlDCQ!=ZFZ2T6GF`$uU7-2D8n>PD@*1;>-svR3B%d3|ob(9}d& zlwecBz4$;56%>JhLgt&Gluk>pkK}MVJ~=sGp+6`l31MWH+Vn*#WeQgM%l7QL<85uM z;~?*E@$ezfB`E{ocS;uAF9zW$^UN?^tq-V=YSorI0n>J^8lu$%4>`tpFHdVYeaF2- zjGdet!I%~~7lCpIyUz>_I{Nzkvn9_Wl_77%?~?cXyzmfnwi|Qy*_oJFB@nA-=rc?I7X~x_iFJ9h^aB`s@ zd{A^JYA!uQh$iWbY<@+LiFYgi64X4k7HonvE3;=w)ft0$NKS}bm zI-}e6p-ivFhg_b?^*ZqzaDSs&4Mg`=S4ScfyzCw7cJi+N$9|TD(A`EJQr#;paWqht zRtXsX(eCG43t;EJT=Yn5<3Hzy|NGuU*?r!B`Nfw7VU?Y#%=2`lh`&Lb+dF}2fT?~y zqnC6_tRsyB`0xlNQ*5v<$_CDFg^J$=f7V{LSaQ)q*aeQr29Ct=omKSnRh~5{aKe(* zbXCM@M8th6fZ8Ctqio9x?h;10xu(rOfB2E*= zugfk82^ldkr~%`=Q_UJ6RtH=w#plsnv@(wNyxofLyo(&HLbOZ}!|p{og{7rNfe)?- zb$0B$CL|wFvdd!~UjqmLY7;@}Ab6X_{2-|L2uRb`*8Vo<&MPf_S0^_kBN6l_;Jp0K z8w{sD*_K_oc+tqQLKzGc6v82nKw^<{;K@&-6m@bHM@JtVIy^4e1rB`wyd&NZ2Q5nU z8JHXWcj!YfconmsReoSh|2Mp2wbRZ23m4PRe*d5N+S(tH(j@Tz>YIT7-x2Be?ZFa- zy>#;DUpxq;?0+D)RT!|R>sG3SDP6$tpI-gv#pW0P{MwhCL9~}MzJO7Qa^S=%?uVJDCQ5LLec87D^y+R{)Ri9pk%geBb#0`;Xs@;URk`d+oL6n&p|#oI5A@k+QEr=EgD0mVOTLJ4-!d97Lyj3D zaYJ~cZnNnx(g}u?j#`Yue0|#ujPz!6hcCU#>3yGln+Ci748?9)9L$aJ;%^zzzc^4c zT>RImUE60&EGB+51s&1d6va!bBq0$4va>an6Nz>#J{miB+#hCHEZODwDRym* zI4c;nu<^BO28_@I8QiWJdKpJO!ui?lR@?L z_Uh2_!y9$0%an4n0I{T2T8kB>AYeVa7KscPGbWmOOe5g%T3WNPz` z#Dvw=u`rM6UReTZ+VfMml-uN^hpDM~!RpMKnx!^fS*x%A*uLy$(8S3Q_ON@^^D)Ts zyrZe9sRe1t((+Z0#{&E3o^fbuQoq(k+^`}utDMP!!{e%c)W=a1c7#Gc%T6i(%swJyIRZXJt>Fiv+oI$s3~I$y9d6MS>`aHr6Rkk!Et@I zmVlF4>K&sl4f`tiEs3P*!&SG{^Xbi?vjJ5#o4G33J8Mf=5iYJ~nYQ#qT=3}Xbjt*Ob_)+Xs?oPGdZ@KpZh((~b5v>ZKwA!bd@!$*(e8fVI@ z9ewd(3#mWtQv8(s=s`E9{t`YjXWlT6Cgl^gv+}}ua%h4IsXJF4y1ruVFkF+*7S`a} zgRdgALKjjcaC2sj2)ON-CVQOi-EE>f;L#}MGg2jQiv@Xpx|UZi6@}&5wY5D3hG@81;ZHyFCBaEx zW{QH8z(cj^{W8mWL>+;j?kjSFEmS|!vk_rzNM{%S_fHC?!s_wW)dVi*X4~-kpMLBw z@~Ww`hm%va|K>*M1@KYWuaBI{bOuAdNDr?x=q?vD{JMw11s0Fv!TMVI0xUm$Wt&a? zXaSTY75+sqUuQj?gfyNj)Vah67~oZYlb_|NBVLM7UH$wyHITkWO4P2lm2Witxz6vp zW#oQMbwfkwkHSW&ArZfc2TV;XRP4Qh<~Q}Q9vrV-ir64 z5V$x0^?$aC$)O~aaT;!AVzSiAFZ$~P%$Qo?%5<(enK91mf5y4FPv~{Ml}ZXcRm@Im z*jI>!QZ*8I@>uB0pUQKj)O1c=ZspUUgajKkvD!!bOVd9qFge0l0ezqh8!+rKoV@ff z^@gq=sVD!{z@wkK!{k~5HeGMIGtT`?s7l&(RX;o2#wI8K_k@3@@bQM*iBKy`;$ko} zw$`6sj}&(OCE|%(2Z7Xt+~AH5!oAU0i(eD}Xi;v?_(Z+c^0572um8fe`hVv2{hHy4 zP>lSjSdxTCnHQ_+KRWb*Nw09F(1$|4aU3PeMs4P#J*u9qB*k+7lG8!+wHbWrV_bof zTyc3=@K~drNmrKl7_V$0H}5a0>~U+dVnJPXp6%daJ}G}#w=uFsCJRndq`NvE#1>tc z9NaV>ZSrrc;JG#+5;`F1H+q+fG_HO7Z>xxHH!z)By+2rV^Or*Fm=et9P&Lr0Jt87d z;AF0YW!(I=jMz_0c(h#?ztP;Fj<3*hWM7HRQfnORFPT{sd3mmkGK?!pLT+=S`PDBC zxMo{F$|*N{Gc4@28%wZ$Tm2|m*rzY~-!`sA9C*$KLo{)UbK^;yPXx#HM+*Ln?-O@? z$leIKfL~M5dP*1!tlZt0>Y09aWj2VkIBBh^`H(|KkFq|hKm_itZ0#RxbJA3(aHTJ@ z+@h<=Ql`Yom}6~9#R|{=)3fBAOZhU&6(Q^0bbs&~*1pF>ZevdOmuY_Z4D3zetY695 zD*BQxFfE|O#SQ-`qRF004FN~_QIWAR4nwoDUwf1H=v2dK1<+`vf7{eOcJ-r_K`1U6 zj8pvY_i#9SwjfE4K1J;R@OTJUe&v^jAU;lvjCeQ8EE{ME6u=V`68^z)TA|Is^@b!I zjzpD;d*tqY#PQ`6 zIA^Vo!T?l8N&MK(N1;UGsoHX7dsYOzsf;w}%|P4W^=~Ni-p_mQ=lZ#l{|spTJ>1W& z`#%T#zh(DZcH8#(zsL-KE6Q(0`K>5h68Ig-euwS_nBo?u@B_S&E4H@4<95KOKchj+W@&oV1a_Rb}ykCN)@U1GAbs$E@OV%M)fs;>4g+RbD#x7u@n>0`RI zmrE5K-hCC7C2KCti##cg`nnbW&U4}9;_-2_unLfh1XlKPFy(}vfb}@bblLxQ?A1+(f0F;^L~$pVOOV_JT`MmaR|qA2F_AkR2?8i;Egi z=qmgG)02uyG};!tDjJ!{yyrq~k-EA%5WC$)#^ppJJohp$0|ul1G^^ft7WkF+FRyzX!QY$vMnAM(j&8=E|4G+?qo-Hy{3P+g5!2(q zw>t#^#?j|P|EU|E%c#IR(kTI(>W+?%b`pCSLxP#IZOzo0S*ySx_r;4pT9YN!10Syl zjJc!5%o+~yaC2K@;hM0FqjKwyxD>*>sW{_dZn7u26xUrFuqlOHz{nXd5HN8AWJlQA z_K-}i!CTXI_`HwP_icLmh3fVBW^YRMvTv+ziFM=zCJhNr3hI|VfQg!XWU6m$wEOzz z{KL-hJqKAXadF*YWo7-=(xL$j=GwJuCH8&STH~+YIc4PeIBF1>CF5nSyfbNAOt&9x zD%&sCYrcuMbu3Z0k-oO9{rK^_jm2y^F&pWL)}-vupKqUJVS$&FXr%ema=C4$Ix}e_ zL7YSbf=flb!**%*>DyadTP<$2IzQfDWD$z98unky2Xz(wYxGxC_*3|c#q+HI2?Wt)j0BG^4US5M@XY;ZV2G_MKFT8MR)6h zj>h2~<614dBnipy#q4_G385h&K!5>w_W=jJGW&!!xlLbP0TwD{m?6#ON=(Ol1!&!2 ziAy{@^57ncSIq)@@^s&ql!(e~tc)31fY~{u_{>xk=?F*>l3f>j%_4F?eE87ew_TXK zX?=mARX3BaISyWjbRHD0`cCy~T?-Yi=_M_+CQ0xEF?0H_yj$%fA!`dBCW+^&q{(`d z@=+QZ8X3vSXJlxT@4Z*Y<5b{=N#YKjp!UQA$xe0&;kH?x!6#mKP6v`fC<#|<;47hB z+QxpFg=UR>!on)B7K^+zuL;(afQ{7&SRdF)#>`n?p$&w>&dTU*t`p>9qN2DUiE;rQ z6{O2QdjV?hy*~Z!`M#q>Fu^MW*F0tn9wuCqM`-1%`TADX(1`qaD9$iZ3JT}r=U0IT zY%9Tj!EGf_&w7xM4r;qH3oRmimoB~G9w!h;nQ=yHC`UYT`j|oSU@_7nE$A?NR*JL- zLk++Mk=)3eeQ643z3!SdyzYp16v^Dvox9NGZV>Oy*7jXhQZ*5s(r+)+s`p{c-QjL?RC8i+qND> z4UK5Wig6Z5=d(QrGl;C8VWK`u_PPSGPf3;IvHIQFDx8vMjyy~hg}~gtfBgGtfsE(u z+gM@ag7M}!5>QcChBT#0ol?75V=tb>efh$L+c+bieD~?zcM?OMcceXLy1xW7^FY>s zYO50rJ@A>~9MXJ}lDel1-6FZC+TY!QWgKP~zYn|kFwtB{MaJ~Yeo(aPLxdx1Xo@gV zFzdq{QW|SZJ!tczA5x$dzVuOH14C991@H_q;5Xw1Qo` z_f7`$roPFLQo7mL*oXi+JF(W?tpR~`O+|BCSXn8-0uQl{Lh-Ct$s<&MLR*R&Rjn6< zB4Sr|M4RlIy`WK*4y?PvmGG3d?Ma@t-`lqPda}FS$EUm4#B8I1({aNYdTeR>mB-II zJ3HAlHyEO(EV>nQ@C)ZR4pPeJ3oC!eXn$u=ST$$9SP> zLWY(1VZfzMQCCAw@>dOcOt#;GfdVxz@DzUDlkb8Smuoi-DK%>aW%hmP!}J*sUg(Zq zzhK#lBqf){d}wW{GWxKVH%o!+j&F#4q2(2ZUgI%R`?<^z$_u1t{9o@mX-&QPA@_V zuVzp`hzlRuq(}neKYk>4%iN1N>oD{F25>sM`C4J8uikEIYBCyclO&vB81$CAXH-5V zhJB0GK*R@LloPA6efJkfwwHYXkM@Y;&dknMf|;6{miVr^v;rY0#R+Men`dw*G^^mE zDWDY2ZPWhtmZhOsqnOV@C5EYUL$Xu7;lu8S&Rl;4>qa>lBQe4?E2d%W63VdXv@~G| z4NvjU&Q;I0?aF$XC}I`Pumn(|iZCK0bk!=|*SK@Fkw&l#e>yE8t(w}S?>&)V<<=p8 zYHGe&7ze@}a*a}J3_Ee^lvS*Lm1m(*038ZK7b(SKr~|mI9Cvd66wn3gfW*#vjw<>4 z*Nh-<&UfCI%x)+%2nFZcvre9jxg=ZT_O+eE2X1YwJM1xQpqjWrLFd7)*4p2m?bs z{j2-ELPU?_zqWhu?Xno<*Ey-#|fn*y^Go4xc=l3rD~yuwnWZ9z$}!9 z8uS=;fDMqfK;xH@=SUhnCnpaz%@g-P!`nM?cumM?y}&?7`}tT`G!P~di@ zO=!!dtzsH42?-U{_wOuscne4Vwb2F_GZkaViU%uEdQaqR{cAVwc;v+FX@ba(ggO8_6BW*oBhX5o;*pLt=pVze^=*%#w*@`g|mozs1|gj#tJ|2<4i}o zJY}*)B*=DU_8s_`w!Sb<+gLjU!V(Yn7rVttS1j5?1=2~x+AWe%?28L&5gqg6{F+8a z?}QAcz~`!m15-)30A5Jt_h$=KfFJD^Ch}#Uk2(yLDZwP|uK!h0;l;Rp4iFlwJ6~TL z1m;`EjvbS24p^Vd?k}~Aa6~)Aa_5$mJOIF}u&AiW;&dR85_GP93|nCMOoL;l-(H-D zhsT!GN~b!UHZ7rV;9w@Io1Ib_1_N#1Ox+Lo1c+!cWEbc+W)VXZM2JW8umYV;sjhNuB~!3 zq?rd2bcCa4w4MT7zMzrM=~E|8Soal;yHpsk$@wRPu$c!ofZSiIz#;A4UWzjkgzyOo z^(YC`mB3D01vbIlXj9KcEn6yJ837v$Vqm6V`T(V;IY@iXz5&YWM$FB=4{N(V=H@V1 zE^mkPM?j)3CXRn=Zq|JCDCZbg;GRJWvUcO1BTC;YP3AU817f)Ib>=dAkekXr%QKp3 zvem6cAhZGTSmgklOT0FI4bmAaIG~}Wm398kUWVSOLCe=x37goq6g0#B;&I0QtK6BH z(5U9|&MpM9&L3_Ax)Xn9*+Rb`-GX0HySd6r7;=PCjKPCO8xIbHiW1ymuBTty`%L?d_C zp%Air5^!`77=W*)rf2vB1Z+R}Me!P6(8L(Def!oCczAf~`O(-(A_Dm+Te)f1GV}JD zA4Y89=kp&$k|g-}zLC_ydgtf=Ow+a>EQeSnU)6C(8wNhU4`VFI$mk*h3!j{{rcOo{ zbg6JoLk-CmQTbXJWg%=FZp2Nh6lfF!fQQ=p&FHdt1KVV~vBC?>;E=xdd6;beMl zwFVS;cJADaj1uLFZ{HOCHtEX9gwG66e!h?F-bWS(Fr7+HN*}2UuuVk`R4+@H>E%zv zuc|W(DXD=NaGY712o_@97=CDnLwKsS14}`#kt~fy68rYG*QV{btow94$bhhgX8Vvu zTna}(q8nM}h%W$eO1~@HIM=A!`#ua76(#D>-+j>zH_@sOe9ZCV$4{`ck3oe*M1Wd5 zb$|aP7YTxqySwsxOJ*fC%8*Pby5EdbWyiBFI z%d)b{cyu|c;_pMDD@%CIY?Sl{&M zXypkQhU?y;Wj=OJP9!i2L|&c&+Qa9YO8k>|V7AdIDesTU>ZI@5P2-o7Gft9l!e+*8 zeCBs^cn$IoMHtYM^fr6K%Y(yeT3TfQ2*pIX-VJ;#Ds^dQxHe4-Y4qNrIhHip@gC@3 zf4Orph--m|rRxN&Py;d1(K-AD@p<6dP9KV2hFw8IR;moT*|OM%+)o9f)Q&hi1;_xr zl;`XR%eJbC44T0}q8Q1%P{;hVBg07nL(xK^DG>$i+pCldFD>1AD|*k~{xw50&CIH0 zYd2_iAgzF3yXM~x*FQ0p&bp|?ZUfkEx?$McXfH)-c5FllJ7}y()6!-qimc8p# z+%Sml%5!t6nOV%tGC1rsg&&e41%&~spzavCIG=|nlh{}6lk!Ffb}vjyi6vAX1eyLa zhoEWqB;dUaoNCq8q!N@2an?AYTMPww_3D1UA=-#t++O4E3&S`g??Or0rY4BugJ~W7 zkh$ydci{d(c(gZ#)XLOIxPYERsom z>|Rvj<`hU7dO&C_aT?wwd8Pw+&oj-++p*}6T@08z+*%KdyZ22}tjCzqr}Pof2p}b& zpwo32q7Zg+0Kpj!ci|H%aAZ$b&Ial=i~)ZY6%|jOI>n#VI-yyXo}R8eNkR&akB@^W z2KUGvPoyz?eLu=6d!bFbQ3XjK1|avYcv9=&`|`j6a;z;mdm4cebI8rf(YSrP3P(`R zR)^K|pYy0N0JI47nH|8}zIxka-R&Sh=iuBut3kk><|UtNW0kH* zEGFO`wbAYes6F`jvgFhKJ=((=5a#vRgFlRT+ddSG@V8LKj!?srkVU+ECwEldCw>Q* zZ5{uYvUS^c_)kOHonijxd}OaadZ)j7{}D%&c{xwrY!ym}J5;kMuwCPMqO9UO90LHq zz50{J{2X7b2+wp#=9l>R6zQ~ObzfiK z_atB+s`u^%Dk>>rQO2B^85yFL;cz$~@Gbh_F#=#5*&sqRSr&`I!?W0sk4P#XZj6~s zMhuX98p4vIlcO^nZSaXyBQgK!UO*4nZPx5&b7}aikGxTP=iO2@A|K$Efig$+h*X)F z_}&huKEM@L;Ai%dUWbKg-o0BFY@#Gv2Vj(6tyz$%shOC`%>a1?1qq^&ZF->x8V}f} zM_yhp0g^q*%*>_4)g@ndrG?PmOL?rG3yO+UMj2u&O6@Yigdv-lATx+&t%Om91JCU4J=!SsQS*G0pDPo+V zf5J?PD+*+V!&2gogBg;`gV`V_#!5<9wphztTgDLk^AW>R;b$b%eJC{)g$V6SRNnYh z=MRh785tVIj*0mv`Lz^b?&PLf*5iA-dIjmzuB}xY_aL3gYcm}WC59)94RZV_zNO?& zNK|RSrhk2XJ+T>!=mtS5&4SZ=_Hp&%sNBEcC~aga&21`xc=)qEk=4@Daw@nnt^3aj4_$_gjfK#^?7owhs3$6R}DXm`YXchv-fx4w&1@lPoO(<9E-V{Y&yp_!#nZ z5QFfL_4DSxR0@~;?NFRLpWyr-@bKO& zN8PR~gK7{}lk?>`Xdv$m4=qR`@VvQS*M+b*=CQ;!44o|*}O9k-;@0S{2w(&2#AFkS;^eQwITSZ92PIeGh zZ`3-HrSP6(c~YOg2Cs){=j`kQp|{YCHZF$q!4`UvJZy0VhEcjI3Uh<{`@CW zajbWsa<&q(R@QiVc=_gYa@9yfLZwF=Ue;@Qbi6`10wl7)DAb5iYCO5rv*GtZBQxC zQC%*d?!2sra!=v5nf&msax8oR8*oz&AXn?PG7j&_PeHAHuC-M&Y%(kDX~gciFJmgi zT;8-;Zk~%g#83M;+g0|`?}C++TY7U(8LtFwtPPLDGHU2F+bKd?_kq|N18Pi`p%~sb zV3?DF?IR@T1O5?Y&)RA01ZJ7d!O)3SF}qqVkh~i~&q%vJc&^NDS|ist_h;$GAI&01 zVmE&L_%R9R&$hE_P)1_>_SAp6=8oJTiQy#({z#1Po_gSx7A!uoAB5sKXXa=EZ{C&z z!||pi)UM{E?QjLMafOb#R-UGBd>d0xc3)nJV9_d0Cr`52P>D$D6vwCij=$MbhjO{6?r7sY_WKu@sgn6!@|O= zd>}J-bWC+|aRJ!&1bAj=gTBUpVRYq!BZ#bheUcN0OoQs)<>Xv)-CJnIYI{cK6U(klA3w+k5_j6QYu>N`$mAjOZw}~t?OGZpFs-gfDH_+jdM^fWQ3YO;2%D$ z`peZ+Jc+z&x}tlpR(5yTsnmcDs|3L$)al)8X|@?q@?(S9g@cDt0~}B2b{orKkv*kB zjDJ^26Z)^`=jWv?z<Eq>OcRtcNzMRLx$uw6sV;H8dr|Urz7 zVI3g+{W4|p_5LE?*VlcYr!YFs+b>4PX-|?-L$T(ndU_K0Cu0xN#+7Br8m;A)f!sE0 zOB{No^UY5wbJHRVEn_~E>H$JcG9zbp3h?m_9N-tOB|`l_u{$Le5BU2L$bh}eR}aEF zc2Ov_>eUS=6&W(rzb=qcLPKC-88%%7-LoP=tM-)W+$d$E)$KyhF zSEh6Toqd50ibW)puewc>mFK7O!u~3%boE99f+}Q;f3M(S=s_3GB=USdc<^8~V+&At z_fP?rrZvQ>+6k<5hez4##cHT+vv(mO_p@ek%5b{p)! zmW^%&_noeSns1b$I(1~c=~<*f1>aj5KR#=!W@Y^# z(iQLoG4SOZ)w7?SsXZMayc)W%X2LQ+FrNYuVxrtRyM6+)t2cG{xnOSOP!Q>^=x0{mr{d=0NS=CRb$v;414jd~-G#uh3mD|i059A~#A zSiIQq4A)(AXU&_mzm$RxPEA@)t{}|3kB~|Cr?0IF4r}Pmvns-i*DHhpTXQACZXZ32 zb*;EfR-Y{G_C~%laWj8T3gtj@$Nc?xa`AljnuC%{wmGK15%%p{Rg`)tw$tSFVDjU_ z=F1gqg;q|QnHqhsc;JR@JxA4>6%&y&^Y>m%1DV~_-zmzTm)o?aat;ehEwqgIH!scL zn-A$9;gCAU(f9)1)O9%!`Z?LEWV>rTN zJ+KJbp0?L1E>a{FX8n2Ay~JWnY+@rI^sh#+OD}O@=5LHmPYWPj6(Y2PFf5?}(M+RK z%ge80EXxAVXBW@q=BjdBlL*Qhit8oFBt8~*dOKJ!aTuPW2^+xsQW*(M{@>eW!rLJI zB1BNGbzi+&_-Q^x+Gis^VD{sur$kJf?AcKsUS0_uS)R-&?%eY7FUii6Zj~)2O*of4 zUPVQzp@_V^u}^m&evM8|9WvqIkUpCR@mQIid&-Z{>Wzty?lzIBY1na$IyB}S6E^fs z05J;6ii)n`{Yl77qHcA-D#<|Q{@~ZQ<$x6y-ds3&c+vxYF?{po%nH>>)TamOwt*39 zf7`W}WQ?rR;=STHWp(;h>2O|GY^#0-(c%l&g$pgNVH|5eh*kz_dJ|&o*8vA0^?1U? z4(-qMwx2FY^o|7hI`gbjJL2PKkQX0@AnLWW)Xy(e&v-rqO&`K5w_Oa~_gZR|Qz zjUg?>`Uf8FE44!vR5ujMIU5+{&6JP1BNxCzf#QBGcC4R!q62e@_yK{9GLN$7)mauN zG#1=zyX20>x3aOI_0L^$4EE`6v<}`VRAF?P+({S)n;Yvnj~)UX2{&NWPIkk&J`e@{+WBl!jN+LK9Co9b{D|Ll? ziY%P!{5o5YMZMc@pK-ViHmOA>L!_*qn{brgd8?cPv*YkkbDleUl4WJ3nSQ`PD2~;-J5^Fwr1?XZAg_qgd0g5# zpN3MBP5v*DMP-;ZhEJ6Z+)z^Xm`R4qZ8(SHpntdVp7o`f0FoGDmEsq>v9!nxx<;;I zfOXpSOeb|Cv{mp|7A`_M<&3=h4u$B`SbGSwBM%I1WX9;(L1$-K6D8cI4}z`@i=!$Hb(D`PpIaIofVq0yM=)Nd+mw>$>>JMp?hg5%O3Uhjb4VQCOf;9+bZz3HSB&B}%O-x*~rV4?8Cnpl9^bEP!M6 z@=AR(^rpK|>r!fTbOz`$0=$!$PEhmswJ0_H-M0!Kp9WnjagR@1-XHAc`I7WGI>{Gv zX~Q!zx<{YZ)Lu!Df;vQIi7EQs@gQ^XO1v+>`rk{XanssK)zK)mG84L>Ut^ipvAFje zHizTV4|9n#0v49uOG06xp_*RX5!uOsWa{7p03_Kd;n%{(kU)}vl~GZ z1ca7ft|p>pvCC`&LNKmx$X=;Uqg_YhktkEu1-N-EaaolO~>Fli(=(5D}D^P>g zUuR9dw0ysyAl@ZriLyK`=ZB}(c5kkcorr?Ny@eQ1{5gMFykVqN18QojK<+D6c==Ku z6v-w!J8LvFH50mQxGt=3Hi6n(%eteUK#!%Nd%x+NK&JhQj#P!A0)})~3vnXu9CWJV z)tZjJm3X0-6X*1?nz$xeR{ygyK&{#@&M}_?e;sZEP81$-DBqy4)2WY2l8A5q>ORHR zTax|b$5?Z^|9qOtdaNKw^Fqe?xgnq)yd;IIR;bkAm=-NLI&GCEBo<*OyxCt$n$vx4 zjNC{YmfAquS9nd!Fc-PzU)!YW2i4B~nYr-?(6g+KKQ8?AVt$m-(c?0!UxtZQTKPB5 zo(Y(5%J!ZQuARahl}V4`r*Bd+Ys~rs%dt*+)&a4UHCL~@(~@wS=YYdw?^+#2l+>Vih=R0-RZ!P(9l+%?0gox zn?ACeI)Yc(%fy~*=pTIW^20SegEBn-(=&lQHaa@~Qd9cNgQuFN+q;K*O7zJoLLCK)T?r zVi-fuk-4yA!=7fhs-fke;g1v(0Kpa?3OX!TBRl!MYhP0Fc>>1xN$1!}Mf!m(t1}Pi z2ku@8Gkkk{%Etpw#o4sPcd$vi+^t3e^(|ii`1fv6fQ}xG?Y~z%h&3Fjt z2n5xJZzE6Q2>=WBfRb}NwyU56Kpt4e0p>7ALzIVIi2l=)S;%>-Np z1AIw-&?x~4fj8;Op*spL;rCfW^Rz;7?o_xb>piS6{rHMX?q((-FCfHYiT#Q&)5dp!z>+I&w@=mE;EK-V&kG=%)9u3a+ z-OF5G&r`WPGbS=(iK|3%Q)jyL#~O{fA$s#2jf{U#BAC94>|At(3c&$2OgyQ0_wL=v zSmV}M?(y-76acsN0-lYt?o$u=*GU1hQ#p@lL)7WMJII=i5SHrF4?{R|Z4FjnbC&C< zO^5zL>fi##yY)xw*RSmfb@zyY2@*C7S*_G?yhuPmPjZD%TaIg0Tj>vx=#0^HQ$y)7 zDTlr)&`<+NJUV%b*8+vBjrMf-JDjera)YPt4d|S4`&901L{L{#2d!EvjLs_iO(Rz9 z-_+nE({gXW!TEB%jmMau+t*#u(9y}6FLKf4*3DZ z%NxfcslrZ&f!s?p%jDKajvig%pT*(D>2uv$>zSZy?WuTXQIVpmYU{$h ziA>@fM8+49uGT3xTYSG0b!AiL9nRS24*Wi{r4%v_iQJ%mL%{R$I%!mPW!tLT z`Es8YUq8>3EA^`&^1B-M>-&o}lUV=g>c!^d<#ktCrA2Wc=2%h>lk$kht%5xlGY3UO z+O!Bp4Wn%!scC2{SZ&C)@FdH~bAyM$V*`FLhdkh(0~!-8GA$VGg)}FL$xm|hzB9$; z(b@RxvFLXYYw$qpbrF;>fPuq zq-x6;++=*ODybI#_SJqg;2!u{VXc-iTYVw%OVdN|xhg6`UcGu`e)mPCJ4uiCk`N$& zAduiZEkqs*;>^`lXEAsjyr9G2Qz7=EB!6p6A$!TV{^d+Wx7bISkbLnb=XvJFq!-wW znp&bN_^ZzOwF=Iz8x6p@-JJA#Vi)5-jGn2THb~-;lN+XN%HKC)yG-cT+Spc?_c=Lt z=jK|M*PMrI=#{Gtx}d0q9(KXhUQrq2HTXeIczl|e+SBa2*;1@RIu#|f#HVhckvBZJ zs_zF}w?CYu}M)*EA9skQK^2GsT;JoMtaXx2B}SNQ8!u(=qZ~HwWy?Oqmq<5 zXfW`DJONef>3a`NQWn<1uce4BRI<19{Y{+{GkVv*6L8sFIlUfWT78UQC4gv!_H#Ti z;B}QYJ#Sg1xw@yg3Q`Xp<+za%$;EamUa6CI*e)j8_lAI@^)%TX4JzA-bVMI0 zI$rFvuW8%>O=h?tPJu|US0I@C*I)nf%AL^~K*VQjrv!}LgcTLt5W(eZB{7IraRjJ9 zt`_O#7Y&eGrRsWhd16lN)#@3yA5`lsJRn%S?s`WtGy{+j`63t8LV}$DId{)>b8C~V z3x$O`A`3SH&WOBcG-Ex4S6HDzeNQ>gz<`F(f7}pxchM185*X^A+AxoWj;$zKW0x z>PGi-XFDgHp7e;+XC`r!=q^a)hKQ2e909!2Gu|+W#S66CNfUG9qT`R}6i%EZI zeG+wtF(^!r;Z+pKheYX#Q*76t+)TL!(wc>AkO_tk83;gVb%wRt5A0uKTs+hT931(8XyKEVmj3QMiA7{v4tZ8j zmx}*zu%5=8xoWVCmr&3)xLQx?<>leA`F?pfAY@Hvv^kC)d-v+qex5J9m&C;6{b)<7 z<{6ZLy_E(+paBU)J6!=SOp7TZlO~rx@nCG?5HK*zYg29dfCH!Sz$@fqjgk|(66{VV z%l50BJ3HAj6{zEo`lkG)*8b&CR>p${)`u(^&lJ3^FI}{c0|A0OfKq`pL5zHyY3h1< zd_i@`+BwGnzf|f(k?rHU0P=k;$^(%AU^`0`xwj!9e^Mtq3{_QmQL5(3%HiQZ&Zz2v zzJ%k)mEqx0tVm`?Bm>0Joq#3EFfuwi`YG+NLuT4)YH(1KflhTwzMThBl~%R1j0NRz zHY^grEi%4X0Tit9ot3L8bp15ozvQ&k!~8(g5g$L_N%m8SSFg^+D-P`Ed6{~p^l~rP zjpE?v!2{z{?+55(#6mq8zv7LRO>gl7+v~deuLRZYhcPbJb6(xWwBO%?N6lh20>O1n z(dCPnW@uvU$kK~?p-tvXM2ljj!Z}!G%rZ|w*cRVu{rcv!(Rt=?F2iEgRnr6I;(!cReUMEk@UOG3uh56OK$`hsaqq_H`za2Z%{E%_k&JztFM?EBe z7qWC;C7uX}B@02Qd*CchjtkSf2DNT$gvy76&y}FyYA#xenwqjS>p6jU9LJ`-N?+D6 zn2Lwngbf(u0ro|%*q|fk&{!gCIZLP&a>zvQSw*1$3+7s1Bv$0D9W95L(AXnB$f&x0 zZ#=it=JYg!*e?3zdDoLkE^z*@{r)W5D*^}q&(w+k-+69@u{`kl51TumUbM6!g9ja$ zUYzqc144dlb8O2FAOOpNLT-;E;~|KzPbvnB^j1X}ouY{>wO7FR+B59M`5Co|o6I23 zDe8+?WfWkumY=?X<)e_2q#!vV1(_vNt0x z73RfrRyV*GLNjG1-!azU{pr%c@#Q2FbQU2{) zB6@X#4%I!<)s^46`rIU-N1Cr3s&A0eLxX2EnD#p_Z9M_SblVtof(<&}$;=!F9bW?P z_Jd~MXCWbc6$V0}J`OreO59z+_y5|v-FC#PO@T|=UA8Ytw--m4n)NyDJQHF4aGJ40KVSc!?X>v6&w_r7lp%rt z`=8$1*{A=|3;y22|9B5qHpzYOb}^m%>&un8jprXYH?%)KwMkb=7Ty-vU;q5h#+QGd zn7;b+hrDhw9$eje{r=jP7=8<6TOEFD$d(*_o6K+L!1UV|e%r$DxbQnJF#Q)|q<*yt z4Ct4tX8b<_fMov*NB%w9Z*a@7)!)#CA%)+TwIv0n|C;zkvMs@^@otaEcLR0+ diff --git a/test/app/create/deposit/widgets/goldens/range_selector_is_infinity_increase_price_reversed.png b/test/app/create/deposit/widgets/goldens/range_selector_is_infinity_increase_price_reversed.png index 5c9e4afe5ba648e05bdeaa0fc2dab1b2920c1fde..5aa5a7caf457c6bb2d64a87342b61b4deff5ff54 100644 GIT binary patch delta 15053 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 QL;VC(RJ&bx>%p`C1NvJJHUIzs delta 16121 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 zupAnalytics.logDeposit( depositedYield: currentYield, - amount0: token0amount.parseTokenAmount(decimals: currentYield.token0.decimals), - amount1: token1amount.parseTokenAmount(decimals: currentYield.token1.decimals), + amount0: token0amount.parseTokenAmount(decimals: currentYield.token0NetworkDecimals), + amount1: token1amount.parseTokenAmount(decimals: currentYield.token1NetworkDecimals), walletAddress: userAddress, )).called(1); }, @@ -1936,16 +1936,16 @@ void main() { final tickLower = V3PoolConversorsMixinWrapper().tickToClosestValidTick( tick: V3PoolConversorsMixinWrapper().priceToTick( price: minPrice, - poolToken0Decimals: currentYield0.token0.decimals, - poolToken1Decimals: currentYield0.token1.decimals, + poolToken0Decimals: currentYield0.token0NetworkDecimals, + poolToken1Decimals: currentYield0.token1NetworkDecimals, ), tickSpacing: currentYield0.tickSpacing); final tickUpper = V3PoolConversorsMixinWrapper().tickToClosestValidTick( tick: V3PoolConversorsMixinWrapper().priceToTick( price: maxPrice, - poolToken0Decimals: currentYield0.token0.decimals, - poolToken1Decimals: currentYield0.token1.decimals, + poolToken0Decimals: currentYield0.token0NetworkDecimals, + poolToken1Decimals: currentYield0.token1NetworkDecimals, ), tickSpacing: currentYield0.tickSpacing); diff --git a/test/app/create/deposit/widgets/preview_deposit_modal/preview_deposit_modal_test.dart b/test/app/create/deposit/widgets/preview_deposit_modal/preview_deposit_modal_test.dart index 0961758..172ba61 100644 --- a/test/app/create/deposit/widgets/preview_deposit_modal/preview_deposit_modal_test.dart +++ b/test/app/create/deposit/widgets/preview_deposit_modal/preview_deposit_modal_test.dart @@ -307,8 +307,8 @@ void main() { final currentPriceAsTick = V3PoolConversorsMixinWrapper().priceToTick( price: currentPrice, - poolToken0Decimals: currentYield.token0.decimals, - poolToken1Decimals: currentYield.token1.decimals, + poolToken0Decimals: currentYield.token0NetworkDecimals, + poolToken1Decimals: currentYield.token1NetworkDecimals, ); when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick); @@ -333,8 +333,8 @@ void main() { final currentPriceAsTick = V3PoolConversorsMixinWrapper().priceToTick( price: currentPrice, - poolToken0Decimals: currentYield.token0.decimals, - poolToken1Decimals: currentYield.token1.decimals, + poolToken0Decimals: currentYield.token0NetworkDecimals, + poolToken1Decimals: currentYield.token1NetworkDecimals, ); when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick); @@ -362,8 +362,8 @@ void main() { final currentPriceAsTick = V3PoolConversorsMixinWrapper().priceToTick( price: currentPrice, - poolToken0Decimals: currentYield.token0.decimals, - poolToken1Decimals: currentYield.token1.decimals, + poolToken0Decimals: currentYield.token0NetworkDecimals, + poolToken1Decimals: currentYield.token1NetworkDecimals, ); when(() => cubit.latestPoolTick).thenReturn(currentPriceAsTick); @@ -539,7 +539,7 @@ void main() { verify( () => cubit.approveToken( currentYield.token0, - depositAmount.parseTokenAmount(decimals: currentYield.token0.decimals), + depositAmount.parseTokenAmount(decimals: currentYield.token0NetworkDecimals), ), ).called(1); }, @@ -556,14 +556,14 @@ void main() { when(() => cubit.state).thenReturn( PreviewDepositModalState.initial( - token0Allowance: depositAmount.parseTokenAmount(decimals: currentYield.token0.decimals), + token0Allowance: depositAmount.parseTokenAmount(decimals: currentYield.token0NetworkDecimals), token1Allowance: token1Allowance, ), ); when(() => cubit.stream).thenAnswer((_) { return Stream.value(PreviewDepositModalState.initial( - token0Allowance: depositAmount.parseTokenAmount(decimals: currentYield.token0.decimals), + token0Allowance: depositAmount.parseTokenAmount(decimals: currentYield.token0NetworkDecimals), token1Allowance: token1Allowance, )); }); @@ -591,14 +591,14 @@ void main() { when(() => cubit.state).thenReturn( PreviewDepositModalState.initial( - token0Allowance: depositAmount.parseTokenAmount(decimals: currentYield.token0.decimals), + token0Allowance: depositAmount.parseTokenAmount(decimals: currentYield.token0NetworkDecimals), token1Allowance: token1Allowance, ), ); when(() => cubit.stream).thenAnswer((_) { return Stream.value(PreviewDepositModalState.initial( - token0Allowance: depositAmount.parseTokenAmount(decimals: currentYield.token0.decimals), + token0Allowance: depositAmount.parseTokenAmount(decimals: currentYield.token0NetworkDecimals), token1Allowance: token1Allowance, )); }); @@ -615,7 +615,7 @@ void main() { verify( () => cubit.approveToken( currentYield.token1, - depositAmount.parseTokenAmount(decimals: currentYield.token1.decimals), + depositAmount.parseTokenAmount(decimals: currentYield.token1NetworkDecimals), ), ).called(1); }, @@ -627,8 +627,8 @@ void main() { in the deposit state""", goldenFileName: "preview_deposit_modal_deposit_state", (tester) async { - final token0Allowance = 400.parseTokenAmount(decimals: currentYield.token0.decimals); - final token1Allowance = 1200.parseTokenAmount(decimals: currentYield.token1.decimals); + final token0Allowance = 400.parseTokenAmount(decimals: currentYield.token0NetworkDecimals); + final token1Allowance = 1200.parseTokenAmount(decimals: currentYield.token1NetworkDecimals); const deposit0Amount = 100.2; const deposit1Amount = 110.2; @@ -661,8 +661,8 @@ void main() { in the deposit state. Once the deposit button is clicked, it should call the deposit function in the cubit passing the correct params (got from the constructor)""", (tester) async { - final token0Allowance = 400.parseTokenAmount(decimals: currentYield.token0.decimals); - final token1Allowance = 1200.parseTokenAmount(decimals: currentYield.token1.decimals); + final token0Allowance = 400.parseTokenAmount(decimals: currentYield.token0NetworkDecimals); + final token1Allowance = 1200.parseTokenAmount(decimals: currentYield.token1NetworkDecimals); const deposit0Amount = 100.2; const deposit1Amount = 110.2; @@ -714,8 +714,8 @@ void main() { maxPrice: maxPrice, minPrice: minPrice, slippage: slippage, - token0Amount: deposit0Amount.parseTokenAmount(decimals: currentYield.token0.decimals), - token1Amount: deposit1Amount.parseTokenAmount(decimals: currentYield.token1.decimals), + token0Amount: deposit0Amount.parseTokenAmount(decimals: currentYield.token0NetworkDecimals), + token1Amount: deposit1Amount.parseTokenAmount(decimals: currentYield.token1NetworkDecimals), ), ).called(1); }, @@ -728,8 +728,8 @@ void main() { when(() => cubit.latestPoolTick).thenReturn( V3PoolConversorsMixinWrapper().priceToTick( price: 0.01, // It should be shown in the card (or very close to it) - poolToken0Decimals: currentYield.token0.decimals, - poolToken1Decimals: currentYield.token1.decimals, + poolToken0Decimals: currentYield.token0NetworkDecimals, + poolToken1Decimals: currentYield.token1NetworkDecimals, isReversed: false, ), ); @@ -746,8 +746,8 @@ void main() { when(() => cubit.latestPoolTick).thenReturn( V3PoolConversorsMixinWrapper().priceToTick( price: 1200, // It should be shown in the card (or very close to it) - poolToken0Decimals: currentYield.token0.decimals, - poolToken1Decimals: currentYield.token1.decimals, + poolToken0Decimals: currentYield.token0NetworkDecimals, + poolToken1Decimals: currentYield.token1NetworkDecimals, isReversed: true, ), ); @@ -766,8 +766,8 @@ void main() { const newPrice = 0.02632; // It should be shown in the card (or very close to it) final newPriceAsTick = V3PoolConversorsMixinWrapper().priceToTick( price: newPrice, - poolToken0Decimals: currentYield.token0.decimals, - poolToken1Decimals: currentYield.token1.decimals, + poolToken0Decimals: currentYield.token0NetworkDecimals, + poolToken1Decimals: currentYield.token1NetworkDecimals, isReversed: false, ); diff --git a/test/app/create/deposit/widgets/range_selector_test.dart b/test/app/create/deposit/widgets/range_selector_test.dart index 00d56cb..2464733 100644 --- a/test/app/create/deposit/widgets/range_selector_test.dart +++ b/test/app/create/deposit/widgets/range_selector_test.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:golden_toolkit/golden_toolkit.dart'; import 'package:zup_app/app/create/deposit/widgets/range_selector.dart'; -import 'package:zup_app/core/dtos/token_dto.dart'; import '../../../../golden_config.dart'; @@ -11,8 +10,8 @@ void main() { Key? key, bool isReversed = false, Function(double price)? onPriceChanged, - TokenDto? poolToken0, - TokenDto? poolToken1, + int? poolToken0Decimals, + int? poolToken1Decimals, int tickSpacing = 10, RangeSelectorType type = RangeSelectorType.minPrice, double? initialPrice, @@ -32,8 +31,8 @@ void main() { displayQuoteTokenSymbol: "Token B", isReversed: isReversed, onPriceChanged: onPriceChanged ?? (_) {}, - poolToken0: poolToken0 ?? TokenDto.fixture().copyWith(symbol: "Token A"), - poolToken1: poolToken1 ?? TokenDto.fixture().copyWith(symbol: "Token B"), + poolToken0Decimals: poolToken0Decimals ?? 18, + poolToken1Decimals: poolToken0Decimals ?? 18, tickSpacing: tickSpacing, type: type, initialPrice: initialPrice, @@ -58,11 +57,7 @@ void main() { zGoldenTest("When the `isReversed` param is true, it should reverse the tokens in the widget", goldenFileName: "range_selector_reversed", (tester) async { return tester.pumpDeviceBuilder( - await goldenBuilder( - isReversed: true, - poolToken0: TokenDto.fixture().copyWith(symbol: "Token 0"), - poolToken1: TokenDto.fixture().copyWith(symbol: "Token 1"), - ), + await goldenBuilder(isReversed: true), ); }); @@ -304,13 +299,13 @@ void main() { "When the price is infinity, and click to increase, the price should increase to the minimum price based on the tick spacing", goldenFileName: "range_selector_is_infinity_increase_price", (tester) async { - const expectedIncreasedPrice = 1.0010004501200209e-12; + const expectedIncreasedPrice = 9.996040641477102e-19; double actualIncreasedPrice = 0; await tester.pumpDeviceBuilder(await goldenBuilder( isInfinity: true, - poolToken0: TokenDto.fixture().copyWith(decimals: 6), - poolToken1: TokenDto.fixture().copyWith(decimals: 18), + poolToken0Decimals: 6, + poolToken1Decimals: 18, onPriceChanged: (price) { actualIncreasedPrice = price; }, @@ -328,14 +323,14 @@ void main() { the price should increase to the minimum price based on the tick spacing""", goldenFileName: "range_selector_is_infinity_increase_price_reversed", (tester) async { - const expectedIncreasedPrice = 1.0008055719626048e-12; + const expectedIncreasedPrice = 9.996040641477102e-19; double actualIncreasedPrice = 0; await tester.pumpDeviceBuilder(await goldenBuilder( isInfinity: true, isReversed: true, - poolToken0: TokenDto.fixture().copyWith(decimals: 6), - poolToken1: TokenDto.fixture().copyWith(decimals: 18), + poolToken0Decimals: 6, + poolToken1Decimals: 18, onPriceChanged: (price) { actualIncreasedPrice = price; }, @@ -359,8 +354,8 @@ void main() { await tester.pumpDeviceBuilder(await goldenBuilder( isInfinity: true, isReversed: true, - poolToken0: TokenDto.fixture().copyWith(decimals: 6), - poolToken1: TokenDto.fixture().copyWith(decimals: 18), + poolToken0Decimals: 6, + poolToken1Decimals: 18, onPriceChanged: (price) { actualIncreasedPrice = price; }, @@ -382,8 +377,8 @@ void main() { await tester.pumpDeviceBuilder(await goldenBuilder( isInfinity: true, - poolToken0: TokenDto.fixture().copyWith(decimals: 6), - poolToken1: TokenDto.fixture().copyWith(decimals: 18), + poolToken0Decimals: 6, + poolToken1Decimals: 18, onPriceChanged: (price) { actualIncreasedPrice = price; }, diff --git a/test/core/enums/protocol_id_test.dart b/test/core/enums/protocol_id_test.dart new file mode 100644 index 0000000..b2d22a2 --- /dev/null +++ b/test/core/enums/protocol_id_test.dart @@ -0,0 +1,18 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:zup_app/core/enums/protocol_id.dart'; + +void main() { + test( + "When calling `isPancakeSwapInfinityCL` and the protocol is indeed pancakeSwapInfinityCL, it should return true", + () { + expect(ProtocolId.pancakeSwapInfinityCL.isPancakeSwapInfinityCL, true); + }, + ); + + test( + "When calling `isPancakeSwapInfinityCL` and the protocol is not pancakeSwapInfinityCL, it should return false", + () { + expect(ProtocolId.unknown.isPancakeSwapInfinityCL, false); + }, + ); +} diff --git a/test/core/pool_service_test.dart b/test/core/pool_service_test.dart index 5c2289c..e511d08 100644 --- a/test/core/pool_service_test.dart +++ b/test/core/pool_service_test.dart @@ -4,14 +4,17 @@ import 'package:mocktail/mocktail.dart'; import 'package:web3kit/core/dtos/transaction_receipt.dart'; import 'package:web3kit/core/dtos/transaction_response.dart'; import 'package:web3kit/web3kit.dart'; +import 'package:zup_app/abis/pancake_swap_infinity_cl_pool_manager.abi.g.dart'; import 'package:zup_app/abis/uniswap_v3_pool.abi.g.dart'; import 'package:zup_app/abis/uniswap_v3_position_manager.abi.g.dart'; import 'package:zup_app/abis/uniswap_v4_position_manager.abi.g.dart'; import 'package:zup_app/abis/uniswap_v4_state_view.abi.g.dart'; +import 'package:zup_app/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/networks.dart'; import 'package:zup_app/core/enums/pool_type.dart'; +import 'package:zup_app/core/enums/protocol_id.dart'; import 'package:zup_app/core/mixins/v4_pool_liquidity_calculations_mixin.dart'; import 'package:zup_app/core/pool_service.dart'; import 'package:zup_app/core/v4_pool_constants.dart'; @@ -26,6 +29,7 @@ void main() { late UniswapV3Pool uniswapV3Pool; late UniswapV3PositionManager positionManagerV3; late UniswapV4PositionManager positionManagerV4; + late PancakeSwapInfinityClPoolManager pancakeSwapInfinityCLPoolManager; late Signer signer; late YieldDto currentYield; late TransactionResponse transactionResponse; @@ -34,6 +38,7 @@ void main() { late UniswapV3PoolImpl uniswapV3PoolImpl; late UniswapV3PositionManagerImpl positionManagerV3Impl; late UniswapV4PositionManagerImpl positionManagerV4Impl; + late PancakeSwapInfinityClPoolManagerImpl pancakeSwapInfinityCLPoolManagerImpl; late EthereumAbiCoder ethereumAbiCoder; setUp(() { @@ -58,9 +63,12 @@ void main() { uniswapV3Pool = UniswapV3PoolMock(); positionManagerV3 = UniswapV3PositionManagerMock(); positionManagerV4 = UniswapV4PositionManagerMock(); + pancakeSwapInfinityCLPoolManager = PancakeSwapInfinityCLPoolManagerMock(); ethereumAbiCoder = EthereumAbiCoderMock(); signer = SignerMock(); + pancakeSwapInfinityCLPoolManagerImpl = PancakeSwapInfinityCLPoolManagerImplMock(); + stateViewImpl = UniswapV4StateViewImplMock(); uniswapV3PoolImpl = UniswapV3PoolImplMock(); positionManagerV3Impl = UniswapV3PositionManagerImplMock(); @@ -68,7 +76,14 @@ void main() { currentYield = YieldDto.fixture(); - sut = PoolService(stateView, uniswapV3Pool, positionManagerV3, positionManagerV4, ethereumAbiCoder); + sut = PoolService( + stateView, + uniswapV3Pool, + positionManagerV3, + positionManagerV4, + ethereumAbiCoder, + pancakeSwapInfinityCLPoolManager, + ); when(() => stateView.fromRpcProvider(contractAddress: any(named: "contractAddress"), rpcUrl: any(named: "rpcUrl"))) .thenReturn(stateViewImpl); @@ -110,6 +125,12 @@ void main() { sqrtPriceX96: BigInt.from(0), tick: expectedTick, )); + when(() => pancakeSwapInfinityCLPoolManagerImpl.getSlot0(id: any(named: "id"))).thenAnswer((_) async => ( + lpFee: BigInt.from(0), + protocolFee: BigInt.from(0), + sqrtPriceX96: BigInt.from(0), + tick: expectedTick, + )); final currentYield0 = currentYield.copyWith(poolType: PoolType.v4, v4StateView: "0x123"); final result = await sut.getPoolTick(currentYield0); @@ -1331,4 +1352,34 @@ void main() { ).called(1); }, ); + + test( + """"When calling `getPoolTick` and the yield protocol is pancakeswap infinity cl, + it should use the pancakeswap inifity cl pool manager to get the tick""", + () async { + final expectedTick = BigInt.from(318675); + + when(() => pancakeSwapInfinityCLPoolManager.fromRpcProvider( + contractAddress: any(named: "contractAddress"), rpcUrl: any(named: "rpcUrl"))).thenReturn( + pancakeSwapInfinityCLPoolManagerImpl, + ); + + when(() => pancakeSwapInfinityCLPoolManagerImpl.getSlot0(id: any(named: "id"))).thenAnswer((_) async => ( + sqrtPriceX96: BigInt.from(0), + tick: expectedTick, + protocolFee: BigInt.from(0), + lpFee: BigInt.from(0), + )); + + final yield0 = currentYield.copyWith( + protocol: ProtocolDto.fixture().copyWith(id: ProtocolId.pancakeSwapInfinityCL), + v4PoolManager: "0x0000001", + ); + + final receivedPoolTick = await sut.getPoolTick(yield0); + expect(receivedPoolTick, expectedTick); + + verify(() => pancakeSwapInfinityCLPoolManagerImpl.getSlot0(id: yield0.poolAddress)).called(1); + }, + ); } diff --git a/test/mocks.dart b/test/mocks.dart index f167e89..14e9072 100644 --- a/test/mocks.dart +++ b/test/mocks.dart @@ -12,6 +12,7 @@ import 'package:url_launcher_platform_interface/url_launcher_platform_interface. import 'package:web3kit/core/dtos/transaction_response.dart'; import 'package:web3kit/web3kit.dart'; import 'package:zup_app/abis/erc_20.abi.g.dart'; +import 'package:zup_app/abis/pancake_swap_infinity_cl_pool_manager.abi.g.dart'; import 'package:zup_app/abis/uniswap_permit2.abi.g.dart'; import 'package:zup_app/abis/uniswap_v3_pool.abi.g.dart'; import 'package:zup_app/abis/uniswap_v3_position_manager.abi.g.dart'; @@ -87,6 +88,10 @@ class UniswapV4PositionManagerMock extends Mock implements UniswapV4PositionMana class UniswapV4PositionManagerImplMock extends Mock implements UniswapV4PositionManagerImpl {} +class PancakeSwapInfinityCLPoolManagerMock extends Mock implements PancakeSwapInfinityClPoolManager {} + +class PancakeSwapInfinityCLPoolManagerImplMock extends Mock implements PancakeSwapInfinityClPoolManagerImpl {} + class UniswapV3PoolImplMock extends Mock implements UniswapV3PoolImpl {} class UniswapV3PoolMock extends Mock implements UniswapV3Pool {} diff --git a/test/widgets/token_selector_modal/token_selector_modal_cubit_test.dart b/test/widgets/token_selector_modal/token_selector_modal_cubit_test.dart index 3f2868a..0ac65fb 100644 --- a/test/widgets/token_selector_modal/token_selector_modal_cubit_test.dart +++ b/test/widgets/token_selector_modal/token_selector_modal_cubit_test.dart @@ -279,8 +279,8 @@ void main() { test("""When calling 'searchToken' and all the tokens in the list returned does not have name and symbol, it should emit the search not found state""", () async { final returnedList = [ - const TokenDto(name: "", symbol: "", decimals: 0, logoUrl: "", addresses: {}), - const TokenDto(name: "", symbol: "", decimals: 0, logoUrl: "", addresses: {}), + TokenDto.fixture().copyWith(name: "", symbol: "", logoUrl: "", addresses: {}), + TokenDto.fixture().copyWith(name: "", symbol: "", decimals: {}, logoUrl: "", addresses: {}), ]; when(() => tokensRepository.searchToken(any(), any())).thenAnswer((_) async => returnedList); @@ -294,8 +294,8 @@ void main() { it should emit the search sucesss state, without the tokens without name and symbol""", () async { final namedToken = TokenDto.fixture(); final returnedList = [ - const TokenDto(name: "", symbol: "", decimals: 0, logoUrl: "", addresses: {}), - const TokenDto(name: "", symbol: "", decimals: 0, logoUrl: "", addresses: {}), + TokenDto.fixture().copyWith(name: "", symbol: "", logoUrl: "", addresses: {}), + TokenDto.fixture().copyWith(name: "", symbol: "", logoUrl: "", addresses: {}), namedToken, ];