---
# System prepended metadata

title: Untitled

---

# 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
```
