# User chooses a base asset IPOR User selects the asset that would be zapped into IPOR Liquidity mining. ## Querry available assets by IPOR front end * Front-end needs an API that it can inquire about the assets available for swapping # Front end makes request to Enso backend Data that would be provided by the front end * Base asset * Asset to zap into (ie. USDC, USDT, DAI, ETH/stETH) * ratio between IPOR and asset. Ratio is provided by the IPOR front-end in absolute terms - not dollar value. Ie 1:1 would be 1 IPOR for 1 USDC # Enso backend prepares transaction: Repo: `https://github.com/IPOR-Labs/ipor-protocol/tree/audit-v2` 1. Swap Base asset needs to be swapped to 2 assets: selected liquidity token (ETH, USDC, USDT, DAI) and IPOR token. IPOR token currently trades on Uniswap V2 and V3. --- 2. Provide liquidty to IPOR Liquidity Pool **IAmmPoolsService.sol** Function separate for each asset: `provideLiquidityUsdt` `provideLiquidityUsdc` `provideLiquidityDai` `provideLiquidityStEth` `provideLiquidityWEth` `provideLiquidityEth` ``` function provideLiquidityUsdt( address beneficiary, uint256 assetAmount ) external; ``` By providing liqudity user/smart contract receives ipToken (ipUSDC, ipUSDT etc.) --- 3. Stake the liquidity token (`ipToken` ) on behalf of the user into the staking module: **IPowerTokenStakeService.sol** Function ``` function stakeLpTokensToLiquidityMining( address beneficiary, address[] calldata lpTokens, uint256[] calldata lpTokenAmounts ) external; ``` --- 4. Stake IPOR for pwIPOR and delegate to ipToken staked in a previous step **IPowerTokenStakeService.sol** ``` function stakeGovernanceTokenToPowerTokenAndDelegate( address beneficiary, uint256 governanceTokenAmount, address[] calldata lpTokens, uint256[] calldata pwTokenAmounts ) external; ``` # Additional information ## stETH Pool One particular pool allows providing multiple assets as liquidity: stETH pool. Any of the following assets can be deposited directly into the pool: stETH -> is deposited without conversion ETH -> is staked for stETH by the contract. **THIS FUNCTION CAN NOT BE USED WITH OTHER TRANSACTIONS SUCH AS STKING OF IPOR.** USE stETH or wETH instead wETH -> is unwrapped and staked for stETH by the contract there are 3 separate functions to provide liquidty `provideLiquidityStEth` `provideLiquidityWEth` `provideLiquidityEth` Hence `stETH` pool can be treated as 3 separate pools. However, as stated above `provideLiquidityEth` has limitations and should not be used in conjunction with other function calls such as staking of IPOR. Transaction will be reverted ## Production addresses { "IporProtocolRouterProxy": "0x16d104009964e694761C0bf09d7Be49B7E3C26fd", "LiquidityMiningProxy": "0xCC3Fc4C9Ba7f8b8aA433Bc586D390A70560FF366", "IporToken": "0x1e4746dC744503b53b4A082cB3607B169a289090", "PowerTokenProxy": "0xD72915B95c37ae1B16B926f85ad61ccA6395409F", "DAI": "0x6B175474E89094C44Da98b954EedeAC495271d0F", "USDC": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "USDT": "0xdAC17F958D2ee523a2206206994597C13D831ec7", "wETH": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" "stETH": "0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84", "ipDAI": "0x8537b194BFf354c4738E9F3C81d67E3371DaDAf8", "ipUSDC": "0x7c0e72f431FD69560D951e4C04A4de3657621a88", "ipUSDT": "0x9Bd2177027edEE300DC9F1fb88F24DB6e5e1edC6", "ipstETH": "0xc40431b6C510AeB45Fbb5e21E40D49F12b0c1F0c", } ## Goerli testnet deployed contracts (relevant ones, all verified) ``` { "IporProtocolRouterProxy": "0x88C9fc28f9D92E4d746900A6Ddbb5F030a6525EF", "LiquidityMiningProxy": "0x45E765d2ebC502e199e916be2e9e921CE46B9579", "IporToken": "0x26B33950EEa29B6661D27534F3aBe9a4fe9B1842", "PowerTokenProxy": "0x9b29EB016A7Ac6e79E6930bDBAa788D8dFE7e845", "DAI": "0x2bAb54316C0585c66f3f091B2ae3Ab3296Ba0Fc3", "USDC": "0xb5bE2fc62eD5d0a79678424Fd1f396111C6e6526", "USDT": "0x242AA4858284e53D8B657E12A610fb5F03043CfA", "ipDAI": "0xA09dDA1D33A815DbbAaF626C9F08e6C4878a6f35", "ipUSDC": "0x2934A24e14A77cB46034e35e72E0EA4cAE45D460", "ipUSDT": "0xDF4479a1B2706De02A978B7Ea9cc8025c3F466e7", "ipstETH": "0x4964A8143aAaf24fed3962730470d49788941595", "sDAI": "0xfbAa3144c9b34885612c74f75380b75628e3B2f4", "stETH": "0xF12aE0992b9Bc74A06f7DaC648be8f740a6d94E3", "stkAAVE": "0x0000000000000000000000000000000000000000", "wETH": "0x92958F20835636De2b5d797eA186D04e9Bf8BFb3" } ``` ## Redeem liquidity test example ``` // SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.20; import "forge-std/Test.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "../contracts/interfaces/IAmmPoolsService.sol"; contract G is Test { address user = 0x9642f499eAd3c9e345d5B4a56B8D7b357E7c9337; // user with ipDai and ipUsdc address iporRouterProxy = 0xD201B7d1d5a3A95cC8484B9d285063b54E5d2054; address ipDai = 0xb3F75634445dD307215AB58f2Da76d3d9C4FfA10; address ipUsdc = 0xA4417460B2B52DD49807Dcee39A0e9104a452f18; address usdc = 0x0e6c016417A0108b76E35939EE7F8F922a4Ef638; function setUp() public { vm.createSelectFork("https://eth-goerli.g.alchemy.com/v2/<ADD Key>", 9535496); } function testRedeemIpDai() public { // read balance of ipDai uint balanceIpDai = IERC20(ipDai).balanceOf(user); console2.log("balanceIpDai", balanceIpDai); // redeem half of ipDai vm.prank(user); IAmmPoolsService(iporRouterProxy).redeemFromAmmPoolDai(user, balanceIpDai / 2); // read balance of ipDai after redeem uint balanceIpDaiAfterRedeem = IERC20(ipDai).balanceOf(user); console2.log("balanceIpDaiAfterRedeem", balanceIpDaiAfterRedeem); } function testRedeemIpUsdc() public { // read balance of ipUsdc uint balanceIpUsdc = IERC20(ipUsdc).balanceOf(user); console2.log("balanceIpUsdc", balanceIpUsdc); // redeem half of ipUsdc vm.prank(user); IAmmPoolsService(iporRouterProxy).redeemFromAmmPoolUsdc(user, balanceIpUsdc / 2); // read balance of ipUsdc after redeem uint balanceIpUsdcAfterRedeem = IERC20(ipUsdc).balanceOf(user); console2.log("balanceIpUsdcAfterRedeem", balanceIpUsdcAfterRedeem); } function testProvideUsdcAndRedeemUSDC() public { // perpetrate new address for interact with protocol address user2 = vm.rememberKey(2345); deal(usdc, user2, 1_000e6); vm.prank(user2); IERC20(usdc).approve(iporRouterProxy, 1_000e6); // provide liquidity vm.prank(user2); IAmmPoolsService(iporRouterProxy).provideLiquidityUsdc(user2, 200e6); // read balance of ipUsdc uint balanceIpUsdc = IERC20(ipUsdc).balanceOf(user2); console2.log("balanceIpUsdc", balanceIpUsdc); // redeem all ipUsdc vm.prank(user2); IAmmPoolsService(iporRouterProxy).redeemFromAmmPoolUsdc(user2, balanceIpUsdc); // read balance of ipUsdc after redeem uint balanceIpUsdcAfterRedeem = IERC20(ipUsdc).balanceOf(user2); console2.log("balanceIpUsdcAfterRedeem", balanceIpUsdcAfterRedeem); } } ``` The test can be found here: `https://github.com/IPOR-Labs/ipor-protocol/tree/goerli-fork-test` ``` $ npm i $forge build ``` In file `test/G.t.sol` enter alchemy key (line 16) ``` $ forge test --match-path "test/G.t.sol" -vvv ```