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