Swapping between LSDs and ETH

The following section details how to swap LSD to LSD, ETH to LSD, or LSD to ETH using unshETH's vdAMM.

We assume that inputAmount (in wei units), inputLSDAddress, outputLSDAddress correspond to the respective input amount, contract address of the LSD being deposited, and contract address of the LSD being received.

In the case that the user is swapping from ETH to a LSD, then the inputLSDAddress should be WETH: 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2.

In the case that the user is swapping from a LSD to ETH, then the outputLSDAddress should be WETH: 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2.

Required Contracts & Interfaces

LSDVault: 0x51A80238B5738725128d3a3e06Ab41c1d4C05C74

//solidity
interface ILSDVault {
  function swapperAddress() external view returns (address);
}
//ethers.js
const ILSDVaultABI = [
  'function swapperAddress() external view returns (address)',
  'function supportedLSDs(uint256 index) external view returns (address)',
]
const LSDVaultAddress = '0x51A80238B5738725128d3a3e06Ab41c1d4C05C74'
const LSDVaultContract = new ethers.Contract(LSDVaultAddress, ILSDVaultABI, provider)

vdAMM

//solidity
address vdaAMMAddress = ILSDVault(LSDVaultAddress).swapperAddress();
//ethers.js
const vdAMMAddress = await LSDVaultContract.swapperAddress()

The vdAMM address is currently:

0x35636b85b68c1b4a216110fb3a5fb447a99db14a.

However this may change in the future, and so it should be dynamically called from the LSDVault contract.

//solidity
interface IVdAmm {
   function getDepositFee(uint256 lsdAmountIn, address lsd) external returns(uint256, uint256);
   function swapLsdToLsdCalcs( uint256 amountIn, address lsdIn, address lsdOut) public view returns (uint256, uint256, uint256, uint256);
   function swapLsdToLsd( uint256 amountIn, address lsdIn, address lsdOut, uint256 minAmountOut) external returns (uint256, uint256, uint256);
   function swapLsdToEth( uint256 amountIn, address lsdIn, uint256 minAmountOut) external returns (uint256, uint256, uint256);
   function swapEthToLsd( address lsdOut, uint256 minAmountOut) external payable returns (uint256, uint256, uint256);
}
//ethers.js
const IVdAmmABI = [
  'function getDepositFee(uint256 lsdAmountIn, address lsd) external returns (uint256, uint256)',
  'function swapLsdToLsdCalcs(uint256 amountIn, address lsdIn, address lsdOut) public view returns (uint256, uint256, uint256, uint256)',
  'function swapLsdToLsd(uint256 amountIn, address lsdIn, address lsdOut, uint256 minAmountOut) external returns (uint256, uint256, uint256)',
  'function swapLsdToEth(uint256 amountIn, address lsdIn, uint256 minAmountOut) external returns (uint256, uint256, uint256)',
  'function swapEthToLsd(address lsdOut, uint256 minAmountOut) external payable returns (uint256, uint256, uint256)',
]
const vdAmmContract = new ethers.Contract(vdAMMAddress, IVdAmmABI, provider)

Supported LSDs

// solidity
uint256 i = 0;
address lsdAddress;
while (true) {
  lsdAddress = ILSDVault(LSDVaultAddress).supportedLSDs(i);
  if (lsdAddress == address(0)) {
    break;
  }
  // Process the LSD address here
  i++;
}
// ethers.js
let i = 0
let lsdAddress
while (true) {
  lsdAddress = await LSDVaultContract.supportedLSDs(i)
  if (lsdAddress === '0x0000000000000000000000000000000000000000') {
    break
  }
  console.log(`LSD Address ${i}: ${lsdAddress}`)
  i++
}

Currently supported LSDs are:

sfrxETH: 0xac3e018457b222d93114458476f3e3416abbe38f

rETH: 0xae78736cd615f374d3085123a210448e74fc6393

wstETH: 0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0

cbETH: 0xbe9895146f7af43049ca1c1ae358b0541ea49704

WETH: 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2

ankrETH: 0xE95A203B1a91a908F9B9CE46459d101078c2c3cb

swETH: 0xf951E335afb289353dc249e82926178EaC7DEd78

More LSDs will be added in the future.

Price Discovery

1. Get the Output Amount (in terms of the input LSD or ETH)

//solidity
(uint256 outputAmount, _ , _ , _)  = IVdAmm(vdAMMAddress).swapLsdToLsdCalcs(inputAmount, inputLSDAddress, outputLSDAddress);
//ethers.js
const [outputAmount] = await vdAmmContract.swapLsdToLsdCalcs(inputAmount, inputLSDAddress, outputLSDAddress)

Submitting the Transaction

1. Approve the LSD to be spent by the unshETHZap (only if input is a LSD).

//solidity
IERC20(inputLSDAddress).approve(vdAMMAddress, inputAmount);
//ethers.js
const IERC20ABI = ['function approve(address spender, uint256 amount) external returns (bool)']
const LSDTokenContract = new ethers.Contract(inputLSDAddress, IERC20ABI, provider)
await LSDTokenContract.approve(vdAMMAddress, inputAmount)

2. Set the Minimum Expected Amount Out

//solidity
uint256 slippage_tolerance  = 10;
// A 10 bps tolerance should be enough to take into consideration the differentiation between the time of calculation of the output amount and the actual output amount that will be calculated during the transaction.
uint256 minAmountOut = outputAmount*(10000 - slippage_tolerance)/10000;
//ethers.js
const slippageTolerance = 10
// A 10 bps tolerance should be enough to take into consideration the differentiation between the time of calculation of the output amount and the actual output amount that will be calculated during the transaction.
const minAmountOut = outputAmount.mul(10000 - slippageTolerance).div(10000)

3. Call the respective swap function

a) Swap LSD to LSD

//solidity
IVdAmm(vdAMMAddress).swapLsdToLsd(inputAmount, inputLSDAddress, outputLSDAddress, minAmountOut);
//ethers.js
await vdAmmContract.swapLsdToLsd(inputAmount, inputLSDAddress, outputLSDAddress, minAmountOut)

b) Swap LSD to ETH

//solidity
IVdAmm(vdAMMAddress).swapLsdToEth(inputAmount, inputLSDAddress, minAmountOut);
//ethers.js
await vdAmmContract.swapLsdToEth(inputAmount, inputLSDAddress, minAmountOut)

c) Swap ETH to LSD

//solidity
IVdAmm(vdAMMAddress).swapEthToLsd{value:inputAmount}(ouptutLSDAddress, minAmountOut);
//ethers.js
await vdAmmContract.swapEthToLsd(outputLSDAddress, minAmountOut, {
  value: inputAmount,
})