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/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/abis/uniswap_v2_pool.abi.json b/lib/abis/uniswap_v2_pool.abi.json
new file mode 100644
index 0000000..09dc5dc
--- /dev/null
+++ b/lib/abis/uniswap_v2_pool.abi.json
@@ -0,0 +1,713 @@
+[
+ {
+ "inputs": [],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "constructor"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "owner",
+ "type": "address"
+ },
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "spender",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "value",
+ "type": "uint256"
+ }
+ ],
+ "name": "Approval",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "sender",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "amount0",
+ "type": "uint256"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "amount1",
+ "type": "uint256"
+ },
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "to",
+ "type": "address"
+ }
+ ],
+ "name": "Burn",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "sender",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "amount0",
+ "type": "uint256"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "amount1",
+ "type": "uint256"
+ }
+ ],
+ "name": "Mint",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "sender",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "amount0In",
+ "type": "uint256"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "amount1In",
+ "type": "uint256"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "amount0Out",
+ "type": "uint256"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "amount1Out",
+ "type": "uint256"
+ },
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "to",
+ "type": "address"
+ }
+ ],
+ "name": "Swap",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": false,
+ "internalType": "uint112",
+ "name": "reserve0",
+ "type": "uint112"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint112",
+ "name": "reserve1",
+ "type": "uint112"
+ }
+ ],
+ "name": "Sync",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "from",
+ "type": "address"
+ },
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "to",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "value",
+ "type": "uint256"
+ }
+ ],
+ "name": "Transfer",
+ "type": "event"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "DOMAIN_SEPARATOR",
+ "outputs": [
+ {
+ "internalType": "bytes32",
+ "name": "",
+ "type": "bytes32"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "MINIMUM_LIQUIDITY",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "PERMIT_TYPEHASH",
+ "outputs": [
+ {
+ "internalType": "bytes32",
+ "name": "",
+ "type": "bytes32"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "name": "allowance",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "spender",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "value",
+ "type": "uint256"
+ }
+ ],
+ "name": "approve",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "name": "balanceOf",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "to",
+ "type": "address"
+ }
+ ],
+ "name": "burn",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "amount0",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amount1",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "decimals",
+ "outputs": [
+ {
+ "internalType": "uint8",
+ "name": "",
+ "type": "uint8"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "factory",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "getReserves",
+ "outputs": [
+ {
+ "internalType": "uint112",
+ "name": "_reserve0",
+ "type": "uint112"
+ },
+ {
+ "internalType": "uint112",
+ "name": "_reserve1",
+ "type": "uint112"
+ },
+ {
+ "internalType": "uint32",
+ "name": "_blockTimestampLast",
+ "type": "uint32"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "_token0",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "_token1",
+ "type": "address"
+ }
+ ],
+ "name": "initialize",
+ "outputs": [],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "kLast",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "to",
+ "type": "address"
+ }
+ ],
+ "name": "mint",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "liquidity",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "name",
+ "outputs": [
+ {
+ "internalType": "string",
+ "name": "",
+ "type": "string"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "name": "nonces",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "owner",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "spender",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "value",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "deadline",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint8",
+ "name": "v",
+ "type": "uint8"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "r",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "s",
+ "type": "bytes32"
+ }
+ ],
+ "name": "permit",
+ "outputs": [],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "price0CumulativeLast",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "price1CumulativeLast",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "to",
+ "type": "address"
+ }
+ ],
+ "name": "skim",
+ "outputs": [],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "amount0Out",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amount1Out",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address",
+ "name": "to",
+ "type": "address"
+ },
+ {
+ "internalType": "bytes",
+ "name": "data",
+ "type": "bytes"
+ }
+ ],
+ "name": "swap",
+ "outputs": [],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "symbol",
+ "outputs": [
+ {
+ "internalType": "string",
+ "name": "",
+ "type": "string"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [],
+ "name": "sync",
+ "outputs": [],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "token0",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "token1",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "totalSupply",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "to",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "value",
+ "type": "uint256"
+ }
+ ],
+ "name": "transfer",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "from",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "to",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "value",
+ "type": "uint256"
+ }
+ ],
+ "name": "transferFrom",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ }
+]
\ No newline at end of file
diff --git a/lib/abis/uniswap_v2_router_02.abi.json b/lib/abis/uniswap_v2_router_02.abi.json
new file mode 100644
index 0000000..c23fa60
--- /dev/null
+++ b/lib/abis/uniswap_v2_router_02.abi.json
@@ -0,0 +1,973 @@
+[
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "_factory",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "_WETH",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "nonpayable",
+ "type": "constructor"
+ },
+ {
+ "inputs": [],
+ "name": "WETH",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "tokenA",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "tokenB",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amountADesired",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amountBDesired",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amountAMin",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amountBMin",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address",
+ "name": "to",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "deadline",
+ "type": "uint256"
+ }
+ ],
+ "name": "addLiquidity",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "amountA",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amountB",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "liquidity",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "token",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amountTokenDesired",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amountTokenMin",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amountETHMin",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address",
+ "name": "to",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "deadline",
+ "type": "uint256"
+ }
+ ],
+ "name": "addLiquidityETH",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "amountToken",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amountETH",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "liquidity",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "payable",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "factory",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "amountOut",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "reserveIn",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "reserveOut",
+ "type": "uint256"
+ }
+ ],
+ "name": "getAmountIn",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "amountIn",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "pure",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "amountIn",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "reserveIn",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "reserveOut",
+ "type": "uint256"
+ }
+ ],
+ "name": "getAmountOut",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "amountOut",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "pure",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "amountOut",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address[]",
+ "name": "path",
+ "type": "address[]"
+ }
+ ],
+ "name": "getAmountsIn",
+ "outputs": [
+ {
+ "internalType": "uint256[]",
+ "name": "amounts",
+ "type": "uint256[]"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "amountIn",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address[]",
+ "name": "path",
+ "type": "address[]"
+ }
+ ],
+ "name": "getAmountsOut",
+ "outputs": [
+ {
+ "internalType": "uint256[]",
+ "name": "amounts",
+ "type": "uint256[]"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "amountA",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "reserveA",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "reserveB",
+ "type": "uint256"
+ }
+ ],
+ "name": "quote",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "amountB",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "pure",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "tokenA",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "tokenB",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "liquidity",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amountAMin",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amountBMin",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address",
+ "name": "to",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "deadline",
+ "type": "uint256"
+ }
+ ],
+ "name": "removeLiquidity",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "amountA",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amountB",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "token",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "liquidity",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amountTokenMin",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amountETHMin",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address",
+ "name": "to",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "deadline",
+ "type": "uint256"
+ }
+ ],
+ "name": "removeLiquidityETH",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "amountToken",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amountETH",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "token",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "liquidity",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amountTokenMin",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amountETHMin",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address",
+ "name": "to",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "deadline",
+ "type": "uint256"
+ }
+ ],
+ "name": "removeLiquidityETHSupportingFeeOnTransferTokens",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "amountETH",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "token",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "liquidity",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amountTokenMin",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amountETHMin",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address",
+ "name": "to",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "deadline",
+ "type": "uint256"
+ },
+ {
+ "internalType": "bool",
+ "name": "approveMax",
+ "type": "bool"
+ },
+ {
+ "internalType": "uint8",
+ "name": "v",
+ "type": "uint8"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "r",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "s",
+ "type": "bytes32"
+ }
+ ],
+ "name": "removeLiquidityETHWithPermit",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "amountToken",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amountETH",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "token",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "liquidity",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amountTokenMin",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amountETHMin",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address",
+ "name": "to",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "deadline",
+ "type": "uint256"
+ },
+ {
+ "internalType": "bool",
+ "name": "approveMax",
+ "type": "bool"
+ },
+ {
+ "internalType": "uint8",
+ "name": "v",
+ "type": "uint8"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "r",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "s",
+ "type": "bytes32"
+ }
+ ],
+ "name": "removeLiquidityETHWithPermitSupportingFeeOnTransferTokens",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "amountETH",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "tokenA",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "tokenB",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "liquidity",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amountAMin",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amountBMin",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address",
+ "name": "to",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "deadline",
+ "type": "uint256"
+ },
+ {
+ "internalType": "bool",
+ "name": "approveMax",
+ "type": "bool"
+ },
+ {
+ "internalType": "uint8",
+ "name": "v",
+ "type": "uint8"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "r",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "s",
+ "type": "bytes32"
+ }
+ ],
+ "name": "removeLiquidityWithPermit",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "amountA",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amountB",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "amountOut",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address[]",
+ "name": "path",
+ "type": "address[]"
+ },
+ {
+ "internalType": "address",
+ "name": "to",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "deadline",
+ "type": "uint256"
+ }
+ ],
+ "name": "swapETHForExactTokens",
+ "outputs": [
+ {
+ "internalType": "uint256[]",
+ "name": "amounts",
+ "type": "uint256[]"
+ }
+ ],
+ "stateMutability": "payable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "amountOutMin",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address[]",
+ "name": "path",
+ "type": "address[]"
+ },
+ {
+ "internalType": "address",
+ "name": "to",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "deadline",
+ "type": "uint256"
+ }
+ ],
+ "name": "swapExactETHForTokens",
+ "outputs": [
+ {
+ "internalType": "uint256[]",
+ "name": "amounts",
+ "type": "uint256[]"
+ }
+ ],
+ "stateMutability": "payable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "amountOutMin",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address[]",
+ "name": "path",
+ "type": "address[]"
+ },
+ {
+ "internalType": "address",
+ "name": "to",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "deadline",
+ "type": "uint256"
+ }
+ ],
+ "name": "swapExactETHForTokensSupportingFeeOnTransferTokens",
+ "outputs": [],
+ "stateMutability": "payable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "amountIn",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amountOutMin",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address[]",
+ "name": "path",
+ "type": "address[]"
+ },
+ {
+ "internalType": "address",
+ "name": "to",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "deadline",
+ "type": "uint256"
+ }
+ ],
+ "name": "swapExactTokensForETH",
+ "outputs": [
+ {
+ "internalType": "uint256[]",
+ "name": "amounts",
+ "type": "uint256[]"
+ }
+ ],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "amountIn",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amountOutMin",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address[]",
+ "name": "path",
+ "type": "address[]"
+ },
+ {
+ "internalType": "address",
+ "name": "to",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "deadline",
+ "type": "uint256"
+ }
+ ],
+ "name": "swapExactTokensForETHSupportingFeeOnTransferTokens",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "amountIn",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amountOutMin",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address[]",
+ "name": "path",
+ "type": "address[]"
+ },
+ {
+ "internalType": "address",
+ "name": "to",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "deadline",
+ "type": "uint256"
+ }
+ ],
+ "name": "swapExactTokensForTokens",
+ "outputs": [
+ {
+ "internalType": "uint256[]",
+ "name": "amounts",
+ "type": "uint256[]"
+ }
+ ],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "amountIn",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amountOutMin",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address[]",
+ "name": "path",
+ "type": "address[]"
+ },
+ {
+ "internalType": "address",
+ "name": "to",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "deadline",
+ "type": "uint256"
+ }
+ ],
+ "name": "swapExactTokensForTokensSupportingFeeOnTransferTokens",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "amountOut",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amountInMax",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address[]",
+ "name": "path",
+ "type": "address[]"
+ },
+ {
+ "internalType": "address",
+ "name": "to",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "deadline",
+ "type": "uint256"
+ }
+ ],
+ "name": "swapTokensForExactETH",
+ "outputs": [
+ {
+ "internalType": "uint256[]",
+ "name": "amounts",
+ "type": "uint256[]"
+ }
+ ],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "amountOut",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amountInMax",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address[]",
+ "name": "path",
+ "type": "address[]"
+ },
+ {
+ "internalType": "address",
+ "name": "to",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "deadline",
+ "type": "uint256"
+ }
+ ],
+ "name": "swapTokensForExactTokens",
+ "outputs": [
+ {
+ "internalType": "uint256[]",
+ "name": "amounts",
+ "type": "uint256[]"
+ }
+ ],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "stateMutability": "payable",
+ "type": "receive"
+ }
+]
\ No newline at end of file
diff --git a/lib/app/create/deposit/deposit_cubit.dart b/lib/app/create/deposit/deposit_cubit.dart
index 0f30cd1..65fbc4c 100644
--- a/lib/app/create/deposit/deposit_cubit.dart
+++ b/lib/app/create/deposit/deposit_cubit.dart
@@ -41,26 +41,31 @@ class DepositCubit extends Cubit with KeysMixin, V3PoolConversorsM
final ZupAnalytics _zupAnalytics;
final StreamController _pooltickStreamController = StreamController.broadcast();
+ final StreamController<({double reserve0, double reserve1})?> _v2PoolReservesStreamController =
+ StreamController.broadcast();
final StreamController _selectedYieldStreamController = StreamController.broadcast();
BigInt? _latestPoolTick;
+ ({double reserve0, double reserve1})? _latestV2PoolReserves;
YieldDto? _selectedYield;
YieldTimeFrame? _selectedYieldTimeframe;
late final Stream selectedYieldStream = _selectedYieldStreamController.stream;
late final Stream poolTickStream = _pooltickStreamController.stream;
+ late final Stream<({double reserve0, double reserve1})?> v2PoolReservesStream =
+ _v2PoolReservesStreamController.stream;
YieldDto? get selectedYield => _selectedYield;
YieldTimeFrame? get selectedYieldTimeframe => _selectedYieldTimeframe;
BigInt? get latestPoolTick => _latestPoolTick;
+ ({double reserve0, double reserve1})? get latestV2PoolReserves => _latestV2PoolReserves;
DepositSettingsDto get depositSettings => _cache.getDepositSettings();
PoolSearchSettingsDto get poolSearchSettings => _cache.getPoolSearchSettings();
void setup() async {
Timer.periodic(const Duration(minutes: 1), (timer) {
- if (_pooltickStreamController.isClosed) return timer.cancel();
-
- if (selectedYield != null) getSelectedPoolTick();
+ if (_pooltickStreamController.isClosed || _v2PoolReservesStreamController.isClosed) return timer.cancel();
+ if (selectedYield != null) getSelectedPoolTickOrReserves();
});
}
@@ -108,7 +113,41 @@ class DepositCubit extends Cubit with KeysMixin, V3PoolConversorsM
_selectedYieldTimeframe = yieldTimeFrame;
_selectedYieldStreamController.add(selectedYield);
- if (selectedYield != null) await getSelectedPoolTick();
+ if (selectedYield != null) await getSelectedPoolTickOrReserves();
+ }
+
+ Future getSelectedPoolTickOrReserves() async {
+ if (selectedYield == null) return;
+
+ if (selectedYield!.poolType.isV2) return await getSelectedPoolV2Reserves();
+ return await getSelectedPoolTick();
+ }
+
+ Future getSelectedPoolV2Reserves() async {
+ if (selectedYield == null) return;
+
+ _latestV2PoolReserves = null;
+ _v2PoolReservesStreamController.add(null);
+
+ final selectedYieldBeforeCall = selectedYield;
+
+ final poolReserves = await _poolService.getV2PoolReserves(selectedYield!);
+
+ final poolReserve0DecimalFormatted = poolReserves.reserve0.parseTokenAmount(
+ decimals: selectedYield!.token0NetworkDecimals,
+ );
+
+ final poolReserve1DecimalFormatted = poolReserves.reserve1.parseTokenAmount(
+ decimals: selectedYield!.token1NetworkDecimals,
+ );
+
+ if (selectedYieldBeforeCall != selectedYield) return await getSelectedPoolV2Reserves();
+
+ _v2PoolReservesStreamController.add(
+ (reserve0: poolReserve0DecimalFormatted, reserve1: poolReserve1DecimalFormatted),
+ );
+
+ _latestV2PoolReserves = (reserve0: poolReserve0DecimalFormatted, reserve1: poolReserve1DecimalFormatted);
}
Future getSelectedPoolTick() async {
diff --git a/lib/app/create/deposit/deposit_page.dart b/lib/app/create/deposit/deposit_page.dart
index d7c933d..7b1a8d8 100644
--- a/lib/app/create/deposit/deposit_page.dart
+++ b/lib/app/create/deposit/deposit_page.dart
@@ -80,6 +80,7 @@ class _DepositPageState extends State
final quoteTokenAmountController = TextEditingController();
final wallet = inject();
final selectRangeSectorKey = GlobalKey();
+ final v2PoolDepositSectionKey = GlobalKey();
ZupNavigator get _navigator => inject();
DepositCubit get _cubit => context.read();
@@ -119,8 +120,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;
@@ -153,9 +154,11 @@ class _DepositPageState extends State
if (yieldDto != null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
- if (selectRangeSectorKey.currentContext != null) {
+ final scrollToKey = yieldDto.poolType.isV2 ? v2PoolDepositSectionKey : selectRangeSectorKey;
+
+ if (scrollToKey.currentContext != null) {
Scrollable.ensureVisible(
- selectRangeSectorKey.currentContext!,
+ scrollToKey.currentContext!,
duration: const Duration(milliseconds: 600),
curve: Curves.fastEaseInToSlowEaseOut,
);
@@ -178,6 +181,35 @@ class _DepositPageState extends State
}
void calculateDepositTokensAmount() {
+ if (_cubit.selectedYield?.poolType.isV2 ?? false) {
+ if (_cubit.latestV2PoolReserves == null || _cubit.selectedYield == null) return;
+
+ final poolReserve0 = _cubit.latestV2PoolReserves!.reserve0;
+ final poolReserve1 = _cubit.latestV2PoolReserves!.reserve1;
+
+ if (isBaseTokenAmountUserInput && baseTokenAmountController.text.isEmpty) {
+ quoteTokenAmountController.clear();
+ return;
+ }
+
+ if (!isBaseTokenAmountUserInput && quoteTokenAmountController.text.isEmpty) {
+ baseTokenAmountController.clear();
+ return;
+ }
+
+ if (isBaseTokenAmountUserInput) {
+ quoteTokenAmountController.text =
+ (poolReserve1 / poolReserve0 * (double.parse(baseTokenAmountController.text))).toString();
+
+ return;
+ }
+
+ baseTokenAmountController.text =
+ (poolReserve0 / poolReserve1 * (double.parse(quoteTokenAmountController.text))).toString();
+
+ return;
+ }
+
if (_cubit.latestPoolTick == null || _cubit.selectedYield == null) return;
if (isOutOfRange.minPrice) return quoteTokenAmountController.clear();
@@ -185,14 +217,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() {
@@ -234,7 +266,7 @@ class _DepositPageState extends State
baseTokenAmountController.text = newBaseTokenAmount;
}
- Future<({String title, Widget? icon, Function()? onPressed})> depositButtonState() async {
+ Future<({String title, Widget? icon, Function()? onPressed})> v3PoolDepositButtonState() async {
final userWalletBaseTokenAmount = await _cubit.getWalletTokenAmount(
baseToken.addresses[_cubit.selectedYield!.network.chainId]!,
network: _cubit.selectedYield!.network,
@@ -308,6 +340,65 @@ class _DepositPageState extends State
);
}
+ Future<({String title, Widget? icon, Function()? onPressed})> v2PoolDepositButtonState() async {
+ if (baseTokenAmountController.text.isEmptyOrZero || quoteTokenAmountController.text.isEmptyOrZero) {
+ return (
+ title: S.of(context).depositPageInvalidTokenAmountV2Pool(
+ token0Symbol: _cubit.selectedYield!.token0.symbol,
+ token1Symbol: _cubit.selectedYield!.token1.symbol,
+ ),
+ icon: null,
+ onPressed: null
+ );
+ }
+
+ final userWalletToken0Amount = await _cubit.getWalletTokenAmount(
+ _cubit.selectedYield!.token0NetworkAddress,
+ network: _cubit.selectedYield!.network,
+ );
+
+ final userWalletToken1Amount = await _cubit.getWalletTokenAmount(
+ _cubit.selectedYield!.token1NetworkAddress,
+ network: _cubit.selectedYield!.network,
+ );
+
+ if (userWalletToken0Amount < (double.tryParse(baseTokenAmountController.text) ?? 0)) {
+ return (
+ title: S.of(context).depositPageInsufficientTokenBalance(tokenSymbol: baseToken.symbol),
+ icon: null,
+ onPressed: null
+ );
+ }
+
+ if (userWalletToken1Amount < (double.tryParse(quoteTokenAmountController.text) ?? 0)) {
+ return (
+ title: S.of(context).depositPageInsufficientTokenBalance(tokenSymbol: quoteToken.symbol),
+ icon: null,
+ onPressed: null
+ );
+ }
+
+ return (
+ title: S.of(context).preview,
+ icon: Assets.icons.scrollFill.svg(),
+ onPressed: () {
+ PreviewDepositModal(
+ key: const Key("preview-deposit-modal"),
+ yieldTimeFrame: _cubit.selectedYieldTimeframe!,
+ deadline: selectedDeadline,
+ maxSlippage: selectedSlippage,
+ currentYield: _cubit.selectedYield!,
+ isReversed: areTokensReversed,
+ token0DepositAmount: double.tryParse(baseTokenAmountController.text) ?? 0,
+ token1DepositAmount: double.tryParse(quoteTokenAmountController.text) ?? 0,
+ ).show(
+ context,
+ currentPoolTick: BigInt.zero,
+ );
+ }
+ );
+ }
+
@override
void initState() {
_cubit.setup();
@@ -399,10 +490,13 @@ class _DepositPageState extends State
const SizedBox(height: 16),
_buildYieldSelectionSector(yields),
const SizedBox(height: 20),
- if (selectedYieldSnapshot.data != null) ...[
+ if (selectedYieldSnapshot.data != null && !(selectedYieldSnapshot.data!.poolType.isV2)) ...[
_buildSelectRangeSector(),
const SizedBox(height: 20),
_buildDepositSection(),
+ ] else if (selectedYieldSnapshot.data != null &&
+ selectedYieldSnapshot.data!.poolType.isV2) ...[
+ _buildDepositSectionForV2Pool()
],
const SizedBox(height: 200)
],
@@ -727,8 +821,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 +852,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 +897,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: () {
@@ -838,7 +932,6 @@ class _DepositPageState extends State
opacity: isRangeInvalid ? 0.2 : 1,
child: StreamBuilder(
stream: _cubit.poolTickStream,
- initialData: _cubit.latestPoolTick,
builder: (context, poolTickSnapshot) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -926,7 +1019,7 @@ class _DepositPageState extends State
}
return FutureBuilder(
- future: depositButtonState(),
+ future: v3PoolDepositButtonState(),
builder: (context, stateSnapshot) {
return ZupPrimaryButton(
alignCenter: true,
@@ -949,4 +1042,97 @@ class _DepositPageState extends State
}),
),
);
+
+ Widget _buildDepositSectionForV2Pool() {
+ return StreamBuilder(
+ key: const Key("v2-pool-deposit-section"),
+ stream: _cubit.v2PoolReservesStream,
+ initialData: _cubit.latestV2PoolReserves,
+ builder: (context, poolReservesSnapshot) {
+ return Column(
+ key: v2PoolDepositSectionKey,
+ children: [
+ TokenAmountInputCard(
+ key: const Key("v2-pool-base-token-input-card"),
+ token: _cubit.selectedYield!.token0,
+ isNative: _cubit.selectedYield!.isToken0Native,
+ disabledText: () {
+ if (!(poolReservesSnapshot.hasData) &&
+ !(isBaseTokenAmountUserInput) &&
+ quoteTokenAmountController.text.isNotEmpty) {
+ return S.of(context).loading;
+ }
+ }.call(),
+ onInput: (_) {
+ setState(() {
+ isBaseTokenAmountUserInput = true;
+
+ calculateDepositTokensAmount();
+ });
+ },
+ controller: baseTokenAmountController,
+ network: _cubit.selectedYield!.network,
+ ),
+ const SizedBox(height: 6),
+ TokenAmountInputCard(
+ key: const Key("v2-pool-quote-token-input-card"),
+ token: _cubit.selectedYield!.token1,
+ isNative: _cubit.selectedYield!.isToken1Native,
+ disabledText: () {
+ if (!(poolReservesSnapshot.hasData) &&
+ isBaseTokenAmountUserInput &&
+ baseTokenAmountController.text.isNotEmpty) {
+ return S.of(context).loading;
+ }
+ }.call(),
+ onInput: (_) {
+ setState(() {
+ isBaseTokenAmountUserInput = false;
+
+ calculateDepositTokensAmount();
+ });
+ },
+ controller: quoteTokenAmountController,
+ network: _cubit.selectedYield!.network,
+ ),
+ const SizedBox(height: 20),
+ StreamBuilder(
+ key: const Key("deposit-button"),
+ stream: wallet.signerStream,
+ initialData: wallet.signer,
+ builder: (context, signerSnapshot) {
+ if (!signerSnapshot.hasData) {
+ return ZupPrimaryButton(
+ width: double.maxFinite,
+ title: S.of(context).connectWallet,
+ icon: Assets.icons.walletBifold.svg(),
+ fixedIcon: true,
+ alignCenter: true,
+ hoverElevation: 0,
+ backgroundColor: ZupColors.brand7,
+ foregroundColor: ZupColors.brand,
+ onPressed: () => ConnectModal().show(context),
+ );
+ }
+
+ return FutureBuilder(
+ future: v2PoolDepositButtonState(),
+ builder: (context, stateSnapshot) {
+ return ZupPrimaryButton(
+ alignCenter: true,
+ title: stateSnapshot.data?.title ?? "Loading...",
+ icon: stateSnapshot.data?.icon,
+ isLoading: stateSnapshot.connectionState == ConnectionState.waiting,
+ fixedIcon: true,
+ onPressed: stateSnapshot.data?.onPressed,
+ width: double.maxFinite,
+ );
+ },
+ );
+ },
+ ),
+ ],
+ );
+ });
+ }
}
diff --git a/lib/app/create/deposit/widgets/preview_deposit_modal/preview_deposit_modal.dart b/lib/app/create/deposit/widgets/preview_deposit_modal/preview_deposit_modal.dart
index 475849e..db4d3db 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
@@ -31,8 +31,8 @@ class PreviewDepositModal extends StatefulWidget with DeviceInfoMixin {
super.key,
required this.currentYield,
required this.isReversed,
- required this.minPrice,
- required this.maxPrice,
+ this.minPrice,
+ this.maxPrice,
required this.token0DepositAmount,
required this.token1DepositAmount,
required this.deadline,
@@ -43,22 +43,20 @@ class PreviewDepositModal extends StatefulWidget with DeviceInfoMixin {
final YieldDto currentYield;
final YieldTimeFrame yieldTimeFrame;
final bool isReversed;
- final ({double price, bool isInfinity}) minPrice;
- final ({double price, bool isInfinity}) maxPrice;
+ final ({double price, bool isInfinity})? minPrice;
+ final ({double price, bool isInfinity})? maxPrice;
final double token0DepositAmount;
final double token1DepositAmount;
final Duration deadline;
final Slippage maxSlippage;
- final double paddingSize = 20;
-
- show(BuildContext context, {required BigInt currentPoolTick}) {
+ show(BuildContext context, {BigInt? currentPoolTick}) {
return ZupModal.show(
context,
showAsBottomSheet: isMobileSize(context),
title: S.of(context).previewDepositModalTitle,
- size: const Size(450, 650),
- padding: EdgeInsets.only(left: paddingSize).copyWith(top: 5),
+ size: Size(450, currentYield.poolType.isV2 ? 505 : 650),
+ padding: const EdgeInsets.only(left: 20).copyWith(top: 5),
content: BlocProvider(
create: (context) => PreviewDepositModalCubit(
zupAnalytics: inject(),
@@ -118,17 +116,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;
@@ -136,20 +134,20 @@ class _PreviewDepositModalState extends State with V3PoolCo
double get minPrice {
BigInt tick() {
- if (widget.isReversed != isReversedLocal && widget.maxPrice.isInfinity) return V3V4PoolConstants.minTick;
+ if (widget.isReversed != isReversedLocal && widget.maxPrice!.isInfinity) return V3V4PoolConstants.minTick;
return priceToTick(
- price: (widget.isReversed == !isReversedLocal) ? widget.maxPrice.price : widget.minPrice.price,
- poolToken0Decimals: widget.currentYield.token0.decimals,
- poolToken1Decimals: widget.currentYield.token1.decimals,
+ price: (widget.isReversed == !isReversedLocal) ? widget.maxPrice!.price : widget.minPrice!.price,
+ 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;
@@ -157,20 +155,20 @@ class _PreviewDepositModalState extends State with V3PoolCo
double get maxPrice {
BigInt tick() {
- if (widget.isReversed != isReversedLocal && widget.minPrice.isInfinity) return V3V4PoolConstants.minTick;
+ if (widget.isReversed != isReversedLocal && widget.minPrice!.isInfinity) return V3V4PoolConstants.minTick;
return priceToTick(
- price: (widget.isReversed == !isReversedLocal) ? widget.minPrice.price : widget.maxPrice.price,
- poolToken0Decimals: widget.currentYield.token0.decimals,
- poolToken1Decimals: widget.currentYield.token1.decimals,
+ price: (widget.isReversed == !isReversedLocal) ? widget.minPrice!.price : widget.maxPrice!.price,
+ 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;
@@ -189,8 +187,8 @@ class _PreviewDepositModalState extends State with V3PoolCo
}
({bool minPrice, bool maxPrice, bool any}) get isOutOfRange {
- final isMinPriceOutOfRange = !widget.minPrice.isInfinity && (minPrice) > currentPrice;
- final isMaxPriceOutOfRanfe = !widget.maxPrice.isInfinity && (maxPrice) < currentPrice;
+ final isMinPriceOutOfRange = !widget.minPrice!.isInfinity && (minPrice) > currentPrice;
+ final isMaxPriceOutOfRanfe = !widget.maxPrice!.isInfinity && (maxPrice) < currentPrice;
return (
minPrice: isMinPriceOutOfRange,
@@ -260,10 +258,10 @@ class _PreviewDepositModalState extends State with V3PoolCo
slippage: widget.maxSlippage,
token0Amount: token0DepositAmount,
token1Amount: token1DepositAmount,
- minPrice: widget.minPrice.price,
- maxPrice: widget.maxPrice.price,
- isMinPriceInfinity: widget.minPrice.isInfinity,
- isMaxPriceInfinity: widget.maxPrice.isInfinity,
+ minPrice: widget.minPrice?.price,
+ maxPrice: widget.maxPrice?.price,
+ isMinPriceInfinity: widget.minPrice?.isInfinity,
+ isMaxPriceInfinity: widget.maxPrice?.isInfinity,
isReversed: widget.isReversed,
),
);
@@ -419,56 +417,59 @@ class _PreviewDepositModalState extends State with V3PoolCo
"${baseToken.symbol}/${quoteToken.symbol}",
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.w500),
),
- const SizedBox(width: 10),
- StreamBuilder(
- stream: cubit.poolTickStream,
- builder: (context, tickSnapshot) {
- return ZupTag(
- title: isOutOfRange.any
- ? S.of(context).previewDepositModalOutOfRange
- : S.of(context).previewDepositModalInRange,
- color: isOutOfRange.any ? ZupColors.orange : ZupColors.green,
- );
- }),
- const Spacer(),
+ if (!widget.currentYield.poolType.isV2) ...[
+ const SizedBox(width: 10),
+ StreamBuilder(
+ stream: cubit.poolTickStream,
+ builder: (context, tickSnapshot) {
+ return ZupTag(
+ title: isOutOfRange.any
+ ? S.of(context).previewDepositModalOutOfRange
+ : S.of(context).previewDepositModalInRange,
+ color: isOutOfRange.any ? ZupColors.orange : ZupColors.green,
+ );
+ }),
+ const Spacer(),
+ ]
],
),
const SizedBox(height: 16),
- CupertinoSlidingSegmentedControl(
- groupValue: isReversedLocal,
- children: {
- false: MouseRegion(
- key: const Key("unreverse-tokens"),
- cursor: SystemMouseCursors.click,
- child: IgnorePointer(
- child: SizedBox(
- height: 15,
- child: Text(
- "${widget.currentYield.token0.symbol} / ${widget.currentYield.token1.symbol}",
- style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w600),
+ if (!widget.currentYield.poolType.isV2)
+ CupertinoSlidingSegmentedControl(
+ groupValue: isReversedLocal,
+ children: {
+ false: MouseRegion(
+ key: const Key("unreverse-tokens"),
+ cursor: SystemMouseCursors.click,
+ child: IgnorePointer(
+ child: SizedBox(
+ height: 15,
+ child: Text(
+ "${widget.currentYield.token0.symbol} / ${widget.currentYield.token1.symbol}",
+ style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w600),
+ ),
),
),
),
- ),
- true: MouseRegion(
- key: const Key("reverse-tokens"),
- cursor: SystemMouseCursors.click,
- child: IgnorePointer(
- child: SizedBox(
- height: 16,
- child: Text(
- "${widget.currentYield.token1.symbol} / ${widget.currentYield.token0.symbol}",
- style: const TextStyle(
- fontSize: 12,
- fontWeight: FontWeight.w600,
+ true: MouseRegion(
+ key: const Key("reverse-tokens"),
+ cursor: SystemMouseCursors.click,
+ child: IgnorePointer(
+ child: SizedBox(
+ height: 16,
+ child: Text(
+ "${widget.currentYield.token1.symbol} / ${widget.currentYield.token0.symbol}",
+ style: const TextStyle(
+ fontSize: 12,
+ fontWeight: FontWeight.w600,
+ ),
),
),
),
),
- ),
- },
- onValueChanged: (value) => setState(() => isReversedLocal = value ?? false),
- ),
+ },
+ onValueChanged: (value) => setState(() => isReversedLocal = value ?? false),
+ ),
const SizedBox(height: 10),
const ZupDivider(),
const SizedBox(height: 10),
@@ -566,14 +567,16 @@ class _PreviewDepositModalState extends State with V3PoolCo
const SizedBox(height: 10),
const ZupDivider(),
const SizedBox(height: 10),
- Row(
- children: [
- Expanded(child: rangeInfoCard(isMinPrice: true)),
- const SizedBox(width: 10),
- Expanded(child: rangeInfoCard(isMinPrice: false)),
- ],
- ),
- const Spacer(),
+ if (!widget.currentYield.poolType.isV2) ...[
+ Row(
+ children: [
+ Expanded(child: rangeInfoCard(isMinPrice: true)),
+ const SizedBox(width: 10),
+ Expanded(child: rangeInfoCard(isMinPrice: false)),
+ ],
+ ),
+ const Spacer(),
+ ],
const SizedBox(height: 10),
ZupPrimaryButton(
key: const Key("deposit-button"),
@@ -617,7 +620,7 @@ class _PreviewDepositModalState extends State with V3PoolCo
Text(
() {
if (isMinPrice) {
- return widget.minPrice.isInfinity
+ return widget.minPrice!.isInfinity
? "0"
: minPrice.maybeFormatCompactCurrency(
isUSD: false,
@@ -627,7 +630,7 @@ class _PreviewDepositModalState extends State with V3PoolCo
);
}
- return widget.maxPrice.isInfinity
+ return widget.maxPrice!.isInfinity
? "∞"
: maxPrice.maybeFormatCompactCurrency(
isUSD: false,
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..47874ca 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
@@ -26,7 +26,7 @@ part "preview_deposit_modal_state.dart";
class PreviewDepositModalCubit extends Cubit with V3PoolConversorsMixin, DeviceInfoMixin {
PreviewDepositModalCubit({
- required BigInt initialPoolTick,
+ BigInt? initialPoolTick,
required PoolService poolService,
required YieldDto currentYield,
required Erc20 erc20,
@@ -39,7 +39,7 @@ class PreviewDepositModalCubit extends Cubit with V3Po
_poolRepository = poolService,
_erc20 = erc20,
_wallet = wallet,
- _latestPoolTick = initialPoolTick,
+ _latestPoolTick = initialPoolTick ?? BigInt.zero,
_navigatorKey = navigatorKey,
_zupAnalytics = zupAnalytics,
_permit2 = permit2,
@@ -156,11 +156,11 @@ class PreviewDepositModalCubit extends Cubit with V3Po
Future deposit({
required BigInt token0Amount,
required BigInt token1Amount,
- required double minPrice,
- required double maxPrice,
- required bool isMinPriceInfinity,
- required bool isMaxPriceInfinity,
- required bool isReversed,
+ double? minPrice,
+ double? maxPrice,
+ bool? isMinPriceInfinity,
+ bool? isMaxPriceInfinity,
+ bool? isReversed,
required Slippage slippage,
required Duration deadline,
}) async {
@@ -170,13 +170,13 @@ class PreviewDepositModalCubit extends Cubit with V3Po
BigInt tickLower() {
BigInt convertPriceToTickLower() {
- if (isMinPriceInfinity && !isReversed) return V3V4PoolConstants.minTick;
- if (isReversed && isMaxPriceInfinity) return V3V4PoolConstants.minTick;
+ if (isMinPriceInfinity! && !(isReversed)!) return V3V4PoolConstants.minTick;
+ if (isReversed! && isMaxPriceInfinity!) return V3V4PoolConstants.minTick;
return priceToTick(
- price: isReversed ? maxPrice : minPrice,
- poolToken0Decimals: _yield.token0.decimals,
- poolToken1Decimals: _yield.token1.decimals,
+ price: isReversed ? maxPrice! : minPrice!,
+ poolToken0Decimals: _yield.token0NetworkDecimals,
+ poolToken1Decimals: _yield.token1NetworkDecimals,
isReversed: isReversed,
);
}
@@ -189,13 +189,13 @@ class PreviewDepositModalCubit extends Cubit with V3Po
BigInt tickUpper() {
BigInt convertPriceToTickUpper() {
- if (isMaxPriceInfinity && !isReversed) return V3V4PoolConstants.maxTick;
- if (isReversed && isMinPriceInfinity) return V3V4PoolConstants.maxTick;
+ if (isMaxPriceInfinity! && !isReversed!) return V3V4PoolConstants.maxTick;
+ if (isReversed! && isMinPriceInfinity!) return V3V4PoolConstants.maxTick;
return priceToTick(
- price: isReversed ? minPrice : maxPrice,
- poolToken0Decimals: _yield.token0.decimals,
- poolToken1Decimals: _yield.token1.decimals,
+ price: isReversed ? minPrice! : maxPrice!,
+ poolToken0Decimals: _yield.token0NetworkDecimals,
+ poolToken1Decimals: _yield.token1NetworkDecimals,
isReversed: isReversed,
);
}
@@ -213,6 +213,18 @@ class PreviewDepositModalCubit extends Cubit with V3Po
final recipient = await _wallet.signer!.address;
final TransactionResponse tx = await () async {
+ if (_yield.poolType.isV2) {
+ return await _poolRepository.sendV2PoolDepositTransaction(
+ _yield,
+ _wallet.signer!,
+ amount0: amount0Desired,
+ amount1: amount1Desired,
+ amount0Min: amount0Min,
+ amount1Min: amount1Min,
+ deadline: deadline,
+ );
+ }
+
if (_yield.poolType.isV3) {
return await _poolRepository.sendV3PoolDepositTransaction(
_yield,
@@ -253,8 +265,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/app/create/widgets/create_page_settings_dropdown.dart b/lib/app/create/widgets/create_page_settings_dropdown.dart
index 8692fc7..cda1056 100644
--- a/lib/app/create/widgets/create_page_settings_dropdown.dart
+++ b/lib/app/create/widgets/create_page_settings_dropdown.dart
@@ -180,36 +180,59 @@ class _CreatePageSettingsDropdownState extends State
],
),
const SizedBox(height: 5),
- Row(
- children: [
- const Text("V4", style: TextStyle(fontWeight: FontWeight.w500, fontSize: 15)),
- const SizedBox(width: 5),
- ZupSwitch(
- key: const Key("pool-types-allowed-v4-switch"),
- value: cache.getPoolSearchSettings().allowV4Search,
- onChanged: (value) async {
- await cache.savePoolSearchSettings(
- settings: cache.getPoolSearchSettings().copyWith(allowV4Search: value),
- );
+ SizedBox(
+ width: 200,
+ child: Wrap(
+ alignment: WrapAlignment.start,
+ runAlignment: WrapAlignment.start,
+ crossAxisAlignment: WrapCrossAlignment.center,
+ children: [
+ const Text("V4", style: TextStyle(fontWeight: FontWeight.w500, fontSize: 15)),
+ const SizedBox(width: 5),
+ ZupSwitch(
+ key: const Key("pool-types-allowed-v4-switch"),
+ value: cache.getPoolSearchSettings().allowV4Search,
+ onChanged: (value) async {
+ await cache.savePoolSearchSettings(
+ settings: cache.getPoolSearchSettings().copyWith(allowV4Search: value),
+ );
- setState(() {});
- },
- ),
- const SizedBox(width: 12),
- const Text("V3", style: TextStyle(fontWeight: FontWeight.w500)),
- const SizedBox(width: 5),
- ZupSwitch(
- key: const Key("pool-types-allowed-v3-switch"),
- value: cache.getPoolSearchSettings().allowV3Search,
- onChanged: (value) async {
- await cache.savePoolSearchSettings(
- settings: cache.getPoolSearchSettings().copyWith(allowV3Search: value),
- );
+ setState(() {});
+ },
+ ),
+ const SizedBox(width: 12),
+ const Text("V3", style: TextStyle(fontWeight: FontWeight.w500)),
+ const SizedBox(width: 5),
+ ZupSwitch(
+ key: const Key("pool-types-allowed-v3-switch"),
+ value: cache.getPoolSearchSettings().allowV3Search,
+ onChanged: (value) async {
+ await cache.savePoolSearchSettings(
+ settings: cache.getPoolSearchSettings().copyWith(allowV3Search: value),
+ );
- setState(() {});
- },
- ),
- ],
+ setState(() {});
+ },
+ ),
+ Row(
+ children: [
+ const Text("V2", style: TextStyle(fontWeight: FontWeight.w500)),
+ const SizedBox(width: 5),
+ ZupSwitch(
+ key: const Key("pool-types-allowed-v2-switch"),
+ value: cache.getPoolSearchSettings().allowV2Search,
+ onChanged: (value) async {
+ await cache.savePoolSearchSettings(
+ settings: cache.getPoolSearchSettings().copyWith(allowV2Search: value),
+ );
+
+ setState(() {});
+ },
+ ),
+ ],
+ )
+ ],
+ ),
),
],
),
diff --git a/lib/core/dtos/pool_search_settings_dto.dart b/lib/core/dtos/pool_search_settings_dto.dart
index 9565b98..bd63e09 100644
--- a/lib/core/dtos/pool_search_settings_dto.dart
+++ b/lib/core/dtos/pool_search_settings_dto.dart
@@ -12,6 +12,7 @@ class PoolSearchSettingsDto with _$PoolSearchSettingsDto {
@Default(PoolSearchSettingsDto.defaultMinLiquidityUSD) @JsonKey(name: 'min_liquidity_usd') num minLiquidityUSD,
@Default(true) bool allowV4Search,
@Default(true) bool allowV3Search,
+ @Default(true) bool allowV2Search,
}) = _PoolSearchSettingsDto;
const PoolSearchSettingsDto._();
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..e405b20 100644
--- a/lib/core/dtos/yield_dto.dart
+++ b/lib/core/dtos/yield_dto.dart
@@ -41,7 +41,6 @@ class YieldDto with _$YieldDto {
required TokenDto token1,
required String poolAddress,
required String positionManagerAddress,
- required int tickSpacing,
required ProtocolDto protocol,
required int feeTier,
required num yield24h,
@@ -49,6 +48,7 @@ class YieldDto with _$YieldDto {
required num yield90d,
required int chainId,
required PoolType poolType,
+ @Default(0) int tickSpacing,
@Default(0) num totalValueLockedUSD,
@Default(EthereumConstants.zeroAddress) @JsonKey(name: "hooksAddress") String v4Hooks,
@JsonKey(name: "poolManagerAddress") String? v4PoolManager,
@@ -61,6 +61,12 @@ 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]!;
+
+ String get token0NetworkAddress => token0.addresses[network.chainId]!;
+ String get token1NetworkAddress => token1.addresses[network.chainId]!;
+
factory YieldDto.fromJson(Map json) => _$YieldDtoFromJson(json);
factory YieldDto.fixture() => YieldDto(
@@ -73,7 +79,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 +90,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/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/lib/core/enums/pool_type.dart b/lib/core/enums/pool_type.dart
index b8945e3..4133305 100644
--- a/lib/core/enums/pool_type.dart
+++ b/lib/core/enums/pool_type.dart
@@ -1,6 +1,8 @@
import 'package:freezed_annotation/freezed_annotation.dart';
enum PoolType {
+ @JsonValue("V2")
+ v2,
@JsonValue("V3")
v3,
@JsonValue("V4")
@@ -8,9 +10,11 @@ enum PoolType {
bool get isV3 => this == PoolType.v3;
bool get isV4 => this == PoolType.v4;
+ bool get isV2 => this == PoolType.v2;
String get label => switch (this) {
PoolType.v3 => "V3",
PoolType.v4 => "V4",
+ PoolType.v2 => "V2",
};
}
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..a2ffaf4 100644
--- a/lib/core/injections.dart
+++ b/lib/core/injections.dart
@@ -7,7 +7,10 @@ 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_v2_pool.abi.g.dart';
+import 'package:zup_app/abis/uniswap_v2_router_02.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';
@@ -115,14 +118,24 @@ Future setupInjections() async {
inject.registerLazySingleton(() => EthereumAbiCoder());
+ inject.registerLazySingleton(
+ () => PancakeSwapInfinityClPoolManager(),
+ );
+
+ inject.registerLazySingleton(() => UniswapV2Pool());
+
+ inject.registerLazySingleton(() => UniswapV2Router02());
+
inject.registerLazySingleton(
() => PoolService(
- inject(),
- inject(),
- inject(),
- inject(),
- inject(),
- ),
+ inject(),
+ inject(),
+ inject(),
+ inject(),
+ inject(),
+ inject(),
+ inject(),
+ inject()),
);
inject.registerLazySingleton(
diff --git a/lib/core/pool_service.dart b/lib/core/pool_service.dart
index 8a85d1a..732785d 100644
--- a/lib/core/pool_service.dart
+++ b/lib/core/pool_service.dart
@@ -1,6 +1,9 @@
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_v2_pool.abi.g.dart';
+import 'package:zup_app/abis/uniswap_v2_router_02.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';
@@ -12,19 +15,45 @@ import 'package:zup_app/core/v4_pool_constants.dart';
class PoolService with V4PoolLiquidityCalculationsMixin {
final UniswapV4StateView _uniswapV4StateView;
final UniswapV3Pool _uniswapV3Pool;
+ final UniswapV2Pool _uniswapV2Pool;
final UniswapV3PositionManager _uniswapV3PositionManager;
final UniswapV4PositionManager _uniswapV4PositionManager;
+ final UniswapV2Router02 _uniswapV2Router02;
final EthereumAbiCoder _ethereumAbiCoder;
+ final PancakeSwapInfinityClPoolManager _pancakeSwapInfinityClPoolManager;
PoolService(
this._uniswapV4StateView,
this._uniswapV3Pool,
+ this._uniswapV2Pool,
this._uniswapV3PositionManager,
this._uniswapV4PositionManager,
+ this._uniswapV2Router02,
this._ethereumAbiCoder,
+ this._pancakeSwapInfinityClPoolManager,
);
+ Future<({BigInt reserve0, BigInt reserve1})> getV2PoolReserves(YieldDto forYield) async {
+ final poolContract = _uniswapV2Pool.fromRpcProvider(
+ contractAddress: forYield.poolAddress,
+ rpcUrl: forYield.network.rpcUrl,
+ );
+
+ final reserves = await poolContract.getReserves();
+
+ return (reserve0: reserves.reserve0, reserve1: reserves.reserve1);
+ }
+
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!,
@@ -34,12 +63,55 @@ class PoolService with V4PoolLiquidityCalculationsMixin {
return (await stateView.getSlot0(poolId: forYield.poolAddress)).tick;
}
- final uniswapV3Pool = _uniswapV3Pool.fromRpcProvider(
- contractAddress: forYield.poolAddress,
- rpcUrl: forYield.network.rpcUrl,
+ if (forYield.poolType.isV3) {
+ final uniswapV3Pool = _uniswapV3Pool.fromRpcProvider(
+ contractAddress: forYield.poolAddress,
+ rpcUrl: forYield.network.rpcUrl,
+ );
+
+ return (await uniswapV3Pool.slot0()).tick;
+ }
+
+ throw Exception('Cannot get pool tick for ${forYield.poolType} pool type');
+ }
+
+ Future sendV2PoolDepositTransaction(
+ YieldDto depositOnYield,
+ Signer signer, {
+ required BigInt amount0,
+ required BigInt amount1,
+ required BigInt amount0Min,
+ required BigInt amount1Min,
+ required Duration deadline,
+ }) async {
+ final v2RouterContract = _uniswapV2Router02.fromSigner(
+ contractAddress: depositOnYield.positionManagerAddress,
+ signer: signer,
);
- return (await uniswapV3Pool.slot0()).tick;
+ if (depositOnYield.isToken0Native || depositOnYield.isToken1Native) {
+ return v2RouterContract.addLiquidityETH(
+ token:
+ depositOnYield.isToken0Native ? depositOnYield.token1NetworkAddress : depositOnYield.token0NetworkAddress,
+ amountTokenDesired: depositOnYield.isToken0Native ? amount1 : amount0,
+ amountTokenMin: depositOnYield.isToken0Native ? amount1Min : amount0Min,
+ amountETHMin: depositOnYield.isToken0Native ? amount0Min : amount1Min,
+ to: await signer.address,
+ deadline: BigInt.from(clock.now().add(deadline).millisecondsSinceEpoch),
+ ethValue: depositOnYield.isToken0Native ? amount0 : amount1,
+ );
+ }
+
+ return await v2RouterContract.addLiquidity(
+ tokenA: depositOnYield.token0NetworkAddress,
+ tokenB: depositOnYield.token1NetworkAddress,
+ amountADesired: amount0,
+ amountBDesired: amount1,
+ amountAMin: amount0Min,
+ amountBMin: amount1Min,
+ to: await signer.address,
+ deadline: BigInt.from(clock.now().add(deadline).millisecondsSinceEpoch),
+ );
}
Future sendV3PoolDepositTransaction(
diff --git a/lib/core/repositories/yield_repository.dart b/lib/core/repositories/yield_repository.dart
index cde644d..cdc18d6 100644
--- a/lib/core/repositories/yield_repository.dart
+++ b/lib/core/repositories/yield_repository.dart
@@ -23,6 +23,7 @@ class YieldRepository {
"allowedPoolTypes": [
if (searchSettings.allowV3Search) "V3",
if (searchSettings.allowV4Search) "V4",
+ if (searchSettings.allowV2Search) "V2",
],
}
});
diff --git a/lib/l10n/en.arb b/lib/l10n/en.arb
index bdea6df..ecfcfbc 100644
--- a/lib/l10n/en.arb
+++ b/lib/l10n/en.arb
@@ -210,6 +210,17 @@
}
}
},
+ "depositPageInvalidTokenAmountV2Pool": "Enter a valid amount for {token0Symbol} or {token1Symbol}",
+ "@depositPageInvalidTokenAmountV2Pool": {
+ "placeholders": {
+ "token0Symbol": {
+ "type": "String"
+ },
+ "token1Symbol": {
+ "type": "String"
+ }
+ }
+ },
"depositPageDeposit": "Deposit",
"connectYourWallet": "Connect your wallet",
"tokenSelectorModalTitle": "Select a token",
diff --git a/lib/l10n/gen/app_localizations.dart b/lib/l10n/gen/app_localizations.dart
index e0f6eac..e6d2d5c 100644
--- a/lib/l10n/gen/app_localizations.dart
+++ b/lib/l10n/gen/app_localizations.dart
@@ -634,6 +634,13 @@ abstract class S {
/// **'Please enter an amount for {tokenSymbol}'**
String depositPagePleaseEnterAmountForToken({required String tokenSymbol});
+ /// No description provided for @depositPageInvalidTokenAmountV2Pool.
+ ///
+ /// In en, this message translates to:
+ /// **'Enter a valid amount for {token0Symbol} or {token1Symbol}'**
+ String depositPageInvalidTokenAmountV2Pool(
+ {required String token0Symbol, required String token1Symbol});
+
/// No description provided for @depositPageDeposit.
///
/// In en, this message translates to:
diff --git a/lib/l10n/gen/app_localizations_en.dart b/lib/l10n/gen/app_localizations_en.dart
index b4ec1ad..ea3b456 100644
--- a/lib/l10n/gen/app_localizations_en.dart
+++ b/lib/l10n/gen/app_localizations_en.dart
@@ -337,6 +337,12 @@ class SEn extends S {
return 'Please enter an amount for $tokenSymbol';
}
+ @override
+ String depositPageInvalidTokenAmountV2Pool(
+ {required String token0Symbol, required String token1Symbol}) {
+ return 'Enter a valid amount for $token0Symbol or $token1Symbol';
+ }
+
@override
String get depositPageDeposit => 'Deposit';
diff --git a/pubspec.lock b/pubspec.lock
index e842ad6..29b70c3 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -1293,7 +1293,7 @@ packages:
description:
path: "."
ref: main
- resolved-ref: "42dfe9f12673e21d2bc15623a6f58a1094969f2c"
+ resolved-ref: "96c38241bbd55f27820c0fbe0e62ed53f2febed7"
url: "https://github.com/Zup-Protocol/web3kit.git"
source: git
version: "0.0.1"
diff --git a/pubspec.yaml b/pubspec.yaml
index 132d90b..3452363 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -53,13 +53,13 @@ dependencies:
firebase_analytics: ^11.4.5
envied: ^1.1.1
-dependency_overrides:
- # zup_core:
- # path: ../zup-core
- # web3kit:
- # path: ../web3kit
- # zup_ui_kit:
- # path: ../zup-ui-kit
+# dependency_overrides:
+# zup_core:
+# path: ../zup-core
+# web3kit:
+# path: ../web3kit
+# zup_ui_kit:
+# path: ../zup-ui-kit
dev_dependencies:
flutter_test:
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/app/create/deposit/deposit_cubit_test.dart b/test/app/create/deposit/deposit_cubit_test.dart
index d3d5284..f3e513b 100644
--- a/test/app/create/deposit/deposit_cubit_test.dart
+++ b/test/app/create/deposit/deposit_cubit_test.dart
@@ -12,6 +12,7 @@ import 'package:zup_app/core/dtos/pool_search_settings_dto.dart';
import 'package:zup_app/core/dtos/yield_dto.dart';
import 'package:zup_app/core/dtos/yields_dto.dart';
import 'package:zup_app/core/enums/networks.dart';
+import 'package:zup_app/core/enums/pool_type.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';
@@ -632,4 +633,338 @@ void main() {
expect(sut.selectedYieldTimeframe, selectedYieldTimeFrame);
});
+
+ test(
+ """When calling 'selectYield' and the yield is v2 pool,
+ it should get the pool reserves from the pool service
+ and update the variable with the formatted values to double""",
+ () async {
+ final expectedReserves = (reserve0: BigInt.from(21578), reserve1: BigInt.from(45678));
+ when(() => poolService.getV2PoolReserves(any())).thenAnswer((_) async => expectedReserves);
+
+ final selectedYield = YieldDto.fixture().copyWith(poolType: PoolType.v2);
+ await sut.selectYield(selectedYield, YieldTimeFrame.day);
+
+ expect(
+ sut.latestV2PoolReserves!.reserve0,
+ expectedReserves.reserve0.parseTokenAmount(decimals: selectedYield.token0NetworkDecimals),
+ );
+
+ expect(
+ sut.latestV2PoolReserves!.reserve1,
+ expectedReserves.reserve1.parseTokenAmount(decimals: selectedYield.token1NetworkDecimals),
+ );
+
+ verify(() => poolService.getV2PoolReserves(selectedYield)).called(1);
+ },
+ );
+
+ test(
+ """When calling 'selectYield' and the yield is v3 pool,
+ it should get the pool tick from the pool service
+ and update the variable with the received values""",
+ () async {
+ final expectedTick = BigInt.from(215781527812761);
+ when(() => poolService.getPoolTick(any())).thenAnswer((_) async => expectedTick);
+
+ final selectedYield = YieldDto.fixture().copyWith(poolType: PoolType.v3);
+ await sut.selectYield(selectedYield, YieldTimeFrame.day);
+
+ expect(
+ sut.latestPoolTick!,
+ expectedTick,
+ );
+
+ verify(() => poolService.getPoolTick(selectedYield)).called(1);
+ },
+ );
+
+ test(
+ """When calling 'selectYield' and the yield is v4 pool,
+ it should get the pool tick from the pool service
+ and update the variable with the received values""",
+ () async {
+ final expectedTick = BigInt.from(1716161616161);
+ when(() => poolService.getPoolTick(any())).thenAnswer((_) async => expectedTick);
+
+ final selectedYield = YieldDto.fixture().copyWith(poolType: PoolType.v4);
+ await sut.selectYield(selectedYield, YieldTimeFrame.month);
+
+ expect(
+ sut.latestPoolTick!,
+ expectedTick,
+ );
+
+ verify(() => poolService.getPoolTick(selectedYield)).called(1);
+ },
+ );
+
+ test(
+ """When calling 'getSelectedPoolTickOrReserves' and the yield is v2 pool,
+ it should get the pool reserves from the pool service
+ and update the variable with the formatted values to double""",
+ () async {
+ final expectedReserves = (reserve0: BigInt.from(9781278), reserve1: BigInt.from(11111655666));
+ when(() => poolService.getV2PoolReserves(any())).thenAnswer((_) async => expectedReserves);
+
+ final selectedYield = YieldDto.fixture().copyWith(poolType: PoolType.v2);
+ await sut.selectYield(selectedYield, YieldTimeFrame.day);
+
+ await sut.getSelectedPoolTickOrReserves();
+
+ expect(
+ sut.latestV2PoolReserves!.reserve0,
+ expectedReserves.reserve0.parseTokenAmount(decimals: selectedYield.token0NetworkDecimals),
+ );
+
+ expect(
+ sut.latestV2PoolReserves!.reserve1,
+ expectedReserves.reserve1.parseTokenAmount(decimals: selectedYield.token1NetworkDecimals),
+ );
+
+ verify(() => poolService.getV2PoolReserves(selectedYield))
+ .called(2); // 2 because it's lso called when the yield is selected
+ },
+ );
+
+ test(
+ """When calling 'getSelectedPoolTickOrReserves' and the yield is v4 pool,
+ it should get the pool tick from the pool service
+ and update the variable with the received values""",
+ () async {
+ final expectedTick = BigInt.from(1716161616161);
+ when(() => poolService.getPoolTick(any())).thenAnswer((_) async => BigInt.zero);
+
+ final selectedYield = YieldDto.fixture().copyWith(poolType: PoolType.v4);
+ await sut.selectYield(selectedYield, YieldTimeFrame.month);
+
+ when(() => poolService.getPoolTick(any())).thenAnswer((_) async => expectedTick);
+
+ await sut.getSelectedPoolTickOrReserves();
+
+ expect(sut.latestPoolTick!, expectedTick);
+
+ verify(() => poolService.getPoolTick(selectedYield))
+ .called(2); // two because it's lso called when the yield is selected
+ },
+ );
+
+ test(
+ """When calling 'getSelectedPoolTickOrReserves' and the yield is v3 pool,
+ it should get the pool tick from the pool service
+ and update the variable with the received values""",
+ () async {
+ final expectedTick = BigInt.from(98987162781);
+ when(() => poolService.getPoolTick(any())).thenAnswer((_) async => BigInt.zero);
+
+ final selectedYield = YieldDto.fixture().copyWith(poolType: PoolType.v3);
+ await sut.selectYield(selectedYield, YieldTimeFrame.month);
+
+ when(() => poolService.getPoolTick(any())).thenAnswer((_) async => expectedTick);
+ await sut.getSelectedPoolTickOrReserves();
+
+ expect(sut.latestPoolTick!, expectedTick);
+
+ verify(() => poolService.getPoolTick(selectedYield))
+ .called(2); // two because it's lso called when the yield is selected
+ },
+ );
+
+ test(
+ """When calling 'getSelectedPoolTickOrReserves' and there is no yield selected,
+ it should not call to get the pool tick or reserves""",
+ () async {
+ await sut.getSelectedPoolTickOrReserves();
+
+ verifyNever(() => poolService.getPoolTick(any()));
+ verifyNever(() => poolService.getV2PoolReserves(any()));
+ },
+ );
+
+ test(
+ """When calling 'getSelectedPoolV2Reserves' and there is no yield selected,
+ it should not call to get the pool reserves""",
+ () async {
+ await sut.getSelectedPoolV2Reserves();
+
+ verifyNever(() => poolService.getV2PoolReserves(any()));
+ },
+ );
+
+ test(
+ """When calling 'getSelectedPoolV2Reserves' it should emit a
+ new pool reserve in the stream with the formatted values""",
+ () async {
+ ({double reserve0, double reserve1})? receivedPoolReserves;
+ final expectedReserves = (reserve0: BigInt.from(9781278), reserve1: BigInt.from(11111655666));
+
+ when(() => poolService.getV2PoolReserves(any())).thenAnswer((_) async => expectedReserves);
+
+ final selectedYield = YieldDto.fixture().copyWith(poolType: PoolType.v2);
+ await sut.selectYield(selectedYield, YieldTimeFrame.day);
+
+ sut.v2PoolReservesStream.listen((poolReserves) {
+ receivedPoolReserves = poolReserves;
+ });
+
+ await sut.getSelectedPoolV2Reserves();
+
+ await Future.delayed(Duration.zero);
+
+ expect(
+ receivedPoolReserves!.reserve0,
+ expectedReserves.reserve0.parseTokenAmount(decimals: selectedYield.token0NetworkDecimals),
+ );
+
+ expect(
+ receivedPoolReserves!.reserve1,
+ expectedReserves.reserve1.parseTokenAmount(decimals: selectedYield.token1NetworkDecimals),
+ );
+ },
+ );
+
+ test(
+ """When calling 'getSelectedPoolV2Reserves' it should
+ update the latest pool reserve variable with the
+ received values formatted""",
+ () async {
+ final expectedReserves = (reserve0: BigInt.from(12516), reserve1: BigInt.from(999991929127));
+
+ when(() => poolService.getV2PoolReserves(any())).thenAnswer((_) async => expectedReserves);
+
+ final selectedYield = YieldDto.fixture().copyWith(poolType: PoolType.v2);
+ await sut.selectYield(selectedYield, YieldTimeFrame.day);
+
+ await sut.getSelectedPoolV2Reserves();
+
+ await Future.delayed(Duration.zero);
+
+ expect(
+ sut.latestV2PoolReserves!.reserve0,
+ expectedReserves.reserve0.parseTokenAmount(decimals: selectedYield.token0NetworkDecimals),
+ );
+
+ expect(
+ sut.latestV2PoolReserves!.reserve1,
+ expectedReserves.reserve1.parseTokenAmount(decimals: selectedYield.token1NetworkDecimals),
+ );
+ },
+ );
+
+ test(
+ """When calling 'getSelectedPoolV2Reserves' and selecting
+ another yield while the call is being made, it should get
+ again the pool reserves for the new selected yield""",
+ () async {
+ final selectedYield1 = YieldDto.fixture().copyWith(poolAddress: "0x1", poolType: PoolType.v2);
+ final selectedYield2 = YieldDto.fixture().copyWith(poolAddress: "0x2", poolType: PoolType.v2);
+
+ final expectedReserves = (reserve0: BigInt.from(12516), reserve1: BigInt.from(999991929127));
+
+ when(() => poolService.getV2PoolReserves(selectedYield1)).thenAnswer(
+ (_) async => (reserve0: BigInt.zero, reserve1: BigInt.zero),
+ );
+
+ when(() => poolService.getV2PoolReserves(selectedYield2)).thenAnswer((_) async => expectedReserves);
+
+ await sut.selectYield(selectedYield1, YieldTimeFrame.day);
+
+ when(() => poolService.getV2PoolReserves(selectedYield1)).thenAnswer((_) async {
+ await sut.selectYield(selectedYield2, YieldTimeFrame.day);
+
+ return (reserve0: BigInt.zero, reserve1: BigInt.zero);
+ });
+
+ await sut.getSelectedPoolV2Reserves();
+
+ expect(
+ sut.latestV2PoolReserves!.reserve0,
+ expectedReserves.reserve0.parseTokenAmount(decimals: selectedYield2.token0NetworkDecimals),
+ );
+
+ expect(
+ sut.latestV2PoolReserves!.reserve1,
+ expectedReserves.reserve1.parseTokenAmount(decimals: selectedYield2.token1NetworkDecimals),
+ );
+
+ verify(() => poolService.getV2PoolReserves(selectedYield2))
+ .called(2); // two because of the selection call and the getSelectedPoolV2Reserves
+ },
+ );
+
+ test(
+ """When calling 'getSelectedPoolV2Reserves' and there is already a previous
+ value in the latest pool reserve, it should make it null before the call""",
+ () async {
+ bool testRun = false;
+
+ when(() => poolService.getV2PoolReserves(any())).thenAnswer(
+ (_) async => (reserve0: BigInt.zero, reserve1: BigInt.zero),
+ );
+
+ final selectedYield = YieldDto.fixture().copyWith(poolType: PoolType.v2);
+ await sut.selectYield(selectedYield, YieldTimeFrame.day);
+
+ when(() => poolService.getV2PoolReserves(any())).thenAnswer(
+ (_) async {
+ testRun = true;
+ expect(sut.latestV2PoolReserves, null);
+ return (reserve0: BigInt.zero, reserve1: BigInt.zero);
+ },
+ );
+
+ await sut.getSelectedPoolTickOrReserves();
+
+ expect(testRun, true); // checker that the call was made and the previous value was null
+ },
+ );
+
+ test(
+ """When calling 'getSelectedPoolV2Reserves' it should emit a null reserve in the stream before the call""",
+ () async {
+ when(() => poolService.getV2PoolReserves(any()))
+ .thenAnswer((_) async => (reserve0: BigInt.zero, reserve1: BigInt.zero));
+
+ final selectedYield = YieldDto.fixture().copyWith(poolType: PoolType.v2);
+ await sut.selectYield(selectedYield, YieldTimeFrame.day);
+
+ expectLater(sut.v2PoolReservesStream, emits(null));
+
+ await sut.getSelectedPoolTickOrReserves();
+ },
+ );
+
+ test(
+ """When every 1 minute is passed, it should refresh the pool
+ reserves in the latest pool reserve variable""",
+ () async {
+ const minutesPassed = 5;
+ when(() => poolService.getV2PoolReserves(any())).thenAnswer(
+ (_) async => (reserve0: BigInt.zero, reserve1: BigInt.zero),
+ );
+
+ final selectedYield = YieldDto.fixture().copyWith(poolType: PoolType.v2);
+
+ fakeAsync((time) async {
+ final expectedNewReserves = (reserve0: BigInt.from(12516), reserve1: BigInt.from(999991929127));
+ await sut.selectYield(selectedYield, YieldTimeFrame.day);
+
+ when(() => poolService.getV2PoolReserves(any())).thenAnswer((_) async => expectedNewReserves);
+
+ time.elapse(const Duration(minutes: minutesPassed));
+
+ expect(
+ sut.latestV2PoolReserves!.reserve0,
+ expectedNewReserves.reserve0.parseTokenAmount(decimals: selectedYield.token0NetworkDecimals),
+ );
+
+ expect(
+ sut.latestV2PoolReserves!.reserve1,
+ expectedNewReserves.reserve1.parseTokenAmount(decimals: selectedYield.token1NetworkDecimals),
+ );
+
+ verify(() => poolService.getV2PoolReserves(selectedYield)).called(minutesPassed + 1); // 1 of the initial call
+ });
+ },
+ );
}
diff --git a/test/app/create/deposit/deposit_page_test.dart b/test/app/create/deposit/deposit_page_test.dart
index 8ff97b5..40dbfda 100644
--- a/test/app/create/deposit/deposit_page_test.dart
+++ b/test/app/create/deposit/deposit_page_test.dart
@@ -19,10 +19,12 @@ import 'package:zup_app/app/create/deposit/widgets/preview_deposit_modal/preview
import 'package:zup_app/core/cache.dart';
import 'package:zup_app/core/dtos/deposit_settings_dto.dart';
import 'package:zup_app/core/dtos/pool_search_settings_dto.dart';
+import 'package:zup_app/core/dtos/protocol_dto.dart';
import 'package:zup_app/core/dtos/token_price_dto.dart';
import 'package:zup_app/core/dtos/yield_dto.dart';
import 'package:zup_app/core/dtos/yields_dto.dart';
import 'package:zup_app/core/enums/networks.dart';
+import 'package:zup_app/core/enums/pool_type.dart';
import 'package:zup_app/core/enums/zup_navigator_paths.dart';
import 'package:zup_app/core/injections.dart';
import 'package:zup_app/core/pool_service.dart';
@@ -2243,4 +2245,361 @@ void main() {
verifyNever(() => appCubit.updateAppNetwork(any()));
},
);
+
+ zGoldenTest(
+ "When selecting a V2 pool yield, it should automatically scroll to the deposit section",
+ goldenFileName: "deposit_page_select_v2_pool_scroll_to_deposit_section",
+ (tester) async {
+ await tester.runAsync(() async {
+ when(() => cubit.depositSettings).thenReturn(DepositSettingsDto());
+ when(() => cubit.v2PoolReservesStream).thenAnswer((_) => const Stream.empty());
+ when(() => cubit.latestV2PoolReserves).thenReturn((reserve0: 0, reserve1: 0));
+ final selectedYield = YieldDto.fixture().copyWith(
+ poolType: PoolType.v2,
+ protocol: ProtocolDto.fixture().copyWith(name: "V2 PPOOL"),
+ );
+
+ final yields = YieldsDto.fixture().copyWith(pools: [selectedYield]);
+
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.state).thenReturn(DepositState.success(yields));
+ when(() => cubit.selectYield(any(), any())).thenAnswer((_) async => () {});
+
+ await tester.pumpDeviceBuilder(await goldenBuilder(isMobile: true));
+ await tester.pumpAndSettle();
+
+ await tester.tap(find.byKey(const Key("yield-card-30d")));
+ await tester.pumpAndSettle();
+ });
+ },
+ );
+
+ zGoldenTest(
+ """When inputting an amount in the base token amount input and the yield is v2, it should calculate
+ the quote token amount correctly based on the pool reserves""",
+ goldenFileName: "deposit_page_calculate_quote_token_amount",
+ (tester) async {
+ await tester.runAsync(() async {
+ const ({double reserve0, double reserve1}) poolReserves = (reserve0: 1, reserve1: 2);
+
+ when(() => cubit.v2PoolReservesStream).thenAnswer((_) => const Stream.empty());
+ when(() => cubit.latestV2PoolReserves).thenReturn(poolReserves);
+
+ final selectedYield = YieldDto.fixture().copyWith(
+ poolType: PoolType.v2,
+ protocol: ProtocolDto.fixture().copyWith(name: "V2 PPOOL"),
+ );
+
+ final yields = YieldsDto.fixture().copyWith(pools: [selectedYield]);
+
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.state).thenReturn(DepositState.success(yields));
+ when(() => cubit.selectYield(any(), any())).thenAnswer((_) async => () {});
+
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.pumpAndSettle();
+
+ await tester.enterText(find.byKey(const Key("v2-pool-base-token-input-card")), "1");
+ await tester.pumpAndSettle();
+ });
+ },
+ );
+
+ zGoldenTest(
+ """When inputting an amount in the quote token amount input and the yield is v2, it should calculate
+ the base token amount correctly based on the pool reserves""",
+ goldenFileName: "deposit_page_calculate_base_token_amount",
+ (tester) async {
+ await tester.runAsync(() async {
+ const ({double reserve0, double reserve1}) poolReserves = (reserve0: 1, reserve1: 2);
+
+ when(() => cubit.v2PoolReservesStream).thenAnswer((_) => const Stream.empty());
+ when(() => cubit.latestV2PoolReserves).thenReturn(poolReserves);
+
+ final selectedYield = YieldDto.fixture().copyWith(
+ poolType: PoolType.v2,
+ protocol: ProtocolDto.fixture().copyWith(name: "V2 PPOOL"),
+ );
+
+ final yields = YieldsDto.fixture().copyWith(pools: [selectedYield]);
+
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.state).thenReturn(DepositState.success(yields));
+ when(() => cubit.selectYield(any(), any())).thenAnswer((_) async => () {});
+
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.pumpAndSettle();
+
+ await tester.enterText(find.byKey(const Key("v2-pool-quote-token-input-card")), "1");
+ await tester.pumpAndSettle();
+ });
+ },
+ );
+
+ zGoldenTest(
+ """When inputting an amount in the quote token amount input with the yield being v2,
+ and then deleting the text, it should also clear the amount in the base token amount input""",
+ goldenFileName: "deposit_page_clear_base_token_amount",
+ (tester) async {
+ await tester.runAsync(() async {
+ const ({double reserve0, double reserve1}) poolReserves = (reserve0: 1, reserve1: 2);
+
+ when(() => cubit.v2PoolReservesStream).thenAnswer((_) => const Stream.empty());
+ when(() => cubit.latestV2PoolReserves).thenReturn(poolReserves);
+
+ final selectedYield = YieldDto.fixture().copyWith(
+ poolType: PoolType.v2,
+ protocol: ProtocolDto.fixture().copyWith(name: "V2 PPOOL"),
+ );
+
+ final yields = YieldsDto.fixture().copyWith(pools: [selectedYield]);
+
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.state).thenReturn(DepositState.success(yields));
+ when(() => cubit.selectYield(any(), any())).thenAnswer((_) async => () {});
+
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.pumpAndSettle();
+
+ await tester.enterText(find.byKey(const Key("v2-pool-quote-token-input-card")), "1");
+ await tester.pumpAndSettle();
+
+ await tester.enterText(find.byKey(const Key("v2-pool-quote-token-input-card")), "");
+ await tester.pumpAndSettle();
+ });
+ },
+ );
+
+ zGoldenTest(
+ """When inputting an amount in the base token amount input with the yield being v2,
+ and then deleting the text, it should also clear the amount in the quote token amount input""",
+ goldenFileName: "deposit_page_clear_quote_token_amount",
+ (tester) async {
+ await tester.runAsync(() async {
+ const ({double reserve0, double reserve1}) poolReserves = (reserve0: 1, reserve1: 2);
+
+ when(() => cubit.v2PoolReservesStream).thenAnswer((_) => const Stream.empty());
+ when(() => cubit.latestV2PoolReserves).thenReturn(poolReserves);
+
+ final selectedYield = YieldDto.fixture().copyWith(
+ poolType: PoolType.v2,
+ protocol: ProtocolDto.fixture().copyWith(name: "V2 PPOOL"),
+ );
+
+ final yields = YieldsDto.fixture().copyWith(pools: [selectedYield]);
+
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.state).thenReturn(DepositState.success(yields));
+ when(() => cubit.selectYield(any(), any())).thenAnswer((_) async => () {});
+
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.pumpAndSettle();
+
+ await tester.enterText(find.byKey(const Key("v2-pool-base-token-input-card")), "1");
+ await tester.pumpAndSettle();
+
+ await tester.enterText(find.byKey(const Key("v2-pool-base-token-input-card")), "");
+ await tester.pumpAndSettle();
+ });
+ },
+ );
+
+ zGoldenTest(
+ """When a wallet is connected, a v2 yield is selected, and there is no deposit amount typed,
+ the deposit button should be disabled with text to input the amount""",
+ goldenFileName: "deposit_page_deposit_v2_pool_button_disabled_input_amount",
+ (tester) async {
+ await tester.runAsync(() async {
+ const ({double reserve0, double reserve1}) poolReserves = (reserve0: 1, reserve1: 2);
+ final signer = SignerMock();
+ final selectedYield = YieldDto.fixture().copyWith(
+ poolType: PoolType.v2,
+ protocol: ProtocolDto.fixture().copyWith(name: "V2 PPOOL"),
+ );
+ final yields = YieldsDto.fixture().copyWith(pools: [selectedYield]);
+
+ when(() => cubit.v2PoolReservesStream).thenAnswer((_) => const Stream.empty());
+ when(() => cubit.latestV2PoolReserves).thenReturn(poolReserves);
+ when(() => wallet.signer).thenReturn(signer);
+ when(() => wallet.signerStream).thenAnswer((_) => Stream.value(signer));
+ when(() => signer.address).thenAnswer((_) => Future.value("0x99E3CfADCD8Feecb5DdF91f88998cFfB3145F78c"));
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.state).thenReturn(DepositState.success(yields));
+
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.pumpAndSettle();
+ });
+ },
+ );
+
+ zGoldenTest(
+ """When a wallet is connected, a v2 yield is selected, and the typed amount in the
+ base token input is less than the available in the wallet, the deposit button should
+ be disabled with the text indicating that the wallet balance is too low""",
+ goldenFileName: "deposit_page_deposit_v2_pool_button_disabled_wallet_balance_too_low_base_token",
+ (tester) async {
+ await tester.runAsync(() async {
+ const ({double reserve0, double reserve1}) poolReserves = (reserve0: 1, reserve1: 2);
+ final signer = SignerMock();
+ final selectedYield = YieldDto.fixture().copyWith(
+ poolType: PoolType.v2,
+ protocol: ProtocolDto.fixture().copyWith(name: "V2 PPOOL"),
+ );
+ final yields = YieldsDto.fixture().copyWith(pools: [selectedYield]);
+ const availableWalletAmount = 0.1;
+
+ when(() => cubit.v2PoolReservesStream).thenAnswer((_) => const Stream.empty());
+ when(() => cubit.latestV2PoolReserves).thenReturn(poolReserves);
+ when(() => wallet.signer).thenReturn(signer);
+ when(() => wallet.signerStream).thenAnswer((_) => Stream.value(signer));
+ when(() => signer.address).thenAnswer((_) => Future.value("0x99E3CfADCD8Feecb5DdF91f88998cFfB3145F78c"));
+ when(() => cubit.getWalletTokenAmount(selectedYield.token1NetworkAddress, network: any(named: "network")))
+ .thenAnswer((_) async => 172678152164213);
+ when(() => cubit.getWalletTokenAmount(selectedYield.token0NetworkAddress, network: any(named: "network")))
+ .thenAnswer((_) async => availableWalletAmount);
+ when(() => wallet.nativeOrTokenBalance(selectedYield.token0NetworkAddress, rpcUrl: any(named: "rpcUrl")))
+ .thenAnswer((_) async => availableWalletAmount);
+ when(() => wallet.nativeOrTokenBalance(selectedYield.token1NetworkAddress, rpcUrl: any(named: "rpcUrl")))
+ .thenAnswer((_) async => 172678152164213);
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.state).thenReturn(DepositState.success(yields));
+
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.pumpAndSettle();
+
+ await tester.enterText(
+ find.byKey(const Key("v2-pool-base-token-input-card")),
+ (availableWalletAmount + 0.5).toString(),
+ );
+ await tester.pumpAndSettle();
+ });
+ },
+ );
+
+ zGoldenTest(
+ """When a wallet is connected, a v2 yield is selected, and the typed amount in the
+ quote token input is less than the available in the wallet, the deposit button should
+ be disabled with the text indicating that the wallet balance is too low""",
+ goldenFileName: "deposit_page_deposit_v2_pool_button_disabled_wallet_balance_too_low_quote_token",
+ (tester) async {
+ await tester.runAsync(() async {
+ const ({double reserve0, double reserve1}) poolReserves = (reserve0: 1, reserve1: 2);
+ final signer = SignerMock();
+ final selectedYield = YieldDto.fixture().copyWith(
+ poolType: PoolType.v2,
+ protocol: ProtocolDto.fixture().copyWith(name: "V2 PPOOL"),
+ );
+ final yields = YieldsDto.fixture().copyWith(pools: [selectedYield]);
+ const availableWalletAmount = 0.1;
+
+ when(() => cubit.v2PoolReservesStream).thenAnswer((_) => const Stream.empty());
+ when(() => cubit.latestV2PoolReserves).thenReturn(poolReserves);
+ when(() => wallet.signer).thenReturn(signer);
+ when(() => wallet.signerStream).thenAnswer((_) => Stream.value(signer));
+ when(() => signer.address).thenAnswer((_) => Future.value("0x99E3CfADCD8Feecb5DdF91f88998cFfB3145F78c"));
+ when(() => cubit.getWalletTokenAmount(selectedYield.token0NetworkAddress, network: any(named: "network")))
+ .thenAnswer((_) async => 172678152164213);
+ when(() => cubit.getWalletTokenAmount(selectedYield.token1NetworkAddress, network: any(named: "network")))
+ .thenAnswer((_) async => availableWalletAmount);
+ when(() => wallet.nativeOrTokenBalance(selectedYield.token1NetworkAddress, rpcUrl: any(named: "rpcUrl")))
+ .thenAnswer((_) async => availableWalletAmount);
+ when(() => wallet.nativeOrTokenBalance(selectedYield.token0NetworkAddress, rpcUrl: any(named: "rpcUrl")))
+ .thenAnswer((_) async => 172678152164213);
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.state).thenReturn(DepositState.success(yields));
+
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.pumpAndSettle();
+
+ await tester.enterText(
+ find.byKey(const Key("v2-pool-quote-token-input-card")),
+ (availableWalletAmount + 0.5).toString(),
+ );
+ await tester.pumpAndSettle();
+ });
+ },
+ );
+
+ zGoldenTest(
+ """When a wallet is connected, a v2 yield is selected, and the amounts to deposit
+ are available in the wallet, the deposit button should be enabled to preview the deposit""",
+ goldenFileName: "deposit_page_deposit_v2_pool_button_enabled_preview",
+ (tester) async {
+ await tester.runAsync(() async {
+ const ({double reserve0, double reserve1}) poolReserves = (reserve0: 1, reserve1: 2);
+ final signer = SignerMock();
+ final selectedYield = YieldDto.fixture().copyWith(
+ poolType: PoolType.v2,
+ protocol: ProtocolDto.fixture().copyWith(name: "V2 PPOOL"),
+ );
+ final yields = YieldsDto.fixture().copyWith(pools: [selectedYield]);
+
+ when(() => cubit.v2PoolReservesStream).thenAnswer((_) => const Stream.empty());
+ when(() => cubit.latestV2PoolReserves).thenReturn(poolReserves);
+ when(() => wallet.signer).thenReturn(signer);
+ when(() => wallet.signerStream).thenAnswer((_) => Stream.value(signer));
+ when(() => signer.address).thenAnswer((_) => Future.value("0x99E3CfADCD8Feecb5DdF91f88998cFfB3145F78c"));
+ when(() => cubit.getWalletTokenAmount(any(), network: any(named: "network")))
+ .thenAnswer((_) async => 172678152164213);
+ when(() => wallet.nativeOrTokenBalance(any(), rpcUrl: any(named: "rpcUrl")))
+ .thenAnswer((_) async => 172678152164213);
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.state).thenReturn(DepositState.success(yields));
+
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.pumpAndSettle();
+
+ await tester.enterText(find.byKey(const Key("v2-pool-quote-token-input-card")), 0.1.toString());
+ await tester.pumpAndSettle();
+ });
+ },
+ );
+
+ zGoldenTest(
+ """When clicking the preview deposit button in the v2 pool, the preview deposit
+ modal should be shown""",
+ goldenFileName: "deposit_page_deposit_v2_pool_button_preview_tap",
+ (tester) async {
+ await tester.runAsync(() async {
+ const ({double reserve0, double reserve1}) poolReserves = (reserve0: 1, reserve1: 2);
+ final signer = SignerMock();
+ final selectedYield = YieldDto.fixture().copyWith(
+ poolType: PoolType.v2,
+ protocol: ProtocolDto.fixture().copyWith(name: "V2 PPOOL"),
+ );
+ final yields = YieldsDto.fixture().copyWith(pools: [selectedYield]);
+
+ when(() => cubit.v2PoolReservesStream).thenAnswer((_) => const Stream.empty());
+ when(() => cubit.latestV2PoolReserves).thenReturn(poolReserves);
+ when(() => wallet.signer).thenReturn(signer);
+ when(() => wallet.signerStream).thenAnswer((_) => Stream.value(signer));
+ when(() => signer.address).thenAnswer((_) => Future.value("0x99E3CfADCD8Feecb5DdF91f88998cFfB3145F78c"));
+ when(() => cubit.getWalletTokenAmount(any(), network: any(named: "network")))
+ .thenAnswer((_) async => 172678152164213);
+ when(() => wallet.nativeOrTokenBalance(any(), rpcUrl: any(named: "rpcUrl")))
+ .thenAnswer((_) async => 172678152164213);
+ when(() => cubit.selectedYield).thenReturn(selectedYield);
+ when(() => cubit.selectedYieldStream).thenAnswer((_) => Stream.value(selectedYield));
+ when(() => cubit.state).thenReturn(DepositState.success(yields));
+
+ await tester.pumpDeviceBuilder(await goldenBuilder(), wrapper: GoldenConfig.localizationsWrapper());
+ await tester.pumpAndSettle();
+
+ await tester.enterText(find.byKey(const Key("v2-pool-quote-token-input-card")), 0.1.toString());
+ await tester.pumpAndSettle();
+
+ await tester.tap(find.byKey(const Key("deposit-button")));
+ await tester.pumpAndSettle();
+ });
+ },
+ );
}
diff --git a/test/app/create/deposit/goldens/deposit_page_calculate_base_token_amount.png b/test/app/create/deposit/goldens/deposit_page_calculate_base_token_amount.png
new file mode 100644
index 0000000..fd2120f
Binary files /dev/null and b/test/app/create/deposit/goldens/deposit_page_calculate_base_token_amount.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_calculate_quote_token_amount.png b/test/app/create/deposit/goldens/deposit_page_calculate_quote_token_amount.png
new file mode 100644
index 0000000..5571409
Binary files /dev/null and b/test/app/create/deposit/goldens/deposit_page_calculate_quote_token_amount.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_clear_base_token_amount.png b/test/app/create/deposit/goldens/deposit_page_clear_base_token_amount.png
new file mode 100644
index 0000000..c187d0e
Binary files /dev/null and b/test/app/create/deposit/goldens/deposit_page_clear_base_token_amount.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_clear_quote_token_amount.png b/test/app/create/deposit/goldens/deposit_page_clear_quote_token_amount.png
new file mode 100644
index 0000000..d674c7b
Binary files /dev/null and b/test/app/create/deposit/goldens/deposit_page_clear_quote_token_amount.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_deposit_v2_pool_button_disabled_input_amount.png b/test/app/create/deposit/goldens/deposit_page_deposit_v2_pool_button_disabled_input_amount.png
new file mode 100644
index 0000000..a791203
Binary files /dev/null and b/test/app/create/deposit/goldens/deposit_page_deposit_v2_pool_button_disabled_input_amount.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_deposit_v2_pool_button_disabled_wallet_balance_too_low_base_token.png b/test/app/create/deposit/goldens/deposit_page_deposit_v2_pool_button_disabled_wallet_balance_too_low_base_token.png
new file mode 100644
index 0000000..a1abd39
Binary files /dev/null and b/test/app/create/deposit/goldens/deposit_page_deposit_v2_pool_button_disabled_wallet_balance_too_low_base_token.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_deposit_v2_pool_button_disabled_wallet_balance_too_low_quote_token.png b/test/app/create/deposit/goldens/deposit_page_deposit_v2_pool_button_disabled_wallet_balance_too_low_quote_token.png
new file mode 100644
index 0000000..f06e0ba
Binary files /dev/null and b/test/app/create/deposit/goldens/deposit_page_deposit_v2_pool_button_disabled_wallet_balance_too_low_quote_token.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_deposit_v2_pool_button_enabled_preview.png b/test/app/create/deposit/goldens/deposit_page_deposit_v2_pool_button_enabled_preview.png
new file mode 100644
index 0000000..704cc35
Binary files /dev/null and b/test/app/create/deposit/goldens/deposit_page_deposit_v2_pool_button_enabled_preview.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_deposit_v2_pool_button_preview_tap.png b/test/app/create/deposit/goldens/deposit_page_deposit_v2_pool_button_preview_tap.png
new file mode 100644
index 0000000..6be2b66
Binary files /dev/null and b/test/app/create/deposit/goldens/deposit_page_deposit_v2_pool_button_preview_tap.png differ
diff --git a/test/app/create/deposit/goldens/deposit_page_select_v2_pool_scroll_to_deposit_section.png b/test/app/create/deposit/goldens/deposit_page_select_v2_pool_scroll_to_deposit_section.png
new file mode 100644
index 0000000..8e39d03
Binary files /dev/null and b/test/app/create/deposit/goldens/deposit_page_select_v2_pool_scroll_to_deposit_section.png differ
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 ad2038b..5aa5a7c 100644
Binary files a/test/app/create/deposit/widgets/goldens/range_selector_is_infinity_increase_price.png and b/test/app/create/deposit/widgets/goldens/range_selector_is_infinity_increase_price.png differ
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 5c9e4af..5aa5a7c 100644
Binary files a/test/app/create/deposit/widgets/goldens/range_selector_is_infinity_increase_price_reversed.png and b/test/app/create/deposit/widgets/goldens/range_selector_is_infinity_increase_price_reversed.png differ
diff --git a/test/app/create/deposit/widgets/preview_deposit_modal/goldens/preview_deposit_modal_v2_pool_type.png b/test/app/create/deposit/widgets/preview_deposit_modal/goldens/preview_deposit_modal_v2_pool_type.png
new file mode 100644
index 0000000..eea5ec3
Binary files /dev/null and b/test/app/create/deposit/widgets/preview_deposit_modal/goldens/preview_deposit_modal_v2_pool_type.png differ
diff --git a/test/app/create/deposit/widgets/preview_deposit_modal/preview_deposit_modal_cubit_test.dart b/test/app/create/deposit/widgets/preview_deposit_modal/preview_deposit_modal_cubit_test.dart
index 464a145..63b47d5 100644
--- a/test/app/create/deposit/widgets/preview_deposit_modal/preview_deposit_modal_cubit_test.dart
+++ b/test/app/create/deposit/widgets/preview_deposit_modal/preview_deposit_modal_cubit_test.dart
@@ -1004,8 +1004,8 @@ void main() {
tickLower: V3PoolConversorsMixinWrapper().tickToClosestValidTick(
tick: V3PoolConversorsMixinWrapper().priceToTick(
price: minPrice,
- poolToken0Decimals: currentYield.token0.decimals,
- poolToken1Decimals: currentYield.token1.decimals,
+ poolToken0Decimals: currentYield.token0NetworkDecimals,
+ poolToken1Decimals: currentYield.token1NetworkDecimals,
isReversed: false,
),
tickSpacing: currentYield.tickSpacing,
@@ -1052,8 +1052,8 @@ void main() {
tickLower: V3PoolConversorsMixinWrapper().tickToClosestValidTick(
tick: V3PoolConversorsMixinWrapper().priceToTick(
price: maxPrice,
- poolToken0Decimals: currentYield.token0.decimals,
- poolToken1Decimals: currentYield.token1.decimals,
+ poolToken0Decimals: currentYield.token0NetworkDecimals,
+ poolToken1Decimals: currentYield.token1NetworkDecimals,
isReversed: isReversed,
),
tickSpacing: currentYield.tickSpacing,
@@ -1177,8 +1177,8 @@ void main() {
tickUpper: V3PoolConversorsMixinWrapper().tickToClosestValidTick(
tick: V3PoolConversorsMixinWrapper().priceToTick(
price: maxPrice,
- poolToken0Decimals: currentYield.token0.decimals,
- poolToken1Decimals: currentYield.token1.decimals,
+ poolToken0Decimals: currentYield.token0NetworkDecimals,
+ poolToken1Decimals: currentYield.token1NetworkDecimals,
isReversed: false,
),
tickSpacing: currentYield.tickSpacing,
@@ -1224,8 +1224,8 @@ void main() {
tickUpper: V3PoolConversorsMixinWrapper().tickToClosestValidTick(
tick: V3PoolConversorsMixinWrapper().priceToTick(
price: minPrice,
- poolToken0Decimals: currentYield.token0.decimals,
- poolToken1Decimals: currentYield.token1.decimals,
+ poolToken0Decimals: currentYield.token0NetworkDecimals,
+ poolToken1Decimals: currentYield.token1NetworkDecimals,
isReversed: isReversed,
),
tickSpacing: currentYield.tickSpacing,
@@ -1702,8 +1702,8 @@ void main() {
verify(() => 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);
@@ -1989,4 +1989,43 @@ void main() {
),
).called(1);
});
+
+ test(
+ """When calling `deposit` and the deposit pool type is v2,
+ it should call the pool service to deposit on v2 with the
+ correct params""",
+ () async {
+ const slippage = Slippage.halfPercent;
+ final amount0 = BigInt.from(1261821789);
+ final amount1 = BigInt.from(1261821789);
+ const deadline = Duration(minutes: 30);
+
+ final currentYield0 = currentYield.copyWith(poolType: PoolType.v2);
+ final sut0 = PreviewDepositModalCubit(
+ initialPoolTick: initialPoolTick,
+ poolService: poolService,
+ currentYield: currentYield0,
+ erc20: erc20,
+ wallet: wallet,
+ uniswapPositionManager: uniswapPositionManager,
+ permit2: permit2,
+ navigatorKey: GlobalKey(),
+ zupAnalytics: zupAnalytics,
+ );
+
+ await sut0.deposit(token0Amount: amount0, token1Amount: amount1, slippage: slippage, deadline: deadline);
+
+ verify(
+ () => poolService.sendV2PoolDepositTransaction(
+ currentYield0,
+ signer,
+ amount0: amount0,
+ amount1: amount1,
+ amount0Min: slippage.calculateMinTokenAmountFromSlippage(amount0),
+ amount1Min: slippage.calculateMinTokenAmountFromSlippage(amount1),
+ deadline: deadline,
+ ),
+ ).called(1);
+ },
+ );
}
diff --git a/test/app/create/deposit/widgets/preview_deposit_modal/preview_deposit_modal_test.dart b/test/app/create/deposit/widgets/preview_deposit_modal/preview_deposit_modal_test.dart
index 0961758..30a4104 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
@@ -17,6 +17,7 @@ import 'package:zup_app/app/create/deposit/widgets/preview_deposit_modal/preview
import 'package:zup_app/core/dtos/token_dto.dart';
import 'package:zup_app/core/dtos/yield_dto.dart';
import 'package:zup_app/core/enums/networks.dart';
+import 'package:zup_app/core/enums/pool_type.dart';
import 'package:zup_app/core/injections.dart';
import 'package:zup_app/core/pool_service.dart';
import 'package:zup_app/core/slippage.dart';
@@ -307,8 +308,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 +334,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 +363,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 +540,7 @@ void main() {
verify(
() => cubit.approveToken(
currentYield.token0,
- depositAmount.parseTokenAmount(decimals: currentYield.token0.decimals),
+ depositAmount.parseTokenAmount(decimals: currentYield.token0NetworkDecimals),
),
).called(1);
},
@@ -556,14 +557,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 +592,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 +616,7 @@ void main() {
verify(
() => cubit.approveToken(
currentYield.token1,
- depositAmount.parseTokenAmount(decimals: currentYield.token1.decimals),
+ depositAmount.parseTokenAmount(decimals: currentYield.token1NetworkDecimals),
),
).called(1);
},
@@ -627,8 +628,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 +662,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 +715,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 +729,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 +747,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 +767,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,
);
@@ -1017,4 +1018,24 @@ void main() {
await tester.pumpAndSettle();
},
);
+
+ zGoldenTest(
+ "When the pool type is v2, the modal should be adapted to show v2 deposit information",
+ goldenFileName: "preview_deposit_modal_v2_pool_type",
+ (tester) async {
+ when(() => cubit.state).thenReturn(
+ PreviewDepositModalState.initial(
+ token0Allowance: BigInt.from(0),
+ token1Allowance: BigInt.from(0),
+ ),
+ );
+
+ await tester.pumpDeviceBuilder(
+ await goldenBuilder(customYield: YieldDto.fixture().copyWith(poolType: PoolType.v2)),
+ wrapper: GoldenConfig.localizationsWrapper(scaffoldMessengerKey: scaffoldMessengerKey),
+ );
+
+ await tester.pumpAndSettle();
+ },
+ );
}
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/app/create/goldens/create_page_select_tokens_stage_pool_search_settings_open.png b/test/app/create/goldens/create_page_select_tokens_stage_pool_search_settings_open.png
index ebcd14b..6b9b139 100644
Binary files a/test/app/create/goldens/create_page_select_tokens_stage_pool_search_settings_open.png and b/test/app/create/goldens/create_page_select_tokens_stage_pool_search_settings_open.png differ
diff --git a/test/app/create/widgets/create_page_settings_dropdown_test.dart b/test/app/create/widgets/create_page_settings_dropdown_test.dart
index ebfe786..4f5e258 100644
--- a/test/app/create/widgets/create_page_settings_dropdown_test.dart
+++ b/test/app/create/widgets/create_page_settings_dropdown_test.dart
@@ -280,4 +280,72 @@ void main() {
).called(1);
},
);
+
+ zGoldenTest("When clicking to disable the V2 switch it should update the UI",
+ goldenFileName: "create_page_settings_dropdown_v2_pool_type_disable", (tester) async {
+ final initialSettings = PoolSearchSettingsDto();
+ when(() => cache.getPoolSearchSettings()).thenReturn(initialSettings);
+
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.pumpAndSettle();
+
+ when(() => cache.getPoolSearchSettings()).thenReturn(
+ initialSettings.copyWith(allowV2Search: false),
+ );
+
+ await tester.tap(find.byKey(const Key("pool-types-allowed-v2-switch")));
+ await tester.pumpAndSettle();
+ });
+ zGoldenTest("When clicking to enable the V2 switch it should update the UI",
+ goldenFileName: "create_page_settings_dropdown_v2_pool_type_enable", (tester) async {
+ final initialSettings = PoolSearchSettingsDto(allowV2Search: false, allowV3Search: false, allowV4Search: false);
+
+ when(() => cache.getPoolSearchSettings()).thenReturn(initialSettings);
+
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.pumpAndSettle();
+
+ when(() => cache.getPoolSearchSettings()).thenReturn(
+ initialSettings.copyWith(allowV2Search: true),
+ );
+
+ await tester.tap(find.byKey(const Key("pool-types-allowed-v2-switch")));
+ await tester.pumpAndSettle();
+ });
+
+ zGoldenTest(
+ "When clicking to disable the V2 switch it should call the cache to update the settings only for the v2 switch",
+ (tester) async {
+ final initialSettings = PoolSearchSettingsDto(allowV2Search: true);
+
+ when(() => cache.getPoolSearchSettings()).thenReturn(initialSettings);
+
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.pumpAndSettle();
+
+ await tester.tap(find.byKey(const Key("pool-types-allowed-v2-switch")));
+ await tester.pumpAndSettle();
+
+ verify(
+ () => cache.savePoolSearchSettings(settings: initialSettings.copyWith(allowV2Search: false)),
+ ).called(1);
+ });
+
+ zGoldenTest(
+ "When clicking to enable the V2 switch it should call the cache to update the settings only for the v2 switch",
+ (tester) async {
+ final initialSettings = PoolSearchSettingsDto(allowV2Search: false);
+
+ when(() => cache.getPoolSearchSettings()).thenReturn(initialSettings);
+
+ await tester.pumpDeviceBuilder(await goldenBuilder());
+ await tester.pumpAndSettle();
+
+ await tester.tap(find.byKey(const Key("pool-types-allowed-v2-switch")));
+ await tester.pumpAndSettle();
+
+ verify(
+ () => cache.savePoolSearchSettings(settings: initialSettings.copyWith(allowV2Search: true)),
+ ).called(1);
+ });
}
diff --git a/test/app/create/widgets/goldens/create_page_setting_dropdown_min_liquidity_non_numeric.png b/test/app/create/widgets/goldens/create_page_setting_dropdown_min_liquidity_non_numeric.png
index f399b79..0776b14 100644
Binary files a/test/app/create/widgets/goldens/create_page_setting_dropdown_min_liquidity_non_numeric.png and b/test/app/create/widgets/goldens/create_page_setting_dropdown_min_liquidity_non_numeric.png differ
diff --git a/test/app/create/widgets/goldens/create_page_settings_dropdown.png b/test/app/create/widgets/goldens/create_page_settings_dropdown.png
index 8d2ed69..a00b1b4 100644
Binary files a/test/app/create/widgets/goldens/create_page_settings_dropdown.png and b/test/app/create/widgets/goldens/create_page_settings_dropdown.png differ
diff --git a/test/app/create/widgets/goldens/create_page_settings_dropdown_low_min_tvl_warning.png b/test/app/create/widgets/goldens/create_page_settings_dropdown_low_min_tvl_warning.png
index 88cf67b..8ebce06 100644
Binary files a/test/app/create/widgets/goldens/create_page_settings_dropdown_low_min_tvl_warning.png and b/test/app/create/widgets/goldens/create_page_settings_dropdown_low_min_tvl_warning.png differ
diff --git a/test/app/create/widgets/goldens/create_page_settings_dropdown_min_liquidity_tooltip.png b/test/app/create/widgets/goldens/create_page_settings_dropdown_min_liquidity_tooltip.png
index 63d3ca9..0587391 100644
Binary files a/test/app/create/widgets/goldens/create_page_settings_dropdown_min_liquidity_tooltip.png and b/test/app/create/widgets/goldens/create_page_settings_dropdown_min_liquidity_tooltip.png differ
diff --git a/test/app/create/widgets/goldens/create_page_settings_dropdown_min_liquidity_warning_field.png b/test/app/create/widgets/goldens/create_page_settings_dropdown_min_liquidity_warning_field.png
index fcc6d03..a112446 100644
Binary files a/test/app/create/widgets/goldens/create_page_settings_dropdown_min_liquidity_warning_field.png and b/test/app/create/widgets/goldens/create_page_settings_dropdown_min_liquidity_warning_field.png differ
diff --git a/test/app/create/widgets/goldens/create_page_settings_dropdown_pool_types_tooltip_hover.png b/test/app/create/widgets/goldens/create_page_settings_dropdown_pool_types_tooltip_hover.png
index 501f6fe..58ab69b 100644
Binary files a/test/app/create/widgets/goldens/create_page_settings_dropdown_pool_types_tooltip_hover.png and b/test/app/create/widgets/goldens/create_page_settings_dropdown_pool_types_tooltip_hover.png differ
diff --git a/test/app/create/widgets/goldens/create_page_settings_dropdown_v2_pool_type_disable.png b/test/app/create/widgets/goldens/create_page_settings_dropdown_v2_pool_type_disable.png
new file mode 100644
index 0000000..ac97e97
Binary files /dev/null and b/test/app/create/widgets/goldens/create_page_settings_dropdown_v2_pool_type_disable.png differ
diff --git a/test/app/create/widgets/goldens/create_page_settings_dropdown_v2_pool_type_enable.png b/test/app/create/widgets/goldens/create_page_settings_dropdown_v2_pool_type_enable.png
new file mode 100644
index 0000000..ec5eb19
Binary files /dev/null and b/test/app/create/widgets/goldens/create_page_settings_dropdown_v2_pool_type_enable.png differ
diff --git a/test/app/create/widgets/goldens/create_page_settings_dropdown_v3_pool_type_disabled.png b/test/app/create/widgets/goldens/create_page_settings_dropdown_v3_pool_type_disabled.png
index 268ed13..52084d6 100644
Binary files a/test/app/create/widgets/goldens/create_page_settings_dropdown_v3_pool_type_disabled.png and b/test/app/create/widgets/goldens/create_page_settings_dropdown_v3_pool_type_disabled.png differ
diff --git a/test/app/create/widgets/goldens/create_page_settings_dropdown_v3_pool_type_enable.png b/test/app/create/widgets/goldens/create_page_settings_dropdown_v3_pool_type_enable.png
index c619409..0dab400 100644
Binary files a/test/app/create/widgets/goldens/create_page_settings_dropdown_v3_pool_type_enable.png and b/test/app/create/widgets/goldens/create_page_settings_dropdown_v3_pool_type_enable.png differ
diff --git a/test/app/create/widgets/goldens/create_page_settings_dropdown_v4_pool_type_disabled.png b/test/app/create/widgets/goldens/create_page_settings_dropdown_v4_pool_type_disabled.png
index 303eebf..e4c2400 100644
Binary files a/test/app/create/widgets/goldens/create_page_settings_dropdown_v4_pool_type_disabled.png and b/test/app/create/widgets/goldens/create_page_settings_dropdown_v4_pool_type_disabled.png differ
diff --git a/test/app/create/widgets/goldens/create_page_settings_dropdown_v4_pool_type_enable.png b/test/app/create/widgets/goldens/create_page_settings_dropdown_v4_pool_type_enable.png
index 650fd94..e296b64 100644
Binary files a/test/app/create/widgets/goldens/create_page_settings_dropdown_v4_pool_type_enable.png and b/test/app/create/widgets/goldens/create_page_settings_dropdown_v4_pool_type_enable.png differ
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 0000000..198aeeb
Binary files /dev/null and b/test/core/enums/goldens/bnb_network_icon.png differ
diff --git a/test/core/enums/networks_test.dart b/test/core/enums/networks_test.dart
index a51c3ed..82d2e44 100644
--- a/test/core/enums/networks_test.dart
+++ b/test/core/enums/networks_test.dart
@@ -3,7 +3,6 @@ import 'package:golden_toolkit/golden_toolkit.dart';
import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart';
import 'package:web3kit/core/dtos/chain_info.dart';
import 'package:web3kit/core/enums/native_currencies.dart';
-import 'package:zup_app/core/dtos/token_dto.dart';
import 'package:zup_app/core/enums/networks.dart';
import '../../golden_config.dart';
@@ -24,6 +23,7 @@ void main() {
expect(AppNetworks.fromValue("allNetworks"), AppNetworks.allNetworks, reason: "All networks should match");
expect(AppNetworks.fromValue("base"), AppNetworks.base, reason: "Base should match");
expect(AppNetworks.fromValue("unichain"), AppNetworks.unichain, reason: "Unichain should match");
+ expect(AppNetworks.fromValue("bnb"), AppNetworks.bnb, reason: "BNB should match");
});
test("Label extension should match for all networks", () {
@@ -33,6 +33,7 @@ void main() {
expect(AppNetworks.allNetworks.label, "All Networks", reason: "All Networks Label should match");
expect(AppNetworks.base.label, "Base", reason: "Base Label should match");
expect(AppNetworks.unichain.label, "Unichain", reason: "Unichain Label should match");
+ expect(AppNetworks.bnb.label, "BNB Chain", reason: "BNB Chain Label should match");
});
test("`testnets` method should return all testnets in the enum, excluding the 'all networks'", () {
@@ -48,6 +49,7 @@ void main() {
AppNetworks.scroll,
AppNetworks.base,
AppNetworks.unichain,
+ AppNetworks.bnb
]),
);
});
@@ -72,6 +74,10 @@ void main() {
expect(AppNetworks.unichain.isTestnet, false);
});
+ test("`isTestnet` method should return false for bnb", () {
+ expect(AppNetworks.bnb.isTestnet, false);
+ });
+
test("Chain info extension should match for all networks", () {
expect(
AppNetworks.sepolia.chainInfo,
@@ -125,12 +131,24 @@ void main() {
ChainInfo(
hexChainId: "0x82",
chainName: "Unichain",
- blockExplorerUrls: const ["https://uniscan.xyz/"],
+ blockExplorerUrls: const ["https://uniscan.xyz"],
nativeCurrency: NativeCurrencies.eth.currencyInfo,
rpcUrls: const ["https://unichain-rpc.publicnode.com"],
),
reason: "Unichain ChainInfo should match",
);
+
+ expect(
+ AppNetworks.bnb.chainInfo,
+ ChainInfo(
+ hexChainId: "0x38",
+ chainName: "BNB Chain",
+ blockExplorerUrls: const ["https://bscscan.com"],
+ nativeCurrency: NativeCurrencies.bnb.currencyInfo,
+ rpcUrls: const ["https://bsc-rpc.publicnode.com"],
+ ),
+ reason: "BNB Chain ChainInfo should match",
+ );
});
test("wrapped native token address should match for all networks", () {
@@ -163,79 +181,11 @@ void main() {
"0x4200000000000000000000000000000000000006",
reason: "Unichain wrapped native token address should match",
);
- });
- test("wrapped native token should match for all networks", () {
expect(
- AppNetworks.sepolia.wrappedNative,
- TokenDto(
- addresses: {
- AppNetworks.sepolia.chainId: "0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14",
- },
- name: "Wrapped Ether",
- decimals: 18,
- symbol: "WETH",
- logoUrl: "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/info/logo.png",
- ),
- reason: "Sepolia default token should match",
- );
-
- expect(
- AppNetworks.mainnet.wrappedNative,
- TokenDto(
- addresses: {
- AppNetworks.mainnet.chainId: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
- },
- name: "Wrapped Ether",
- decimals: 18,
- symbol: "WETH",
- logoUrl: "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/info/logo.png",
- ),
- reason: "Ethereum default token should match",
- );
-
- expect(
- AppNetworks.scroll.wrappedNative,
- TokenDto(
- addresses: {
- AppNetworks.scroll.chainId: "0x5300000000000000000000000000000000000004",
- },
- name: "Wrapped Ether",
- decimals: 18,
- symbol: "WETH",
- logoUrl:
- "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/scroll/assets/0x5300000000000000000000000000000000000004/logo.png",
- ),
- reason: "Scroll default token should match",
- );
-
- expect(
- AppNetworks.base.wrappedNative,
- TokenDto(
- addresses: {
- AppNetworks.base.chainId: "0x4200000000000000000000000000000000000006",
- },
- name: "Wrapped Ether",
- decimals: 18,
- symbol: "WETH",
- logoUrl:
- "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/base/assets/0x4200000000000000000000000000000000000006/logo.png",
- ),
- reason: "Base default token should match",
- );
-
- expect(
- AppNetworks.unichain.wrappedNative,
- TokenDto(
- addresses: {
- AppNetworks.unichain.chainId: "0x4200000000000000000000000000000000000006",
- },
- name: "Wrapped Ether",
- decimals: 18,
- symbol: "WETH",
- logoUrl: "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/unichain/logo.png",
- ),
- reason: "Unichain default token should match",
+ AppNetworks.bnb.wrappedNativeTokenAddress,
+ "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c",
+ reason: "BNB wrapped native token address should match",
);
});
@@ -269,6 +219,12 @@ void main() {
"https://unichain-rpc.publicnode.com",
reason: "Unichain rpc url should match",
);
+
+ expect(
+ AppNetworks.bnb.rpcUrl,
+ "https://bsc-rpc.publicnode.com",
+ reason: "BNB rpc url should match",
+ );
});
test("openTx should open the correct url for each network", () async {
@@ -350,4 +306,11 @@ void main() {
device: GoldenDevice.square,
));
});
+
+ zGoldenTest("BNB network icon should match", goldenFileName: "bnb_network_icon", (tester) async {
+ await tester.pumpDeviceBuilder(await goldenDeviceBuilder(
+ AppNetworks.bnb.icon,
+ device: GoldenDevice.square,
+ ));
+ });
}
diff --git a/test/core/enums/pool_type_test.dart b/test/core/enums/pool_type_test.dart
index ddcfa80..b19fb17 100644
--- a/test/core/enums/pool_type_test.dart
+++ b/test/core/enums/pool_type_test.dart
@@ -18,8 +18,17 @@ void main() {
expect(PoolType.v3.isV4, false);
});
+ test('When calling `isV2` and the pool is indeed v2, it should return true', () {
+ expect(PoolType.v2.isV2, true);
+ });
+
+ test('When calling `isV2` and the pool is not v2, it should return false', () {
+ expect(PoolType.v3.isV2, false);
+ });
+
test('label should return correct string', () {
expect(PoolType.v3.label, "V3");
expect(PoolType.v4.label, "V4");
+ expect(PoolType.v2.label, "V2");
});
}
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 c19d037..4a875af 100644
--- a/test/core/pool_service_test.dart
+++ b/test/core/pool_service_test.dart
@@ -4,16 +4,22 @@ 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_v2_pool.abi.g.dart';
+import 'package:zup_app/abis/uniswap_v2_router_02.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/slippage.dart';
import 'package:zup_app/core/v4_pool_constants.dart';
import '../mocks.dart';
@@ -24,8 +30,11 @@ void main() {
late PoolService sut;
late UniswapV4StateView stateView;
late UniswapV3Pool uniswapV3Pool;
+ late UniswapV2Pool uniswapV2Pool;
+ late UniswapV2Router02 uniswapV2Router02;
late UniswapV3PositionManager positionManagerV3;
late UniswapV4PositionManager positionManagerV4;
+ late PancakeSwapInfinityClPoolManager pancakeSwapInfinityCLPoolManager;
late Signer signer;
late YieldDto currentYield;
late TransactionResponse transactionResponse;
@@ -34,6 +43,9 @@ void main() {
late UniswapV3PoolImpl uniswapV3PoolImpl;
late UniswapV3PositionManagerImpl positionManagerV3Impl;
late UniswapV4PositionManagerImpl positionManagerV4Impl;
+ late PancakeSwapInfinityClPoolManagerImpl pancakeSwapInfinityCLPoolManagerImpl;
+ late UniswapV2PoolImpl uniswapV2PoolImpl;
+ late UniswapV2Router02Impl uniswapV2Router02Impl;
late EthereumAbiCoder ethereumAbiCoder;
setUp(() {
@@ -56,11 +68,17 @@ void main() {
transactionResponse = TransactionResponseMock();
stateView = UniswapV4StateViewMock();
uniswapV3Pool = UniswapV3PoolMock();
+ uniswapV2Pool = UniswapV2PoolMock();
+ uniswapV2Router02 = UniswapV2Router02Mock();
positionManagerV3 = UniswapV3PositionManagerMock();
positionManagerV4 = UniswapV4PositionManagerMock();
+ pancakeSwapInfinityCLPoolManager = PancakeSwapInfinityCLPoolManagerMock();
ethereumAbiCoder = EthereumAbiCoderMock();
signer = SignerMock();
+ pancakeSwapInfinityCLPoolManagerImpl = PancakeSwapInfinityCLPoolManagerImplMock();
+ uniswapV2PoolImpl = UniswapV2PoolImplMock();
+ uniswapV2Router02Impl = UniswapV2Router02ImplMock();
stateViewImpl = UniswapV4StateViewImplMock();
uniswapV3PoolImpl = UniswapV3PoolImplMock();
positionManagerV3Impl = UniswapV3PositionManagerImplMock();
@@ -68,7 +86,25 @@ void main() {
currentYield = YieldDto.fixture();
- sut = PoolService(stateView, uniswapV3Pool, positionManagerV3, positionManagerV4, ethereumAbiCoder);
+ sut = PoolService(
+ stateView,
+ uniswapV3Pool,
+ uniswapV2Pool,
+ positionManagerV3,
+ positionManagerV4,
+ uniswapV2Router02,
+ ethereumAbiCoder,
+ pancakeSwapInfinityCLPoolManager,
+ );
+
+ when(() =>
+ uniswapV2Pool.fromRpcProvider(contractAddress: any(named: "contractAddress"), rpcUrl: any(named: "rpcUrl")))
+ .thenReturn(uniswapV2PoolImpl);
+
+ when(() => uniswapV2Router02.fromRpcProvider(
+ contractAddress: any(named: "contractAddress"),
+ rpcUrl: any(named: "rpcUrl"),
+ )).thenReturn(uniswapV2Router02Impl);
when(() => stateView.fromRpcProvider(contractAddress: any(named: "contractAddress"), rpcUrl: any(named: "rpcUrl")))
.thenReturn(stateViewImpl);
@@ -98,6 +134,9 @@ void main() {
when(() => transactionResponse.waitConfirmation()).thenAnswer((_) async => TransactionReceipt(hash: "0x123"));
when(() => transactionResponse.hash).thenReturn("0x123");
+ when(() =>
+ uniswapV2Pool.fromRpcProvider(contractAddress: any(named: "contractAddress"), rpcUrl: any(named: "rpcUrl")))
+ .thenReturn(uniswapV2PoolImpl);
});
test(
@@ -110,6 +149,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);
@@ -254,7 +299,7 @@ void main() {
recipient: recipient,
tickLower: tickLower,
tickUpper: tickUpper,
- token0: network.wrappedNative.addresses[network.chainId]!,
+ token0: network.wrappedNativeTokenAddress,
token1: token1Address,
)),
).called(1);
@@ -1331,4 +1376,257 @@ 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);
+ },
+ );
+
+ test(
+ "When calling 'getV2PoolReserves' it should call the pool contract to get it and return the result",
+ () async {
+ final expectedReserves = (reserve0: BigInt.from(861287), reserve1: BigInt.from(98687));
+
+ when(() => uniswapV2PoolImpl.getReserves()).thenAnswer(
+ (_) async => (
+ reserve0: expectedReserves.reserve0,
+ reserve1: expectedReserves.reserve1,
+ blockTimestampLast: BigInt.from(0)
+ ),
+ );
+
+ final receivedReserves = await sut.getV2PoolReserves(currentYield);
+ expect(receivedReserves, expectedReserves);
+ },
+ );
+
+ test(
+ "When calling `sendV2PoolDepositTransaction` it should connect the passed signer to the v2 router contract",
+ () async {
+ when(() => uniswapV2Router02.fromSigner(
+ contractAddress: any(named: "contractAddress"),
+ signer: any(named: "signer"))).thenReturn(uniswapV2Router02Impl);
+ when(
+ () => uniswapV2Router02Impl.addLiquidity(
+ tokenA: any(named: "tokenA"),
+ tokenB: any(named: "tokenB"),
+ amountADesired: any(named: "amountADesired"),
+ amountBDesired: any(named: "amountBDesired"),
+ amountAMin: any(named: "amountAMin"),
+ amountBMin: any(named: "amountBMin"),
+ to: any(named: "to"),
+ deadline: any(named: "deadline")),
+ ).thenAnswer((_) async => transactionResponse);
+
+ await sut.sendV2PoolDepositTransaction(
+ currentYield,
+ signer,
+ amount0: BigInt.from(0),
+ amount1: BigInt.from(0),
+ amount0Min: BigInt.from(0),
+ amount1Min: BigInt.from(0),
+ deadline: Duration.zero,
+ );
+
+ verify(
+ () => uniswapV2Router02.fromSigner(contractAddress: currentYield.positionManagerAddress, signer: signer),
+ ).called(1);
+ },
+ );
+
+ test(
+ """When calling `sendV2PoolDepositTransaction` and the pool token0 is native,
+ it should use the `addLiquidityETH` method with the correct params""",
+ () async {
+ withClock(Clock.fixed(DateTime(2027)), () async {
+ when(() => uniswapV2Router02.fromSigner(
+ contractAddress: any(named: "contractAddress"),
+ signer: any(named: "signer"))).thenReturn(uniswapV2Router02Impl);
+
+ when(
+ () => uniswapV2Router02Impl.addLiquidityETH(
+ token: any(named: "token"),
+ amountETHMin: any(named: "amountETHMin"),
+ amountTokenDesired: any(named: "amountTokenDesired"),
+ amountTokenMin: any(named: "amountTokenMin"),
+ ethValue: any(named: "ethValue"),
+ to: any(named: "to"),
+ deadline: any(named: "deadline")),
+ ).thenAnswer((_) async => transactionResponse);
+
+ final amount0 = BigInt.from(12718929112712516);
+ final amount1 = BigInt.from(88627236);
+ final amount0Min = Slippage.halfPercent.calculateMinTokenAmountFromSlippage(amount0);
+ final amount1Min = Slippage.halfPercent.calculateMinTokenAmountFromSlippage(amount1);
+ const deadline = Duration(minutes: 3211);
+ final walletSigner = signer;
+ final signerAddress = await signer.address;
+
+ await sut.sendV2PoolDepositTransaction(
+ currentYield.copyWith(
+ token0: TokenDto.fixture().copyWith(
+ addresses: {currentYield.chainId: EthereumConstants.zeroAddress},
+ ),
+ ),
+ walletSigner,
+ amount0: amount0,
+ amount1: amount1,
+ amount0Min: amount0Min,
+ amount1Min: amount1Min,
+ deadline: deadline,
+ );
+
+ verify(
+ () => uniswapV2Router02Impl.addLiquidityETH(
+ token: currentYield.token1NetworkAddress,
+ amountTokenDesired: amount1,
+ amountTokenMin: amount1Min,
+ amountETHMin: amount0Min,
+ to: signerAddress,
+ deadline: BigInt.from(clock.now().add(deadline).millisecondsSinceEpoch),
+ ethValue: amount0,
+ ),
+ ).called(1);
+ });
+ },
+ );
+
+ test(
+ """When calling `sendV2PoolDepositTransaction` and the pool token1 is native,
+ it should use the `addLiquidityETH` method with the correct params""",
+ () async {
+ withClock(Clock.fixed(DateTime(1998)), () async {
+ when(() => uniswapV2Router02.fromSigner(
+ contractAddress: any(named: "contractAddress"),
+ signer: any(named: "signer"))).thenReturn(uniswapV2Router02Impl);
+
+ when(
+ () => uniswapV2Router02Impl.addLiquidityETH(
+ token: any(named: "token"),
+ amountETHMin: any(named: "amountETHMin"),
+ amountTokenDesired: any(named: "amountTokenDesired"),
+ amountTokenMin: any(named: "amountTokenMin"),
+ ethValue: any(named: "ethValue"),
+ to: any(named: "to"),
+ deadline: any(named: "deadline")),
+ ).thenAnswer((_) async => transactionResponse);
+
+ final amount0 = BigInt.from(12718929112712516);
+ final amount1 = BigInt.from(88627236);
+ final amount0Min = Slippage.halfPercent.calculateMinTokenAmountFromSlippage(amount0);
+ final amount1Min = Slippage.halfPercent.calculateMinTokenAmountFromSlippage(amount1);
+ const deadline = Duration(minutes: 3211);
+ final walletSigner = signer;
+ final signerAddress = await signer.address;
+
+ await sut.sendV2PoolDepositTransaction(
+ currentYield.copyWith(
+ token1: TokenDto.fixture().copyWith(
+ addresses: {currentYield.chainId: EthereumConstants.zeroAddress},
+ ),
+ ),
+ walletSigner,
+ amount0: amount0,
+ amount1: amount1,
+ amount0Min: amount0Min,
+ amount1Min: amount1Min,
+ deadline: deadline,
+ );
+
+ verify(
+ () => uniswapV2Router02Impl.addLiquidityETH(
+ token: currentYield.token0NetworkAddress,
+ amountTokenDesired: amount0,
+ amountTokenMin: amount0Min,
+ amountETHMin: amount1Min,
+ to: signerAddress,
+ deadline: BigInt.from(clock.now().add(deadline).millisecondsSinceEpoch),
+ ethValue: amount1,
+ ),
+ ).called(1);
+ });
+ },
+ );
+
+ test(
+ """When calling `sendV2PoolDepositTransaction` it should correctly
+ call the correct function in the router to deposit with the correct
+ params""",
+ () async {
+ withClock(Clock.fixed(DateTime(1500)), () async {
+ when(() => uniswapV2Router02.fromSigner(
+ contractAddress: any(named: "contractAddress"),
+ signer: any(named: "signer"))).thenReturn(uniswapV2Router02Impl);
+
+ when(
+ () => uniswapV2Router02Impl.addLiquidity(
+ amountADesired: any(named: "amountADesired"),
+ to: any(named: "to"),
+ deadline: any(named: "deadline"),
+ amountAMin: any(named: "amountAMin"),
+ amountBDesired: any(named: "amountBDesired"),
+ amountBMin: any(named: "amountBMin"),
+ tokenA: any(named: "tokenA"),
+ tokenB: any(named: "tokenB"),
+ ),
+ ).thenAnswer((_) async => transactionResponse);
+
+ final amount0 = BigInt.from(12718929112712516);
+ final amount1 = BigInt.from(88627236);
+ final amount0Min = Slippage.halfPercent.calculateMinTokenAmountFromSlippage(amount0);
+ final amount1Min = Slippage.halfPercent.calculateMinTokenAmountFromSlippage(amount1);
+ const deadline = Duration(minutes: 3211);
+ final walletSigner = signer;
+ final signerAddress = await signer.address;
+
+ await sut.sendV2PoolDepositTransaction(
+ currentYield,
+ walletSigner,
+ amount0: amount0,
+ amount1: amount1,
+ amount0Min: amount0Min,
+ amount1Min: amount1Min,
+ deadline: deadline,
+ );
+
+ verify(
+ () => uniswapV2Router02Impl.addLiquidity(
+ amountADesired: amount0,
+ amountAMin: amount0Min,
+ amountBDesired: amount1,
+ amountBMin: amount1Min,
+ tokenA: currentYield.token0NetworkAddress,
+ tokenB: currentYield.token1NetworkAddress,
+ to: signerAddress,
+ deadline: BigInt.from(clock.now().add(deadline).millisecondsSinceEpoch),
+ ),
+ ).called(1);
+ });
+ },
+ );
}
diff --git a/test/core/repositories/yield_repository_test.dart b/test/core/repositories/yield_repository_test.dart
index 2962c19..c4c8980 100644
--- a/test/core/repositories/yield_repository_test.dart
+++ b/test/core/repositories/yield_repository_test.dart
@@ -51,6 +51,7 @@ void main() {
"allowedPoolTypes": [
"V3",
"V4",
+ "V2",
],
}
}),
@@ -89,6 +90,7 @@ void main() {
"minTvlUsd": searchSettings.minLiquidityUSD,
"allowedPoolTypes": [
"V4",
+ "V2",
],
}
}),
@@ -125,7 +127,43 @@ void main() {
}, data: {
"filters": {
"minTvlUsd": searchSettings.minLiquidityUSD,
- "allowedPoolTypes": ["V3"],
+ "allowedPoolTypes": ["V3", "V2"],
+ }
+ }),
+ ).called(1);
+ });
+
+ test("When the V2 Pool is disabled in the search settings, it should not be included in the request", () async {
+ final yields = YieldsDto.fixture();
+
+ when(() => dio.post(any(), queryParameters: any(named: "queryParameters"), data: any(named: "data"))).thenAnswer(
+ (_) async => Response(
+ data: {"bestYields": yields.toJson()},
+ statusCode: 200,
+ requestOptions: RequestOptions(),
+ ),
+ );
+
+ const token0Address = "0x123";
+ const token1Address = "0x456";
+ const network = AppNetworks.sepolia;
+ final searchSettings = PoolSearchSettingsDto.fixture().copyWith(allowV2Search: false);
+
+ await sut.getSingleNetworkYield(
+ token0Address: token0Address,
+ token1Address: token1Address,
+ network: network,
+ searchSettings: searchSettings,
+ );
+
+ verify(
+ () => dio.post("/pools/search/${network.chainId}", queryParameters: {
+ "token0Address": token0Address,
+ "token1Address": token1Address
+ }, data: {
+ "filters": {
+ "minTvlUsd": searchSettings.minLiquidityUSD,
+ "allowedPoolTypes": ["V3", "V4"],
}
}),
).called(1);
diff --git a/test/mocks.dart b/test/mocks.dart
index f167e89..e0a7927 100644
--- a/test/mocks.dart
+++ b/test/mocks.dart
@@ -12,7 +12,10 @@ 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_v2_pool.abi.g.dart';
+import 'package:zup_app/abis/uniswap_v2_router_02.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';
@@ -87,10 +90,22 @@ 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 {}
+class UniswapV2PoolMock extends Mock implements UniswapV2Pool {}
+
+class UniswapV2PoolImplMock extends Mock implements UniswapV2PoolImpl {}
+
+class UniswapV2Router02Mock extends Mock implements UniswapV2Router02 {}
+
+class UniswapV2Router02ImplMock extends Mock implements UniswapV2Router02Impl {}
+
class WalletMock extends Mock implements Wallet {}
class YieldRepositoryMock extends Mock implements YieldRepository {}
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,
];