# EVC For On-chain Management: Migrate a Leveraged Position from Silo to Euler

Euler's *Ethereum Vault Connector* (**EVC**) framework can be a powerful tool for managing on chain positions. The EVC, essentially, acts as an authenticated multicall contract. Allowing several operations to be batched while maintaining an awareness of the execution context, which in turn allows it to provide a flexible environment for financial operations.

The EVC offers several benefits for on chain trading including; standardized authentication for operator contracts to act on behalf of user accounts and built in sub accounts for segregating a user's EOA into multiple liability positions.

One of its most powerful features for scripting on chain trades is it's `batch` mechanism which allows several operations to be combined into a single batch call that will execute atomically. Making it much easier to create a more streamlined and gas efficient execution script and guaranteeing consistency when structuring complex positions. For lending vaults that are EVC aware and are implemented via the EVCs standardized liquidity constraints, it also provides a **checks deferred execution context** for batched operations.

This means that a trader can perform any number of complex operations on an account and as long the account is eventually solvent, the transaction will be valid. This eliminates the common necessity of calling a standardized flashloan and instead provides flash liquidity access out of the box.

The common, manual procedure of **"looping"** a defi lending position can be scripted away using an EVC batch by simply taking the maximum loan upfront, swapping into the collateral asset, and depositing into a vault thus satisfying the solvency checks at the end of the batch.

Below I describe how EVC batching can be used to migrate a position from one lending protocol (one which is not EVC aware ) onto an Euler vault based lending market in a streamlined, all solidity script, without using a flashloan.

Let's say we have an open leveraged position of PT-wstkscusd on silo with a liability of USDC.e and we would like to transfer this open position to the equivalent lending market on Euler without unwinding the position and reopening it and without access to extra capital.

We simply need to pay off our silo USDC.e debt, withdraw the PT-wstkscusd collateral from silo and deposit it into Euler. Thanks to the EVC deferred checks mechanism we can source the liquidity for this migration by taking our Euler loan upfront, satisfying our silo liabilities and eventually becoming solvent when we move the collateral over.

Let's Jump into the code and go over some of the finer points of integrating with these protocols.

First we will need to import the contract interfaces that we will be interacting with

```solidity
// EvcShift.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import { ISilo } from "silo-core/contracts/interfaces/ISilo.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "evc/interfaces/IEthereumVaultConnector.sol";
import { EVault } from "evk/EVault/EVault.sol";
import { EVCUtil } from "evc/utils/EVCUtil.sol";
```

Than to setup our special purpose contract for executing the migration we will inherit EVCUtil, here we only need to define the canonical EVC contract address deployed by Euler and effective for the lending market we are operating on:

```solidity
contract EvcShift {
    constructor(address _evc) EVCUtil(_evc) {}
}
```

Next we need to define three utility functions for our operation. A view function to read our repayment and withdrawal amounts from `silo`, an approval function to provide an allowance to the Euler deposit vault for our collateral, and a function that executes our silo repayment on behalf of the borrower. The `repaySiloAndWithdraw` will be called from inside our batch, since an external call from the evc to a contract that is not EVC aware, sees the `EVC` address as `msg.sender` in order that we may pay the debt from funds held in this contract, we write a special purpose function so that silo's `transferFrom` call see's our contract as `msg.sender`

```solidity
    function repaySiloAndWithdraw(
        address _silo,
        address _collateralSilo,
        address _borrower,
        uint256 _amount,
        uint256 _maxWithdraw
    ) external {
        IERC20(ISilo(_silo).asset()).approve(address(_silo), _amount);
        ISilo(_silo).repay(_amount, _borrower);
        ISilo(_collateralSilo).withdraw(_maxWithdraw, address(this), _borrower, ISilo.CollateralType.Protected);
    }
    function getMigrationAmounts(
        address _repaySilo,
        address _collateralSilo,
        address _collateralShare,
        address _borrower
    ) internal view returns (uint256, uint256) {
        uint256 _amount = ISilo(_repaySilo).maxRepay(_borrower);
        uint256 _maxWithdraw = ISilo(_collateralSilo).convertToAssets(
            IERC20(_collateralShare).balanceOf(address(_borrower)),
            ISilo.AssetType.Protected
        );
        return (_amount, _maxWithdraw);
    }
    function approveDepositVault(
        address _collateralAsset,
        address _depositVault,
        uint256 _amount
    ) internal {
        IERC20(_collateralAsset).approve(address(_depositVault), _amount);
    }
```

Now, we can implement a function that executes our entire batch. First we provide as arguments all of the variables we will need for the entire operation. Note, we provide `_collateralShare` address as the protected collateral share token from the silo collateral vault since, protected (non-borrowable) assets on silo have a special status and share token address. The share token is held by our `_borrower` EOA and are used to secure the solvency of our account with regard to our debt. In order to allow withdrawal of the assets these protected shares represent by our contract, our EOA simply needs to execute and ERC20 approval on the protected share contract providing an allowance to our special purpose `EvcShift` contract prior to executing our `migrateLoan` function.

Since our contract will be able to execute a withdraw for the silo assets, we will want to ensure that only the borrower can call this function. We will also enforce that the `_e_account` Euler sub account is in fact a sub account of the borrower to prevent any mistakes.

Also, note that we provide `_e_account` argument, this is the address of our Euler subaccount of our EOA to which we want to migrate the position. We will also derive this prior to executing the `migrateLoan` function.

We will execute both of these actions in the script we write to deploy and execute this transaction. Additionally, we will approve our `evcShift` contract as an operator in the `evc` context to act on behalf of our `_e_account`. This will all be done in solidity using Forge's scripting.

Further, in our `migrateLoan` function we have used our utility functions to get the borrowers max repayment and withdraw amounts from the `Silo` vaults and have preemptively issued an allowance for the Euler `_depositVault` to eventually transfer our collateral assets allowing for deposit.

```solidity
    function migrateLoan(
        address _repaySilo,
        address _collateralSilo,
        address _borrowToken,
        address _collateralShare,
        address _borrower,
        address _e_account,
        address _depositVault,
        address _borrowVault
    ) public {
		require(msg.sender == _borrower, "Only borrower can call this function");
		require(_haveCommonOwner(_borrower, _e_account), "_e_account must be subaccount");
        (uint256 _amount, uint256 _maxWithdraw) = getMigrationAmounts(_repaySilo, _collateralSilo, _collateralShare, _borrower);
        approveDepositVault(ISilo(_collateralSilo).asset(), _depositVault, _maxWithdraw);
    }
```

Now we are ready to define our batch of operations to securely migrate this position. This is also in our `migrateLoan` function. First we declare a list of our `BatchItems`, we will use 7 operations in this batch.

```solidity
        IEVC.BatchItem[] memory items = new IEVC.BatchItem[](6);
```

Now in order to take a loan or deposit into Euler we need to set both vaults as collateral and our borrowing vault as a controller. This allows the EVC to seize our assets in the event of a solvency violation. We can see that we encode the transaction data according to the ABI. However, for these operations we are calling the EVC directly and thus must set the `onBehalfOfAccount` as `address(0)` this is because the will use a delegate call, preserving the `msg.sender` and authenticating the caller (our `evcShift` contract) as an operator for the `_e_account` for which we are enabling.

```solidity
        items[0] = IEVC.BatchItem({
            onBehalfOfAccount: address(0),
            targetContract: address(evc),
            value: 0,
            data: abi.encodeWithSelector(IEVC.enableCollateral.selector, _e_account, _borrowVault)
        });
        items[1] = IEVC.BatchItem({
            onBehalfOfAccount: address(0),
            targetContract: address(evc),
            value: 0,
            data: abi.encodeWithSelector(IEVC.enableCollateral.selector, _e_account, _depositVault)
        });
        items[2] = IEVC.BatchItem({
            onBehalfOfAccount: address(0),
            targetContract: address(evc),
            value: 0,
            data: abi.encodeWithSelector(IEVC.enableController.selector, _e_account, _borrowVault)
        });
```

Once the vaults are enabled, we can take a borrow out of the `_borrowVault` on behalf of our `_e_account`. We have taken this loan against the `_e_account` with the receiver of the funds being our `EvcShift` contract. Since the `_borrowVault` is EVC aware it authenticates that `EvcShift` contract we are calling from is authorized to act on behalf of our `_e_account`.

```solidity
        items[3] = IEVC.BatchItem({
            onBehalfOfAccount: _e_account,
            targetContract: _borrowVault,
            value: 0,
            data: abi.encodeWithSelector(EVault.borrow.selector, _amount, address(this))
        });
```

Once we have the USDC.e to pay off our loan we can now call our `repaySiloAndWithdraw` function, paying off the debt of `_borrower` since anyone can repay anyone else's debt in silo, no special permissions are needed here. The `repaySilAndWithdraw` function will also call withdraw on the collateral silo. Now, there should be no liability in the `silo` vault and we can now withdraw the collateral asset's into our `EvcShift` contract. Recall that we would need to have already granted this authority to our `EvcShift` contract by having our `_borrower` EOA approve an allowance.

```solidity
        items[4] = IEVC.BatchItem({
            onBehalfOfAccount: address(this),
            targetContract: address(this),
            value: 0,
            data: abi.encodeWithSelector(this.repaySiloAndWithdraw.selector, _repaySilo, _collateralSilo, _borrower, _amount, _maxWithdraw)
```

Finally, to prevent all the previous operations from reverting, we deposit our collateral into the Euler `_depositVault` to satisfy our solvency requirements created by the loan. Since the collateral assets were withdrawn into our `EvcShift` contract we call this function on behalf of the `EvcShift` itself with the `_e_account` as the receiver.

```solidity
        items[5] = IEVC.BatchItem({
            onBehalfOfAccount: address(this),
            targetContract: _depositVault,
            value: 0,
            data: abi.encodeWithSelector(
                EVault.deposit.selector,
                _maxWithdraw,
                _e_account
            )
        });
```

Now that we have structured all the operations in our batch we can execute it through the `evc` contract and have the `EvcShift` contract release its operational authority over our `_e_account` for safety

```solidity
        evc.batch(items);
        evc.setAccountOperator(_e_account, address(this), false);
```

Now we can write our deploy and execution script. Start by importing the necessary contracts

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {Script, console} from "forge-std/Script.sol";

import {EvcShift} from "../src/EvcShift.sol";
import {ISiloConfig} from "silo-core/contracts/interfaces/ISiloConfig.sol";
import {ISilo} from "silo-core/contracts/interfaces/ISilo.sol";
import {SonicLib} from "../test/common/SonicLib.sol";
import "evc/interfaces/IEthereumVaultConnector.sol";
import { EVault } from "evk/EVault/EVault.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
```

Next, we set up the script, we will also add a utility function to derive our subaccount according to the EVC context:

```solidity
contract EvcShiftScript is Script {
    EvcShift public evcShift;
    address borrower;
    ISiloConfig siloConfig;
    address ptUSDCSilo;
    address usdcSilo;
	
    function getSubaccount(address _account, uint256 _index) public returns (address) {
        return address(uint160(uint160(_account)^_index));
    }
}
```

Next we define our `run` function, start by getting our silo config data:

```solidity
    function run() public {
        borrower = msg.sender;
        address subaccount = getSubaccount(borrower, 1);
        siloConfig = ISiloConfig(SonicLib.Silo_Config_Address_wstkscUSDC);
        (ptUSDCSilo, usdcSilo) = siloConfig.getSilos();
}
```

We are now ready to start the broadcasting portion of our script. First we deploy our migration contract, than we approve the contract to withdraw our `silo` collateral and set it as an operator on our sub account:

```solidity
        vm.startBroadcast();
        evcShift = new EvcShift(SonicLib.Euler_Vault_Connector);
        ISilo(SonicLib.Silo_Address_ProtectedSHare_PT).approve(address(evcShift), UINT256_MAX);
        IEVC(SonicLib.Euler_Vault_Connector).setAccountOperator(subaccount, address(evcShift), true);
        bool isAuthorized = IEVC(SonicLib.Euler_Vault_Connector).isAccountOperatorAuthorized(subaccount, address(evcShift));
        console.log("isAuthorized: ", isAuthorized);
```

At this point, we can execute the migrations function.

```solidity
        evcShift.migrateLoan(
            usdcSilo,
            ptUSDCSilo,
            address(ISilo(usdcSilo).asset()),
            SonicLib.Silo_Address_ProtectedSHare_PT,
            borrower,
            subaccount, // EVC subaccount
            address(SonicLib.Euler_Deposit_Vault),
            address(SonicLib.Euler_Borrow_Vault)
        );
```

Once that is complete. We will want to revoke all approval on silo just in case and ensure the operator released authorization.

```solidity
        ISilo(SonicLib.Silo_Address_ProtectedSHare_PT).approve(address(evcShift), 0);
        console.log("Silo Allowance: ", ISilo(SonicLib.Silo_Address_ProtectedSHare_PT).allowance(address(evcShift), address(SonicLib.Silo_Address_ProtectedSHare_PT)));

        isAuthorized = IEVC(SonicLib.Euler_Vault_Connector).isAccountOperatorAuthorized(subaccount, address(evcShift));
        console.log("isAuthorized: ", isAuthorized);
        if (isAuthorized) revert();
        vm.stopBroadcast();
```

We can than print everything out to ensure consistency.

```solidity
        console.log("maxRepayment USDC Silo: ", ISilo(usdcSilo).maxRepay(borrower));
        console.log("maxWithdraw PTUSDC Silo: ", ISilo(ptUSDCSilo).maxWithdraw(borrower, ISilo.CollateralType.Protected));

        console.log("Assets Stuck in Shift: ", IERC20(address(ISilo(ptUSDCSilo).asset())).balanceOf(address(evcShift)));
        console.log("Debt of subaccount: ", EVault(SonicLib.Euler_Borrow_Vault).debtOf(subaccount));
        console.log("Collateral of subaccount: ", EVault(SonicLib.Euler_Deposit_Vault).balanceOf(subaccount));
```

We can than run our script with fork to test it is working. `forge script script/EvcShift.s.sol --fork-url $RPC_URL --sender $BORROWER_EOA`

Full Code:

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import { ISilo } from "silo-core/contracts/interfaces/ISilo.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "evc/interfaces/IEthereumVaultConnector.sol";
import { EVault } from "evk/EVault/EVault.sol";
import { EVCUtil } from "evc/utils/EVCUtil.sol";

contract EvcShift is EVCUtil {
    constructor(address _evc) EVCUtil(_evc) {}

    function repaySiloAndWithdraw(
        address _silo,
        address _collateralSilo,
        address _borrower,
        uint256 _amount,
        uint256 _maxWithdraw
    ) external {
        IERC20(ISilo(_silo).asset()).approve(address(_silo), _amount);
        ISilo(_silo).repay(_amount, _borrower);
        ISilo(_collateralSilo).withdraw(_maxWithdraw, address(this), _borrower, ISilo.CollateralType.Protected);
    }
    function getMigrationAmounts(
        address _repaySilo,
        address _collateralSilo,
        address _collateralShare,
        address _borrower
    ) internal view returns (uint256, uint256) {
        uint256 _amount = ISilo(_repaySilo).maxRepay(_borrower);
        uint256 _maxWithdraw = ISilo(_collateralSilo).convertToAssets(
            IERC20(_collateralShare).balanceOf(address(_borrower)),
            ISilo.AssetType.Protected
        );
        return (_amount, _maxWithdraw);
    }
    function approveDepositVault(
        address _collateralAsset,
        address _depositVault,
        uint256 _amount
    ) internal {
        IERC20(_collateralAsset).approve(address(_depositVault), _amount);
    }
    function migrateLoan(
        address _repaySilo,
        address _collateralSilo,
        address _borrowToken,
        address _collateralShare,
        address _borrower,
        address _e_account,
        address _depositVault,
        address _borrowVault
    ) public {
        require(msg.sender == _borrower, "Only borrower can call this function");   
        require(_haveCommonOwner(_borrower, _e_account), "Borrower and EVC account must have a common owner");

        (uint256 _amount, uint256 _maxWithdraw) = getMigrationAmounts(_repaySilo, _collateralSilo, _collateralShare, _borrower);
        approveDepositVault(ISilo(_collateralSilo).asset(), _depositVault, _maxWithdraw);

        IEVC.BatchItem[] memory items = new IEVC.BatchItem[](6);
        items[0] = IEVC.BatchItem({
            onBehalfOfAccount: address(0),
            targetContract: address(evc),
            value: 0,
            data: abi.encodeWithSelector(IEVC.enableCollateral.selector, _e_account, _borrowVault)
        });
        items[1] = IEVC.BatchItem({
            onBehalfOfAccount: address(0),
            targetContract: address(evc),
            value: 0,
            data: abi.encodeWithSelector(IEVC.enableCollateral.selector, _e_account, _depositVault)
        });
        items[2] = IEVC.BatchItem({
            onBehalfOfAccount: address(0),
            targetContract: address(evc),
            value: 0,
            data: abi.encodeWithSelector(IEVC.enableController.selector, _e_account, _borrowVault)
        });
        items[3] = IEVC.BatchItem({
            onBehalfOfAccount: _e_account,
            targetContract: _borrowVault,
            value: 0,
            data: abi.encodeWithSelector(EVault.borrow.selector, _amount, address(this))
        });
        items[4] = IEVC.BatchItem({
            onBehalfOfAccount: address(this),
            targetContract: address(this),
            value: 0,
            data: abi.encodeWithSelector(this.repaySiloAndWithdraw.selector, _repaySilo, _collateralSilo, _borrower, _amount, _maxWithdraw)
        });
        items[5] = IEVC.BatchItem({
            onBehalfOfAccount: address(this),
            targetContract: _depositVault,
            value: 0,
            data: abi.encodeWithSelector(
                EVault.deposit.selector,
                _maxWithdraw,
                _e_account
            )
        });
        evc.batch(items);
        evc.setAccountOperator(_e_account, address(this), false);
    }
}
```

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {Script, console} from "forge-std/Script.sol";

import {EvcShift} from "../src/EvcShift.sol";
import {ISiloConfig} from "silo-core/contracts/interfaces/ISiloConfig.sol";
import {ISilo} from "silo-core/contracts/interfaces/ISilo.sol";
import {SonicLib} from "../test/common/SonicLib.sol";
import "evc/interfaces/IEthereumVaultConnector.sol";
import { EVault } from "evk/EVault/EVault.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

contract EvcShiftScript is Script {
    EvcShift public evcShift;
    address borrower;
    ISiloConfig siloConfig;
    address ptUSDCSilo;
    address usdcSilo;
    // function setUp() public {}
    function getSubaccount(address _account, uint256 _index) public returns (address) {
        return address(uint160(uint160(_account)^_index));
    }
    function run() public {
        borrower = msg.sender;
        console.log("Borrower: ", borrower);
        address subaccount = getSubaccount(borrower, 1);
        siloConfig = ISiloConfig(SonicLib.Silo_Config_Address_wstkscUSDC);
        (ptUSDCSilo, usdcSilo) = siloConfig.getSilos();
        uint256 maxRepayment = ISilo(usdcSilo).maxRepay(borrower);

        vm.startBroadcast();
        evcShift = new EvcShift(SonicLib.Euler_Vault_Connector);
        ISilo(SonicLib.Silo_Address_ProtectedSHare_PT).approve(address(evcShift), UINT256_MAX);
        IEVC(SonicLib.Euler_Vault_Connector).setAccountOperator(subaccount, address(evcShift), true);
        bool isAuthorized = IEVC(SonicLib.Euler_Vault_Connector).isAccountOperatorAuthorized(subaccount, address(evcShift));
        console.log("isAuthorized: ", isAuthorized);

        evcShift.migrateLoan(
            usdcSilo,
            ptUSDCSilo,
            address(ISilo(usdcSilo).asset()),
            SonicLib.Silo_Address_ProtectedSHare_PT,
            borrower,
            subaccount, // EVC subaccount
            address(SonicLib.Euler_Deposit_Vault),
            address(SonicLib.Euler_Borrow_Vault)
        );

        ISilo(SonicLib.Silo_Address_ProtectedSHare_PT).approve(address(evcShift), 0);
        console.log("Silo Allowance: ", ISilo(SonicLib.Silo_Address_ProtectedSHare_PT).allowance(address(evcShift), address(SonicLib.Silo_Address_ProtectedSHare_PT)));
        isAuthorized = IEVC(SonicLib.Euler_Vault_Connector).isAccountOperatorAuthorized(subaccount, address(evcShift));
        console.log("isAuthorized: ", isAuthorized);
        if (isAuthorized) revert();
        vm.stopBroadcast();

        console.log("maxRepayment USDC Silo: ", ISilo(usdcSilo).maxRepay(borrower));
        console.log("maxWithdraw PTUSDC Silo: ", ISilo(ptUSDCSilo).maxWithdraw(borrower, ISilo.CollateralType.Protected));

        console.log("Assets Stuck in Shift: ", IERC20(address(ISilo(ptUSDCSilo).asset())).balanceOf(address(evcShift)));
        console.log("Debt of subaccount: ", EVault(SonicLib.Euler_Borrow_Vault).debtOf(subaccount));
        console.log("Collateral of subaccount: ", EVault(SonicLib.Euler_Deposit_Vault).balanceOf(subaccount));
        
    }
}
```
