YearnRouter4626Ext
Intro
The purpose of the YearnRouter4626Ext contract is to improve the UX of the protocol by removing approval or deposit transactions at different steps. For example, instead of users acquiring coveYFI, approving it, then staking it, they can simply use the YearnRouter4626Ext contract to batch the steps into 1 transaction. For this purpose, the router is permissionless and stateless. Furthermore Permit2 can be used for reducing needed approvals if the token does not support Permit.
Permit availability
- ❌ YFI
- ✅ coveYFI
- ✅ Curve v2 factory LP token
- ✅ Yearn Vault v2
- ✅ Yearn Vault v3
- ❌ Yearn Gauge
- ✅ Cove Yearn Strategy (TokenizedStrategy)
- ✅ Cove Reward Gauge (Auto compounding gauge, non-autocompounding gauge, coveYFI rewards gauge)
- ❌ StakeDAO Gauge
How to build multicall for users
The general structure of the multicall will look like this
(Optional) User TX #0: inputToken.approve(PERMIT2, MAX_UINT_256)
User TX #1:
router.multicall()
with below array as the calldata:
[
(Optional) router.selfPermit()
router.pullToken()
or router.pullTokenWithPermit2()
,
(Optional) router.approve()
,
router.deposit()
/ router.redeemFromRouter
,
(Optional) router.approve()
,
router.deposit()
/ router.redeemFromRouter
,
]
Router will pull the input token from the user, and deposit it for itself for intermediary vaults, and deposit for user for the last vault, transferring desired vault shares to the user.
- Determine if the input token supports Permit. If not use permit2
- Case if the input token supports Permit:
- Request signature from the user wallet for Permit transfer. Use the EXACT amount the user wishes to use, not the max amount, as this could allow malicious actors to use the approval in the future.
- Use this signature as a param for
router.selfPermit()
call, build the calldata, and add to multicall() param array - Build the
router.pullToken()
calldata, and add to the multicall() param array
- Case if the input token does NOT support Permit, use Permit2:
- Check if the user has approved Permit2
asset.allowance(address(user), address(PERMIT2)
- If not, request user to send an approval TX with the max amount so Permit2 can be used
asset.approve(address(PERMIT2), type(uint256).max)
- Once the user has approved Permit2, build the
router.pullTokenWithPermit2()
calldata and add to the multicall() param array
- Check if the user has approved Permit2
- Case if the input token supports Permit:
- Calculate expected shares minted / assets withdrawn via
preview*
functions- Build an array of input to output tokens as an array of addresses
- Utilize
previewDeposit
orpreviewRedeem
for calculating expected shares minted at each deposit step or expected assets received at each redeems. - Note that if the path contains a Yearn Vault v2, the estimation may not be accurate
- Check if the router has enough approvals for each deposit step
- For each step, check if allowance is enough
asset.allowance(address(router), address(vault))
- If not, include
router.approve(asset, vault, MAX_UINT256)
calls before each deposits so that the Router is able to deposit successfully - (Note that this is a one-time approval for the first user to deposit into the vault via the router. We should not include unnecessary approvals for the users after)
- For redeeming/withdrawing, where the input token is the vault, no approval is necessary
- Simulate the
router.multicall()
with the params and check that expected return data at each step is correct.
Multicall call data examples
Deposits
LPToken → yVaultV2 → yvGauge → CoveYearnStrat → RewardsGauge
Alice wishes to deposit 1e18 of her curve YFI/ETH LP token to cove’s auto-compounding gauge.
Alice is the first user to use this path.
Assume the previewDeposit
call showed:
1e18 LP token → 0.9e18 Yearn Vault V2 → 0.9e18 Yearn Gauge → 0.8e18 CoveYearnStrategy → 0.8e18 AutoCompoundingRewardsGauge
Frontend requests Permit signature for transferring the 1e18 LP token from Alice to the router and generates v, r, s.
Ben hears about this opportunity and wants to replicate what Alice did but with 2e18 LP token.
Assume the previewDeposit
call showed:
1e18 LP token → 0.9e18 Yearn Vault V2 → 0.9e18 Yearn Gauge → 0.8e18 CoveYearnStrategy → 0.8e18 AutoCompoundingRewardsGauge
Frontend requests Permit signature for transferring the 2e18 LP token from Ben to the router and generates v, r, s.
yvGauge → CoveYearnStrat → RewardsGauge
Alice already holds 0.9e18 yearn gauge tokens and wants to earn COVE token on top.
stakeDaoGauge → yVaultV2 → yvGauge → CoveYearnStrat → RewardsGauge
Alice already holds 0.9e18 stakeDaoGauge tokens and wants to move to Cove’s auto compounding gauge.
This is a special case where we must combine redeem and deposit calls together
stakeDaoGauge → yVaultV2 → yvGauge → NonCompoundingRewardsGauge
Alice already holds 0.9e18 stakeDaoGauge tokens and wants to move to cove’s non-auto compounding gauge.
This is a special case where we must combine redeem and deposit calls together