<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[High Yield Development]]></title><description><![CDATA[High Yield Development]]></description><link>https://yielddev.io</link><generator>RSS for Node</generator><lastBuildDate>Sat, 11 Apr 2026 10:46:19 GMT</lastBuildDate><atom:link href="https://yielddev.io/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Deleverage & Unhedge PT Long Position]]></title><description><![CDATA[Now that we have implemented our opening and closing scripts for our leveraged, delta neutral PT Long position and have written several useful utilities. Creating new scripts will be dead simple.
Let's start by deleveraging our position. In the event...]]></description><link>https://yielddev.io/deleverage-and-unhedge-pt-long-position</link><guid isPermaLink="true">https://yielddev.io/deleverage-and-unhedge-pt-long-position</guid><category><![CDATA[defi]]></category><category><![CDATA[defi development company]]></category><category><![CDATA[yield farming]]></category><category><![CDATA[euler-finance]]></category><category><![CDATA[Ethereum]]></category><category><![CDATA[Solidity]]></category><category><![CDATA[pendle]]></category><category><![CDATA[EVC]]></category><category><![CDATA[Blockchain]]></category><category><![CDATA[Web3]]></category><dc:creator><![CDATA[Yield Dev]]></dc:creator><pubDate>Wed, 09 Apr 2025 07:00:36 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1743729857851/4044733b-e48c-48ff-bad2-3f1a755eae2e.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Now that we have implemented our opening and closing scripts for our leveraged, delta neutral PT Long position and have written several useful utilities. Creating new scripts will be dead simple.</p>
<p>Let's start by deleveraging our position. In the event that wS price rises, while our position's USD equity will remain neutral, our collateralization ratio will approach liquidation. Thus we need a way to reduce our overall size in the position to make sure our starting capital remains large enough relative to the position size to collateralize the position.</p>
<p>Do do these we simply need to execute a swap of some portion of our <code>HEDGED_TOKEN</code> for the <code>LIABILITY_TOKEN</code> and use it to pay down our debt. Our exposure will remain neutral but our overall loan to value ratio will be reduced.</p>
<p>We can see that our previously written utility for this pattern <code>batchSwapAndRepay</code> will make implementing this a breeze. We just need to input the proper parameters in the context of reducing our leverage.</p>
<p>First setup our script to define the proper vaults associated with this trade, the same as the <a target="_blank" href="https://yielddev.io/in-depth-implementing-a-delta-neutral-long-pt-trade?ref=twitter-share">previous posts</a></p>
<pre><code class="lang-solidity"><span class="hljs-comment">// delverage.s.sol</span>

<span class="hljs-comment">// SPDX-License-Identifier: UNLICENSED</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.28;</span>

<span class="hljs-keyword">import</span> <span class="hljs-string">"./common/EScript.s.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">SonicLib</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./common/SonicLib.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">DeleverageScript</span> <span class="hljs-keyword">is</span> <span class="hljs-title">EScript</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">run</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        borrower <span class="hljs-operator">=</span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>;
        e_account <span class="hljs-operator">=</span> getSubaccount(borrower, <span class="hljs-number">2</span>);
        evc <span class="hljs-operator">=</span> IEVC(SonicLib.EVC);

        <span class="hljs-keyword">address</span> LIABILITY_VAULT <span class="hljs-operator">=</span> SonicLib.EULER_WS_VAULT;
        <span class="hljs-keyword">address</span> HEDGED_VAULT <span class="hljs-operator">=</span> SonicLib.EULER_PT_STS_VAULT;
        <span class="hljs-keyword">address</span> COLLATERAL_VAULT <span class="hljs-operator">=</span> SonicLib.EULER_USDC_VAULT;
        <span class="hljs-keyword">address</span> LIABILITY_TOKEN <span class="hljs-operator">=</span> SonicLib.WS;
        <span class="hljs-keyword">address</span> HEDGED_TOKEN <span class="hljs-operator">=</span> SonicLib.PT_STS;
        <span class="hljs-keyword">address</span> COLLATERAL_TOKEN <span class="hljs-operator">=</span> SonicLib.USDC;

        <span class="hljs-keyword">uint256</span> deleverage_amount <span class="hljs-operator">=</span> assetsBalance(HEDGED_VAULT) <span class="hljs-operator">/</span> <span class="hljs-number">4</span>;

        (<span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> swapJson, <span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> verifyJson) <span class="hljs-operator">=</span> getRoutingData(
            HEDGED_VAULT, LIABILITY_VAULT, HEDGED_TOKEN, LIABILITY_TOKEN, deleverage_amount
        );

        broadcastBatch(batchSwapAndRepay(
            HEDGED_VAULT, LIABILITY_VAULT, deleverage_amount, swapJson, verifyJson)
        );

        logPositionInfo(COLLATERAL_VAULT, HEDGED_VAULT, LIABILITY_VAULT);

    }
}
</code></pre>
<p>Now we calculate our <code>deleverage_amount</code> as the fraction of our <code>HEDGED_TOKEN</code> that we want to liquidate. In this example we will reduce our size by 1/4th (25%)</p>
<p>Now, we just need to fetch our routing payload, noting that we are swapping the <code>HEDGED_TOKEN</code> PT-stS into the <code>LIABILITY_TOKEN</code> wS, with the <code>LIABILITY_VAULT</code> as the ultimate destination for the output tokens.</p>
<p>Once, we have that payload our <code>batchSwapAndRepay</code> batch array will organize the operations to withdraw the PT-stS, execute the swap and repay the debt in the <code>LIABILITY_VAULT</code>.</p>
<p>Finally, we can see our balances show that our USD collateral remain the same and our <code>HEDGED_TOKEN</code> balance and <code>LIABILITY_TOKEN</code> debt are reduced by 1/4.</p>
<h2 id="heading-unhedge">Unhedge</h2>
<p>Just as easily as we deleveraged the position, we can <em>unhedge</em> it. We can gain exposure to the price movements of the underlying wS by simply swapping the <code>COLLATERALTOKEN</code> which we used as our hedge into the <code>HEDGED_TOKEN</code> giving us excess exposure to the <code>HEDGE_TOKEN</code> relative to our <code>LIABILITY_TOKEN</code> debt amount.</p>
<p>Again, we setup our script the same a before with all of the appropriate vaults labeled exactly the same.</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// undehge.s.sol</span>
<span class="hljs-comment">// SPDX-License-Identifier: UNLICENSED</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.28;</span>

<span class="hljs-keyword">import</span> <span class="hljs-string">"./common/EScript.s.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">SonicLib</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./common/SonicLib.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">IERC20</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">UnhedgeScript</span> <span class="hljs-keyword">is</span> <span class="hljs-title">EScript</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">run</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        borrower <span class="hljs-operator">=</span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>;
        e_account <span class="hljs-operator">=</span> getSubaccount(borrower, <span class="hljs-number">2</span>);
        evc <span class="hljs-operator">=</span> IEVC(SonicLib.EVC);

        <span class="hljs-keyword">address</span> LIABILITY_VAULT <span class="hljs-operator">=</span> SonicLib.EULER_WS_VAULT;
        <span class="hljs-keyword">address</span> HEDGED_VAULT <span class="hljs-operator">=</span> SonicLib.EULER_PT_STS_VAULT;
        <span class="hljs-keyword">address</span> COLLATERAL_VAULT <span class="hljs-operator">=</span> SonicLib.EULER_USDC_VAULT;
        <span class="hljs-keyword">address</span> LIABILITY_TOKEN <span class="hljs-operator">=</span> SonicLib.WS;
        <span class="hljs-keyword">address</span> HEDGED_TOKEN <span class="hljs-operator">=</span> SonicLib.PT_STS;
        <span class="hljs-keyword">address</span> COLLATERAL_TOKEN <span class="hljs-operator">=</span> SonicLib.USDC;

        <span class="hljs-keyword">uint256</span> collateral_unhedge_amount <span class="hljs-operator">=</span> assetsBalance(COLLATERAL_VAULT);

        (<span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> swapJson, <span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> verifyJson) <span class="hljs-operator">=</span> getRoutingData(
            COLLATERAL_VAULT, HEDGED_VAULT, COLLATERAL_TOKEN, HEDGED_TOKEN, collateral_unhedge_amount
        );

        broadcastBatch(batchWithdrawAndSwap(
            COLLATERAL_VAULT, collateral_unhedge_amount, swapJson, verifyJson)
        );

        logPositionInfo(COLLATERAL_VAULT, HEDGED_VAULT, LIABILITY_VAULT);
    }
}
</code></pre>
<p>In this example, we set the <code>collateral_unhedge_amount</code> to the total amount of our <code>COLLATERAL_VAULT</code> balance thus completely unhedging our position and gaining wS exposure equivalent to our starting capital.</p>
<p>We can also easily fetch the payload data, with the <code>COLLATERAL_TOKEN</code> (USDC) as the input and the <code>HEDGED_TOKEN</code> as the output with <code>HEDGED_VAULT</code> as the destination.</p>
<p>Once, we have the swap data, we can broadcast a batch constructed with the <code>batchWithdrawAndSwap</code> pattern using the <code>COLLATERAL_VAULT</code> as the <code>_input_vault</code>.</p>
<p>Just like that, we have converted the position to maximize our exposure to the underlying wS price movements. Here we can see how our script framework makes constructing new position's and managing existing one's a breeze, giving us almost infinite flexibility.</p>
<p>High yield defi engineering is available on demand at <a target="_blank" href="https://yielddev.com/">YieldDev Studio</a></p>
<p>Feel free to reach out on —&gt; <a target="_blank" href="https://x.com/yielddev">x for anything, technical or otherwise</a></p>
<p>Full code described above can be found on <a target="_blank" href="https://github.com/yielddev/AdvancedYieldFarming">github</a></p>
]]></content:encoded></item><item><title><![CDATA[In-Depth: Closing a Delta Neutral PT Leveraged Long]]></title><description><![CDATA[Since we have an open position in PT-stS / wS collateralized with USDC, as described in our previous post, we may want to construct some additional scripts in order to manage this position. This will be rather straight forward, since we have previous...]]></description><link>https://yielddev.io/in-depth-closing-a-delta-neutral-pt-leveraged-long</link><guid isPermaLink="true">https://yielddev.io/in-depth-closing-a-delta-neutral-pt-leveraged-long</guid><category><![CDATA[Solidity]]></category><category><![CDATA[Ethereum]]></category><category><![CDATA[pendle]]></category><category><![CDATA[euler]]></category><category><![CDATA[euler-finance]]></category><category><![CDATA[EVC]]></category><category><![CDATA[yield farming]]></category><category><![CDATA[defi]]></category><category><![CDATA[defi development company]]></category><dc:creator><![CDATA[Yield Dev]]></dc:creator><pubDate>Mon, 07 Apr 2025 07:00:38 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1743728922939/8bba0fd0-302a-4ca0-bdec-889b88c2027e.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Since we have an open position in PT-stS / wS collateralized with USDC, as described in our <a target="_blank" href="https://yielddev.io/in-depth-implementing-a-delta-neutral-long-pt-trade">previous post</a>, we may want to construct some additional scripts in order to manage this position. This will be rather straight forward, since we have previously implemented a scripting framework for this position. With a couple of modifications, we can build out scripts for three scenarios.</p>
<p>First, fully close out the position into USDC by selling our hedged asset PT-stS for wS, paying off the entire outstanding debt and then swapping the remaining wS for USDC.</p>
<p>Second, deleverage the position by selling off a portion of our PT-stS hedged asset to pay off our wS liability thus increasing our collateralization ratio.</p>
<p>Lastly, We will create a script to unhedge our position, swapping our USDC collateral for more of the PT-stS hedged asset thus increasing our net exposure to the wS price.</p>
<p>To start out, we create a new file <code>close.s.sol</code> and create our <code>CloseScript</code> contract. We being by setting up our trade parameters exactly the same as in our open script.</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// SPDX-License-Identifier: UNLICENSED</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.28;</span>

<span class="hljs-keyword">import</span> <span class="hljs-string">"./common/EScript.s.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">SonicLib</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./common/SonicLib.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">CloseScript</span> <span class="hljs-keyword">is</span> <span class="hljs-title">EScript</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">run</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        borrower <span class="hljs-operator">=</span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>;
        e_account <span class="hljs-operator">=</span> getSubaccount(borrower, <span class="hljs-number">0</span>);
        evc <span class="hljs-operator">=</span> IEVC(SonicLib.EVC);

        <span class="hljs-keyword">address</span> LIABILITY_VAULT <span class="hljs-operator">=</span> SonicLib.EULER_WS_VAULT;
        <span class="hljs-keyword">address</span> HEDGED_VAULT <span class="hljs-operator">=</span> SonicLib.EULER_PT_STS_VAULT;
        <span class="hljs-keyword">address</span> COLLATERAL_VAULT <span class="hljs-operator">=</span> SonicLib.EULER_USDC_VAULT;
        <span class="hljs-keyword">address</span> LIABILITY_TOKEN <span class="hljs-operator">=</span> SonicLib.WS;
        <span class="hljs-keyword">address</span> HEDGED_TOKEN <span class="hljs-operator">=</span> SonicLib.PT_STS;
        <span class="hljs-keyword">address</span> COLLATERAL_TOKEN <span class="hljs-operator">=</span> SonicLib.USDC;
    }
}
</code></pre>
<p>Next, we are going to continue simplifying our script framework by generalizing some functions that will be reusable across all of our scripts.</p>
<p>The first instance of this is getting out <code>euler-orderflow-router</code> swap data. Since we will constantly be getting payloads to execute an <code>exactIn</code> swap for our various assets and write them to json payloads for use in our script, we can create a utility function for reuse and readability.</p>
<p>For this reason, let's jump back to our <code>EScript.s.sol</code> file and implement a generalize getter function.</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// Escript.s.sol</span>

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getRoutingData</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> _inputVault,
        <span class="hljs-keyword">address</span> _outputVault,
        <span class="hljs-keyword">address</span> _inputToken,
        <span class="hljs-keyword">address</span> _outputToken,
        <span class="hljs-keyword">uint256</span> _amount
    </span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> _swapJson, <span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> _verifyJson</span>) </span>{
        requestPayload(_inputVault, _outputVault, _inputToken, _outputToken, _amount);
        _swapJson <span class="hljs-operator">=</span> getJsonFile(<span class="hljs-string">"./script/payloads/swapData.json"</span>);
        _verifyJson <span class="hljs-operator">=</span> getJsonFile(<span class="hljs-string">"./script/payloads/verifyData.json"</span>);
    }
</code></pre>
<p>Here, we define our parameters for the swap and request a payload using our previously defined <code>requestPayload</code> function. Since this function writes the payload data to a json file, we can just read these two files to memory and return them.</p>
<p>This allows us to simplify retrieving router data in our script.</p>
<pre><code class="lang-solidity">
<span class="hljs-comment">// close.s.sol</span>
        <span class="hljs-keyword">uint256</span> hedged_balance <span class="hljs-operator">=</span> assetsBalance(HEDGED_VAULT);
        (<span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> swapJson, <span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> verifyJson) <span class="hljs-operator">=</span> getRoutingData(
            HEDGED_VAULT, LIABILITY_VAULT, HEDGED_TOKEN, LIABILITY_TOKEN, hedged_balance
        );
</code></pre>
<p>Since, our goal is to close the position. We have first retrieved our <code>hedged_balance</code> the amount of the <code>HEDGE_TOKEN</code> PT-stS which we have deposited as collateral. We will be swapping this entire amount for the <code>LIABILITY_TOKEN</code> wS. Thus we pass these parameters to the <code>getRoutingData</code> function. We have passed the <code>LIABILITY_VAULT</code> as the <code>_outputVault</code> parameter since this will be the destination of our swap's output assets which we will claim as a deposit via the <code>verifyJson</code> payload execution. We will then be able to execute repayWithShares on the <code>LIABILITY_VAULT</code> to pay down our debt.</p>
<p>Now that we have our swap data taken care of, we can construct the batch to execute. We will need to make two additional operation along with our two swap operations. First, we will need to define a batch item to withdraw the <code>HEDGED_TOKEN</code> to the swapper address. Secondly, we will need an operation at the end of the batch to repay the debt with our new purchased <code>LIABILITY_TOKEN</code>.</p>
<p>Since these are, once again, common operations in position management, we can create some utility functions for returning these <code>IEVC.BatchItem</code> structs in our <code>EScript.s.sol</code> file.</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// Escript.s.sol</span>

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">batchWithdrawTo</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _vault, <span class="hljs-keyword">uint256</span> _amount, <span class="hljs-keyword">address</span> _to</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params">IEVC.BatchItem <span class="hljs-keyword">memory</span></span>) </span>{
        <span class="hljs-keyword">return</span> IEVC.BatchItem({
            onBehalfOfAccount: e_account,
            targetContract: _vault,
            <span class="hljs-built_in">value</span>: <span class="hljs-number">0</span>,
            data: <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodeWithSelector</span>(EVault.withdraw.<span class="hljs-built_in">selector</span>, _amount, _to, e_account)
        });
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">batchRepayWithShares</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _vault, <span class="hljs-keyword">uint256</span> _amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params">IEVC.BatchItem <span class="hljs-keyword">memory</span></span>) </span>{
        <span class="hljs-keyword">return</span> IEVC.BatchItem({
            onBehalfOfAccount: e_account,
            targetContract: _vault,
            <span class="hljs-built_in">value</span>: <span class="hljs-number">0</span>,
            data: <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodeWithSelector</span>(EVault.repayWithShares.<span class="hljs-built_in">selector</span>, _amount, e_account)
        });
    }
</code></pre>
<p>Here we define <code>batchWithdrawTo</code> which returns a batch item for withdrawing assets from a given vault in a given amount to a given recipient (the <code>swapperAddress</code> in this use case). We also define <code>batchRepayWithShares</code> which repays the debt of our e_account in a given vault with the <code>e_accounts</code> own deposit shares.</p>
<p>We can now construct a batch to execute this withdraw, swap and repay. Once again, this is a common pattern for on chain position management. Thus we can create another general function for returning this entire batch of <code>BatchItems</code> as we may want to use this pattern in many scenarios where it makes sense to repay debt by swapping a collateral, whether we are deleveraging or closing out a position.</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// EScript.s.sol</span>

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">batchSwapAndRepay</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> _inputVault,
        <span class="hljs-keyword">address</span> _repayVault,
        <span class="hljs-keyword">uint256</span> _amountIn,
        <span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> _swapJson,
        <span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> _verifyJson
    </span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params">IEVC.BatchItem[] <span class="hljs-keyword">memory</span> items</span>) </span>{
        items <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> IEVC.BatchItem[](<span class="hljs-number">4</span>);
        items[<span class="hljs-number">0</span>] <span class="hljs-operator">=</span> batchWithdrawTo(_inputVault, _amountIn, vm.parseJsonAddress(_swapJson, <span class="hljs-string">".swapperAddress"</span>));
        items[<span class="hljs-number">1</span>] <span class="hljs-operator">=</span> batchPayload(vm.parseJsonAddress(_swapJson, <span class="hljs-string">".swapperAddress"</span>), vm.parseJsonBytes(_swapJson, <span class="hljs-string">".swapperData"</span>));
        items[<span class="hljs-number">2</span>] <span class="hljs-operator">=</span> batchPayload(vm.parseJsonAddress(_verifyJson, <span class="hljs-string">".verifierAddress"</span>), vm.parseJsonBytes(_verifyJson, <span class="hljs-string">".verifierData"</span>));
        items[<span class="hljs-number">3</span>] <span class="hljs-operator">=</span> batchRepayWithShares(_repayVault, <span class="hljs-keyword">type</span>(<span class="hljs-keyword">uint256</span>).<span class="hljs-built_in">max</span>);
    }
</code></pre>
<p>Here, we have generalized this pattern with <code>batchSwapAndRepay</code>. We can easily construct a batch which withdraws the amount of assets from the <code>_inputVault</code> swaps and skims them according to the json payloads provided and then repays the maximum amount of debt repayable by the amount of deposit shares available or the amount of debt outstanding. This function then returns the entire batch as an array of batch items, ready for execution.</p>
<p>Once again, we encounter a functionality we will constantly be calling with out scripts, that of actually executing the batch.</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// EScript.s.sol</span>

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">broadcastBatch</span>(<span class="hljs-params">IEVC.BatchItem[] <span class="hljs-keyword">memory</span> _items</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        vm.startBroadcast();
        evc.batch(_items);
        vm.stopBroadcast();
    }
</code></pre>
<p>Here we simplify our function call for executing an array of <code>BatchItems</code> via the <code>EVC</code> inside of the vm's broadcast context. All we need to do is pass it our batch array.</p>
<p>Finally displaying the balances of our vaults will be useful for ensuring that the output of the scripts execution matches our expectations when we run the script in a fork. So, we add a logging function for our vault balances.</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// EScript.s.sol</span>

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">logPositionInfo</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> _collateralVault,
        <span class="hljs-keyword">address</span> _hedgedVault,
        <span class="hljs-keyword">address</span> _liabilityVault
    </span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> </span>{
        console.log(<span class="hljs-string">"--------------------------------"</span>);
        console.log(<span class="hljs-string">"COLLATERAL VAULT: "</span>, assetsBalance(_collateralVault));
        console.log(<span class="hljs-string">"HEDGED VAULT: "</span>,assetsBalance(_hedgedVault));
        <span class="hljs-keyword">uint256</span> debt <span class="hljs-operator">=</span> debtBalance(_liabilityVault);
        <span class="hljs-keyword">if</span> (debt <span class="hljs-operator">&gt;</span> <span class="hljs-number">0</span>) {
            console.log(<span class="hljs-string">"LIABILITY VAULT DEBT: "</span>, debt);
        } <span class="hljs-keyword">else</span> {
            console.log(<span class="hljs-string">"LIABILITY VAULT ASSET BALANCE: "</span>, assetsBalance(_liabilityVault));
        }
        console.log(<span class="hljs-string">"--------------------------------"</span>);
    }
</code></pre>
<p>We are finally ready to jump back to our <code>close.s.sol</code> file and execute the batch in the context of our trade.</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// close.s.sol</span>

        broadcastBatch(
            batchSwapAndRepay(
                HEDGED_VAULT, LIABILITY_VAULT, hedged_balance,
                swapJson, verifyJson
            )
        );

        logPositionInfo(COLLATERAL_VAULT, HEDGED_VAULT, LIABILITY_VAULT);
</code></pre>
<p>We pass our <code>HEDGED_VAULT</code>, since PT-stS is the input token for our swap and repay, along with the <code>LIABILITY_VAULT</code> as our <code>_repayVault</code> and our hedged_balance as the amount. We also pass through our previously retrieved swap data. <code>batchSwapAndRepay</code> will return the <code>EVC.BatchItem</code> array that we need to execute this operation thus we can pass the entire function call as an argument directly to <code>broadcastBatch</code> which will handle the execution.</p>
<p>After that, we can call <code>logPositionInfo()</code> and see our balance status after the execution is completed.</p>
<p>Now, after running this we may notice that our position has a profit in the form of a log showing <code>LIABILITY VAULT ASSET BALANCE: xxxxx</code> indicating that after repaying the debt, we had excess <code>LIABILITY_TOKEN</code>s aka profit.</p>
<p>So, we will now be wanting to swap these profits to cash. In order to do so, we simply call <code>getRoutingData</code> for our new transaction.</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// close.s.sol</span>

        <span class="hljs-keyword">uint256</span> collateral_balance <span class="hljs-operator">=</span> assetsBalance(COLLATERAL_VAULT);
        <span class="hljs-keyword">uint256</span> liability_balance <span class="hljs-operator">=</span> assetsBalance(LIABILITY_VAULT);

        (swapJson, verifyJson) <span class="hljs-operator">=</span> getRoutingData(
            LIABILITY_VAULT, COLLATERAL_VAULT, LIABILITY_TOKEN, COLLATERAL_TOKEN, liability_balance
        );
</code></pre>
<p>Here we retrieve routing data for the <code>LIABILTY_TOKEN</code>'s <code>liability_balance</code> of wS and swapped for our desired output the <code>COLLATERAL_TOKEN</code> (USDC). we have also taken note of the <code>COLLATERAL_VAULT</code> <code>collateral_balance</code> which we will use later to calculate our net USDC profit.</p>
<p>Since we have the swap data, we need to construct a batch similar to our previous one except that we do not need to execute a repay function after the <code>swapVerify</code> payload has deposited our output token shares in the vault. So we will create another utility function to return a batch for this pattern of <code>BatchWithdrawAndSwap</code></p>
<pre><code class="lang-solidity"><span class="hljs-comment">// EScript.s.sol</span>

    <span class="hljs-comment">/// @notice Withdraw from the inputVault, swap and deposit to the outputVault</span>
    <span class="hljs-comment">/// @param _swapJson The json file for the swap router data</span>
    <span class="hljs-comment">/// @param _verifyJson The json file for the verify router data</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">batchWithdrawAndSwap</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> _inputVault,
        <span class="hljs-keyword">uint256</span> _amountIn,
        <span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> _swapJson,
        <span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> _verifyJson
    </span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params">IEVC.BatchItem[] <span class="hljs-keyword">memory</span> items</span>) </span>{
        items <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> IEVC.BatchItem[](<span class="hljs-number">3</span>);
        items[<span class="hljs-number">0</span>] <span class="hljs-operator">=</span> batchWithdrawTo(_inputVault, _amountIn, vm.parseJsonAddress(_swapJson, <span class="hljs-string">".swapperAddress"</span>));
        items[<span class="hljs-number">1</span>] <span class="hljs-operator">=</span> batchPayload(vm.parseJsonAddress(_swapJson, <span class="hljs-string">".swapperAddress"</span>), vm.parseJsonBytes(_swapJson, <span class="hljs-string">".swapperData"</span>));
        items[<span class="hljs-number">2</span>] <span class="hljs-operator">=</span> batchPayload(vm.parseJsonAddress(_verifyJson, <span class="hljs-string">".verifierAddress"</span>), vm.parseJsonBytes(_verifyJson, <span class="hljs-string">".verifierData"</span>));
    }
</code></pre>
<p>Exactly the same as before, except without a repay operation. Note, also, that the <code>batchWithdrawAndSwap</code> function only needs to "know" about the <code>_inputVault</code> so that it can withdraw the assets to the <code>swapperAddress</code>. Otherwise, the swap payload already has the context of which token to swap to and which vault will receive it. The <code>verifierData</code> that executes within the batch takes care of ensuring that the tokens were delivered to destination vault and were <code>skimmed</code> converting them to deposits on behalf of our <code>e_account</code>.</p>
<p>Now we can easily execute this batch as we did before.</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// close.s.sol</span>
        broadcastBatch(batchWithdrawAndSwap(
            LIABILITY_VAULT, liability_balance,
            swapJson, verifyJson)
        );
        logPositionInfo(COLLATERAL_VAULT, HEDGED_VAULT, LIABILITY_VAULT);
        console.log(<span class="hljs-string">"COLLATERAL PROFIT: "</span>, assetsBalance(COLLATERAL_VAULT) <span class="hljs-operator">-</span> collateral_balance);
</code></pre>
<p>Here, we use the <code>LIABILITY_VAULT</code> as the <code>_inputVault</code> from which to withdraw and the balance of funds to withdraw.</p>
<p>Once this batch is broadcast we can display our vault info. we can also get the difference in our <code>COLLATERAL_VAULT</code> balance to display our profit in USDC.</p>
<pre><code class="lang-bash">forge script --fork-url https://rpc.soniclabs.com --sender <span class="hljs-variable">$SENDER</span> script/close.s.sol --ffi
</code></pre>
<p>With this framework of script utilities, we can exert very fine control over the leverage and exposure of our position in addition to allowing us to move quickly and flexibly across trades with maximum capital efficiency.</p>
<p>From here, implementing scripts which allow us to unhedge our exposure or deleverage our position will be a breeze.</p>
<p>High yield defi engineering is available on demand at <a target="_blank" href="https://yielddev.com/">YieldDev Studio</a></p>
<p>Feel free to reach out on —&gt; <a target="_blank" href="https://x.com/yielddev">x for anything, technical or otherwise</a></p>
<p>Full code described above can be found on <a target="_blank" href="https://github.com/yielddev/AdvancedYieldFarming">github</a></p>
<p>Full code of <code>close.s.sol</code></p>
<pre><code class="lang-solidity"><span class="hljs-comment">// SPDX-License-Identifier: UNLICENSED</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.28;</span>

<span class="hljs-keyword">import</span> <span class="hljs-string">"./common/EScript.s.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">SonicLib</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./common/SonicLib.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">CloseScript</span> <span class="hljs-keyword">is</span> <span class="hljs-title">EScript</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">run</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        borrower <span class="hljs-operator">=</span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>;
        e_account <span class="hljs-operator">=</span> getSubaccount(borrower, <span class="hljs-number">2</span>);
        evc <span class="hljs-operator">=</span> IEVC(SonicLib.EVC);

        <span class="hljs-keyword">address</span> LIABILITY_VAULT <span class="hljs-operator">=</span> SonicLib.EULER_WS_VAULT;
        <span class="hljs-keyword">address</span> HEDGED_VAULT <span class="hljs-operator">=</span> SonicLib.EULER_PT_STS_VAULT;
        <span class="hljs-keyword">address</span> COLLATERAL_VAULT <span class="hljs-operator">=</span> SonicLib.EULER_USDC_VAULT;
        <span class="hljs-keyword">address</span> LIABILITY_TOKEN <span class="hljs-operator">=</span> SonicLib.WS;
        <span class="hljs-keyword">address</span> HEDGED_TOKEN <span class="hljs-operator">=</span> SonicLib.PT_STS;
        <span class="hljs-keyword">address</span> COLLATERAL_TOKEN <span class="hljs-operator">=</span> SonicLib.USDC;

        <span class="hljs-keyword">uint256</span> hedged_balance <span class="hljs-operator">=</span> assetsBalance(HEDGED_VAULT);
        (<span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> swapJson, <span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> verifyJson) <span class="hljs-operator">=</span> getRoutingData(
            HEDGED_VAULT, LIABILITY_VAULT, HEDGED_TOKEN, LIABILITY_TOKEN, hedged_balance
        );

        broadcastBatch(batchSwapAndRepay(
            HEDGED_VAULT, LIABILITY_VAULT, hedged_balance,
            swapJson, verifyJson
        ));
        logPositionInfo(COLLATERAL_VAULT, HEDGED_VAULT, LIABILITY_VAULT);

        <span class="hljs-keyword">uint256</span> collateral_balance <span class="hljs-operator">=</span> assetsBalance(COLLATERAL_VAULT);
        <span class="hljs-keyword">uint256</span> liability_balance <span class="hljs-operator">=</span> assetsBalance(LIABILITY_VAULT);

        (swapJson, verifyJson) <span class="hljs-operator">=</span> getRoutingData(
            LIABILITY_VAULT, COLLATERAL_VAULT, LIABILITY_TOKEN, COLLATERAL_TOKEN, liability_balance
        );

        broadcastBatch(batchWithdrawAndSwap(
            LIABILITY_VAULT, liability_balance,
            swapJson, verifyJson)
        );
        logPositionInfo(COLLATERAL_VAULT, HEDGED_VAULT, LIABILITY_VAULT);
        console.log(<span class="hljs-string">"COLLATERAL PROFIT: "</span>, assetsBalance(COLLATERAL_VAULT) <span class="hljs-operator">-</span> collateral_balance);
    }
}
</code></pre>
]]></content:encoded></item><item><title><![CDATA[In-Depth: Implementing A Delta Neutral Long PT Trade]]></title><description><![CDATA[In this series, I will do an in-depth walkthrough demonstrating how to implement foundry scripts for opening and managing a delta neutral yield farming position with Euler's evc. The specific trade, illustrated here, was described conceptually in thi...]]></description><link>https://yielddev.io/in-depth-implementing-a-delta-neutral-long-pt-trade</link><guid isPermaLink="true">https://yielddev.io/in-depth-implementing-a-delta-neutral-long-pt-trade</guid><category><![CDATA[Software Engineering]]></category><category><![CDATA[Solidity]]></category><category><![CDATA[Ethereum]]></category><category><![CDATA[yield farming]]></category><category><![CDATA[euler-finance]]></category><category><![CDATA[EVC]]></category><category><![CDATA[lending]]></category><category><![CDATA[Interest Rates]]></category><category><![CDATA[pendle]]></category><category><![CDATA[defi]]></category><dc:creator><![CDATA[Yield Dev]]></dc:creator><pubDate>Thu, 03 Apr 2025 01:20:35 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1743654717670/91e92930-ee89-45ec-8927-39f15a71407e.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this series, I will do an in-depth walkthrough demonstrating how to implement foundry scripts for opening and managing a delta neutral yield farming position with Euler's <code>evc</code>. The specific trade, illustrated here, was described conceptually in this <a target="_blank" href="https://yielddev.io/deep-delta-neutral-fixed-pt-yield-on-sonic-with-euler">previous post</a></p>
<p>We will simplify and generalize our scripts, in order that they may be used to create delta neutral yield positions across any PT asset on Euler.</p>
<p>To begin Let's create a new directory called <code>AdvancedYieldFarming</code> and initialize a foundry project inside of it.</p>
<pre><code class="lang-bash"> forge init
</code></pre>
<p>We are also going to install <code>ethereum-vault-connector</code> and <code>evk/periphery</code> libraries from Euler in order to build our scripts</p>
<pre><code class="lang-bash">forge install euler-xyz/ethereum-vault-connector
forge install euler-xyz/evk-periphery
</code></pre>
<p>To start out, we will create a subdirectory in our <code>script</code> folder called <code>common</code>. Inside of which, we will create a new script file called <code>EScript.s.sol</code> this file will act as the base for any scripts we write and will hold utility logic, so that our main scripts can be focused strictly on building the transaction logic.</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// SPDX-License-Identifier: UNLICENSED</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.28;</span>

<span class="hljs-comment">// EScript.s.sol</span>
<span class="hljs-keyword">import</span> {<span class="hljs-title">Script</span>, <span class="hljs-title">console</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"forge-std/Script.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"evc/interfaces/IEthereumVaultConnector.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">EVault</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"evk/EVault/EVault.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">EScript</span> <span class="hljs-keyword">is</span> <span class="hljs-title">Script</span> </span>{
    IEVC evc;
    <span class="hljs-keyword">address</span> borrower; <span class="hljs-comment">// eoa</span>
    <span class="hljs-keyword">address</span> e_account; <span class="hljs-comment">// subaccount</span>
}
</code></pre>
<p>Here, we have imported the basics and setup our contract as a <code>Script</code> with a couple of global variables that will be universal to any transaction we build. Namely, the <code>EVC</code>, the <code>borrower</code> which will be our <code>msg.sender</code> and the e_account, which we can set to our Euler compliant sub account. This sub account will be the global account that holds our position.</p>
<p>From here, we can implement two utility functions that will be necessary for our script. First, we need a way of deriving our sub account based on our index. We also need a way to read in json file data, this will be necessary to utilize the swap routing payloads which we are going to fetch from the <code>euler-orderflow-router</code>. We can define these functions in our <code>EScript</code> contract.</p>
<pre><code class="lang-solidity">    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getSubaccount</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _account, <span class="hljs-keyword">uint256</span> _index</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">address</span></span>) </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">address</span>(<span class="hljs-keyword">uint160</span>(<span class="hljs-keyword">uint160</span>(_account)<span class="hljs-operator">^</span>_index));
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getJsonFile</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> _filePath</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span></span>) </span>{
        <span class="hljs-keyword">return</span> vm.readFile(_filePath);
    }
</code></pre>
<p>In the context of the <code>evc</code>, one general operation we will often need to execute will be the enabling/disabling of the vaults as collateral or controllers. The operations indicate to the <code>evc</code> which vaults to use as collateral and allow us to take loans from the controllers. Thus, we can create two utility functions which return the <code>BatchItem</code> payload necessary for a given vault.</p>
<pre><code class="lang-solidity">    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">enableCollateral</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _vault</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params">IEVC.BatchItem <span class="hljs-keyword">memory</span></span>) </span>{
        <span class="hljs-keyword">return</span> IEVC.BatchItem({
            onBehalfOfAccount: <span class="hljs-keyword">address</span>(<span class="hljs-number">0</span>),
            targetContract: <span class="hljs-keyword">address</span>(evc),
            <span class="hljs-built_in">value</span>: <span class="hljs-number">0</span>,
            data: <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodeWithSelector</span>(
                IEVC.enableCollateral.<span class="hljs-built_in">selector</span>,
                e_account,
                _vault
            )
        });
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">enableController</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _vault</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params">IEVC.BatchItem <span class="hljs-keyword">memory</span></span>) </span>{
        <span class="hljs-keyword">return</span> IEVC.BatchItem({
            onBehalfOfAccount: <span class="hljs-keyword">address</span>(<span class="hljs-number">0</span>),
            targetContract: <span class="hljs-keyword">address</span>(evc),
            <span class="hljs-built_in">value</span>: <span class="hljs-number">0</span>,
            data: <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodeWithSelector</span>(
                IEVC.enableController.<span class="hljs-built_in">selector</span>,
                e_account,
                _vault
            )
        });
    }
</code></pre>
<p>These functions will deliver the batch data for enabling a specific vault for the Euler sub account which we globally set in our main scripts <code>run</code> logic.</p>
<p>Another frequent operation we will need to make use of is executing a swap's routing payload. The swap data we retrieve will give us a target contract along with all the data needed, this makes executing it within a batch simple enough to allow us to create a general function to batch this operation and execute it on behalf of our current sub account.</p>
<pre><code class="lang-solidity">    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">batchPayload</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _target, <span class="hljs-keyword">bytes</span> <span class="hljs-keyword">memory</span> _data</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params">IEVC.BatchItem <span class="hljs-keyword">memory</span></span>) </span>{
        <span class="hljs-keyword">return</span> IEVC.BatchItem({
            onBehalfOfAccount: e_account,
            targetContract: _target,
            <span class="hljs-built_in">value</span>: <span class="hljs-number">0</span>,
            data: _data
        });
    }
</code></pre>
<p>In order that we may open a leveraged long position, we are going to need an operation to borrow funds and have them delivered to the swapper for trading. So we can create a general borrow function to retrieve the <code>BatchItem</code> which accomplishes this transaction on a given vault to a given address in a specified amount.</p>
<pre><code class="lang-solidity">    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">batchBorrowTo</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _vault, <span class="hljs-keyword">uint256</span> _amount, <span class="hljs-keyword">address</span> _to</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params">IEVC.BatchItem <span class="hljs-keyword">memory</span></span>) </span>{
        <span class="hljs-keyword">return</span> IEVC.BatchItem({
            onBehalfOfAccount: e_account,
            targetContract: _vault,
            <span class="hljs-built_in">value</span>: <span class="hljs-number">0</span>,
            data: <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodeWithSelector</span>(EVault.borrow.<span class="hljs-built_in">selector</span>, _amount, _to)
        });
    }
</code></pre>
<p>Now that we have implemented all of the necessary batch operations to open our position, we will add a few general getters to check our balances on the vaults.</p>
<pre><code class="lang-solidity">    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">sharesBalance</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _vault</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> EVault(_vault).balanceOf(e_account);
    }
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">assetsBalance</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _vault</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> EVault(_vault).convertToAssets(EVault(_vault).balanceOf(e_account));
    }
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">debtBalance</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _vault</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> EVault(_vault).debtOf(e_account);
    }
</code></pre>
<p>Now since we will be opening this trade on Sonic. We will create a library to hold all of the relevant constant sonic contract addresses. This will be in our common directory.</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// SPDX-License-Identifier: UNLICENSED</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.28;</span>

<span class="hljs-class"><span class="hljs-keyword">library</span> <span class="hljs-title">SonicLib</span> </span>{
    <span class="hljs-keyword">address</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">constant</span> EVC <span class="hljs-operator">=</span> <span class="hljs-number">0x4860C903f6Ad709c3eDA46D3D502943f184D4315</span>;

    <span class="hljs-keyword">address</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">constant</span> EULER_USDC_VAULT <span class="hljs-operator">=</span> <span class="hljs-number">0x196F3C7443E940911EE2Bb88e019Fd71400349D9</span>;
    <span class="hljs-keyword">address</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">constant</span> EULER_PT_STS_VAULT <span class="hljs-operator">=</span> <span class="hljs-number">0xdBc46ff39Cae7f37c39363B0CA474497dAD1d3cf</span>;
    <span class="hljs-keyword">address</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">constant</span> EULER_WS_VAULT <span class="hljs-operator">=</span> <span class="hljs-number">0x9144C0F0614dD0acE859C61CC37e5386d2Ada43A</span>;

    <span class="hljs-keyword">address</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">constant</span> USDC <span class="hljs-operator">=</span> <span class="hljs-number">0x29219dd400f2Bf60E5a23d13Be72B486D4038894</span>;
    <span class="hljs-keyword">address</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">constant</span> PT_STS <span class="hljs-operator">=</span> <span class="hljs-number">0x420df605D062F8611EFb3F203BF258159b8FfFdE</span>;
    <span class="hljs-keyword">address</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">constant</span> WS <span class="hljs-operator">=</span> <span class="hljs-number">0x039e2fB66102314Ce7b64Ce5Ce3E5183bc94aD38</span>;
}
</code></pre>
<p>Since we have all of our utilities completed, we can move on to writing the scripts run logic. We can create a file called <code>script/open.s.sol</code> and import our <code>EScript</code> base contract and the <code>SonicLib</code>. We also set our global variables to be used in the script. This is where we will define which of our Euler subaccounts will hold our position.</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// SPDX-License-Identifier: UNLICENSED</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.28;</span>

<span class="hljs-comment">// script/open.s.sol</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">"./common/EScript.s.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">SonicLib</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./common/SonicLib.sol"</span>;
<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">OpenScript</span> <span class="hljs-keyword">is</span> <span class="hljs-title">EScript</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">run</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        borrower <span class="hljs-operator">=</span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>;
        e_account <span class="hljs-operator">=</span> getSubaccount(borrower, <span class="hljs-number">0</span>);
        evc <span class="hljs-operator">=</span> IEVC(SonicLib.EVC);
    }
}
</code></pre>
<p>Note, we have also imported a library called <code>SonicLib</code> from the common directory. This file will hold specific, constant addresses for our trade.</p>
<p>First, let's define each asset we will use in our trade.</p>
<p>For this trade, we are going to have a <code>hedged</code> asset earning fixed yield in the form of a PT, specifically PT-stS. The purchase of this asset will be funded by borrowing wS, our <code>liability</code> asset. Since The PT resolves to $S staked in beets at maturity, this liability neutralizes our exposure from the PT. We will collateralize this wS loan with our initial capital in USDC.e in it's vault, this will be our <code>collateral</code>.</p>
<p>So we need to set these variables in the code, defining both the token and vault for each asset.</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// script/open.s.sol</span>

        <span class="hljs-keyword">address</span> LIABILITY_VAULT <span class="hljs-operator">=</span> SonicLib.EULER_WS_VAULT;
        <span class="hljs-keyword">address</span> HEDGED_VAULT <span class="hljs-operator">=</span> SonicLib.EULER_PT_STS_VAULT;
        <span class="hljs-keyword">address</span> COLLATERAL_VAULT <span class="hljs-operator">=</span> SonicLib.EULER_USDC_VAULT;

        <span class="hljs-keyword">address</span> LIABILITY_TOKEN <span class="hljs-operator">=</span> SonicLib.WS;
        <span class="hljs-keyword">address</span> HEDGED_TOKEN <span class="hljs-operator">=</span> SonicLib.PT_STS;
        <span class="hljs-keyword">address</span> COLLATERAL_TOKEN <span class="hljs-operator">=</span> SonicLib.USDC;
</code></pre>
<p>Now that we understand each asset's purpose in our trade, we can set the specific addresses in our SonicLib corresponding to the <code>Sonic</code> address of the asset.</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// SPDX-License-Identifier: UNLICENSED</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.28;</span>

<span class="hljs-class"><span class="hljs-keyword">library</span> <span class="hljs-title">SonicLib</span> </span>{
    <span class="hljs-keyword">address</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">constant</span> EVC <span class="hljs-operator">=</span> <span class="hljs-number">0x4860C903f6Ad709c3eDA46D3D502943f184D4315</span>;

    <span class="hljs-keyword">address</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">constant</span> EULER_USDC_VAULT <span class="hljs-operator">=</span> <span class="hljs-number">0x196F3C7443E940911EE2Bb88e019Fd71400349D9</span>;
    <span class="hljs-keyword">address</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">constant</span> EULER_PT_STS_VAULT <span class="hljs-operator">=</span> <span class="hljs-number">0xdBc46ff39Cae7f37c39363B0CA474497dAD1d3cf</span>;
    <span class="hljs-keyword">address</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">constant</span> EULER_WS_VAULT <span class="hljs-operator">=</span> <span class="hljs-number">0x9144C0F0614dD0acE859C61CC37e5386d2Ada43A</span>;

    <span class="hljs-keyword">address</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">constant</span> USDC <span class="hljs-operator">=</span> <span class="hljs-number">0x29219dd400f2Bf60E5a23d13Be72B486D4038894</span>;
    <span class="hljs-keyword">address</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">constant</span> PT_STS <span class="hljs-operator">=</span> <span class="hljs-number">0x420df605D062F8611EFb3F203BF258159b8FfFdE</span>;
    <span class="hljs-keyword">address</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">constant</span> WS <span class="hljs-operator">=</span> <span class="hljs-number">0x039e2fB66102314Ce7b64Ce5Ce3E5183bc94aD38</span>;
}
</code></pre>
<h2 id="heading-fetching-the-swap-routing-payload">Fetching The Swap Routing Payload</h2>
<p>To fetch the <code>euler-orderflow-router</code> we will need to setup a typescript script to call the api and write the data to a json file for use in our script. This was covered in our <a target="_blank" href="https://yielddev.io/closing-leveraged-long-on-euler">previous post on using euler-orderflow-router</a> . Here we will modify the implementation to be more generalizable.</p>
<p>first we init a typescript project. Install our dependencies and create a new directory called <code>api</code></p>
<pre><code class="lang-bash">npm init -y
npm install --save-dev typescript ts-node @types/node &amp;&amp; npm install ethers@^6.0.0 dotenv
npm install yargs @types/yargs
mkdir -p api
</code></pre>
<p>Now we create a new file called <code>common.ts</code> to hold our api get logic. We define a function for structuring a query to get a payload for an <code>exactIn</code> swap data, where all of our <code>inputToken</code> <code>amountIn</code> will be consumed and swapped for the <code>outputToken</code></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">"axios"</span>;
<span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> fs <span class="hljs-keyword">from</span> <span class="hljs-string">"fs"</span>;

<span class="hljs-keyword">const</span> SWAP_API_URL = <span class="hljs-string">"https://swap.euler.finance"</span>;
<span class="hljs-comment">// Get params for an exact in swap to close a position</span>
<span class="hljs-comment">// funds are delivered to the liability Vault</span>
<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getParamsExactIn</span>(<span class="hljs-params">
    account: <span class="hljs-built_in">string</span>,
    inputVaultAddress: <span class="hljs-built_in">string</span>,
    outputVaultAddress: <span class="hljs-built_in">string</span>,
    inputToken: <span class="hljs-built_in">string</span>,
    outputToken: <span class="hljs-built_in">string</span>,
    amountIn: <span class="hljs-built_in">string</span>,
</span>) </span>{
    <span class="hljs-keyword">const</span> queryParams = {
        chainId: <span class="hljs-string">"146"</span>, <span class="hljs-comment">// sonic chain</span>
        tokenIn: inputToken,
        tokenOut: outputToken,
        amount: amountIn, <span class="hljs-comment">// the amount to swap </span>
        targetDebt: <span class="hljs-string">"0"</span>, <span class="hljs-comment">// irrelevant in this exactIn flow</span>
        currentDebt: amountIn, <span class="hljs-comment">// irrelevant in this exactIn flow</span>
        receiver: outputVaultAddress,
        vaultIn: inputVaultAddress, <span class="hljs-comment">// left over tokenIn goes here, irrelevent for exactIn flow</span>
        origin: account, <span class="hljs-comment">// account executing the tx</span>
        accountIn: account, <span class="hljs-comment">// account holding the collateral</span>
        accountOut: account, <span class="hljs-comment">// account to swap to, the account that skim will deliver to </span>
        slippage: <span class="hljs-string">"0.15"</span>, <span class="hljs-comment">// 0.15% slippage</span>
        deadline: <span class="hljs-built_in">String</span>(<span class="hljs-built_in">Math</span>.floor(<span class="hljs-built_in">Date</span>.now() / <span class="hljs-number">1000</span>) + <span class="hljs-number">10</span> * <span class="hljs-number">60</span>), <span class="hljs-comment">// 10 minutes from now</span>
        swapperMode: <span class="hljs-string">"0"</span>, <span class="hljs-comment">// exact input mode = 0 </span>
        isRepay: <span class="hljs-string">"false"</span>, <span class="hljs-comment">// we will manually add a call to repay the debt</span>
    };
    <span class="hljs-keyword">return</span> queryParams;
}

<span class="hljs-comment">// Get payload for an exact in swap to close a position</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getPayload</span>(<span class="hljs-params">queryParams: <span class="hljs-built_in">any</span></span>) </span>{

    <span class="hljs-keyword">const</span> { data: response } = <span class="hljs-keyword">await</span> axios.get(
        <span class="hljs-string">`<span class="hljs-subst">${SWAP_API_URL}</span>/swap`</span>,
        {
            params: queryParams
        }
    );

    <span class="hljs-keyword">return</span> response.data
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">writeToJsonFile</span>(<span class="hljs-params">data: <span class="hljs-built_in">any</span>, filename: <span class="hljs-built_in">string</span></span>) </span>{
    fs.writeFileSync(filename, <span class="hljs-built_in">JSON</span>.stringify(data, <span class="hljs-literal">null</span>, <span class="hljs-number">2</span>));
}
</code></pre>
<p>We have also defined a function to send the query request to the API in a get request. As well as a function to write payload data to a json file.</p>
<p>We can, at this point, write the driver script to execute on our payload. We will create a file called <code>ExactInputSwap.ts</code> for this purpose</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { getParamsExactIn, getPayload, writeToJsonFile } <span class="hljs-keyword">from</span> <span class="hljs-string">"./common"</span>;
<span class="hljs-keyword">import</span> yargs <span class="hljs-keyword">from</span> <span class="hljs-string">'yargs'</span>;
<span class="hljs-keyword">import</span> { hideBin } <span class="hljs-keyword">from</span> <span class="hljs-string">'yargs/helpers'</span>;

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">main</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-comment">// Parse command line arguments</span>
    <span class="hljs-keyword">const</span> argv = <span class="hljs-keyword">await</span> yargs(hideBin(process.argv))
        .options({
            <span class="hljs-string">'address'</span>: {
                <span class="hljs-keyword">type</span>: <span class="hljs-string">'string'</span>,
                description: <span class="hljs-string">'The wallet address'</span>,
                demandOption: <span class="hljs-literal">true</span>
            },
            <span class="hljs-string">'amount'</span>: {
                <span class="hljs-keyword">type</span>: <span class="hljs-string">'string'</span>,
                description: <span class="hljs-string">'Amount to swap in ether'</span>,
                demandOption: <span class="hljs-literal">true</span>
            },
            <span class="hljs-string">'input-vault'</span>: {
                <span class="hljs-keyword">type</span>: <span class="hljs-string">'string'</span>,
                description: <span class="hljs-string">'Input vault address'</span>,
                demandOption: <span class="hljs-literal">true</span>
            },
            <span class="hljs-string">'output-vault'</span>: {
                <span class="hljs-keyword">type</span>: <span class="hljs-string">'string'</span>,
                description: <span class="hljs-string">'Output vault address'</span>,
                demandOption: <span class="hljs-literal">true</span>
            },
            <span class="hljs-string">'input-token'</span>: {
                <span class="hljs-keyword">type</span>: <span class="hljs-string">'string'</span>,
                description: <span class="hljs-string">'Input token address'</span>,
                demandOption: <span class="hljs-literal">true</span>
            },
            <span class="hljs-string">'output-token'</span>: {
                <span class="hljs-keyword">type</span>: <span class="hljs-string">'string'</span>,
                description: <span class="hljs-string">'Output token address'</span>,
                demandOption: <span class="hljs-literal">true</span>
            },
            <span class="hljs-comment">// 'output-dir': {</span>
            <span class="hljs-comment">//     type: 'string',</span>
            <span class="hljs-comment">//     description: 'Output directory for payload files',</span>
            <span class="hljs-comment">//     default: "./script/payloads"</span>
            <span class="hljs-comment">// }</span>
        })
        .help()
        .argv;

    <span class="hljs-keyword">const</span> swapParams = getParamsExactIn(
        argv.address,
        argv[<span class="hljs-string">"input-vault"</span>],
        argv[<span class="hljs-string">"output-vault"</span>],
        argv[<span class="hljs-string">"input-token"</span>],
        argv[<span class="hljs-string">"output-token"</span>],
        BigInt(argv.amount).toString()
    );

    <span class="hljs-keyword">const</span> swapPayload = <span class="hljs-keyword">await</span> getPayload(swapParams);

    <span class="hljs-keyword">const</span> outputDir = argv[<span class="hljs-string">"output-dir"</span>];
    <span class="hljs-keyword">await</span> writeToJsonFile(swapPayload.swap, <span class="hljs-string">`script/payloads/swapData.json`</span>);
    <span class="hljs-keyword">await</span> writeToJsonFile(swapPayload.verify, <span class="hljs-string">`script/payloads/verifyData.json`</span>);

    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Payloads written to files in:"</span>, <span class="hljs-string">`script/payloads`</span>);
}

main().catch(<span class="hljs-function">(<span class="hljs-params">error</span>) =&gt;</span> {
    <span class="hljs-built_in">console</span>.error(error);
    process.exitCode = <span class="hljs-number">1</span>;
});
</code></pre>
<p>Here, we have outlined the command line args necessary for our swap, we than construct our query based on these args and make the request.</p>
<p>Once we have received the payload, they will be written to two separate json files in our <code>script/payload</code> directory. The <code>euler-orderflow-router</code> pattern was previously discussed in <a target="_blank" href="https://yielddev.io/closing-leveraged-long-on-euler">this post</a></p>
<h2 id="heading-back-to-solidity">Back To Solidity</h2>
<p>Now that we have our API call script complete, we can jump back to our <code>EScript.s.sol</code> file and add a utility function which will allow us to call this script and write fresh payload data from inside our solidity execution script.</p>
<p>For this function, we will make use of the <code>vm.ffi()</code> cheatcode. Allowing us to execute arbitrary code on our machine.</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// Escript.s.sol</span>

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">requestPayload</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> _inputVault,
        <span class="hljs-keyword">address</span> _outputVault,
        <span class="hljs-keyword">address</span> _inputToken,
        <span class="hljs-keyword">address</span> _outputToken,
        <span class="hljs-keyword">uint256</span> _amount
    </span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        <span class="hljs-keyword">string</span>[] <span class="hljs-keyword">memory</span> inputs <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-keyword">string</span>[](<span class="hljs-number">14</span>);
        inputs[<span class="hljs-number">0</span>] <span class="hljs-operator">=</span> <span class="hljs-string">"ts-node"</span>;
        inputs[<span class="hljs-number">1</span>] <span class="hljs-operator">=</span> <span class="hljs-string">"api/ExactInputSwap.ts"</span>;
        inputs[<span class="hljs-number">2</span>] <span class="hljs-operator">=</span> <span class="hljs-string">"--address"</span>;
        inputs[<span class="hljs-number">3</span>] <span class="hljs-operator">=</span> vm.toString(e_account);
        inputs[<span class="hljs-number">4</span>] <span class="hljs-operator">=</span> <span class="hljs-string">"--input-vault"</span>;
        inputs[<span class="hljs-number">5</span>] <span class="hljs-operator">=</span> vm.toString(_inputVault);
        inputs[<span class="hljs-number">6</span>] <span class="hljs-operator">=</span> <span class="hljs-string">"--output-vault"</span>;
        inputs[<span class="hljs-number">7</span>] <span class="hljs-operator">=</span> vm.toString(_outputVault);
        inputs[<span class="hljs-number">8</span>] <span class="hljs-operator">=</span> <span class="hljs-string">"--input-token"</span>;
        inputs[<span class="hljs-number">9</span>] <span class="hljs-operator">=</span> vm.toString(_inputToken);
        inputs[<span class="hljs-number">10</span>] <span class="hljs-operator">=</span> <span class="hljs-string">"--output-token"</span>;
        inputs[<span class="hljs-number">11</span>] <span class="hljs-operator">=</span> vm.toString(_outputToken);
        inputs[<span class="hljs-number">12</span>] <span class="hljs-operator">=</span> <span class="hljs-string">"--amount"</span>;
        inputs[<span class="hljs-number">13</span>] <span class="hljs-operator">=</span> vm.toString(_amount);

        vm.ffi(inputs);
    }
</code></pre>
<p>Here we can pass through all of our trade's parameters to the scripts arguments and execute the request for a swap payload in the context of our current trades execution.</p>
<p>In order that we are able to read the payload file, we must update our foundry configs in <code>foundry.toml</code></p>
<pre><code class="lang-toml"><span class="hljs-attr">fs_permissions</span> = [{ access = <span class="hljs-string">"read"</span>, path = <span class="hljs-string">"./script/payloads"</span>}]
</code></pre>
<p>Now that our utilities are updated, we can jump back to the execution script and finish implementing this trades logic.</p>
<p>After defining all of our assets and vaults, we are going to get the balance of our <code>COLLATERAL_TOKEN</code> (USDC) for deposit. We are also going to get our maximum borrow amount of <code>LIABILITY_TOKEN</code> based on our USDC balance.</p>
<p>Since, at the time of writing, wS is worth 0.5 USDC and we have determined to go 7x long our initial capital, as outlined in <a target="_blank" href="https://yielddev.io/deep-delta-neutral-fixed-pt-yield-on-sonic-with-euler">the post describing this trade</a> we can calculate our <code>maxDebt</code> at 2 wS per dollar.</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// script/open.s.sol</span>

        <span class="hljs-keyword">uint256</span> collateral_balance <span class="hljs-operator">=</span> IERC20(COLLATERAL_TOKEN).balanceOf(borrower);
        <span class="hljs-keyword">uint256</span> maxDebt <span class="hljs-operator">=</span> collateral_balance <span class="hljs-operator">*</span> <span class="hljs-number">2e12</span> <span class="hljs-operator">*</span> <span class="hljs-number">7</span>;
</code></pre>
<p>Now that we have all of our amounts, we can call the <code>euler-orderflow-router</code> to write our payload via the previously made utility function. We also parse the resulting files in order that we may have the data ready to go.</p>
<pre><code class="lang-solidity">        requestPayload(
            LIABILITY_VAULT,
            HEDGED_VAULT,
            LIABILITY_TOKEN,
            HEDGED_TOKEN,
            maxDebt
        );

        <span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> swapJson <span class="hljs-operator">=</span> getJsonFile(<span class="hljs-string">"./script/payloads/swapData.json"</span>);
        <span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> verifyJson <span class="hljs-operator">=</span> getJsonFile(<span class="hljs-string">"./script/payloads/verifyData.json"</span>);
</code></pre>
<p>Next, we can construct our <code>EVC Batch</code> of operations, we will need 7 items, starting with enabling all of the necessary vaults.</p>
<pre><code class="lang-solidity">
        IEVC.BatchItem[] <span class="hljs-keyword">memory</span> batchItems <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> IEVC.BatchItem[](<span class="hljs-number">7</span>);

        batchItems[<span class="hljs-number">0</span>] <span class="hljs-operator">=</span> enableCollateral(COLLATERAL_VAULT);
        batchItems[<span class="hljs-number">1</span>] <span class="hljs-operator">=</span> enableCollateral(HEDGED_VAULT);
        batchItems[<span class="hljs-number">2</span>] <span class="hljs-operator">=</span> enableController(LIABILITY_VAULT);
</code></pre>
<p>We can, then, deposit our initial capital as collateral and borrow our full amount of leverage from the liability vault into the swapper contract.</p>
<pre><code class="lang-solidity">        batchItems[<span class="hljs-number">3</span>] <span class="hljs-operator">=</span> batchDeposit(COLLATERAL_VAULT, collateral_balance);
        batchItems[<span class="hljs-number">4</span>] <span class="hljs-operator">=</span> batchBorrowTo(LIABILITY_VAULT, maxDebt, vm.parseJsonAddress(swapJson, <span class="hljs-string">".swapperAddress"</span>));
</code></pre>
<p>Once that is batched, we batch the payloads for swapping and verifying(which also deposits our <code>outputToken</code> into it's collateral vault).</p>
<pre><code class="lang-solidity">        batchItems[<span class="hljs-number">5</span>] <span class="hljs-operator">=</span> batchPayload(vm.parseJsonAddress(swapJson, <span class="hljs-string">".swapperAddress"</span>), vm.parseJsonBytes(swapJson, <span class="hljs-string">".swapperData"</span>));
        batchItems[<span class="hljs-number">6</span>] <span class="hljs-operator">=</span> batchPayload(vm.parseJsonAddress(verifyJson, <span class="hljs-string">".verifierAddress"</span>), vm.parseJsonBytes(verifyJson, <span class="hljs-string">".verifierData"</span>));
</code></pre>
<p>And with that, our batch is ready to cook! The only other execution we need, inside of our broadcast section, is the approve the <code>COLLATERAL_VAULT</code> an allowance to transfer our <code>COLLATERAL_TOKEN</code> since our batch does contain a deposit transaction.</p>
<pre><code class="lang-solidity">        vm.startBroadcast();

        IERC20(COLLATERAL_TOKEN).approve(COLLATERAL_VAULT, collateral_balance);
        evc.batch(batchItems);

        vm.stopBroadcast();
</code></pre>
<p>Once our broadcast is complete, we can fetch our vault balances to make sure the result is as expected.</p>
<pre><code class="lang-solidity">        console.log(<span class="hljs-string">"LIABILITY TOKEN DEBT: "</span>, debtBalance(LIABILITY_VAULT));
        console.log(<span class="hljs-string">"HEDGED TOKEN BALANCE: "</span>, assetsBalance(HEDGED_VAULT));
        console.log(<span class="hljs-string">"COLLATERAL TOKEN BALANCE: "</span>, assetsBalance(COLLATERAL_VAULT));
</code></pre>
<p>This is script can be run with</p>
<pre><code class="lang-bash">forge script --fork-url https://rpc.soniclabs.com --sender <span class="hljs-variable">$SENDER</span> script/Hedged/close.s.sol --ffi
</code></pre>
<p>Here, we can see how utilizing programmatic, scripted execution allows capital allocators and yield farmers to easily create more complex positions available with the <code>EVC</code>'s modular design. Integrating multiple assets and protocols. Further scripts written in this series, for managing positions like this, will demonstrate the speed and flexibility in positioning that are available to traders who take advantage of expert defi development.</p>
<p>High yield defi engineering is available on demand at <a target="_blank" href="https://yielddev.com">YieldDev Studio</a></p>
<p>Feel free to reach out on —&gt; <a target="_blank" href="https://x.com/yielddev">x for anything, technical or otherwise</a></p>
<p>Full code described above can be found on <a target="_blank" href="https://github.com/yielddev/AdvancedYieldFarming">github</a></p>
<p>Full script code:</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// SPDX-License-Identifier: UNLICENSED</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.28;</span>

<span class="hljs-keyword">import</span> <span class="hljs-string">"./common/EScript.s.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">SonicLib</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./common/SonicLib.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">IERC20</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"</span>;
<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">OpenScript</span> <span class="hljs-keyword">is</span> <span class="hljs-title">EScript</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">run</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        borrower <span class="hljs-operator">=</span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>;
        e_account <span class="hljs-operator">=</span> getSubaccount(borrower, <span class="hljs-number">0</span>);
        evc <span class="hljs-operator">=</span> IEVC(SonicLib.EVC);

        <span class="hljs-keyword">address</span> LIABILITY_VAULT <span class="hljs-operator">=</span> SonicLib.EULER_WS_VAULT;
        <span class="hljs-keyword">address</span> HEDGED_VAULT <span class="hljs-operator">=</span> SonicLib.EULER_PT_STS_VAULT;
        <span class="hljs-keyword">address</span> COLLATERAL_VAULT <span class="hljs-operator">=</span> SonicLib.EULER_USDC_VAULT;

        <span class="hljs-keyword">address</span> LIABILITY_TOKEN <span class="hljs-operator">=</span> SonicLib.WS;
        <span class="hljs-keyword">address</span> HEDGED_TOKEN <span class="hljs-operator">=</span> SonicLib.PT_STS;
        <span class="hljs-keyword">address</span> COLLATERAL_TOKEN <span class="hljs-operator">=</span> SonicLib.USDC;

        <span class="hljs-keyword">uint256</span> collateral_balance <span class="hljs-operator">=</span> IERC20(COLLATERAL_TOKEN).balanceOf(borrower);

        <span class="hljs-keyword">uint256</span> maxDebt <span class="hljs-operator">=</span> collateral_balance <span class="hljs-operator">*</span> <span class="hljs-number">2e12</span> <span class="hljs-operator">*</span> <span class="hljs-number">7</span>;

        requestPayload(
            LIABILITY_VAULT,
            HEDGED_VAULT,
            LIABILITY_TOKEN,
            HEDGED_TOKEN,
            maxDebt
        );

        <span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> swapJson <span class="hljs-operator">=</span> getJsonFile(<span class="hljs-string">"./script/payloads/swapData.json"</span>);
        <span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> verifyJson <span class="hljs-operator">=</span> getJsonFile(<span class="hljs-string">"./script/payloads/verifyData.json"</span>);

        vm.label(vm.parseJsonAddress(swapJson, <span class="hljs-string">".swapperAddress"</span>), <span class="hljs-string">"swapperAddress"</span>);
        vm.label(vm.parseJsonAddress(verifyJson, <span class="hljs-string">".verifierAddress"</span>), <span class="hljs-string">"verifierAddress"</span>);

        IEVC.BatchItem[] <span class="hljs-keyword">memory</span> batchItems <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> IEVC.BatchItem[](<span class="hljs-number">7</span>);

        batchItems[<span class="hljs-number">0</span>] <span class="hljs-operator">=</span> enableCollateral(COLLATERAL_VAULT);
        batchItems[<span class="hljs-number">1</span>] <span class="hljs-operator">=</span> enableCollateral(HEDGED_VAULT);
        batchItems[<span class="hljs-number">2</span>] <span class="hljs-operator">=</span> enableController(LIABILITY_VAULT);

        batchItems[<span class="hljs-number">3</span>] <span class="hljs-operator">=</span> batchDeposit(COLLATERAL_VAULT, collateral_balance);
        batchItems[<span class="hljs-number">4</span>] <span class="hljs-operator">=</span> batchBorrowTo(LIABILITY_VAULT, maxDebt, vm.parseJsonAddress(swapJson, <span class="hljs-string">".swapperAddress"</span>));

        batchItems[<span class="hljs-number">5</span>] <span class="hljs-operator">=</span> batchPayload(vm.parseJsonAddress(swapJson, <span class="hljs-string">".swapperAddress"</span>), vm.parseJsonBytes(swapJson, <span class="hljs-string">".swapperData"</span>));
        batchItems[<span class="hljs-number">6</span>] <span class="hljs-operator">=</span> batchPayload(vm.parseJsonAddress(verifyJson, <span class="hljs-string">".verifierAddress"</span>), vm.parseJsonBytes(verifyJson, <span class="hljs-string">".verifierData"</span>));

        vm.startBroadcast();

        IERC20(COLLATERAL_TOKEN).approve(COLLATERAL_VAULT, collateral_balance);
        evc.batch(batchItems);

        vm.stopBroadcast();

        console.log(<span class="hljs-string">"LIABILITY TOKEN DEBT: "</span>, debtBalance(LIABILITY_VAULT));
        console.log(<span class="hljs-string">"HEDGED TOKEN BALANCE: "</span>, assetsBalance(HEDGED_VAULT));
        console.log(<span class="hljs-string">"COLLATERAL TOKEN BALANCE: "</span>, assetsBalance(COLLATERAL_VAULT));

    }
}
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Deep Delta Neutral Fixed PT Yield on Sonic with Euler]]></title><description><![CDATA[Euler's MEV cluster supports liquidity for going leverage long many of the Pendle PT fixed yield tokens available on sonic.
Accessing leverage in the form of of the PT's underlying token allows for massive leverage on the fixed rate, providing huge p...]]></description><link>https://yielddev.io/deep-delta-neutral-fixed-pt-yield-on-sonic-with-euler</link><guid isPermaLink="true">https://yielddev.io/deep-delta-neutral-fixed-pt-yield-on-sonic-with-euler</guid><category><![CDATA[euler-finance]]></category><category><![CDATA[euler]]></category><category><![CDATA[EVC]]></category><category><![CDATA[Solidity]]></category><category><![CDATA[yield farming]]></category><category><![CDATA[yield]]></category><category><![CDATA[farming]]></category><category><![CDATA[defi]]></category><category><![CDATA[defi development services]]></category><category><![CDATA[defi development company]]></category><category><![CDATA[pendle]]></category><category><![CDATA[sonic]]></category><category><![CDATA[Ethereum]]></category><category><![CDATA[Cryptocurrency]]></category><dc:creator><![CDATA[Yield Dev]]></dc:creator><pubDate>Tue, 01 Apr 2025 02:19:31 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1743473405989/b78023ca-581e-4ceb-b5aa-e143d23941ec.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Euler's MEV cluster supports liquidity for going leverage long many of the Pendle PT fixed yield tokens available on sonic.</p>
<p>Accessing leverage in the form of of the PT's underlying token allows for massive leverage on the fixed rate, providing huge positive carry to be short the PT rate.</p>
<p>For example there are two markets for PT tokens on $S staked tokens which earn a fixed yield by selling the variable yield and points exposure of the underlying staked S.</p>
<p>One is the the PT-stS token and the other is the PT-wOS token which earn fixed yield on $S from the beets and origin protocol respectively.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743473396007/685157cd-ed11-49d7-ae8b-eddf9aafa60c.png" alt /></p>
<p>The Euler MEV vault enables these PT tokens to be used as collateral to borrow the PT's underlying $S token in the form of wS (<em>wrapped Sonic</em>). Providing an 8.31x max multiplier on your capital to execute this trade. The PT-stS trade is particularly interesting as it trades at a APY of 11.07% and expires in 57 days.</p>
<p>With wS borrowing at a rate of 6.17%, the maximum return on equity for this position sits at 46.89% assuming the the actual rate of borrowed wS is realized at its current rate.</p>
<p>A nice extra return if you already have an allocation to $S exposure in your portfolio.</p>
<p><strong>However, what if you don't want exposure to the price of $S but still want to participate in this trade?</strong></p>
<p>Due to the unique design of the Euler lending market and the configuration of the MEV cluster, capital allocators can easily participate in this yield while remaining delta neutral.</p>
<p>Since the wS lending vault allows both USDC and PT-stS to be used as collateral, the positions equity can be entirely allocated to USDC thus funding the the entire purchase of PT-stS with a wS loan. This neutralizes all price exposure since the PT-stS will settle to S at maturity and the liability amount is denominated in wS, with the over collateraliztion requirement of the loan being satisfied by our USDC equity.</p>
<p>In this way, we participate in the carry trade without the currency exposure risk. The LTV settings of the wS vault allow us to borrow wS at a 77% max LTV against USDC and 88% max LTV against the PT-stS portion of our position.</p>
<p>We can calculate our max capital multiplier for a multi collateral position like this in this way. (Assuming 100% of our equity is in cash):</p>
<p>$$(M - 1) = 0.77 + 0.88(M - 1)$$</p><p>Our liquidation LTV is also a function of our multiple, as we must weight the LLTV of USDC (<em>78%</em>) and the LLTV of PT-stS (<em>91.5%</em>)</p>
<p>$$\text{LiquidationLTV}_{weighted} = \frac{0.78 + 0.915(M - 1)}{1 + (M - 1)}$$</p><p>The max weighted LTV allows us to run this trade at a multiple of 7.42x our starting capital. Since it's delta neutral, it won't lose USD value based on price moves from the underlying wS token. However, we still need to manage our collateralization ratio to that we do not violate the over collateralization requirements as the price of wS rises.</p>
<p>Note that as the price of wS rises, the LTV of the position will rise much more quickly than the weighted LLTV. This position requires tight risk management.</p>
<p>To allow for some headroom, assume we run this trade at 7x our starting capital. We simply need our capital in the USDC.e vault earning it's variable yield, <em>5.76%</em> at the moment. We can then borrow 7x our starting capital worth of wS, paying <em>6.17</em> currently and immediately swap it for PT-stS for a fixed yield of 11%. These PT-stS are immediately deposited as collateral.</p>
<p>our returns, in APY, are then.</p>
<p>$$(11 - 6.17) \times 7 + 5.76 = 39.57$$</p><p>Using the EVC batch we can easily construct this trade to execute in a single transaction and remain in compliance with our collateral requirements. We simply batch all the transaction data for the individual trade operations outline above, including the swap, into a single <code>evc.batch()</code> call. This process is illustrated, in depth, in this <a target="_blank" href="https://yielddev.io/closing-leveraged-long-on-euler">blog post</a></p>
<p>The flexibility of EVC for batching transactions allows an on chain trader to easily and quickly use a single transaction to open, close and manage a position such as this one.</p>
<p>An advanced capital allocator, could farm this delta neutral yield while waiting to go long $S by having a script ready to swap some or all of the USDC collateral for more PT-stS in an instant.</p>
<p>A wise risk manager would have a script and a keeper bot ready to deleverage the position by swapping some of the PT-stS for wS and paying back some or all of the loan, reducing the liquidation risk if the price rises.</p>
<p>A technical degen might have a script to increase the leverage as the price of wS falls to maintain a capital multiplier of 7x.</p>
<p>All of this position management can be scripted out and executed via evc batch in a single transaction execution while maintaining the capital compliance of the position.</p>
<p>This delta neutral fixed rate trade can be executed across any PT asset whose underlying is borrowable against both USDC and the PT, for example WETH based PT's are also suitable for this trade.</p>
<p>For technical help, visit <a target="_blank" href="https://yielddev.io">https://</a><a target="_blank" href="https://yielddev.com">Yielddev Studio</a></p>
]]></content:encoded></item><item><title><![CDATA[Closing a Pendle PT leveraged Long on Euler With Euler Orderflow Router and EVC with One Transaction]]></title><description><![CDATA[Euler Finance maintains a an incredibly useful tool for aggregating and routing on chain swaps called euler-orderflow-router. The euler-orderflow-router API allows users to fetch payload data for routing on chain swaps and even offers various swap mo...]]></description><link>https://yielddev.io/closing-leveraged-long-on-euler</link><guid isPermaLink="true">https://yielddev.io/closing-leveraged-long-on-euler</guid><category><![CDATA[euler-finance]]></category><category><![CDATA[defi]]></category><category><![CDATA[defi development services]]></category><category><![CDATA[defi development company]]></category><category><![CDATA[yield farming]]></category><category><![CDATA[yield]]></category><category><![CDATA[euler]]></category><category><![CDATA[EVC]]></category><category><![CDATA[pendle]]></category><category><![CDATA[leverage]]></category><dc:creator><![CDATA[Yield Dev]]></dc:creator><pubDate>Fri, 28 Mar 2025 00:56:18 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1743123354399/bbaf15d5-c476-4d1e-b841-2b22b9e8f7ba.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Euler Finance maintains a an incredibly useful tool for aggregating and routing on chain swaps called <a target="_blank" href="https://github.com/euler-xyz/euler-orderflow-router"><code>euler-orderflow-router</code></a>. The <code>euler-orderflow-router</code> API allows users to fetch payload data for routing on chain swaps and even offers various swap modes making it aware of the swap's context.</p>
<p>For example, paying down a specific amount of debt, where the swaps output is meant to deleverage an active borrow position, is achieved by requesting a payload in <code>target debt mode</code> which will return swap data for purchasing a specific amount of the liability asset to pay the debt on a given account down to a specified amount. Additionally, it provides the transaction data for calling the peripheral contract to repay the debt and verify the swapped amount, called the <code>swapVerifier</code>. By fetching this routing data off chain and structuring them in an EVC batch transaction (which defers liquidity checks) we can efficiently and easily payoff a debt using an accounts collateral.</p>
<p>This is a potent tool for risk management, as it allows for deleveraging a position position with no additional collateral. It also allows on chain traders to ratchet up leveraged positions in a controlled manner without looping.</p>
<p>For leveraged traders, it can also be used for unwinding and closing out a leveraged long position entirely by swapping the entirety of the collateral assets and then, using an EVC batch, repaying the debt and leaving us with the net equity of our long position in cash.</p>
<p>In this post we are going to be looking at how this is implemented. Using typescript to fetch the swap routing data from the api and then using solidity (via forge scripts) to write our <code>evc batch</code> execution.</p>
<p>Let us take, for example, our <a target="_blank" href="https://yielddev.io/all-solidity-evc-script">previously discussed long PT-wstkscUSD position</a> which is leveraged against USDC.e liability vault. Since, we are short the rate and the USDC.e borrow spread is compressed perhaps we would like to have a script ready to close out the entire position in one shot and claim our USDC profit.</p>
<p>First, We will need to route our order through the Euler <code>swapper</code> contract by providing specific function call data. Euler Swap works in two parts, first the swap executes the asset swap via whichever DEX route is provided in the data, after that we must call the swap verifier, which acts to reconcile the actual result of the swap with the output amount expected according to the slippage parameter and to execute follow up actions such as claiming the resulting tokens as a vault deposit or repaying debt.</p>
<p>To obtain the transaction data that routes our swap efficiently and accurately and needs to be provided to these two function we call the <code>euler-orderflow-router</code> API, an off chain service which retrieves and formats this data, conveniently delivering it to us for use in our transaction.</p>
<p>So we must write a typescript script which fetches this info for us. let us create a file called: <code>closeToLiability.ts</code></p>
<p>We will import the required libraries and set the swap api endpoint provided by Euler.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { ethers } <span class="hljs-keyword">from</span> <span class="hljs-string">"ethers"</span>;
<span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">"axios"</span>;
<span class="hljs-keyword">import</span> dotenv <span class="hljs-keyword">from</span> <span class="hljs-string">"dotenv"</span>;
<span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> fs <span class="hljs-keyword">from</span> <span class="hljs-string">"fs"</span>;

dotenv.config();

<span class="hljs-keyword">const</span> SWAP_API_URL = <span class="hljs-string">"https://swap.euler.finance"</span>;
</code></pre>
<p>now we will want to also setup an interface for interacting with the EVault contract to gather some basic information.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">interface</span> EVault {
    convertToAssets(amount: bigint): <span class="hljs-built_in">Promise</span>&lt;bigint&gt;;
    balanceOf(account: <span class="hljs-built_in">string</span>): <span class="hljs-built_in">Promise</span>&lt;bigint&gt;;
    previewRedeem(amount: bigint): <span class="hljs-built_in">Promise</span>&lt;bigint&gt;;
}
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getEVaultContract</span>(<span class="hljs-params">address: <span class="hljs-built_in">string</span>, provider: ethers.JsonRpcProvider</span>): <span class="hljs-title">Promise</span>&lt;<span class="hljs-title">EVault</span>&gt; </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> ethers.Contract(
        address,
        [
            <span class="hljs-string">"function convertToAssets(uint256) view returns (uint256)"</span>,
            <span class="hljs-string">"function balanceOf(address) view returns (uint256)"</span>,
            <span class="hljs-string">"function previewRedeem(uint256) view returns (uint256)"</span>,
            <span class="hljs-string">"function debtOf(address) view returns (uint256)"</span>
        ],
        provider
    ) <span class="hljs-keyword">as</span> unknown <span class="hljs-keyword">as</span> EVault;
}
</code></pre>
<p>From here we can create the function to query the data that we are going to need for our swap. This query outlines all the info we will need to provide. In swapper mode, we are using <code>0</code> <code>exact input</code> mode, this means that the swap is going to consume all of the <code>amountIn</code> that we give it and purchase as much <code>tokenOut</code> as possible.</p>
<p>Since we are cashing out our entire position, all of the output token will be delivered to the <code>liabilityVaultAddress</code> which we have set in the <code>receiver</code> field. Once the funds are in the vault we will repay the debt and keep the rest of our balance as a deposit earning interest in the vault.</p>
<p>Note, also, that there is an <code>isRepay</code> field which we have set to false. This field is useful if the resulting output tokens from the swap are meant to pay a specified portion of the debt. This allows the swapper to calculate an input amount that results in purchasing up to the total amount of the debt when paired with swapper mode <code>2</code> target debt mode. however, since we want to swap <strong>all</strong> of our collateral via <code>exactInput</code> mode, setting is repay to true results in triggering an error on the vault <code>EVault__RepayTooMuch</code>. For this reason we will manually repay the debt and deposit the remainder as an interest bearing asset.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getSwapPayload</span>(<span class="hljs-params">
    account: <span class="hljs-built_in">string</span>,
    collateralVaultAddress: <span class="hljs-built_in">string</span>,
    liabilityVaultAddress: <span class="hljs-built_in">string</span>,
    collateralToken: <span class="hljs-built_in">string</span>,
    liabilityToken: <span class="hljs-built_in">string</span>,
    amountIn: <span class="hljs-built_in">string</span>,
</span>) </span>{

    <span class="hljs-keyword">const</span> queryParams = {
        chainId: <span class="hljs-string">"146"</span>, <span class="hljs-comment">// sonic chain</span>
        tokenIn: collateralToken,
        tokenOut: liabilityToken,
        amount: amountIn, <span class="hljs-comment">// the amount to swap </span>
        targetDebt: <span class="hljs-string">"0"</span>, <span class="hljs-comment">// irrelevant in this exactIn flow</span>
        currentDebt: amountIn, <span class="hljs-comment">// irrelevant in this exactIn flow</span>
        receiver: liabilityVaultAddress,
        vaultIn: collateralVaultAddress, <span class="hljs-comment">// left over tokenIn goes here, irrelevent for exactIn flow</span>
        origin: account, <span class="hljs-comment">// account executing the tx</span>
        accountIn: account, <span class="hljs-comment">// account holding the collateral</span>
        accountOut: account, <span class="hljs-comment">// account to swap to, the account that skim will deliver to </span>
        slippage: <span class="hljs-string">"0.15"</span>, <span class="hljs-comment">// 0.15% slippage</span>
        deadline: <span class="hljs-built_in">String</span>(<span class="hljs-built_in">Math</span>.floor(<span class="hljs-built_in">Date</span>.now() / <span class="hljs-number">1000</span>) + <span class="hljs-number">10</span> * <span class="hljs-number">60</span>), <span class="hljs-comment">// 10 minutes from now</span>
        swapperMode: <span class="hljs-string">"0"</span>, <span class="hljs-comment">// exact input mode = 0 </span>
        isRepay: <span class="hljs-string">"false"</span>, <span class="hljs-comment">// we will manually add a call to repay the debt</span>
    };

    <span class="hljs-keyword">const</span> { data: response } = <span class="hljs-keyword">await</span> axios.get(
        <span class="hljs-string">`<span class="hljs-subst">${SWAP_API_URL}</span>/swap`</span>,
        {
            params: queryParams
        }
    );

    <span class="hljs-keyword">return</span> response.data
}
</code></pre>
<p>Now we can set up all the info we need to retrieve our payload in the main function. Here we will need to set address to the actual Euler subaccount which holds our position. we will also fetch the total collateral balance to use as the <code>amountIn</code> in our request.</p>
<p>Once we have received the payload we will write the json data to two separate files, one for each of the required transactions. The first is the swapper data for actually routing the swap, the second is the verifier data which will make sure we got equal to or more output tokens than the minimum amount expected according to the slippage parameter. The verifier will also be responsible for executing the <code>skim</code> function on the liability vault which claims the tokens as a deposit for our account since they will be sent directly to vault after the swap.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">main</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> provider = <span class="hljs-keyword">new</span> ethers.JsonRpcProvider(process.env.SONIC_RPC_URL);
    <span class="hljs-keyword">const</span> address = $EULER_SUBACCOUNT_ADDRESS; <span class="hljs-comment">// the address of the euler subaccount holding the position</span>
    <span class="hljs-keyword">const</span> COLLATERAL_VAULT = <span class="hljs-string">"0xF6E2ddf7a149C171E591C8d58449e371E6dc7570"</span>; <span class="hljs-comment">// PTUSDC Vault</span>
    <span class="hljs-keyword">const</span> LIABILITY_VAULT = <span class="hljs-string">"0x196F3C7443E940911EE2Bb88e019Fd71400349D9"</span> <span class="hljs-comment">// USDC Vault</span>

    <span class="hljs-keyword">const</span> COLLATERAL_TOKEN = <span class="hljs-string">"0xBe27993204Ec64238F71A527B4c4D5F4949034C3"</span>; <span class="hljs-comment">// PTUSDC</span>
    <span class="hljs-keyword">const</span> LIABILITY_TOKEN = <span class="hljs-string">"0x29219dd400f2Bf60E5a23d13Be72B486D4038894"</span>; <span class="hljs-comment">// USDC</span>

    <span class="hljs-keyword">const</span> evaultContract = <span class="hljs-keyword">await</span> getEVaultContract(COLLATERAL_VAULT, provider);
    <span class="hljs-keyword">const</span> collateralBalance = <span class="hljs-keyword">await</span> evaultContract.balanceOf(address);
    <span class="hljs-keyword">const</span> withdrawAmount = <span class="hljs-keyword">await</span> evaultContract.previewRedeem(collateralBalance);

    <span class="hljs-keyword">const</span> swapPayload = <span class="hljs-keyword">await</span> getSwapPayload(
        address,
        COLLATERAL_VAULT,
        LIABILITY_VAULT,
        COLLATERAL_TOKEN,
        LIABILITY_TOKEN,
        withdrawAmount.toString()
    );

    <span class="hljs-keyword">await</span> writeToJsonFile(swapPayload.swap, <span class="hljs-string">"./script/closeSwapPayload.json"</span>);
    <span class="hljs-keyword">await</span> writeToJsonFile(swapPayload.verify, <span class="hljs-string">"./script/closeSwapVerify.json"</span>);
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Payloads written to files"</span>)
}
main().catch(<span class="hljs-function">(<span class="hljs-params">error</span>) =&gt;</span> {
    <span class="hljs-built_in">console</span>.error(error);
    process.exitCode = <span class="hljs-number">1</span>;
});
</code></pre>
<p>Now that we have the payload written to a json file, we can write a solidity script to execute on that data.</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// SPDX-License-Identifier: UNLICENSED</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.28;</span>

<span class="hljs-keyword">import</span> {<span class="hljs-title">Script</span>, <span class="hljs-title">console</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"forge-std/Script.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">SonicLib</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../test/common/SonicLib.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"evc/interfaces/IEthereumVaultConnector.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">EVault</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"evk/EVault/EVault.sol"</span>;
</code></pre>
<p>Setup our script contract</p>
<pre><code class="lang-solidity"><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">ClosePositionScript</span> <span class="hljs-keyword">is</span> <span class="hljs-title">Script</span> </span>{
    <span class="hljs-keyword">address</span> _e_account1;
}
</code></pre>
<p>With two utility functions, One to get the json file data, the other to derive our Euler subaccount.</p>
<pre><code class="lang-solidity">    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getSubaccount</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _account, <span class="hljs-keyword">uint256</span> _index</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">address</span></span>) </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">address</span>(<span class="hljs-keyword">uint160</span>(<span class="hljs-keyword">uint160</span>(_account)<span class="hljs-operator">^</span>_index));
    }
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getJsonFile</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> _filePath</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span></span>) </span>{
        <span class="hljs-keyword">return</span> vm.readFile(_filePath);
    }
</code></pre>
<p>Next our run function holds the main logic</p>
<pre><code class="lang-solidity">    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">run</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        borrower <span class="hljs-operator">=</span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>;
        _e_account1 <span class="hljs-operator">=</span> getSubaccount(borrower, <span class="hljs-number">1</span>);
}
</code></pre>
<p>Now we need to setup our vault addresses and gather the payload info:</p>
<pre><code class="lang-solidity">    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">run</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        borrower <span class="hljs-operator">=</span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>;
        _e_account1 <span class="hljs-operator">=</span> getSubaccount(borrower, <span class="hljs-number">1</span>);

        <span class="hljs-keyword">address</span> PT_USDC_VAULT <span class="hljs-operator">=</span> SonicLib.EULER_PT_USDC_VAULT;
        <span class="hljs-keyword">address</span> USDC_VAULT <span class="hljs-operator">=</span> SonicLib.EULER_USDC_VAULT;

        <span class="hljs-keyword">address</span> PT_USDC <span class="hljs-operator">=</span> SonicLib.PT_USDC;
        <span class="hljs-keyword">address</span> USDC <span class="hljs-operator">=</span> SonicLib.USDC;

        <span class="hljs-keyword">address</span> evc <span class="hljs-operator">=</span> SonicLib.Euler_Vault_Connector;

        <span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> swapJson <span class="hljs-operator">=</span> getJsonFile(<span class="hljs-string">"./script/closeSwapPayload.json"</span>);
        <span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> verifyJson <span class="hljs-operator">=</span> getJsonFile(<span class="hljs-string">"./script/closeSwapVerify.json"</span>);
    }
</code></pre>
<p>Note that, in order to open a json file via <code>vm.readFile</code> we need to add permissions in our <code>foundry.toml</code> configuration. To do this, simply add the following line:</p>
<pre><code class="lang-toml"><span class="hljs-comment"># foundry.toml</span>
<span class="hljs-attr">fs_permissions</span> = [{ access = <span class="hljs-string">"read"</span>, path = <span class="hljs-string">"./script"</span>}]
</code></pre>
<p>Now we fetch the balance of our collateral shares, which we will be redeeming in order to swap and then construct the EVC Batch transaction, consisting of 4 transaction items. Let's go over each item in the batch to illustrate the process.</p>
<p>First, we construct a <code>BatchItem</code> which executes the redemption of our total collateral <code>shares</code> balance from our subaccount, in order to execute the swap; we set the receiver of the assets from this redemption to the <code>swapperAddress</code> in our <code>swapJson</code> file.</p>
<p>Next, our second <code>BatchItem</code> will call the swapper contract at <code>swapperAddress</code> with the routing data we have saved in our <code>swapJson</code> file. This execution will swap our collateral assets for the liability asset.</p>
<p>Third, we construct a call to the <code>verifierAddress</code> this contract holds logic that verifies our swap output was executed with acceptable slippage. Further, since it is aware of the context of our swap, it also executes the logic to call <code>skim()</code> on the vault address we have set as the receiver, in this case the USDC.e vault. The <code>skim()</code> call converts the assets which were swapped into the vault into deposits on behalf of our <code>accountOut</code> which we set to our Euler subaccount when requesting the payload. After this is called we will have shares representing our deposit.</p>
<p>Finally, we call the liability vault (USDC.e vault) to repay our outstanding debt with the newly minted shares we received from our asset deposit. Here we pass in <code>type(uint256).max</code> to use our entire balance to payoff our <code>owed</code> amount in the vault. Any remaining shares after the debt is paid off will be our remaining equity in the position in cash in the vault.</p>
<pre><code class="lang-solidity">        <span class="hljs-keyword">uint256</span> shares <span class="hljs-operator">=</span> EVault(PT_USDC_VAULT).balanceOf(_e_account1);

        IEVC.BatchItem[] <span class="hljs-keyword">memory</span> items <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> IEVC.BatchItem[](<span class="hljs-number">4</span>);
        items[<span class="hljs-number">0</span>] <span class="hljs-operator">=</span> IEVC.BatchItem({
            onBehalfOfAccount: _e_account1,
            targetContract: PT_USDC_VAULT,
            <span class="hljs-built_in">value</span>: <span class="hljs-number">0</span>,
            data: <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodeWithSelector</span>(
                EVault.redeem.<span class="hljs-built_in">selector</span>,
                shares,
                vm.parseJsonAddress(swapJson, <span class="hljs-string">".swapperAddress"</span>),
                _e_account1
            )
        });
        items[<span class="hljs-number">1</span>] <span class="hljs-operator">=</span> IEVC.BatchItem({
            onBehalfOfAccount: _e_account1,
            targetContract: vm.parseJsonAddress(swapJson, <span class="hljs-string">".swapperAddress"</span>),
            <span class="hljs-built_in">value</span>: <span class="hljs-number">0</span>,
            data: vm.parseJsonBytes(swapJson, <span class="hljs-string">".swapperData"</span>)
        });
        items[<span class="hljs-number">2</span>] <span class="hljs-operator">=</span> IEVC.BatchItem({
            onBehalfOfAccount: _e_account1,
            targetContract: vm.parseJsonAddress(verifyJson, <span class="hljs-string">".verifierAddress"</span>),
            <span class="hljs-built_in">value</span>: <span class="hljs-number">0</span>,
            data: vm.parseJsonBytes(verifyJson, <span class="hljs-string">".verifierData"</span>)
        });
        items[<span class="hljs-number">3</span>] <span class="hljs-operator">=</span> IEVC.BatchItem({
            onBehalfOfAccount: _e_account1,
            targetContract: USDC_VAULT,
            <span class="hljs-built_in">value</span>: <span class="hljs-number">0</span>,
            data: <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodeWithSelector</span>(EVault.repayWithShares.<span class="hljs-built_in">selector</span>, <span class="hljs-keyword">type</span>(<span class="hljs-keyword">uint256</span>).<span class="hljs-built_in">max</span>, _e_account1)
        });

        vm.startBroadcast();
        IEVC(evc).batch(items);
        vm.stopBroadcast();

        console.log(<span class="hljs-string">"PT BALANCE: "</span>, EVault(PT_USDC_VAULT).balanceOf(_e_account1));
        console.log(<span class="hljs-string">"USDC DEBT: "</span>, EVault(USDC_VAULT).debtOf(_e_account1));
        console.log(<span class="hljs-string">"USDC BALANCE: "</span>, EVault(USDC_VAULT).convertToAssets(EVault(USDC_VAULT).balanceOf(_e_account1)));
</code></pre>
<p>We also add a few log statements to print out our position info ensuring that our collateral balance and debt are both zero. Additionally, we print our USDC.e vault asset balance after the entire transaction is complete to see our remaining assets after the position had been closed.</p>
<p>we can run this script in a fork to see how it all shakes out:</p>
<pre><code class="lang-bash">forge script --fork-url https://rpc.soniclabs.com script/closePosition.s.sol --sender <span class="hljs-variable">$SENDER</span>
</code></pre>
<p>Our logs should match expectations.</p>
<pre><code class="lang-bash">  PT BALANCE:  0
  ETH DEBT:  0
  USDC BALANCE:  10666666666
</code></pre>
<p>With this example we illustrate the utility of the <code>euler-orderflow-router</code> tool for managing highly leveraged on chain position. By using EVC batch we can defer all account solvency checks until the end of position closing process, allowing us to swap our entire position, close out our margin and end up with our positions equity in cash. All of this can be done in a single transaction with no additional capital or liquidity.</p>
<p>Knowledge of EVC and its peripheral tools provide a massive advantage to on chain traders and yield farmers, allowing them to construct flexible, use case specific scripts for quickly managing and maintaining high yield positions while also taking advantage of capital efficiency inherent in the protocol.</p>
<p>At <a target="_blank" href="https://yielddev.com">YieldDev Studio</a> we specialize in developing cutting edge, bespoke Defi applications and tools.</p>
<p>Feel free to contact me on X -&gt; <a target="_blank" href="https://x.com/yielddev">@yielddev</a></p>
<p>Full Code:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { ethers } <span class="hljs-keyword">from</span> <span class="hljs-string">"ethers"</span>;
<span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">"axios"</span>;
<span class="hljs-keyword">import</span> dotenv <span class="hljs-keyword">from</span> <span class="hljs-string">"dotenv"</span>;

<span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> fs <span class="hljs-keyword">from</span> <span class="hljs-string">"fs"</span>;

dotenv.config();

<span class="hljs-keyword">const</span> SWAP_API_URL = <span class="hljs-string">"https://swap.euler.finance"</span>;
<span class="hljs-keyword">interface</span> EVault {
    convertToAssets(amount: bigint): <span class="hljs-built_in">Promise</span>&lt;bigint&gt;;
    balanceOf(account: <span class="hljs-built_in">string</span>): <span class="hljs-built_in">Promise</span>&lt;bigint&gt;;
    previewRedeem(amount: bigint): <span class="hljs-built_in">Promise</span>&lt;bigint&gt;;
}
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getEVaultContract</span>(<span class="hljs-params">address: <span class="hljs-built_in">string</span>, provider: ethers.JsonRpcProvider</span>): <span class="hljs-title">Promise</span>&lt;<span class="hljs-title">EVault</span>&gt; </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> ethers.Contract(
        address,
        [
            <span class="hljs-string">"function convertToAssets(uint256) view returns (uint256)"</span>,
            <span class="hljs-string">"function balanceOf(address) view returns (uint256)"</span>,
            <span class="hljs-string">"function previewRedeem(uint256) view returns (uint256)"</span>,
            <span class="hljs-string">"function debtOf(address) view returns (uint256)"</span>
        ],
        provider
    ) <span class="hljs-keyword">as</span> unknown <span class="hljs-keyword">as</span> EVault;
}

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getSwapPayload</span>(<span class="hljs-params">
    account: <span class="hljs-built_in">string</span>,
    collateralVaultAddress: <span class="hljs-built_in">string</span>,
    liabilityVaultAddress: <span class="hljs-built_in">string</span>,
    collateralToken: <span class="hljs-built_in">string</span>,
    liabilityToken: <span class="hljs-built_in">string</span>,
    amountIn: <span class="hljs-built_in">string</span>,
</span>) </span>{

    <span class="hljs-keyword">const</span> queryParams = {
        chainId: <span class="hljs-string">"146"</span>,
        tokenIn: collateralToken,
        tokenOut: liabilityToken,
        amount: amountIn, <span class="hljs-comment">// the amount to swap </span>
        targetDebt: <span class="hljs-string">"0"</span>, <span class="hljs-comment">// irrelevant in this exactIn flow</span>
        currentDebt: amountIn, <span class="hljs-comment">// irrelevant in this exactIn flow</span>
        receiver: liabilityVaultAddress,
        vaultIn: collateralVaultAddress, <span class="hljs-comment">// left over tokenIn goes here, irrelevent for exactIn flow</span>
        origin: account, <span class="hljs-comment">// account executing the tx</span>
        accountIn: account, <span class="hljs-comment">// account holding the collateral</span>
        accountOut: account, <span class="hljs-comment">// account to swap to, the account that skim will deliver to </span>
        slippage: <span class="hljs-string">"0.15"</span>, <span class="hljs-comment">// 0.15% slippage</span>
        deadline: <span class="hljs-built_in">String</span>(<span class="hljs-built_in">Math</span>.floor(<span class="hljs-built_in">Date</span>.now() / <span class="hljs-number">1000</span>) + <span class="hljs-number">10</span> * <span class="hljs-number">60</span>), <span class="hljs-comment">// 10 minutes from now</span>
        swapperMode: <span class="hljs-string">"0"</span>, <span class="hljs-comment">// exact input mode = 0 </span>
        isRepay: <span class="hljs-string">"false"</span>, <span class="hljs-comment">// we will manually add a call to repay the debt</span>
    };

    <span class="hljs-keyword">const</span> { data: response } = <span class="hljs-keyword">await</span> axios.get(
        <span class="hljs-string">`<span class="hljs-subst">${SWAP_API_URL}</span>/swap`</span>,
        {
            params: queryParams
        }
    );

    <span class="hljs-keyword">return</span> response.data
}

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">writeToJsonFile</span>(<span class="hljs-params">data: <span class="hljs-built_in">any</span>, filename: <span class="hljs-built_in">string</span></span>) </span>{
    fs.writeFileSync(filename, <span class="hljs-built_in">JSON</span>.stringify(data, <span class="hljs-literal">null</span>, <span class="hljs-number">2</span>));
}

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">main</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> provider = <span class="hljs-keyword">new</span> ethers.JsonRpcProvider(process.env.SONIC_RPC_URL);
    <span class="hljs-keyword">const</span> address = <span class="hljs-string">""</span>; <span class="hljs-comment">// acc1</span>
    <span class="hljs-keyword">const</span> COLLATERAL_VAULT = <span class="hljs-string">"0xF6E2ddf7a149C171E591C8d58449e371E6dc7570"</span>; <span class="hljs-comment">// PTUSDC Vault</span>
    <span class="hljs-keyword">const</span> LIABILITY_VAULT = <span class="hljs-string">"0x196F3C7443E940911EE2Bb88e019Fd71400349D9"</span> <span class="hljs-comment">// USDC Vault</span>

    <span class="hljs-keyword">const</span> COLLATERAL_TOKEN = <span class="hljs-string">"0xBe27993204Ec64238F71A527B4c4D5F4949034C3"</span>; <span class="hljs-comment">// PTUSDC</span>
    <span class="hljs-keyword">const</span> LIABILITY_TOKEN = <span class="hljs-string">"0x29219dd400f2Bf60E5a23d13Be72B486D4038894"</span>; <span class="hljs-comment">// USDC</span>

    <span class="hljs-keyword">const</span> evaultContract = <span class="hljs-keyword">await</span> getEVaultContract(COLLATERAL_VAULT, provider);
    <span class="hljs-keyword">const</span> collateralBalance = <span class="hljs-keyword">await</span> evaultContract.balanceOf(address);
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"COLLATERAL BALANCE: "</span>, collateralBalance)
    <span class="hljs-keyword">const</span> withdrawAmount = <span class="hljs-keyword">await</span> evaultContract.previewRedeem(collateralBalance);

    <span class="hljs-keyword">const</span> swapPayload = <span class="hljs-keyword">await</span> getSwapPayload(
        address,
        COLLATERAL_VAULT,
        LIABILITY_VAULT,
        COLLATERAL_TOKEN,
        LIABILITY_TOKEN,
        withdrawAmount.toString()
    );

    <span class="hljs-keyword">await</span> writeToJsonFile(swapPayload.swap, <span class="hljs-string">"./script/closeSwapPayload.json"</span>);
    <span class="hljs-keyword">await</span> writeToJsonFile(swapPayload.verify, <span class="hljs-string">"./script/closeSwapVerify.json"</span>);
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Payloads written to files"</span>)
}
main().catch(<span class="hljs-function">(<span class="hljs-params">error</span>) =&gt;</span> {
    <span class="hljs-built_in">console</span>.error(error);
    process.exitCode = <span class="hljs-number">1</span>;
});
</code></pre>
<pre><code class="lang-solidity"><span class="hljs-comment">// SPDX-License-Identifier: UNLICENSED</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.28;</span>

<span class="hljs-keyword">import</span> {<span class="hljs-title">Script</span>, <span class="hljs-title">console</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"forge-std/Script.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">SonicLib</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../test/common/SonicLib.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"evc/interfaces/IEthereumVaultConnector.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">EVault</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"evk/EVault/EVault.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">ClosePositionScript</span> <span class="hljs-keyword">is</span> <span class="hljs-title">Script</span> </span>{
    <span class="hljs-keyword">address</span> borrower;
    <span class="hljs-keyword">address</span> _e_account1;
    <span class="hljs-keyword">address</span> _e_account2;
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getSubaccount</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _account, <span class="hljs-keyword">uint256</span> _index</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">address</span></span>) </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">address</span>(<span class="hljs-keyword">uint160</span>(<span class="hljs-keyword">uint160</span>(_account)<span class="hljs-operator">^</span>_index));
    }
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getJsonFile</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> _filePath</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span></span>) </span>{
        <span class="hljs-keyword">return</span> vm.readFile(_filePath);
    } 

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">run</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        borrower <span class="hljs-operator">=</span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>;
        _e_account1 <span class="hljs-operator">=</span> getSubaccount(borrower, <span class="hljs-number">1</span>);

        <span class="hljs-keyword">address</span> PT_USDC_VAULT <span class="hljs-operator">=</span> SonicLib.EULER_PT_USDC_VAULT;
        <span class="hljs-keyword">address</span> USDC_VAULT <span class="hljs-operator">=</span> SonicLib.EULER_USDC_VAULT;

        <span class="hljs-keyword">address</span> PT_USDC <span class="hljs-operator">=</span> SonicLib.PT_USDC;
        <span class="hljs-keyword">address</span> USDC <span class="hljs-operator">=</span> SonicLib.USDC;

        <span class="hljs-keyword">address</span> evc <span class="hljs-operator">=</span> SonicLib.Euler_Vault_Connector;

        <span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> swapJson <span class="hljs-operator">=</span> getJsonFile(<span class="hljs-string">"./script/closeSwapPayload.json"</span>);
        <span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> verifyJson <span class="hljs-operator">=</span> getJsonFile(<span class="hljs-string">"./script/closeSwapVerify.json"</span>);

        <span class="hljs-keyword">uint256</span> shares <span class="hljs-operator">=</span> EVault(PT_USDC_VAULT).balanceOf(_e_account1);

        IEVC.BatchItem[] <span class="hljs-keyword">memory</span> items <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> IEVC.BatchItem[](<span class="hljs-number">4</span>);
        items[<span class="hljs-number">0</span>] <span class="hljs-operator">=</span> IEVC.BatchItem({
            onBehalfOfAccount: _e_account1,
            targetContract: PT_USDC_VAULT,
            <span class="hljs-built_in">value</span>: <span class="hljs-number">0</span>,
            data: <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodeWithSelector</span>(
                EVault.redeem.<span class="hljs-built_in">selector</span>,
                shares,
                vm.parseJsonAddress(swapJson, <span class="hljs-string">".swapperAddress"</span>),
                _e_account1
            )
        });
        items[<span class="hljs-number">1</span>] <span class="hljs-operator">=</span> IEVC.BatchItem({
            onBehalfOfAccount: _e_account1,
            targetContract: vm.parseJsonAddress(swapJson, <span class="hljs-string">".swapperAddress"</span>),
            <span class="hljs-built_in">value</span>: <span class="hljs-number">0</span>,
            data: vm.parseJsonBytes(swapJson, <span class="hljs-string">".swapperData"</span>)
        });
        items[<span class="hljs-number">2</span>] <span class="hljs-operator">=</span> IEVC.BatchItem({
            onBehalfOfAccount: _e_account1,
            targetContract: vm.parseJsonAddress(verifyJson, <span class="hljs-string">".verifierAddress"</span>),
            <span class="hljs-built_in">value</span>: <span class="hljs-number">0</span>,
            data: vm.parseJsonBytes(verifyJson, <span class="hljs-string">".verifierData"</span>)
        });
        items[<span class="hljs-number">3</span>] <span class="hljs-operator">=</span> IEVC.BatchItem({
            onBehalfOfAccount: _e_account1,
            targetContract: USDC_VAULT,
            <span class="hljs-built_in">value</span>: <span class="hljs-number">0</span>,
            data: <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodeWithSelector</span>(EVault.repayWithShares.<span class="hljs-built_in">selector</span>, <span class="hljs-keyword">type</span>(<span class="hljs-keyword">uint256</span>).<span class="hljs-built_in">max</span>, _e_account1)
        });

        vm.startBroadcast();
        IEVC(evc).batch(items);
        vm.stopBroadcast();

        console.log(<span class="hljs-string">"PT BALANCE: "</span>, EVault(PT_USDC_VAULT).balanceOf(_e_account1));
        console.log(<span class="hljs-string">"USDC DEBT: "</span>, EVault(USDC_VAULT).debtOf(_e_account1));
        console.log(<span class="hljs-string">"USDC BALANCE: "</span>, EVault(USDC_VAULT).convertToAssets(EVault(USDC_VAULT).balanceOf(_e_account1)));
        <span class="hljs-comment">// forge script --fork-url https://rpc.soniclabs.com script/closePosition.s.sol --sender $SENDER</span>

    }
}
</code></pre>
]]></content:encoded></item><item><title><![CDATA[EVC For On-chain Management: Migrate a Leveraged Position from Silo to Euler]]></title><description><![CDATA[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 th...]]></description><link>https://yielddev.io/all-solidity-evc-script</link><guid isPermaLink="true">https://yielddev.io/all-solidity-evc-script</guid><category><![CDATA[defi]]></category><category><![CDATA[defi development company]]></category><category><![CDATA[defi development services]]></category><category><![CDATA[Solidity]]></category><category><![CDATA[Ethereum]]></category><category><![CDATA[EVC]]></category><category><![CDATA[euler]]></category><category><![CDATA[yield farming]]></category><category><![CDATA[yield]]></category><dc:creator><![CDATA[Yield Dev]]></dc:creator><pubDate>Sun, 16 Mar 2025 18:12:51 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1742088565244/285c3901-0c49-48e1-90ae-88a3620f864d.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Euler's <em>Ethereum Vault Connector</em> (<strong>EVC</strong>) 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.</p>
<p>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.</p>
<p>One of its most powerful features for scripting on chain trades is it's <code>batch</code> 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 <strong>checks deferred execution context</strong> for batched operations.</p>
<p>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.</p>
<p>The common, manual procedure of <strong>"looping"</strong> 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.</p>
<p>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.</p>
<p>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.</p>
<p>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.</p>
<p>Let's Jump into the code and go over some of the finer points of integrating with these protocols.</p>
<p>First we will need to import the contract interfaces that we will be interacting with</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// EvcShift.sol</span>
<span class="hljs-comment">// SPDX-License-Identifier: MIT</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.28;</span>
<span class="hljs-keyword">import</span> { <span class="hljs-title">ISilo</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"silo-core/contracts/interfaces/ISilo.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">IERC20</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts/token/ERC20/IERC20.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"evc/interfaces/IEthereumVaultConnector.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">EVault</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"evk/EVault/EVault.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">EVCUtil</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"evc/utils/EVCUtil.sol"</span>;
</code></pre>
<p>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:</p>
<pre><code class="lang-solidity"><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">EvcShift</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _evc</span>) <span class="hljs-title">EVCUtil</span>(<span class="hljs-params">_evc</span>) </span>{}
}
</code></pre>
<p>Next we need to define three utility functions for our operation. A view function to read our repayment and withdrawal amounts from <code>silo</code>, 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 <code>repaySiloAndWithdraw</code> will be called from inside our batch, since an external call from the evc to a contract that is not EVC aware, sees the <code>EVC</code> address as <code>msg.sender</code> in order that we may pay the debt from funds held in this contract, we write a special purpose function so that silo's <code>transferFrom</code> call see's our contract as <code>msg.sender</code></p>
<pre><code class="lang-solidity">    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">repaySiloAndWithdraw</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> _silo,
        <span class="hljs-keyword">address</span> _collateralSilo,
        <span class="hljs-keyword">address</span> _borrower,
        <span class="hljs-keyword">uint256</span> _amount,
        <span class="hljs-keyword">uint256</span> _maxWithdraw
    </span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> </span>{
        IERC20(ISilo(_silo).asset()).approve(<span class="hljs-keyword">address</span>(_silo), _amount);
        ISilo(_silo).repay(_amount, _borrower);
        ISilo(_collateralSilo).withdraw(_maxWithdraw, <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), _borrower, ISilo.CollateralType.Protected);
    }
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getMigrationAmounts</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> _repaySilo,
        <span class="hljs-keyword">address</span> _collateralSilo,
        <span class="hljs-keyword">address</span> _collateralShare,
        <span class="hljs-keyword">address</span> _borrower
    </span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span>, <span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">uint256</span> _amount <span class="hljs-operator">=</span> ISilo(_repaySilo).maxRepay(_borrower);
        <span class="hljs-keyword">uint256</span> _maxWithdraw <span class="hljs-operator">=</span> ISilo(_collateralSilo).convertToAssets(
            IERC20(_collateralShare).balanceOf(<span class="hljs-keyword">address</span>(_borrower)),
            ISilo.AssetType.Protected
        );
        <span class="hljs-keyword">return</span> (_amount, _maxWithdraw);
    }
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">approveDepositVault</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> _collateralAsset,
        <span class="hljs-keyword">address</span> _depositVault,
        <span class="hljs-keyword">uint256</span> _amount
    </span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> </span>{
        IERC20(_collateralAsset).approve(<span class="hljs-keyword">address</span>(_depositVault), _amount);
    }
</code></pre>
<p>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 <code>_collateralShare</code> 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 <code>_borrower</code> 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 <code>EvcShift</code> contract prior to executing our <code>migrateLoan</code> function.</p>
<p>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 <code>_e_account</code> Euler sub account is in fact a sub account of the borrower to prevent any mistakes.</p>
<p>Also, note that we provide <code>_e_account</code> 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 <code>migrateLoan</code> function.</p>
<p>We will execute both of these actions in the script we write to deploy and execute this transaction. Additionally, we will approve our <code>evcShift</code> contract as an operator in the <code>evc</code> context to act on behalf of our <code>_e_account</code>. This will all be done in solidity using Forge's scripting.</p>
<p>Further, in our <code>migrateLoan</code> function we have used our utility functions to get the borrowers max repayment and withdraw amounts from the <code>Silo</code> vaults and have preemptively issued an allowance for the Euler <code>_depositVault</code> to eventually transfer our collateral assets allowing for deposit.</p>
<pre><code class="lang-solidity">    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">migrateLoan</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> _repaySilo,
        <span class="hljs-keyword">address</span> _collateralSilo,
        <span class="hljs-keyword">address</span> _borrowToken,
        <span class="hljs-keyword">address</span> _collateralShare,
        <span class="hljs-keyword">address</span> _borrower,
        <span class="hljs-keyword">address</span> _e_account,
        <span class="hljs-keyword">address</span> _depositVault,
        <span class="hljs-keyword">address</span> _borrowVault
    </span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        <span class="hljs-built_in">require</span>(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span> _borrower, <span class="hljs-string">"Only borrower can call this function"</span>);
        <span class="hljs-built_in">require</span>(_haveCommonOwner(_borrower, _e_account), <span class="hljs-string">"_e_account must be subaccount"</span>);
        (<span class="hljs-keyword">uint256</span> _amount, <span class="hljs-keyword">uint256</span> _maxWithdraw) <span class="hljs-operator">=</span> getMigrationAmounts(_repaySilo, _collateralSilo, _collateralShare, _borrower);
        approveDepositVault(ISilo(_collateralSilo).asset(), _depositVault, _maxWithdraw);
    }
</code></pre>
<p>Now we are ready to define our batch of operations to securely migrate this position. This is also in our <code>migrateLoan</code> function. First we declare a list of our <code>BatchItems</code>, we will use 7 operations in this batch.</p>
<pre><code class="lang-solidity">        IEVC.BatchItem[] <span class="hljs-keyword">memory</span> items <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> IEVC.BatchItem[](<span class="hljs-number">6</span>);
</code></pre>
<p>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 <code>onBehalfOfAccount</code> as <code>address(0)</code> this is because the will use a delegate call, preserving the <code>msg.sender</code> and authenticating the caller (our <code>evcShift</code> contract) as an operator for the <code>_e_account</code> for which we are enabling.</p>
<pre><code class="lang-solidity">        items[<span class="hljs-number">0</span>] <span class="hljs-operator">=</span> IEVC.BatchItem({
            onBehalfOfAccount: <span class="hljs-keyword">address</span>(<span class="hljs-number">0</span>),
            targetContract: <span class="hljs-keyword">address</span>(evc),
            <span class="hljs-built_in">value</span>: <span class="hljs-number">0</span>,
            data: <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodeWithSelector</span>(IEVC.enableCollateral.<span class="hljs-built_in">selector</span>, _e_account, _borrowVault)
        });
        items[<span class="hljs-number">1</span>] <span class="hljs-operator">=</span> IEVC.BatchItem({
            onBehalfOfAccount: <span class="hljs-keyword">address</span>(<span class="hljs-number">0</span>),
            targetContract: <span class="hljs-keyword">address</span>(evc),
            <span class="hljs-built_in">value</span>: <span class="hljs-number">0</span>,
            data: <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodeWithSelector</span>(IEVC.enableCollateral.<span class="hljs-built_in">selector</span>, _e_account, _depositVault)
        });
        items[<span class="hljs-number">2</span>] <span class="hljs-operator">=</span> IEVC.BatchItem({
            onBehalfOfAccount: <span class="hljs-keyword">address</span>(<span class="hljs-number">0</span>),
            targetContract: <span class="hljs-keyword">address</span>(evc),
            <span class="hljs-built_in">value</span>: <span class="hljs-number">0</span>,
            data: <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodeWithSelector</span>(IEVC.enableController.<span class="hljs-built_in">selector</span>, _e_account, _borrowVault)
        });
</code></pre>
<p>Once the vaults are enabled, we can take a borrow out of the <code>_borrowVault</code> on behalf of our <code>_e_account</code>. We have taken this loan against the <code>_e_account</code> with the receiver of the funds being our <code>EvcShift</code> contract. Since the <code>_borrowVault</code> is EVC aware it authenticates that <code>EvcShift</code> contract we are calling from is authorized to act on behalf of our <code>_e_account</code>.</p>
<pre><code class="lang-solidity">        items[<span class="hljs-number">3</span>] <span class="hljs-operator">=</span> IEVC.BatchItem({
            onBehalfOfAccount: _e_account,
            targetContract: _borrowVault,
            <span class="hljs-built_in">value</span>: <span class="hljs-number">0</span>,
            data: <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodeWithSelector</span>(EVault.borrow.<span class="hljs-built_in">selector</span>, _amount, <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>))
        });
</code></pre>
<p>Once we have the USDC.e to pay off our loan we can now call our <code>repaySiloAndWithdraw</code> function, paying off the debt of <code>_borrower</code> since anyone can repay anyone else's debt in silo, no special permissions are needed here. The <code>repaySilAndWithdraw</code> function will also call withdraw on the collateral silo. Now, there should be no liability in the <code>silo</code> vault and we can now withdraw the collateral asset's into our <code>EvcShift</code> contract. Recall that we would need to have already granted this authority to our <code>EvcShift</code> contract by having our <code>_borrower</code> EOA approve an allowance.</p>
<pre><code class="lang-solidity">        items[<span class="hljs-number">4</span>] <span class="hljs-operator">=</span> IEVC.BatchItem({
            onBehalfOfAccount: <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>),
            targetContract: <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>),
            <span class="hljs-built_in">value</span>: <span class="hljs-number">0</span>,
            data: <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodeWithSelector</span>(<span class="hljs-built_in">this</span>.repaySiloAndWithdraw.<span class="hljs-built_in">selector</span>, _repaySilo, _collateralSilo, _borrower, _amount, _maxWithdraw)
</code></pre>
<p>Finally, to prevent all the previous operations from reverting, we deposit our collateral into the Euler <code>_depositVault</code> to satisfy our solvency requirements created by the loan. Since the collateral assets were withdrawn into our <code>EvcShift</code> contract we call this function on behalf of the <code>EvcShift</code> itself with the <code>_e_account</code> as the receiver.</p>
<pre><code class="lang-solidity">        items[<span class="hljs-number">5</span>] <span class="hljs-operator">=</span> IEVC.BatchItem({
            onBehalfOfAccount: <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>),
            targetContract: _depositVault,
            <span class="hljs-built_in">value</span>: <span class="hljs-number">0</span>,
            data: <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodeWithSelector</span>(
                EVault.deposit.<span class="hljs-built_in">selector</span>,
                _maxWithdraw,
                _e_account
            )
        });
</code></pre>
<p>Now that we have structured all the operations in our batch we can execute it through the <code>evc</code> contract and have the <code>EvcShift</code> contract release its operational authority over our <code>_e_account</code> for safety</p>
<pre><code class="lang-solidity">        evc.batch(items);
        evc.setAccountOperator(_e_account, <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), <span class="hljs-literal">false</span>);
</code></pre>
<p>Now we can write our deploy and execution script. Start by importing the necessary contracts</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// SPDX-License-Identifier: MIT</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.28;</span>

<span class="hljs-keyword">import</span> {<span class="hljs-title">Script</span>, <span class="hljs-title">console</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"forge-std/Script.sol"</span>;

<span class="hljs-keyword">import</span> {<span class="hljs-title">EvcShift</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../src/EvcShift.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">ISiloConfig</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"silo-core/contracts/interfaces/ISiloConfig.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">ISilo</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"silo-core/contracts/interfaces/ISilo.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">SonicLib</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../test/common/SonicLib.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"evc/interfaces/IEthereumVaultConnector.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">EVault</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"evk/EVault/EVault.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">IERC20</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts/token/ERC20/IERC20.sol"</span>;
</code></pre>
<p>Next, we set up the script, we will also add a utility function to derive our subaccount according to the EVC context:</p>
<pre><code class="lang-solidity"><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">EvcShiftScript</span> <span class="hljs-keyword">is</span> <span class="hljs-title">Script</span> </span>{
    EvcShift <span class="hljs-keyword">public</span> evcShift;
    <span class="hljs-keyword">address</span> borrower;
    ISiloConfig siloConfig;
    <span class="hljs-keyword">address</span> ptUSDCSilo;
    <span class="hljs-keyword">address</span> usdcSilo;

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getSubaccount</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _account, <span class="hljs-keyword">uint256</span> _index</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">address</span></span>) </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">address</span>(<span class="hljs-keyword">uint160</span>(<span class="hljs-keyword">uint160</span>(_account)<span class="hljs-operator">^</span>_index));
    }
}
</code></pre>
<p>Next we define our <code>run</code> function, start by getting our silo config data:</p>
<pre><code class="lang-solidity">    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">run</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        borrower <span class="hljs-operator">=</span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>;
        <span class="hljs-keyword">address</span> subaccount <span class="hljs-operator">=</span> getSubaccount(borrower, <span class="hljs-number">1</span>);
        siloConfig <span class="hljs-operator">=</span> ISiloConfig(SonicLib.Silo_Config_Address_wstkscUSDC);
        (ptUSDCSilo, usdcSilo) <span class="hljs-operator">=</span> siloConfig.getSilos();
}
</code></pre>
<p>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 <code>silo</code> collateral and set it as an operator on our sub account:</p>
<pre><code class="lang-solidity">        vm.startBroadcast();
        evcShift <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> EvcShift(SonicLib.Euler_Vault_Connector);
        ISilo(SonicLib.Silo_Address_ProtectedSHare_PT).approve(<span class="hljs-keyword">address</span>(evcShift), UINT256_MAX);
        IEVC(SonicLib.Euler_Vault_Connector).setAccountOperator(subaccount, <span class="hljs-keyword">address</span>(evcShift), <span class="hljs-literal">true</span>);
        <span class="hljs-keyword">bool</span> isAuthorized <span class="hljs-operator">=</span> IEVC(SonicLib.Euler_Vault_Connector).isAccountOperatorAuthorized(subaccount, <span class="hljs-keyword">address</span>(evcShift));
        console.log(<span class="hljs-string">"isAuthorized: "</span>, isAuthorized);
</code></pre>
<p>At this point, we can execute the migrations function.</p>
<pre><code class="lang-solidity">        evcShift.migrateLoan(
            usdcSilo,
            ptUSDCSilo,
            <span class="hljs-keyword">address</span>(ISilo(usdcSilo).asset()),
            SonicLib.Silo_Address_ProtectedSHare_PT,
            borrower,
            subaccount, <span class="hljs-comment">// EVC subaccount</span>
            <span class="hljs-keyword">address</span>(SonicLib.Euler_Deposit_Vault),
            <span class="hljs-keyword">address</span>(SonicLib.Euler_Borrow_Vault)
        );
</code></pre>
<p>Once that is complete. We will want to revoke all approval on silo just in case and ensure the operator released authorization.</p>
<pre><code class="lang-solidity">        ISilo(SonicLib.Silo_Address_ProtectedSHare_PT).approve(<span class="hljs-keyword">address</span>(evcShift), <span class="hljs-number">0</span>);
        console.log(<span class="hljs-string">"Silo Allowance: "</span>, ISilo(SonicLib.Silo_Address_ProtectedSHare_PT).allowance(<span class="hljs-keyword">address</span>(evcShift), <span class="hljs-keyword">address</span>(SonicLib.Silo_Address_ProtectedSHare_PT)));

        isAuthorized <span class="hljs-operator">=</span> IEVC(SonicLib.Euler_Vault_Connector).isAccountOperatorAuthorized(subaccount, <span class="hljs-keyword">address</span>(evcShift));
        console.log(<span class="hljs-string">"isAuthorized: "</span>, isAuthorized);
        <span class="hljs-keyword">if</span> (isAuthorized) <span class="hljs-keyword">revert</span>();
        vm.stopBroadcast();
</code></pre>
<p>We can than print everything out to ensure consistency.</p>
<pre><code class="lang-solidity">        console.log(<span class="hljs-string">"maxRepayment USDC Silo: "</span>, ISilo(usdcSilo).maxRepay(borrower));
        console.log(<span class="hljs-string">"maxWithdraw PTUSDC Silo: "</span>, ISilo(ptUSDCSilo).maxWithdraw(borrower, ISilo.CollateralType.Protected));

        console.log(<span class="hljs-string">"Assets Stuck in Shift: "</span>, IERC20(<span class="hljs-keyword">address</span>(ISilo(ptUSDCSilo).asset())).balanceOf(<span class="hljs-keyword">address</span>(evcShift)));
        console.log(<span class="hljs-string">"Debt of subaccount: "</span>, EVault(SonicLib.Euler_Borrow_Vault).debtOf(subaccount));
        console.log(<span class="hljs-string">"Collateral of subaccount: "</span>, EVault(SonicLib.Euler_Deposit_Vault).balanceOf(subaccount));
</code></pre>
<p>We can than run our script with fork to test it is working. <code>forge script script/EvcShift.s.sol --fork-url $RPC_URL --sender $BORROWER_EOA</code></p>
<p>Full Code:</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// SPDX-License-Identifier: MIT</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.28;</span>
<span class="hljs-keyword">import</span> { <span class="hljs-title">ISilo</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"silo-core/contracts/interfaces/ISilo.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">IERC20</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts/token/ERC20/IERC20.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"evc/interfaces/IEthereumVaultConnector.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">EVault</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"evk/EVault/EVault.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">EVCUtil</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"evc/utils/EVCUtil.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">EvcShift</span> <span class="hljs-keyword">is</span> <span class="hljs-title">EVCUtil</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _evc</span>) <span class="hljs-title">EVCUtil</span>(<span class="hljs-params">_evc</span>) </span>{}

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

        (<span class="hljs-keyword">uint256</span> _amount, <span class="hljs-keyword">uint256</span> _maxWithdraw) <span class="hljs-operator">=</span> getMigrationAmounts(_repaySilo, _collateralSilo, _collateralShare, _borrower);
        approveDepositVault(ISilo(_collateralSilo).asset(), _depositVault, _maxWithdraw);

        IEVC.BatchItem[] <span class="hljs-keyword">memory</span> items <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> IEVC.BatchItem[](<span class="hljs-number">6</span>);
        items[<span class="hljs-number">0</span>] <span class="hljs-operator">=</span> IEVC.BatchItem({
            onBehalfOfAccount: <span class="hljs-keyword">address</span>(<span class="hljs-number">0</span>),
            targetContract: <span class="hljs-keyword">address</span>(evc),
            <span class="hljs-built_in">value</span>: <span class="hljs-number">0</span>,
            data: <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodeWithSelector</span>(IEVC.enableCollateral.<span class="hljs-built_in">selector</span>, _e_account, _borrowVault)
        });
        items[<span class="hljs-number">1</span>] <span class="hljs-operator">=</span> IEVC.BatchItem({
            onBehalfOfAccount: <span class="hljs-keyword">address</span>(<span class="hljs-number">0</span>),
            targetContract: <span class="hljs-keyword">address</span>(evc),
            <span class="hljs-built_in">value</span>: <span class="hljs-number">0</span>,
            data: <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodeWithSelector</span>(IEVC.enableCollateral.<span class="hljs-built_in">selector</span>, _e_account, _depositVault)
        });
        items[<span class="hljs-number">2</span>] <span class="hljs-operator">=</span> IEVC.BatchItem({
            onBehalfOfAccount: <span class="hljs-keyword">address</span>(<span class="hljs-number">0</span>),
            targetContract: <span class="hljs-keyword">address</span>(evc),
            <span class="hljs-built_in">value</span>: <span class="hljs-number">0</span>,
            data: <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodeWithSelector</span>(IEVC.enableController.<span class="hljs-built_in">selector</span>, _e_account, _borrowVault)
        });
        items[<span class="hljs-number">3</span>] <span class="hljs-operator">=</span> IEVC.BatchItem({
            onBehalfOfAccount: _e_account,
            targetContract: _borrowVault,
            <span class="hljs-built_in">value</span>: <span class="hljs-number">0</span>,
            data: <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodeWithSelector</span>(EVault.borrow.<span class="hljs-built_in">selector</span>, _amount, <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>))
        });
        items[<span class="hljs-number">4</span>] <span class="hljs-operator">=</span> IEVC.BatchItem({
            onBehalfOfAccount: <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>),
            targetContract: <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>),
            <span class="hljs-built_in">value</span>: <span class="hljs-number">0</span>,
            data: <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodeWithSelector</span>(<span class="hljs-built_in">this</span>.repaySiloAndWithdraw.<span class="hljs-built_in">selector</span>, _repaySilo, _collateralSilo, _borrower, _amount, _maxWithdraw)
        });
        items[<span class="hljs-number">5</span>] <span class="hljs-operator">=</span> IEVC.BatchItem({
            onBehalfOfAccount: <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>),
            targetContract: _depositVault,
            <span class="hljs-built_in">value</span>: <span class="hljs-number">0</span>,
            data: <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodeWithSelector</span>(
                EVault.deposit.<span class="hljs-built_in">selector</span>,
                _maxWithdraw,
                _e_account
            )
        });
        evc.batch(items);
        evc.setAccountOperator(_e_account, <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), <span class="hljs-literal">false</span>);
    }
}
</code></pre>
<pre><code class="lang-solidity"><span class="hljs-comment">// SPDX-License-Identifier: MIT</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.28;</span>

<span class="hljs-keyword">import</span> {<span class="hljs-title">Script</span>, <span class="hljs-title">console</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"forge-std/Script.sol"</span>;

<span class="hljs-keyword">import</span> {<span class="hljs-title">EvcShift</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../src/EvcShift.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">ISiloConfig</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"silo-core/contracts/interfaces/ISiloConfig.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">ISilo</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"silo-core/contracts/interfaces/ISilo.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">SonicLib</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../test/common/SonicLib.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"evc/interfaces/IEthereumVaultConnector.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">EVault</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"evk/EVault/EVault.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">IERC20</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts/token/ERC20/IERC20.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">EvcShiftScript</span> <span class="hljs-keyword">is</span> <span class="hljs-title">Script</span> </span>{
    EvcShift <span class="hljs-keyword">public</span> evcShift;
    <span class="hljs-keyword">address</span> borrower;
    ISiloConfig siloConfig;
    <span class="hljs-keyword">address</span> ptUSDCSilo;
    <span class="hljs-keyword">address</span> usdcSilo;
    <span class="hljs-comment">// function setUp() public {}</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getSubaccount</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _account, <span class="hljs-keyword">uint256</span> _index</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">address</span></span>) </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">address</span>(<span class="hljs-keyword">uint160</span>(<span class="hljs-keyword">uint160</span>(_account)<span class="hljs-operator">^</span>_index));
    }
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">run</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        borrower <span class="hljs-operator">=</span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>;
        console.log(<span class="hljs-string">"Borrower: "</span>, borrower);
        <span class="hljs-keyword">address</span> subaccount <span class="hljs-operator">=</span> getSubaccount(borrower, <span class="hljs-number">1</span>);
        siloConfig <span class="hljs-operator">=</span> ISiloConfig(SonicLib.Silo_Config_Address_wstkscUSDC);
        (ptUSDCSilo, usdcSilo) <span class="hljs-operator">=</span> siloConfig.getSilos();
        <span class="hljs-keyword">uint256</span> maxRepayment <span class="hljs-operator">=</span> ISilo(usdcSilo).maxRepay(borrower);

        vm.startBroadcast();
        evcShift <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> EvcShift(SonicLib.Euler_Vault_Connector);
        ISilo(SonicLib.Silo_Address_ProtectedSHare_PT).approve(<span class="hljs-keyword">address</span>(evcShift), UINT256_MAX);
        IEVC(SonicLib.Euler_Vault_Connector).setAccountOperator(subaccount, <span class="hljs-keyword">address</span>(evcShift), <span class="hljs-literal">true</span>);
        <span class="hljs-keyword">bool</span> isAuthorized <span class="hljs-operator">=</span> IEVC(SonicLib.Euler_Vault_Connector).isAccountOperatorAuthorized(subaccount, <span class="hljs-keyword">address</span>(evcShift));
        console.log(<span class="hljs-string">"isAuthorized: "</span>, isAuthorized);

        evcShift.migrateLoan(
            usdcSilo,
            ptUSDCSilo,
            <span class="hljs-keyword">address</span>(ISilo(usdcSilo).asset()),
            SonicLib.Silo_Address_ProtectedSHare_PT,
            borrower,
            subaccount, <span class="hljs-comment">// EVC subaccount</span>
            <span class="hljs-keyword">address</span>(SonicLib.Euler_Deposit_Vault),
            <span class="hljs-keyword">address</span>(SonicLib.Euler_Borrow_Vault)
        );

        ISilo(SonicLib.Silo_Address_ProtectedSHare_PT).approve(<span class="hljs-keyword">address</span>(evcShift), <span class="hljs-number">0</span>);
        console.log(<span class="hljs-string">"Silo Allowance: "</span>, ISilo(SonicLib.Silo_Address_ProtectedSHare_PT).allowance(<span class="hljs-keyword">address</span>(evcShift), <span class="hljs-keyword">address</span>(SonicLib.Silo_Address_ProtectedSHare_PT)));
        isAuthorized <span class="hljs-operator">=</span> IEVC(SonicLib.Euler_Vault_Connector).isAccountOperatorAuthorized(subaccount, <span class="hljs-keyword">address</span>(evcShift));
        console.log(<span class="hljs-string">"isAuthorized: "</span>, isAuthorized);
        <span class="hljs-keyword">if</span> (isAuthorized) <span class="hljs-keyword">revert</span>();
        vm.stopBroadcast();

        console.log(<span class="hljs-string">"maxRepayment USDC Silo: "</span>, ISilo(usdcSilo).maxRepay(borrower));
        console.log(<span class="hljs-string">"maxWithdraw PTUSDC Silo: "</span>, ISilo(ptUSDCSilo).maxWithdraw(borrower, ISilo.CollateralType.Protected));

        console.log(<span class="hljs-string">"Assets Stuck in Shift: "</span>, IERC20(<span class="hljs-keyword">address</span>(ISilo(ptUSDCSilo).asset())).balanceOf(<span class="hljs-keyword">address</span>(evcShift)));
        console.log(<span class="hljs-string">"Debt of subaccount: "</span>, EVault(SonicLib.Euler_Borrow_Vault).debtOf(subaccount));
        console.log(<span class="hljs-string">"Collateral of subaccount: "</span>, EVault(SonicLib.Euler_Deposit_Vault).balanceOf(subaccount));

    }
}
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Stable Yield Maxxing: Shifting an Open Position from Silo to Euler for Optimized Returns]]></title><description><![CDATA[Silo’s PT Lending Market and the Leverage Opportunity
During the recent Sonic yield farming frenzy, Silo launched a PT-wstkscUSD-29MAY2025 vs. USDC.e lending market, allowing traders to unlock leveraged fixed yields on stablecoins.
For those prioriti...]]></description><link>https://yielddev.io/stable-yield-maxxing-shifting-an-open-position-from-silo-to-euler-for-optimized-returns</link><guid isPermaLink="true">https://yielddev.io/stable-yield-maxxing-shifting-an-open-position-from-silo-to-euler-for-optimized-returns</guid><category><![CDATA[silo]]></category><category><![CDATA[pendle]]></category><category><![CDATA[defi]]></category><category><![CDATA[euler]]></category><category><![CDATA[yield farming]]></category><category><![CDATA[Stablecoins ]]></category><dc:creator><![CDATA[Yield Dev]]></dc:creator><pubDate>Thu, 13 Mar 2025 01:07:48 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1741828680680/d6cfe41b-6883-4ef6-ad36-5d9d2ef272d0.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h4 id="heading-silos-pt-lending-market-and-the-leverage-opportunity"><strong>Silo’s PT Lending Market and the Leverage Opportunity</strong></h4>
<p>During the recent <strong>Sonic</strong> yield farming frenzy, Silo launched a <strong>PT-wstkscUSD-29MAY2025 vs. USDC.e</strong> lending market, allowing traders to unlock leveraged fixed yields on stablecoins.</p>
<p>For those prioritizing predictable returns over speculative points/gems farming, this strategy presents a lucrative opportunity.</p>
<p>Here’s how it works:</p>
<ul>
<li><p>Users can <strong>deposit PT assets</strong> (fixed-yielding) at approximately <strong>12% APY</strong>.</p>
</li>
<li><p>They can <strong>borrow USDC.e</strong> at a variable rate to <strong>increase leverage</strong> on the trade.</p>
</li>
<li><p>The profit stems from the <strong>spread between the fixed PT yield and the variable USDC.e rate</strong>, which is currently subsidized by both <strong>Silo and Sonic points</strong>.</p>
</li>
<li><p>If the realized variable borrowing rate at PT maturity is lower than the fixed yield at which the PT was purchased, the trader secures this <strong>spread as profit</strong>.</p>
</li>
</ul>
<p>This also functions as a <strong>leveraged short yield trade</strong>—if the variable rate matches the fixed rate, the return is <strong>zero</strong> unless the PT fixed rate drops, causing the collateral to appreciate relative to the debt, leading to trader profit.</p>
<p>Currently, <strong>wstkscUSD’s yield is artificially high due to Sonic and Ring point incentives</strong>, making it an attractive negative carry trade for those betting on lower future yields.</p>
<h4 id="heading-silo-arbitrage-and-the-market-response"><strong>Silo Arbitrage and the Market Response</strong></h4>
<p>At launch, the Silo trade was quickly arbitraged:</p>
<ul>
<li><p>PT assets were initially <strong>selling at ~15% fixed yield</strong>.</p>
</li>
<li><p>Leverage was rapidly introduced, pulling the <strong>Silo USDC.e borrowing rate to ~10%</strong>.</p>
</li>
<li><p>This, in turn, <strong>compressed the PT yield down to similar levels</strong>.</p>
</li>
</ul>
<p>The best entry was <strong>early on</strong>, when PT yields were higher, allowing traders to lock in profits as borrowing costs rose gradually.</p>
<hr />
<h3 id="heading-euler-vault-emerges-a-more-capital-efficient-alternative"><strong>Euler Vault Emerges: A More Capital-Efficient Alternative</strong></h3>
<p>A few days later, <strong>MEV Capital</strong> introduced an <strong>Euler lending vault</strong> with a similar PT strategy:</p>
<ul>
<li><p><strong>Depositing PT fixed-yielding collateral</strong></p>
</li>
<li><p><strong>Borrowing USDC.e at a variable rate</strong></p>
</li>
</ul>
<p>However, Euler’s <strong>Interest Rate Model (IRM)</strong> proved far superior to Silo’s:</p>
<ul>
<li><p>Despite the <strong>larger supply of USDC.e</strong> being borrowable against multiple markets, Euler’s <strong>IRM offers better funding for the short-yield trade</strong>.</p>
</li>
<li><p><strong>Comparison at 50% utilization:</strong></p>
<ul>
<li><p><strong>Silo USDC.e borrow rate:</strong> <strong>11.74% APY</strong></p>
</li>
<li><p><strong>Euler USDC.e borrow rate:</strong> <strong>5.44% APY</strong></p>
</li>
</ul>
</li>
</ul>
<p>This <strong>significant cost difference</strong> is especially crucial for highly leveraged traders, making Euler a <strong>far more attractive funding environment</strong>.</p>
<p>However, early traders who <strong>locked into Silo</strong> when it was the only leveraged PT vault needed a <strong>seamless way to migrate their positions</strong> to Euler.</p>
<hr />
<h3 id="heading-the-migration-plan-seamlessly-shifting-from-silo-to-euler"><strong>The Migration Plan: Seamlessly Shifting from Silo to Euler</strong></h3>
<p>Rather than manually unwinding the position—an inefficient and capital-intensive process—a secure, capital-efficient <strong>migration solution</strong> was developed.</p>
<p>With deep knowledge of both codebases, we quickly built and deployed a <strong>bespoke contract</strong> to execute the transfer within hours of the Euler MEV vault’s activation.</p>
<h4 id="heading-migration-steps"><strong>Migration Steps:</strong></h4>
<ol>
<li><p><strong>Authorize the contract</strong> to claim the Silo collateral assets.</p>
</li>
<li><p><strong>Enable contract permissions</strong> as an operator on the Euler Vault Connector (EVC).</p>
</li>
<li><p><strong>Take a flash loan</strong> in USDC.e for the total Silo debt balance.</p>
</li>
<li><p><strong>Use the flash loan</strong> to <strong>repay the Silo vault debt</strong>.</p>
</li>
<li><p><strong>Withdraw</strong> the <strong>full PT collateral</strong>, as no debt remains.</p>
</li>
<li><p><strong>Enable PT collateral</strong> in Euler and <strong>set USDC.e borrowing vault</strong> as a controller.</p>
</li>
<li><p><strong>Deposit PT collateral</strong> into the Euler vault.</p>
</li>
<li><p><strong>Borrow USDC.e</strong> from the Euler vault against the deposited PT collateral.</p>
</li>
<li><p><strong>Repay the flash loan</strong> in full.</p>
</li>
<li><p><strong>Revoke contract permissions</strong> over the user’s account.</p>
</li>
<li><p><strong>Reset Silo collateral approval</strong> to <strong>prevent future risks</strong>.</p>
</li>
</ol>
<p>All of this executed from forge script and hardware wallet with steps 3 through 10 being a single contract call.</p>
<hr />
<h3 id="heading-conclusion-efficient-yield-optimization"><strong>Conclusion: Efficient Yield Optimization</strong></h3>
<p>This migration strategy enabled seamless <strong>position transfer between lending platforms</strong> without requiring manual unwinding, additional capital, or unnecessary trading exposure.</p>
<p>By capitalizing on <strong>Euler’s superior IRM and lower borrowing rates</strong>, traders can significantly <strong>improve their leveraged yield strategies</strong> while maintaining capital efficiency.</p>
<p>This approach highlights the importance of <strong>early entry, market inefficiencies, and technical execution</strong> in DeFi yield arbitrage.</p>
<p>Full write up with more technical details to come. Feel free to contact me on X → <a target="_blank" href="https://x.com/yielddev">@yielddev</a>  </p>
<p><a target="_blank" href="https://yielddev.com">YieldDev Studio</a></p>
]]></content:encoded></item><item><title><![CDATA[On Collateral Valuation and Design in Decentralized Financial Applications]]></title><description><![CDATA[When implementing a financial application involving loans, be it leveraged yield farming, collateralized lending markets or decentralized perpetual futures exchanges, serious consideration must be given to the process and method used to price assets ...]]></description><link>https://yielddev.io/on-collateral-valuation-and-design-in-decentralized-financial-applications</link><guid isPermaLink="true">https://yielddev.io/on-collateral-valuation-and-design-in-decentralized-financial-applications</guid><category><![CDATA[defi]]></category><category><![CDATA[defi development company]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[finance]]></category><category><![CDATA[fintech]]></category><category><![CDATA[Oracle]]></category><category><![CDATA[Chainlink]]></category><category><![CDATA[Lending platform]]></category><category><![CDATA[leverage]]></category><category><![CDATA[yield farming]]></category><category><![CDATA[Yield Farming and Staking]]></category><category><![CDATA[Yield Farming Contract ]]></category><dc:creator><![CDATA[Yield Dev]]></dc:creator><pubDate>Fri, 14 Feb 2025 08:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1741829548312/63e16b2b-46ff-4b1c-aac9-3f2611d9ede8.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When implementing a financial application involving loans, be it leveraged yield farming, collateralized lending markets or decentralized perpetual futures exchanges, serious consideration must be given to the process and method used to price assets within the decentralized, on chain environment.</p>
<p>This consideration is separate from designing aspects like collateral ratio (LTV) and pertains specifically to gathering and utilizing asset price information which will eventually be included in the LTV calculation.</p>
<p>While the oracle problem is largely solved by many industry provider like chainlink, pyth or redbrick; understanding the what the prices delivered by these providers are actually based off of and how to best integrate them into the specific use case of your DeFi application are of critical importance.</p>
<p>For the most part, this issue comes down to risk management particularly as it pertains to liquidations on any platform that allows users access to leveraged products and loans.</p>
<p>Defi application offering leverage, generally serve two parties the <code>liquidity provider</code> and the <code>trader</code> and thus, must balance offering the highest possible leverage for the trader and the most collateral safety for the <code>LP</code>. When a trader's collateral is liquidated at a price that results in proceeds less than the amount of the loan owed to the <code>LP</code> this balance is violated and the difference in value is marked as <code>bad debt</code> resulting in an impairment of the liquidity provider pools funds.</p>
<p>For this reason, a safety ratio is often built into the lending markets design resulting in two critical numbers attached to any loan that is opened. The initial margin requirement and the maintenance margin requirement.</p>
<h2 id="heading-there-is-no-price">There Is No <em>Price</em></h2>
<p>Thinking of the price as single, definitive number is a misconception. Allow us to briefly re-conceptualize price. Price is, of course, <em>what someone is willing to pay.</em> However, if we invert this statement, price is <em>what someone is willing to receive</em>. From this stand point, it is easy to imagine that these numbers are different depending on whether we are looking to buy something or looking to sell it. We need only imagine a limit order book for any asset to understand that, at any given moment, the only definitive is that there is one price at which we know a party is willing to buy and another price at which we know a party is willing to sell. These must be different prices, or else the two parties would have already bought and sold from each other leaving us to have to find a different party to buy or sell from at an even different price point.</p>
<p>True market pricing, involves a bid / ask price. a price for buying and a price for selling.</p>
<h2 id="heading-mark-to-market-mark-to-model">Mark To Market, Mark To Model</h2>
<p>So, When our DeFi application calls an oracle and gets a single number, we think of it as a price but is it a market price? No. In this case we are working with some <em>model</em> of a price. A useful approximation used to evaluate the our assets in an application specific way.</p>
<p>For example, if we have a DeFi application with two pools, one of ETH and one of WBTC. We might fetch some general price, like the last trade price, for both BTC and ETH in terms of USD. We then multiply the amount of ETH by the USD price of eth and the amount of WBTC by the USD price of BTC, add the two numbers together, call it our TVL and display it on our front end. <em>"$xxx,xxx,xxx Total Value Locked"</em></p>
<p>Pretty harmless assumption to make that the last trade price is a good price to use in this scenario because the result of this calculation is merely cosmetic and not mission critical to the application. But, can we really expect the receive $xxx,xxx,xxx in exchange for liquidating both pools for USD at this moment. Absolutely not, we have just naively marked marked all the assets locked in out pools at the last trade price.</p>
<p>However, in the case of a loan or leveraged position, we are fetching a price and marking an asset value because we expect we might actually have to liquidate the assets immediately, into the market in the case of a collateral ratio violation (loan liquidation). In this situation, we actually we want to be very confident of the value we can actually get for selling these assets at this moment. The last trade price is not a good model to mark by for several reasons. How long ago was this trade made at this price? In what size was the this trade executed at for this price? Was this trade executed as a buy or sell?</p>
<p>We can see, though this example is absurd, that there are many considerations about how a price was arrived at (the model) when marking collateral to certain price. In order to manage the risk of a lending market, we need to have strong confidence in what value can we actually receive if we liquidated a certain amount of a collateral asset at this moment.</p>
<h2 id="heading-the-models">The Models</h2>
<p>Considering that we know understand why we need different models of pricing for different situations, let us review a few basic price types. First, we can think about a perps exchange allowing counterparties to trade futures against each other on a perpetual basis. Exchanges like Bitmex or even DEXs rely on an <em>index</em> price to mark the value of futures contracts P/L against the associated margin of a trader. Combining prices from several liquid exchanges, using a formula that weights each price based on the volume, they create a Volume weighted average price across all the exchanges and mark position based off of this model of pricing.</p>
<p>If we had some simple graphic displaying the ticker of a given asset, meant for nothing more than to give the user a general idea of what price that asset might be trading around. A website will often call an exchanges api, which provides the current bid/ask price and then take the mid-point, a price directly in between the spread and display it as the price.</p>
<p>On chain applications, typically using the popular UniswapV3 price as an oracle, are calling a price from a highly liquid uniswapV3 pool which represents the time weighted average price of an asset, <strong>TWP</strong>. A the <em>TWAP</em> calculates the geometric mean of relative prices of the two assets in a pool over a certain rolling window. The rationale for using this model on chain, versus the VWAP (volume weighted price) used in traditional markets, is primarily on of security. This is because one popular vectors of attack against lending markets is to temporarily manipulate the price of an asset inside of the pool used as a reference price and earn a greater profit than the cost of the manipulation on the lending market as a consequence of this mis-pricing. However, since this price is calculated over a certain window of time, an attacker must manipulate the price over a longer period in order to affect the mark price used in lending markets thus making such an attack infeasible.</p>
<h2 id="heading-liquidity-and-size">Liquidity and Size</h2>
<p>An additional consideration when pricing collateral assets, especially with regards to liquidation is the size of the potential liquidation and the depth of the liquidity in the market you might liquidate into.</p>
<p>This is particularly important for exotic markets, even less exotic markets such as options, where the market liquidity may be thinner than a standard blue-chip asset like ETH. Fragmentations of market liquidity pools is also an important factor to take note of when designing a lending market.</p>
<p>Especially in ultra high leverage markets, where the margin of safety with regards to a user's collateral ration is razor thin, the price slippage of liquidating a large position into a thin market can cause serious insolvency issues and create bad debt. Practically speaking, the higher the leverage and the thinner the market liquidity, the more pessimistic your implementation should be when assigning a value collateral assets.</p>
<p>One design consideration that could be made is adjusting the pricing model <em>in addition to the collateral ratio</em> to manage risk on a size aware basis. It may be appropriate for a smaller loan (relative to the market liquidity) to have a lower collateral ratio requirement than a larger one on the basis that liquidations on a smaller loan will execute at a better effective price. Further, the oracle mechanism that fetches the price and assigns a value to the collateral of a given position can take into account the size of the position when marking a price given the available liquidity providing a more pessimistic (less risky) model to mark the collateral. Though, in practice, fetching the depth of market liquidity is a difficult task and is currently an emergent research topic.</p>
<h2 id="heading-implementation">Implementation</h2>
<p>So, now let us outline the actionable take aways from these considerations.</p>
<p>First, when fetching oraclized price data on-chain, we must implement EIP-7726. Since, oracles typically return a fixed price ticker, it is essential to wrap them in a standard interface that shifts our conception (as well as our interactions) with this price data from one of fetching a definitive price to fetching a <em>quote</em></p>
<p>The <code>getQuote</code> interface, implemented by EIP-7726, allows contract calls to request a <em>quote</em> for a certain amount of a certain asset in terms of another asset.</p>
<p>In this way, we get to the heart of our actual requirement in a lending market. The price of 1 ether is not actually relevant to us when we are trying to manage the risk of a USDC loan collateralized by ETH. Instead, we care about how much USDC we can receive were we to liquidate a specific amount of collateral. Thus, getting a quote for a position's collateral assets more closely aligns our conceptualization of the oracles function to the requirements of our application.</p>
<p>We can also implement bid/ask awareness when fetching the price of an asset. In the event of a liquidation are we buying or selling into this market? Specifying the direction of a liquidation helps us fetch a more pessimistic price assumption.</p>
<p>Further our design needs to carefully consider the purpose of quoting these asset's value. Do we need an index price? weighting across several markets based on volume in order to enforce some contract between counterparties? or do we need a price that is resistant to manipulation such as TWAP?</p>
<p>Lastly, we need to consider the amount leverage that is offered to traders (borrowers) on our platform and how can we best model a quote that effectively manages the risk to our liquidity provider (lenders). How will the platform manager open interest vs market depth to create a platform large liquidations don't encounter price slippage that effects the solvency of the LPs.</p>
<p>Here must make careful considerations when designing a DeFi application that balances risk management, high leverage and usability to carefully engineer an efficient and valuable platform.</p>
<p>At <a target="_blank" href="https://yielddev.com">Yield Dev Studio</a> we specialize in DeFi design and development. Reach out if you before you undertake your next project.</p>
]]></content:encoded></item><item><title><![CDATA[EVC Basics Part 3 - Testing our base contracts]]></title><description><![CDATA[Now that we have implemented the minimal required contracts for our lending market, we can begin setting up some simple test to explore how the EVC and the vaults work in practice.
Following along with the previous two tutorials, we should have a for...]]></description><link>https://yielddev.io/evc-basics-part-3-testing-our-base-contracts</link><guid isPermaLink="true">https://yielddev.io/evc-basics-part-3-testing-our-base-contracts</guid><category><![CDATA[EVC]]></category><category><![CDATA[euler]]></category><category><![CDATA[Solidity]]></category><category><![CDATA[Ethereum]]></category><category><![CDATA[Smart Contracts]]></category><category><![CDATA[Software Engineering]]></category><dc:creator><![CDATA[Yield Dev]]></dc:creator><pubDate>Thu, 13 Feb 2025 16:55:08 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1739420562180/0e05c7f1-ee8a-4a86-b0a6-27551f997c04.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Now that we have implemented the minimal required contracts for our lending market, we can begin setting up some simple test to explore how the EVC and the vaults work in practice.</p>
<p>Following along with the previous two tutorials, we should have a forge project with the following src/ directory.</p>
<pre><code class="lang-solidity">
<span class="hljs-operator">-</span>src<span class="hljs-operator">/</span>
    <span class="hljs-operator">-</span>EVCClient.sol
    <span class="hljs-operator">-</span>VaultBase.sol
    <span class="hljs-operator">-</span>VaultSimple.sol
    <span class="hljs-operator">-</span>VaultSimpleBorrowable
</code></pre>
<p>We will start off by testing the deposit only <code>VaultSimple.sol</code> The initial setup will involve importing the EVC contract and a MockERC20 for testing as well as our <code>VaultSimple</code> contract. In the testy <code>setUp()</code> we will deploy everything and then run a simple test to ensure its metadata is properly defined.</p>
<p>Additionally we define a test user, Alice, which will hold 1000 units of our mock token for depositing</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// test_vault_simple.t.sol</span>

<span class="hljs-comment">// SPDX-License-Identifier: UNLICENSED</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.13;</span>
<span class="hljs-keyword">import</span> { <span class="hljs-title">Test</span>, <span class="hljs-title">console</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"forge-std/Test.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"evc/EthereumVaultConnector.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">MockERC20</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"solmate/test/utils/mocks/MockERC20.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">VaultSimple</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../src/VaultSimple.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">VaultSimpleTest</span> <span class="hljs-keyword">is</span> <span class="hljs-title">Test</span> </span>{
    IEVC evc;
    MockERC20 collateralAsset;
    VaultSimple vault;
    <span class="hljs-keyword">address</span> alice <span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">0x69</span>);
    <span class="hljs-keyword">uint256</span> aliceDeposit <span class="hljs-operator">=</span> <span class="hljs-number">1000</span>;

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setUp</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        evc <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> EthereumVaultConnector();
        collateralAsset <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> MockERC20(<span class="hljs-string">"Collateral Asset"</span>, <span class="hljs-string">"CA"</span>, <span class="hljs-number">18</span>);
        vault <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> VaultSimple(<span class="hljs-keyword">address</span>(evc), collateralAsset, <span class="hljs-string">"Simple Collateral Vault"</span>, <span class="hljs-string">"SCV"</span>);

        collateralAsset.mint(alice, aliceDeposit);
        vm.prank(alice);
        collateralAsset.approve(<span class="hljs-keyword">address</span>(vault), aliceDeposit);
    }

    <span class="hljs-comment">// Test vault deployed with metadata</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test_deployed_vault</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        assertEq(vault.<span class="hljs-built_in">name</span>(), <span class="hljs-string">"Simple Collateral Vault"</span>);
        assertEq(vault.symbol(), <span class="hljs-string">"SCV"</span>);
    }

}
</code></pre>
<p>Next, we will test out a simple deposit which, according to ERC4626, should simply wrap <code>alice</code>'s tokens in our vault's shares at a 1:1 ratio.</p>
<pre><code class="lang-solidity">    <span class="hljs-comment">// Test Deposit </span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test_deposit</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        vm.prank(alice);
        vault.deposit(aliceDeposit, alice); <span class="hljs-comment">// direct call to deposit is rerouted through the EVC via it's modifier logic</span>

        assertEq(vault.balanceOf(alice), aliceDeposit); <span class="hljs-comment">// 1:1 share to asset caclulation in this case</span>
        assertEq(collateralAsset.balanceOf(alice), <span class="hljs-number">0</span>);
        assertEq(vault.totalAssets(), aliceDeposit);
    }
</code></pre>
<p>Further, we will test the withdraw functionality,</p>
<pre><code class="lang-solidity">    <span class="hljs-comment">// Test Withdraw</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test_withdraw</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        vm.prank(alice);
        vault.deposit(aliceDeposit, alice);

        vm.prank(alice);
        vault.withdraw(shares, alice, alice); <span class="hljs-comment">// withdraw a certain amount of assets for the vaults shares</span>
        assertEq(vault.balanceOf(alice), <span class="hljs-number">0</span>);
        assertEq(collateralAsset.balanceOf(alice), shares);
    }
</code></pre>
<p><code>mint()</code> and <code>redeem()</code> are similar operations to deposit withdraw and are also defined in <code>ERC4626</code>. Where <code>deposit</code>/<code>withdraw</code> take the amount of tokens to put into, or remove from the vault as arguments, the <code>mint/</code> <code>redeem</code> functions take the amount of shares worth of tokens to be removed or added to the vault. We test these functions as well.</p>
<pre><code class="lang-solidity">    <span class="hljs-comment">// Test mint and redeem </span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test_mint_redeem</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        vm.prank(alice);
        vault.mint(aliceDeposit, alice); <span class="hljs-comment">// here put the amount of shares we want to mint as an argument</span>

        assertEq(vault.balanceOf(alice), aliceDeposit); <span class="hljs-comment">// 1:1 share to asset caclulation in this case</span>
        assertEq(collateralAsset.balanceOf(alice), <span class="hljs-number">0</span>);
        assertEq(vault.totalAssets(), aliceDeposit); 

        <span class="hljs-keyword">uint256</span> shares <span class="hljs-operator">=</span> vault.balanceOf(alice);

        vm.prank(alice);
        vault.redeem(shares, alice, alice); <span class="hljs-comment">// redeem a certain amount of shares for the underlying asset</span>
    }
</code></pre>
<p>Thus far, we have only tested the basic functionality inherited from <code>ERC4626</code> vaults. In this next example we will see how the EVC facilitates lending interactions in our <code>VaultSimpleBorrowable</code>.</p>
<p>Setting up our tests, similarly to our <code>VaultSimple</code> test, We deploy an EVC contract along with our borrowable vault.</p>
<pre><code class="lang-solidity"><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">VaultSimpleBorrowableTest</span> <span class="hljs-keyword">is</span> <span class="hljs-title">Test</span> </span>{
    IEVC evc;
    MockERC20 borrowableAsset;
    VaultSimpleBorrowable vault;
    <span class="hljs-keyword">address</span> alice <span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">0x69</span>);
    <span class="hljs-keyword">uint256</span> aliceDeposit <span class="hljs-operator">=</span> <span class="hljs-number">1000</span>;
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setUp</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        evc <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> EthereumVaultConnector();
        borrowableAsset <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> MockERC20(<span class="hljs-string">"Borrowable Asset"</span>, <span class="hljs-string">"CA"</span>, <span class="hljs-number">18</span>);
        vault <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> VaultSimpleBorrowable(<span class="hljs-keyword">address</span>(evc), borrowableAsset, <span class="hljs-string">"Simple Borrowable Vault"</span>, <span class="hljs-string">"SBV"</span>);

        borrowableAsset.mint(alice, aliceDeposit);
        vm.prank(alice);
        borrowableAsset.approve(<span class="hljs-keyword">address</span>(vault), <span class="hljs-keyword">type</span>(<span class="hljs-keyword">uint256</span>).<span class="hljs-built_in">max</span>);
    }
}
</code></pre>
<p>First thing to note is that we constructed this vault as a simple borrowable vault using its own deposits as collateral for its own assets at 90% of its value. We did this for simplicity to demonstrate borrowing functionality. In a later post we will extend this contract to a more real world scenario where we can borrow from this vault with our initial <code>VaultSimple</code> deposits as collateral.</p>
<p>First, our test will have Alice deposit into the vault and than borrow 90% of those tokens and subsequently repay those same tokens. Doing this we will see how the EVC comes into play in these interaction.</p>
<p>After Alice has deposited, in order that she may borrow from the vault she must take two actions via the EVC. First, calling the <code>evc.enableController(alice, vault)</code> which enables the vault to control Alice's account through the EVC according the terms of the vaults collateral requirements. This effectively gives the vault a lien on Alice's available collaterals. Second, Alice must opt-in to allowing the EVC to use certain deposits as collateral for various actions, this is done by calling <code>evc.enableCollateral(alice, vault)</code>. In summary, Alice must authorize the EVC to, both use specific vaults as collateral and allow a specific vault to control that collateral.</p>
<pre><code class="lang-solidity">    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test_borrow_repay</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        vm.prank(alice);
        <span class="hljs-keyword">uint256</span> shares <span class="hljs-operator">=</span> vault.deposit(aliceDeposit, alice);

        assertEq(vault.balanceOf(alice), aliceDeposit);
        assertEq(borrowableAsset.balanceOf(alice), <span class="hljs-number">0</span>);
        assertEq(vault.totalAssets(), aliceDeposit);

        vm.prank(alice);
        evc.enableController(alice, <span class="hljs-keyword">address</span>(vault));

        vm.prank(alice);
        evc.enableCollateral(alice, <span class="hljs-keyword">address</span>(vault));

        vm.prank(alice);
        vault.borrow((aliceDeposit <span class="hljs-operator">*</span> <span class="hljs-number">9</span>) <span class="hljs-operator">/</span> <span class="hljs-number">10</span>, alice);

        vm.prank(alice);
        vault.repay((aliceDeposit <span class="hljs-operator">*</span> <span class="hljs-number">9</span>) <span class="hljs-operator">/</span> <span class="hljs-number">10</span>, alice);

        assertEq(borrowableAsset.balanceOf(alice), <span class="hljs-number">0</span>);
        assertEq(vault.balanceOf(alice), shares);
        assertEq(vault.debtOf(alice), <span class="hljs-number">0</span>);

    }
</code></pre>
<p>Recall that the <code>callThroughEVC</code> function modifiers on the <code>repay</code>/<code>borrow</code> function reroute these direct function calls to be made through the EVC. However, the EVC itself exposes some powerful functionality which we can use directly to call this contract with better UX. One such feature is the EVC <code>batch</code> allowing us to use the EVC as a multicall combining all these calls into a single transaction.</p>
<p>To do this we create a list of <code>IEVC.BatchItem</code> structs containing the encoded transaction execution data which the EVC with authenticate and execute in a single transaction.</p>
<pre><code class="lang-solidity">    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test_borrow_with_evc_batch</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{

        IEVC.BatchItem[] <span class="hljs-keyword">memory</span> items <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> IEVC.BatchItem[](<span class="hljs-number">4</span>);
        items[<span class="hljs-number">0</span>] <span class="hljs-operator">=</span> IEVC.BatchItem({
            targetContract: <span class="hljs-keyword">address</span>(vault),
            onBehalfOfAccount: alice,
            <span class="hljs-built_in">value</span>: <span class="hljs-number">0</span>, 
            data: <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodeWithSelector</span>(VaultSimple.deposit.<span class="hljs-built_in">selector</span>, aliceDeposit, alice)
        });
        items[<span class="hljs-number">1</span>] <span class="hljs-operator">=</span> IEVC.BatchItem({
            targetContract: <span class="hljs-keyword">address</span>(evc),
            onBehalfOfAccount: <span class="hljs-keyword">address</span>(<span class="hljs-number">0</span>),
            <span class="hljs-built_in">value</span>: <span class="hljs-number">0</span>, 
            data: <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodeWithSelector</span>(IEVC.enableController.<span class="hljs-built_in">selector</span>, alice, <span class="hljs-keyword">address</span>(vault))
        });
        items[<span class="hljs-number">2</span>] <span class="hljs-operator">=</span> IEVC.BatchItem({
            targetContract: <span class="hljs-keyword">address</span>(evc),
            onBehalfOfAccount: <span class="hljs-keyword">address</span>(<span class="hljs-number">0</span>),
            <span class="hljs-built_in">value</span>: <span class="hljs-number">0</span>, 
            data: <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodeWithSelector</span>(IEVC.enableCollateral.<span class="hljs-built_in">selector</span>, alice, <span class="hljs-keyword">address</span>(vault))
        });
        items[<span class="hljs-number">3</span>] <span class="hljs-operator">=</span> IEVC.BatchItem({
            targetContract: <span class="hljs-keyword">address</span>(vault),
            onBehalfOfAccount: alice,
            <span class="hljs-built_in">value</span>: <span class="hljs-number">0</span>, 
            data: <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodeWithSelector</span>(VaultSimpleBorrowable.borrow.<span class="hljs-built_in">selector</span>, (aliceDeposit <span class="hljs-operator">*</span> <span class="hljs-number">9</span>) <span class="hljs-operator">/</span> <span class="hljs-number">10</span>, alice)
        });

        vm.prank(alice);
        evc.batchSimulation(items);
        vm.prank(alice);
        evc.batch(items);

        assertEq(borrowableAsset.balanceOf(alice), (aliceDeposit <span class="hljs-operator">*</span> <span class="hljs-number">9</span>) <span class="hljs-operator">/</span> <span class="hljs-number">10</span>);
        assertEq(vault.maxWithdraw(alice), aliceDeposit <span class="hljs-operator">-</span> (aliceDeposit <span class="hljs-operator">*</span> <span class="hljs-number">9</span>) <span class="hljs-operator">/</span> <span class="hljs-number">10</span>);
        assertEq(vault.debtOf(alice), (aliceDeposit <span class="hljs-operator">*</span> <span class="hljs-number">9</span>) <span class="hljs-operator">/</span> <span class="hljs-number">10</span>);
    }
</code></pre>
<p>By implementing these tests, we have seen how the EVC is used to authenticate and mediate interactions between a user's account and our vaults.</p>
<p>In the next post we will further extend our <code>VaultSimpleBorrowable</code> to illustrate how the EVC is used to mediate interactions <em>between</em> vaults. Thus, completing our simple lending market.</p>
]]></content:encoded></item><item><title><![CDATA[EVC Basics Part 2: Borrowable-Vault]]></title><description><![CDATA[The next part of our lending market is going to be a borrowable vault which wraps an asset that can be lent out against the collateral in our deposit only vault. In order to do this we will need to extend our VaultSimple contract with additional func...]]></description><link>https://yielddev.io/evc-basics-part-2-borrowable-vault</link><guid isPermaLink="true">https://yielddev.io/evc-basics-part-2-borrowable-vault</guid><category><![CDATA[defi]]></category><category><![CDATA[yield farming]]></category><category><![CDATA[Solidity]]></category><category><![CDATA[EVC]]></category><category><![CDATA[Ethereum]]></category><category><![CDATA[ethereum smart contracts]]></category><category><![CDATA[finance]]></category><dc:creator><![CDATA[Yield Dev]]></dc:creator><pubDate>Tue, 11 Feb 2025 08:00:53 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1739238459404/a5649913-8a8b-4a7e-a491-88798c3600b1.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The next part of our lending market is going to be a borrowable vault which wraps an asset that can be lent out against the collateral in our deposit only vault. In order to do this we will need to extend our <code>VaultSimple</code> contract with additional functionality. The additional functionality added to our borrowable vault will implement a controller, allowing our borrowable vault to <code>control</code> an accounts deposited collateral in order to be able to seize them in the event of a collateral requirement violation aka a liquidation event.</p>
<p>Full code can, once again, be found in the <a target="_blank" href="https://github.com/euler-xyz/evc-playground/blob/master/src/vaults/solmate/VaultSimpleBorrowable.sol#L317">EVC Playground Repo</a></p>
<p>To get started, we will import the <code>VaultSimple</code> we will be extending and define some borrow specific events.</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// SPDX-License-Identifier: GPL-2.0-or-later</span>

<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.19;</span>

<span class="hljs-keyword">import</span> <span class="hljs-string">"./VaultSimple.sol"</span>;

<span class="hljs-comment">/// @title VaultSimpleBorrowable</span>
<span class="hljs-comment">/// @notice This contract extends VaultSimple to add borrowing functionality.</span>
<span class="hljs-comment">/// @notice In this contract, the EVC is authenticated before any action that may affect the state of the vault or an</span>
<span class="hljs-comment">/// account. This is done to ensure that if it's EVC calling, the account is correctly authorized and the vault is</span>
<span class="hljs-comment">/// enabled as a controller if needed. This contract does not take the account health into account when calculating max</span>
<span class="hljs-comment">/// withdraw and max redeem values. This contract does not implement the interest accrual hence it returns raw values of</span>
<span class="hljs-comment">/// total borrows and 0 for the interest accumulator in the interest accrual-related functions.</span>
<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">VaultSimpleBorrowable</span> <span class="hljs-keyword">is</span> <span class="hljs-title">VaultSimple</span> </span>{
    <span class="hljs-keyword">using</span> <span class="hljs-title">SafeTransferLib</span> <span class="hljs-title"><span class="hljs-keyword">for</span></span> <span class="hljs-title">ERC20</span>;
    <span class="hljs-keyword">using</span> <span class="hljs-title">FixedPointMathLib</span> <span class="hljs-title"><span class="hljs-keyword">for</span></span> <span class="hljs-title"><span class="hljs-keyword">uint256</span></span>;

    <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">BorrowCapSet</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> newBorrowCap</span>)</span>;
    <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">Borrow</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> caller, <span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> owner, <span class="hljs-keyword">uint256</span> assets</span>)</span>;
    <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">Repay</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> caller, <span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> receiver, <span class="hljs-keyword">uint256</span> assets</span>)</span>;

    <span class="hljs-function"><span class="hljs-keyword">error</span> <span class="hljs-title">BorrowCapExceeded</span>(<span class="hljs-params"></span>)</span>;
    <span class="hljs-function"><span class="hljs-keyword">error</span> <span class="hljs-title">AccountUnhealthy</span>(<span class="hljs-params"></span>)</span>;
    <span class="hljs-function"><span class="hljs-keyword">error</span> <span class="hljs-title">OutstandingDebt</span>(<span class="hljs-params"></span>)</span>;

    <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> borrowCap;
    <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">internal</span> _totalBorrowed;
    <span class="hljs-keyword">mapping</span>(<span class="hljs-keyword">address</span> account <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> <span class="hljs-keyword">uint256</span> assets) <span class="hljs-keyword">internal</span> owed;

    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> _evc,
        ERC20 _asset,
        <span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> _name,
        <span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> _symbol
    </span>) <span class="hljs-title">VaultSimple</span>(<span class="hljs-params">_evc, _asset, _name, _symbol</span>) </span>{}
}
</code></pre>
<p>The borrowable vault will also need a setter function to allow the vault governor (<code>owner</code>) to set the borrow cap of the vault</p>
<pre><code class="lang-solidity">    <span class="hljs-comment">/// @notice Sets the borrow cap.</span>
    <span class="hljs-comment">/// @param newBorrowCap The new borrow cap.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setBorrowCap</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> newBorrowCap</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">onlyOwner</span> </span>{
        borrowCap <span class="hljs-operator">=</span> newBorrowCap;
        <span class="hljs-keyword">emit</span> BorrowCapSet(newBorrowCap);
    }
</code></pre>
<p>Next, the vault will implement two view function to get information about the debt. The first being the <code>totalBorrowed</code> amount for the entire vault. This implementation will call the <code>_accrueInterestCalculation</code>, which is an internal function that will be implemented later on, to return the total borrowed including outstanding interest. For now, we simply assume 0 interest rate as we will define an interest rate model later on.</p>
<pre><code class="lang-solidity">    <span class="hljs-comment">/// @notice Returns the total borrowed assets from the vault.</span>
    <span class="hljs-comment">/// @return The total borrowed assets from the vault.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">totalBorrowed</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        (<span class="hljs-keyword">uint256</span> currentTotalBorrowed,,) <span class="hljs-operator">=</span> _accrueInterestCalculate();
        <span class="hljs-keyword">return</span> currentTotalBorrowed;
    }

    <span class="hljs-comment">/// @notice Calculates the accrued interest.</span>
    <span class="hljs-comment">/// @dev Because this contract does not implement the interest accrual, this function does not need to calculate the</span>
    <span class="hljs-comment">/// interest, but only returns the current value of total borrows, 0 for the interest accumulator and false for the</span>
    <span class="hljs-comment">/// update flag. This function is needed so that it can be overriden by child contracts without a need to override</span>
    <span class="hljs-comment">/// other functions which use it.</span>
    <span class="hljs-comment">/// @return The total borrowed amount, the interest accumulator and a boolean value that indicates whether the data</span>
    <span class="hljs-comment">/// should be updated.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_accrueInterestCalculate</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span>, <span class="hljs-keyword">uint256</span>, <span class="hljs-keyword">bool</span></span>) </span>{
        <span class="hljs-keyword">return</span> (_totalBorrowed, <span class="hljs-number">0</span>, <span class="hljs-literal">false</span>);
    }
</code></pre>
<p>Additionally, the borrowable vault will expose a view function for the users account debt, implemented by calling an internal function to return the accounts mapping of its outstanding debt.</p>
<pre><code class="lang-solidity">    <span class="hljs-comment">/// @notice Returns the debt of an account.</span>
    <span class="hljs-comment">/// @param account The account to check.</span>
    <span class="hljs-comment">/// @return The debt of the account.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">debtOf</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> account</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> _debtOf(account);
    }

    <span class="hljs-comment">/// @notice Returns the debt of an account.</span>
    <span class="hljs-comment">/// @param account The account to check.</span>
    <span class="hljs-comment">/// @return The debt of the account.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_debtOf</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> account</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> owed[account];
    }
</code></pre>
<p>Next, we implement function overrides for <code>maxWithdraw</code> and <code>maxRedeem</code> since, in the context of our borrowable vault, the maximum amount withdraw-able must be adjusted to account for the fact that some funds from the vault might be lent out at the time of withdrawal. Thus the maximum amount that can be withdrawn from the vault is either the user's deposited balance or the total amount in the vault if some of the user's balance happens to be lent out.</p>
<pre><code class="lang-solidity">    <span class="hljs-comment">/// @notice Returns the maximum amount that can be withdrawn by an owner.</span>
    <span class="hljs-comment">/// @dev This function is overridden to take into account the fact that some of the assets may be borrowed.</span>
    <span class="hljs-comment">/// @param owner The owner of the assets.</span>
    <span class="hljs-comment">/// @return The maximum amount that can be withdrawn.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">maxWithdraw</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> owner</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">uint256</span> totAssets <span class="hljs-operator">=</span> _totalAssets;
        <span class="hljs-keyword">uint256</span> ownerAssets <span class="hljs-operator">=</span> _convertToAssets(balanceOf[owner], <span class="hljs-literal">false</span>);

        <span class="hljs-keyword">return</span> ownerAssets <span class="hljs-operator">&gt;</span> totAssets ? totAssets : ownerAssets;
    }

    <span class="hljs-comment">/// @notice Returns the maximum amount that can be redeemed by an owner.</span>
    <span class="hljs-comment">/// @dev This function is overridden to take into account the fact that some of the assets may be borrowed.</span>
    <span class="hljs-comment">/// @param owner The owner of the assets.</span>
    <span class="hljs-comment">/// @return The maximum amount that can be redeemed.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">maxRedeem</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> owner</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">uint256</span> totAssets <span class="hljs-operator">=</span> _totalAssets;
        <span class="hljs-keyword">uint256</span> ownerShares <span class="hljs-operator">=</span> balanceOf[owner];

        <span class="hljs-keyword">return</span> _convertToAssets(ownerShares, <span class="hljs-literal">false</span>) <span class="hljs-operator">&gt;</span> totAssets ? _convertToShares(totAssets, <span class="hljs-literal">false</span>) : ownerShares;
    }
</code></pre>
<p>Now since our borrowable vault has a different application requirements than our deposit only vault, we need to override the vault snapshot function to return the vault's state values that are relevant to the calculations required by our application. In the case of a borrowable vault, this would be the total assets and the currentTotalBorrowed. Further, since this function is called to cache the state of the vault prior to any actions that effect the vault's state (i.e before a borrow, repayment etc) we use this opportunity to update the <code>_accrueInterest</code> calculation ensuring the totalBorrowed value remains consistent and up to date.</p>
<pre><code class="lang-solidity">    <span class="hljs-comment">/// @notice Creates a snapshot of the vault.</span>
    <span class="hljs-comment">/// @dev This function is called before any action that may affect the vault's state. Considering that and the fact</span>
    <span class="hljs-comment">/// that this function is only called once per the EVC checks deferred context, it can be also used to accrue</span>
    <span class="hljs-comment">/// interest.</span>
    <span class="hljs-comment">/// @return A snapshot of the vault's state.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">doCreateVaultSnapshot</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bytes</span> <span class="hljs-keyword">memory</span></span>) </span>{
        (<span class="hljs-keyword">uint256</span> currentTotalBorrowed,) <span class="hljs-operator">=</span> _accrueInterest();

        <span class="hljs-comment">// make total assets and total borrows snapshot:</span>
        <span class="hljs-keyword">return</span> <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encode</span>(_totalAssets, currentTotalBorrowed);
    }

    <span class="hljs-comment">/// @notice Accrues interest.</span>
    <span class="hljs-comment">/// @dev Because this contract does not implement the interest accrual, this function does not need to update the</span>
    <span class="hljs-comment">/// state, but only returns the current value of total borrows and 0 for the interest accumulator. This function is</span>
    <span class="hljs-comment">/// needed so that it can be overriden by child contracts without a need to override other functions which use it.</span>
    <span class="hljs-comment">/// @return The current values of total borrowed and interest accumulator.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_accrueInterest</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span>, <span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> (_totalBorrowed, <span class="hljs-number">0</span>);
    }
</code></pre>
<p>Additionally, we will need to update the vault status check logic, <code>doCheckVaultStatus</code> is a check that is called <em>after</em> an action that may change a vault's state. Thus, this function takes the previous vault snapshot as an argument and implements logic the validate that the new state of the vault, after an action has been called, remains consistent and conforms to the constraints of our vaults requirements. In this case, that means we check that the <code>supplyCap</code> and the <code>borrowCap</code> have not been violated. Further, since the interest rate of a lending market is a function of the amount borrowed and the total assets in the vault, both of which may have been updated in the action preceding this check, we use this opportunity to call the internal <code>_updateInteres()</code> function. For now, we will leave this unimplemented as we are not charging interest in this example</p>
<pre><code class="lang-solidity">
    <span class="hljs-comment">/// @notice Checks the vault's status.</span>
    <span class="hljs-comment">/// @dev This function is called after any action that may affect the vault's state. Considering that and the fact</span>
    <span class="hljs-comment">/// that this function is only called once per the EVC checks deferred context, it can be also used to update the</span>
    <span class="hljs-comment">/// interest rate. `IVault.checkVaultStatus` can only be called from the EVC and only while checks are in progress</span>
    <span class="hljs-comment">/// because of the `onlyEVCWithChecksInProgress` modifier. So it can't be called at any other time to reset the</span>
    <span class="hljs-comment">/// snapshot mid-batch.</span>
    <span class="hljs-comment">/// @param oldSnapshot The snapshot of the vault's state before the action.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">doCheckVaultStatus</span>(<span class="hljs-params"><span class="hljs-keyword">bytes</span> <span class="hljs-keyword">memory</span> oldSnapshot</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> </span>{
        <span class="hljs-comment">// sanity check in case the snapshot hasn't been taken</span>
        <span class="hljs-keyword">if</span> (oldSnapshot.<span class="hljs-built_in">length</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span>) <span class="hljs-keyword">revert</span> SnapshotNotTaken();

        <span class="hljs-comment">// use the vault status hook to update the interest rate (it should happen only once per transaction).</span>
        <span class="hljs-comment">// EVC.forgiveVaultStatus check should never be used for this vault, otherwise the interest rate will not be</span>
        <span class="hljs-comment">// updated.</span>
        <span class="hljs-comment">// this contract doesn't implement the interest accrual, so this function does nothing. needed for the sake of</span>
        <span class="hljs-comment">// inheritance</span>
        _updateInterest();

        <span class="hljs-comment">// validate the vault state here:</span>
        (<span class="hljs-keyword">uint256</span> initialAssets, <span class="hljs-keyword">uint256</span> initialBorrowed) <span class="hljs-operator">=</span> <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">decode</span>(oldSnapshot, (<span class="hljs-keyword">uint256</span>, <span class="hljs-keyword">uint256</span>));
        <span class="hljs-keyword">uint256</span> finalAssets <span class="hljs-operator">=</span> _totalAssets;
        (<span class="hljs-keyword">uint256</span> finalBorrowed,,) <span class="hljs-operator">=</span> _accrueInterestCalculate();

        <span class="hljs-comment">// the supply cap can be implemented like this:</span>
        <span class="hljs-keyword">if</span> (
            supplyCap <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span> <span class="hljs-operator">&amp;</span><span class="hljs-operator">&amp;</span> finalAssets <span class="hljs-operator">+</span> finalBorrowed <span class="hljs-operator">&gt;</span> supplyCap
                <span class="hljs-operator">&amp;</span><span class="hljs-operator">&amp;</span> finalAssets <span class="hljs-operator">+</span> finalBorrowed <span class="hljs-operator">&gt;</span> initialAssets <span class="hljs-operator">+</span> initialBorrowed
        ) {
            <span class="hljs-keyword">revert</span> SupplyCapExceeded();
        }

        <span class="hljs-comment">// or the borrow cap can be implemented like this:</span>
        <span class="hljs-keyword">if</span> (borrowCap <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span> <span class="hljs-operator">&amp;</span><span class="hljs-operator">&amp;</span> finalBorrowed <span class="hljs-operator">&gt;</span> borrowCap <span class="hljs-operator">&amp;</span><span class="hljs-operator">&amp;</span> finalBorrowed <span class="hljs-operator">&gt;</span> initialBorrowed) {
            <span class="hljs-keyword">revert</span> BorrowCapExceeded();
        }
    }

    <span class="hljs-comment">/// @notice Updates the interest rate.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_updateInterest</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> </span>{}
</code></pre>
<p>Along with requiring vault checks <em>after</em> an action updates the vault's state, our borrowable vault will also require checks <em>after</em> an account's state is updated. For the purposes of our lending market, these checks will be centered around ensuring that any action does not result in our account ending up in an unhealthy state. To do this we will first call the internal <code>_calculateLiabilityAndCollateral</code> call and then check that the liability value is not greater than the value of our collateral. Since our EVC lending market is modular and extendable to any arbitrary collateral vault we use the account and a list of collaterals for the account as arguments.</p>
<p>In this example we will simply create a placeholder collateralValue calculation, implementing a check that requires the only collateral value returned be the same asset deposited into this (the borrowable vault) at 90% of it's value. Later on will modify this function to integrate with our initial deposit only vault. We will also save gas by offering a flag bypassing the collateral check if the account has no liabilities (<em>debt</em>)</p>
<pre><code class="lang-solidity">    <span class="hljs-comment">/// @notice Checks the status of an account.</span>
    <span class="hljs-comment">/// @dev This function is called after any action that may affect the account's state.</span>
    <span class="hljs-comment">/// @param account The account to check.</span>
    <span class="hljs-comment">/// @param collaterals The collaterals of the account.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">doCheckAccountStatus</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> account, <span class="hljs-keyword">address</span>[] <span class="hljs-keyword">calldata</span> collaterals</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> </span>{
        (, <span class="hljs-keyword">uint256</span> liabilityValue, <span class="hljs-keyword">uint256</span> collateralValue) <span class="hljs-operator">=</span>
            _calculateLiabilityAndCollateral(account, collaterals, <span class="hljs-literal">true</span>);

        <span class="hljs-keyword">if</span> (liabilityValue <span class="hljs-operator">&gt;</span> collateralValue) {
            <span class="hljs-keyword">revert</span> AccountUnhealthy();
        }
    }

    <span class="hljs-comment">/// @notice Calculates the liability and collateral of an account.</span>
    <span class="hljs-comment">/// @param account The account.</span>
    <span class="hljs-comment">/// @param collaterals The collaterals of the account.</span>
    <span class="hljs-comment">/// @param skipCollateralIfNoLiability A flag indicating whether to skip collateral calculation if the account has</span>
    <span class="hljs-comment">/// no liability.</span>
    <span class="hljs-comment">/// @return liabilityAssets The liability assets.</span>
    <span class="hljs-comment">/// @return liabilityValue The liability value.</span>
    <span class="hljs-comment">/// @return collateralValue The risk-adjusted collateral value.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_calculateLiabilityAndCollateral</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> account,
        <span class="hljs-keyword">address</span>[] <span class="hljs-keyword">memory</span> collaterals,
        <span class="hljs-keyword">bool</span> skipCollateralIfNoLiability
    </span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span> liabilityAssets, <span class="hljs-keyword">uint256</span> liabilityValue, <span class="hljs-keyword">uint256</span> collateralValue</span>) </span>{
        liabilityAssets <span class="hljs-operator">=</span> _debtOf(account);

        <span class="hljs-keyword">if</span> (liabilityAssets <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span> <span class="hljs-operator">&amp;</span><span class="hljs-operator">&amp;</span> skipCollateralIfNoLiability) {
            <span class="hljs-keyword">return</span> (<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (liabilityAssets <span class="hljs-operator">&gt;</span> <span class="hljs-number">0</span>) {
            <span class="hljs-comment">// pricing doesn't matter</span>
            liabilityValue <span class="hljs-operator">=</span> liabilityAssets;
        }

        <span class="hljs-comment">// in this simple example, let's say that it's only possible to borrow against</span>
        <span class="hljs-comment">// the same asset up to 90% of its value</span>
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">uint256</span> i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&lt;</span> collaterals.<span class="hljs-built_in">length</span>; <span class="hljs-operator">+</span><span class="hljs-operator">+</span>i) {
            <span class="hljs-keyword">if</span> (collaterals[i] <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>)) {
                collateralValue <span class="hljs-operator">=</span> _convertToAssets(balanceOf[account], <span class="hljs-literal">false</span>) <span class="hljs-operator">*</span> <span class="hljs-number">9</span> <span class="hljs-operator">/</span> <span class="hljs-number">10</span>;
                <span class="hljs-keyword">break</span>;
            }
        }
    }
</code></pre>
<p>Now, two of our view function from our <code>SimpleVault</code> will need to be overridden. The <code>_convertToAssets</code> and <code>_convertToShares</code> use the totalAssets to determine the share to asset conversion, however in the context of our lending vault we need to account for the totalBorrowed amount in this calculation.</p>
<pre><code class="lang-solidity">    <span class="hljs-comment">/// @dev This function is overridden to take into account the fact that some of the assets may be borrowed.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_convertToShares</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> assets, <span class="hljs-keyword">bool</span> roundUp</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        (<span class="hljs-keyword">uint256</span> currentTotalBorrowed,,) <span class="hljs-operator">=</span> _accrueInterestCalculate();

        <span class="hljs-keyword">return</span> roundUp
            ? assets.mulDivUp(totalSupply <span class="hljs-operator">+</span> <span class="hljs-number">1</span>, _totalAssets <span class="hljs-operator">+</span> currentTotalBorrowed <span class="hljs-operator">+</span> <span class="hljs-number">1</span>)
            : assets.mulDivDown(totalSupply <span class="hljs-operator">+</span> <span class="hljs-number">1</span>, _totalAssets <span class="hljs-operator">+</span> currentTotalBorrowed <span class="hljs-operator">+</span> <span class="hljs-number">1</span>);
    }

    <span class="hljs-comment">/// @dev This function is overridden to take into account the fact that some of the assets may be borrowed.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_convertToAssets</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> shares, <span class="hljs-keyword">bool</span> roundUp</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        (<span class="hljs-keyword">uint256</span> currentTotalBorrowed,,) <span class="hljs-operator">=</span> _accrueInterestCalculate();

        <span class="hljs-keyword">return</span> roundUp
            ? shares.mulDivUp(_totalAssets <span class="hljs-operator">+</span> currentTotalBorrowed <span class="hljs-operator">+</span> <span class="hljs-number">1</span>, totalSupply <span class="hljs-operator">+</span> <span class="hljs-number">1</span>)
            : shares.mulDivDown(_totalAssets <span class="hljs-operator">+</span> currentTotalBorrowed <span class="hljs-operator">+</span> <span class="hljs-number">1</span>, totalSupply <span class="hljs-operator">+</span> <span class="hljs-number">1</span>);
    }
</code></pre>
<p>Our vault will also need to implement methods to increase and decrease the amount owed by any account taking a loan from the vault.</p>
<pre><code class="lang-solidity">    <span class="hljs-comment">/// @notice Increases the owed amount of an account.</span>
    <span class="hljs-comment">/// @param account The account.</span>
    <span class="hljs-comment">/// @param assets The assets.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_increaseOwed</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> account, <span class="hljs-keyword">uint256</span> assets</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> </span>{
        owed[account] <span class="hljs-operator">+</span><span class="hljs-operator">=</span> assets;
        _totalBorrowed <span class="hljs-operator">+</span><span class="hljs-operator">=</span> assets;
    }

    <span class="hljs-comment">/// @notice Decreases the owed amount of an account.</span>
    <span class="hljs-comment">/// @param account The account.</span>
    <span class="hljs-comment">/// @param assets The assets.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_decreaseOwed</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> account, <span class="hljs-keyword">uint256</span> assets</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> </span>{
        owed[account] <span class="hljs-operator">-</span><span class="hljs-operator">=</span> assets;

        <span class="hljs-keyword">uint256</span> __totalBorrowed <span class="hljs-operator">=</span> _totalBorrowed;
        _totalBorrowed <span class="hljs-operator">=</span> __totalBorrowed <span class="hljs-operator">&gt;</span><span class="hljs-operator">=</span> assets ? __totalBorrowed <span class="hljs-operator">-</span> assets : <span class="hljs-number">0</span>;
    }
</code></pre>
<p>Finally, we need to implement the lending functionality for our vault. First, we provide a view function to get the liability status of an account. We also need to provide the functionality for a users account to disassociate with the vault as a controller if it has no debt.</p>
<pre><code class="lang-solidity">    <span class="hljs-comment">/// @notice Disables the controller.</span>
    <span class="hljs-comment">/// @dev The controller is only disabled if the account has no debt. If the account has outstanding debt, the</span>
    <span class="hljs-comment">/// function reverts.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">disableController</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title">nonReentrant</span> </span>{
        <span class="hljs-comment">// ensure that the account does not have any liabilities before disabling controller</span>
        <span class="hljs-keyword">address</span> msgSender <span class="hljs-operator">=</span> _msgSender();
        <span class="hljs-keyword">if</span> (_debtOf(msgSender) <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span>) {
            EVCClient.disableController(msgSender);
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">revert</span> OutstandingDebt();
        }
    }

    <span class="hljs-comment">/// @notice Retrieves the liability and collateral value of a given account.</span>
    <span class="hljs-comment">/// @dev Account status is considered healthy if the collateral value is greater than or equal to the liability.</span>
    <span class="hljs-comment">/// @param account The address of the account to retrieve the liability and collateral value for.</span>
    <span class="hljs-comment">/// @return liabilityValue The total liability value of the account.</span>
    <span class="hljs-comment">/// @return collateralValue The total collateral value of the account.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getAccountLiabilityStatus</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> account</span>)
        <span class="hljs-title"><span class="hljs-keyword">external</span></span>
        <span class="hljs-title"><span class="hljs-keyword">view</span></span>
        <span class="hljs-title"><span class="hljs-keyword">virtual</span></span>
        <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span> liabilityValue, <span class="hljs-keyword">uint256</span> collateralValue</span>)
    </span>{
        (, liabilityValue, collateralValue) <span class="hljs-operator">=</span> _calculateLiabilityAndCollateral(account, getCollaterals(account), <span class="hljs-literal">false</span>);
    }
</code></pre>
<p>We now have all the functionality necessary to write our borrow logic. To do this we must first add a modifier to the function requiring that it be called through the EVC to ensure that borrow conforms to the constraints of our vault by requiring it to be called via the EVC.</p>
<p>By setting the <code>msgSender</code> to <code>_msgSenderForBorrow()</code> we ensure that, since the function can only be called via the EVC, the EVC caller is calling the operation on behalf of an account which it is authorized to do so. This means it checks whether the caller an the authenticated EVC caller or an enabled controller vault.</p>
<p>Next, the function creates a vault snapshot to make sure the state variables remain consistent throughout the operation. We can than increase the amount owed by the account, emit a borrow event and transfer the borrowed assets to the receiver.</p>
<p>Finally, the accounting is finalized by reducing the global <code>_totalAssets</code> counter by the amount borrowed. Note that, the global <code>_totalBorrowed</code> amount is already updated via the <code>_increseOwed()</code> function's logic. This operation requires that we call the checks function for the vault and account after all state updating operations, ensuring the maximum borrow amount for the vault is respected and that the account health requirement for the borrower is also respected.</p>
<pre><code class="lang-solidity">    <span class="hljs-comment">/// @notice Borrows assets.</span>
    <span class="hljs-comment">/// @param assets The amount of assets to borrow.</span>
    <span class="hljs-comment">/// @param receiver The receiver of the assets.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">borrow</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> assets, <span class="hljs-keyword">address</span> receiver</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">callThroughEVC</span> <span class="hljs-title">nonReentrant</span> </span>{
        <span class="hljs-keyword">address</span> msgSender <span class="hljs-operator">=</span> _msgSenderForBorrow();

        createVaultSnapshot();

        <span class="hljs-built_in">require</span>(assets <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span>, <span class="hljs-string">"ZERO_ASSETS"</span>);

        _increaseOwed(msgSender, assets);

        <span class="hljs-keyword">emit</span> Borrow(msgSender, receiver, assets);

        asset.safeTransfer(receiver, assets);

        _totalAssets <span class="hljs-operator">-</span><span class="hljs-operator">=</span> assets;

        requireAccountAndVaultStatusCheck(msgSender);
    }
</code></pre>
<p>The next function our lending market is going to need to implement is the ability for a debt to be repaid. Again, this function will be modified to require calling it through the EVC. However, the <code>_msgSender()</code> for this function will set to the result of the <code>evc.getCurrentOnBehalfOfAccount()</code> call. This is the account which has authenticated the call with the EVC. Since anyone can payoff the debt of an account, we don't need to call messageSenderForBorrow() which ensures the caller is controllerEnabled.</p>
<p>Once the call is authenticated and before any state is updated, we again need to create a vault snapshot. We are then free to transfer the repayment assets from the <code>msgSender</code> to the vault contract. update the <code>_totalAssets</code> global variable and call the <code>_decreaseOwed</code> function on the account being paid off. Once again, since we are updating an account and vault's state, we need to call <code>requireAccountAndVaultStatusCheck</code> after all operation have been completed to ensure consistency. However, keeping in mind our application specific requirements (<em>a lending market</em>) we can reason that we may want to allow an account to be unhealthy after repaying debt.</p>
<p>For example, if an account begins this operation in an unhealthy state and is paying back an amount that does not fully restore the accounts solvency we may still want to allow the operation to complete since it benefits the stability of our lending market. So, instead of passing the account being operated on as the status check argument, we can pass <code>address(0)</code> which tells the logic of the <code>requireAccountAndVaultStatusCheck</code> to simply call the <code>evc.requireVaultStatusCheck()</code> along.</p>
<pre><code class="lang-solidity">    <span class="hljs-comment">/// @notice Repays a debt.</span>
    <span class="hljs-comment">/// @dev This function transfers the specified amount of assets from the caller to the vault.</span>
    <span class="hljs-comment">/// @param assets The amount of assets to repay.</span>
    <span class="hljs-comment">/// @param receiver The receiver of the repayment.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">repay</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> assets, <span class="hljs-keyword">address</span> receiver</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">callThroughEVC</span> <span class="hljs-title">nonReentrant</span> </span>{
        <span class="hljs-keyword">address</span> msgSender <span class="hljs-operator">=</span> _msgSender();

        <span class="hljs-comment">// sanity check: the receiver must be under control of the EVC. otherwise, we allowed to disable this vault as</span>
        <span class="hljs-comment">// the controller for an account with debt</span>
        <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>isControllerEnabled(receiver, <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>))) {
            <span class="hljs-keyword">revert</span> ControllerDisabled();
        }

        createVaultSnapshot();

        <span class="hljs-built_in">require</span>(assets <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span>, <span class="hljs-string">"ZERO_ASSETS"</span>);

        asset.safeTransferFrom(msgSender, <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), assets);

        _totalAssets <span class="hljs-operator">+</span><span class="hljs-operator">=</span> assets;

        _decreaseOwed(receiver, assets);

        <span class="hljs-keyword">emit</span> Repay(msgSender, receiver, assets);

        requireAccountAndVaultStatusCheck(<span class="hljs-keyword">address</span>(<span class="hljs-number">0</span>));
    }
</code></pre>
<p>The final functionality we may want to implement for our lending pool vault is <code>pullDebt</code>. This will allow a caller to rebalance the debt from a given account onto their own account. No funds will actually be transferred in this process, the accounting of the debt will simply be moved from one account to another. This is still equivalent to the caller creating a borrow, so we must authenticate the msgSender with <code>_msgSenderForBorrow</code> in the same way we did in the <code>borrow</code> function. After creating a snapshot of the vault and checking the arguments are valid, we can update the state of both accounts; decreasing the debt of the <code>from</code> parameter and increasing the debt of the caller.</p>
<p>We finally require a vault check and an account status check on the function caller (<code>msgSender</code>)</p>
<pre><code class="lang-solidity">    <span class="hljs-comment">/// @notice Pulls debt from an account.</span>
    <span class="hljs-comment">/// @dev This function decreases the debt of one account and increases the debt of another.</span>
    <span class="hljs-comment">/// @dev Despite the lack of asset transfers, this function emits Repay and Borrow events.</span>
    <span class="hljs-comment">/// @param from The account to pull the debt from.</span>
    <span class="hljs-comment">/// @param assets The amount of debt to pull.</span>
    <span class="hljs-comment">/// @return A boolean indicating whether the operation was successful.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">pullDebt</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> <span class="hljs-keyword">from</span>, <span class="hljs-keyword">uint256</span> assets</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">callThroughEVC</span> <span class="hljs-title">nonReentrant</span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bool</span></span>) </span>{
        <span class="hljs-keyword">address</span> msgSender <span class="hljs-operator">=</span> _msgSenderForBorrow();

        <span class="hljs-comment">// sanity check: the account from which the debt is pulled must be under control of the EVC.</span>
        <span class="hljs-comment">// _msgSenderForBorrow() checks that `msgSender` is controlled by this vault</span>
        <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>isControllerEnabled(<span class="hljs-keyword">from</span>, <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>))) {
            <span class="hljs-keyword">revert</span> ControllerDisabled();
        }

        createVaultSnapshot();

        <span class="hljs-built_in">require</span>(assets <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span>, <span class="hljs-string">"ZERO_AMOUNT"</span>);
        <span class="hljs-built_in">require</span>(msgSender <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-keyword">from</span>, <span class="hljs-string">"SELF_DEBT_PULL"</span>);

        _decreaseOwed(<span class="hljs-keyword">from</span>, assets);
        _increaseOwed(msgSender, assets);

        <span class="hljs-keyword">emit</span> Repay(msgSender, <span class="hljs-keyword">from</span>, assets);
        <span class="hljs-keyword">emit</span> Borrow(msgSender, msgSender, assets);

        requireAccountAndVaultStatusCheck(msgSender);

        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
    }
</code></pre>
<p>With that we have implemented all of the initial functionality to create a borrowable vault in our lending market.</p>
]]></content:encoded></item><item><title><![CDATA[Euler Vault Connector Basics]]></title><description><![CDATA[Euler vaults implement an innovative new primitive for DeFi lending markets allowing for modular and open innovation in financial instruments on chain. Allowing developers to extend lending markets by creating vaults which wrap arbitrary assets and a...]]></description><link>https://yielddev.io/euler-vault-connector-basics</link><guid isPermaLink="true">https://yielddev.io/euler-vault-connector-basics</guid><category><![CDATA[EVC]]></category><category><![CDATA[defi]]></category><category><![CDATA[Ethereum]]></category><category><![CDATA[Solidity]]></category><dc:creator><![CDATA[Yield Dev]]></dc:creator><pubDate>Mon, 10 Feb 2025 08:00:23 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1739233913587/7417c35e-8f65-4971-8950-2a5fb2de0299.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Euler vaults implement an innovative new primitive for DeFi lending markets allowing for modular and open innovation in financial instruments on chain. Allowing developers to extend lending markets by creating vaults which wrap arbitrary assets and allow them to be used as collateral in loans for other assets.</p>
<h3 id="heading-a-modular-financial-engineering-platform">A Modular Financial Engineering Platform</h3>
<p>An EVC contract is deployed to act as the mediator for our modular financial system. Acting as the authentication mechanism, the EVC mediates interactions between users and the various vaults in our system. The EVC is implemented as a robustly featured multi-call contract, authenticating user operations and ensuring the resulting state of these operations conform to the constraints defined by our vaults.</p>
<p>Composable nodes in the EVC framework are implemented as <code>EVCClient</code> vaults extending the ERC4626 standard with a set of utilities required to allow the EVC to act as an authentication mechanism for operation between vaults in on our platform. These client vaults, are extended further to define the bespoke financial logic pertaining to a specific assets on the platform and allowing for a highly composable set of custom financial transaction to be implemented without fragmenting liquidity or siloing capital within a monolithic DeFi protocol.</p>
<p>These vaults are meant to act as an entry point to a modular financial system, wrapping user assets in a highly flexible financial primitive which can be used to create complex agreements between vaults on the platform or extended, in an open and permission-less way, by other platforms and developers.</p>
<h3 id="heading-basics">Basics</h3>
<p>The most basic EVC setup involves two initial vaults; a deposit only vault specifically for accepting a collateral asset and a borrowable vault which accepts deposits for an asset meant to be lent out against the collateral asset. These vaults, along with the EVC, create the basis for a simple lending market.</p>
<p>This example illustrates the simplicity of the EVC's design, as a simple lending market can be implemented utilizing two vaults. Borrowers would deposit their collateral into the deposit vault and liquidity providers can deposit into the borrowable vault, while the EVC mediates interactions between these two vaults ensuring that they conform to the rules of our lending market.</p>
<h3 id="heading-walkthrough">Walkthrough</h3>
<p>In this guide we will be walking through reimplementing the <code>VaultSimple</code> contract from the EVC Playground repository. The full code can be found in the <a target="_blank" href="https://github.com/euler-xyz/evc-playground">EVC playground repository</a>. Further reading on the design and concepts around the EVC can be found in the <a target="_blank" href="https://evc.wtf/docs/whitepaper/#introduction">whitepaper</a></p>
<p>While the deposit only vault wraps an asset to be utilized as collateral by the EVC infrastructure, the borrowable vault can be extended to implement the specific terms of the lending market's loans. Including the interest rate, price oracle, collateral ratio requirements and, in the case of multiple collaterals, the allowable collateral assets.</p>
<p>First we will break down the implementation of the simplest borrow-only collateral vault.</p>
<h3 id="heading-evcclient">EVCClient</h3>
<p>All vaults interacting with the EVC must implement an EVCClient interface exposing a set of utilities for authenticating the contract callers in the context of the EVC which includes scheduling status check and implementing the ability to unilaterally liquidate collateral shares when appropriate.</p>
<p>These utilities include a getter for determining which collaterals are enabled for an account as well as getters for determining which controllers(<code>Vaults</code>) are enabled for executing on account. It also exposes functionality for disabling a controller.</p>
<p>The client also implements a set of checks, which vault contracts that inherit the EVCClient can schedule when appropriate to ensure conformity to the requirements of the EVC. Including:</p>
<p><code>requireAccountStatusCheck</code> <code>requireVaultStatusCheck</code> <code>requireAccountAndVaultStatusCheck</code> <code>forgiveAccountStatusCheck</code> <code>isAccountStatusCheckDeferred</code> <code>isVaultStatusCheckDeferred</code></p>
<p>These checks are used to ensure the solvency of an account or vault after any action that may result in transfer of funds.</p>
<p>Finally, the client implements a <code>liquidateCollateralShares</code> function, allowing the controller to unilaterally seize shares of collateral in the event of a liquidation.</p>
<p>This is the collateral vault of our lending market, and in order to conform to the requirements of the vault controller must implement a couple of basic functions and checks. These requirements can be inherited from the <code>VaultBase</code> contract which itself is a EVCClient contract. Here we have reentrancy locks, vault and account status checks and snapshots.</p>
<pre><code class="lang-solidity">    <span class="hljs-comment">/// @notice Creates a snapshot of the vault state</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createVaultSnapshot</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> </span>{
        <span class="hljs-comment">// We delete snapshots on `checkVaultStatus`, which can only happen at the end of the EVC batch. Snapshots are</span>
        <span class="hljs-comment">// taken before any action is taken on the vault that affects the cault asset records and deleted at the end, so</span>
        <span class="hljs-comment">// that asset calculations are always based on the state before the current batch of actions.</span>
        <span class="hljs-keyword">if</span> (snapshot.<span class="hljs-built_in">length</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span>) {
            snapshot <span class="hljs-operator">=</span> doCreateVaultSnapshot();
        }
    }

    <span class="hljs-comment">/// @notice Checks the vault status</span>
    <span class="hljs-comment">/// @dev Executed as a result of requiring vault status check on the EVC.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">checkVaultStatus</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">onlyEVCWithChecksInProgress</span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bytes4</span> magicValue</span>) </span>{
        doCheckVaultStatus(snapshot);
        <span class="hljs-keyword">delete</span> snapshot;

        <span class="hljs-keyword">return</span> IVault.checkVaultStatus.<span class="hljs-built_in">selector</span>;
    }

    <span class="hljs-comment">/// @notice Checks the account status</span>
    <span class="hljs-comment">/// @dev Executed on a controller as a result of requiring account status check on the EVC.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">checkAccountStatus</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> account,
        <span class="hljs-keyword">address</span>[] <span class="hljs-keyword">calldata</span> collaterals
    </span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title">onlyEVCWithChecksInProgress</span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bytes4</span> magicValue</span>) </span>{
        doCheckAccountStatus(account, collaterals);

        <span class="hljs-keyword">return</span> IVault.checkAccountStatus.<span class="hljs-built_in">selector</span>;
    }
</code></pre>
<p>Here we see the <code>creatVaultSnapshot</code> implementation which saves the state of the vault so that any action taken on the vault that might effect asset calculation are calculated based on the state of the vault before the functions execution started. Preventing many exploits.</p>
<p>We also see the implementation of <code>checkVaultStatus</code>, this calls the doCheckVaultStatus function and deletes the the current snapshot</p>
<p>Finally, <code>checkAccountStatus</code> calls the <code>doCheckAccountStatus</code> function when the EVC requires a check of the accounts health.</p>
<p>In addition to implementing certain basic functionality required by the vault controller, the VaultBase also defines several functions which must be implemented in our borrowable vault contract to configure the lending market.</p>
<pre><code class="lang-solidity">    <span class="hljs-comment">/// @notice Creates a snapshot of the vault state</span>
    <span class="hljs-comment">/// @dev Must be overridden by child contracts</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">doCreateVaultSnapshot</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bytes</span> <span class="hljs-keyword">memory</span> snapshot</span>)</span>;

    <span class="hljs-comment">/// @notice Checks the vault status</span>
    <span class="hljs-comment">/// @dev Must be overridden by child contracts</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">doCheckVaultStatus</span>(<span class="hljs-params"><span class="hljs-keyword">bytes</span> <span class="hljs-keyword">memory</span> snapshot</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span></span>;

    <span class="hljs-comment">/// @notice Checks the account status</span>
    <span class="hljs-comment">/// @dev Must be overridden by child contracts</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">doCheckAccountStatus</span>(<span class="hljs-params"><span class="hljs-keyword">address</span>, <span class="hljs-keyword">address</span>[] <span class="hljs-keyword">calldata</span></span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span></span>;

    <span class="hljs-comment">/// @notice Disables a controller for an account</span>
    <span class="hljs-comment">/// @dev Must be overridden by child contracts. Must call the EVC.disableController() only if it's safe to do so</span>
    <span class="hljs-comment">/// (i.e. the account has repaid their debt in full)</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">disableController</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span></span>;
</code></pre>
<p>In order that we may build out our <code>VaultSimple</code> contract, we will be importing and inheriting the <code>VaultBase.sol</code> so that our resulting vault is a compliant EVCClient vault. The code for these abstract base contracts can be found here:</p>
<p><a target="_blank" href="https://github.com/euler-xyz/evc-playground/blob/master/src/vaults/VaultBase.sol"><code>VaultBase.sol</code></a> <a target="_blank" href="https://github.com/euler-xyz/evc-playground/blob/master/src/utils/EVCClient.sol">EVCClient.sol</a></p>
<h3 id="heading-implementing-vaultsimplesol-a-deposit-only-collateral-vault">Implementing <code>VaultSimple.sol</code>: A Deposit Only, Collateral Vault</h3>
<p>Now that we understand the Base Vault and the EVCClient implementation, we can begin building our simple vault by inheriting the BaseVault contract.</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// SPDX-License-Identifier: GPL-2.0-or-later</span>

<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.19;</span>

<span class="hljs-keyword">import</span> <span class="hljs-string">"solmate/auth/Owned.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"solmate/tokens/ERC4626.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"solmate/utils/SafeTransferLib.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"solmate/utils/FixedPointMathLib.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"../VaultBase.sol"</span>;

<span class="hljs-comment">/// @title VaultSimple</span>
<span class="hljs-comment">/// @dev It provides basic functionality for vaults.</span>
<span class="hljs-comment">/// @notice In this contract, the EVC is authenticated before any action that may affect the state of the vault or an</span>
<span class="hljs-comment">/// account. This is done to ensure that if it's EVC calling, the account is correctly authorized. Unlike solmate,</span>
<span class="hljs-comment">/// VaultSimple implementation prevents from share inflation attack by using virtual assets and shares. Look into</span>
<span class="hljs-comment">/// Open-Zeppelin documentation for more details. This vault implements internal balance tracking. This contract does</span>
<span class="hljs-comment">/// not take the supply cap into account when calculating max deposit and max mint values.</span>
<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">VaultSimple</span> <span class="hljs-keyword">is</span> <span class="hljs-title">VaultBase</span>, <span class="hljs-title">Owned</span>, <span class="hljs-title">ERC4626</span> </span>{
    <span class="hljs-keyword">using</span> <span class="hljs-title">SafeTransferLib</span> <span class="hljs-title"><span class="hljs-keyword">for</span></span> <span class="hljs-title">ERC20</span>;
    <span class="hljs-keyword">using</span> <span class="hljs-title">FixedPointMathLib</span> <span class="hljs-title"><span class="hljs-keyword">for</span></span> <span class="hljs-title"><span class="hljs-keyword">uint256</span></span>;

    <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">SupplyCapSet</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> newSupplyCap</span>)</span>;

    <span class="hljs-function"><span class="hljs-keyword">error</span> <span class="hljs-title">SnapshotNotTaken</span>(<span class="hljs-params"></span>)</span>;
    <span class="hljs-function"><span class="hljs-keyword">error</span> <span class="hljs-title">SupplyCapExceeded</span>(<span class="hljs-params"></span>)</span>;

    <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">internal</span> _totalAssets;
    <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> supplyCap;

    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> _evc,
        ERC20 _asset,
        <span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> _name,
        <span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> _symbol
    </span>) <span class="hljs-title">VaultBase</span>(<span class="hljs-params">_evc</span>) <span class="hljs-title">Owned</span>(<span class="hljs-params"><span class="hljs-built_in">msg</span>.sender</span>) <span class="hljs-title">ERC4626</span>(<span class="hljs-params">_asset, _name, _symbol</span>) </span>{}
}
</code></pre>
<p>We also inherit from ERC4626, as our Simple Vault will wrap an ERC4626 standard interface for use in the EVC context.</p>
<p>Now we can implement a Simple, Deposit only vault meant to wrap collateral assets for use by the EVC.</p>
<p>Firstly, our vault must implement the vault snapshot function, in the case of a deposit only vault we simply return the <code>_totalAssets</code> managed by the vault. This snapshot is meant to ensure that any calculation involving the vaults state are based off of the state of the vault before any operations occur, this is important for maintaining consistency in vaults with more complex logic.</p>
<pre><code class="lang-solidity">    <span class="hljs-comment">/// @notice Creates a snapshot of the vault.</span>
    <span class="hljs-comment">/// @dev This function is called before any action that may affect the vault's state.</span>
    <span class="hljs-comment">/// @return A snapshot of the vault's state.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">doCreateVaultSnapshot</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bytes</span> <span class="hljs-keyword">memory</span></span>) </span>{
        <span class="hljs-comment">// make total assets snapshot here and return it:</span>
        <span class="hljs-keyword">return</span> <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encode</span>(_totalAssets);
    }
</code></pre>
<p>Next, we define the <code>doCheckVaultStatus</code> function required by the <code>BaseVault</code> interface. here we ensure the vault snapshot was taken before validating the vault state and implementing the vault specific check required by our application. In the case of our deposit only collateral vault this is simply implementing a check to enforce the supply cap.</p>
<pre><code class="lang-solidity">    <span class="hljs-comment">/// @notice Checks the vault's status.</span>
    <span class="hljs-comment">/// @dev This function is called after any action that may affect the vault's state.</span>
    <span class="hljs-comment">/// @param oldSnapshot The snapshot of the vault's state before the action.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">doCheckVaultStatus</span>(<span class="hljs-params"><span class="hljs-keyword">bytes</span> <span class="hljs-keyword">memory</span> oldSnapshot</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> </span>{
        <span class="hljs-comment">// sanity check in case the snapshot hasn't been taken</span>
        <span class="hljs-keyword">if</span> (oldSnapshot.<span class="hljs-built_in">length</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span>) <span class="hljs-keyword">revert</span> SnapshotNotTaken();

        <span class="hljs-comment">// validate the vault state here:</span>
        <span class="hljs-keyword">uint256</span> initialSupply <span class="hljs-operator">=</span> <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">decode</span>(oldSnapshot, (<span class="hljs-keyword">uint256</span>));
        <span class="hljs-keyword">uint256</span> finalSupply <span class="hljs-operator">=</span> _convertToAssets(totalSupply, <span class="hljs-literal">false</span>);

        <span class="hljs-comment">// the supply cap can be implemented like this:</span>
        <span class="hljs-keyword">if</span> (supplyCap <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span> <span class="hljs-operator">&amp;</span><span class="hljs-operator">&amp;</span> finalSupply <span class="hljs-operator">&gt;</span> supplyCap <span class="hljs-operator">&amp;</span><span class="hljs-operator">&amp;</span> finalSupply <span class="hljs-operator">&gt;</span> initialSupply) {
            <span class="hljs-keyword">revert</span> SupplyCapExceeded();
        }
    }
</code></pre>
<p>The next function required by our <code>BaseVault</code> parent contract's interface is the <code>doCheckAccountStatus</code> check. This function is actually not required to implement our deposit only functionality so we can leave it blank. However, typically any transaction that may effect the solvency of a user's account will require this check to be called at the end of any set of operations to ensure that the preceding operation did not result in a state that violates the rules of our lending market (i.e insolvency, bad debt etc)</p>
<pre><code class="lang-solidity">    <span class="hljs-comment">/// @notice Checks the status of an account.</span>
    <span class="hljs-comment">/// @dev This function is called after any action that may affect the account's state.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">doCheckAccountStatus</span>(<span class="hljs-params"><span class="hljs-keyword">address</span>, <span class="hljs-keyword">address</span>[] <span class="hljs-keyword">calldata</span></span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> </span>{
        <span class="hljs-comment">// no need to do anything here because the vault does not allow borrowing</span>
    }
</code></pre>
<p>Now we are also required to implement a disableController function to allow an account to disassociate with a vault. However our deposit only vault, since it does not allow borrowing, should not ever be a controller.</p>
<pre><code class="lang-solidity">    <span class="hljs-comment">/// @notice Disables the controller.</span>
    <span class="hljs-comment">/// @dev The controller is only disabled if the account has no debt.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">disableController</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title">nonReentrant</span> </span>{
        <span class="hljs-comment">// this vault doesn't allow borrowing, so we can't check that the account has no debt.</span>
        <span class="hljs-comment">// this vault should never be a controller, but user errors can happen</span>
        EVCClient.disableController(_msgSender());
    }
</code></pre>
<p>Now that we've implemented the <code>VaultBase</code> interface, we move on to typical view functions required to implement a ERC4626 vault. These getter/view functions return an internal calculation for converting between assets(deposited into the vault) and shares (the wrapped representation of these assets).</p>
<pre><code class="lang-solidity">    <span class="hljs-comment">/// @notice Returns the total assets of the vault.</span>
    <span class="hljs-comment">/// @return The total assets.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">totalAssets</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> _totalAssets;
    }

    <span class="hljs-comment">/// @notice Converts assets to shares.</span>
    <span class="hljs-comment">/// @dev That function is manipulable in its current form as it uses exact values. Considering that other vaults may</span>
    <span class="hljs-comment">/// rely on it, for a production vault, a manipulation resistant mechanism should be implemented.</span>
    <span class="hljs-comment">/// @dev Considering that this function may be relied on by controller vaults, it's read-only re-entrancy protected.</span>
    <span class="hljs-comment">/// @param assets The assets to convert.</span>
    <span class="hljs-comment">/// @return The converted shares.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">convertToShares</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> assets</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title">nonReentrantRO</span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> _convertToShares(assets, <span class="hljs-literal">false</span>);
    }

    <span class="hljs-comment">/// @notice Converts shares to assets.</span>
    <span class="hljs-comment">/// @dev That function is manipulable in its current form as it uses exact values. Considering that other vaults may</span>
    <span class="hljs-comment">/// rely on it, for a production vault, a manipulation resistant mechanism should be implemented.</span>
    <span class="hljs-comment">/// @dev Considering that this function may be relied on by controller vaults, it's read-only re-entrancy protected.</span>
    <span class="hljs-comment">/// @param shares The shares to convert.</span>
    <span class="hljs-comment">/// @return The converted assets.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">convertToAssets</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> shares</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title">nonReentrantRO</span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> _convertToAssets(shares, <span class="hljs-literal">false</span>);
    }

    <span class="hljs-comment">/// @notice Simulates the effects of depositing a certain amount of assets at the current block.</span>
    <span class="hljs-comment">/// @param assets The amount of assets to simulate depositing.</span>
    <span class="hljs-comment">/// @return The amount of shares that would be minted.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">previewDeposit</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> assets</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> _convertToShares(assets, <span class="hljs-literal">false</span>);
    }

    <span class="hljs-comment">/// @notice Simulates the effects of minting a certain amount of shares at the current block.</span>
    <span class="hljs-comment">/// @param shares The amount of shares to simulate minting.</span>
    <span class="hljs-comment">/// @return The amount of assets that would be deposited.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">previewMint</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> shares</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> _convertToAssets(shares, <span class="hljs-literal">true</span>);
    }

    <span class="hljs-comment">/// @notice Simulates the effects of withdrawing a certain amount of assets at the current block.</span>
    <span class="hljs-comment">/// @param assets The amount of assets to simulate withdrawing.</span>
    <span class="hljs-comment">/// @return The amount of shares that would be burned.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">previewWithdraw</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> assets</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> _convertToShares(assets, <span class="hljs-literal">true</span>);
    }

    <span class="hljs-comment">/// @notice Simulates the effects of redeeming a certain amount of shares at the current block.</span>
    <span class="hljs-comment">/// @param shares The amount of shares to simulate redeeming.</span>
    <span class="hljs-comment">/// @return The amount of assets that would be redeemed.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">previewRedeem</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> shares</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> _convertToAssets(shares, <span class="hljs-literal">false</span>);
    }
</code></pre>
<p>These views need to further call the conversion calculation of shares to assets and assets to shares as required by ERC4262:</p>
<pre><code class="lang-solidity">    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_convertToShares</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> assets, <span class="hljs-keyword">bool</span> roundUp</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> roundUp
            ? assets.mulDivUp(totalSupply <span class="hljs-operator">+</span> <span class="hljs-number">1</span>, _totalAssets <span class="hljs-operator">+</span> <span class="hljs-number">1</span>)
            : assets.mulDivDown(totalSupply <span class="hljs-operator">+</span> <span class="hljs-number">1</span>, _totalAssets <span class="hljs-operator">+</span> <span class="hljs-number">1</span>);
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_convertToAssets</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> shares, <span class="hljs-keyword">bool</span> roundUp</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> roundUp
            ? shares.mulDivUp(_totalAssets <span class="hljs-operator">+</span> <span class="hljs-number">1</span>, totalSupply <span class="hljs-operator">+</span> <span class="hljs-number">1</span>)
            : shares.mulDivDown(_totalAssets <span class="hljs-operator">+</span> <span class="hljs-number">1</span>, totalSupply <span class="hljs-operator">+</span> <span class="hljs-number">1</span>);
    }
</code></pre>
<p>Now we need an approval method in order for our vaults shares to conform to the ERC20 standard. We override this function to set <code>msgSender</code> to the result of <code>_msgSender()</code> a function exposed by the <a target="_blank" href="https://github.com/euler-xyz/ethereum-vault-connector/blob/master/src/utils/EVCUtil.sol#L104"><code>EVCUtil</code></a> contract and inherited from the <a target="_blank" href="https://github.com/euler-xyz/evc-playground/blob/master/src/utils/EVCClient.sol#L11"><code>EVCClient</code></a> base contract. This allows this function to be called and authenticated via the EVC on behalf of a given account if this call is being made by the EVC, which is not required as it does not implement the <code>callThroughEVC</code> modifier:</p>
<pre><code class="lang-solidity">    <span class="hljs-comment">/// @notice Approves a spender to spend a certain amount.</span>
    <span class="hljs-comment">/// @param spender The spender to approve.</span>
    <span class="hljs-comment">/// @param amount The amount to approve.</span>
    <span class="hljs-comment">/// @return A boolean indicating whether the approval was successful.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">approve</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> spender, <span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bool</span></span>) </span>{
        <span class="hljs-keyword">address</span> msgSender <span class="hljs-operator">=</span> _msgSender();

        allowance[msgSender][spender] <span class="hljs-operator">=</span> amount;

        <span class="hljs-keyword">emit</span> Approval(msgSender, spender, amount);

        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
    }
</code></pre>
<p>In the same vein, our ERC20 compliant shares of this vault will require a transfer mechanism. However, since this vault must be used as a deposit vault for collateral we need to implement a vault snapshot as well as an account and vault status check to ensure that both the user's account and the vault remain solvent and consistent after each transfer.</p>
<p>To do this we simply call the <code>requireAccountAndVaultStatusCheck</code> defined on the EVCClient contract we have inherited. This function further calls the EVC to schedule these checks.</p>
<p>Note that, again, we have set the <code>msgSender</code> to the <code>_msgSender()</code> in the context of the EVC. So even thought the EVC is the canonical caller of this operation, the logic is operating on the account authenticated in the EVC's <code>onBehalfOf</code> context. Since this function's operation always results in the transfer of funds, the <code>callThroughEVC</code> modifier is implemented requiring that the this call is delegated via the <code>evc</code> ensuring the scheduled account and vault checks are triggered at the end of its operations.</p>
<pre><code class="lang-solidity">    <span class="hljs-comment">/// @notice Transfers a certain amount of shares to a recipient.</span>
    <span class="hljs-comment">/// @param to The recipient of the transfer.</span>
    <span class="hljs-comment">/// @param amount The amount shares to transfer.</span>
    <span class="hljs-comment">/// @return A boolean indicating whether the transfer was successful.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">transfer</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> to, <span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title">callThroughEVC</span> <span class="hljs-title">nonReentrant</span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bool</span></span>) </span>{
        <span class="hljs-keyword">address</span> msgSender <span class="hljs-operator">=</span> _msgSender();

        createVaultSnapshot();

        balanceOf[msgSender] <span class="hljs-operator">-</span><span class="hljs-operator">=</span> amount;

        <span class="hljs-comment">// Cannot overflow because the sum of all user</span>
        <span class="hljs-comment">// balances can't exceed the max uint256 value.</span>
        <span class="hljs-keyword">unchecked</span> {
            balanceOf[to] <span class="hljs-operator">+</span><span class="hljs-operator">=</span> amount;
        }

        <span class="hljs-keyword">emit</span> Transfer(msgSender, to, amount);

        <span class="hljs-comment">// despite the fact that the vault status check might not be needed for shares transfer with current logic, it's</span>
        <span class="hljs-comment">// added here so that if anyone changes the snapshot/vault status check mechanisms in the inheriting contracts,</span>
        <span class="hljs-comment">// they will not forget to add the vault status check here</span>
        requireAccountAndVaultStatusCheck(msgSender);

        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
    }
</code></pre>
<p>Similarly to our transfer, our mint function also needs to check the consistency of our vault. Which, if you recall, simply enforces our supply cap.</p>
<pre><code class="lang-solidity">    <span class="hljs-comment">/// @notice Mints a certain amount of shares for a receiver.</span>
    <span class="hljs-comment">/// @param shares The shares to mint.</span>
    <span class="hljs-comment">/// @param receiver The receiver of the mint.</span>
    <span class="hljs-comment">/// @return assets The assets equivalent to the minted shares.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">mint</span>(<span class="hljs-params">
        <span class="hljs-keyword">uint256</span> shares,
        <span class="hljs-keyword">address</span> receiver
    </span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title">callThroughEVC</span> <span class="hljs-title">nonReentrant</span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span> assets</span>) </span>{
        <span class="hljs-keyword">address</span> msgSender <span class="hljs-operator">=</span> _msgSender();

        createVaultSnapshot();

        assets <span class="hljs-operator">=</span> _convertToAssets(shares, <span class="hljs-literal">true</span>); <span class="hljs-comment">// No need to check for rounding error, previewMint rounds up.</span>

        <span class="hljs-comment">// Need to transfer before minting or ERC777s could reenter.</span>
        asset.safeTransferFrom(msgSender, <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), assets);

        _totalAssets <span class="hljs-operator">+</span><span class="hljs-operator">=</span> assets;

        _mint(receiver, shares);

        <span class="hljs-keyword">emit</span> Deposit(msgSender, receiver, assets, shares);

        requireVaultStatusCheck();
    }
</code></pre>
<p>Similar to the transfer function our withdraw function will need to check the Account and vault status to ensure the solvency of accounts, I.E make sure users with open debts don't withdraw more collateral than the minimum amount required to sustain their loan's margin requirements.</p>
<pre><code class="lang-solidity">    <span class="hljs-comment">/// @notice Withdraws a certain amount of assets for a receiver.</span>
    <span class="hljs-comment">/// @param assets The assets to withdraw.</span>
    <span class="hljs-comment">/// @param receiver The receiver of the withdrawal.</span>
    <span class="hljs-comment">/// @param owner The owner of the assets.</span>
    <span class="hljs-comment">/// @return shares The shares equivalent to the withdrawn assets.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">withdraw</span>(<span class="hljs-params">
        <span class="hljs-keyword">uint256</span> assets,
        <span class="hljs-keyword">address</span> receiver,
        <span class="hljs-keyword">address</span> owner
    </span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title">callThroughEVC</span> <span class="hljs-title">nonReentrant</span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span> shares</span>) </span>{
        <span class="hljs-keyword">address</span> msgSender <span class="hljs-operator">=</span> _msgSender();

        createVaultSnapshot();

        shares <span class="hljs-operator">=</span> _convertToShares(assets, <span class="hljs-literal">true</span>); <span class="hljs-comment">// No need to check for rounding error, previewWithdraw rounds up.</span>

        <span class="hljs-keyword">if</span> (msgSender <span class="hljs-operator">!</span><span class="hljs-operator">=</span> owner) {
            <span class="hljs-keyword">uint256</span> allowed <span class="hljs-operator">=</span> allowance[owner][msgSender]; <span class="hljs-comment">// Saves gas for limited approvals.</span>

            <span class="hljs-keyword">if</span> (allowed <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-keyword">type</span>(<span class="hljs-keyword">uint256</span>).<span class="hljs-built_in">max</span>) {
                allowance[owner][msgSender] <span class="hljs-operator">=</span> allowed <span class="hljs-operator">-</span> shares;
            }
        }

        _burn(owner, shares);

        <span class="hljs-keyword">emit</span> Withdraw(msgSender, receiver, owner, assets, shares);

        asset.safeTransfer(receiver, assets);

        _totalAssets <span class="hljs-operator">-</span><span class="hljs-operator">=</span> assets;

        requireAccountAndVaultStatusCheck(owner);
    }
</code></pre>
<p>Redeem, an ERC4626 compliant function allows a user to get the amount of assets out of the vault in return for a given number of shares. This function, as you can probably imagine, also requires checks on the account and vault to ensure solvency.</p>
<pre><code class="lang-solidity">    <span class="hljs-comment">/// @notice Redeems a certain amount of shares for a receiver.</span>
    <span class="hljs-comment">/// @param shares The shares to redeem.</span>
    <span class="hljs-comment">/// @param receiver The receiver of the redemption.</span>
    <span class="hljs-comment">/// @param owner The owner of the shares.</span>
    <span class="hljs-comment">/// @return assets The assets equivalent to the redeemed shares.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">redeem</span>(<span class="hljs-params">
        <span class="hljs-keyword">uint256</span> shares,
        <span class="hljs-keyword">address</span> receiver,
        <span class="hljs-keyword">address</span> owner
    </span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title">callThroughEVC</span> <span class="hljs-title">nonReentrant</span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span> assets</span>) </span>{
        <span class="hljs-keyword">address</span> msgSender <span class="hljs-operator">=</span> _msgSender();

        createVaultSnapshot();

        <span class="hljs-keyword">if</span> (msgSender <span class="hljs-operator">!</span><span class="hljs-operator">=</span> owner) {
            <span class="hljs-keyword">uint256</span> allowed <span class="hljs-operator">=</span> allowance[owner][msgSender]; <span class="hljs-comment">// Saves gas for limited approvals.</span>

            <span class="hljs-keyword">if</span> (allowed <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-keyword">type</span>(<span class="hljs-keyword">uint256</span>).<span class="hljs-built_in">max</span>) {
                allowance[owner][msgSender] <span class="hljs-operator">=</span> allowed <span class="hljs-operator">-</span> shares;
            }
        }

        <span class="hljs-comment">// Check for rounding error since we round down in previewRedeem.</span>
        <span class="hljs-built_in">require</span>((assets <span class="hljs-operator">=</span> _convertToAssets(shares, <span class="hljs-literal">false</span>)) <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span>, <span class="hljs-string">"ZERO_ASSETS"</span>);

        _burn(owner, shares);

        <span class="hljs-keyword">emit</span> Withdraw(msgSender, receiver, owner, assets, shares);

        asset.safeTransfer(receiver, assets);

        _totalAssets <span class="hljs-operator">-</span><span class="hljs-operator">=</span> assets;

        requireAccountAndVaultStatusCheck(owner);
    }
</code></pre>
<p>With that, we have implemented an EVC compliant ERC4626 vault for use as a deposit only collateral vault in our lending market.</p>
<p>Next we will need to implement a Borrowable vault. Our Borrowable Vault will extend our <code>SimpleVault</code> with the necessary functionality to allow the creation of loans in our market.</p>
<p>Here's the full code for the <code>SimpleVault</code> deposit only collateral vault.</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// SPDX-License-Identifier: GPL-2.0-or-later</span>

<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.19;</span>

<span class="hljs-keyword">import</span> <span class="hljs-string">"solmate/auth/Owned.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"solmate/tokens/ERC4626.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"solmate/utils/SafeTransferLib.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"solmate/utils/FixedPointMathLib.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"../VaultBase.sol"</span>;

<span class="hljs-comment">/// @title VaultSimple</span>
<span class="hljs-comment">/// @dev It provides basic functionality for vaults.</span>
<span class="hljs-comment">/// @notice In this contract, the EVC is authenticated before any action that may affect the state of the vault or an</span>
<span class="hljs-comment">/// account. This is done to ensure that if it's EVC calling, the account is correctly authorized. Unlike solmate,</span>
<span class="hljs-comment">/// VaultSimple implementation prevents from share inflation attack by using virtual assets and shares. Look into</span>
<span class="hljs-comment">/// Open-Zeppelin documentation for more details. This vault implements internal balance tracking. This contract does</span>
<span class="hljs-comment">/// not take the supply cap into account when calculating max deposit and max mint values.</span>
<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">VaultSimple</span> <span class="hljs-keyword">is</span> <span class="hljs-title">VaultBase</span>, <span class="hljs-title">Owned</span>, <span class="hljs-title">ERC4626</span> </span>{
    <span class="hljs-keyword">using</span> <span class="hljs-title">SafeTransferLib</span> <span class="hljs-title"><span class="hljs-keyword">for</span></span> <span class="hljs-title">ERC20</span>;
    <span class="hljs-keyword">using</span> <span class="hljs-title">FixedPointMathLib</span> <span class="hljs-title"><span class="hljs-keyword">for</span></span> <span class="hljs-title"><span class="hljs-keyword">uint256</span></span>;

    <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">SupplyCapSet</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> newSupplyCap</span>)</span>;

    <span class="hljs-function"><span class="hljs-keyword">error</span> <span class="hljs-title">SnapshotNotTaken</span>(<span class="hljs-params"></span>)</span>;
    <span class="hljs-function"><span class="hljs-keyword">error</span> <span class="hljs-title">SupplyCapExceeded</span>(<span class="hljs-params"></span>)</span>;

    <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">internal</span> _totalAssets;
    <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> supplyCap;

    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> _evc,
        ERC20 _asset,
        <span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> _name,
        <span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> _symbol
    </span>) <span class="hljs-title">VaultBase</span>(<span class="hljs-params">_evc</span>) <span class="hljs-title">Owned</span>(<span class="hljs-params"><span class="hljs-built_in">msg</span>.sender</span>) <span class="hljs-title">ERC4626</span>(<span class="hljs-params">_asset, _name, _symbol</span>) </span>{}

    <span class="hljs-comment">/// @notice Sets the supply cap of the vault.</span>
    <span class="hljs-comment">/// @param newSupplyCap The new supply cap.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setSupplyCap</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> newSupplyCap</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">onlyOwner</span> </span>{
        supplyCap <span class="hljs-operator">=</span> newSupplyCap;
        <span class="hljs-keyword">emit</span> SupplyCapSet(newSupplyCap);
    }

    <span class="hljs-comment">/// @notice Creates a snapshot of the vault.</span>
    <span class="hljs-comment">/// @dev This function is called before any action that may affect the vault's state.</span>
    <span class="hljs-comment">/// @return A snapshot of the vault's state.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">doCreateVaultSnapshot</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bytes</span> <span class="hljs-keyword">memory</span></span>) </span>{
        <span class="hljs-comment">// make total assets snapshot here and return it:</span>
        <span class="hljs-keyword">return</span> <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encode</span>(_totalAssets);
    }

    <span class="hljs-comment">/// @notice Checks the vault's status.</span>
    <span class="hljs-comment">/// @dev This function is called after any action that may affect the vault's state.</span>
    <span class="hljs-comment">/// @param oldSnapshot The snapshot of the vault's state before the action.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">doCheckVaultStatus</span>(<span class="hljs-params"><span class="hljs-keyword">bytes</span> <span class="hljs-keyword">memory</span> oldSnapshot</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> </span>{
        <span class="hljs-comment">// sanity check in case the snapshot hasn't been taken</span>
        <span class="hljs-keyword">if</span> (oldSnapshot.<span class="hljs-built_in">length</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span>) <span class="hljs-keyword">revert</span> SnapshotNotTaken();

        <span class="hljs-comment">// validate the vault state here:</span>
        <span class="hljs-keyword">uint256</span> initialSupply <span class="hljs-operator">=</span> <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">decode</span>(oldSnapshot, (<span class="hljs-keyword">uint256</span>));
        <span class="hljs-keyword">uint256</span> finalSupply <span class="hljs-operator">=</span> _convertToAssets(totalSupply, <span class="hljs-literal">false</span>);

        <span class="hljs-comment">// the supply cap can be implemented like this:</span>
        <span class="hljs-keyword">if</span> (supplyCap <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span> <span class="hljs-operator">&amp;</span><span class="hljs-operator">&amp;</span> finalSupply <span class="hljs-operator">&gt;</span> supplyCap <span class="hljs-operator">&amp;</span><span class="hljs-operator">&amp;</span> finalSupply <span class="hljs-operator">&gt;</span> initialSupply) {
            <span class="hljs-keyword">revert</span> SupplyCapExceeded();
        }
    }

    <span class="hljs-comment">/// @notice Checks the status of an account.</span>
    <span class="hljs-comment">/// @dev This function is called after any action that may affect the account's state.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">doCheckAccountStatus</span>(<span class="hljs-params"><span class="hljs-keyword">address</span>, <span class="hljs-keyword">address</span>[] <span class="hljs-keyword">calldata</span></span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> </span>{
        <span class="hljs-comment">// no need to do anything here because the vault does not allow borrowing</span>
    }

    <span class="hljs-comment">/// @notice Disables the controller.</span>
    <span class="hljs-comment">/// @dev The controller is only disabled if the account has no debt.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">disableController</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title">nonReentrant</span> </span>{
        <span class="hljs-comment">// this vault doesn't allow borrowing, so we can't check that the account has no debt.</span>
        <span class="hljs-comment">// this vault should never be a controller, but user errors can happen</span>
        EVCClient.disableController(_msgSender());
    }

    <span class="hljs-comment">/// @notice Returns the total assets of the vault.</span>
    <span class="hljs-comment">/// @return The total assets.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">totalAssets</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> _totalAssets;
    }

    <span class="hljs-comment">/// @notice Converts assets to shares.</span>
    <span class="hljs-comment">/// @dev That function is manipulable in its current form as it uses exact values. Considering that other vaults may</span>
    <span class="hljs-comment">/// rely on it, for a production vault, a manipulation resistant mechanism should be implemented.</span>
    <span class="hljs-comment">/// @dev Considering that this function may be relied on by controller vaults, it's read-only re-entrancy protected.</span>
    <span class="hljs-comment">/// @param assets The assets to convert.</span>
    <span class="hljs-comment">/// @return The converted shares.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">convertToShares</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> assets</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title">nonReentrantRO</span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> _convertToShares(assets, <span class="hljs-literal">false</span>);
    }

    <span class="hljs-comment">/// @notice Converts shares to assets.</span>
    <span class="hljs-comment">/// @dev That function is manipulable in its current form as it uses exact values. Considering that other vaults may</span>
    <span class="hljs-comment">/// rely on it, for a production vault, a manipulation resistant mechanism should be implemented.</span>
    <span class="hljs-comment">/// @dev Considering that this function may be relied on by controller vaults, it's read-only re-entrancy protected.</span>
    <span class="hljs-comment">/// @param shares The shares to convert.</span>
    <span class="hljs-comment">/// @return The converted assets.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">convertToAssets</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> shares</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title">nonReentrantRO</span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> _convertToAssets(shares, <span class="hljs-literal">false</span>);
    }

    <span class="hljs-comment">/// @notice Simulates the effects of depositing a certain amount of assets at the current block.</span>
    <span class="hljs-comment">/// @param assets The amount of assets to simulate depositing.</span>
    <span class="hljs-comment">/// @return The amount of shares that would be minted.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">previewDeposit</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> assets</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> _convertToShares(assets, <span class="hljs-literal">false</span>);
    }

    <span class="hljs-comment">/// @notice Simulates the effects of minting a certain amount of shares at the current block.</span>
    <span class="hljs-comment">/// @param shares The amount of shares to simulate minting.</span>
    <span class="hljs-comment">/// @return The amount of assets that would be deposited.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">previewMint</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> shares</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> _convertToAssets(shares, <span class="hljs-literal">true</span>);
    }

    <span class="hljs-comment">/// @notice Simulates the effects of withdrawing a certain amount of assets at the current block.</span>
    <span class="hljs-comment">/// @param assets The amount of assets to simulate withdrawing.</span>
    <span class="hljs-comment">/// @return The amount of shares that would be burned.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">previewWithdraw</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> assets</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> _convertToShares(assets, <span class="hljs-literal">true</span>);
    }

    <span class="hljs-comment">/// @notice Simulates the effects of redeeming a certain amount of shares at the current block.</span>
    <span class="hljs-comment">/// @param shares The amount of shares to simulate redeeming.</span>
    <span class="hljs-comment">/// @return The amount of assets that would be redeemed.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">previewRedeem</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> shares</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> _convertToAssets(shares, <span class="hljs-literal">false</span>);
    }

    <span class="hljs-comment">/// @notice Approves a spender to spend a certain amount.</span>
    <span class="hljs-comment">/// @param spender The spender to approve.</span>
    <span class="hljs-comment">/// @param amount The amount to approve.</span>
    <span class="hljs-comment">/// @return A boolean indicating whether the approval was successful.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">approve</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> spender, <span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bool</span></span>) </span>{
        <span class="hljs-keyword">address</span> msgSender <span class="hljs-operator">=</span> _msgSender();

        allowance[msgSender][spender] <span class="hljs-operator">=</span> amount;

        <span class="hljs-keyword">emit</span> Approval(msgSender, spender, amount);

        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
    }

    <span class="hljs-comment">/// @notice Transfers a certain amount of shares to a recipient.</span>
    <span class="hljs-comment">/// @param to The recipient of the transfer.</span>
    <span class="hljs-comment">/// @param amount The amount shares to transfer.</span>
    <span class="hljs-comment">/// @return A boolean indicating whether the transfer was successful.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">transfer</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> to, <span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title">callThroughEVC</span> <span class="hljs-title">nonReentrant</span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bool</span></span>) </span>{
        <span class="hljs-keyword">address</span> msgSender <span class="hljs-operator">=</span> _msgSender();

        createVaultSnapshot();

        balanceOf[msgSender] <span class="hljs-operator">-</span><span class="hljs-operator">=</span> amount;

        <span class="hljs-comment">// Cannot overflow because the sum of all user</span>
        <span class="hljs-comment">// balances can't exceed the max uint256 value.</span>
        <span class="hljs-keyword">unchecked</span> {
            balanceOf[to] <span class="hljs-operator">+</span><span class="hljs-operator">=</span> amount;
        }

        <span class="hljs-keyword">emit</span> Transfer(msgSender, to, amount);

        <span class="hljs-comment">// despite the fact that the vault status check might not be needed for shares transfer with current logic, it's</span>
        <span class="hljs-comment">// added here so that if anyone changes the snapshot/vault status check mechanisms in the inheriting contracts,</span>
        <span class="hljs-comment">// they will not forget to add the vault status check here</span>
        requireAccountAndVaultStatusCheck(msgSender);

        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
    }

    <span class="hljs-comment">/// @notice Transfers a certain amount of shares from a sender to a recipient.</span>
    <span class="hljs-comment">/// @param from The sender of the transfer.</span>
    <span class="hljs-comment">/// @param to The recipient of the transfer.</span>
    <span class="hljs-comment">/// @param amount The amount of shares to transfer.</span>
    <span class="hljs-comment">/// @return A boolean indicating whether the transfer was successful.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">transferFrom</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> <span class="hljs-keyword">from</span>,
        <span class="hljs-keyword">address</span> to,
        <span class="hljs-keyword">uint256</span> amount
    </span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title">callThroughEVC</span> <span class="hljs-title">nonReentrant</span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bool</span></span>) </span>{
        <span class="hljs-keyword">address</span> msgSender <span class="hljs-operator">=</span> _msgSender();

        createVaultSnapshot();

        <span class="hljs-keyword">uint256</span> allowed <span class="hljs-operator">=</span> allowance[<span class="hljs-keyword">from</span>][msgSender]; <span class="hljs-comment">// Saves gas for limited approvals.</span>

        <span class="hljs-keyword">if</span> (allowed <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-keyword">type</span>(<span class="hljs-keyword">uint256</span>).<span class="hljs-built_in">max</span>) {
            allowance[<span class="hljs-keyword">from</span>][msgSender] <span class="hljs-operator">=</span> allowed <span class="hljs-operator">-</span> amount;
        }

        balanceOf[<span class="hljs-keyword">from</span>] <span class="hljs-operator">-</span><span class="hljs-operator">=</span> amount;

        <span class="hljs-comment">// Cannot overflow because the sum of all user</span>
        <span class="hljs-comment">// balances can't exceed the max uint256 value.</span>
        <span class="hljs-keyword">unchecked</span> {
            balanceOf[to] <span class="hljs-operator">+</span><span class="hljs-operator">=</span> amount;
        }

        <span class="hljs-keyword">emit</span> Transfer(<span class="hljs-keyword">from</span>, to, amount);

        <span class="hljs-comment">// despite the fact that the vault status check might not be needed for shares transfer with current logic, it's</span>
        <span class="hljs-comment">// added here so that if anyone changes the snapshot/vault status check mechanisms in the inheriting contracts,</span>
        <span class="hljs-comment">// they will not forget to add the vault status check here</span>
        requireAccountAndVaultStatusCheck(<span class="hljs-keyword">from</span>);

        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
    }

    <span class="hljs-comment">/// @notice Deposits a certain amount of assets for a receiver.</span>
    <span class="hljs-comment">/// @param assets The assets to deposit.</span>
    <span class="hljs-comment">/// @param receiver The receiver of the deposit.</span>
    <span class="hljs-comment">/// @return shares The shares equivalent to the deposited assets.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">deposit</span>(<span class="hljs-params">
        <span class="hljs-keyword">uint256</span> assets,
        <span class="hljs-keyword">address</span> receiver
    </span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title">callThroughEVC</span> <span class="hljs-title">nonReentrant</span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span> shares</span>) </span>{
        <span class="hljs-keyword">address</span> msgSender <span class="hljs-operator">=</span> _msgSender();

        createVaultSnapshot();

        <span class="hljs-comment">// Check for rounding error since we round down in previewDeposit.</span>
        <span class="hljs-built_in">require</span>((shares <span class="hljs-operator">=</span> _convertToShares(assets, <span class="hljs-literal">false</span>)) <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span>, <span class="hljs-string">"ZERO_SHARES"</span>);

        <span class="hljs-comment">// Need to transfer before minting or ERC777s could reenter.</span>
        asset.safeTransferFrom(msgSender, <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), assets);

        _totalAssets <span class="hljs-operator">+</span><span class="hljs-operator">=</span> assets;

        _mint(receiver, shares);

        <span class="hljs-keyword">emit</span> Deposit(msgSender, receiver, assets, shares);

        requireVaultStatusCheck();
    }

    <span class="hljs-comment">/// @notice Mints a certain amount of shares for a receiver.</span>
    <span class="hljs-comment">/// @param shares The shares to mint.</span>
    <span class="hljs-comment">/// @param receiver The receiver of the mint.</span>
    <span class="hljs-comment">/// @return assets The assets equivalent to the minted shares.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">mint</span>(<span class="hljs-params">
        <span class="hljs-keyword">uint256</span> shares,
        <span class="hljs-keyword">address</span> receiver
    </span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title">callThroughEVC</span> <span class="hljs-title">nonReentrant</span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span> assets</span>) </span>{
        <span class="hljs-keyword">address</span> msgSender <span class="hljs-operator">=</span> _msgSender();

        createVaultSnapshot();

        assets <span class="hljs-operator">=</span> _convertToAssets(shares, <span class="hljs-literal">true</span>); <span class="hljs-comment">// No need to check for rounding error, previewMint rounds up.</span>

        <span class="hljs-comment">// Need to transfer before minting or ERC777s could reenter.</span>
        asset.safeTransferFrom(msgSender, <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), assets);

        _totalAssets <span class="hljs-operator">+</span><span class="hljs-operator">=</span> assets;

        _mint(receiver, shares);

        <span class="hljs-keyword">emit</span> Deposit(msgSender, receiver, assets, shares);

        requireVaultStatusCheck();
    }

    <span class="hljs-comment">/// @notice Withdraws a certain amount of assets for a receiver.</span>
    <span class="hljs-comment">/// @param assets The assets to withdraw.</span>
    <span class="hljs-comment">/// @param receiver The receiver of the withdrawal.</span>
    <span class="hljs-comment">/// @param owner The owner of the assets.</span>
    <span class="hljs-comment">/// @return shares The shares equivalent to the withdrawn assets.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">withdraw</span>(<span class="hljs-params">
        <span class="hljs-keyword">uint256</span> assets,
        <span class="hljs-keyword">address</span> receiver,
        <span class="hljs-keyword">address</span> owner
    </span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title">callThroughEVC</span> <span class="hljs-title">nonReentrant</span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span> shares</span>) </span>{
        <span class="hljs-keyword">address</span> msgSender <span class="hljs-operator">=</span> _msgSender();

        createVaultSnapshot();

        shares <span class="hljs-operator">=</span> _convertToShares(assets, <span class="hljs-literal">true</span>); <span class="hljs-comment">// No need to check for rounding error, previewWithdraw rounds up.</span>

        <span class="hljs-keyword">if</span> (msgSender <span class="hljs-operator">!</span><span class="hljs-operator">=</span> owner) {
            <span class="hljs-keyword">uint256</span> allowed <span class="hljs-operator">=</span> allowance[owner][msgSender]; <span class="hljs-comment">// Saves gas for limited approvals.</span>

            <span class="hljs-keyword">if</span> (allowed <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-keyword">type</span>(<span class="hljs-keyword">uint256</span>).<span class="hljs-built_in">max</span>) {
                allowance[owner][msgSender] <span class="hljs-operator">=</span> allowed <span class="hljs-operator">-</span> shares;
            }
        }

        _burn(owner, shares);

        <span class="hljs-keyword">emit</span> Withdraw(msgSender, receiver, owner, assets, shares);

        asset.safeTransfer(receiver, assets);

        _totalAssets <span class="hljs-operator">-</span><span class="hljs-operator">=</span> assets;

        requireAccountAndVaultStatusCheck(owner);
    }

    <span class="hljs-comment">/// @notice Redeems a certain amount of shares for a receiver.</span>
    <span class="hljs-comment">/// @param shares The shares to redeem.</span>
    <span class="hljs-comment">/// @param receiver The receiver of the redemption.</span>
    <span class="hljs-comment">/// @param owner The owner of the shares.</span>
    <span class="hljs-comment">/// @return assets The assets equivalent to the redeemed shares.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">redeem</span>(<span class="hljs-params">
        <span class="hljs-keyword">uint256</span> shares,
        <span class="hljs-keyword">address</span> receiver,
        <span class="hljs-keyword">address</span> owner
    </span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title">callThroughEVC</span> <span class="hljs-title">nonReentrant</span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span> assets</span>) </span>{
        <span class="hljs-keyword">address</span> msgSender <span class="hljs-operator">=</span> _msgSender();

        createVaultSnapshot();

        <span class="hljs-keyword">if</span> (msgSender <span class="hljs-operator">!</span><span class="hljs-operator">=</span> owner) {
            <span class="hljs-keyword">uint256</span> allowed <span class="hljs-operator">=</span> allowance[owner][msgSender]; <span class="hljs-comment">// Saves gas for limited approvals.</span>

            <span class="hljs-keyword">if</span> (allowed <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-keyword">type</span>(<span class="hljs-keyword">uint256</span>).<span class="hljs-built_in">max</span>) {
                allowance[owner][msgSender] <span class="hljs-operator">=</span> allowed <span class="hljs-operator">-</span> shares;
            }
        }

        <span class="hljs-comment">// Check for rounding error since we round down in previewRedeem.</span>
        <span class="hljs-built_in">require</span>((assets <span class="hljs-operator">=</span> _convertToAssets(shares, <span class="hljs-literal">false</span>)) <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span>, <span class="hljs-string">"ZERO_ASSETS"</span>);

        _burn(owner, shares);

        <span class="hljs-keyword">emit</span> Withdraw(msgSender, receiver, owner, assets, shares);

        asset.safeTransfer(receiver, assets);

        _totalAssets <span class="hljs-operator">-</span><span class="hljs-operator">=</span> assets;

        requireAccountAndVaultStatusCheck(owner);
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_convertToShares</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> assets, <span class="hljs-keyword">bool</span> roundUp</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> roundUp
            ? assets.mulDivUp(totalSupply <span class="hljs-operator">+</span> <span class="hljs-number">1</span>, _totalAssets <span class="hljs-operator">+</span> <span class="hljs-number">1</span>)
            : assets.mulDivDown(totalSupply <span class="hljs-operator">+</span> <span class="hljs-number">1</span>, _totalAssets <span class="hljs-operator">+</span> <span class="hljs-number">1</span>);
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_convertToAssets</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> shares, <span class="hljs-keyword">bool</span> roundUp</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> roundUp
            ? shares.mulDivUp(_totalAssets <span class="hljs-operator">+</span> <span class="hljs-number">1</span>, totalSupply <span class="hljs-operator">+</span> <span class="hljs-number">1</span>)
            : shares.mulDivDown(_totalAssets <span class="hljs-operator">+</span> <span class="hljs-number">1</span>, totalSupply <span class="hljs-operator">+</span> <span class="hljs-number">1</span>);
    }
}
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Implementing an UUPSUpgradeable Flash Loan Vault with Foundry]]></title><description><![CDATA[Making An Upgradeable FlashLoan Vault
Building off of our last tutorial, Creating a FlashLoan Vault we will now be able to learn how to develop and deploy Upgradeable contracts from foundry using the openzeppelin-upgrades library.
In our last version...]]></description><link>https://yielddev.io/implementing-a-uupsupgradeable-flash-loan-vault-with-foundry</link><guid isPermaLink="true">https://yielddev.io/implementing-a-uupsupgradeable-flash-loan-vault-with-foundry</guid><category><![CDATA[upgradeable]]></category><category><![CDATA[defi]]></category><category><![CDATA[Solidity]]></category><category><![CDATA[Ethereum]]></category><category><![CDATA[openzeppelin]]></category><category><![CDATA[foundry]]></category><category><![CDATA[eth]]></category><dc:creator><![CDATA[Yield Dev]]></dc:creator><pubDate>Wed, 21 Feb 2024 17:25:34 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1708536247142/b770a9f0-156f-4573-96ca-97770e19f642.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-making-an-upgradeable-flashloan-vault">Making An Upgradeable FlashLoan Vault</h2>
<p>Building off of our last tutorial, <a target="_blank" href="https://yielddev.io/implementing-a-flash-loan-vault-with-erc3156-and-erc4626">Creating a FlashLoan Vault</a> we will now be able to learn how to develop and deploy Upgradeable contracts from foundry using the openzeppelin-upgrades library.</p>
<p>In our last version we built a Flashloan vault with a fixed fee for loans of any size. However, what happens when we realize that this pricing model makes little sense and decide instead to charge a percentage of the loan size as the fee?</p>
<p>With our previous version we would be forced to deploy an entirely new lending protocol and instruct all of our users, both depositors and borrowers to target a new contract and migrate all of their funds. This is particularly problematic given that our borrowers have developed and deployed specially built IERC3156FlashBorrower contracts that are designed to target our lending facility. This puts a major overhead on our users and customers for such a trivial change.</p>
<p>The solution to this issue is to deploy our flash lending protocol as an upgradeable contract. This way we can make changes to the protocol without having to redeploy the entire contract and migrate all of our users. All previous users can continue to target the same address but they will be interacting with our new V2 logic.</p>
<p>In this post we will show how easily we can reimplement our initial FlashLoan vault design as an upgradeable contract and then make a change to the fee structure and, again easily, deploy the version 2 to our users without the need for a migration or downtime. If you would like to follow along please see the <a target="_blank" href="https://yielddev.io/implementing-a-flash-loan-vault-with-erc3156-and-erc4626">previous post</a> for the initial implementation of the FlashLoanVault.</p>
<h2 id="heading-installation">Installation</h2>
<p>The first thing we will need to do is install the necessary tools. One being the <code>openzeppelin-foundry-upgrades</code> library to facilitate our management of the upgradeable contracts via foundry. The second being the <code>openzeppelin-contracts-upgradeable</code> library which contains the upgradeable versions of the OpenZeppelin standard contracts we will be inheriting in our smart contract.</p>
<pre><code class="lang-bash">forge install OpenZeppelin/openzeppelin-foundry-upgrades --no-commit
forge install OpenZeppelin/openzeppelin-contracts-upgradeable@v5.0.1 --no-commit
</code></pre>
<p>Once we have these installed we can add the following to our remapping configurations in foundry.toml. We will also need to add the <code>extra_output</code> configuration to include the <code>storageLayout</code> output. These configs will help us manage the build artifacts necessary for our upgradeable deployment.</p>
<p><code>foundry.toml</code></p>
<pre><code class="lang-toml"><span class="hljs-attr">build_info</span> = <span class="hljs-literal">true</span>
<span class="hljs-attr">extra_output</span> = [<span class="hljs-string">"storageLayout"</span>]
<span class="hljs-attr">remappings</span> = [
    <span class="hljs-string">'@openzeppelin/contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/'</span>,
    <span class="hljs-string">'@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/'</span>,
    <span class="hljs-string">'@solady/contracts/=lib/solady/src/'</span>,
]
</code></pre>
<h2 id="heading-reimplementation">(Re)Implementation</h2>
<p>Now our reimplementation of the FlashLoan vault will only require some minor changes. Starting with the imports, we will need to import the upgradeable versions of the OpenZeppelin contracts we will be inheriting. For this example, we only need to import the ERC4626Upgradeable contract to replace our previous ERC4626 import. Then, we can add imports for the UUPSUpgradeable and Ownable2StepUpgradeable contracts which will allow us to manage the upgradeability of our contract as well as require special permissions from the owner to perform the upgrade.</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// FlashLoanVault.sol</span>
<span class="hljs-keyword">import</span> { <span class="hljs-title">UUPSUpgradeable</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">ERC4626Upgradeable</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC4626Upgradeable.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">Ownable2StepUpgradeable</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"</span>;
</code></pre>
<p>Once we have the upgrades imported, we can change the inheritance structure of our contract to match it.</p>
<pre><code class="lang-solidity"><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">FlashLoanVault</span> <span class="hljs-keyword">is</span> <span class="hljs-title">ERC4626Upgradeable</span>, <span class="hljs-title">IERC3156FlashLender</span>, <span class="hljs-title">UUPSUpgradeable</span>, <span class="hljs-title">Ownable2StepUpgradeable</span> </span>{}
</code></pre>
<p>Now, one thing in particular to note about upgradeable contracts is that they do not have a constructor function, instead we will use an initialize function to set the initial state of our contract as well as it's inherited contracts.</p>
<pre><code class="lang-solidity">    <span class="hljs-comment">// constructor(address _depositToken) ERC4626(IERC20(_depositToken)) ERC20("Shares Vault", "SV") {}</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">initialize</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> _depositToken
    </span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">initializer</span> </span>{
        __Ownable2Step_init();
        __Ownable_init(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>);
        __ERC4626_init(IERC20(_depositToken));
        __ERC20_init(<span class="hljs-string">"Shares Vault"</span>, <span class="hljs-string">"SV"</span>);
    }
</code></pre>
<p>After we delete the constructor, we can implement our intialize function to set the initial state of our base contracts. Note the initializer modifier which is used to ensure that the initialize function is only called once.</p>
<p>The final change we need to make to our implementation is adding an <code>_authorizeUpgrade</code> function which will be used to ensure that only the owner of the contract can perform the upgrade. In order that we may achieve this, we simply add the <code>onlyOwner</code> modifier to the function.</p>
<pre><code class="lang-solidity">    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_authorizeUpgrade</span>(<span class="hljs-params"><span class="hljs-keyword">address</span></span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title">onlyOwner</span> </span>{}
</code></pre>
<h2 id="heading-deploying-a-uupsupgradable-contract-in-foundry-tests">Deploying a UUPSUpgradable Contract in Foundry Tests</h2>
<p>Now that we have the upgradeable reimplementation of our FlashLoanVault complete we can move on to testing the upgradeability. The first thing we will need to change in our <code>FlashLoanVault.t.sol</code> test file is the deployment setup of our contract.</p>
<pre><code class="lang-solidity"><span class="hljs-keyword">import</span> {<span class="hljs-title">Upgrades</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"openzeppelin-foundry-upgrades/Upgrades.sol"</span>;
</code></pre>
<p>We import the openzeppelin-foundry-upgrades/Upgrades.sol library which will assist us in deploying our new contracts.</p>
<p>After that we modify our FlashLoan Vault deployment in the setup function to use the <code>deployUUPSProxy</code> helper. Once our proxy is deployed we will declare our <code>vault</code> variable used in testing to be an instance of <code>FlashLoanVault</code> <strong>at the proxy address</strong>.</p>
<pre><code class="lang-solidity">        proxy <span class="hljs-operator">=</span> Upgrades.deployUUPSProxy(<span class="hljs-string">"FlashLoanVault.sol"</span>,
            <span class="hljs-built_in">abi</span>.encodeCall(FlashLoanVault.initialize, (<span class="hljs-keyword">address</span>(usdc)))
        );
        vault <span class="hljs-operator">=</span> FlashLoanVault(proxy);
        <span class="hljs-comment">//vault = new FlashLoanVault(address(usdc));</span>
</code></pre>
<p>Now, we can run all of our previously written tests and see that they all pass and the expected logic is the same as before.</p>
<p>Note: When we build and run this new implementation we will have to add the <code>--ffi</code> flag to enable the openzeppelin foundry upgrades library to be used in our tests. </p>
<pre><code class="lang-bash">forge clean &amp;&amp; forge build &amp;&amp; forge <span class="hljs-built_in">test</span> -vv --ffi
</code></pre>
<h2 id="heading-making-an-upgrade">Making an Upgrade</h2>
<p>At this point, we have our upgradeable FlashLoanVault with exactly the same functional logic as before. Including the fixed fee of 0.1 USDC</p>
<p>Now, we can test changing the logic of the fee structure in a V2.</p>
<p>All we need to do to implement the upgrade is make a copy of our <code>FlashLoanVault.sol</code> file and name it <code>FlashLoanVaultV2.sol</code>. After this, we simply change the name of the contract to indicate it's the second version and add a reference tag to the previous implementation so that the openzeppelin upgrades library knows how to manage the upgrade. The reference is simply added as a comment above the contract declaration detailing the contract to be upgraded.</p>
<p><code>FlashLoanVaultV2.sol</code></p>
<pre><code class="lang-solidity"><span class="hljs-comment">/// @custom:oz-upgrades-from FlashLoanVault</span>
<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">FlashLoanVaultV2</span> <span class="hljs-keyword">is</span> <span class="hljs-title">ERC4626Upgradeable</span>, <span class="hljs-title">IERC3156FlashLender</span>, <span class="hljs-title">UUPSUpgradeable</span>, <span class="hljs-title">Ownable2StepUpgradeable</span> </span>{}
</code></pre>
<p>With the version 2 now setup, we can upgrade the logic of the <code>flashFee</code> function to change the fee structure of our flash loan protocol.</p>
<pre><code class="lang-solidity">    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">flashFee</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> token, <span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> (amount <span class="hljs-operator">*</span> <span class="hljs-number">100</span>) <span class="hljs-operator">/</span> <span class="hljs-number">10000</span>;
    }
</code></pre>
<p>Here, we have simply changed the formula to calculate the fee to be 100 basis points or 1% of the loan amount.</p>
<h2 id="heading-deploying-an-upgrade">Deploying an Upgrade</h2>
<p>Now that we have our V2 implementation complete, we can deploy it in our tests to see the procedure and test that the new functionality is working properly.</p>
<p>All we do is add a new test in our <code>FlashLoanVault.t.sol</code> file called <code>test_upgrade_fee()</code>.</p>
<p>Deploying the upgrade is actually rather simple utilizing the <code>Upgrades</code> library we simply call the <code>Upgrade.upgradeProxy</code> function and pass the proxy address and the new implementation file <code>"FlashLoanVaultV2.sol"</code> as arguments.</p>
<p><code>FlashLoanVault.t.sol</code></p>
<pre><code class="lang-solidity">    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test_upgrade_fee</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        Upgrades.upgradeProxy(
            proxy,
            <span class="hljs-string">"FlashLoanVaultV2.sol"</span>,
            <span class="hljs-string">""</span>
        );
        vm.startPrank(user1);
        usdc.<span class="hljs-built_in">transfer</span>(<span class="hljs-keyword">address</span>(borrower), <span class="hljs-number">0</span><span class="hljs-number">.01</span> <span class="hljs-literal">ether</span>);
        vault.flashLoan(borrower, <span class="hljs-keyword">address</span>(usdc), <span class="hljs-number">1</span> <span class="hljs-literal">ether</span>, <span class="hljs-string">""</span>);

        <span class="hljs-comment">// Check we paid the fee 1% of 1 ether = 0.01 ether</span>
        assertEq(usdc.balanceOf(<span class="hljs-keyword">address</span>(vault)), <span class="hljs-number">10.01</span> <span class="hljs-literal">ether</span>); 
        assertEq(usdc.balanceOf(<span class="hljs-keyword">address</span>(user1)), <span class="hljs-number">9.99</span> <span class="hljs-literal">ether</span>);
        assertEq(usdc.balanceOf(<span class="hljs-keyword">address</span>(borrower)), <span class="hljs-number">0</span>);

        usdc.<span class="hljs-built_in">transfer</span>(<span class="hljs-keyword">address</span>(borrower), <span class="hljs-number">0</span><span class="hljs-number">.1</span> <span class="hljs-literal">ether</span>);
        vault.flashLoan(borrower, <span class="hljs-keyword">address</span>(usdc), <span class="hljs-number">10</span> <span class="hljs-literal">ether</span>, <span class="hljs-string">""</span>);

        <span class="hljs-comment">// no we paid the fee 1% of 10 ether = 0.1 ether</span>
        assertEq(usdc.balanceOf(<span class="hljs-keyword">address</span>(vault)), <span class="hljs-number">10.11</span> <span class="hljs-literal">ether</span>); 
        assertEq(usdc.balanceOf(<span class="hljs-keyword">address</span>(user1)), <span class="hljs-number">9.89</span> <span class="hljs-literal">ether</span>);
        assertEq(usdc.balanceOf(<span class="hljs-keyword">address</span>(borrower)), <span class="hljs-number">0</span>);
    }
</code></pre>
<p>Once we have the upgrade deployed, we can execute our tests using <code>user1</code> to make two flashloans of 1 USDC and 10 USDC, each time checking that the fee was paid correctly and in the amount of 1% of the loan size.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this post we have seen how to reimplement our FlashLoanVault contract as a UUPSupgradeable contract to allow for future changes to the protocol logic without requiring action from our user or any downtime to the protocol.</p>
<p>We have also seen how to utilize openzeppelin's foundry-upgrades library to deploy and test the contracts as well as how to upgrade a live contract to a version 2 implementation.</p>
<h2 id="heading-full-code">Full Code</h2>
<p><code>FlashLoanVault.sol</code></p>
<pre><code class="lang-solidity"><span class="hljs-comment">// SPDX-License-Identifier: UNLICENSED</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> 0.8.20;</span>
<span class="hljs-keyword">import</span> { <span class="hljs-title">UUPSUpgradeable</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">ERC4626Upgradeable</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC4626Upgradeable.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">Ownable2StepUpgradeable</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">IERC3156FlashBorrower</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts/interfaces/IERC3156FlashBorrower.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">IERC3156FlashLender</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts/interfaces/IERC3156FlashLender.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">SafeTransferLib</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"solady/utils/SafeTransferLib.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">ERC20</span>, <span class="hljs-title">IERC20</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts/token/ERC20/ERC20.sol"</span>;
<span class="hljs-function"><span class="hljs-keyword">error</span> <span class="hljs-title">FlashLoanVault__WrongToken</span>(<span class="hljs-params"></span>)</span>;
<span class="hljs-function"><span class="hljs-keyword">error</span> <span class="hljs-title">FlashLoanVault__RepayFailed</span>(<span class="hljs-params"></span>)</span>;
<span class="hljs-function"><span class="hljs-keyword">error</span> <span class="hljs-title">FlashLoanVault__MaxLoanExceeded</span>(<span class="hljs-params"></span>)</span>;
<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">FlashLoanVault</span> <span class="hljs-keyword">is</span> <span class="hljs-title">ERC4626Upgradeable</span>, <span class="hljs-title">IERC3156FlashLender</span>, <span class="hljs-title">UUPSUpgradeable</span>, <span class="hljs-title">Ownable2StepUpgradeable</span> </span>{
    <span class="hljs-keyword">using</span> <span class="hljs-title">SafeTransferLib</span> <span class="hljs-title"><span class="hljs-keyword">for</span></span> <span class="hljs-title">IERC20</span>;
    <span class="hljs-comment">// constructor(address _depositToken) ERC4626(IERC20(_depositToken)) ERC20("Shares Vault", "SV") {</span>
    <span class="hljs-comment">// }</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">initialize</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> _depositToken
    </span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">initializer</span> </span>{
        __Ownable2Step_init();
        __Ownable_init(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>);
        __ERC4626_init(IERC20(_depositToken));
        __ERC20_init(<span class="hljs-string">"Shares Vault"</span>, <span class="hljs-string">"SV"</span>);
    }
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">maxFlashLoan</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> token</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">if</span> (token <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(asset())) <span class="hljs-keyword">revert</span> FlashLoanVault__WrongToken();
        <span class="hljs-keyword">return</span> IERC20(token).balanceOf(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>));
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">flashFee</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> token, <span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-number">0</span><span class="hljs-number">.1</span> <span class="hljs-literal">ether</span>;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">flashLoan</span>(<span class="hljs-params">
        IERC3156FlashBorrower receiver,
        <span class="hljs-keyword">address</span> token,
        <span class="hljs-keyword">uint256</span> amount,
        <span class="hljs-keyword">bytes</span> <span class="hljs-keyword">calldata</span> data
    </span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bool</span></span>)
    </span>{
        <span class="hljs-keyword">if</span> (amount <span class="hljs-operator">&gt;</span> maxFlashLoan(token)) <span class="hljs-keyword">revert</span> FlashLoanVault__MaxLoanExceeded();

        <span class="hljs-keyword">uint256</span> fee <span class="hljs-operator">=</span> flashFee(token, amount);
        <span class="hljs-keyword">uint256</span> totalDebt <span class="hljs-operator">=</span> amount <span class="hljs-operator">+</span> fee;
        SafeTransferLib.safeTransfer(<span class="hljs-keyword">address</span>(asset()), <span class="hljs-keyword">address</span>(receiver), amount);
        <span class="hljs-keyword">if</span> (receiver.onFlashLoan(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, token, amount, fee, data) <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-built_in">keccak256</span>(<span class="hljs-string">"IERC3156FlashBorrower.onFlashLoan"</span>)) <span class="hljs-keyword">revert</span> FlashLoanVault__RepayFailed();

        SafeTransferLib.safeTransferFrom(<span class="hljs-keyword">address</span>(asset()), <span class="hljs-keyword">address</span>(receiver), <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), totalDebt);
        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_authorizeUpgrade</span>(<span class="hljs-params"><span class="hljs-keyword">address</span></span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title">onlyOwner</span> </span>{}

}
</code></pre>
<p><code>FlashLoanVaultV2.sol</code></p>
<pre><code class="lang-solidity"><span class="hljs-comment">// SPDX-License-Identifier: UNLICENSED</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> 0.8.20;</span>
<span class="hljs-keyword">import</span> { <span class="hljs-title">UUPSUpgradeable</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">ERC4626Upgradeable</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC4626Upgradeable.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">Ownable2StepUpgradeable</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">IERC3156FlashBorrower</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts/interfaces/IERC3156FlashBorrower.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">IERC3156FlashLender</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts/interfaces/IERC3156FlashLender.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">SafeTransferLib</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"solady/utils/SafeTransferLib.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">ERC20</span>, <span class="hljs-title">IERC20</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts/token/ERC20/ERC20.sol"</span>;
<span class="hljs-function"><span class="hljs-keyword">error</span> <span class="hljs-title">FlashLoanVault__WrongToken</span>(<span class="hljs-params"></span>)</span>;
<span class="hljs-function"><span class="hljs-keyword">error</span> <span class="hljs-title">FlashLoanVault__RepayFailed</span>(<span class="hljs-params"></span>)</span>;
<span class="hljs-function"><span class="hljs-keyword">error</span> <span class="hljs-title">FlashLoanVault__MaxLoanExceeded</span>(<span class="hljs-params"></span>)</span>;
<span class="hljs-comment">/// @custom:oz-upgrades-from FlashLoanVault</span>
<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">FlashLoanVaultV2</span> <span class="hljs-keyword">is</span> <span class="hljs-title">ERC4626Upgradeable</span>, <span class="hljs-title">IERC3156FlashLender</span>, <span class="hljs-title">UUPSUpgradeable</span>, <span class="hljs-title">Ownable2StepUpgradeable</span> </span>{
    <span class="hljs-keyword">using</span> <span class="hljs-title">SafeTransferLib</span> <span class="hljs-title"><span class="hljs-keyword">for</span></span> <span class="hljs-title">IERC20</span>;
    <span class="hljs-comment">// constructor(address _depositToken) ERC4626(IERC20(_depositToken)) ERC20("Shares Vault", "SV") {</span>
    <span class="hljs-comment">// }</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">initialize</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> _depositToken
    </span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">initializer</span> </span>{
        __Ownable2Step_init();
        __Ownable_init(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>);
        __ERC4626_init(IERC20(_depositToken));
        __ERC20_init(<span class="hljs-string">"Shares Vault"</span>, <span class="hljs-string">"SV"</span>);
    }
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">maxFlashLoan</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> token</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">if</span> (token <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(asset())) <span class="hljs-keyword">revert</span> FlashLoanVault__WrongToken();
        <span class="hljs-keyword">return</span> IERC20(token).balanceOf(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>));
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">flashFee</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> token, <span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> (amount <span class="hljs-operator">*</span> <span class="hljs-number">100</span>) <span class="hljs-operator">/</span> <span class="hljs-number">10000</span>;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">flashLoan</span>(<span class="hljs-params">
        IERC3156FlashBorrower receiver,
        <span class="hljs-keyword">address</span> token,
        <span class="hljs-keyword">uint256</span> amount,
        <span class="hljs-keyword">bytes</span> <span class="hljs-keyword">calldata</span> data
    </span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bool</span></span>)
    </span>{
        <span class="hljs-keyword">if</span> (amount <span class="hljs-operator">&gt;</span> maxFlashLoan(token)) <span class="hljs-keyword">revert</span> FlashLoanVault__MaxLoanExceeded();

        <span class="hljs-keyword">uint256</span> fee <span class="hljs-operator">=</span> flashFee(token, amount);
        <span class="hljs-keyword">uint256</span> totalDebt <span class="hljs-operator">=</span> amount <span class="hljs-operator">+</span> fee;
        SafeTransferLib.safeTransfer(<span class="hljs-keyword">address</span>(asset()), <span class="hljs-keyword">address</span>(receiver), amount);
        <span class="hljs-keyword">if</span> (receiver.onFlashLoan(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, token, amount, fee, data) <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-built_in">keccak256</span>(<span class="hljs-string">"IERC3156FlashBorrower.onFlashLoan"</span>)) <span class="hljs-keyword">revert</span> FlashLoanVault__RepayFailed();

        SafeTransferLib.safeTransferFrom(<span class="hljs-keyword">address</span>(asset()), <span class="hljs-keyword">address</span>(receiver), <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), totalDebt);
        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_authorizeUpgrade</span>(<span class="hljs-params"><span class="hljs-keyword">address</span></span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> </span>{}

}
</code></pre>
<p><code>FlashLoanVault.t.sol</code></p>
<pre><code class="lang-solidity"><span class="hljs-comment">// SPDX-License-Identifier: UNLICENSED</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.20;</span>

<span class="hljs-keyword">import</span> {<span class="hljs-title">Test</span>, <span class="hljs-title">console2</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"forge-std/Test.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">FlashLoanVault</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../src/FlashLoanVault.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">FlashLoanVaultV2</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../src/FlashLoanVaultV2.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">ERC20</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts/token/ERC20/ERC20.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">IERC20</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts/token/ERC20/IERC20.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">IERC3156FlashBorrower</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts/interfaces/IERC3156FlashBorrower.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">Upgrades</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"openzeppelin-foundry-upgrades/Upgrades.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">FlashLoanVaultTest</span> <span class="hljs-keyword">is</span> <span class="hljs-title">Test</span> </span>{
    <span class="hljs-keyword">address</span> OwnerWallet;
    <span class="hljs-keyword">address</span> user1;
    <span class="hljs-keyword">address</span> user2;
    <span class="hljs-keyword">address</span> user3;
    <span class="hljs-keyword">address</span> proxy;
    FlashLoanVault vault;
    MockFlashLoanReceiver borrower;
    MockUSDC usdc;

    <span class="hljs-function"><span class="hljs-keyword">error</span> <span class="hljs-title">FlashLoanVault__WrongToken</span>(<span class="hljs-params"></span>)</span>;
    <span class="hljs-function"><span class="hljs-keyword">error</span> <span class="hljs-title">FlashLoanVault__RepayFailed</span>(<span class="hljs-params"></span>)</span>;
    <span class="hljs-function"><span class="hljs-keyword">error</span> <span class="hljs-title">FlashLoanVault__MaxLoanExceeded</span>(<span class="hljs-params"></span>)</span>;

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setUp</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        OwnerWallet <span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">69</span>);

        user1 <span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">420</span>);
        user2 <span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">666</span>);
        user3 <span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">777</span>);

        usdc <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> MockUSDC();
        usdc.mint(user1, <span class="hljs-number">10</span> <span class="hljs-literal">ether</span>);
        usdc.mint(user2, <span class="hljs-number">10</span> <span class="hljs-literal">ether</span>);
        usdc.mint(OwnerWallet, <span class="hljs-number">10</span> <span class="hljs-literal">ether</span>);
        proxy <span class="hljs-operator">=</span> Upgrades.deployUUPSProxy(<span class="hljs-string">"FlashLoanVault.sol"</span>,
            <span class="hljs-built_in">abi</span>.encodeCall(FlashLoanVault.initialize, (<span class="hljs-keyword">address</span>(usdc)))
        );
        vault <span class="hljs-operator">=</span> FlashLoanVault(proxy);
        <span class="hljs-comment">//vault = new FlashLoanVault(address(usdc));</span>

        vm.startPrank(OwnerWallet);
        usdc.approve(<span class="hljs-keyword">address</span>(vault), <span class="hljs-number">10</span> <span class="hljs-literal">ether</span>); 
        vault.deposit(<span class="hljs-number">7</span> <span class="hljs-literal">ether</span>, OwnerWallet);
        vm.stopPrank();

        vm.startPrank(user2);
        usdc.approve(<span class="hljs-keyword">address</span>(vault), <span class="hljs-number">10</span> <span class="hljs-literal">ether</span>);
        vault.deposit(<span class="hljs-number">3</span> <span class="hljs-literal">ether</span>, user2);
        vm.stopPrank();

        vm.startPrank(user1);
        borrower <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> MockFlashLoanReceiver(<span class="hljs-keyword">address</span>(vault));
        vm.stopPrank();
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test_flashLoan</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        vm.startPrank(user1);
        usdc.<span class="hljs-built_in">transfer</span>(<span class="hljs-keyword">address</span>(borrower), <span class="hljs-number">0</span><span class="hljs-number">.1</span> <span class="hljs-literal">ether</span>);
        vault.flashLoan(borrower, <span class="hljs-keyword">address</span>(usdc), <span class="hljs-number">10</span>, <span class="hljs-string">""</span>);
        assertEq(usdc.balanceOf(<span class="hljs-keyword">address</span>(vault)), <span class="hljs-number">10.1</span> <span class="hljs-literal">ether</span>);
        assertEq(usdc.balanceOf(<span class="hljs-keyword">address</span>(borrower)), <span class="hljs-number">0</span>);
    }
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test_ten_flashloans</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        vm.startPrank(user1);
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">uint</span> i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&lt;</span> <span class="hljs-number">10</span>; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
            usdc.<span class="hljs-built_in">transfer</span>(<span class="hljs-keyword">address</span>(borrower), <span class="hljs-number">0</span><span class="hljs-number">.1</span> <span class="hljs-literal">ether</span>);
            vault.flashLoan(borrower, <span class="hljs-keyword">address</span>(usdc), <span class="hljs-number">1</span>, <span class="hljs-string">""</span>);
            assertEq(usdc.balanceOf(<span class="hljs-keyword">address</span>(borrower)), <span class="hljs-number">0</span>);
        }
        vm.stopPrank();
        assertEq(usdc.balanceOf(<span class="hljs-keyword">address</span>(vault)), <span class="hljs-number">11</span> <span class="hljs-literal">ether</span>);
        assertApproxEqAbs(vault.previewRedeem(<span class="hljs-number">7</span> <span class="hljs-literal">ether</span>), <span class="hljs-number">7.7</span> <span class="hljs-literal">ether</span>, <span class="hljs-number">1</span>);
        assertApproxEqAbs(vault.previewRedeem(<span class="hljs-number">3</span> <span class="hljs-literal">ether</span>), <span class="hljs-number">3.3</span> <span class="hljs-literal">ether</span>, <span class="hljs-number">1</span>);
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test_revert_loan_repay_failed</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        vm.startPrank(user1);
        FlashLoanAttacker attacker <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> FlashLoanAttacker();
        usdc.<span class="hljs-built_in">transfer</span>(<span class="hljs-keyword">address</span>(attacker), <span class="hljs-number">0</span><span class="hljs-number">.1</span> <span class="hljs-literal">ether</span>);
        vm.expectRevert();
        vault.flashLoan(attacker, <span class="hljs-keyword">address</span>(usdc), <span class="hljs-number">10</span>, <span class="hljs-string">""</span>);
    }
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test_upgrade_fee</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        Upgrades.upgradeProxy(
            proxy,
            <span class="hljs-string">"FlashLoanVaultV2.sol"</span>,
            <span class="hljs-string">""</span>
        );
        vm.startPrank(user1);
        usdc.<span class="hljs-built_in">transfer</span>(<span class="hljs-keyword">address</span>(borrower), <span class="hljs-number">0</span><span class="hljs-number">.01</span> <span class="hljs-literal">ether</span>);
        vault.flashLoan(borrower, <span class="hljs-keyword">address</span>(usdc), <span class="hljs-number">1</span> <span class="hljs-literal">ether</span>, <span class="hljs-string">""</span>);

        <span class="hljs-comment">// Check we paid the fee 1% of 1 ether = 0.01 ether</span>
        assertEq(usdc.balanceOf(<span class="hljs-keyword">address</span>(vault)), <span class="hljs-number">10.01</span> <span class="hljs-literal">ether</span>); 
        assertEq(usdc.balanceOf(<span class="hljs-keyword">address</span>(user1)), <span class="hljs-number">9.99</span> <span class="hljs-literal">ether</span>);
        assertEq(usdc.balanceOf(<span class="hljs-keyword">address</span>(borrower)), <span class="hljs-number">0</span>);

        usdc.<span class="hljs-built_in">transfer</span>(<span class="hljs-keyword">address</span>(borrower), <span class="hljs-number">0</span><span class="hljs-number">.1</span> <span class="hljs-literal">ether</span>);
        vault.flashLoan(borrower, <span class="hljs-keyword">address</span>(usdc), <span class="hljs-number">10</span> <span class="hljs-literal">ether</span>, <span class="hljs-string">""</span>);

        <span class="hljs-comment">// no we paid the fee 1% of 10 ether = 0.1 ether</span>
        assertEq(usdc.balanceOf(<span class="hljs-keyword">address</span>(vault)), <span class="hljs-number">10.11</span> <span class="hljs-literal">ether</span>); 
        assertEq(usdc.balanceOf(<span class="hljs-keyword">address</span>(user1)), <span class="hljs-number">9.89</span> <span class="hljs-literal">ether</span>);
        assertEq(usdc.balanceOf(<span class="hljs-keyword">address</span>(borrower)), <span class="hljs-number">0</span>);
    }
}

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">MockFlashLoanReceiver</span> <span class="hljs-keyword">is</span> <span class="hljs-title">IERC3156FlashBorrower</span>, <span class="hljs-title">Test</span></span>{
    <span class="hljs-keyword">address</span> <span class="hljs-keyword">public</span> lender;

    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _lender</span>) </span>{
        lender <span class="hljs-operator">=</span> _lender;
    }
    <span class="hljs-comment">//</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">onFlashLoan</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> initiator, <span class="hljs-keyword">address</span> token, <span class="hljs-keyword">uint256</span> amount, <span class="hljs-keyword">uint256</span> fee, <span class="hljs-keyword">bytes</span> <span class="hljs-keyword">calldata</span> data</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bytes32</span></span>) </span>{
        <span class="hljs-comment">// check we have received the loan</span>
        assertEq(IERC20(token).balanceOf(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>)), amount<span class="hljs-operator">+</span>fee);
        <span class="hljs-comment">// Hypothetically do something with the loan</span>
        <span class="hljs-comment">// approve the repayment </span>
        IERC20(token).approve(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, amount <span class="hljs-operator">+</span> fee);
        <span class="hljs-keyword">return</span> <span class="hljs-built_in">keccak256</span>(<span class="hljs-string">"IERC3156FlashBorrower.onFlashLoan"</span>);
    }
}

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">FlashLoanAttacker</span> <span class="hljs-keyword">is</span> <span class="hljs-title">IERC3156FlashBorrower</span></span>{
    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>)</span>{}
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">onFlashLoan</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> initiator, <span class="hljs-keyword">address</span> token, <span class="hljs-keyword">uint256</span> amount, <span class="hljs-keyword">uint256</span> fee, <span class="hljs-keyword">bytes</span> <span class="hljs-keyword">calldata</span> data</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bytes32</span></span>) </span>{
        <span class="hljs-comment">// try to send the loan to ourselves</span>
        IERC20(token).<span class="hljs-built_in">transfer</span>(initiator, amount <span class="hljs-operator">+</span> fee); 
        <span class="hljs-keyword">return</span> <span class="hljs-built_in">keccak256</span>(<span class="hljs-string">"IERC3156FlashBorrower.onFlashLoan"</span>);
    }
}

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">MockUSDC</span> <span class="hljs-keyword">is</span> <span class="hljs-title">ERC20</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) <span class="hljs-title">ERC20</span>(<span class="hljs-params"><span class="hljs-string">"USDC"</span>, <span class="hljs-string">"USDC"</span></span>) </span>{
    }
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">mint</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> to, <span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        _mint(to, amount);
    }
}
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Implementing a Flash Loan Vault with ERC3156 and ERC4626]]></title><description><![CDATA[Flashloan Vault
If you've followed our previous article on implementing Vault with ERC4626, then you are already aware of how easy it is to implement a token vault with a profit sharing distribution mechanism.
Building on this knowledge we will imple...]]></description><link>https://yielddev.io/implementing-a-flash-loan-vault-with-erc3156-and-erc4626</link><guid isPermaLink="true">https://yielddev.io/implementing-a-flash-loan-vault-with-erc3156-and-erc4626</guid><category><![CDATA[ERC3156]]></category><category><![CDATA[flash loan]]></category><category><![CDATA[defi]]></category><category><![CDATA[Ethereum]]></category><category><![CDATA[Solidity]]></category><category><![CDATA[EVM]]></category><category><![CDATA[foundry]]></category><category><![CDATA[openzeppelin]]></category><category><![CDATA[Vault]]></category><dc:creator><![CDATA[Yield Dev]]></dc:creator><pubDate>Tue, 20 Feb 2024 19:52:35 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1708458727137/304e374b-f2d0-4487-b82e-e226809c2179.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-flashloan-vault">Flashloan Vault</h2>
<p>If you've followed our <a target="_blank" href="https://yielddev.io/implement-an-erc4626-token-vault">previous article</a> on implementing Vault with ERC4626, then you are already aware of how easy it is to implement a token vault with a profit sharing distribution mechanism.</p>
<p>Building on this knowledge we will implement a flashloan lending facility into our vault allowing our shareholding depositors to earn additional yield from the flashloan fees.</p>
<p>Implementing flashloan functionality is standardized by <a target="_blank" href="https://eips.ethereum.org/EIPS/eip-3156">ERC3156</a> which defines the interface for for both the flashloan lender and the flashloan borrower.</p>
<p>We will again use the OpenZeppelin library as scaffold to implement the flashloan functionality.</p>
<h2 id="heading-getting-started">Getting Started</h2>
<p><code>FlashLoanVault.sol</code></p>
<pre><code class="lang-solidity"><span class="hljs-keyword">import</span> { <span class="hljs-title">ERC4626</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">ERC20</span>, <span class="hljs-title">IERC20</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts/token/ERC20/ERC20.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">IERC3156FlashBorrower</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts/interfaces/IERC3156FlashBorrower.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">IERC3156FlashLender</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts/interfaces/IERC3156FlashLender.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">SafeTransferLib</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"solady/utils/SafeTransferLib.sol"</span>;
<span class="hljs-function"><span class="hljs-keyword">error</span> <span class="hljs-title">FlashLoanVault__WrongToken</span>(<span class="hljs-params"></span>)</span>;
<span class="hljs-function"><span class="hljs-keyword">error</span> <span class="hljs-title">FlashLoanVault__RepayFailed</span>(<span class="hljs-params"></span>)</span>;
<span class="hljs-function"><span class="hljs-keyword">error</span> <span class="hljs-title">FlashLoanVault__MaxLoanExceeded</span>(<span class="hljs-params"></span>)</span>;
</code></pre>
<p>The first thing we need to do is import the necessary libraries and interfaces. We'll need the ERC4626 contract in order to implement the tokenized vault shares. Additionally we need to import both IERC3156FlashBorrower and IERC3156FlashLender interfaces from the OpenZeppelin library.</p>
<p>In addition to inheriting, we are also defining some custom errors that we will be using later on.</p>
<p>We'll also be using the SafeTransferLib from solady in order to confidently handle token transfers. To install solady simply run:</p>
<pre><code class="lang-bash">forge install vectorized/solady
</code></pre>
<p>and in your foundry.toml add a remapping the import with:</p>
<pre><code class="lang-toml"><span class="hljs-attr">remappings</span> = [
   <span class="hljs-string">'@solady/contracts/=lib/solady/src/'</span>,
]
</code></pre>
<p>save your remappings with:</p>
<pre><code class="lang-bash">forge remappings &gt; remappings.txt
</code></pre>
<p>Once we have that, we can define our <code>FlashLoanVault</code> contract by inheriting the token vault and the flashlender interface in addition to declaring our usage of the SafeTransferLib.</p>
<pre><code class="lang-solidity"><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">FlashLoanVault</span> <span class="hljs-keyword">is</span> <span class="hljs-title">ERC4626</span>, <span class="hljs-title">IERC3156FlashLender</span> </span>{
    <span class="hljs-keyword">using</span> <span class="hljs-title">SafeTransferLib</span> <span class="hljs-title"><span class="hljs-keyword">for</span></span> <span class="hljs-title">IERC20</span>;
    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _depositToken</span>) <span class="hljs-title">ERC4626</span>(<span class="hljs-params">IERC20(<span class="hljs-params">_depositToken</span>)</span>) <span class="hljs-title">ERC20</span>(<span class="hljs-params"><span class="hljs-string">"Shares Vault"</span>, <span class="hljs-string">"SV"</span></span>) </span>{

    }
}
</code></pre>
<p>Now, if you've ready our previous article about implementing a vault with ERC4626, you'll notice that we wont be implementing the <code>shareProfits</code> function in this contract. Our flashlending protocol will be able to receive its fees directly and they will automatically and immediately be available to our shareholder.</p>
<p>In order to implement an ERC3156 compliant flash lender we need to implement three simple functions: <code>maxFlashLoan(address token)</code>, <code>flashFee(address token, uint256 amount)</code> and <code>flashLoan(IERC3156FlashBorrower receiver, address token, uint256 amount, bytes calldata data)</code>.</p>
<p>Firstly, we are only going to allow flashloans of the deposit token that is being pooled in our vault. So we are going to enforce this in the <code>maxFlashLoan</code> function by reverting if the token requested is not the deposit token. Otherwise, the max flashloan allowed will be the total balance of the deposit tokens in the vault.</p>
<pre><code class="lang-solidity">    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">maxFlashLoan</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> token</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">if</span> (token <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(asset())) <span class="hljs-keyword">revert</span> FlashLoanVault__WrongToken();
        <span class="hljs-keyword">return</span> IERC20(token).balanceOf(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>));
    }
</code></pre>
<p>Next, we need to implement the <code>flashFee</code> function which will return the fee that will be charged for our flashloan. Here, we will be using a flat rate (rather expensive) fee of 0.1 ether, regardless of the amount of the loan.</p>
<pre><code class="lang-solidity">    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">flashFee</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> token, <span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-number">0</span><span class="hljs-number">.1</span> <span class="hljs-literal">ether</span>;
    }
</code></pre>
<p>Finally we can implement the <code>flashLoan</code> function which will be called when a flashloan is initiated. The caller must pass a compliant IERC3156FlashBorrower as the loan receiver, note: only a smart contract can receive a flashloan since it must contain the appropriate logic to pay the loan back within the same transaction. Next, our borrower must specify the token and amount of the loan, and any additional data that the borrower may need to process the loan in the receiver contract, this data is simply passed along to the <code>receiver</code> contract after the loan is distributed.</p>
<pre><code class="lang-solidity">    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">flashLoan</span>(<span class="hljs-params">
        IERC3156FlashBorrower receiver,
        <span class="hljs-keyword">address</span> token,
        <span class="hljs-keyword">uint256</span> amount,
        <span class="hljs-keyword">bytes</span> <span class="hljs-keyword">calldata</span> data
    </span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bool</span></span>)
    </span>{
        <span class="hljs-keyword">if</span> (amount <span class="hljs-operator">&gt;</span> maxFlashLoan(token)) <span class="hljs-keyword">revert</span> FlashLoanVault__MaxLoanExceeded();

        <span class="hljs-keyword">uint256</span> fee <span class="hljs-operator">=</span> flashFee(token, amount);
        <span class="hljs-keyword">uint256</span> totalDebt <span class="hljs-operator">=</span> amount <span class="hljs-operator">+</span> fee;
        SafeTransferLib.safeTransfer(<span class="hljs-keyword">address</span>(asset()), <span class="hljs-keyword">address</span>(receiver), amount);
        <span class="hljs-keyword">if</span> (receiver.onFlashLoan(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, token, amount, fee, data) <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-built_in">keccak256</span>(<span class="hljs-string">"IERC3156FlashBorrower.onFlashLoan"</span>)) <span class="hljs-keyword">revert</span> FlashLoanVault__RepayFailed();

        SafeTransferLib.safeTransferFrom(<span class="hljs-keyword">address</span>(asset()), <span class="hljs-keyword">address</span>(receiver), <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), totalDebt);
        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
    }
</code></pre>
<p>Here we are simply checking that the flashloan amount requested is within the maximum allowed, then we calculate the fee and cache the amount we expect to be paid back, loan + fee, in the <code>totalDebt</code> variable.</p>
<p>After, that we can transfer the loan to the receiver. Once the receiver has the loan our lender contract will call the receivers <code>onFlashLoan()</code> function this is where the receiver can implement any logic desired utilizing the loan funds as long as it does two things: 1. Results in the funds being transferred back to the lender at the end of the transaction and 2. Returns the keccak256 hash of the <code>IERC3156FlashBorrower.onFlashLoan</code> function signature. If the receiver fails to do either of these things the transaction will revert and the loan will not be completed.</p>
<p>Once, we've called the <code>onFlashLoan</code> function we can call <code>safetTransferFrom</code> on the receiver for the amount of the loan + fee, and the flashloan is repaid.</p>
<p>Note: since we've implemented the repayment using safeTransferFrom any receiver contract wishing to borrow from our vault must execute an <code>approve</code> function granting the vault an allowance to transfer the total debt amount by the end of the transaction.</p>
<p>Secondly, we've used the <code>SafeTransferLib</code> here to handle the token transfers, the solady <code>SafeTransferLib</code> is an optimized gas efficient implementation that allows us to be sure the whole transaction will revert if the loan repayment fails.</p>
<h2 id="heading-testing">Testing</h2>
<p>Now that we have implemented our flashloan lender vault, we can write some tests to have a look at how it works and how to interact with it.</p>
<p>We'll start with the basic foundry test contract. We'll also import the <code>FlashLoanVault</code> contract and the <code>IERC3156FlashBorrower</code> interface as well as the ERC20 contracts. All of which we will need for testing. Then, we can setup some test users and declare the custom errors we defined in the <code>FlashLoanVault</code> contract, which we will also want to test.</p>
<p><code>FlashLoanVaultTest.sol</code></p>
<pre><code class="lang-solidity"><span class="hljs-comment">// SPDX-License-Identifier: UNLICENSED</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.20;</span>

<span class="hljs-keyword">import</span> {<span class="hljs-title">Test</span>, <span class="hljs-title">console2</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"forge-std/Test.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">FlashLoanVault</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../src/FlashLoanVault.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">ERC20</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts/token/ERC20/ERC20.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">IERC20</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts/token/ERC20/IERC20.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">IERC3156FlashBorrower</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts/interfaces/IERC3156FlashBorrower.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">FlashLoanVaultTest</span> <span class="hljs-keyword">is</span> <span class="hljs-title">Test</span> </span>{
    <span class="hljs-keyword">address</span> OwnerWallet;
    <span class="hljs-keyword">address</span> user1;
    <span class="hljs-keyword">address</span> user2;
    <span class="hljs-keyword">address</span> user3;
    FlashLoanVault vault;

    <span class="hljs-function"><span class="hljs-keyword">error</span> <span class="hljs-title">FlashLoanVault__WrongToken</span>(<span class="hljs-params"></span>)</span>;
    <span class="hljs-function"><span class="hljs-keyword">error</span> <span class="hljs-title">FlashLoanVault__RepayFailed</span>(<span class="hljs-params"></span>)</span>;
    <span class="hljs-function"><span class="hljs-keyword">error</span> <span class="hljs-title">FlashLoanVault__MaxLoanExceeded</span>(<span class="hljs-params"></span>)</span>;

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setUp</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        OwnerWallet <span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">69</span>);

        user1 <span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">420</span>);
        user2 <span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">666</span>);
        user3 <span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">777</span>);
    }

}
</code></pre>
<p>Once we have the basics, we notice that we will need some extra setup in order to test all our functionality. The first thing we'll need is a deposit token to use in our vault. So we will create a MockUSDC token similar to what we did in our <a target="_blank" href="https://yielddev.io/implement-an-erc4626-token-vault">ERC4626 vault article</a>. At the bottom of our test file we can just define a simple mock token contract.</p>
<p><code>FlashLoanVaultTest.t.sol</code></p>
<pre><code class="lang-solidity"><span class="hljs-comment">// FlashLoanVaultTest.t.sol</span>
<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">MockUSDC</span> <span class="hljs-keyword">is</span> <span class="hljs-title">ERC20</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) <span class="hljs-title">ERC20</span>(<span class="hljs-params"><span class="hljs-string">"USDC"</span>, <span class="hljs-string">"USDC"</span></span>) </span>{
    }
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">mint</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> to, <span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        _mint(to, amount);
    }
}
`
</code></pre>
<p>Additionally, we will need a Mock Borrower to test our flashloans. The ERC3156FlashBorrower interface only needs to implement the <code>onFlashLoan</code> function in order to be compliant. Our mock borrower will take the lender address as a constructor argument to ensure that it knows to where to repay the loan. Note: a this only for testing purposes, a real flash borrower would need to implement further checks to ensure security.</p>
<pre><code class="lang-solidity"><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">MockFlashLoanReceiver</span> <span class="hljs-keyword">is</span> <span class="hljs-title">IERC3156FlashBorrower</span>, <span class="hljs-title">Test</span></span>{
    <span class="hljs-keyword">address</span> <span class="hljs-keyword">public</span> lender;

    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _lender</span>) </span>{
        lender <span class="hljs-operator">=</span> _lender;
    }
    <span class="hljs-comment">//</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">onFlashLoan</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> initiator, <span class="hljs-keyword">address</span> token, <span class="hljs-keyword">uint256</span> amount, <span class="hljs-keyword">uint256</span> fee, <span class="hljs-keyword">bytes</span> <span class="hljs-keyword">calldata</span> data</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bytes32</span></span>) </span>{
        <span class="hljs-comment">// check we have received the loan</span>
        assertEq(IERC20(token).balanceOf(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>)), amount<span class="hljs-operator">+</span>fee);
        <span class="hljs-comment">// Hypothetically do something with the loan here</span>
        <span class="hljs-comment">// approve the repayment </span>
        IERC20(token).approve(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, amount <span class="hljs-operator">+</span> fee);
        <span class="hljs-comment">// return this signature so the lender knows we are a compliant borrower</span>
        <span class="hljs-keyword">return</span> <span class="hljs-built_in">keccak256</span>(<span class="hljs-string">"IERC3156FlashBorrower.onFlashLoan"</span>);
    }
}
</code></pre>
<p>Normally, this is where the borrower will implement its own custom logic to make use of the loan funds. In our case we will simply want to test that we have received the appropriate funds and have enough to back the loan with an assertion. For this reason, we have inherited the <code>Test</code> base contract from from foundry in order to use the <code>assertEq</code> function. After that we approved the repayment of funds and returned the appropriate selector hash to signal to the lender that we are a compliant borrower.</p>
<p>Now that we have the appropriate mocks, we can return to our test setup function and create our flash loan protocol.</p>
<p><code>FlashLoanVaultTest.t.sol</code></p>
<pre><code class="lang-solidity"><span class="hljs-comment">// FlashLoanVaultTest.t.sol</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setUp</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        OwnerWallet <span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">69</span>);

        user1 <span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">420</span>);
        user2 <span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">666</span>);
        user3 <span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">777</span>);

        usdc <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> MockUSDC();
        usdc.mint(user1, <span class="hljs-number">10</span> <span class="hljs-literal">ether</span>);
        usdc.mint(user2, <span class="hljs-number">10</span> <span class="hljs-literal">ether</span>);
        usdc.mint(OwnerWallet, <span class="hljs-number">10</span> <span class="hljs-literal">ether</span>);
        vault <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> FlashLoanVault(<span class="hljs-keyword">address</span>(usdc));

        vm.startPrank(OwnerWallet);
        usdc.approve(<span class="hljs-keyword">address</span>(vault), <span class="hljs-number">10</span> <span class="hljs-literal">ether</span>); 
        vault.deposit(<span class="hljs-number">7</span> <span class="hljs-literal">ether</span>, OwnerWallet);
        vm.stopPrank();

        vm.startPrank(user2);
        usdc.approve(<span class="hljs-keyword">address</span>(vault), <span class="hljs-number">10</span> <span class="hljs-literal">ether</span>);
        vault.deposit(<span class="hljs-number">3</span> <span class="hljs-literal">ether</span>, user2);
        vm.stopPrank();

        vm.startPrank(user1);
        borrower <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> MockFlashLoanReceiver(<span class="hljs-keyword">address</span>(vault));
        vm.stopPrank();
    }
</code></pre>
<p>All we've done here is setup our user wallets with some USDC and then created a vault and deposited some funds into it. We now have a vault with 10 USDC and two users with shares equivalent to 1 USDC each. We have also deployed a borrower contract targeting our vault as the lender.</p>
<p>We are ready to test the flashloan with the <code>test_flashloan()</code> function.</p>
<pre><code class="lang-solidity">    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test_flashLoan</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        vm.startPrank(user1);
        usdc.<span class="hljs-built_in">transfer</span>(<span class="hljs-keyword">address</span>(borrower), <span class="hljs-number">0</span><span class="hljs-number">.1</span> <span class="hljs-literal">ether</span>);
        vault.flashLoan(borrower, <span class="hljs-keyword">address</span>(usdc), <span class="hljs-number">10</span>, <span class="hljs-string">""</span>);
        assertEq(usdc.balanceOf(<span class="hljs-keyword">address</span>(vault)), <span class="hljs-number">10.1</span> <span class="hljs-literal">ether</span>);
        assertEq(usdc.balanceOf(<span class="hljs-keyword">address</span>(borrower)), <span class="hljs-number">0</span>);
    }
</code></pre>
<p>In this test, we've had our test user1 transfer 0.1 USDC to the borrower contract to use as the loan fee. Then we executed a flashloan on the fault. Our borrower contract will test that it has received the loan by calling <code>assertEq</code> mid transaction in the <code>onFlashLoan</code> function. After the flashloan call completes, our test checks that the vault balance has increased by 0.1 USDC and maintained its initial balance of 10 USDC as well as checking that the borrower contract is now empty. All of which are indicative of a successful flashloan.</p>
<h2 id="heading-testing-the-shareholders-profits">Testing the Shareholder's Profits</h2>
<p>Now that we have a basic flashloan working, we can run a function that executes several flashloans thus accumulating profits to the vault and check that the shareholder profits are being distributed correctly.</p>
<pre><code class="lang-solidity">    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test_ten_flashloans</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        vm.startPrank(user1);
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">uint</span> i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&lt;</span> <span class="hljs-number">10</span>; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
            usdc.<span class="hljs-built_in">transfer</span>(<span class="hljs-keyword">address</span>(borrower), <span class="hljs-number">0</span><span class="hljs-number">.1</span> <span class="hljs-literal">ether</span>);
            vault.flashLoan(borrower, <span class="hljs-keyword">address</span>(usdc), <span class="hljs-number">1</span>, <span class="hljs-string">""</span>);
            assertEq(usdc.balanceOf(<span class="hljs-keyword">address</span>(borrower)), <span class="hljs-number">0</span>);
        }
        vm.stopPrank();
        assertEq(usdc.balanceOf(<span class="hljs-keyword">address</span>(vault)), <span class="hljs-number">11</span> <span class="hljs-literal">ether</span>);
        assertApproxEqAbs(vault.previewRedeem(<span class="hljs-number">7</span> <span class="hljs-literal">ether</span>), <span class="hljs-number">7.7</span> <span class="hljs-literal">ether</span>, <span class="hljs-number">1</span>);
        assertApproxEqAbs(vault.previewRedeem(<span class="hljs-number">3</span> <span class="hljs-literal">ether</span>), <span class="hljs-number">3.3</span> <span class="hljs-literal">ether</span>, <span class="hljs-number">1</span>);
    }
</code></pre>
<p>In this test, we've use a simple loop to do 10 flashloans which should result in 1 USDC of profit to the vault. We then check that the vault has accumulated 11 USDC and that the shareholder profits are being distributed correctly according to their holding. Since, the vault has profited 10% on its 10 USDC of liquidity, the shareholders should be able to redeem 10% more than their initial deposit for each share.</p>
<p>Note here that we are using <code>assertApproxEqAbs</code> to check the shareholder redeem value are within 1 unit of what we expect. This is due to the rounding precision effects of integer division which was discussed in the <a target="_blank" href="https://yielddev.io/implement-an-erc4626-token-vault">shareholder vault article</a>.</p>
<h2 id="heading-testing-errors">Testing Errors</h2>
<p>Finally, we can test that our custom errors are being thrown when they should be. Namely, when a flashloan is requested for a token that is not the vault deposit token and when a flashloan is requested that exceeds the amount of liquidity available in the vault.</p>
<pre><code class="lang-solidity">    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test_flashloan_revert_wrong_token</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        vm.startPrank(user1);
        vm.expectRevert(<span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodeWithSelector</span>(FlashLoanVault__WrongToken.<span class="hljs-built_in">selector</span>));
        vault.flashLoan(borrower, <span class="hljs-keyword">address</span>(vault), <span class="hljs-number">10</span>, <span class="hljs-string">""</span>);
    }
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test_revertMaxLoanExceeded</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        vm.startPrank(user1);
        vm.expectRevert(<span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodeWithSelector</span>(FlashLoanVault__MaxLoanExceeded.<span class="hljs-built_in">selector</span>));
        vault.flashLoan(borrower, <span class="hljs-keyword">address</span>(usdc), <span class="hljs-number">100</span> <span class="hljs-literal">ether</span>, <span class="hljs-string">""</span>);
    }
</code></pre>
<p>To implement these checks, we utilize <code>vm.expectRevert</code> and compare the returned error data to the selector of the custom error we declared earlier.</p>
<h2 id="heading-testing-an-attack">Testing an Attack</h2>
<p>Finally, we can test that our flashloan protocol is secure by attempting to attack it. We can do this by creating a malicious borrower contract that does not repay the loan. We will first need to define an attacker contract that attempts to take the loan and transfer it directly to the attacker. at the bottom of our test file we can define the attacker contract.</p>
<pre><code class="lang-solidity"><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">FlashLoanAttacker</span> <span class="hljs-keyword">is</span> <span class="hljs-title">IERC3156FlashBorrower</span></span>{
    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>)</span>{}
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">onFlashLoan</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> initiator, <span class="hljs-keyword">address</span> token, <span class="hljs-keyword">uint256</span> amount, <span class="hljs-keyword">uint256</span> fee, <span class="hljs-keyword">bytes</span> <span class="hljs-keyword">calldata</span> data</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bytes32</span></span>) </span>{
        <span class="hljs-comment">// try to send the loan to ourselves</span>
        IERC20(token).<span class="hljs-built_in">transfer</span>(initiator, amount <span class="hljs-operator">+</span> fee); 
        <span class="hljs-keyword">return</span> <span class="hljs-built_in">keccak256</span>(<span class="hljs-string">"IERC3156FlashBorrower.onFlashLoan"</span>);
    }
}
</code></pre>
<p>Finally, in our test we can have the attacker call for a flashloan and expect it to revert.</p>
<pre><code class="lang-solidity">    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test_revert_loan_repay_failed</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        vm.startPrank(user1);
        FlashLoanAttacker attacker <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> FlashLoanAttacker();
        usdc.<span class="hljs-built_in">transfer</span>(<span class="hljs-keyword">address</span>(attacker), <span class="hljs-number">0</span><span class="hljs-number">.1</span> <span class="hljs-literal">ether</span>);
        vm.expectRevert();
        vault.flashLoan(attacker, <span class="hljs-keyword">address</span>(usdc), <span class="hljs-number">10</span>, <span class="hljs-string">""</span>);
    }
</code></pre>
<p>In this test, we've used a general <code>vm.expectRevert()</code> since we are expecting the SafeTransferLib to revert when it fails to <code>safeTrasferFrom</code> the attacker contract in the appropriate amount to pay back the loan and fee.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Here we have seen the relative ease with which a flashloan protocol can be implemented utilizing the ERC3156 interface to provide standardized flashloan functionality and the ERC4626 interface to provide a simple profit distributing vault mechanism for depositors to provide pooled capital and receive a yield.</p>
<h2 id="heading-full-code">Full Code</h2>
<p><code>FlashLoanVault.sol</code></p>
<pre><code class="lang-solidity"><span class="hljs-keyword">import</span> { <span class="hljs-title">ERC4626</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">IERC3156FlashBorrower</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts/interfaces/IERC3156FlashBorrower.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">IERC3156FlashLender</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts/interfaces/IERC3156FlashLender.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">SafeTransferLib</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"solady/utils/SafeTransferLib.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">ERC20</span>, <span class="hljs-title">IERC20</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts/token/ERC20/ERC20.sol"</span>;
<span class="hljs-function"><span class="hljs-keyword">error</span> <span class="hljs-title">FlashLoanVault__WrongToken</span>(<span class="hljs-params"></span>)</span>;
<span class="hljs-function"><span class="hljs-keyword">error</span> <span class="hljs-title">FlashLoanVault__RepayFailed</span>(<span class="hljs-params"></span>)</span>;
<span class="hljs-function"><span class="hljs-keyword">error</span> <span class="hljs-title">FlashLoanVault__MaxLoanExceeded</span>(<span class="hljs-params"></span>)</span>;
<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">FlashLoanVault</span> <span class="hljs-keyword">is</span> <span class="hljs-title">ERC4626</span>, <span class="hljs-title">IERC3156FlashLender</span> </span>{
    <span class="hljs-keyword">using</span> <span class="hljs-title">SafeTransferLib</span> <span class="hljs-title"><span class="hljs-keyword">for</span></span> <span class="hljs-title">IERC20</span>;
    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _depositToken</span>) <span class="hljs-title">ERC4626</span>(<span class="hljs-params">IERC20(<span class="hljs-params">_depositToken</span>)</span>) <span class="hljs-title">ERC20</span>(<span class="hljs-params"><span class="hljs-string">"Shares Vault"</span>, <span class="hljs-string">"SV"</span></span>) </span>{

    }
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">maxFlashLoan</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> token</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">if</span> (token <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(asset())) <span class="hljs-keyword">revert</span> FlashLoanVault__WrongToken();
        <span class="hljs-keyword">return</span> IERC20(token).balanceOf(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>));
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">flashFee</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> token, <span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-number">0</span><span class="hljs-number">.1</span> <span class="hljs-literal">ether</span>;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">flashLoan</span>(<span class="hljs-params">
        IERC3156FlashBorrower receiver,
        <span class="hljs-keyword">address</span> token,
        <span class="hljs-keyword">uint256</span> amount,
        <span class="hljs-keyword">bytes</span> <span class="hljs-keyword">calldata</span> data
    </span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bool</span></span>)
    </span>{
        <span class="hljs-keyword">if</span> (amount <span class="hljs-operator">&gt;</span> maxFlashLoan(token)) <span class="hljs-keyword">revert</span> FlashLoanVault__MaxLoanExceeded();

        <span class="hljs-keyword">uint256</span> fee <span class="hljs-operator">=</span> flashFee(token, amount);
        <span class="hljs-keyword">uint256</span> totalDebt <span class="hljs-operator">=</span> amount <span class="hljs-operator">+</span> fee;
        SafeTransferLib.safeTransfer(<span class="hljs-keyword">address</span>(asset()), <span class="hljs-keyword">address</span>(receiver), amount);
        <span class="hljs-keyword">if</span> (receiver.onFlashLoan(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, token, amount, fee, data) <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-built_in">keccak256</span>(<span class="hljs-string">"IERC3156FlashBorrower.onFlashLoan"</span>)) <span class="hljs-keyword">revert</span> FlashLoanVault__RepayFailed();

        SafeTransferLib.safeTransferFrom(<span class="hljs-keyword">address</span>(asset()), <span class="hljs-keyword">address</span>(receiver), <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), totalDebt);
        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
    }


}
</code></pre>
<p><code>FlashLoanVaultTest.t.sol</code></p>
<pre><code class="lang-solidity"><span class="hljs-comment">// SPDX-License-Identifier: UNLICENSED</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.20;</span>

<span class="hljs-keyword">import</span> {<span class="hljs-title">Test</span>, <span class="hljs-title">console2</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"forge-std/Test.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">FlashLoanVault</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../src/FlashLoanVault.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">ERC20</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts/token/ERC20/ERC20.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">IERC20</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts/token/ERC20/IERC20.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">IERC3156FlashBorrower</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts/interfaces/IERC3156FlashBorrower.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">FlashLoanVaultTest</span> <span class="hljs-keyword">is</span> <span class="hljs-title">Test</span> </span>{
    <span class="hljs-keyword">address</span> OwnerWallet;
    <span class="hljs-keyword">address</span> user1;
    <span class="hljs-keyword">address</span> user2;
    <span class="hljs-keyword">address</span> user3;
    FlashLoanVault vault;
    MockFlashLoanReceiver borrower;
    MockUSDC usdc;

    <span class="hljs-function"><span class="hljs-keyword">error</span> <span class="hljs-title">FlashLoanVault__WrongToken</span>(<span class="hljs-params"></span>)</span>;
    <span class="hljs-function"><span class="hljs-keyword">error</span> <span class="hljs-title">FlashLoanVault__RepayFailed</span>(<span class="hljs-params"></span>)</span>;
    <span class="hljs-function"><span class="hljs-keyword">error</span> <span class="hljs-title">FlashLoanVault__MaxLoanExceeded</span>(<span class="hljs-params"></span>)</span>;

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setUp</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        OwnerWallet <span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">69</span>);

        user1 <span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">420</span>);
        user2 <span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">666</span>);
        user3 <span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">777</span>);

        usdc <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> MockUSDC();
        usdc.mint(user1, <span class="hljs-number">10</span> <span class="hljs-literal">ether</span>);
        usdc.mint(user2, <span class="hljs-number">10</span> <span class="hljs-literal">ether</span>);
        usdc.mint(OwnerWallet, <span class="hljs-number">10</span> <span class="hljs-literal">ether</span>);
        vault <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> FlashLoanVault(<span class="hljs-keyword">address</span>(usdc));

        vm.startPrank(OwnerWallet);
        usdc.approve(<span class="hljs-keyword">address</span>(vault), <span class="hljs-number">10</span> <span class="hljs-literal">ether</span>); 
        vault.deposit(<span class="hljs-number">7</span> <span class="hljs-literal">ether</span>, OwnerWallet);
        vm.stopPrank();

        vm.startPrank(user2);
        usdc.approve(<span class="hljs-keyword">address</span>(vault), <span class="hljs-number">10</span> <span class="hljs-literal">ether</span>);
        vault.deposit(<span class="hljs-number">3</span> <span class="hljs-literal">ether</span>, user2);
        vm.stopPrank();

        vm.startPrank(user1);
        borrower <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> MockFlashLoanReceiver(<span class="hljs-keyword">address</span>(vault));
        vm.stopPrank();
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test_flashLoan</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        vm.startPrank(user1);
        usdc.<span class="hljs-built_in">transfer</span>(<span class="hljs-keyword">address</span>(borrower), <span class="hljs-number">0</span><span class="hljs-number">.1</span> <span class="hljs-literal">ether</span>);
        vault.flashLoan(borrower, <span class="hljs-keyword">address</span>(usdc), <span class="hljs-number">10</span>, <span class="hljs-string">""</span>);
        assertEq(usdc.balanceOf(<span class="hljs-keyword">address</span>(vault)), <span class="hljs-number">10.1</span> <span class="hljs-literal">ether</span>);
        assertEq(usdc.balanceOf(<span class="hljs-keyword">address</span>(borrower)), <span class="hljs-number">0</span>);
    }
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test_ten_flashloans</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        vm.startPrank(user1);
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">uint</span> i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&lt;</span> <span class="hljs-number">10</span>; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
            usdc.<span class="hljs-built_in">transfer</span>(<span class="hljs-keyword">address</span>(borrower), <span class="hljs-number">0</span><span class="hljs-number">.1</span> <span class="hljs-literal">ether</span>);
            vault.flashLoan(borrower, <span class="hljs-keyword">address</span>(usdc), <span class="hljs-number">1</span>, <span class="hljs-string">""</span>);
            assertEq(usdc.balanceOf(<span class="hljs-keyword">address</span>(borrower)), <span class="hljs-number">0</span>);
        }
        vm.stopPrank();
        assertEq(usdc.balanceOf(<span class="hljs-keyword">address</span>(vault)), <span class="hljs-number">11</span> <span class="hljs-literal">ether</span>);
        assertApproxEqAbs(vault.previewRedeem(<span class="hljs-number">7</span> <span class="hljs-literal">ether</span>), <span class="hljs-number">7.7</span> <span class="hljs-literal">ether</span>, <span class="hljs-number">1</span>);
        assertApproxEqAbs(vault.previewRedeem(<span class="hljs-number">3</span> <span class="hljs-literal">ether</span>), <span class="hljs-number">3.3</span> <span class="hljs-literal">ether</span>, <span class="hljs-number">1</span>);
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test_revert_loan_repay_failed</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        vm.startPrank(user1);
        FlashLoanAttacker attacker <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> FlashLoanAttacker();
        usdc.<span class="hljs-built_in">transfer</span>(<span class="hljs-keyword">address</span>(attacker), <span class="hljs-number">0</span><span class="hljs-number">.1</span> <span class="hljs-literal">ether</span>);
        vm.expectRevert();
        vault.flashLoan(attacker, <span class="hljs-keyword">address</span>(usdc), <span class="hljs-number">10</span>, <span class="hljs-string">""</span>);
    }
}

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">MockFlashLoanReceiver</span> <span class="hljs-keyword">is</span> <span class="hljs-title">IERC3156FlashBorrower</span>, <span class="hljs-title">Test</span></span>{
    <span class="hljs-keyword">address</span> <span class="hljs-keyword">public</span> lender;

    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _lender</span>) </span>{
        lender <span class="hljs-operator">=</span> _lender;
    }
    <span class="hljs-comment">//</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">onFlashLoan</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> initiator, <span class="hljs-keyword">address</span> token, <span class="hljs-keyword">uint256</span> amount, <span class="hljs-keyword">uint256</span> fee, <span class="hljs-keyword">bytes</span> <span class="hljs-keyword">calldata</span> data</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bytes32</span></span>) </span>{
        <span class="hljs-comment">// check we have received the loan</span>
        assertEq(IERC20(token).balanceOf(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>)), amount<span class="hljs-operator">+</span>fee);
        <span class="hljs-comment">// Hypothetically do something with the loan</span>
        <span class="hljs-comment">// approve the repayment </span>
        IERC20(token).approve(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, amount <span class="hljs-operator">+</span> fee);
        <span class="hljs-keyword">return</span> <span class="hljs-built_in">keccak256</span>(<span class="hljs-string">"IERC3156FlashBorrower.onFlashLoan"</span>);
    }
}

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">FlashLoanAttacker</span> <span class="hljs-keyword">is</span> <span class="hljs-title">IERC3156FlashBorrower</span></span>{
    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>)</span>{}
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">onFlashLoan</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> initiator, <span class="hljs-keyword">address</span> token, <span class="hljs-keyword">uint256</span> amount, <span class="hljs-keyword">uint256</span> fee, <span class="hljs-keyword">bytes</span> <span class="hljs-keyword">calldata</span> data</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bytes32</span></span>) </span>{
        <span class="hljs-comment">// try to send the loan to ourselves</span>
        IERC20(token).<span class="hljs-built_in">transfer</span>(initiator, amount <span class="hljs-operator">+</span> fee); 
        <span class="hljs-keyword">return</span> <span class="hljs-built_in">keccak256</span>(<span class="hljs-string">"IERC3156FlashBorrower.onFlashLoan"</span>);
    }
}

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">MockUSDC</span> <span class="hljs-keyword">is</span> <span class="hljs-title">ERC20</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) <span class="hljs-title">ERC20</span>(<span class="hljs-params"><span class="hljs-string">"USDC"</span>, <span class="hljs-string">"USDC"</span></span>) </span>{
    }
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">mint</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> to, <span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        _mint(to, amount);
    }
}
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Implement an ERC4626 Token Vault]]></title><description><![CDATA[Setting Up a Profit Sharing Vault
A common pattern in Defi is to create a vault that holds assets and distributes profits to the vault's token holders. Users will pool their assets in the vault and receive a corresponding token representing their sha...]]></description><link>https://yielddev.io/implement-an-erc4626-token-vault</link><guid isPermaLink="true">https://yielddev.io/implement-an-erc4626-token-vault</guid><category><![CDATA[Solidity]]></category><category><![CDATA[defi]]></category><category><![CDATA[erc4626]]></category><category><![CDATA[Tokenomics]]></category><category><![CDATA[openzeppelin]]></category><category><![CDATA[foundry]]></category><dc:creator><![CDATA[Yield Dev]]></dc:creator><pubDate>Sun, 18 Feb 2024 20:13:42 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1708287379849/59c57270-9503-494b-b8a7-139f8b87394d.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-setting-up-a-profit-sharing-vault">Setting Up a Profit Sharing Vault</h2>
<p>A common pattern in Defi is to create a vault that holds assets and distributes profits to the vault's token holders. Users will pool their assets in the vault and receive a corresponding token representing their share of the vault's assets. When the assets in the vault grow from share creation, each share's corresponding value remains the same. When the assets in the vault grow from profit accumulation, each share's corresponding value grows proportionally.</p>
<p>Here we will demonstrate the basic implementation of such a vault, utilizing the ERC4626 standard.</p>
<p>ERC4626 is tokenized vault standard which itself extends an ERC20 token. This means that the vault contract includes an ERC20 token to be used as the vault's shares. Further, the vault will have the deployer define an asset token to be used as a deposit asset exchanged for shares in the vault.</p>
<p>Utilizing the openzeppelin library we can implement a simple version of this contract incredibly easily. Everything we need to get started with the vault comes fully functional, completely out of the box.</p>
<p>once we implement a simple vault, we'll explore some of its functionality through testing.</p>
<h2 id="heading-setting-up-the-vault-contract">Setting Up the Vault Contract</h2>
<p>First, we can start with an empty foundry project, install the openzeppelin library and implement our basic vault through inheritance.</p>
<p><code>src/SharesVault.sol</code></p>
<pre><code class="lang-solidity"><span class="hljs-keyword">import</span> { <span class="hljs-title">ERC4626</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">ERC20</span>, <span class="hljs-title">IERC20</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts/token/ERC20/ERC20.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">SharesVault</span> <span class="hljs-keyword">is</span> <span class="hljs-title">ERC4626</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _depositToken</span>) <span class="hljs-title">ERC4626</span>(<span class="hljs-params">IERC20(<span class="hljs-params">_depositToken</span>)</span>) <span class="hljs-title">ERC20</span>(<span class="hljs-params"><span class="hljs-string">"Shares Vault"</span>, <span class="hljs-string">"SV"</span></span>) </span>{

    }
}
</code></pre>
<p>That's it! That's all we need to get started, simply import the ERC4626 and ERC20 contracts from the openzeppelin library and inherit them in our contract. Then in the constructor we pass the address of our asset token which will get passed along to the ERC4626 constructor as an ERC20 token. We now have a fully functional and ERC4626 compliant shares vault. Later on in this post we will see how to add some functionality to this implementation specific to our business logic.</p>
<h2 id="heading-setting-up-the-tests">Setting Up the Tests</h2>
<p>Now we can set up some tests to see how it works. The standard foundry test setup should do fine for now. but we will also need to generate a Mock token to act as our asset token. So, import ERC20 at the top and at the bottom of the test file we will implement a simple token.</p>
<p><code>test/SharesVault.t.sol</code></p>
<pre><code class="lang-solidity"><span class="hljs-comment">// SPDX-License-Identifier: UNLICENSED</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.20;</span>

<span class="hljs-keyword">import</span> {<span class="hljs-title">Test</span>, <span class="hljs-title">console2</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"forge-std/Test.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">SharesVault</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../src/SharesVault.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">ERC20</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts/token/ERC20/ERC20.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">TestSharesVault</span> <span class="hljs-keyword">is</span> <span class="hljs-title">Test</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setUp</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{

    }
}

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">MockERC20</span> <span class="hljs-keyword">is</span> <span class="hljs-title">ERC20</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">constructor</span> (<span class="hljs-params"><span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> name_, <span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> symbol_</span>) <span class="hljs-title">ERC20</span>(<span class="hljs-params">name_, symbol_</span>) </span>{}

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">mint</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> to, <span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> </span>{
        _mint(to, amount);
    }  
}
</code></pre>
<p>Now that we have the basic scaffolding of our test setup we can implement the setup and deployment of our vault. This is a simple setup which only requires us to deploy an instance of the <code>SharesVault</code> contract with our <code>MockERC20</code> token passed to the constructor as its deposit token. We can then mint some <code>depositToken</code> to our test users. We have named our vault <code>shares</code> here for better readability when making token transactions. <em>remember our</em> <code>SharesVault</code> instance <strong>is also</strong> our <code>shares</code> token</p>
<pre><code class="lang-solidity"><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">TestSharesVault</span> <span class="hljs-keyword">is</span> <span class="hljs-title">Test</span> </span>{
    <span class="hljs-keyword">address</span> OwnerWallet;
    <span class="hljs-keyword">address</span> user1;
    <span class="hljs-keyword">address</span> user2;
    <span class="hljs-keyword">address</span> user3;
    SharesVault shares;
    MockERC20 depositToken; <span class="hljs-comment">// assets token</span>

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setUp</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        OwnerWallet <span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">69</span>);

        user1 <span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">420</span>);
        user2 <span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">666</span>);
        user3 <span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">777</span>);

        vm.prank(OwnerWallet);
        depositToken <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> MockERC20(<span class="hljs-string">"Deposit Token"</span>, <span class="hljs-string">"DT"</span>);
        depositToken.mint(user1, <span class="hljs-number">10</span> <span class="hljs-literal">ether</span>);
        depositToken.mint(user2, <span class="hljs-number">10</span> <span class="hljs-literal">ether</span>);
        depositToken.mint(OwnerWallet, <span class="hljs-number">10</span> <span class="hljs-literal">ether</span>);
        shares <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> SharesVault(<span class="hljs-keyword">address</span>(depositToken));
        vm.stopPrank();
    }
}
</code></pre>
<h2 id="heading-testing-the-share-creation-mechanism">Testing the Share Creation Mechanism</h2>
<p>Once We have the vault deployed we can start utilizing its functions. The ERC4626 standard includes two function which handle the use case of a user depositing assets and receiving shares in return.</p>
<p>The <code>deposit(uint256 assets, address receiver)</code> function which allows the user to define the amount of asset they want to deposit and receive a corresponding amount of shares.</p>
<p>The <code>mint(uint256 shares, address receiver)</code> function which allows the user to define the amount of shares they want to mint and the vault will pull the corresponding amount of assets from the user in exchange for those minted shares.</p>
<p>We can see these functions in action by first implementing an approval from the user to the vault and then calling the deposit or mint function. After that we can check our balance of shares and the balance of the deposit tokens in the vault.</p>
<pre><code class="lang-solidity">
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test_deposit</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        vm.startPrank(user1);
        depositToken.approve(<span class="hljs-keyword">address</span>(shares), <span class="hljs-number">1</span> <span class="hljs-literal">ether</span>);
        shares.deposit(<span class="hljs-number">1</span> <span class="hljs-literal">ether</span>, user1);
        assertEq(shares.balanceOf(user1), <span class="hljs-number">1</span> <span class="hljs-literal">ether</span>);
        assertEq(depositToken.balanceOf(<span class="hljs-keyword">address</span>(shares)), <span class="hljs-number">1</span> <span class="hljs-literal">ether</span>);
        vm.stopPrank();
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test_mint</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        vm.startPrank(user1);
        depositToken.approve(<span class="hljs-keyword">address</span>(shares), <span class="hljs-number">1</span> <span class="hljs-literal">ether</span>);
        shares.mint(<span class="hljs-number">1</span> <span class="hljs-literal">ether</span>, user1);
        assertEq(shares.balanceOf(user1), <span class="hljs-number">1</span> <span class="hljs-literal">ether</span>);
        assertEq(depositToken.balanceOf(<span class="hljs-keyword">address</span>(shares)), <span class="hljs-number">1</span> <span class="hljs-literal">ether</span>);
        vm.stopPrank();
    }
</code></pre>
<p>We can see that, since our test <code>user1</code> is the only depositor to the vault, we have been minted a share in a 1:1 ratio with the deposit token.</p>
<h2 id="heading-testing-the-share-redemption-mechanism">Testing the Share Redemption Mechanism</h2>
<p>Now, we can have a look at the share redemption mechanism included in our ERC4626 contract. The standard includes two functions for exchanging our shares for the underlying assets of the vault.</p>
<p>The <code>withdraw(uint256 assets, address receiver, address owner)</code> function which allows the user to define the amount of assets they want to withdraw and the vault will pull and burn the corresponding amount of shares from the user in exchange for those assets.</p>
<p>The <code>burn(uint256 shares, address receiver, address owner)</code> function which allows the user to define the amount of shares they want to burn and receive a corresponding amount of assets.</p>
<p>Both of these functions require the <code>msg.sender</code> of the transaction to be the owner of the shares being burned but the withdrawn assets can be sent to any receiver the owner defines via the <code>address receiver</code> parameter.</p>
<p>Our tests will look very similar to the share creation tests, expect that we will first run <code>test_mint()</code> so that our tests start off with a user who has some shares and assets in the vault.</p>
<pre><code class="lang-solidity">    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test_withdraw</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        test_mint();
        vm.startPrank(user1);
        shares.withdraw(<span class="hljs-number">1</span> <span class="hljs-literal">ether</span>, user1, user1);
        assertEq(shares.balanceOf(user1), <span class="hljs-number">0</span>);
        assertEq(depositToken.balanceOf(<span class="hljs-keyword">address</span>(shares)), <span class="hljs-number">0</span>);
        assertEq(depositToken.balanceOf(user1), <span class="hljs-number">10</span> <span class="hljs-literal">ether</span>);
        vm.stopPrank();
    }
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test_redeem</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        test_mint();
        vm.startPrank(user1);
        shares.redeem(<span class="hljs-number">1</span> <span class="hljs-literal">ether</span>, user1, user1);
        assertEq(shares.balanceOf(user1), <span class="hljs-number">0</span>);
        assertEq(depositToken.balanceOf(<span class="hljs-keyword">address</span>(shares)), <span class="hljs-number">0</span>);
        assertEq(depositToken.balanceOf(user1), <span class="hljs-number">10</span> <span class="hljs-literal">ether</span>);
        vm.stopPrank();
    }
</code></pre>
<p>We can see here that our user is able to use either of these functions to redeem their shares in exchange for the underlying assets of the vault to which they are entitled. Which function they use is simply determined by which parameter they wish to define. Either, the amount of shares to redeem or the amount of assets to withdraw.</p>
<h2 id="heading-implementing-profit-sharing">Implementing Profit Sharing.</h2>
<p>Now that we have seen the usage of a fully compliant vault, we can implement some custom functionality to support our business logic.</p>
<p>In this case, lets say, we want to implement a profit sharing mechanism so that shareholders in the vault can receive a dividend of the profits generated by the vaults capital. We'll say that these profits are occasionally distributed from the vault owner to the shareholders.</p>
<p>Thus, we simply need a function which the owner can call to transfer profits, in the form of the asset token into the vault.</p>
<pre><code class="lang-solidity"><span class="hljs-keyword">import</span> { <span class="hljs-title">ERC4626</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">ERC20</span>, <span class="hljs-title">IERC20</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts/token/ERC20/ERC20.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">SafeERC20</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">SharesVault</span> <span class="hljs-keyword">is</span> <span class="hljs-title">ERC4626</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _depositToken</span>) <span class="hljs-title">ERC4626</span>(<span class="hljs-params">IERC20(<span class="hljs-params">_depositToken</span>)</span>) <span class="hljs-title">ERC20</span>(<span class="hljs-params"><span class="hljs-string">"Shares Vault"</span>, <span class="hljs-string">"SV"</span></span>) </span>{

    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">shareProfits</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        SafeERC20.safeTransferFrom(IERC20(asset()), <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), amount);
    }
}
</code></pre>
<p>All we've done here is import the SafeERC20 utility from openzeppelin and implemented a shareProfits function which executes a safeTransferFrom of the asset token from the caller to the vault. Note, however, that this is not strictly necessary. As any asset tokens set directly to the contract without going through the share creation mechanism will result in the vaults total assets being increase while the <code>totalSupply</code> of shares remains the same, thus the existing shareholders receive their portion of these profits regardless if this function is executed or not. We implement it here for convenience and to demonstrate the concept.</p>
<p>Now we can test it out. In order to do so, we will need to setup a test state where the vault has some shareholders and assets. We can achieve this by simply adding a utility function to our test file.</p>
<pre><code class="lang-solidity">    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setup_shareholders</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        vm.startPrank(user1);
        depositToken.approve(<span class="hljs-keyword">address</span>(shares), <span class="hljs-number">10</span> <span class="hljs-literal">ether</span>);
        shares.deposit(<span class="hljs-number">10</span> <span class="hljs-literal">ether</span>, user1);
        vm.stopPrank();
        vm.startPrank(user2);
        depositToken.approve(<span class="hljs-keyword">address</span>(shares), <span class="hljs-number">10</span> <span class="hljs-literal">ether</span>);
        shares.deposit(<span class="hljs-number">10</span> <span class="hljs-literal">ether</span>, user2);
        vm.stopPrank();
        assertEq(shares.balanceOf(user1), <span class="hljs-number">10</span> <span class="hljs-literal">ether</span>);
        assertEq(shares.balanceOf(user2), <span class="hljs-number">10</span> <span class="hljs-literal">ether</span>);
    }
</code></pre>
<p>This will give us two shareholders with equal shares in the vault and 20 ether of assets in the vault. Then we can implement a test were the vault receives some profits from the owner.</p>
<pre><code class="lang-solidity">    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test_profitSharing</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        setup_shareholders();
        vm.startPrank(OwnerWallet);
        depositToken.approve(<span class="hljs-keyword">address</span>(shares), <span class="hljs-number">2</span> <span class="hljs-literal">ether</span>);
        shares.shareProfits(<span class="hljs-number">2</span> <span class="hljs-literal">ether</span>);
        vm.stopPrank();
        assertEq(depositToken.balanceOf(<span class="hljs-keyword">address</span>(shares)), <span class="hljs-number">22</span> <span class="hljs-literal">ether</span>);
        <span class="hljs-keyword">uint256</span> user1_value <span class="hljs-operator">=</span> shares.previewRedeem(<span class="hljs-number">10</span> <span class="hljs-literal">ether</span>);
        assertEq(user1_value, <span class="hljs-number">11</span> <span class="hljs-literal">ether</span>);
    }
</code></pre>
<p>After the owner executes <code>shareProfits</code> and transfers in 2 asset tokens, we can use one of the vaults view functions to get an accounting of our shares new value. In this case, we'll use the <code>previewRedeem</code> function which allows us to see the value of our shares if we were to redeem them for the underlying assets and we'll pass it our <code>user1</code> balance of 10 shares.</p>
<p>Our hypothetical <code>user1</code> deposited 10 asset tokens in exchange for 10 shares. The pools total assets was 20 with 20 shares outstanding, since we had a second user do the same. Now, the pool has earned a profit of 2 asset tokens so we would expect the value of our shares to have grown to 11 asset tokens. 1 token being half the profits since we hold half of the shares.</p>
<p>However if we run this test with <code>forge test -vv</code> we see that it fails! and produces the following output.</p>
<pre><code class="lang-solidity">[FAIL. Reason: assertion failed] test_profitSharing() (<span class="hljs-built_in">gas</span>: <span class="hljs-number">195837</span>)
Logs:
  <span class="hljs-built_in">Error</span>: a <span class="hljs-operator">=</span><span class="hljs-operator">=</span> b not satisfied [<span class="hljs-keyword">uint</span>]
        Left: <span class="hljs-number">10999999999999999999</span>
       Right: <span class="hljs-number">11000000000000000000</span>
</code></pre>
<p>So, what happened? In fact we are only short 1 wei of what we expected.</p>
<p>If we go to the <a target="_blank" href="https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/extensions/ERC4626.sol#L166">openzeppelin implementation</a> we can have a look at how this happened. It is fundamentally an issue of precision. we called <code>previewRedeem</code> in our test which itself calls <code>_convertToAssets</code></p>
<pre><code class="lang-solidity">    <span class="hljs-comment">/** @dev See {IERC4626-previewRedeem}. */</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">previewRedeem</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> shares</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> _convertToAssets(shares, Math.Rounding.Floor);
    }

    <span class="hljs-comment">/**
     * @dev Internal conversion function (from shares to assets) with support for rounding direction.
     */</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_convertToAssets</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> shares, Math.Rounding rounding</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> shares.mulDiv(totalAssets() <span class="hljs-operator">+</span> <span class="hljs-number">1</span>, totalSupply() <span class="hljs-operator">+</span> <span class="hljs-number">10</span> <span class="hljs-operator">*</span><span class="hljs-operator">*</span> _decimalsOffset(), rounding);
    }
</code></pre>
<p>We can see now that conversion function takes the shares amount * totalAssets+1 (plus one avoids having 0 in the denominator during share creation so that the first depositor can receive a 1:1 exchange of assets for shares, +1 here preserves that relationship) the product is then divided by the totalSupply of shares + virtual liquidity ( <code>totalSupply() + 10 ** _decimalOffset()</code>).</p>
<p>The <code>_decimalOffset</code> default value is 0, this number is used to prevent certain kinds of inflations attacks which we will go over later. Here, its default is 10**0 or 1. So we are effectively adding 1 to the totalSupply in addition to adding one to the totalAssets.</p>
<p>This all results in the amount of assets proportional to the shares amount, this final number is then rounded down. running these numbers in python with integer division we can see that the result is 10999999999999999999.</p>
<pre><code class="lang-python"><span class="hljs-number">10000000000000000000</span> * <span class="hljs-number">22000000000000000001</span> // <span class="hljs-number">20000000000000000001</span>
<span class="hljs-number">220000000000000000010000000000000000000</span> // <span class="hljs-number">20000000000000000001</span>
</code></pre>
<p>Since the vault always rounds in favor of the vault, in this case rounding down, the precision of redemption calculations will sometimes result in the loss of value, from the user to the pool. This loss becomes more pronounced the lower the amount of shares being redeemed since it accounts for a larger percentage of the expected return. From the user's perspective this sort of loss should be considered transaction slippage.</p>
<h2 id="heading-inflation-attack-vector">Inflation Attack Vector</h2>
<p>Interestingly, this kind of precision error also occurs in the <code>deposit</code> and <code>mint</code> functions. In fact, under certain circumstances a depositor can receive 0 shares for their assets because of this precision behavior and ends up making a donation to the vault, thus giving free money to the current shareholder.</p>
<p>This is actually an attack vector on ERC4626 vaults, known as an inflation attack, where by an attacking shareholder can front run a large share creation transaction with a large donation to the vault at a specific amount such that share creation transaction will round down to 0 shares.</p>
<p>The inflation attack is the reason for the <code>_decimalsOffset</code> variable to be included as virtual liquidity in the calculation of assets and shares. Effectively, virtual liquidity increases the precision of the shares token making it more expensive for the attacker to donate a sufficient amount to cause a victim to receive 0 shares.</p>
<p>Openzeppelin has a good <a target="_blank" href="https://docs.openzeppelin.com/contracts/5.x/erc4626">explanation of this attack and the solution</a> in their documentation.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Now that we have setup a basic functional vault, we can implement this kind ERC4626 compliant vault with some custom functionality into many different kinds of Defi application such as a lending protocol, AMM, pooled yield vault, DAOs etc.</p>
<h2 id="heading-final-code">Final Code</h2>
<p><code>src/SharesVault.sol</code></p>
<pre><code class="lang-solidity"><span class="hljs-keyword">import</span> { <span class="hljs-title">ERC4626</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">ERC20</span>, <span class="hljs-title">IERC20</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts/token/ERC20/ERC20.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">SafeERC20</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">SharesVault</span> <span class="hljs-keyword">is</span> <span class="hljs-title">ERC4626</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _depositToken</span>) <span class="hljs-title">ERC4626</span>(<span class="hljs-params">IERC20(<span class="hljs-params">_depositToken</span>)</span>) <span class="hljs-title">ERC20</span>(<span class="hljs-params"><span class="hljs-string">"Shares Vault"</span>, <span class="hljs-string">"SV"</span></span>) </span>{

    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">shareProfits</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        SafeERC20.safeTransferFrom(IERC20(asset()), <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), amount);
    }

}
</code></pre>
<p><code>test/SharesVault.t.sol</code></p>
<pre><code class="lang-solidity"><span class="hljs-comment">// SPDX-License-Identifier: UNLICENSED</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.20;</span>

<span class="hljs-keyword">import</span> {<span class="hljs-title">Test</span>, <span class="hljs-title">console2</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"forge-std/Test.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">SharesVault</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../src/SharesVault.sol"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">ERC20</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts/token/ERC20/ERC20.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">TestSharesVault</span> <span class="hljs-keyword">is</span> <span class="hljs-title">Test</span> </span>{
    <span class="hljs-keyword">address</span> OwnerWallet;
    <span class="hljs-keyword">address</span> user1;
    <span class="hljs-keyword">address</span> user2;
    <span class="hljs-keyword">address</span> user3;
    SharesVault shares;
    MockERC20 depositToken; <span class="hljs-comment">// assets token</span>

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setUp</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        OwnerWallet <span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">69</span>);

        user1 <span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">420</span>);
        user2 <span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">666</span>);
        user3 <span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">777</span>);

        vm.prank(OwnerWallet);
        depositToken <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> MockERC20(<span class="hljs-string">"Deposit Token"</span>, <span class="hljs-string">"DT"</span>);
        depositToken.mint(user1, <span class="hljs-number">10</span> <span class="hljs-literal">ether</span>);
        depositToken.mint(user2, <span class="hljs-number">10</span> <span class="hljs-literal">ether</span>);
        depositToken.mint(OwnerWallet, <span class="hljs-number">10</span> <span class="hljs-literal">ether</span>);
        shares <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> SharesVault(<span class="hljs-keyword">address</span>(depositToken));
        vm.stopPrank();
    }
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setup_shareholders</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        vm.startPrank(user1);
        depositToken.approve(<span class="hljs-keyword">address</span>(shares), <span class="hljs-number">10</span> <span class="hljs-literal">ether</span>);
        shares.deposit(<span class="hljs-number">10</span> <span class="hljs-literal">ether</span>, user1);
        vm.stopPrank();
        vm.startPrank(user2);
        depositToken.approve(<span class="hljs-keyword">address</span>(shares), <span class="hljs-number">10</span> <span class="hljs-literal">ether</span>);
        shares.deposit(<span class="hljs-number">10</span> <span class="hljs-literal">ether</span>, user2);
        vm.stopPrank();
        assertEq(shares.balanceOf(user1), <span class="hljs-number">10</span> <span class="hljs-literal">ether</span>);
        assertEq(shares.balanceOf(user2), <span class="hljs-number">10</span> <span class="hljs-literal">ether</span>);
    }
    <span class="hljs-comment">// Two functions handle the use case of a user depositing tokens and receiving shares in return.</span>
    <span class="hljs-comment">// deposit(uint256 assets, address receiver)</span>
    <span class="hljs-comment">// mint(uint256 shares, address receiver)</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test_deposit</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        vm.startPrank(user1);
        depositToken.approve(<span class="hljs-keyword">address</span>(shares), <span class="hljs-number">1</span> <span class="hljs-literal">ether</span>);
        shares.deposit(<span class="hljs-number">1</span> <span class="hljs-literal">ether</span>, user1);
        assertEq(shares.balanceOf(user1), <span class="hljs-number">1</span> <span class="hljs-literal">ether</span>);
        assertEq(depositToken.balanceOf(<span class="hljs-keyword">address</span>(shares)), <span class="hljs-number">1</span> <span class="hljs-literal">ether</span>);
        vm.stopPrank();
    }
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test_mint</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        vm.startPrank(user1);
        depositToken.approve(<span class="hljs-keyword">address</span>(shares), <span class="hljs-number">1</span> <span class="hljs-literal">ether</span>);
        shares.mint(<span class="hljs-number">1</span> <span class="hljs-literal">ether</span>, user1);
        assertEq(shares.balanceOf(user1), <span class="hljs-number">1</span> <span class="hljs-literal">ether</span>);
        assertEq(depositToken.balanceOf(<span class="hljs-keyword">address</span>(shares)), <span class="hljs-number">1</span> <span class="hljs-literal">ether</span>);
        vm.stopPrank();
    }
    <span class="hljs-comment">// Two functions handle the redemption of shares for the underlying asset</span>
    <span class="hljs-comment">// withdraw(uint256 assets, address receiver)</span>
    <span class="hljs-comment">// redeem(uint256 shares, address receiver)</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test_withdraw</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        test_mint();
        vm.startPrank(user1);
        shares.withdraw(<span class="hljs-number">1</span> <span class="hljs-literal">ether</span>, user1, user1);
        assertEq(shares.balanceOf(user1), <span class="hljs-number">0</span>);
        assertEq(depositToken.balanceOf(<span class="hljs-keyword">address</span>(shares)), <span class="hljs-number">0</span>);
        assertEq(depositToken.balanceOf(user1), <span class="hljs-number">10</span> <span class="hljs-literal">ether</span>);
        vm.stopPrank();
    }
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test_redeem</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        test_mint();
        vm.startPrank(user1);
        shares.redeem(<span class="hljs-number">1</span> <span class="hljs-literal">ether</span>, user1, user1);
        assertEq(shares.balanceOf(user1), <span class="hljs-number">0</span>);
        assertEq(depositToken.balanceOf(<span class="hljs-keyword">address</span>(shares)), <span class="hljs-number">0</span>);
        assertEq(depositToken.balanceOf(user1), <span class="hljs-number">10</span> <span class="hljs-literal">ether</span>);
        vm.stopPrank();
    }
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test_profitSharing</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        setup_shareholders();
        vm.startPrank(OwnerWallet);
        depositToken.approve(<span class="hljs-keyword">address</span>(shares), <span class="hljs-number">2</span> <span class="hljs-literal">ether</span>);
        shares.shareProfits(<span class="hljs-number">2</span> <span class="hljs-literal">ether</span>);
        vm.stopPrank();
        assertEq(depositToken.balanceOf(<span class="hljs-keyword">address</span>(shares)), <span class="hljs-number">22</span> <span class="hljs-literal">ether</span>);
        <span class="hljs-keyword">uint256</span> user1_value <span class="hljs-operator">=</span> shares.previewRedeem(<span class="hljs-number">10</span> <span class="hljs-literal">ether</span>);
        assertEq(user1_value, <span class="hljs-number">11</span> <span class="hljs-literal">ether</span>);
    }
}

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">MockERC20</span> <span class="hljs-keyword">is</span> <span class="hljs-title">ERC20</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">constructor</span> (<span class="hljs-params"><span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> name_, <span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> symbol_</span>) <span class="hljs-title">ERC20</span>(<span class="hljs-params">name_, symbol_</span>) </span>{
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">mint</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> to, <span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> </span>{
        _mint(to, amount);
    }
}
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Setting Up a Foundry Project and Installing Open-Zeppelin at a Specific Version]]></title><description><![CDATA[Installing
Foundry is an excellent modern framework for developing and testing solidity smart contracts. One of the primary benefits is being able to write tests directly in solidity.
First, we need to install the toolchain installer
$ curl -L https:...]]></description><link>https://yielddev.io/setting-up-a-foundry-project-and-installing-open-zeppelin-at-a-specific-version</link><guid isPermaLink="true">https://yielddev.io/setting-up-a-foundry-project-and-installing-open-zeppelin-at-a-specific-version</guid><category><![CDATA[Solidity]]></category><category><![CDATA[openzeppelin]]></category><category><![CDATA[foundry]]></category><category><![CDATA[Forge]]></category><category><![CDATA[Ethereum]]></category><category><![CDATA[development]]></category><category><![CDATA[Blockchain]]></category><dc:creator><![CDATA[Yield Dev]]></dc:creator><pubDate>Thu, 01 Feb 2024 07:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1708288626219/bbe5d091-3c12-43b4-8061-19e9deecf4e6.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-installing">Installing</h2>
<p>Foundry is an excellent modern framework for developing and testing solidity smart contracts. One of the primary benefits is being able to write tests directly in solidity.</p>
<p>First, we need to install the toolchain installer</p>
<pre><code class="lang-bash">$ curl -L https://foundry.paradigm.xyz | bash
</code></pre>
<p>Then we can install everything we need with:</p>
<pre><code class="lang-bash">$ foundryup
</code></pre>
<p>After that we can create a hello world project.</p>
<pre><code class="lang-bash">$ forge init hello_world
</code></pre>
<p>This sets up a foundry project with an example contract and test. Now you can run</p>
<pre><code class="lang-bash">$ forge build 
$ forge <span class="hljs-built_in">test</span>
</code></pre>
<p>now you can have a look at the example test file and see how tests are written in solidity.</p>
<h2 id="heading-install-external-libraries-and-remap-imports">Install external libraries and Remap imports</h2>
<p>To install an external library like openzeppelin you can run</p>
<pre><code class="lang-bash">$ forge install Openzeppelin/openzeppelin-contracts@v5.0.1 --no-commit
</code></pre>
<p>Then in your foundry.toml file you can add the following line to include the library in your project.</p>
<pre><code class="lang-toml"><span class="hljs-attr">remappings</span> = [
   <span class="hljs-string">'@openzeppelin/contracts@v5.0.0/=lib/openzeppelin-contracts/contracts/'</span>
]
</code></pre>
<p>after that run :</p>
<pre><code class="lang-bash">$ forge remappings &gt; remappings.txt
</code></pre>
<p>now you can import openzeppelin files and lock their versions in your project. like so:</p>
<pre><code class="lang-solidity"><span class="hljs-keyword">import</span> { <span class="hljs-title">ERC20</span>, <span class="hljs-title">IERC20</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts/token/ERC20/ERC20.sol"</span>;
</code></pre>
]]></content:encoded></item></channel></rss>