Morpho: Vault Rebalancer
Weekly check: if a Morpho USDC vault on Base offers better APY than your current vault (configurable threshold), automatically withdraws and redeposits into the better one. Sends a summary.
Trigger: Cronjob → Actions: Analyze Vault APYs, For Each, Morpho Vault Deposit, Format Rebalance Summary, Format Status Report, Morpho Get Best Apy, Morpho Vault Get Position, If, Notification, Notification, Morpho Vault Withdraw
Morpho: Vault Rebalancer
Created by
B3 Team
1.
Root
2.
Get Best Apy
3.
Check Positions
5.
Analyze Vault APYs
6.
If Rebalance
About
Weekly check: if a Morpho USDC vault on Base offers better APY than your current vault (configurable threshold), automatically withdraws and redeposits into the better one. Sends a summary.
Preview
Analyze Vault APYs
Action
Code:function transform(input) { var iterations = input.iterations || []; var bestVault = input.bestVault; var allVaults = input.allVaults || []; var thresholdPct = input.threshold != null ? Number(input.threshold) : 2; var thresholdDec = thresholdPct / 100; var currentVault = null; var currentShares = '0'; var currentFormattedAssets = '0'; for (var i = 0; i < iterations.length; i++) { var pos = iterations[i].get_position; if (pos && pos.assets && pos.assets !== '0') { currentVault = allVaults[i]; currentShares = pos.shares; currentFormattedAssets = pos.formattedAssets; break; } } if (!currentVault) { return { action: 'no_position', bestVaultName: bestVault.name, bestApy: (bestVault.state.netApy * 100).toFixed(2), thresholdPct: thresholdPct }; } var currentApy = currentVault.state.netApy; var bestApy = bestVault.state.netApy; var diff = bestApy - currentApy; if (diff >= thresholdDec && bestVault.address.toLowerCase() !== currentVault.address.toLowerCase()) { return { action: 'rebalance', currentVaultAddress: currentVault.address, currentVaultName: currentVault.name, currentApy: (currentApy * 100).toFixed(2), withdrawShares: currentShares, currentAssets: currentFormattedAssets, depositVaultAddress: bestVault.address, depositVaultName: bestVault.name, bestApy: (bestApy * 100).toFixed(2), diffPct: (diff * 100).toFixed(2), thresholdPct: thresholdPct }; } return { action: 'hold', currentVaultName: currentVault.name, currentApy: (currentApy * 100).toFixed(2), currentAssets: currentFormattedAssets, bestVaultName: bestVault.name, bestApy: (bestApy * 100).toFixed(2), diffPct: (diff * 100).toFixed(2), thresholdPct: thresholdPct }; }
Input:{"allVaults":"{{get_best_apy.result.allVaults}}","bestVault":"{{get_best_apy.result.bestVault}}","iterations":"{{check_positions.result.iterations}}","threshold":"{{$props.apy_threshold}}"}
check_positions
Action
Array:get_best_apy:allVaults
deposit
Action
Amount:withdraw:estimatedAssetsReceived
Chain:
Base
BaseToken:
0x8335...2913
Vault:Analyze Vault APYs:output.depositVaultAddress
Format Rebalance Summary
Action
Code:function transform(input) { var lines = []; lines.push('<b>Morpho Vault Rebalanced</b>'); lines.push(''); lines.push('From: <b>' + input.currentVaultName + '</b> (' + input.currentApy + '% APY)'); lines.push('To: <b>' + input.depositVaultName + '</b> (' + input.bestApy + '% APY)'); lines.push('APY gain: +' + input.diffPct + '%'); lines.push('Amount: ' + input.assetsFormatted + ' USDC'); lines.push(''); lines.push('<a href="https://basescan.org/tx/' + input.withdrawTx + '">Withdraw tx</a>'); lines.push('<a href="https://basescan.org/tx/' + input.depositTx + '">Deposit tx</a>'); return lines.join('\n'); }
Input:{"assetsFormatted":"{{withdraw.result.estimatedAssetsReceivedFormatted}}","bestApy":"{{analyze.result.output.bestApy}}","currentApy":"{{analyze.result.output.currentApy}}","currentVaultName":"{{analyze.result.output.currentVaultName}}","depositTx":"{{deposit.result.transactionHash}}","depositVaultName":"{{analyze.result.output.depositVaultName}}","diffPct":"{{analyze.result.output.diffPct}}","withdrawTx":"{{withdraw.result.transactionHash}}"}
Format Status Report
Action
Code:function transform(input) { var a = input.analyze; var lines = []; lines.push('<b>Morpho Weekly Check</b>'); lines.push(''); if (a.action === 'no_position') { lines.push('No active position found.'); lines.push('Best vault: <b>' + a.bestVaultName + '</b> (' + a.bestApy + '% APY)'); } else { lines.push('Current: <b>' + a.currentVaultName + '</b> (' + a.currentApy + '% APY)'); lines.push('Best: <b>' + a.bestVaultName + '</b> (' + a.bestApy + '% APY)'); lines.push('Difference: ' + a.diffPct + '% (threshold: ' + a.thresholdPct + '%)'); lines.push('Holding ' + a.currentAssets + ' USDC'); lines.push(''); lines.push('No rebalance needed.'); } return lines.join('\n'); }
Input:{"analyze":"{{analyze.result.output}}"}
get_best_apy
Action
Chain:
Base
BaseMin Tvl Usd:1000000
Token:
0x8335...2913
get_position
Action
Account Address:Prop: accountAddress
Chain:
Base
BaseVault:Item: address
if_rebalance
Action
Condition:{"$and":[{"{{analyze.result.output.action}}":{"$eq":"rebalance"}}]}
notify_rebalance
Message configured
{{format_rebalance.result.output}}
notify_status
Message configured
{{format_status.result.output}}
root
Action
Schedule:
{{$props.schedule}}
withdraw
Action
Amount:Analyze Vault APYs:output.withdrawShares
Chain:
Base
BaseVault:Analyze Vault APYs:output.currentVaultAddress
Withdraw Type:shares
Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.
Loading chat...