Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

External Liquidity Providers #13

Merged
merged 207 commits into from
Oct 17, 2024
Merged

External Liquidity Providers #13

merged 207 commits into from
Oct 17, 2024

Conversation

naddison36
Copy link
Collaborator

@naddison36 naddison36 commented Aug 23, 2024

Lido Automated Redemption Manager (ARM)

Upgrades the existing OSwapWEthStEth contract originally deployed for experimenting with AMMs to a new Lido Automated Redemption Manager (ARM) LidoARM implementation.

The new implementation supports:

  • Multiple liquidity providers who are issued a LP token
  • Asynchronous withdrawal queue for liquidity providers
  • A performance fee
  • A cap on the total amount of assets held by the ARM
  • A deposit cap at a liquidity provider level

Existing features include:

  • A buy and sell price
  • Integration to Lido's withdrawal queue to convert stETH back to WETH

Contracts Diagrams

lidoContracts

LidoARM

LidoARMHierarchy

LidoARMSquashed

CapManager

CapManagerHierarchy

CapManagerSquashed

ZapperLidoARM

ZapperLidoARMHierarchy

ZapperLidoARMSquashed

Testnet

The following contracts have been deployed to the Lido ARM 4 Tenderly Testnet

Contrcts

Name Address
LidoARM 0x85B78AcA6Deae198fBF201c82DAF6Ca21942acc6
CapManager 0x3817e247023B4f489352758397040b1fd33b300a
ZapperLidoARM 0xE93C385761D590bDFE35C8dc9910916C17F62649

ABI

Lido ARM

[{"inputs":[{"internalType":"address","name":"_steth","type":"address"},{"internalType":"address","name":"_weth","type":"address"},{"internalType":"address","name":"_lidoWithdrawalQueue","type":"address"},{"internalType":"uint256","name":"_claimDelay","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"allowance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"ERC20InsufficientAllowance","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"ERC20InsufficientBalance","type":"error"},{"inputs":[{"internalType":"address","name":"approver","type":"address"}],"name":"ERC20InvalidApprover","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"ERC20InvalidReceiver","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"ERC20InvalidSender","type":"error"},{"inputs":[{"internalType":"address","name":"spender","type":"address"}],"name":"ERC20InvalidSpender","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[{"internalType":"uint8","name":"bits","type":"uint8"},{"internalType":"int256","name":"value","type":"int256"}],"name":"SafeCastOverflowedIntDowncast","type":"error"},{"inputs":[{"internalType":"int256","name":"value","type":"int256"}],"name":"SafeCastOverflowedIntToUint","type":"error"},{"inputs":[{"internalType":"uint8","name":"bits","type":"uint8"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"SafeCastOverflowedUintDowncast","type":"error"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"SafeCastOverflowedUintToInt","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"previousAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"}],"name":"AdminChanged","type":"event"},{"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":"capManager","type":"address"}],"name":"CapManagerUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256[]","name":"requestIds","type":"uint256[]"}],"name":"ClaimLidoWithdrawals","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"crossPrice","type":"uint256"}],"name":"CrossPriceUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"feeCollector","type":"address"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"FeeCollected","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newFeeCollector","type":"address"}],"name":"FeeCollectorUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"FeeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"}],"name":"OperatorChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"withdrawer","type":"address"},{"indexed":true,"internalType":"uint256","name":"requestId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"}],"name":"RedeemClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"withdrawer","type":"address"},{"indexed":true,"internalType":"uint256","name":"requestId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"queued","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"claimTimestamp","type":"uint256"}],"name":"RedeemRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"requestIds","type":"uint256[]"}],"name":"RequestLidoWithdrawals","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"traderate0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"traderate1","type":"uint256"}],"name":"TraderateChanged","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"},{"inputs":[],"name":"FEE_SCALE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_CROSS_PRICE_DEVIATION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PRICE_SCALE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseAsset","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"capManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claimDelay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"requestIds","type":"uint256[]"}],"name":"claimLidoWithdrawals","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"requestId","type":"uint256"}],"name":"claimRedeem","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"collectFees","outputs":[{"internalType":"uint256","name":"fees","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"convertToAssets","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"convertToShares","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"crossPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"fee","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeCollector","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feesAccrued","outputs":[{"internalType":"uint256","name":"fees","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"address","name":"_operator","type":"address"},{"internalType":"uint256","name":"_fee","type":"uint256"},{"internalType":"address","name":"_feeCollector","type":"address"},{"internalType":"address","name":"_capManager","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lastAvailableAssets","outputs":[{"internalType":"int128","name":"","type":"int128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lidoWithdrawalQueue","outputs":[{"internalType":"contract IStETHWithdrawal","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lidoWithdrawalQueueAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"liquidityAsset","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextWithdrawalIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"operator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"previewDeposit","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"previewRedeem","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"requestLidoWithdrawals","outputs":[{"internalType":"uint256[]","name":"requestIds","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"requestRedeem","outputs":[{"internalType":"uint256","name":"requestId","type":"uint256"},{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_capManager","type":"address"}],"name":"setCapManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newCrossPrice","type":"uint256"}],"name":"setCrossPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_fee","type":"uint256"}],"name":"setFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_feeCollector","type":"address"}],"name":"setFeeCollector","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOperator","type":"address"}],"name":"setOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"setOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"buyT1","type":"uint256"},{"internalType":"uint256","name":"sellT1","type":"uint256"}],"name":"setPrices","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"steth","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","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":"contract IERC20","name":"inToken","type":"address"},{"internalType":"contract IERC20","name":"outToken","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"swapExactTokensForTokens","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":"swapTokensForExactTokens","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"inToken","type":"address"},{"internalType":"contract IERC20","name":"outToken","type":"address"},{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"amountInMax","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"swapTokensForExactTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token0","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token1","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"traderate0","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"traderate1","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"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"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"weth","outputs":[{"internalType":"contract IWETH","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"requestId","type":"uint256"}],"name":"withdrawalRequests","outputs":[{"internalType":"address","name":"withdrawer","type":"address"},{"internalType":"bool","name":"claimed","type":"bool"},{"internalType":"uint40","name":"claimTimestamp","type":"uint40"},{"internalType":"uint128","name":"assets","type":"uint128"},{"internalType":"uint128","name":"queued","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdrawsClaimed","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdrawsQueued","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]

CapManager

[{"inputs":[{"internalType":"address","name":"_arm","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"enabled","type":"bool"}],"name":"AccountCapEnabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"previousAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"}],"name":"AdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"liquidityProvider","type":"address"},{"indexed":false,"internalType":"uint256","name":"cap","type":"uint256"}],"name":"LiquidityProviderCap","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"}],"name":"OperatorChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"cap","type":"uint256"}],"name":"TotalAssetsCap","type":"event"},{"inputs":[],"name":"accountCapEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"arm","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_operator","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"liquidityProvider","type":"address"}],"name":"liquidityProviderCaps","outputs":[{"internalType":"uint256","name":"cap","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"operator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"liquidityProvider","type":"address"},{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"postDepositHook","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_accountCapEnabled","type":"bool"}],"name":"setAccountCapEnabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_liquidityProviders","type":"address[]"},{"internalType":"uint256","name":"cap","type":"uint256"}],"name":"setLiquidityProviderCaps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOperator","type":"address"}],"name":"setOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"setOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint248","name":"_totalAssetsCap","type":"uint248"}],"name":"setTotalAssetsCap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalAssetsCap","outputs":[{"internalType":"uint248","name":"","type":"uint248"}],"stateMutability":"view","type":"function"}]

ZapperLidoARM

[{"inputs":[{"internalType":"address","name":"_weth","type":"address"},{"internalType":"address","name":"_lidoArm","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"previousAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"}],"name":"AdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Zap","type":"event"},{"inputs":[],"name":"deposit","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"lidoArm","outputs":[{"internalType":"contract ILiquidityProviderARM","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"rescueERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"setOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"weth","outputs":[{"internalType":"contract IWETH","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]

Deployment

Tenderly Testnet

# set the TESTNET_URL environment variable in the .env file
make deploy-testnet

Mainnet

The mainnet deploy script is script/deploy/mainnet/003_UpgradeLidoARMScript.sol.
This will update the contract addresses in build/deployments-1.json.

# set the PROVIDER_URL and DEPLOYER_PRIVATE_KEY environment variables in the .env file
make deploy

Deployment Plan

  • Claim all outstanding Lido withdrawal requests
  • Deploy the new contracts
    • CapManager
    • LidoARM
    • ZapperLidoARM
  • Set the CapManager's total asset cap to 740 WETH
  • Deployed contracts verified
  • Raise AMM v1 2/5 multisig tx to
    • transfers out any WETH and stETH from the AMM v1 contract to itself
    • upgrade the existing AMM proxy to the Lido ARM
    • approve the Lido ARM to transfer a tiny amount of WETH
    • Initialize the Lido ARM with
      • token symbol ARM-WETH-stETH and name Lido ARM
      • Operator as the ARM Defender Relayer 0x39878253374355DBcc15C86458F084fb6f2d6DE7
      • 20% performance fee
      • Fee collector as the ARM BuyBack contract
    • set initial prices
    • set new owner as the Mainnet 5/8 multisig 0xbe2AB3d3d8F6a32b96414ebbd865dBD276d3d899
    • deposit an initial 10 WETH for mainnet testing
  • Mainnet testing
    • Operator sets prices so swaps are executed
    • request and claim Lido withdrawals
  • Deposit 100 WETH
  • Deposit the remaining 635 WETH
  • Transfer LP tokens to treasury multisig 0x6E3fddab68Bf1EBaf9daCF9F7907c7Bc0951D1dc

Testing

The following will run all the ARM fork and smoke tests

make test

Testnet using Hardhat tasks

# Treasury converts 10 ETH to WETH
# uncomment the following in the .env file
# IMPERSONATE=0x6e3fddab68bf1ebaf9dacf9f7907c7bc0951d1dc
npx hardhat depositWETH --amount 8 --network testnet
npx hardhat approve --spender 0x85B78AcA6Deae198fBF201c82DAF6Ca21942acc6 --amount 100 --symbol WETH --network testnet

npx hardhat snapLido --network testnet

# Treasury deposit WETH to the Lido ARM
npx hardhat depositLido --amount 8 --network testnet
npx hardhat snapLido --network testnet

npx hardhat snapLido --network testnet

# Set cap for ARM Relayer
# uncomment the following in the .env file
# IMPERSONATE=0x39878253374355DBcc15C86458F084fb6f2d6DE7
npx hardhat setLiquidityProviderCaps --accounts 0x39878253374355DBcc15C86458F084fb6f2d6DE7 --cap 12 --network testnet

npx hardhat depositWETH --amount 8 --network testnet
npx hardhat approve --spender 0x85B78AcA6Deae198fBF201c82DAF6Ca21942acc6 --amount 100 --symbol WETH --network testnet
npx hardhat depositLido --amount 1.5 --network testnet
npx hardhat snapLido --network testnet

# Relayer does some swaps
npx hardhat submitLido --amount 9 --network testnet
npx hardhat approve --spender 0x85B78AcA6Deae198fBF201c82DAF6Ca21942acc6 --amount 100 --symbol STETH --network testnet
npx hardhat swapLido --amount 1 --from stETH --network testnet
npx hardhat snapLido --network testnet
npx hardhat swapLido --amount 2 --from stETH --network testnet
npx hardhat snapLido --network testnet
npx hardhat swapLido --amount 1 --from WETH --network testnet
npx hardhat snapLido --network testnet
npx hardhat swapLido --amount 5 --to WETH --network testnet
npx hardhat snapLido --network testnet
npx hardhat swapLido --amount 3 --to stETH --network testnet
npx hardhat snapLido --network testnet

npx hardhat requestRedeemLido --amount 1 --network testnet
npx hardhat claimRedeemLido --id 0 --network testnet
npx hardhat snapLido --network testnet

npx hardhat collectFees --network testnet

npx hardhat depositWETH --amount 8 --network testnet
npx hardhat depositLido --amount 8 --network testnet
npx hardhat snapLido --network testnet

npx hardhat requestRedeemLido --amount 3 --network testnet
npx hardhat snapLido --network testnet

npx hardhat claimRedeemLido --id 1 --network testnet
npx hardhat snapLido --network testnet

npx hardhat claimRedeemLido --id 2 --network testnet
npx hardhat snapLido --network testnet

npx hardhat requestLidoWithdrawals --amount 2 --network testnet
npx hardhat snapLido --network testnet

npx hardhat depositLido --asset ETH --amount 4 --network testnet
npx hardhat snapLido --network testnet

src/contracts/MultiLP.sol Outdated Show resolved Hide resolved
…est to claimTimestamp

Fixed compilation of contracts using PerformanceFee
Renamed liquidityToken to liquidityAsset
Renamed _assetsInWithdrawalQueue to _externalWithdrawQueue
clement-ux and others added 11 commits October 12, 2024 09:33
* fix: remove zap restricted method..

* style: change liquidity provider param name.

* fix: change state then write call.

* fix: state change then transfer token.

* fix: use `fees` instead of `fee` when collecting fees.

* fix: move up `nextWithdrawalIndex` increase.

* fix: avoid code duplication.

* fix: pack variable action (read & write).
Fixed comment typos
* Set buy and sell prices in initialize function
setCrossPrice also validates buy and sell prices against the new cross price

* fix: use `newCrossPrice` instead of `crossPrice`.

* fix: use `<=` instead of `<` for `setCrossPrice`.

* test: add reverting test for `setCrossPrice`.

* fix: revert commit `a7947f3`.

* fix: move `setCrossPrice` test to new file.

* test: add more test for `setCrossPrice`.

* feat: allow anyone to call claimLidoWithdrawals.

* fix: adjust failing test

* fix: remove testing stuff.

---------

Co-authored-by: Clément <clemmoller@gmail.com>
initial performance fee set to 20%
initial total assets cap set to 400 ether
* Increased rounding in swapTokensForExactTokens to cover 1-2 wei lost with stETH transfers
Added test_SwapTokensForExactTokens_Steth_Transfer_Truncated test
Added test modifiers disableCaps, setPrices and setArmBalances

* Updated fuzz tests for SwapTokensForExactTokens

* Added to SwapExactTokensForTokens fuzz tests

* Added workaround to swapExactTokensForTokens fuzz test

* fix: adjust test structure.

* fix: initialize `accountCapEnabled` to false.

* fix: adjust lastTotalAsset check after swap.

---------

Co-authored-by: Clément <clemmoller@gmail.com>
Copy link

Code

naddison36 and others added 9 commits October 16, 2024 12:38
* chore: add solmate.

* feat: add mock for stETH.

* feat: add mock for Lido Withdraw.

* fix: cleanup variables

* feat: create shared contract for invariant.

* [WIP] feat: add handler logic.

* feat: add more users.

* [WIP] feat: add distribution handler.

* chore: add invariant config

* fix: address null user bug.

* feat: add LP Handler.

* [WIP]: feat: swapHandler.

* feat: add swapTokensForExactTokens to handler.

* fix: adjust handler  weight.

* fix: set default verbosity to 3.

* fix: adjust console log

* feat: add Owner handler.

* fix: account withdraw queue in available liquidity.

* fix: adjust failing tx.

* fix: use vm to send ETH instead of .call.

* feat: add Lido Liquidity Manager Handler.

* feat: check preview return corerct amount.

* fix: use correct price

* fix: adjust new availableTotalAssets

* chore: use --fail-fast for test

* [WIP] feat: add first invariants.

* [WIP] feat: add more invariants.

* [WIP] feat: adjust handler and add more invariants.

* [WIP] feat: add invariant for lido liquidity manager.

* feat: add donation handler.

* test: add new concrete scenario test.

* test: fix CI.

* try to fix CI.

* test[invariant]: ensure enough liquiidty before claiming fees.

* chore: add --show-progress for `make test`

* test[invariant]: adjust `withdrawsClaimable` removal.

* fix: use aproxEq instead of Eq due to rounding error

* chore: remove --show-progress

* test[invariant]: only request when there is enough liquidity

* test[invariant]: add more invariant.

* fix: remove wrong invariant.

* fix: adjust with latest update.

* test: adjust with lastest update.

* test[invariant]: use owner to set price.

* chore: add --show-progress for tests.

* fix: use new claimDelay.

* docs: adjust comments.

* test[invariant]: add setCrossPrice to handlers.

* test[invariant]: adjust test.

* forge fmt

* feat: add stats for invariants.

* chore: ignore warning in Proxy.sol.

* fix: adjust with new variable name.

* test[invariant]: adjust swap handler with latest update.

* fix: import console.

* fix: adjust claimResquest for invariant.

* test[invariant]: WIP.

* test[invariant]: add LLM to invariant.

* test[invariant]: adjust owner handler.

* perf: optimize calls for logs.

* perf: adjust % for call distribution.

* feat: add skip %.

* test[invariant]: adjust last invariant.

* fix: log stat bool.

* test[invariant]: up only shares values invariant.

* fix: adjust type uint120 to uint128

* fix: prevetn setCrossPrice to revert.

* feat: remove all user funds after invariants.

* test[invariant]: add approx up-only invariant [WIP]

* test[invariant]: add invariant for approx up-only.

* docs: adjust natspec and description.

* forge fmt

* fix: increase error tolerance.
* fix: remove etherscan setting from config.

* chore: update forge version.

* chore: exclude invariant from classic test.
@naddison36 naddison36 merged commit f506939 into main Oct 17, 2024
1 of 2 checks passed
@naddison36 naddison36 deleted the nicka/lp branch October 17, 2024 21:29
// Wrap all the received ETH to WETH.
weth.deposit{value: etherAfter}();

emit ClaimLidoWithdrawals(requestIds);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 We have the amount out in the RequestLidoWithdrawals event. It would seem symmetrical to also have it in the ClaimLidoWithdrawals. Plus it allows tracking the lidoWithdrawalQueueAmount via only events.

// Store the updated queued amount which reserves liquidity assets (WETH) in the withdrawal queue
withdrawsQueued = queued;

uint40 claimTimestamp = uint40(block.timestamp + claimDelay);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟣 Not a safecast. Somewhere around the year 34,963, this will roll over. As it is, it will allow for instant redeems during that ten minute time. With a safecast, it would block new claim requests for that ten minutes, before allowing them request and collect again as normal.

Anyone with an old claims from before the roll over is going to be an LP for another couple wànsuì one way or the other, regardless of what this does.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants